mirror of
https://github.com/NEZNAMY/TAB.git
synced 2026-11-13 12:42:06 -05:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
363e58bf91 | ||
|
|
daca3f5b71 | ||
|
|
a7d2ec084b | ||
|
|
167f8cd0ff | ||
|
|
170307f45f | ||
|
|
2f36377780 |
No files matched your search
@@ -94,4 +94,6 @@ public interface FeatureManager {
|
||||
void markObjective();
|
||||
|
||||
void markDisplayObjective();
|
||||
|
||||
TabFeature[] getValues();
|
||||
}
|
||||
@@ -310,4 +310,12 @@ public interface TabPlayer {
|
||||
* @return player's game profile name as seen by other players
|
||||
*/
|
||||
String getNickname();
|
||||
|
||||
/**
|
||||
* Returns player's chat message signing key. Returned object is a direct
|
||||
* object from the server, which is different per platform.
|
||||
*
|
||||
* @return Player's direct chat message signing key
|
||||
*/
|
||||
Object getProfilePublicKey();
|
||||
}
|
||||
@@ -15,7 +15,7 @@ public class MiniMessageFormat implements RGBFormatter {
|
||||
String format = text.replace(EnumChatFormat.RESET.getFormat(), ""); //remove &r from header/footer newline inserts
|
||||
try {
|
||||
return LegacyComponentSerializer.legacySection().serialize(MiniMessage.miniMessage().deserialize(format));
|
||||
} catch (Exception ignored) {
|
||||
} catch (Throwable ignored) {
|
||||
return text;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,6 +97,9 @@ public class PacketPlayOutPlayerInfo implements TabPacket {
|
||||
/** Player's skin, null for empty skin */
|
||||
private Skin skin;
|
||||
|
||||
/** Player's chat signing key */
|
||||
private Object profilePublicKey;
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameters. Suitable for
|
||||
* {@link EnumPlayerInfoAction}.ADD_PLAYER action
|
||||
@@ -113,8 +116,10 @@ public class PacketPlayOutPlayerInfo implements TabPacket {
|
||||
* Player's GameMode
|
||||
* @param displayName
|
||||
* Player's display name
|
||||
* @param profilePublicKey
|
||||
* Player's chat signing key
|
||||
*/
|
||||
public PlayerInfoData(String name, UUID uniqueId, Skin skin, int latency, EnumGamemode gameMode, IChatBaseComponent displayName) {
|
||||
public PlayerInfoData(String name, UUID uniqueId, Skin skin, int latency, EnumGamemode gameMode, IChatBaseComponent displayName, Object profilePublicKey) {
|
||||
Preconditions.checkNotNull(uniqueId, "uuid");
|
||||
this.name = name;
|
||||
this.uniqueId = uniqueId;
|
||||
@@ -122,6 +127,7 @@ public class PacketPlayOutPlayerInfo implements TabPacket {
|
||||
this.latency = latency;
|
||||
this.gameMode = gameMode;
|
||||
this.displayName = displayName;
|
||||
this.profilePublicKey = profilePublicKey;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -182,8 +188,8 @@ public class PacketPlayOutPlayerInfo implements TabPacket {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("PlayerInfoData{latency=%s,gameMode=%s,displayName=%s,name=%s,uniqueId=%s,skin=%s}",
|
||||
latency, gameMode, displayName, name, uniqueId, skin);
|
||||
return String.format("PlayerInfoData{latency=%s,gameMode=%s,displayName=%s,name=%s,uniqueId=%s,skin=%s,profilePublicKey=%s}",
|
||||
latency, gameMode, displayName, name, uniqueId, skin, profilePublicKey);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -299,6 +305,25 @@ public class PacketPlayOutPlayerInfo implements TabPacket {
|
||||
public void setSkin(Skin skin) {
|
||||
this.skin = skin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #profilePublicKey}
|
||||
*
|
||||
* @return profilePublicKey
|
||||
*/
|
||||
public Object getProfilePublicKey() {
|
||||
return profilePublicKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets {@link #profilePublicKey} to specified value
|
||||
*
|
||||
* @param profilePublicKey
|
||||
* profilePublicKey to use
|
||||
*/
|
||||
public void setProfilePublicKey(Object profilePublicKey) {
|
||||
this.profilePublicKey = profilePublicKey;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -102,7 +102,7 @@ public class BukkitPacketBuilder extends PacketBuilder {
|
||||
parameters.add(data.getLatency());
|
||||
parameters.add(data.getGameMode() == null ? null : nms.EnumGamemode_values[nms.EnumGamemode_values.length-EnumGamemode.VALUES.length+data.getGameMode().ordinal()]); //not_set was removed in 1.17
|
||||
parameters.add(data.getDisplayName() == null ? null : toNMSComponent(data.getDisplayName(), clientVersion));
|
||||
if (nms.getMinorVersion() >= 19) parameters.add(null);
|
||||
if (nms.getMinorVersion() >= 19) parameters.add(data.getProfilePublicKey());
|
||||
items.add(nms.newPlayerInfoData.newInstance(parameters.toArray()));
|
||||
}
|
||||
nms.setField(nmsPacket, nms.PacketPlayOutPlayerInfo_PLAYERS, items);
|
||||
@@ -364,8 +364,9 @@ public class BukkitPacketBuilder extends PacketBuilder {
|
||||
Property pr = profile.getProperties().get("textures").iterator().next();
|
||||
skin = new Skin(pr.getValue(), pr.getSignature());
|
||||
}
|
||||
Object profilePublicKey = nms.getMinorVersion() >= 19 ? nms.PlayerInfoData_getProfilePublicKeyRecord.invoke(nmsData) : null;
|
||||
listData.add(new PlayerInfoData(profile.getName(), profile.getId(), skin,
|
||||
(int) nms.PlayerInfoData_getLatency.invoke(nmsData), gameMode, listName));
|
||||
(int) nms.PlayerInfoData_getLatency.invoke(nmsData), gameMode, listName, profilePublicKey));
|
||||
}
|
||||
return new PacketPlayOutPlayerInfo(action, listData);
|
||||
}
|
||||
|
||||
@@ -289,6 +289,19 @@ public class BukkitTabPlayer extends ITabPlayer {
|
||||
return getPlayer().getGameMode().getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getProfilePublicKey() {
|
||||
if (NMSStorage.getInstance().getMinorVersion() < 19) return null;
|
||||
try {
|
||||
Object key = NMSStorage.getInstance().EntityHuman_ProfilePublicKey.get(handle);
|
||||
if (key == null) return null;
|
||||
return NMSStorage.getInstance().ProfilePublicKey_getRecord.invoke(key);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
TAB.getInstance().getErrorManager().printError("Failed to get profile key of " + getName(), e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Channel getChannel() {
|
||||
try {
|
||||
|
||||
@@ -75,6 +75,8 @@ public class BukkitModernNMSStorage extends ModernNMSStorage {
|
||||
if (minorVersion >= 19) {
|
||||
Registry = Class.forName("net.minecraft.core.Registry");
|
||||
IRegistry = Class.forName("net.minecraft.core.IRegistry");
|
||||
ProfilePublicKey = Class.forName("net.minecraft.world.entity.player.ProfilePublicKey");
|
||||
ProfilePublicKey$a = Class.forName("net.minecraft.world.entity.player.ProfilePublicKey$a");
|
||||
}
|
||||
|
||||
// Player Info
|
||||
|
||||
@@ -75,6 +75,8 @@ public class MojangModernNMSStorage extends ModernNMSStorage {
|
||||
if (minorVersion >= 19) {
|
||||
Registry = Class.forName("net.minecraft.core.IdMap");
|
||||
IRegistry = Class.forName("net.minecraft.core.Registry");
|
||||
ProfilePublicKey = Class.forName("net.minecraft.world.entity.player.ProfilePublicKey");
|
||||
ProfilePublicKey$a = Class.forName("net.minecraft.world.entity.player.ProfilePublicKey$Data");
|
||||
}
|
||||
|
||||
// Player Info
|
||||
|
||||
@@ -39,6 +39,7 @@ public abstract class NMSStorage {
|
||||
public Field PLAYER_CONNECTION;
|
||||
public Field NETWORK_MANAGER;
|
||||
public Field CHANNEL;
|
||||
public Field EntityHuman_ProfilePublicKey;
|
||||
public Method getHandle;
|
||||
public Method sendPacket;
|
||||
public Method getProfile;
|
||||
@@ -129,6 +130,8 @@ public abstract class NMSStorage {
|
||||
protected Class<?> EnumPlayerInfoAction;
|
||||
protected Class<?> PlayerInfoData;
|
||||
protected Class<?> EnumGamemode;
|
||||
protected Class<?> ProfilePublicKey;
|
||||
protected Class<?> ProfilePublicKey$a;
|
||||
public Constructor<?> newPacketPlayOutPlayerInfo;
|
||||
public Constructor<?> newPlayerInfoData;
|
||||
public Field PacketPlayOutPlayerInfo_ACTION;
|
||||
@@ -137,6 +140,8 @@ public abstract class NMSStorage {
|
||||
public Method PlayerInfoData_getLatency;
|
||||
public Method PlayerInfoData_getGamemode;
|
||||
public Method PlayerInfoData_getDisplayName;
|
||||
public Method PlayerInfoData_getProfilePublicKeyRecord;
|
||||
public Method ProfilePublicKey_getRecord;
|
||||
public Enum[] EnumPlayerInfoAction_values;
|
||||
public Enum[] EnumGamemode_values;
|
||||
|
||||
@@ -282,6 +287,11 @@ public abstract class NMSStorage {
|
||||
PlayerInfoData_getDisplayName = getMethods(PlayerInfoData, IChatBaseComponent).get(0);
|
||||
EnumPlayerInfoAction_values = getEnumValues(EnumPlayerInfoAction);
|
||||
EnumGamemode_values = getEnumValues(EnumGamemode);
|
||||
if (minorVersion >= 19) {
|
||||
EntityHuman_ProfilePublicKey = getFields(EntityHuman, ProfilePublicKey).get(0);
|
||||
ProfilePublicKey_getRecord = getMethods(ProfilePublicKey, ProfilePublicKey$a).get(0);
|
||||
PlayerInfoData_getProfilePublicKeyRecord = getMethods(PlayerInfoData, ProfilePublicKey$a).get(0);
|
||||
}
|
||||
}
|
||||
|
||||
protected void spawnEntityLiving() throws ReflectiveOperationException {
|
||||
|
||||
+3
-1
@@ -10,6 +10,7 @@ import me.neznamy.tab.api.protocol.*;
|
||||
import me.neznamy.tab.api.protocol.PacketPlayOutPlayerInfo.EnumGamemode;
|
||||
import me.neznamy.tab.api.protocol.PacketPlayOutPlayerInfo.EnumPlayerInfoAction;
|
||||
import me.neznamy.tab.api.protocol.PacketPlayOutPlayerInfo.PlayerInfoData;
|
||||
import net.md_5.bungee.protocol.PlayerPublicKey;
|
||||
import net.md_5.bungee.protocol.Property;
|
||||
import net.md_5.bungee.protocol.packet.*;
|
||||
import net.md_5.bungee.protocol.packet.PlayerListItem.Item;
|
||||
@@ -64,6 +65,7 @@ public class BungeePacketBuilder extends PacketBuilder {
|
||||
}
|
||||
item.setUsername(data.getName());
|
||||
item.setUuid(data.getUniqueId());
|
||||
item.setPublicKey((PlayerPublicKey) data.getProfilePublicKey());
|
||||
items.add(item);
|
||||
}
|
||||
PlayerListItem bungeePacket = new PlayerListItem();
|
||||
@@ -108,7 +110,7 @@ public class BungeePacketBuilder extends PacketBuilder {
|
||||
List<PlayerInfoData> listData = new ArrayList<>();
|
||||
for (Item i : item.getItems()) {
|
||||
Skin skin = i.getProperties() == null || i.getProperties().length == 0 ? null : new Skin(i.getProperties()[0].getValue(), i.getProperties()[0].getSignature());
|
||||
listData.add(new PlayerInfoData(i.getUsername(), i.getUuid(), skin, i.getPing(), EnumGamemode.VALUES[i.getGamemode()+1], IChatBaseComponent.deserialize(i.getDisplayName())));
|
||||
listData.add(new PlayerInfoData(i.getUsername(), i.getUuid(), skin, i.getPing(), EnumGamemode.VALUES[i.getGamemode()+1], IChatBaseComponent.deserialize(i.getDisplayName()), i.getPublicKey()));
|
||||
}
|
||||
return new PacketPlayOutPlayerInfo(EnumPlayerInfoAction.valueOf(item.getAction().toString().replace("GAMEMODE", "GAME_MODE")), listData);
|
||||
}
|
||||
|
||||
@@ -8,11 +8,11 @@ import me.neznamy.tab.api.protocol.Skin;
|
||||
import me.neznamy.tab.api.util.Preconditions;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import me.neznamy.tab.shared.proxy.ProxyTabPlayer;
|
||||
import net.md_5.bungee.api.ProxyConfig;
|
||||
import net.md_5.bungee.api.ProxyServer;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
import net.md_5.bungee.protocol.DefinedPacket;
|
||||
import net.md_5.bungee.protocol.Protocol;
|
||||
import net.md_5.bungee.protocol.packet.LoginRequest;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
@@ -25,6 +25,7 @@ public class BungeeTabPlayer extends ProxyTabPlayer {
|
||||
|
||||
/** Inaccessible bungee internals */
|
||||
private static Method InitialHandler_getLoginProfile;
|
||||
private static Method InitialHandler_getLoginRequest;
|
||||
private static Method ChannelWrapper_getHandle;
|
||||
private static Method LoginResult_Property_getValue;
|
||||
private static Method LoginResult_Property_getSignature;
|
||||
@@ -34,13 +35,11 @@ public class BungeeTabPlayer extends ProxyTabPlayer {
|
||||
private static Object directionData;
|
||||
private static Method getId;
|
||||
|
||||
/** Rewriting offline tablist uuids coming from backend servers to online uuids on online servers */
|
||||
private static boolean tablistRewriteEnabled = true;
|
||||
|
||||
static {
|
||||
try {
|
||||
Class<?> initialHandler = Class.forName("net.md_5.bungee.connection.InitialHandler");
|
||||
InitialHandler_getLoginProfile = initialHandler.getMethod("getLoginProfile");
|
||||
InitialHandler_getLoginRequest = initialHandler.getMethod("getLoginRequest");
|
||||
Class<?> channelWrapper = Class.forName("net.md_5.bungee.netty.ChannelWrapper");
|
||||
ChannelWrapper_getHandle = channelWrapper.getMethod("getHandle");
|
||||
Class<?> loginResult = Class.forName("net.md_5.bungee.connection.LoginResult");
|
||||
@@ -60,11 +59,6 @@ public class BungeeTabPlayer extends ProxyTabPlayer {
|
||||
} catch (ReflectiveOperationException e) {
|
||||
TAB.getInstance().getErrorManager().criticalError("Failed to initialize bungee internal fields", e);
|
||||
}
|
||||
try {
|
||||
tablistRewriteEnabled = !(boolean) ProxyConfig.class.getMethod("isDisableTabListRewrite").invoke(ProxyServer.getInstance().getConfig());
|
||||
} catch (ReflectiveOperationException e) {
|
||||
// not Waterfall, always true on BungeeCord
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -74,7 +68,7 @@ public class BungeeTabPlayer extends ProxyTabPlayer {
|
||||
* BungeeCord player
|
||||
*/
|
||||
public BungeeTabPlayer(ProxiedPlayer p) {
|
||||
super(p, p.getUniqueId(), p.getName(), p.getServer() != null ? p.getServer().getInfo().getName() : "-", -1, tablistRewriteEnabled);
|
||||
super(p, p.getUniqueId(), p.getName(), p.getServer() != null ? p.getServer().getInfo().getName() : "-", -1, true);
|
||||
try {
|
||||
channel = (Channel) ChannelWrapper_getHandle.invoke(wrapperField.get(getPlayer().getPendingConnection()));
|
||||
} catch (IllegalAccessException | InvocationTargetException e) {
|
||||
@@ -171,6 +165,16 @@ public class BungeeTabPlayer extends ProxyTabPlayer {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getProfilePublicKey() {
|
||||
try {
|
||||
return ((LoginRequest) InitialHandler_getLoginRequest.invoke(getPlayer().getPendingConnection())).getPublicKey();
|
||||
} catch (ReflectiveOperationException e) {
|
||||
TAB.getInstance().getErrorManager().printError("Failed to get profile key of " + getPlayer().getName(), e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendPluginMessage(byte[] message) {
|
||||
Preconditions.checkNotNull(message, "message");
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
# Project properties - we put these here so they can be modified without causing a recompile of the build scripts
|
||||
projectVersion=3.1.3
|
||||
projectVersion=3.1.4
|
||||
|
||||
# Gradle properties
|
||||
org.gradle.daemon=true
|
||||
|
||||
@@ -22,7 +22,7 @@ event = "3.0.0"
|
||||
bukkit = "1.16.5-R0.1-SNAPSHOT"
|
||||
bungeecord = "1.19-R0.1-SNAPSHOT"
|
||||
krypton = "0.59.2"
|
||||
velocity = "3.0.1"
|
||||
velocity = "3.1.2-SNAPSHOT"
|
||||
|
||||
# Platform extras
|
||||
placeholderapi = "2.11.1"
|
||||
|
||||
@@ -327,4 +327,9 @@ public class FeatureManagerImpl implements FeatureManager {
|
||||
public TabFeature getFeature(String name) {
|
||||
return features.get(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TabFeature[] getValues() {
|
||||
return values;
|
||||
}
|
||||
}
|
||||
@@ -415,4 +415,8 @@ public abstract class ITabPlayer implements TabPlayer {
|
||||
public void setNickname(String nickname) {
|
||||
this.nickname = nickname;
|
||||
}
|
||||
|
||||
public void setChannel(Channel channel) {
|
||||
this.channel = channel;
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -126,7 +126,7 @@ public class GlobalPlayerList extends TabFeature {
|
||||
format = playerlist.getTabFormat(p, viewer);
|
||||
}
|
||||
return new PacketPlayOutPlayerInfo(EnumPlayerInfoAction.ADD_PLAYER, new PlayerInfoData(p.getName(), p.getTablistUUID(), p.getSkin(),
|
||||
p.getPing(), vanishedAsSpectators && p.isVanished() ? EnumGamemode.SPECTATOR : EnumGamemode.CREATIVE, format));
|
||||
p.getPing(), vanishedAsSpectators && p.isVanished() ? EnumGamemode.SPECTATOR : EnumGamemode.CREATIVE, format, p.getProfilePublicKey()));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -41,10 +41,10 @@ public class Layout extends TabFeature {
|
||||
groups.forEach(g -> list.addAll(g.getSlots(p)));
|
||||
for (FixedSlot slot : fixedSlots.values()) {
|
||||
p.setProperty(slot, slot.getPropertyName(), slot.getText());
|
||||
list.add(new PlayerInfoData("", slot.getId(), slot.getSkin(), slot.getPing(), EnumGamemode.CREATIVE, IChatBaseComponent.optimizedComponent(p.getProperty(slot.getPropertyName()).updateAndGet())));
|
||||
list.add(new PlayerInfoData("", slot.getId(), slot.getSkin(), slot.getPing(), EnumGamemode.CREATIVE, IChatBaseComponent.optimizedComponent(p.getProperty(slot.getPropertyName()).updateAndGet()), null));
|
||||
}
|
||||
for (int slot : emptySlots) {
|
||||
list.add(new PlayerInfoData("", manager.getUUID(slot), manager.getSkinManager().getDefaultSkin(), manager.getEmptySlotPing(), EnumGamemode.CREATIVE, new IChatBaseComponent("")));
|
||||
list.add(new PlayerInfoData("", manager.getUUID(slot), manager.getSkinManager().getDefaultSkin(), manager.getEmptySlotPing(), EnumGamemode.CREATIVE, new IChatBaseComponent(""), null));
|
||||
}
|
||||
if (p.getVersion().getMinorVersion() < 8 || p.isBedrockPlayer()) return;
|
||||
p.sendCustomPacket(new PacketPlayOutPlayerInfo(EnumPlayerInfoAction.ADD_PLAYER, list), this);
|
||||
|
||||
@@ -45,9 +45,9 @@ public class PlayerSlot {
|
||||
PlayerInfoData data;
|
||||
TabPlayer player = this.player; //avoiding NPE from concurrent access
|
||||
if (player != null) {
|
||||
data = new PlayerInfoData("", id, player.getSkin(), player.getPing(), EnumGamemode.SURVIVAL, playerlist == null ? new IChatBaseComponent(player.getName()) : playerlist.getTabFormat(player, p));
|
||||
data = new PlayerInfoData("", id, player.getSkin(), player.getPing(), EnumGamemode.SURVIVAL, playerlist == null ? new IChatBaseComponent(player.getName()) : playerlist.getTabFormat(player, p), null);
|
||||
} else {
|
||||
data = new PlayerInfoData("", id, layout.getManager().getSkinManager().getDefaultSkin(), layout.getManager().getEmptySlotPing(), EnumGamemode.SURVIVAL, new IChatBaseComponent(text));
|
||||
data = new PlayerInfoData("", id, layout.getManager().getSkinManager().getDefaultSkin(), layout.getManager().getEmptySlotPing(), EnumGamemode.SURVIVAL, new IChatBaseComponent(text), null);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
@@ -122,7 +122,7 @@ public class RedisPlayer {
|
||||
|
||||
public PacketPlayOutPlayerInfo getAddPacket() {
|
||||
return new PacketPlayOutPlayerInfo(EnumPlayerInfoAction.ADD_PLAYER, new PlayerInfoData(nickname, uniqueId, skin, 0, EnumGamemode.SURVIVAL,
|
||||
disabledPlayerList ? null : IChatBaseComponent.optimizedComponent(tabFormat)));
|
||||
disabledPlayerList ? null : IChatBaseComponent.optimizedComponent(tabFormat), null));
|
||||
}
|
||||
|
||||
public PacketPlayOutPlayerInfo getUpdatePacket() {
|
||||
|
||||
@@ -53,7 +53,6 @@ public class PlayerPlaceholderImpl extends TabPlaceholder implements PlayerPlace
|
||||
if (output == null) return false; //bridge placeholders, they are updated using updateValue method
|
||||
String obj = getReplacements().findReplacement(String.valueOf(output));
|
||||
String newValue = obj == null ? identifier : setPlaceholders(obj, p);
|
||||
newValue = getReplacements().findReplacement(newValue);
|
||||
|
||||
//make invalid placeholders return identifier instead of nothing
|
||||
if (identifier.equals(newValue) && !lastValues.containsKey(p)) {
|
||||
|
||||
@@ -44,7 +44,6 @@ public class ServerPlaceholderImpl extends TabPlaceholder implements ServerPlace
|
||||
public boolean update() {
|
||||
String obj = getReplacements().findReplacement(String.valueOf(request()));
|
||||
String newValue = obj == null ? identifier : setPlaceholders(obj, null);
|
||||
newValue = getReplacements().findReplacement(newValue);
|
||||
|
||||
//make invalid placeholders return identifier instead of nothing
|
||||
if (identifier.equals(newValue) && lastValue == null) {
|
||||
|
||||
@@ -326,6 +326,16 @@ public class VelocityTabPlayer extends ProxyTabPlayer {
|
||||
return 0; //shrug
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getProfilePublicKey() {
|
||||
try {
|
||||
return getPlayer().getIdentifiedKey();
|
||||
} catch (NoSuchMethodError e) {
|
||||
//3.1.1 or lower
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendPluginMessage(byte[] message) {
|
||||
Preconditions.checkNotNull(message, "message");
|
||||
|
||||
Reference in new issue
Block a user