diff --git a/src/main/java/com/comphenix/protocol/wrappers/Converters.java b/src/main/java/com/comphenix/protocol/wrappers/Converters.java index f96a6235..43a0edf1 100644 --- a/src/main/java/com/comphenix/protocol/wrappers/Converters.java +++ b/src/main/java/com/comphenix/protocol/wrappers/Converters.java @@ -17,6 +17,7 @@ package com.comphenix.protocol.wrappers; import java.lang.reflect.Array; import java.lang.reflect.Method; import java.lang.reflect.Modifier; +import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Optional; @@ -310,4 +311,22 @@ public class Converters { } }; } + + public static List toList(Iterable iterable) { + if (iterable instanceof List) { + return (List) iterable; + } + + List result = new ArrayList<>(); + if (iterable instanceof Collection) { + Collection coll = (Collection) iterable; + result.addAll(coll); + } else { + for (T elem : iterable) { + result.add(elem); + } + } + + return result; + } } diff --git a/src/main/java/com/comphenix/protocol/wrappers/WrappedServerPing.java b/src/main/java/com/comphenix/protocol/wrappers/WrappedServerPing.java index 1b5d0e30..c2a12d72 100644 --- a/src/main/java/com/comphenix/protocol/wrappers/WrappedServerPing.java +++ b/src/main/java/com/comphenix/protocol/wrappers/WrappedServerPing.java @@ -39,16 +39,9 @@ import java.util.List; public class WrappedServerPing implements ClonableWrapper { private static final Class GAME_PROFILE = MinecraftReflection.getGameProfileClass(); - // For converting to the underlying array - private static final EquivalentConverter> PROFILE_CONVERT = - BukkitConverters.getArrayConverter(GAME_PROFILE, BukkitConverters.getWrappedGameProfileConverter()); - // Get profile from player private static final FieldAccessor ENTITY_HUMAN_PROFILE = Accessors.getFieldAccessor( - MinecraftReflection.getEntityPlayerClass().getSuperclass(), GAME_PROFILE, true); - - // Server ping fields - private static final Class SERVER_PING = MinecraftReflection.getServerPingClass(); + MinecraftReflection.getEntityPlayerClass().getSuperclass(), GAME_PROFILE, true); private final ServerPingImpl impl; @@ -59,9 +52,6 @@ public class WrappedServerPing implements ClonableWrapper { */ public WrappedServerPing() { this.impl = newImpl(); - - resetPlayers(); - resetVersion(); } private WrappedServerPing(Object handle) { @@ -119,10 +109,11 @@ public class WrappedServerPing implements ClonableWrapper { /** * Retrieve the message of the day. - * @return The messge of the day. + * @return The message of the day. */ public WrappedChatComponent getMotD() { - return WrappedChatComponent.fromHandle(impl.getMotD()); + Object handle = impl.getMotD(); + return handle != null ? WrappedChatComponent.fromHandle(handle) : null; } /** @@ -130,7 +121,7 @@ public class WrappedServerPing implements ClonableWrapper { * @param description - message of the day. */ public void setMotD(WrappedChatComponent description) { - impl.setMotD(description.getHandle()); + impl.setMotD(description != null ? description.getHandle() : null); } /** @@ -267,12 +258,7 @@ public class WrappedServerPing implements ClonableWrapper { * @return Logged in players or an empty list if no player names will be displayed. */ public ImmutableList getPlayers() { - if (!isPlayersVisible()) - return ImmutableList.of(); - Object playerProfiles = impl.getPlayers(); - if (playerProfiles == null) - return ImmutableList.of(); - return ImmutableList.copyOf(PROFILE_CONVERT.getSpecific(playerProfiles)); + return impl.getPlayers(); } /** @@ -282,7 +268,7 @@ public class WrappedServerPing implements ClonableWrapper { public void setPlayers(Iterable profile) { if (!isPlayersVisible()) resetPlayers(); - impl.setPlayers((profile != null) ? PROFILE_CONVERT.getGeneric(profile) : null); + impl.setPlayers(profile); } /** diff --git a/src/main/java/com/comphenix/protocol/wrappers/ping/LegacyServerPing.java b/src/main/java/com/comphenix/protocol/wrappers/ping/LegacyServerPing.java index 85e28c5f..2b9d0256 100644 --- a/src/main/java/com/comphenix/protocol/wrappers/ping/LegacyServerPing.java +++ b/src/main/java/com/comphenix/protocol/wrappers/ping/LegacyServerPing.java @@ -14,6 +14,8 @@ import com.comphenix.protocol.wrappers.AbstractWrapper; import com.comphenix.protocol.wrappers.BukkitConverters; import com.comphenix.protocol.wrappers.WrappedChatComponent; import com.comphenix.protocol.wrappers.WrappedGameProfile; +import com.comphenix.protocol.wrappers.WrappedServerPing; + import com.google.common.collect.ImmutableList; import org.bukkit.Bukkit; @@ -27,7 +29,7 @@ import java.util.List; * Represents a server ping packet data. * @author Kristian */ -public class LegacyServerPing extends AbstractWrapper implements ServerPingImpl { +public final class LegacyServerPing extends AbstractWrapper implements ServerPingImpl { private static final Class GAME_PROFILE = MinecraftReflection.getGameProfileClass(); // For converting to the underlying array @@ -132,32 +134,27 @@ public class LegacyServerPing extends AbstractWrapper implements ServerPingImpl /** * Retrieve the message of the day. - * @return The messge of the day. + * @return The message of the day. */ - public WrappedChatComponent getMotD() { - return WrappedChatComponent.fromHandle(DESCRIPTION.get(handle)); + @Override + public Object getMotD() { + return DESCRIPTION.get(handle); } /** * Set the message of the day. * @param description - message of the day. */ + @Override public void setMotD(Object description) { DESCRIPTION.set(handle, description); } - /** - * Set the message of the day. - * @param message - the message. - */ - public void setMotD(String message) { - setMotD(WrappedChatComponent.fromLegacyText(message)); - } - /** * Retrieve the compressed PNG file that is being displayed as a favicon. * @return The favicon, or NULL if no favicon will be displayed. */ + @Override public String getFavicon() { return (String) FAVICON.get(handle); } @@ -166,6 +163,7 @@ public class LegacyServerPing extends AbstractWrapper implements ServerPingImpl * Set the compressed PNG file that is being displayed. * @param image - the new compressed image or NULL if no favicon should be displayed. */ + @Override public void setFavicon(String image) { FAVICON.set(handle, image); } @@ -197,6 +195,7 @@ public class LegacyServerPing extends AbstractWrapper implements ServerPingImpl * @return whether the server enforces secure chat. * @since 1.19.1 */ + @Override public boolean isEnforceSecureChat() { int index = MinecraftVersion.FEATURE_PREVIEW_UPDATE.atOrAbove() ? 0 : 1; return (Boolean) BOOLEAN_ACCESSORS[index].get(handle); @@ -207,6 +206,7 @@ public class LegacyServerPing extends AbstractWrapper implements ServerPingImpl * @param enforceSecureChat true if enabled, false otherwise. * @since 1.19.1 */ + @Override public void setEnforceSecureChat(boolean enforceSecureChat) { int index = MinecraftVersion.FEATURE_PREVIEW_UPDATE.atOrAbove() ? 0 : 1; BOOLEAN_ACCESSORS[index].set(handle, enforceSecureChat); @@ -218,6 +218,7 @@ public class LegacyServerPing extends AbstractWrapper implements ServerPingImpl * @throws IllegalStateException If the player count has been hidden via {@link #setPlayersVisible(boolean)}. * @see #setPlayersOnline(int) */ + @Override public int getPlayersOnline() { if (players == null) throw new IllegalStateException("The player count has been hidden."); @@ -231,6 +232,7 @@ public class LegacyServerPing extends AbstractWrapper implements ServerPingImpl * negative, as well as higher than the player maximum. * @param online - online players. */ + @Override public void setPlayersOnline(int online) { if (players == null) resetPlayers(); @@ -243,6 +245,7 @@ public class LegacyServerPing extends AbstractWrapper implements ServerPingImpl * @throws IllegalStateException If the player maximum has been hidden via {@link #setPlayersVisible(boolean)}. * @see #setPlayersMaximum(int) */ + @Override public int getPlayersMaximum() { if (players == null) throw new IllegalStateException("The player maximum has been hidden."); @@ -256,6 +259,7 @@ public class LegacyServerPing extends AbstractWrapper implements ServerPingImpl * is less than the player count. * @param maximum - maximum player count. */ + @Override public void setPlayersMaximum(int maximum) { if (players == null) resetPlayers(); @@ -268,6 +272,7 @@ public class LegacyServerPing extends AbstractWrapper implements ServerPingImpl * Note that this may set the current player count and maximum to their respective real values. * @param visible - TRUE if it should be visible, FALSE otherwise. */ + @Override public void setPlayersVisible(boolean visible) { if (arePlayersVisible() != visible) { if (visible) { @@ -287,6 +292,7 @@ public class LegacyServerPing extends AbstractWrapper implements ServerPingImpl * If not, the client will display ??? in the same location. * @return TRUE if the player statistics is visible, FALSE otherwise. */ + @Override public boolean arePlayersVisible() { return players != null; } @@ -295,6 +301,7 @@ public class LegacyServerPing extends AbstractWrapper implements ServerPingImpl * Retrieve a copy of all the logged in players. * @return Logged in players or an empty list if no player names will be displayed. */ + @Override public ImmutableList getPlayers() { if (players == null) return ImmutableList.of(); @@ -306,33 +313,21 @@ public class LegacyServerPing extends AbstractWrapper implements ServerPingImpl /** * Set the displayed list of logged in players. - * @param profile - every logged in player. + * @param playerSample - every logged in player. */ - public void setPlayers(Object profile) { + @Override + public void setPlayers(Iterable playerSample) { if (players == null) resetPlayers(); - PLAYERS_PROFILES.set(players, profile); - } - /** - * Set the displayed lst of logged in players. - * @param players - the players to display. - */ - public void setBukkitPlayers(Iterable players) { - final List profiles = new ArrayList<>(); - - for (Player player : players) { - Object profile = ENTITY_HUMAN_PROFILE.get(BukkitUnwrapper.getInstance().unwrapItem(player)); - profiles.add(WrappedGameProfile.fromHandle(profile)); - } - - setPlayers(profiles); + PLAYERS_PROFILES.set(players, PROFILE_CONVERT.getGeneric(playerSample)); } /** * Retrieve the version name of the current server. * @return The version name. */ + @Override public String getVersionName() { return (String) VERSION_NAME.get(version); } @@ -341,6 +336,7 @@ public class LegacyServerPing extends AbstractWrapper implements ServerPingImpl * Set the version name of the current server. * @param name - the new version name. */ + @Override public void setVersionName(String name) { VERSION_NAME.set(version, name); } @@ -349,6 +345,7 @@ public class LegacyServerPing extends AbstractWrapper implements ServerPingImpl * Retrieve the protocol number. * @return The protocol. */ + @Override public int getVersionProtocol() { return (Integer) VERSION_PROTOCOL.get(version); } @@ -357,6 +354,7 @@ public class LegacyServerPing extends AbstractWrapper implements ServerPingImpl * Set the version protocol * @param protocol - the protocol number. */ + @Override public void setVersionProtocol(int protocol) { VERSION_PROTOCOL.set(version, protocol); } @@ -367,11 +365,11 @@ public class LegacyServerPing extends AbstractWrapper implements ServerPingImpl */ public LegacyServerPing deepClone() { LegacyServerPing copy = new LegacyServerPing(); - WrappedChatComponent motd = getMotD(); + Object motd = getMotD(); copy.setPlayers(getPlayers()); copy.setFavicon(getFavicon()); - copy.setMotD(motd != null ? motd.deepClone() : null); + copy.setMotD(motd != null ? WrappedChatComponent.fromHandle(motd).getHandle() : null); copy.setVersionName(getVersionName()); copy.setVersionProtocol(getVersionProtocol()); diff --git a/src/main/java/com/comphenix/protocol/wrappers/ping/ServerPingImpl.java b/src/main/java/com/comphenix/protocol/wrappers/ping/ServerPingImpl.java index 7606c3f3..fc940587 100644 --- a/src/main/java/com/comphenix/protocol/wrappers/ping/ServerPingImpl.java +++ b/src/main/java/com/comphenix/protocol/wrappers/ping/ServerPingImpl.java @@ -1,20 +1,20 @@ package com.comphenix.protocol.wrappers.ping; -import java.util.List; +import java.util.Optional; -import com.comphenix.protocol.wrappers.WrappedChatComponent; import com.comphenix.protocol.wrappers.WrappedGameProfile; -import com.comphenix.protocol.wrappers.WrappedServerPing.CompressedImage; -public interface ServerPingImpl { +import com.google.common.collect.ImmutableList; + +public interface ServerPingImpl extends Cloneable { Object getMotD(); void setMotD(Object description); int getPlayersMaximum(); void setPlayersMaximum(int maxPlayers); int getPlayersOnline(); void setPlayersOnline(int onlineCount); - Object getPlayers(); - void setPlayers(Object playerSample); + ImmutableList getPlayers(); + void setPlayers(Iterable playerSample); String getVersionName(); void setVersionName(String versionName); int getVersionProtocol(); diff --git a/src/main/java/com/comphenix/protocol/wrappers/ping/ServerPingRecord.java b/src/main/java/com/comphenix/protocol/wrappers/ping/ServerPingRecord.java index f997fc24..c0e91708 100644 --- a/src/main/java/com/comphenix/protocol/wrappers/ping/ServerPingRecord.java +++ b/src/main/java/com/comphenix/protocol/wrappers/ping/ServerPingRecord.java @@ -1,12 +1,11 @@ package com.comphenix.protocol.wrappers.ping; import java.nio.charset.StandardCharsets; -import java.util.Base64; import java.util.List; import java.util.Optional; -import com.comphenix.protocol.events.AbstractStructure; import com.comphenix.protocol.events.InternalStructure; +import com.comphenix.protocol.reflect.EquivalentConverter; import com.comphenix.protocol.reflect.StructureModifier; import com.comphenix.protocol.reflect.accessors.Accessors; import com.comphenix.protocol.reflect.accessors.ConstructorAccessor; @@ -15,9 +14,10 @@ import com.comphenix.protocol.utility.MinecraftReflection; import com.comphenix.protocol.utility.MinecraftVersion; import com.comphenix.protocol.wrappers.*; +import com.google.common.collect.ImmutableList; import org.bukkit.Bukkit; -public class ServerPingRecord implements ServerPingImpl { +public final class ServerPingRecord implements ServerPingImpl { private static Class SERVER_PING; private static Class PLAYER_SAMPLE_CLASS; private static Class SERVER_DATA_CLASS; @@ -26,6 +26,8 @@ public class ServerPingRecord implements ServerPingImpl { private static ConstructorAccessor PING_CTOR; + private static EquivalentConverter> PROFILE_LIST_CONVERTER; + private static boolean initialized = false; private static void initialize() { @@ -46,6 +48,8 @@ public class ServerPingRecord implements ServerPingImpl { SAMPLE_WRAPPER = AutoWrapper.wrap(PlayerSample.class, PLAYER_SAMPLE_CLASS); FAVICON_WRAPPER = AutoWrapper.wrap(Favicon.class, MinecraftReflection.getMinecraftClass("network.protocol.status.ServerPing$a")); + PROFILE_LIST_CONVERTER = BukkitConverters.getListConverter(BukkitConverters.getWrappedGameProfileConverter()); + DEFAULT_DESCRIPTION = WrappedChatComponent.fromLegacyText("A Minecraft Server"); } catch (Exception ex) { ex.printStackTrace(); // TODO @@ -56,15 +60,44 @@ public class ServerPingRecord implements ServerPingImpl { public int max; public int online; public Object sample; + + public PlayerSample(int max, int online, Object sample) { + this.max = max; + this.online = online; + this.sample = sample; + } + + public PlayerSample() { + this(0, 0, null); + } } public static final class ServerData { public String name; public int protocol; + + public ServerData(String name, int protocol) { + this.name = name; + this.protocol = protocol; + } + + public ServerData() { + this("", 0); + } } + static final byte[] EMPTY_FAVICON = new byte[0]; + public static final class Favicon { public byte[] iconBytes; + + public Favicon(byte[] iconBytes) { + this.iconBytes = iconBytes; + } + + public Favicon() { + this(EMPTY_FAVICON); + } } private static AutoWrapper SAMPLE_WRAPPER; @@ -81,24 +114,21 @@ public class ServerPingRecord implements ServerPingImpl { private boolean playersVisible = true; private static ServerData defaultData() { - ServerData data = new ServerData(); - data.name = MinecraftVersion.getCurrentVersion().toString(); - data.protocol = MinecraftProtocolVersion.getCurrentVersion(); - return data; + String name = MinecraftVersion.getCurrentVersion().toString(); + int protocol = MinecraftProtocolVersion.getCurrentVersion(); + + return new ServerData(name, protocol); } private static PlayerSample defaultSample() { - PlayerSample sample = new PlayerSample(); - sample.max = Bukkit.getMaxPlayers(); - sample.online = Bukkit.getOnlinePlayers().size(); - sample.sample = null; - return sample; + int max = Bukkit.getMaxPlayers(); + int online = Bukkit.getOnlinePlayers().size(); + + return new PlayerSample(max, online, null); } private static Favicon defaultFavicon() { - Favicon favicon = new Favicon(); - favicon.iconBytes = new byte[0]; - return favicon; + return new Favicon(); } public ServerPingRecord(Object handle) { @@ -112,25 +142,13 @@ public class ServerPingRecord implements ServerPingImpl { StructureModifier> optionals = structure.getOptionals(Converters.passthrough(Object.class)); Optional sampleHandle = optionals.readSafely(0); - if (sampleHandle.isPresent()) { - this.playerSample = SAMPLE_WRAPPER.wrap(sampleHandle.get()); - } else { - this.playerSample = defaultSample(); - } + this.playerSample = sampleHandle.isPresent() ? SAMPLE_WRAPPER.wrap(sampleHandle.get()) : defaultSample(); Optional dataHandle = optionals.readSafely(1); - if (dataHandle.isPresent()) { - this.serverData = DATA_WRAPPER.wrap(dataHandle.get()); - } else { - this.serverData = defaultData(); - } + this.serverData = dataHandle.isPresent() ? DATA_WRAPPER.wrap(dataHandle.get()) : defaultData(); Optional faviconHandle = optionals.readSafely(2); - if (faviconHandle.isPresent()) { - this.favicon = FAVICON_WRAPPER.wrap(faviconHandle.get()); - } else { - this.favicon = defaultFavicon(); - } + this.favicon = faviconHandle.isPresent() ? FAVICON_WRAPPER.wrap(faviconHandle.get()) : defaultFavicon(); this.enforceSafeChat = structure.getBooleans().readSafely(0); } @@ -139,6 +157,8 @@ public class ServerPingRecord implements ServerPingImpl { initialize(); this.description = DEFAULT_DESCRIPTION; + this.playerSample = defaultSample(); + this.serverData = defaultData(); this.favicon = defaultFavicon(); } @@ -173,13 +193,28 @@ public class ServerPingRecord implements ServerPingImpl { } @Override - public Object getPlayers() { - return playerSample; + public ImmutableList getPlayers() { + if (playerSample.sample == null) { + return ImmutableList.of(); + } + + List list = PROFILE_LIST_CONVERTER.getSpecific(playerSample.sample); + if (list == null) { + return ImmutableList.of(); + } + + return ImmutableList.copyOf(list); } @Override - public void setPlayers(Object playerSample) { - this.playerSample.sample = playerSample; + public void setPlayers(Iterable playerSample) { + if (playerSample == null) { + this.playerSample.sample = null; + return; + } + + List list = Converters.toList(playerSample); + this.playerSample.sample = PROFILE_LIST_CONVERTER.getGeneric(list); } @Override @@ -244,10 +279,11 @@ public class ServerPingRecord implements ServerPingImpl { @Override public Object getHandle() { + Object descHandle = description != null ? description : DEFAULT_DESCRIPTION; Optional playersHandle = Optional.ofNullable(playerSample != null ? SAMPLE_WRAPPER.unwrap(playerSample) : null); Optional versionHandle = Optional.ofNullable(serverData != null ? DATA_WRAPPER.unwrap(serverData) : null); Optional favHandle = Optional.ofNullable(favicon != null ? FAVICON_WRAPPER.unwrap(favicon) : null); - return PING_CTOR.invoke(description, playersHandle, versionHandle, favHandle, enforceSafeChat); + return PING_CTOR.invoke(descHandle, playersHandle, versionHandle, favHandle, enforceSafeChat); } }