diff --git a/src/main/java/com/comphenix/protocol/utility/MinecraftVersion.java b/src/main/java/com/comphenix/protocol/utility/MinecraftVersion.java index 8b04650b..a358b269 100644 --- a/src/main/java/com/comphenix/protocol/utility/MinecraftVersion.java +++ b/src/main/java/com/comphenix/protocol/utility/MinecraftVersion.java @@ -1,426 +1,426 @@ -/* - * ProtocolLib - Bukkit server library that allows access to the Minecraft protocol. - * Copyright (C) 2012 Kristian S. Stangeland - * - * This program is free software; you can redistribute it and/or modify it under the terms of the - * GNU General Public License as published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with this program; - * if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - * 02111-1307 USA - */ - -package com.comphenix.protocol.utility; - -import java.io.Serializable; -import java.text.SimpleDateFormat; -import java.util.Locale; -import java.util.Objects; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import com.comphenix.protocol.ProtocolLibrary; -import com.google.common.collect.ComparisonChain; -import com.google.common.collect.Ordering; -import org.bukkit.Bukkit; -import org.bukkit.Server; - -/** - * Determine the current Minecraft version. - * - * @author Kristian - */ -public final class MinecraftVersion implements Comparable, Serializable { - - /** - * Version 1.19.3 - introducing feature preview - */ - public static final MinecraftVersion FEATURE_PREVIEW_UPDATE = new MinecraftVersion("1.19.3"); - /** - * Version 1.19 - the wild update - */ - public static final MinecraftVersion WILD_UPDATE = new MinecraftVersion("1.19"); - /** - * Version 1.18 - caves and cliffs part 2 - */ - public static final MinecraftVersion CAVES_CLIFFS_2 = new MinecraftVersion("1.18"); - /** - * Version 1.17 - caves and cliffs part 1 - */ - public static final MinecraftVersion CAVES_CLIFFS_1 = new MinecraftVersion("1.17"); - /** - * Version 1.16.2 - breaking change to the nether update - */ - public static final MinecraftVersion NETHER_UPDATE_2 = new MinecraftVersion("1.16.2"); - /** - * Version 1.16.0 - the nether update - */ - public static final MinecraftVersion NETHER_UPDATE = new MinecraftVersion("1.16"); - /** - * Version 1.15 - the bee update - */ - public static final MinecraftVersion BEE_UPDATE = new MinecraftVersion("1.15"); - /** - * Version 1.14 - village and pillage update. - */ - public static final MinecraftVersion VILLAGE_UPDATE = new MinecraftVersion("1.14"); - /** - * Version 1.13 - update aquatic. - */ - public static final MinecraftVersion AQUATIC_UPDATE = new MinecraftVersion("1.13"); - /** - * Version 1.12 - the world of color update. - */ - public static final MinecraftVersion COLOR_UPDATE = new MinecraftVersion("1.12"); - /** - * Version 1.11 - the exploration update. - */ - public static final MinecraftVersion EXPLORATION_UPDATE = new MinecraftVersion("1.11"); - /** - * Version 1.10 - the frostburn update. - */ - public static final MinecraftVersion FROSTBURN_UPDATE = new MinecraftVersion("1.10"); - /** - * Version 1.9 - the combat update. - */ - public static final MinecraftVersion COMBAT_UPDATE = new MinecraftVersion("1.9"); - /** - * Version 1.8 - the "bountiful" update. - */ - public static final MinecraftVersion BOUNTIFUL_UPDATE = new MinecraftVersion("1.8"); - /** - * Version 1.7.8 - the update that changed the skin format (and distribution - R.I.P. player disguise) - */ - public static final MinecraftVersion SKIN_UPDATE = new MinecraftVersion("1.7.8"); - /** - * Version 1.7.2 - the update that changed the world. - */ - public static final MinecraftVersion WORLD_UPDATE = new MinecraftVersion("1.7.2"); - /** - * Version 1.6.1 - the horse update. - */ - public static final MinecraftVersion HORSE_UPDATE = new MinecraftVersion("1.6.1"); - /** - * Version 1.5.0 - the redstone update. - */ - public static final MinecraftVersion REDSTONE_UPDATE = new MinecraftVersion("1.5.0"); - /** - * Version 1.4.2 - the scary update (Wither Boss). - */ - public static final MinecraftVersion SCARY_UPDATE = new MinecraftVersion("1.4.2"); - - /** - * The latest release version of minecraft. - */ - public static final MinecraftVersion LATEST = FEATURE_PREVIEW_UPDATE; - - // used when serializing - private static final long serialVersionUID = -8695133558996459770L; - - /** - * Regular expression used to parse version strings. - */ - private static final Pattern VERSION_PATTERN = Pattern.compile(".*\\(.*MC.\\s*([a-zA-z0-9\\-.]+).*"); - - /** - * The current version of minecraft, lazy initialized by MinecraftVersion.currentVersion() - */ - private static MinecraftVersion currentVersion; - - private final int major; - private final int minor; - private final int build; - // The development stage - private final String development; - - // Snapshot? - private final SnapshotVersion snapshot; - private volatile Boolean atCurrentOrAbove; - - /** - * Determine the current Minecraft version. - * - * @param server - the Bukkit server that will be used to examine the MC version. - */ - public MinecraftVersion(Server server) { - this(extractVersion(server.getVersion())); - } - - /** - * Construct a version object from the format major.minor.build, or the snapshot format. - * - * @param versionOnly - the version in text form. - */ - public MinecraftVersion(String versionOnly) { - this(versionOnly, true); - } - - /** - * Construct a version format from the standard release version or the snapshot verison. - * - * @param versionOnly - the version. - * @param parseSnapshot - TRUE to parse the snapshot, FALSE otherwise. - */ - private MinecraftVersion(String versionOnly, boolean parseSnapshot) { - String[] section = versionOnly.split("-"); - SnapshotVersion snapshot = null; - int[] numbers = new int[3]; - - try { - numbers = this.parseVersion(section[0]); - } catch (NumberFormatException cause) { - // Skip snapshot parsing - if (!parseSnapshot) { - throw cause; - } - - try { - // Determine if the snapshot is newer than the current release version - snapshot = new SnapshotVersion(section[0]); - SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd", Locale.US); - - MinecraftVersion latest = new MinecraftVersion(ProtocolLibrary.MAXIMUM_MINECRAFT_VERSION, false); - boolean newer = snapshot.getSnapshotDate().compareTo( - format.parse(ProtocolLibrary.MINECRAFT_LAST_RELEASE_DATE)) > 0; - - numbers[0] = latest.getMajor(); - numbers[1] = latest.getMinor() + (newer ? 1 : -1); - } catch (Exception e) { - throw new IllegalStateException("Cannot parse " + section[0], e); - } - } - - this.major = numbers[0]; - this.minor = numbers[1]; - this.build = numbers[2]; - this.development = section.length > 1 ? section[1] : (snapshot != null ? "snapshot" : null); - this.snapshot = snapshot; - } - - /** - * Construct a version object directly. - * - * @param major - major version number. - * @param minor - minor version number. - * @param build - build version number. - */ - public MinecraftVersion(int major, int minor, int build) { - this(major, minor, build, null); - } - - /** - * Construct a version object directly. - * - * @param major - major version number. - * @param minor - minor version number. - * @param build - build version number. - * @param development - development stage. - */ - public MinecraftVersion(int major, int minor, int build, String development) { - this.major = major; - this.minor = minor; - this.build = build; - this.development = development; - this.snapshot = null; - } - - /** - * Extract the Minecraft version from CraftBukkit itself. - * - * @param text - the server version in text form. - * @return The underlying MC version. - * @throws IllegalStateException If we could not parse the version string. - */ - public static String extractVersion(String text) { - Matcher version = VERSION_PATTERN.matcher(text); - - if (version.matches() && version.group(1) != null) { - return version.group(1); - } else { - throw new IllegalStateException("Cannot parse version String '" + text + "'"); - } - } - - /** - * Parse the given server version into a Minecraft version. - * - * @param serverVersion - the server version. - * @return The resulting Minecraft version. - */ - public static MinecraftVersion fromServerVersion(String serverVersion) { - return new MinecraftVersion(extractVersion(serverVersion)); - } - - public static MinecraftVersion getCurrentVersion() { - if (currentVersion == null) { - currentVersion = fromServerVersion(Bukkit.getVersion()); - } - - return currentVersion; - } - - public static void setCurrentVersion(MinecraftVersion version) { - currentVersion = version; - } - - public static boolean atOrAbove(MinecraftVersion version) { - return getCurrentVersion().isAtLeast(version); - } - - private int[] parseVersion(String version) { - String[] elements = version.split("\\."); - int[] numbers = new int[3]; - - // Make sure it's even a valid version - if (elements.length < 1) { - throw new IllegalStateException("Corrupt MC version: " + version); - } - - // The String 1 or 1.2 is interpreted as 1.0.0 and 1.2.0 respectively. - for (int i = 0; i < Math.min(numbers.length, elements.length); i++) { - numbers[i] = Integer.parseInt(elements[i].trim()); - } - return numbers; - } - - /** - * Major version number - * - * @return Current major version number. - */ - public int getMajor() { - return this.major; - } - - /** - * Minor version number - * - * @return Current minor version number. - */ - public int getMinor() { - return this.minor; - } - - /** - * Build version number - * - * @return Current build version number. - */ - public int getBuild() { - return this.build; - } - - /** - * Retrieve the development stage. - * - * @return Development stage, or NULL if this is a release. - */ - public String getDevelopmentStage() { - return this.development; - } - - /** - * Retrieve the snapshot version, or NULL if this is a release. - * - * @return The snapshot version. - */ - public SnapshotVersion getSnapshot() { - return this.snapshot; - } - - /** - * Determine if this version is a snapshot. - * - * @return The snapshot version. - */ - public boolean isSnapshot() { - return this.snapshot != null; - } - - /** - * Checks if this version is at or above the current version the server is running. - * - * @return true if this version is equal or newer than the server version, false otherwise. - */ - public boolean atOrAbove() { - if (this.atCurrentOrAbove == null) { - this.atCurrentOrAbove = MinecraftVersion.atOrAbove(this); - } - - return this.atCurrentOrAbove; - } - - /** - * Retrieve the version String (major.minor.build) only. - * - * @return A normal version string. - */ - public String getVersion() { - if (this.getDevelopmentStage() == null) { - return String.format("%s.%s.%s", this.getMajor(), this.getMinor(), this.getBuild()); - } else { - return String.format("%s.%s.%s-%s%s", this.getMajor(), this.getMinor(), this.getBuild(), - this.getDevelopmentStage(), this.isSnapshot() ? this.snapshot : ""); - } - } - - @Override - public int compareTo(MinecraftVersion o) { - if (o == null) { - return 1; - } - - return ComparisonChain.start() - .compare(this.getMajor(), o.getMajor()) - .compare(this.getMinor(), o.getMinor()) - .compare(this.getBuild(), o.getBuild()) - .compare(this.getDevelopmentStage(), o.getDevelopmentStage(), Ordering.natural().nullsLast()) - .compare(this.getSnapshot(), o.getSnapshot(), Ordering.natural().nullsFirst()) - .result(); - } - - public boolean isAtLeast(MinecraftVersion other) { - if (other == null) { - return false; - } - - return this.compareTo(other) >= 0; - } - - @Override - public boolean equals(Object obj) { - if (obj == null) { - return false; - } - if (obj == this) { - return true; - } - - if (obj instanceof MinecraftVersion) { - MinecraftVersion other = (MinecraftVersion) obj; - - return this.getMajor() == other.getMajor() && - this.getMinor() == other.getMinor() && - this.getBuild() == other.getBuild() && - Objects.equals(this.getDevelopmentStage(), other.getDevelopmentStage()); - } - - return false; - } - - @Override - public int hashCode() { - return Objects.hash(this.getMajor(), this.getMinor(), this.getBuild()); - } - - @Override - public String toString() { - // Convert to a String that we can parse back again - return String.format("(MC: %s)", this.getVersion()); - } -} +/* + * ProtocolLib - Bukkit server library that allows access to the Minecraft protocol. + * Copyright (C) 2012 Kristian S. Stangeland + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; + * if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA + */ + +package com.comphenix.protocol.utility; + +import java.io.Serializable; +import java.text.SimpleDateFormat; +import java.util.Locale; +import java.util.Objects; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import com.comphenix.protocol.ProtocolLibrary; +import com.google.common.collect.ComparisonChain; +import com.google.common.collect.Ordering; +import org.bukkit.Bukkit; +import org.bukkit.Server; + +/** + * Determine the current Minecraft version. + * + * @author Kristian + */ +public final class MinecraftVersion implements Comparable, Serializable { + + /** + * Version 1.19.3 - introducing feature preview + */ + public static final MinecraftVersion FEATURE_PREVIEW_UPDATE = new MinecraftVersion("1.19.3"); + /** + * Version 1.19 - the wild update + */ + public static final MinecraftVersion WILD_UPDATE = new MinecraftVersion("1.19"); + /** + * Version 1.18 - caves and cliffs part 2 + */ + public static final MinecraftVersion CAVES_CLIFFS_2 = new MinecraftVersion("1.18"); + /** + * Version 1.17 - caves and cliffs part 1 + */ + public static final MinecraftVersion CAVES_CLIFFS_1 = new MinecraftVersion("1.17"); + /** + * Version 1.16.2 - breaking change to the nether update + */ + public static final MinecraftVersion NETHER_UPDATE_2 = new MinecraftVersion("1.16.2"); + /** + * Version 1.16.0 - the nether update + */ + public static final MinecraftVersion NETHER_UPDATE = new MinecraftVersion("1.16"); + /** + * Version 1.15 - the bee update + */ + public static final MinecraftVersion BEE_UPDATE = new MinecraftVersion("1.15"); + /** + * Version 1.14 - village and pillage update. + */ + public static final MinecraftVersion VILLAGE_UPDATE = new MinecraftVersion("1.14"); + /** + * Version 1.13 - update aquatic. + */ + public static final MinecraftVersion AQUATIC_UPDATE = new MinecraftVersion("1.13"); + /** + * Version 1.12 - the world of color update. + */ + public static final MinecraftVersion COLOR_UPDATE = new MinecraftVersion("1.12"); + /** + * Version 1.11 - the exploration update. + */ + public static final MinecraftVersion EXPLORATION_UPDATE = new MinecraftVersion("1.11"); + /** + * Version 1.10 - the frostburn update. + */ + public static final MinecraftVersion FROSTBURN_UPDATE = new MinecraftVersion("1.10"); + /** + * Version 1.9 - the combat update. + */ + public static final MinecraftVersion COMBAT_UPDATE = new MinecraftVersion("1.9"); + /** + * Version 1.8 - the "bountiful" update. + */ + public static final MinecraftVersion BOUNTIFUL_UPDATE = new MinecraftVersion("1.8"); + /** + * Version 1.7.8 - the update that changed the skin format (and distribution - R.I.P. player disguise) + */ + public static final MinecraftVersion SKIN_UPDATE = new MinecraftVersion("1.7.8"); + /** + * Version 1.7.2 - the update that changed the world. + */ + public static final MinecraftVersion WORLD_UPDATE = new MinecraftVersion("1.7.2"); + /** + * Version 1.6.1 - the horse update. + */ + public static final MinecraftVersion HORSE_UPDATE = new MinecraftVersion("1.6.1"); + /** + * Version 1.5.0 - the redstone update. + */ + public static final MinecraftVersion REDSTONE_UPDATE = new MinecraftVersion("1.5.0"); + /** + * Version 1.4.2 - the scary update (Wither Boss). + */ + public static final MinecraftVersion SCARY_UPDATE = new MinecraftVersion("1.4.2"); + + /** + * The latest release version of minecraft. + */ + public static final MinecraftVersion LATEST = FEATURE_PREVIEW_UPDATE; + + // used when serializing + private static final long serialVersionUID = -8695133558996459770L; + + /** + * Regular expression used to parse version strings. + */ + private static final Pattern VERSION_PATTERN = Pattern.compile(".*\\(.*MC.\\s*([a-zA-z0-9\\-.]+).*"); + + /** + * The current version of minecraft, lazy initialized by MinecraftVersion.currentVersion() + */ + private static MinecraftVersion currentVersion; + + private final int major; + private final int minor; + private final int build; + // The development stage + private final String development; + + // Snapshot? + private final SnapshotVersion snapshot; + private volatile Boolean atCurrentOrAbove; + + /** + * Determine the current Minecraft version. + * + * @param server - the Bukkit server that will be used to examine the MC version. + */ + public MinecraftVersion(Server server) { + this(extractVersion(server.getVersion())); + } + + /** + * Construct a version object from the format major.minor.build, or the snapshot format. + * + * @param versionOnly - the version in text form. + */ + public MinecraftVersion(String versionOnly) { + this(versionOnly, true); + } + + /** + * Construct a version format from the standard release version or the snapshot verison. + * + * @param versionOnly - the version. + * @param parseSnapshot - TRUE to parse the snapshot, FALSE otherwise. + */ + private MinecraftVersion(String versionOnly, boolean parseSnapshot) { + String[] section = versionOnly.split("-"); + SnapshotVersion snapshot = null; + int[] numbers = new int[3]; + + try { + numbers = this.parseVersion(section[0]); + } catch (NumberFormatException cause) { + // Skip snapshot parsing + if (!parseSnapshot) { + throw cause; + } + + try { + // Determine if the snapshot is newer than the current release version + snapshot = new SnapshotVersion(section[0]); + SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd", Locale.US); + + MinecraftVersion latest = new MinecraftVersion(ProtocolLibrary.MAXIMUM_MINECRAFT_VERSION, false); + boolean newer = snapshot.getSnapshotDate().compareTo( + format.parse(ProtocolLibrary.MINECRAFT_LAST_RELEASE_DATE)) > 0; + + numbers[0] = latest.getMajor(); + numbers[1] = latest.getMinor() + (newer ? 1 : -1); + } catch (Exception e) { + throw new IllegalStateException("Cannot parse " + section[0], e); + } + } + + this.major = numbers[0]; + this.minor = numbers[1]; + this.build = numbers[2]; + this.development = section.length > 1 ? section[1] : (snapshot != null ? "snapshot" : null); + this.snapshot = snapshot; + } + + /** + * Construct a version object directly. + * + * @param major - major version number. + * @param minor - minor version number. + * @param build - build version number. + */ + public MinecraftVersion(int major, int minor, int build) { + this(major, minor, build, null); + } + + /** + * Construct a version object directly. + * + * @param major - major version number. + * @param minor - minor version number. + * @param build - build version number. + * @param development - development stage. + */ + public MinecraftVersion(int major, int minor, int build, String development) { + this.major = major; + this.minor = minor; + this.build = build; + this.development = development; + this.snapshot = null; + } + + /** + * Extract the Minecraft version from CraftBukkit itself. + * + * @param text - the server version in text form. + * @return The underlying MC version. + * @throws IllegalStateException If we could not parse the version string. + */ + public static String extractVersion(String text) { + Matcher version = VERSION_PATTERN.matcher(text); + + if (version.matches() && version.group(1) != null) { + return version.group(1); + } else { + throw new IllegalStateException("Cannot parse version String '" + text + "'"); + } + } + + /** + * Parse the given server version into a Minecraft version. + * + * @param serverVersion - the server version. + * @return The resulting Minecraft version. + */ + public static MinecraftVersion fromServerVersion(String serverVersion) { + return new MinecraftVersion(extractVersion(serverVersion)); + } + + public static MinecraftVersion getCurrentVersion() { + if (currentVersion == null) { + currentVersion = fromServerVersion(Bukkit.getVersion()); + } + + return currentVersion; + } + + public static void setCurrentVersion(MinecraftVersion version) { + currentVersion = version; + } + + public static boolean atOrAbove(MinecraftVersion version) { + return getCurrentVersion().isAtLeast(version); + } + + private int[] parseVersion(String version) { + String[] elements = version.split("\\."); + int[] numbers = new int[3]; + + // Make sure it's even a valid version + if (elements.length < 1) { + throw new IllegalStateException("Corrupt MC version: " + version); + } + + // The String 1 or 1.2 is interpreted as 1.0.0 and 1.2.0 respectively. + for (int i = 0; i < Math.min(numbers.length, elements.length); i++) { + numbers[i] = Integer.parseInt(elements[i].trim()); + } + return numbers; + } + + /** + * Major version number + * + * @return Current major version number. + */ + public int getMajor() { + return this.major; + } + + /** + * Minor version number + * + * @return Current minor version number. + */ + public int getMinor() { + return this.minor; + } + + /** + * Build version number + * + * @return Current build version number. + */ + public int getBuild() { + return this.build; + } + + /** + * Retrieve the development stage. + * + * @return Development stage, or NULL if this is a release. + */ + public String getDevelopmentStage() { + return this.development; + } + + /** + * Retrieve the snapshot version, or NULL if this is a release. + * + * @return The snapshot version. + */ + public SnapshotVersion getSnapshot() { + return this.snapshot; + } + + /** + * Determine if this version is a snapshot. + * + * @return The snapshot version. + */ + public boolean isSnapshot() { + return this.snapshot != null; + } + + /** + * Checks if this version is at or above the current version the server is running. + * + * @return true if this version is equal or newer than the server version, false otherwise. + */ + public boolean atOrAbove() { + if (this.atCurrentOrAbove == null) { + this.atCurrentOrAbove = MinecraftVersion.atOrAbove(this); + } + + return this.atCurrentOrAbove; + } + + /** + * Retrieve the version String (major.minor.build) only. + * + * @return A normal version string. + */ + public String getVersion() { + if (this.getDevelopmentStage() == null) { + return String.format("%s.%s.%s", this.getMajor(), this.getMinor(), this.getBuild()); + } else { + return String.format("%s.%s.%s-%s%s", this.getMajor(), this.getMinor(), this.getBuild(), + this.getDevelopmentStage(), this.isSnapshot() ? this.snapshot : ""); + } + } + + @Override + public int compareTo(MinecraftVersion o) { + if (o == null) { + return 1; + } + + return ComparisonChain.start() + .compare(this.getMajor(), o.getMajor()) + .compare(this.getMinor(), o.getMinor()) + .compare(this.getBuild(), o.getBuild()) + .compare(this.getDevelopmentStage(), o.getDevelopmentStage(), Ordering.natural().nullsLast()) + .compare(this.getSnapshot(), o.getSnapshot(), Ordering.natural().nullsFirst()) + .result(); + } + + public boolean isAtLeast(MinecraftVersion other) { + if (other == null) { + return false; + } + + return this.compareTo(other) >= 0; + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (obj == this) { + return true; + } + + if (obj instanceof MinecraftVersion) { + MinecraftVersion other = (MinecraftVersion) obj; + + return this.getMajor() == other.getMajor() && + this.getMinor() == other.getMinor() && + this.getBuild() == other.getBuild() && + Objects.equals(this.getDevelopmentStage(), other.getDevelopmentStage()); + } + + return false; + } + + @Override + public int hashCode() { + return Objects.hash(this.getMajor(), this.getMinor(), this.getBuild()); + } + + @Override + public String toString() { + // Convert to a String that we can parse back again + return String.format("(MC: %s)", this.getVersion()); + } +} diff --git a/src/main/java/com/comphenix/protocol/wrappers/Converters.java b/src/main/java/com/comphenix/protocol/wrappers/Converters.java index 47dc0886..ff6b92fa 100644 --- a/src/main/java/com/comphenix/protocol/wrappers/Converters.java +++ b/src/main/java/com/comphenix/protocol/wrappers/Converters.java @@ -1,220 +1,220 @@ -/** - * ProtocolLib - Bukkit server library that allows access to the Minecraft protocol. Copyright (C) 2017 Dan Mulloy - *

- * This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public - * License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later - * version. - *

- * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - *

- * You should have received a copy of the GNU General Public License along with this program; if not, write to the Free - * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ -package com.comphenix.protocol.wrappers; - -import java.lang.reflect.Array; -import java.util.Collection; -import java.util.Optional; -import java.util.function.Function; - -import com.comphenix.protocol.reflect.EquivalentConverter; -import com.comphenix.protocol.utility.MinecraftReflection; - -/** - * Utility class for converters - * @author dmulloy2 - */ -@SuppressWarnings("unchecked") -public class Converters { - - /** - * Returns a converter that ignores null elements, so that the underlying converter doesn't have to worry about them. - * @param converter Underlying converter - * @param Element type - * @return An ignore null converter - */ - public static EquivalentConverter ignoreNull(final EquivalentConverter converter) { - return new EquivalentConverter() { - @Override - public T getSpecific(Object generic) { - return generic != null ? converter.getSpecific(generic) : null; - } - - @Override - public Object getGeneric(T specific) { - return specific != null ? converter.getGeneric(specific) : null; - } - - @Override - public Class getSpecificType() { - return converter.getSpecificType(); - } - }; - } - - /** - * Returns a converter that passes generic and specific values through without converting. - * @param clazz Element class - * @param Element type - * @return A passthrough converter - */ - public static EquivalentConverter passthrough(final Class clazz) { - return ignoreNull(new EquivalentConverter() { - @Override - public T getSpecific(Object generic) { - return (T) generic; - } - - @Override - public Object getGeneric(T specific) { - return specific; - } - - @Override - public Class getSpecificType() { - return clazz; - } - }); - } - - /** - * Creates a simple converter for wrappers with {@code getHandle()} and {@code fromHandle(...)} methods. With Java 8, - * converters can be reduced to a single line (see {@link BukkitConverters#getWrappedGameProfileConverter()}). - * @param toHandle Function from wrapper to handle (i.e. {@code getHandle()}) - * @param fromHandle Function from handle to wrapper (i.e. {@code fromHandle(Object)}) - * @param Wrapper type - * @return A handle converter - */ - public static EquivalentConverter handle(final Function toHandle, - final Function fromHandle, final Class specificType) { - return new EquivalentConverter() { - @Override - public T getSpecific(Object generic) { - return fromHandle.apply(generic); - } - - @Override - public Object getGeneric(T specific) { - return toHandle.apply(specific); - } - - @Override - public Class getSpecificType() { - return specificType; - } - }; - } - - /** - * Creates a generic array converter. Converts a NMS object array to and from a wrapper array by converting - * each element individually. - * - * @param nmsClass NMS class - * @param converter Underlying converter - * @param Generic type - * @return An array converter - */ - public static EquivalentConverter array(final Class nmsClass, final EquivalentConverter converter) { - return new EquivalentConverter() { - @Override - public T[] getSpecific(Object generic) { - Object[] array = (Object[]) generic; - Class clazz = getSpecificType(); - T[] result = clazz.cast(Array.newInstance(clazz.getComponentType(), array.length)); - - // Unwrap every item - for (int i = 0; i < result.length; i++) { - result[i] = converter.getSpecific(array[i]); - } - - return result; - } - - @Override - public Object getGeneric(T[] specific) { - Object[] result = (Object[]) Array.newInstance(nmsClass, specific.length); - - // Wrap every item - for (int i = 0; i < result.length; i++) { - result[i] = converter.getGeneric(specific[i]); - } - - return result; - } - - @Override - public Class getSpecificType() { - return (Class) MinecraftReflection.getArrayClass(converter.getSpecificType()); - } - }; - } - - public static EquivalentConverter> optional(final EquivalentConverter converter) { - return new EquivalentConverter>() { - @Override - public Object getGeneric(Optional specific) { - return specific.map(converter::getGeneric); - } - - @Override - public Optional getSpecific(Object generic) { - Optional optional = (Optional) generic; - return optional.map(converter::getSpecific); - } - - @Override - public Class> getSpecificType() { - return (Class>) Optional.empty().getClass(); - } - }; - } - - public static > EquivalentConverter collection( - final EquivalentConverter elementConverter, - final Function, C> genericToSpecificCollectionFactory, - final Function> specificToGenericCollectionFactory - ) { - return ignoreNull(new EquivalentConverter() { - - @Override - public Object getGeneric(C specific) { - // generics are very cool, thank you java - Collection targetCollection = (Collection) specificToGenericCollectionFactory.apply(specific); - for (T element : specific) { - Object generic = elementConverter.getGeneric(element); - if (generic != null) { - targetCollection.add(generic); - } - } - - return targetCollection; - } - - @Override - public C getSpecific(Object generic) { - if (generic instanceof Collection) { - Collection sourceCollection = (Collection) generic; - C targetCollection = genericToSpecificCollectionFactory.apply(sourceCollection); - // copy over all elements into a new collection - for (Object element : sourceCollection) { - T specific = elementConverter.getSpecific(element); - if (specific != null) { - targetCollection.add(specific); - } - } - - return targetCollection; - } - // not valid - return null; - } - - @Override - public Class getSpecificType() { - return (Class) Collection.class; - } - }); - } -} +/** + * ProtocolLib - Bukkit server library that allows access to the Minecraft protocol. Copyright (C) 2017 Dan Mulloy + *

+ * This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later + * version. + *

+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + *

+ * You should have received a copy of the GNU General Public License along with this program; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package com.comphenix.protocol.wrappers; + +import java.lang.reflect.Array; +import java.util.Collection; +import java.util.Optional; +import java.util.function.Function; + +import com.comphenix.protocol.reflect.EquivalentConverter; +import com.comphenix.protocol.utility.MinecraftReflection; + +/** + * Utility class for converters + * @author dmulloy2 + */ +@SuppressWarnings("unchecked") +public class Converters { + + /** + * Returns a converter that ignores null elements, so that the underlying converter doesn't have to worry about them. + * @param converter Underlying converter + * @param Element type + * @return An ignore null converter + */ + public static EquivalentConverter ignoreNull(final EquivalentConverter converter) { + return new EquivalentConverter() { + @Override + public T getSpecific(Object generic) { + return generic != null ? converter.getSpecific(generic) : null; + } + + @Override + public Object getGeneric(T specific) { + return specific != null ? converter.getGeneric(specific) : null; + } + + @Override + public Class getSpecificType() { + return converter.getSpecificType(); + } + }; + } + + /** + * Returns a converter that passes generic and specific values through without converting. + * @param clazz Element class + * @param Element type + * @return A passthrough converter + */ + public static EquivalentConverter passthrough(final Class clazz) { + return ignoreNull(new EquivalentConverter() { + @Override + public T getSpecific(Object generic) { + return (T) generic; + } + + @Override + public Object getGeneric(T specific) { + return specific; + } + + @Override + public Class getSpecificType() { + return clazz; + } + }); + } + + /** + * Creates a simple converter for wrappers with {@code getHandle()} and {@code fromHandle(...)} methods. With Java 8, + * converters can be reduced to a single line (see {@link BukkitConverters#getWrappedGameProfileConverter()}). + * @param toHandle Function from wrapper to handle (i.e. {@code getHandle()}) + * @param fromHandle Function from handle to wrapper (i.e. {@code fromHandle(Object)}) + * @param Wrapper type + * @return A handle converter + */ + public static EquivalentConverter handle(final Function toHandle, + final Function fromHandle, final Class specificType) { + return new EquivalentConverter() { + @Override + public T getSpecific(Object generic) { + return fromHandle.apply(generic); + } + + @Override + public Object getGeneric(T specific) { + return toHandle.apply(specific); + } + + @Override + public Class getSpecificType() { + return specificType; + } + }; + } + + /** + * Creates a generic array converter. Converts a NMS object array to and from a wrapper array by converting + * each element individually. + * + * @param nmsClass NMS class + * @param converter Underlying converter + * @param Generic type + * @return An array converter + */ + public static EquivalentConverter array(final Class nmsClass, final EquivalentConverter converter) { + return new EquivalentConverter() { + @Override + public T[] getSpecific(Object generic) { + Object[] array = (Object[]) generic; + Class clazz = getSpecificType(); + T[] result = clazz.cast(Array.newInstance(clazz.getComponentType(), array.length)); + + // Unwrap every item + for (int i = 0; i < result.length; i++) { + result[i] = converter.getSpecific(array[i]); + } + + return result; + } + + @Override + public Object getGeneric(T[] specific) { + Object[] result = (Object[]) Array.newInstance(nmsClass, specific.length); + + // Wrap every item + for (int i = 0; i < result.length; i++) { + result[i] = converter.getGeneric(specific[i]); + } + + return result; + } + + @Override + public Class getSpecificType() { + return (Class) MinecraftReflection.getArrayClass(converter.getSpecificType()); + } + }; + } + + public static EquivalentConverter> optional(final EquivalentConverter converter) { + return new EquivalentConverter>() { + @Override + public Object getGeneric(Optional specific) { + return specific.map(converter::getGeneric); + } + + @Override + public Optional getSpecific(Object generic) { + Optional optional = (Optional) generic; + return optional.map(converter::getSpecific); + } + + @Override + public Class> getSpecificType() { + return (Class>) Optional.empty().getClass(); + } + }; + } + + public static > EquivalentConverter collection( + final EquivalentConverter elementConverter, + final Function, C> genericToSpecificCollectionFactory, + final Function> specificToGenericCollectionFactory + ) { + return ignoreNull(new EquivalentConverter() { + + @Override + public Object getGeneric(C specific) { + // generics are very cool, thank you java + Collection targetCollection = (Collection) specificToGenericCollectionFactory.apply(specific); + for (T element : specific) { + Object generic = elementConverter.getGeneric(element); + if (generic != null) { + targetCollection.add(generic); + } + } + + return targetCollection; + } + + @Override + public C getSpecific(Object generic) { + if (generic instanceof Collection) { + Collection sourceCollection = (Collection) generic; + C targetCollection = genericToSpecificCollectionFactory.apply(sourceCollection); + // copy over all elements into a new collection + for (Object element : sourceCollection) { + T specific = elementConverter.getSpecific(element); + if (specific != null) { + targetCollection.add(specific); + } + } + + return targetCollection; + } + // not valid + return null; + } + + @Override + public Class getSpecificType() { + return (Class) Collection.class; + } + }); + } +} diff --git a/src/main/java/com/comphenix/protocol/wrappers/WrappedParticle.java b/src/main/java/com/comphenix/protocol/wrappers/WrappedParticle.java index 8fd2c288..3f464577 100644 --- a/src/main/java/com/comphenix/protocol/wrappers/WrappedParticle.java +++ b/src/main/java/com/comphenix/protocol/wrappers/WrappedParticle.java @@ -1,184 +1,184 @@ -package com.comphenix.protocol.wrappers; - -import java.lang.reflect.Modifier; - -import com.comphenix.protocol.reflect.FuzzyReflection; -import com.comphenix.protocol.reflect.StructureModifier; -import com.comphenix.protocol.reflect.accessors.Accessors; -import com.comphenix.protocol.reflect.accessors.MethodAccessor; -import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract; -import com.comphenix.protocol.utility.MinecraftReflection; -import com.comphenix.protocol.utility.MinecraftVersion; -import org.bukkit.Color; -import org.bukkit.Particle; -import org.bukkit.inventory.ItemStack; -import org.joml.Vector3f; - -/** - * Represents an immutable wrapped ParticleParam in 1.13 - */ -public class WrappedParticle { - private static Class VECTOR_3FA; - - private static MethodAccessor toBukkit; - private static MethodAccessor toNMS; - private static MethodAccessor toCraftData; - - private static void ensureMethods() { - if (toBukkit != null && toNMS != null) { - return; - } - - FuzzyReflection fuzzy = FuzzyReflection.fromClass(MinecraftReflection.getCraftBukkitClass("CraftParticle")); - FuzzyMethodContract contract = FuzzyMethodContract - .newBuilder() - .requireModifier(Modifier.STATIC) - .returnTypeExact(Particle.class) - .parameterExactType(MinecraftReflection.getParticleParam()) - .build(); - toBukkit = Accessors.getMethodAccessor(fuzzy.getMethod(contract)); - - contract = FuzzyMethodContract - .newBuilder() - .requireModifier(Modifier.STATIC) - .returnTypeExact(MinecraftReflection.getParticleParam()) - .parameterCount(2) - .build(); - toNMS = Accessors.getMethodAccessor(fuzzy.getMethod(contract)); - - Class cbData = MinecraftReflection.getCraftBukkitClass("block.data.CraftBlockData"); - fuzzy = FuzzyReflection.fromClass(cbData); - contract = FuzzyMethodContract - .newBuilder() - .requireModifier(Modifier.STATIC) - .returnTypeExact(cbData) - .parameterExactArray(MinecraftReflection.getIBlockDataClass()) - .build(); - toCraftData = Accessors.getMethodAccessor(fuzzy.getMethod(contract)); - } - - private final Particle particle; - private final T data; - private final Object handle; - - private WrappedParticle(Object handle, Particle particle, T data) { - this.handle = handle; - this.particle = particle; - this.data = data; - } - - /** - * @return This particle's Bukkit type - */ - public Particle getParticle() { - return particle; - } - - /** - * Gets this Particle's Bukkit/ProtocolLib data. The type of this data depends on the - * {@link #getParticle() Particle type}. For Block particles it will be {@link WrappedBlockData}, - * for Item crack particles, it will be an {@link ItemStack}, and for redstone particles it will - * be {@link Particle.DustOptions} - * - * @return The particle data - */ - public T getData() { - return data; - } - - /** - * @return NMS handle - */ - public Object getHandle() { - return handle; - } - - public static WrappedParticle fromHandle(Object handle) { - ensureMethods(); - - Particle bukkit = (Particle) toBukkit.invoke(null, handle); - Object data = null; - - switch (bukkit) { - case BLOCK_CRACK: - case BLOCK_DUST: - case FALLING_DUST: - data = getBlockData(handle); - break; - case ITEM_CRACK: - data = getItem(handle); - break; - case REDSTONE: - data = getRedstone(handle); - break; - default: - break; - } - - return new WrappedParticle<>(handle, bukkit, data); - } - - private static WrappedBlockData getBlockData(Object handle) { - return new StructureModifier<>(handle.getClass()) - .withTarget(handle) - .withType(MinecraftReflection.getIBlockDataClass(), BukkitConverters.getWrappedBlockDataConverter()) - .read(0); - } - - private static Object getItem(Object handle) { - return new StructureModifier<>(handle.getClass()) - .withTarget(handle) - .withType(MinecraftReflection.getItemStackClass(), BukkitConverters.getItemStackConverter()) - .read(0); - } - - private static Object getRedstone(Object handle) { - int r, g, b; - float alpha; - - if (MinecraftVersion.FEATURE_PREVIEW_UPDATE.atOrAbove()) { - StructureModifier modifier = new StructureModifier<>(handle.getClass()).withTarget(handle); - Vector3f rgb = (Vector3f) modifier.withType(Vector3f.class).read(0); - - r = (int) (rgb.x() * 255); - g = (int) (rgb.y() * 255); - b = (int) (rgb.z() * 255); - alpha = (float) modifier.withType(float.class).read(0); - } else if (MinecraftVersion.CAVES_CLIFFS_1.atOrAbove()) { - if (VECTOR_3FA == null) { - VECTOR_3FA = MinecraftReflection.getLibraryClass("com.mojang.math.Vector3fa"); - } - - StructureModifier modifier = new StructureModifier<>(handle.getClass()).withTarget(handle); - - Object rgb = modifier.withType(VECTOR_3FA).read(0); - StructureModifier rgbModifier = new StructureModifier<>(VECTOR_3FA).withTarget(rgb); - - r = (int) (rgbModifier.withType(float.class).read(0) * 255); - g = (int) (rgbModifier.withType(float.class).read(1) * 255); - b = (int) (rgbModifier.withType(float.class).read(2) * 255); - alpha = (float) modifier.withType(float.class).read(0); - } else { - StructureModifier modifier = new StructureModifier<>(handle.getClass()).withTarget(handle).withType(float.class); - r = (int) (modifier.read(0) * 255); - g = (int) (modifier.read(1) * 255); - b = (int) (modifier.read(2) * 255); - alpha = modifier.read(3); - } - - return new Particle.DustOptions(Color.fromRGB(r, g, b), alpha); - } - - public static WrappedParticle create(Particle particle, T data) { - ensureMethods(); - - Object bukkitData = data; - if (data instanceof WrappedBlockData) { - WrappedBlockData blockData = (WrappedBlockData) data; - bukkitData = toCraftData.invoke(null, blockData.getHandle()); - } - - Object handle = toNMS.invoke(null, particle, bukkitData); - return new WrappedParticle<>(handle, particle, data); - } -} +package com.comphenix.protocol.wrappers; + +import java.lang.reflect.Modifier; + +import com.comphenix.protocol.reflect.FuzzyReflection; +import com.comphenix.protocol.reflect.StructureModifier; +import com.comphenix.protocol.reflect.accessors.Accessors; +import com.comphenix.protocol.reflect.accessors.MethodAccessor; +import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract; +import com.comphenix.protocol.utility.MinecraftReflection; +import com.comphenix.protocol.utility.MinecraftVersion; +import org.bukkit.Color; +import org.bukkit.Particle; +import org.bukkit.inventory.ItemStack; +import org.joml.Vector3f; + +/** + * Represents an immutable wrapped ParticleParam in 1.13 + */ +public class WrappedParticle { + private static Class VECTOR_3FA; + + private static MethodAccessor toBukkit; + private static MethodAccessor toNMS; + private static MethodAccessor toCraftData; + + private static void ensureMethods() { + if (toBukkit != null && toNMS != null) { + return; + } + + FuzzyReflection fuzzy = FuzzyReflection.fromClass(MinecraftReflection.getCraftBukkitClass("CraftParticle")); + FuzzyMethodContract contract = FuzzyMethodContract + .newBuilder() + .requireModifier(Modifier.STATIC) + .returnTypeExact(Particle.class) + .parameterExactType(MinecraftReflection.getParticleParam()) + .build(); + toBukkit = Accessors.getMethodAccessor(fuzzy.getMethod(contract)); + + contract = FuzzyMethodContract + .newBuilder() + .requireModifier(Modifier.STATIC) + .returnTypeExact(MinecraftReflection.getParticleParam()) + .parameterCount(2) + .build(); + toNMS = Accessors.getMethodAccessor(fuzzy.getMethod(contract)); + + Class cbData = MinecraftReflection.getCraftBukkitClass("block.data.CraftBlockData"); + fuzzy = FuzzyReflection.fromClass(cbData); + contract = FuzzyMethodContract + .newBuilder() + .requireModifier(Modifier.STATIC) + .returnTypeExact(cbData) + .parameterExactArray(MinecraftReflection.getIBlockDataClass()) + .build(); + toCraftData = Accessors.getMethodAccessor(fuzzy.getMethod(contract)); + } + + private final Particle particle; + private final T data; + private final Object handle; + + private WrappedParticle(Object handle, Particle particle, T data) { + this.handle = handle; + this.particle = particle; + this.data = data; + } + + /** + * @return This particle's Bukkit type + */ + public Particle getParticle() { + return particle; + } + + /** + * Gets this Particle's Bukkit/ProtocolLib data. The type of this data depends on the + * {@link #getParticle() Particle type}. For Block particles it will be {@link WrappedBlockData}, + * for Item crack particles, it will be an {@link ItemStack}, and for redstone particles it will + * be {@link Particle.DustOptions} + * + * @return The particle data + */ + public T getData() { + return data; + } + + /** + * @return NMS handle + */ + public Object getHandle() { + return handle; + } + + public static WrappedParticle fromHandle(Object handle) { + ensureMethods(); + + Particle bukkit = (Particle) toBukkit.invoke(null, handle); + Object data = null; + + switch (bukkit) { + case BLOCK_CRACK: + case BLOCK_DUST: + case FALLING_DUST: + data = getBlockData(handle); + break; + case ITEM_CRACK: + data = getItem(handle); + break; + case REDSTONE: + data = getRedstone(handle); + break; + default: + break; + } + + return new WrappedParticle<>(handle, bukkit, data); + } + + private static WrappedBlockData getBlockData(Object handle) { + return new StructureModifier<>(handle.getClass()) + .withTarget(handle) + .withType(MinecraftReflection.getIBlockDataClass(), BukkitConverters.getWrappedBlockDataConverter()) + .read(0); + } + + private static Object getItem(Object handle) { + return new StructureModifier<>(handle.getClass()) + .withTarget(handle) + .withType(MinecraftReflection.getItemStackClass(), BukkitConverters.getItemStackConverter()) + .read(0); + } + + private static Object getRedstone(Object handle) { + int r, g, b; + float alpha; + + if (MinecraftVersion.FEATURE_PREVIEW_UPDATE.atOrAbove()) { + StructureModifier modifier = new StructureModifier<>(handle.getClass()).withTarget(handle); + Vector3f rgb = (Vector3f) modifier.withType(Vector3f.class).read(0); + + r = (int) (rgb.x() * 255); + g = (int) (rgb.y() * 255); + b = (int) (rgb.z() * 255); + alpha = (float) modifier.withType(float.class).read(0); + } else if (MinecraftVersion.CAVES_CLIFFS_1.atOrAbove()) { + if (VECTOR_3FA == null) { + VECTOR_3FA = MinecraftReflection.getLibraryClass("com.mojang.math.Vector3fa"); + } + + StructureModifier modifier = new StructureModifier<>(handle.getClass()).withTarget(handle); + + Object rgb = modifier.withType(VECTOR_3FA).read(0); + StructureModifier rgbModifier = new StructureModifier<>(VECTOR_3FA).withTarget(rgb); + + r = (int) (rgbModifier.withType(float.class).read(0) * 255); + g = (int) (rgbModifier.withType(float.class).read(1) * 255); + b = (int) (rgbModifier.withType(float.class).read(2) * 255); + alpha = (float) modifier.withType(float.class).read(0); + } else { + StructureModifier modifier = new StructureModifier<>(handle.getClass()).withTarget(handle).withType(float.class); + r = (int) (modifier.read(0) * 255); + g = (int) (modifier.read(1) * 255); + b = (int) (modifier.read(2) * 255); + alpha = modifier.read(3); + } + + return new Particle.DustOptions(Color.fromRGB(r, g, b), alpha); + } + + public static WrappedParticle create(Particle particle, T data) { + ensureMethods(); + + Object bukkitData = data; + if (data instanceof WrappedBlockData) { + WrappedBlockData blockData = (WrappedBlockData) data; + bukkitData = toCraftData.invoke(null, blockData.getHandle()); + } + + Object handle = toNMS.invoke(null, particle, bukkitData); + return new WrappedParticle<>(handle, particle, data); + } +}