[Bukkit] Add NMS Implementations for 1.8.x and 1.9.x
This commit is contained in:
1 parent
4efb55f685
commit
48cdeb7283
26 files changed
+1829
-7
No files matched your search
@@ -18,7 +18,11 @@ val platforms = setOf(
|
||||
projects.bukkit.paper1205,
|
||||
projects.bukkit.paper1212,
|
||||
projects.bukkit.paper1214,
|
||||
projects.bukkit.v18R1,
|
||||
projects.bukkit.v18R2,
|
||||
projects.bukkit.v18R3,
|
||||
projects.bukkit.v19R1,
|
||||
projects.bukkit.v19R2,
|
||||
projects.bukkit.v110R1,
|
||||
projects.bukkit.v111R1,
|
||||
projects.bukkit.v112R1,
|
||||
|
||||
@@ -156,7 +156,7 @@ public class BukkitPlatform implements BackendPlatform {
|
||||
}
|
||||
Bukkit.getConsoleSender().sendMessage("§c[TAB] Please use " +
|
||||
"a plugin version with full support for your server version for optimal experience. This plugin version " +
|
||||
"has full support for 1.8.8 and 1.12 - 1.21.8.");
|
||||
"has full support for 1.8 - 1.21.8.");
|
||||
}
|
||||
return new BukkitImplementationProvider();
|
||||
}
|
||||
|
||||
+1
-3
@@ -40,7 +40,6 @@ public class NMSPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
private static final Field PlayerInfoData_DisplayName = ReflectionUtils.getOnlyField(PlayerInfoData, IChatBaseComponent.class);
|
||||
private static final Field PlayerInfoData_GameMode = ReflectionUtils.getOnlyField(PlayerInfoData, EnumGamemode.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);
|
||||
|
||||
/**
|
||||
@@ -98,8 +97,7 @@ public class NMSPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public void setPlayerListHeaderFooter(@NonNull TabComponent header, @NonNull TabComponent footer) {
|
||||
PacketPlayOutPlayerListHeaderFooter packet = new PacketPlayOutPlayerListHeaderFooter();
|
||||
HEADER.set(packet, header.convert());
|
||||
PacketPlayOutPlayerListHeaderFooter packet = new PacketPlayOutPlayerListHeaderFooter(header.convert());
|
||||
FOOTER.set(packet, footer.convert());
|
||||
sendPacket(packet);
|
||||
}
|
||||
|
||||
+1
-3
@@ -40,7 +40,6 @@ public class NMSPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
private static final Field PlayerInfoData_DisplayName = ReflectionUtils.getOnlyField(PlayerInfoData, IChatBaseComponent.class);
|
||||
private static final Field PlayerInfoData_GameMode = ReflectionUtils.getOnlyField(PlayerInfoData, EnumGamemode.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);
|
||||
|
||||
/**
|
||||
@@ -98,8 +97,7 @@ public class NMSPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public void setPlayerListHeaderFooter(@NonNull TabComponent header, @NonNull TabComponent footer) {
|
||||
PacketPlayOutPlayerListHeaderFooter packet = new PacketPlayOutPlayerListHeaderFooter();
|
||||
HEADER.set(packet, header.convert());
|
||||
PacketPlayOutPlayerListHeaderFooter packet = new PacketPlayOutPlayerListHeaderFooter(header.convert());
|
||||
FOOTER.set(packet, footer.convert());
|
||||
sendPacket(packet);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
dependencies {
|
||||
implementation(projects.bukkit)
|
||||
compileOnly("org.bukkit:craftbukkit:1.8-R0.1-SNAPSHOT")
|
||||
}
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
package me.neznamy.tab.platforms.bukkit.v1_8_R1;
|
||||
|
||||
import me.neznamy.chat.ChatModifier;
|
||||
import me.neznamy.chat.component.ObjectComponent;
|
||||
import me.neznamy.tab.platforms.bukkit.provider.ComponentConverter;
|
||||
import net.minecraft.server.v1_8_R1.ChatComponentText;
|
||||
import net.minecraft.server.v1_8_R1.ChatMessage;
|
||||
import net.minecraft.server.v1_8_R1.EnumChatFormat;
|
||||
import net.minecraft.server.v1_8_R1.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) {
|
||||
return new ChatComponentText("<Keybind components were added in 1.12>");
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public Object newObjectComponent(@NotNull String atlas, @NotNull String sprite) {
|
||||
return new ChatComponentText(ObjectComponent.ERROR_MESSAGE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyStyle(@NotNull Object nmsComponent, @NotNull ChatModifier modifier) {
|
||||
((IChatBaseComponent)nmsComponent).setChatModifier(
|
||||
new net.minecraft.server.v1_8_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);
|
||||
}
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
package me.neznamy.tab.platforms.bukkit.v1_8_R1;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import lombok.Getter;
|
||||
import me.neznamy.tab.platforms.bukkit.BukkitTabPlayer;
|
||||
import me.neznamy.tab.platforms.bukkit.provider.ComponentConverter;
|
||||
import me.neznamy.tab.platforms.bukkit.provider.ImplementationProvider;
|
||||
import me.neznamy.tab.shared.platform.Scoreboard;
|
||||
import me.neznamy.tab.shared.platform.TabList;
|
||||
import me.neznamy.tab.shared.util.ReflectionUtils;
|
||||
import me.neznamy.tab.shared.util.function.FunctionWithException;
|
||||
import net.minecraft.server.v1_8_R1.NetworkManager;
|
||||
import org.bukkit.craftbukkit.v1_8_R1.entity.CraftPlayer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
/**
|
||||
* Implementation provider using direct NMS code for 1.8.
|
||||
*/
|
||||
@Getter
|
||||
public class NMSImplementationProvider implements ImplementationProvider {
|
||||
|
||||
private static final Field channel = ReflectionUtils.getOnlyField(NetworkManager.class, Channel.class);
|
||||
|
||||
@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 -> (Channel) channel.get(((CraftPlayer)player.getPlayer()).getHandle().playerConnection.networkManager);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPing(@NotNull BukkitTabPlayer player) {
|
||||
return ((CraftPlayer)player.getPlayer()).getHandle().ping;
|
||||
}
|
||||
}
|
||||
+157
@@ -0,0 +1,157 @@
|
||||
package me.neznamy.tab.platforms.bukkit.v1_8_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_8_R1.*;
|
||||
import org.bukkit.craftbukkit.v1_8_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 EnumNameTagVisibility[] visibilities = 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(maybeCut(objective.getTitle().toLegacyText(), Limitations.SCOREBOARD_TITLE_PRE_1_13));
|
||||
obj.a(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();
|
||||
obj.setDisplayName(maybeCut(objective.getTitle().toLegacyText(), Limitations.SCOREBOARD_TITLE_PRE_1_13));
|
||||
obj.a(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.a(visibilities[team.getVisibility().ordinal()]);
|
||||
t.setPrefix(maybeCut(team.getPrefix().toLegacyText(), Limitations.TEAM_PREFIX_SUFFIX_PRE_1_13));
|
||||
t.setSuffix(maybeCut(team.getSuffix().toLegacyText(), Limitations.TEAM_PREFIX_SUFFIX_PRE_1_13));
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
@SuppressWarnings("unchecked")
|
||||
public void onPacketSend(@NonNull Object packet) {
|
||||
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 (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, 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);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private String maybeCut(@NonNull String string, int length) {
|
||||
if (player.getVersion().getMinorVersion() < 13 || TAB.getInstance().getConfiguration().getConfig().isPacketEventsCompensation()) {
|
||||
return cutTo(string, length);
|
||||
}
|
||||
return string;
|
||||
}
|
||||
}
|
||||
+186
@@ -0,0 +1,186 @@
|
||||
package me.neznamy.tab.platforms.bukkit.v1_8_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_8_R1.*;
|
||||
import org.bukkit.craftbukkit.v1_8_R1.entity.CraftPlayer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
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> {
|
||||
|
||||
private static final Field ACTION = ReflectionUtils.getOnlyField(PacketPlayOutPlayerInfo.class, 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 PlayerInfoData_GameMode = ReflectionUtils.getOnlyField(PlayerInfoData.class, EnumGamemode.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(EnumPlayerInfoAction.REMOVE_PLAYER, entry, "", null, 0, 0, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateDisplayName0(@NonNull UUID entry, @Nullable TabComponent displayName) {
|
||||
sendPacket(EnumPlayerInfoAction.UPDATE_DISPLAY_NAME, entry, "", null, 0, 0, displayName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateLatency(@NonNull UUID entry, int latency) {
|
||||
sendPacket(EnumPlayerInfoAction.UPDATE_LATENCY, entry, "", null, latency, 0, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateGameMode(@NonNull UUID entry, int gameMode) {
|
||||
sendPacket(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.21.2
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateHat(@NonNull UUID entry, boolean showHat) {
|
||||
// Added in 1.21.4
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addEntry0(@NonNull Entry entry) {
|
||||
sendPacket(EnumPlayerInfoAction.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;
|
||||
EnumPlayerInfoAction action = (EnumPlayerInfoAction) ACTION.get(packet);
|
||||
for (PlayerInfoData nmsData : (List<PlayerInfoData>) PLAYERS.get(packet)) {
|
||||
GameProfile profile = nmsData.a();
|
||||
UUID id = profile.getId();
|
||||
if (action == EnumPlayerInfoAction.UPDATE_DISPLAY_NAME || action == EnumPlayerInfoAction.ADD_PLAYER) {
|
||||
TabComponent expectedName = getForcedDisplayNames().get(id);
|
||||
if (expectedName != null) PlayerInfoData_DisplayName.set(nmsData, expectedName.convert());
|
||||
}
|
||||
if (action == EnumPlayerInfoAction.UPDATE_GAME_MODE || action == EnumPlayerInfoAction.ADD_PLAYER) {
|
||||
Integer forcedGameMode = getForcedGameModes().get(id);
|
||||
if (forcedGameMode != null) PlayerInfoData_GameMode.set(nmsData, forcedGameMode);
|
||||
}
|
||||
if (action == EnumPlayerInfoAction.UPDATE_LATENCY || action == EnumPlayerInfoAction.ADD_PLAYER) {
|
||||
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 == EnumPlayerInfoAction.ADD_PLAYER) {
|
||||
TAB.getInstance().getFeatureManager().onEntryAdd(player, id, profile.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
private void sendPacket(@NonNull EnumPlayerInfoAction action, @NonNull UUID id, @NonNull String name,
|
||||
@Nullable Skin skin, int latency, int gameMode, @Nullable TabComponent displayName) {
|
||||
PacketPlayOutPlayerInfo packet = new PacketPlayOutPlayerInfo(action);
|
||||
PLAYERS.set(packet, Collections.singletonList(new PlayerInfoData(
|
||||
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.3-R0.1-SNAPSHOT")
|
||||
}
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
package me.neznamy.tab.platforms.bukkit.v1_8_R2;
|
||||
|
||||
import me.neznamy.chat.ChatModifier;
|
||||
import me.neznamy.chat.component.ObjectComponent;
|
||||
import me.neznamy.tab.platforms.bukkit.provider.ComponentConverter;
|
||||
import net.minecraft.server.v1_8_R2.ChatComponentText;
|
||||
import net.minecraft.server.v1_8_R2.ChatMessage;
|
||||
import net.minecraft.server.v1_8_R2.EnumChatFormat;
|
||||
import net.minecraft.server.v1_8_R2.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) {
|
||||
return new ChatComponentText("<Keybind components were added in 1.12>");
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public Object newObjectComponent(@NotNull String atlas, @NotNull String sprite) {
|
||||
return new ChatComponentText(ObjectComponent.ERROR_MESSAGE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyStyle(@NotNull Object nmsComponent, @NotNull ChatModifier modifier) {
|
||||
((IChatBaseComponent)nmsComponent).setChatModifier(
|
||||
new net.minecraft.server.v1_8_R2.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_8_R2;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import lombok.Getter;
|
||||
import me.neznamy.tab.platforms.bukkit.BukkitTabPlayer;
|
||||
import me.neznamy.tab.platforms.bukkit.provider.ComponentConverter;
|
||||
import me.neznamy.tab.platforms.bukkit.provider.ImplementationProvider;
|
||||
import me.neznamy.tab.shared.platform.Scoreboard;
|
||||
import me.neznamy.tab.shared.platform.TabList;
|
||||
import me.neznamy.tab.shared.util.function.FunctionWithException;
|
||||
import org.bukkit.craftbukkit.v1_8_R2.entity.CraftPlayer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Implementation provider using direct NMS code for 1.8.3.
|
||||
*/
|
||||
@Getter
|
||||
public class NMSImplementationProvider implements ImplementationProvider {
|
||||
|
||||
@NotNull
|
||||
private final ComponentConverter componentConverter = new NMSComponentConverter();
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public Scoreboard newScoreboard(@NotNull BukkitTabPlayer player) {
|
||||
return new NMSPacketScoreboard(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public TabList newTabList(@NotNull BukkitTabPlayer player) {
|
||||
return new NMSPacketTabList(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public FunctionWithException<BukkitTabPlayer, Channel> getChannelFunction() {
|
||||
return player -> ((CraftPlayer)player.getPlayer()).getHandle().playerConnection.networkManager.k;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPing(@NotNull BukkitTabPlayer player) {
|
||||
return ((CraftPlayer)player.getPlayer()).getHandle().ping;
|
||||
}
|
||||
}
|
||||
+157
@@ -0,0 +1,157 @@
|
||||
package me.neznamy.tab.platforms.bukkit.v1_8_R2;
|
||||
|
||||
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_R2.*;
|
||||
import org.bukkit.craftbukkit.v1_8_R2.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(maybeCut(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();
|
||||
obj.setDisplayName(maybeCut(objective.getTitle().toLegacyText(), Limitations.SCOREBOARD_TITLE_PRE_1_13));
|
||||
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(maybeCut(team.getPrefix().toLegacyText(), Limitations.TEAM_PREFIX_SUFFIX_PRE_1_13));
|
||||
t.setSuffix(maybeCut(team.getSuffix().toLegacyText(), Limitations.TEAM_PREFIX_SUFFIX_PRE_1_13));
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
@SuppressWarnings("unchecked")
|
||||
public void onPacketSend(@NonNull Object packet) {
|
||||
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 (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, 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);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private String maybeCut(@NonNull String string, int length) {
|
||||
if (player.getVersion().getMinorVersion() < 13 || TAB.getInstance().getConfiguration().getConfig().isPacketEventsCompensation()) {
|
||||
return cutTo(string, length);
|
||||
}
|
||||
return string;
|
||||
}
|
||||
}
|
||||
+187
@@ -0,0 +1,187 @@
|
||||
package me.neznamy.tab.platforms.bukkit.v1_8_R2;
|
||||
|
||||
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_R2.*;
|
||||
import net.minecraft.server.v1_8_R2.PacketPlayOutPlayerInfo.EnumPlayerInfoAction;
|
||||
import net.minecraft.server.v1_8_R2.PacketPlayOutPlayerInfo.PlayerInfoData;
|
||||
import org.bukkit.craftbukkit.v1_8_R2.entity.CraftPlayer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
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> {
|
||||
|
||||
private static final Field ACTION = ReflectionUtils.getOnlyField(PacketPlayOutPlayerInfo.class, 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 PlayerInfoData_GameMode = ReflectionUtils.getOnlyField(PlayerInfoData.class, WorldSettings.EnumGamemode.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(EnumPlayerInfoAction.REMOVE_PLAYER, entry, "", null, 0, 0, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateDisplayName0(@NonNull UUID entry, @Nullable TabComponent displayName) {
|
||||
sendPacket(EnumPlayerInfoAction.UPDATE_DISPLAY_NAME, entry, "", null, 0, 0, displayName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateLatency(@NonNull UUID entry, int latency) {
|
||||
sendPacket(EnumPlayerInfoAction.UPDATE_LATENCY, entry, "", null, latency, 0, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateGameMode(@NonNull UUID entry, int gameMode) {
|
||||
sendPacket(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.21.2
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateHat(@NonNull UUID entry, boolean showHat) {
|
||||
// Added in 1.21.4
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addEntry0(@NonNull Entry entry) {
|
||||
sendPacket(EnumPlayerInfoAction.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;
|
||||
EnumPlayerInfoAction action = (EnumPlayerInfoAction) ACTION.get(packet);
|
||||
for (PlayerInfoData nmsData : (List<PlayerInfoData>) PLAYERS.get(packet)) {
|
||||
GameProfile profile = nmsData.a();
|
||||
UUID id = profile.getId();
|
||||
if (action == EnumPlayerInfoAction.UPDATE_DISPLAY_NAME || action == EnumPlayerInfoAction.ADD_PLAYER) {
|
||||
TabComponent expectedName = getForcedDisplayNames().get(id);
|
||||
if (expectedName != null) PlayerInfoData_DisplayName.set(nmsData, expectedName.convert());
|
||||
}
|
||||
if (action == EnumPlayerInfoAction.UPDATE_GAME_MODE || action == EnumPlayerInfoAction.ADD_PLAYER) {
|
||||
Integer forcedGameMode = getForcedGameModes().get(id);
|
||||
if (forcedGameMode != null) PlayerInfoData_GameMode.set(nmsData, forcedGameMode);
|
||||
}
|
||||
if (action == EnumPlayerInfoAction.UPDATE_LATENCY || action == EnumPlayerInfoAction.ADD_PLAYER) {
|
||||
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 == EnumPlayerInfoAction.ADD_PLAYER) {
|
||||
TAB.getInstance().getFeatureManager().onEntryAdd(player, id, profile.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
private void sendPacket(@NonNull EnumPlayerInfoAction action, @NonNull UUID id, @NonNull String name,
|
||||
@Nullable Skin skin, int latency, int gameMode, @Nullable TabComponent displayName) {
|
||||
PacketPlayOutPlayerInfo packet = new PacketPlayOutPlayerInfo(action);
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
dependencies {
|
||||
implementation(projects.bukkit)
|
||||
compileOnly("org.bukkit:craftbukkit:1.9-R0.1-SNAPSHOT")
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
package me.neznamy.tab.platforms.bukkit.v1_9_R1;
|
||||
|
||||
import me.neznamy.chat.ChatModifier;
|
||||
import me.neznamy.chat.component.ObjectComponent;
|
||||
import me.neznamy.tab.platforms.bukkit.provider.ComponentConverter;
|
||||
import net.minecraft.server.v1_9_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 ChatComponentText("<Keybind components were added in 1.12>");
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public Object newObjectComponent(@NotNull String atlas, @NotNull String sprite) {
|
||||
return new ChatComponentText(ObjectComponent.ERROR_MESSAGE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyStyle(@NotNull Object nmsComponent, @NotNull ChatModifier modifier) {
|
||||
((IChatBaseComponent)nmsComponent).setChatModifier(
|
||||
new net.minecraft.server.v1_9_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_9_R1;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import lombok.Getter;
|
||||
import me.neznamy.tab.platforms.bukkit.BukkitTabPlayer;
|
||||
import me.neznamy.tab.platforms.bukkit.provider.ComponentConverter;
|
||||
import me.neznamy.tab.platforms.bukkit.provider.ImplementationProvider;
|
||||
import me.neznamy.tab.shared.platform.Scoreboard;
|
||||
import me.neznamy.tab.shared.platform.TabList;
|
||||
import me.neznamy.tab.shared.util.function.FunctionWithException;
|
||||
import org.bukkit.craftbukkit.v1_9_R1.entity.CraftPlayer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Implementation provider using direct NMS code for 1.9 - 1.9.2.
|
||||
*/
|
||||
@Getter
|
||||
public class NMSImplementationProvider implements ImplementationProvider {
|
||||
|
||||
@NotNull
|
||||
private final ComponentConverter componentConverter = new NMSComponentConverter();
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public Scoreboard newScoreboard(@NotNull BukkitTabPlayer player) {
|
||||
return new NMSPacketScoreboard(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public TabList newTabList(@NotNull BukkitTabPlayer player) {
|
||||
return new NMSPacketTabList(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public FunctionWithException<BukkitTabPlayer, Channel> getChannelFunction() {
|
||||
return player -> ((CraftPlayer)player.getPlayer()).getHandle().playerConnection.networkManager.channel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPing(@NotNull BukkitTabPlayer player) {
|
||||
return ((CraftPlayer)player.getPlayer()).getHandle().ping;
|
||||
}
|
||||
}
|
||||
+159
@@ -0,0 +1,159 @@
|
||||
package me.neznamy.tab.platforms.bukkit.v1_9_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_9_R1.*;
|
||||
import org.bukkit.craftbukkit.v1_9_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(maybeCut(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();
|
||||
obj.setDisplayName(maybeCut(objective.getTitle().toLegacyText(), Limitations.SCOREBOARD_TITLE_PRE_1_13));
|
||||
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.a(collisions[team.getCollision().ordinal()]);
|
||||
t.setPrefix(maybeCut(team.getPrefix().toLegacyText(), Limitations.TEAM_PREFIX_SUFFIX_PRE_1_13));
|
||||
t.setSuffix(maybeCut(team.getSuffix().toLegacyText(), Limitations.TEAM_PREFIX_SUFFIX_PRE_1_13));
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
@SuppressWarnings("unchecked")
|
||||
public void onPacketSend(@NonNull Object packet) {
|
||||
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 (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, 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);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private String maybeCut(@NonNull String string, int length) {
|
||||
if (player.getVersion().getMinorVersion() < 13 || TAB.getInstance().getConfiguration().getConfig().isPacketEventsCompensation()) {
|
||||
return cutTo(string, length);
|
||||
}
|
||||
return string;
|
||||
}
|
||||
}
|
||||
+188
@@ -0,0 +1,188 @@
|
||||
package me.neznamy.tab.platforms.bukkit.v1_9_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_9_R1.*;
|
||||
import net.minecraft.server.v1_9_R1.PacketPlayOutPlayerInfo.EnumPlayerInfoAction;
|
||||
import net.minecraft.server.v1_9_R1.PacketPlayOutPlayerInfo.PlayerInfoData;
|
||||
import org.bukkit.craftbukkit.v1_9_R1.entity.CraftPlayer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
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> {
|
||||
|
||||
private static final Field ACTION = ReflectionUtils.getOnlyField(PacketPlayOutPlayerInfo.class, EnumPlayerInfoAction.class);
|
||||
private static final Field PLAYERS = ReflectionUtils.getOnlyField(PacketPlayOutPlayerInfo.class, List.class);
|
||||
|
||||
private static final Field PlayerInfoData_Profile = ReflectionUtils.getOnlyField(PlayerInfoData.class, GameProfile.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 PlayerInfoData_GameMode = ReflectionUtils.getOnlyField(PlayerInfoData.class, WorldSettings.EnumGamemode.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(EnumPlayerInfoAction.REMOVE_PLAYER, entry, "", null, 0, 0, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateDisplayName0(@NonNull UUID entry, @Nullable TabComponent displayName) {
|
||||
sendPacket(EnumPlayerInfoAction.UPDATE_DISPLAY_NAME, entry, "", null, 0, 0, displayName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateLatency(@NonNull UUID entry, int latency) {
|
||||
sendPacket(EnumPlayerInfoAction.UPDATE_LATENCY, entry, "", null, latency, 0, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateGameMode(@NonNull UUID entry, int gameMode) {
|
||||
sendPacket(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.21.2
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateHat(@NonNull UUID entry, boolean showHat) {
|
||||
// Added in 1.21.4
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addEntry0(@NonNull Entry entry) {
|
||||
sendPacket(EnumPlayerInfoAction.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;
|
||||
EnumPlayerInfoAction action = (EnumPlayerInfoAction) ACTION.get(packet);
|
||||
for (Object nmsData : (List<Object>) PLAYERS.get(packet)) {
|
||||
GameProfile profile = (GameProfile) PlayerInfoData_Profile.get(nmsData);
|
||||
UUID id = profile.getId();
|
||||
if (action == EnumPlayerInfoAction.UPDATE_DISPLAY_NAME || action == EnumPlayerInfoAction.ADD_PLAYER) {
|
||||
TabComponent expectedName = getForcedDisplayNames().get(id);
|
||||
if (expectedName != null) PlayerInfoData_DisplayName.set(nmsData, expectedName.convert());
|
||||
}
|
||||
if (action == EnumPlayerInfoAction.UPDATE_GAME_MODE || action == EnumPlayerInfoAction.ADD_PLAYER) {
|
||||
Integer forcedGameMode = getForcedGameModes().get(id);
|
||||
if (forcedGameMode != null) PlayerInfoData_GameMode.set(nmsData, forcedGameMode);
|
||||
}
|
||||
if (action == EnumPlayerInfoAction.UPDATE_LATENCY || action == EnumPlayerInfoAction.ADD_PLAYER) {
|
||||
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 == EnumPlayerInfoAction.ADD_PLAYER) {
|
||||
TAB.getInstance().getFeatureManager().onEntryAdd(player, id, profile.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
private void sendPacket(@NonNull EnumPlayerInfoAction action, @NonNull UUID id, @NonNull String name,
|
||||
@Nullable Skin skin, int latency, int gameMode, @Nullable TabComponent displayName) {
|
||||
PacketPlayOutPlayerInfo packet = new PacketPlayOutPlayerInfo(action);
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
dependencies {
|
||||
implementation(projects.bukkit)
|
||||
compileOnly("org.bukkit:craftbukkit:1.9.4-R0.1-SNAPSHOT")
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
package me.neznamy.tab.platforms.bukkit.v1_9_R2;
|
||||
|
||||
import me.neznamy.chat.ChatModifier;
|
||||
import me.neznamy.chat.component.ObjectComponent;
|
||||
import me.neznamy.tab.platforms.bukkit.provider.ComponentConverter;
|
||||
import net.minecraft.server.v1_9_R2.*;
|
||||
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 ChatComponentText("<Keybind components were added in 1.12>");
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public Object newObjectComponent(@NotNull String atlas, @NotNull String sprite) {
|
||||
return new ChatComponentText(ObjectComponent.ERROR_MESSAGE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyStyle(@NotNull Object nmsComponent, @NotNull ChatModifier modifier) {
|
||||
((IChatBaseComponent)nmsComponent).setChatModifier(
|
||||
new net.minecraft.server.v1_9_R2.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_9_R2;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import lombok.Getter;
|
||||
import me.neznamy.tab.platforms.bukkit.BukkitTabPlayer;
|
||||
import me.neznamy.tab.platforms.bukkit.provider.ComponentConverter;
|
||||
import me.neznamy.tab.platforms.bukkit.provider.ImplementationProvider;
|
||||
import me.neznamy.tab.shared.platform.Scoreboard;
|
||||
import me.neznamy.tab.shared.platform.TabList;
|
||||
import me.neznamy.tab.shared.util.function.FunctionWithException;
|
||||
import org.bukkit.craftbukkit.v1_9_R2.entity.CraftPlayer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Implementation provider using direct NMS code for 1.9.4.
|
||||
*/
|
||||
@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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPing(@NotNull BukkitTabPlayer player) {
|
||||
return ((CraftPlayer)player.getPlayer()).getHandle().ping;
|
||||
}
|
||||
}
|
||||
+159
@@ -0,0 +1,159 @@
|
||||
package me.neznamy.tab.platforms.bukkit.v1_9_R2;
|
||||
|
||||
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_9_R2.*;
|
||||
import org.bukkit.craftbukkit.v1_9_R2.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(maybeCut(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();
|
||||
obj.setDisplayName(maybeCut(objective.getTitle().toLegacyText(), Limitations.SCOREBOARD_TITLE_PRE_1_13));
|
||||
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(maybeCut(team.getPrefix().toLegacyText(), Limitations.TEAM_PREFIX_SUFFIX_PRE_1_13));
|
||||
t.setSuffix(maybeCut(team.getSuffix().toLegacyText(), Limitations.TEAM_PREFIX_SUFFIX_PRE_1_13));
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
@SuppressWarnings("unchecked")
|
||||
public void onPacketSend(@NonNull Object packet) {
|
||||
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 (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, 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);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private String maybeCut(@NonNull String string, int length) {
|
||||
if (player.getVersion().getMinorVersion() < 13 || TAB.getInstance().getConfiguration().getConfig().isPacketEventsCompensation()) {
|
||||
return cutTo(string, length);
|
||||
}
|
||||
return string;
|
||||
}
|
||||
}
|
||||
+193
@@ -0,0 +1,193 @@
|
||||
package me.neznamy.tab.platforms.bukkit.v1_9_R2;
|
||||
|
||||
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_9_R2.*;
|
||||
import net.minecraft.server.v1_9_R2.PacketPlayOutPlayerInfo.EnumPlayerInfoAction;
|
||||
import org.bukkit.craftbukkit.v1_9_R2.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, 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 PlayerInfoData_GameMode = ReflectionUtils.getOnlyField(PlayerInfoData, WorldSettings.EnumGamemode.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(EnumPlayerInfoAction.REMOVE_PLAYER, entry, "", null, 0, 0, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateDisplayName0(@NonNull UUID entry, @Nullable TabComponent displayName) {
|
||||
sendPacket(EnumPlayerInfoAction.UPDATE_DISPLAY_NAME, entry, "", null, 0, 0, displayName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateLatency(@NonNull UUID entry, int latency) {
|
||||
sendPacket(EnumPlayerInfoAction.UPDATE_LATENCY, entry, "", null, latency, 0, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateGameMode(@NonNull UUID entry, int gameMode) {
|
||||
sendPacket(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.21.2
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateHat(@NonNull UUID entry, boolean showHat) {
|
||||
// Added in 1.21.4
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addEntry0(@NonNull Entry entry) {
|
||||
sendPacket(EnumPlayerInfoAction.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;
|
||||
EnumPlayerInfoAction action = (EnumPlayerInfoAction) ACTION.get(packet);
|
||||
for (Object nmsData : (List<Object>) PLAYERS.get(packet)) {
|
||||
GameProfile profile = (GameProfile) PlayerInfoData_Profile.get(nmsData);
|
||||
UUID id = profile.getId();
|
||||
if (action == EnumPlayerInfoAction.UPDATE_DISPLAY_NAME || action == EnumPlayerInfoAction.ADD_PLAYER) {
|
||||
TabComponent expectedName = getForcedDisplayNames().get(id);
|
||||
if (expectedName != null) PlayerInfoData_DisplayName.set(nmsData, expectedName.convert());
|
||||
}
|
||||
if (action == EnumPlayerInfoAction.UPDATE_GAME_MODE || action == EnumPlayerInfoAction.ADD_PLAYER) {
|
||||
Integer forcedGameMode = getForcedGameModes().get(id);
|
||||
if (forcedGameMode != null) PlayerInfoData_GameMode.set(nmsData, forcedGameMode);
|
||||
}
|
||||
if (action == EnumPlayerInfoAction.UPDATE_LATENCY || action == EnumPlayerInfoAction.ADD_PLAYER) {
|
||||
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 == EnumPlayerInfoAction.ADD_PLAYER) {
|
||||
TAB.getInstance().getFeatureManager().onEntryAdd(player, id, profile.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
private void sendPacket(@NonNull EnumPlayerInfoAction action, @NonNull UUID id, @NonNull String name,
|
||||
@Nullable Skin skin, int latency, int gameMode, @Nullable TabComponent displayName) {
|
||||
PacketPlayOutPlayerInfo packet = new PacketPlayOutPlayerInfo(action);
|
||||
PLAYERS.set(packet, Collections.singletonList(newPlayerInfoData.newInstance(
|
||||
packet,
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,11 @@ val platforms = setOf(
|
||||
rootProject.projects.bukkit.paper1205,
|
||||
rootProject.projects.bukkit.paper1212,
|
||||
rootProject.projects.bukkit.paper1214,
|
||||
rootProject.projects.bukkit.v18R1,
|
||||
rootProject.projects.bukkit.v18R2,
|
||||
rootProject.projects.bukkit.v18R3,
|
||||
rootProject.projects.bukkit.v19R1,
|
||||
rootProject.projects.bukkit.v19R2,
|
||||
rootProject.projects.bukkit.v110R1,
|
||||
rootProject.projects.bukkit.v111R1,
|
||||
rootProject.projects.bukkit.v112R1,
|
||||
|
||||
@@ -35,7 +35,11 @@ include(":bukkit")
|
||||
include(":bukkit:paper_1_20_5")
|
||||
include(":bukkit:paper_1_21_2")
|
||||
include(":bukkit:paper_1_21_4")
|
||||
include(":bukkit:v1_8_R1")
|
||||
include(":bukkit:v1_8_R2")
|
||||
include(":bukkit:v1_8_R3")
|
||||
include(":bukkit:v1_9_R1")
|
||||
include(":bukkit:v1_9_R2")
|
||||
include(":bukkit:v1_10_R1")
|
||||
include(":bukkit:v1_11_R1")
|
||||
include(":bukkit:v1_12_R1")
|
||||
|
||||
Reference in new issue
Block a user