Use the MCPC JAR remapper when loading classes. Fixes #11
This will allow plugins to use MinecraftReflection.getMinecraftClass() in both CraftBukkit and MCPC.
Dieser Commit ist enthalten in:
Ursprung
8c8ca3746b
Commit
4be582ef87
@ -19,6 +19,7 @@ package com.comphenix.protocol.utility;
|
|||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import com.google.common.base.Strings;
|
||||||
import com.google.common.collect.Maps;
|
import com.google.common.collect.Maps;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -27,12 +28,19 @@ import com.google.common.collect.Maps;
|
|||||||
* @author Kristian
|
* @author Kristian
|
||||||
*/
|
*/
|
||||||
class CachedPackage {
|
class CachedPackage {
|
||||||
private Map<String, Class<?>> cache;
|
private final Map<String, Class<?>> cache;
|
||||||
private String packageName;
|
private final String packageName;
|
||||||
|
private final ClassSource source;
|
||||||
|
|
||||||
public CachedPackage(String packageName) {
|
/**
|
||||||
|
* Construct a new cached package.
|
||||||
|
* @param packageName - the name of the current package.
|
||||||
|
* @param source - the class source.
|
||||||
|
*/
|
||||||
|
public CachedPackage(String packageName, ClassSource source) {
|
||||||
this.packageName = packageName;
|
this.packageName = packageName;
|
||||||
this.cache = Maps.newConcurrentMap();
|
this.cache = Maps.newConcurrentMap();
|
||||||
|
this.source = source;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -57,11 +65,9 @@ class CachedPackage {
|
|||||||
// Concurrency is not a problem - we don't care if we look up a class twice
|
// Concurrency is not a problem - we don't care if we look up a class twice
|
||||||
if (result == null) {
|
if (result == null) {
|
||||||
// Look up the class dynamically
|
// Look up the class dynamically
|
||||||
result = CachedPackage.class.getClassLoader().
|
result = source.loadClass(combine(packageName, className));
|
||||||
loadClass(combine(packageName, className));
|
|
||||||
cache.put(className, result);
|
cache.put(className, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
} catch (ClassNotFoundException e) {
|
} catch (ClassNotFoundException e) {
|
||||||
@ -75,9 +81,11 @@ class CachedPackage {
|
|||||||
* @param className - the class name.
|
* @param className - the class name.
|
||||||
* @return We full class path.
|
* @return We full class path.
|
||||||
*/
|
*/
|
||||||
private String combine(String packageName, String className) {
|
public static String combine(String packageName, String className) {
|
||||||
if (packageName.length() == 0)
|
if (Strings.isNullOrEmpty(packageName))
|
||||||
return className;
|
return className;
|
||||||
|
if (Strings.isNullOrEmpty(className))
|
||||||
|
return packageName;
|
||||||
return packageName + "." + className;
|
return packageName + "." + className;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,37 @@
|
|||||||
|
package com.comphenix.protocol.utility;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents an abstract class loader that can only retrieve classes by their canonical name.
|
||||||
|
* @author Kristian
|
||||||
|
*/
|
||||||
|
abstract class ClassSource {
|
||||||
|
/**
|
||||||
|
* Construct a class source from the current class loader.
|
||||||
|
* @return A package source.
|
||||||
|
*/
|
||||||
|
public static ClassSource fromClassLoader() {
|
||||||
|
return fromClassLoader(ClassSource.class.getClassLoader());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a class source from the given class loader.
|
||||||
|
* @param loader - the class loader.
|
||||||
|
* @return The corresponding package source.
|
||||||
|
*/
|
||||||
|
public static ClassSource fromClassLoader(final ClassLoader loader) {
|
||||||
|
return new ClassSource() {
|
||||||
|
@Override
|
||||||
|
public Class<?> loadClass(String canonicalName) throws ClassNotFoundException {
|
||||||
|
return loader.loadClass(canonicalName);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve a class by name.
|
||||||
|
* @param canonicalName - the full canonical name of the class.
|
||||||
|
* @return The corresponding class
|
||||||
|
* @throws ClassNotFoundException If the class could not be found.
|
||||||
|
*/
|
||||||
|
public abstract Class<?> loadClass(String canonicalName) throws ClassNotFoundException;
|
||||||
|
}
|
@ -30,6 +30,7 @@ import java.util.HashSet;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
@ -42,6 +43,10 @@ import org.bukkit.Bukkit;
|
|||||||
import org.bukkit.Server;
|
import org.bukkit.Server;
|
||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
|
||||||
|
import com.comphenix.protocol.ProtocolLibrary;
|
||||||
|
import com.comphenix.protocol.error.ErrorReporter;
|
||||||
|
import com.comphenix.protocol.error.Report;
|
||||||
|
import com.comphenix.protocol.error.ReportType;
|
||||||
import com.comphenix.protocol.injector.BukkitUnwrapper;
|
import com.comphenix.protocol.injector.BukkitUnwrapper;
|
||||||
import com.comphenix.protocol.injector.packet.PacketRegistry;
|
import com.comphenix.protocol.injector.packet.PacketRegistry;
|
||||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||||
@ -52,6 +57,8 @@ import com.comphenix.protocol.reflect.fuzzy.FuzzyClassContract;
|
|||||||
import com.comphenix.protocol.reflect.fuzzy.FuzzyFieldContract;
|
import com.comphenix.protocol.reflect.fuzzy.FuzzyFieldContract;
|
||||||
import com.comphenix.protocol.reflect.fuzzy.FuzzyMatchers;
|
import com.comphenix.protocol.reflect.fuzzy.FuzzyMatchers;
|
||||||
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
|
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
|
||||||
|
import com.comphenix.protocol.utility.RemappedClassSource.RemapperUnavaibleException;
|
||||||
|
import com.comphenix.protocol.utility.RemappedClassSource.RemapperUnavaibleException.Reason;
|
||||||
import com.comphenix.protocol.wrappers.WrappedDataWatcher;
|
import com.comphenix.protocol.wrappers.WrappedDataWatcher;
|
||||||
import com.comphenix.protocol.wrappers.nbt.NbtFactory;
|
import com.comphenix.protocol.wrappers.nbt.NbtFactory;
|
||||||
import com.comphenix.protocol.wrappers.nbt.NbtType;
|
import com.comphenix.protocol.wrappers.nbt.NbtType;
|
||||||
@ -63,6 +70,9 @@ import com.google.common.base.Joiner;
|
|||||||
* @author Kristian
|
* @author Kristian
|
||||||
*/
|
*/
|
||||||
public class MinecraftReflection {
|
public class MinecraftReflection {
|
||||||
|
public static final ReportType REPORT_CANNOT_FIND_MCPC_REMAPPER = new ReportType("Cannot find MCPC remapper.");
|
||||||
|
public static final ReportType REPORT_CANNOT_LOAD_CPC_REMAPPER = new ReportType("Unable to load MCPC remapper.");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Regular expression that matches a Minecraft object.
|
* Regular expression that matches a Minecraft object.
|
||||||
* <p>
|
* <p>
|
||||||
@ -76,11 +86,22 @@ public class MinecraftReflection {
|
|||||||
*/
|
*/
|
||||||
private static String DYNAMIC_PACKAGE_MATCHER = null;
|
private static String DYNAMIC_PACKAGE_MATCHER = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Entity package in Forge 1.5.2
|
||||||
|
*/
|
||||||
|
private static final String FORGE_ENTITY_PACKAGE = "net.minecraft.entity";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The package name of all the classes that belongs to the native code in Minecraft.
|
* The package name of all the classes that belongs to the native code in Minecraft.
|
||||||
*/
|
*/
|
||||||
private static String MINECRAFT_PREFIX_PACKAGE = "net.minecraft.server";
|
private static String MINECRAFT_PREFIX_PACKAGE = "net.minecraft.server";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a regular expression that will match the version string in a package:
|
||||||
|
* org.bukkit.craftbukkit.v1_6_R2 -> v1_6_R2
|
||||||
|
*/
|
||||||
|
private static final Pattern PACKAGE_VERSION_MATCHER = Pattern.compile(".*\\.(v\\d+_\\d+_\\w*\\d+)");
|
||||||
|
|
||||||
private static String MINECRAFT_FULL_PACKAGE = null;
|
private static String MINECRAFT_FULL_PACKAGE = null;
|
||||||
private static String CRAFTBUKKIT_PACKAGE = null;
|
private static String CRAFTBUKKIT_PACKAGE = null;
|
||||||
|
|
||||||
@ -99,9 +120,15 @@ public class MinecraftReflection {
|
|||||||
private static Method craftBukkitMethod;
|
private static Method craftBukkitMethod;
|
||||||
private static boolean craftItemStackFailed;
|
private static boolean craftItemStackFailed;
|
||||||
|
|
||||||
|
// The NMS version
|
||||||
|
private static String packageVersion;
|
||||||
|
|
||||||
// net.minecraft.server
|
// net.minecraft.server
|
||||||
private static Class<?> itemStackArrayClass;
|
private static Class<?> itemStackArrayClass;
|
||||||
|
|
||||||
|
// The current class source
|
||||||
|
private static ClassSource classSource;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether or not we're currently initializing the reflection handler.
|
* Whether or not we're currently initializing the reflection handler.
|
||||||
*/
|
*/
|
||||||
@ -152,6 +179,12 @@ public class MinecraftReflection {
|
|||||||
Class<?> craftClass = craftServer.getClass();
|
Class<?> craftClass = craftServer.getClass();
|
||||||
CRAFTBUKKIT_PACKAGE = getPackage(craftClass.getCanonicalName());
|
CRAFTBUKKIT_PACKAGE = getPackage(craftClass.getCanonicalName());
|
||||||
|
|
||||||
|
// Parse the package version
|
||||||
|
Matcher packageMatcher = PACKAGE_VERSION_MATCHER.matcher(CRAFTBUKKIT_PACKAGE);
|
||||||
|
if (packageMatcher.matches()) {
|
||||||
|
packageVersion = packageMatcher.group(1);
|
||||||
|
}
|
||||||
|
|
||||||
// Libigot patch
|
// Libigot patch
|
||||||
handleLibigot();
|
handleLibigot();
|
||||||
|
|
||||||
@ -161,12 +194,18 @@ public class MinecraftReflection {
|
|||||||
|
|
||||||
MINECRAFT_FULL_PACKAGE = getPackage(getHandle.getReturnType().getCanonicalName());
|
MINECRAFT_FULL_PACKAGE = getPackage(getHandle.getReturnType().getCanonicalName());
|
||||||
|
|
||||||
// Pretty important invariant
|
// Pretty important invariantt
|
||||||
if (!MINECRAFT_FULL_PACKAGE.startsWith(MINECRAFT_PREFIX_PACKAGE)) {
|
if (!MINECRAFT_FULL_PACKAGE.startsWith(MINECRAFT_PREFIX_PACKAGE)) {
|
||||||
|
// See if we got the Forge entity package
|
||||||
|
if (MINECRAFT_FULL_PACKAGE.equals(FORGE_ENTITY_PACKAGE)) {
|
||||||
|
// USe the standard NMS versioned package
|
||||||
|
MINECRAFT_FULL_PACKAGE = CachedPackage.combine(MINECRAFT_PREFIX_PACKAGE, packageVersion);
|
||||||
|
} else {
|
||||||
// Assume they're the same instead
|
// Assume they're the same instead
|
||||||
MINECRAFT_PREFIX_PACKAGE = MINECRAFT_FULL_PACKAGE;
|
MINECRAFT_PREFIX_PACKAGE = MINECRAFT_FULL_PACKAGE;
|
||||||
|
}
|
||||||
|
|
||||||
// The package is usualy flat, so go with that assumtion
|
// The package is usualy flat, so go with that assumption
|
||||||
String matcher =
|
String matcher =
|
||||||
(MINECRAFT_PREFIX_PACKAGE.length() > 0 ?
|
(MINECRAFT_PREFIX_PACKAGE.length() > 0 ?
|
||||||
Pattern.quote(MINECRAFT_PREFIX_PACKAGE + ".") : "") + "\\w+";
|
Pattern.quote(MINECRAFT_PREFIX_PACKAGE + ".") : "") + "\\w+";
|
||||||
@ -195,6 +234,15 @@ public class MinecraftReflection {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the package version of the underlying CraftBukkit server.
|
||||||
|
* @return The package version, or NULL if not applicable (before 1.4.6).
|
||||||
|
*/
|
||||||
|
public static String getPackageVersion() {
|
||||||
|
getMinecraftPackage();
|
||||||
|
return packageVersion;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update the dynamic package matcher.
|
* Update the dynamic package matcher.
|
||||||
* @param regex - the Minecraft package regex.
|
* @param regex - the Minecraft package regex.
|
||||||
@ -1260,7 +1308,7 @@ public class MinecraftReflection {
|
|||||||
@SuppressWarnings("rawtypes")
|
@SuppressWarnings("rawtypes")
|
||||||
public static Class getCraftBukkitClass(String className) {
|
public static Class getCraftBukkitClass(String className) {
|
||||||
if (craftbukkitPackage == null)
|
if (craftbukkitPackage == null)
|
||||||
craftbukkitPackage = new CachedPackage(getCraftBukkitPackage());
|
craftbukkitPackage = new CachedPackage(getCraftBukkitPackage(), getClassSource());
|
||||||
return craftbukkitPackage.getPackageClass(className);
|
return craftbukkitPackage.getPackageClass(className);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1272,7 +1320,7 @@ public class MinecraftReflection {
|
|||||||
*/
|
*/
|
||||||
public static Class<?> getMinecraftClass(String className) {
|
public static Class<?> getMinecraftClass(String className) {
|
||||||
if (minecraftPackage == null)
|
if (minecraftPackage == null)
|
||||||
minecraftPackage = new CachedPackage(getMinecraftPackage());
|
minecraftPackage = new CachedPackage(getMinecraftPackage(), getClassSource());
|
||||||
return minecraftPackage.getPackageClass(className);
|
return minecraftPackage.getPackageClass(className);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1284,11 +1332,36 @@ public class MinecraftReflection {
|
|||||||
*/
|
*/
|
||||||
private static Class<?> setMinecraftClass(String className, Class<?> clazz) {
|
private static Class<?> setMinecraftClass(String className, Class<?> clazz) {
|
||||||
if (minecraftPackage == null)
|
if (minecraftPackage == null)
|
||||||
minecraftPackage = new CachedPackage(getMinecraftPackage());
|
minecraftPackage = new CachedPackage(getMinecraftPackage(), getClassSource());
|
||||||
minecraftPackage.setPackageClass(className, clazz);
|
minecraftPackage.setPackageClass(className, clazz);
|
||||||
return clazz;
|
return clazz;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the current class source.
|
||||||
|
* @return The class source.
|
||||||
|
*/
|
||||||
|
private static ClassSource getClassSource() {
|
||||||
|
ErrorReporter reporter = ProtocolLibrary.getErrorReporter();
|
||||||
|
|
||||||
|
// Lazy pattern again
|
||||||
|
if (classSource == null) {
|
||||||
|
// Attempt to use MCPC
|
||||||
|
try {
|
||||||
|
return classSource = new RemappedClassSource().initialize();
|
||||||
|
} catch (RemapperUnavaibleException e) {
|
||||||
|
if (e.getReason() != Reason.MCPC_NOT_PRESENT)
|
||||||
|
reporter.reportWarning(MinecraftReflection.class, Report.newBuilder(REPORT_CANNOT_FIND_MCPC_REMAPPER));
|
||||||
|
} catch (Exception e) {
|
||||||
|
reporter.reportWarning(MinecraftReflection.class, Report.newBuilder(REPORT_CANNOT_LOAD_CPC_REMAPPER));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Just use the default class loader
|
||||||
|
classSource = ClassSource.fromClassLoader();
|
||||||
|
}
|
||||||
|
return classSource;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the first class that matches a specified Minecraft name.
|
* Retrieve the first class that matches a specified Minecraft name.
|
||||||
* @param className - the specific Minecraft class.
|
* @param className - the specific Minecraft class.
|
||||||
|
@ -0,0 +1,147 @@
|
|||||||
|
package com.comphenix.protocol.utility;
|
||||||
|
|
||||||
|
// Thanks to Bergerkiller for his excellent hack. :D
|
||||||
|
|
||||||
|
// Copyright (C) 2013 bergerkiller
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
// this software and associated documentation files (the "Software"), to deal in
|
||||||
|
// the Software without restriction, including without limitation the rights to use,
|
||||||
|
// copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||||
|
// Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
// subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
|
||||||
|
import com.comphenix.protocol.reflect.FieldUtils;
|
||||||
|
import com.comphenix.protocol.reflect.MethodUtils;
|
||||||
|
import com.comphenix.protocol.utility.RemappedClassSource.RemapperUnavaibleException.Reason;
|
||||||
|
|
||||||
|
class RemappedClassSource extends ClassSource {
|
||||||
|
private Object classRemapper;
|
||||||
|
private Method mapType;
|
||||||
|
private ClassLoader loader;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a new remapped class source using the default class loader.
|
||||||
|
*/
|
||||||
|
public RemappedClassSource() {
|
||||||
|
this(RemappedClassSource.class.getClassLoader());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a new renampped class source with the provided class loader.
|
||||||
|
* @param loader - the class loader.
|
||||||
|
*/
|
||||||
|
public RemappedClassSource(ClassLoader loader) {
|
||||||
|
this.loader = loader;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempt to load the MCPC remapper.
|
||||||
|
* @return TRUE if we succeeded, FALSE otherwise.
|
||||||
|
* @throws RemapperUnavaibleException If the remapper is not present.
|
||||||
|
*/
|
||||||
|
public RemappedClassSource initialize() {
|
||||||
|
try {
|
||||||
|
if (Bukkit.getServer() == null || !Bukkit.getServer().getVersion().contains("MCPC-Plus")) {
|
||||||
|
throw new RemapperUnavaibleException(Reason.MCPC_NOT_PRESENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Obtain the Class remapper used by MCPC+
|
||||||
|
this.classRemapper = FieldUtils.readField(getClass().getClassLoader(), "remapper", true);
|
||||||
|
|
||||||
|
if (this.classRemapper == null) {
|
||||||
|
throw new RemapperUnavaibleException(Reason.REMAPPER_DISABLED);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize some fields and methods used by the Jar Remapper
|
||||||
|
Class<?> renamerClazz = classRemapper.getClass();
|
||||||
|
|
||||||
|
this.mapType = MethodUtils.getAccessibleMethod(renamerClazz, "map",
|
||||||
|
new Class<?>[] { String.class });
|
||||||
|
|
||||||
|
return this;
|
||||||
|
|
||||||
|
} catch (RemapperUnavaibleException e) {
|
||||||
|
throw e;
|
||||||
|
} catch (Exception e) {
|
||||||
|
// Damn it
|
||||||
|
throw new RuntimeException("Cannot access MCPC remapper.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<?> loadClass(String canonicalName) throws ClassNotFoundException {
|
||||||
|
final String remapped = getClassName(canonicalName);
|
||||||
|
|
||||||
|
try {
|
||||||
|
return loader.loadClass(remapped);
|
||||||
|
} catch (ClassNotFoundException e) {
|
||||||
|
throw new ClassNotFoundException("Cannot find " + canonicalName + "(Remapped: " + remapped + ")");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the obfuscated class name given an unobfuscated canonical class name.
|
||||||
|
* @param path - the canonical class name.
|
||||||
|
* @return The obfuscated class name.
|
||||||
|
*/
|
||||||
|
private String getClassName(String path) {
|
||||||
|
try {
|
||||||
|
String remapped = (String) mapType.invoke(classRemapper, path.replace('.', '/'));
|
||||||
|
return remapped.replace('/', '.');
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException("Cannot remap class name.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class RemapperUnavaibleException extends RuntimeException {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
public enum Reason {
|
||||||
|
MCPC_NOT_PRESENT("The server is not running MCPC+"),
|
||||||
|
REMAPPER_DISABLED("Running an MCPC+ server but the remapper is unavailable. Please turn it on!");
|
||||||
|
|
||||||
|
private final String message;
|
||||||
|
|
||||||
|
private Reason(String message) {
|
||||||
|
this.message = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve a human-readable version of this reason.
|
||||||
|
* @return The human-readable verison.
|
||||||
|
*/
|
||||||
|
public String getMessage() {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final Reason reason;
|
||||||
|
|
||||||
|
public RemapperUnavaibleException(Reason reason) {
|
||||||
|
super(reason.getMessage());
|
||||||
|
this.reason = reason;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the reasont he remapper is unavailable.
|
||||||
|
* @return The reason.
|
||||||
|
*/
|
||||||
|
public Reason getReason() {
|
||||||
|
return reason;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
In neuem Issue referenzieren
Einen Benutzer sperren