line endings

This commit is contained in:
Dan Mulloy 2022-12-13 20:25:07 -05:00
parent 574c34ad5c
commit 92b604506f
No known key found for this signature in database
GPG Key ID: BFACD592A5F0DFD6
3 changed files with 830 additions and 830 deletions

View File

@ -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<MinecraftVersion>, 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<MinecraftVersion>, 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());
}
}

View File

@ -1,220 +1,220 @@
/**
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol. Copyright (C) 2017 Dan Mulloy
* <p>
* 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.
* <p>
* 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.
* <p>
* 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 <T> Element type
* @return An ignore null converter
*/
public static <T> EquivalentConverter<T> ignoreNull(final EquivalentConverter<T> converter) {
return new EquivalentConverter<T>() {
@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<T> getSpecificType() {
return converter.getSpecificType();
}
};
}
/**
* Returns a converter that passes generic and specific values through without converting.
* @param clazz Element class
* @param <T> Element type
* @return A passthrough converter
*/
public static <T> EquivalentConverter<T> passthrough(final Class<T> clazz) {
return ignoreNull(new EquivalentConverter<T>() {
@Override
public T getSpecific(Object generic) {
return (T) generic;
}
@Override
public Object getGeneric(T specific) {
return specific;
}
@Override
public Class<T> 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 <T> Wrapper type
* @return A handle converter
*/
public static <T> EquivalentConverter<T> handle(final Function<T, Object> toHandle,
final Function<Object, T> fromHandle, final Class<T> specificType) {
return new EquivalentConverter<T>() {
@Override
public T getSpecific(Object generic) {
return fromHandle.apply(generic);
}
@Override
public Object getGeneric(T specific) {
return toHandle.apply(specific);
}
@Override
public Class<T> 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 <T> Generic type
* @return An array converter
*/
public static <T> EquivalentConverter<T[]> array(final Class<?> nmsClass, final EquivalentConverter<T> converter) {
return new EquivalentConverter<T[]>() {
@Override
public T[] getSpecific(Object generic) {
Object[] array = (Object[]) generic;
Class<T[]> 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<T[]> getSpecificType() {
return (Class<T[]>) MinecraftReflection.getArrayClass(converter.getSpecificType());
}
};
}
public static <T> EquivalentConverter<Optional<T>> optional(final EquivalentConverter<T> converter) {
return new EquivalentConverter<Optional<T>>() {
@Override
public Object getGeneric(Optional<T> specific) {
return specific.map(converter::getGeneric);
}
@Override
public Optional<T> getSpecific(Object generic) {
Optional<Object> optional = (Optional<Object>) generic;
return optional.map(converter::getSpecific);
}
@Override
public Class<Optional<T>> getSpecificType() {
return (Class<Optional<T>>) Optional.empty().getClass();
}
};
}
public static <T, C extends Collection<T>> EquivalentConverter<C> collection(
final EquivalentConverter<T> elementConverter,
final Function<Collection<Object>, C> genericToSpecificCollectionFactory,
final Function<C, Collection<?>> specificToGenericCollectionFactory
) {
return ignoreNull(new EquivalentConverter<C>() {
@Override
public Object getGeneric(C specific) {
// generics are very cool, thank you java
Collection<Object> targetCollection = (Collection<Object>) 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<Object> sourceCollection = (Collection<Object>) 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<C> getSpecificType() {
return (Class) Collection.class;
}
});
}
}
/**
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol. Copyright (C) 2017 Dan Mulloy
* <p>
* 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.
* <p>
* 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.
* <p>
* 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 <T> Element type
* @return An ignore null converter
*/
public static <T> EquivalentConverter<T> ignoreNull(final EquivalentConverter<T> converter) {
return new EquivalentConverter<T>() {
@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<T> getSpecificType() {
return converter.getSpecificType();
}
};
}
/**
* Returns a converter that passes generic and specific values through without converting.
* @param clazz Element class
* @param <T> Element type
* @return A passthrough converter
*/
public static <T> EquivalentConverter<T> passthrough(final Class<T> clazz) {
return ignoreNull(new EquivalentConverter<T>() {
@Override
public T getSpecific(Object generic) {
return (T) generic;
}
@Override
public Object getGeneric(T specific) {
return specific;
}
@Override
public Class<T> 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 <T> Wrapper type
* @return A handle converter
*/
public static <T> EquivalentConverter<T> handle(final Function<T, Object> toHandle,
final Function<Object, T> fromHandle, final Class<T> specificType) {
return new EquivalentConverter<T>() {
@Override
public T getSpecific(Object generic) {
return fromHandle.apply(generic);
}
@Override
public Object getGeneric(T specific) {
return toHandle.apply(specific);
}
@Override
public Class<T> 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 <T> Generic type
* @return An array converter
*/
public static <T> EquivalentConverter<T[]> array(final Class<?> nmsClass, final EquivalentConverter<T> converter) {
return new EquivalentConverter<T[]>() {
@Override
public T[] getSpecific(Object generic) {
Object[] array = (Object[]) generic;
Class<T[]> 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<T[]> getSpecificType() {
return (Class<T[]>) MinecraftReflection.getArrayClass(converter.getSpecificType());
}
};
}
public static <T> EquivalentConverter<Optional<T>> optional(final EquivalentConverter<T> converter) {
return new EquivalentConverter<Optional<T>>() {
@Override
public Object getGeneric(Optional<T> specific) {
return specific.map(converter::getGeneric);
}
@Override
public Optional<T> getSpecific(Object generic) {
Optional<Object> optional = (Optional<Object>) generic;
return optional.map(converter::getSpecific);
}
@Override
public Class<Optional<T>> getSpecificType() {
return (Class<Optional<T>>) Optional.empty().getClass();
}
};
}
public static <T, C extends Collection<T>> EquivalentConverter<C> collection(
final EquivalentConverter<T> elementConverter,
final Function<Collection<Object>, C> genericToSpecificCollectionFactory,
final Function<C, Collection<?>> specificToGenericCollectionFactory
) {
return ignoreNull(new EquivalentConverter<C>() {
@Override
public Object getGeneric(C specific) {
// generics are very cool, thank you java
Collection<Object> targetCollection = (Collection<Object>) 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<Object> sourceCollection = (Collection<Object>) 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<C> getSpecificType() {
return (Class) Collection.class;
}
});
}
}

View File

@ -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<T> {
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<Object> 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<Object> modifier = new StructureModifier<>(handle.getClass()).withTarget(handle);
Object rgb = modifier.withType(VECTOR_3FA).read(0);
StructureModifier<Object> rgbModifier = new StructureModifier<>(VECTOR_3FA).withTarget(rgb);
r = (int) (rgbModifier.<Float>withType(float.class).read(0) * 255);
g = (int) (rgbModifier.<Float>withType(float.class).read(1) * 255);
b = (int) (rgbModifier.<Float>withType(float.class).read(2) * 255);
alpha = (float) modifier.withType(float.class).read(0);
} else {
StructureModifier<Float> 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 <T> WrappedParticle<T> 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<T> {
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<Object> 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<Object> modifier = new StructureModifier<>(handle.getClass()).withTarget(handle);
Object rgb = modifier.withType(VECTOR_3FA).read(0);
StructureModifier<Object> rgbModifier = new StructureModifier<>(VECTOR_3FA).withTarget(rgb);
r = (int) (rgbModifier.<Float>withType(float.class).read(0) * 255);
g = (int) (rgbModifier.<Float>withType(float.class).read(1) * 255);
b = (int) (rgbModifier.<Float>withType(float.class).read(2) * 255);
alpha = (float) modifier.withType(float.class).read(0);
} else {
StructureModifier<Float> 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 <T> WrappedParticle<T> 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);
}
}