Update for ProtocolLib 4.1.

This commit is contained in:
filoghost 2016-08-25 09:59:24 +02:00
parent 213dd85f4b
commit 2168bf4cfd
11 changed files with 435 additions and 259 deletions

View File

@ -47,6 +47,9 @@ public class HolographicDisplays extends JavaPlugin {
// Since 1.9 there is a different offset for the nametag. // Since 1.9 there is a different offset for the nametag.
private static boolean is19orGreater; private static boolean is19orGreater;
// Since 1.10 there is a difference in the entity metadata packet index for items.
private static boolean is110orGreater;
// The new version found by the updater, null if there is no new version. // The new version found by the updater, null if there is no new version.
private static String newVersion; private static String newVersion;
@ -137,6 +140,7 @@ public class HolographicDisplays extends JavaPlugin {
} else if ("v1_10_R1".equals(version)) { } else if ("v1_10_R1".equals(version)) {
is18orGreater = true; is18orGreater = true;
is19orGreater = true; is19orGreater = true;
is110orGreater = true;
nmsManager = new com.gmail.filoghost.holographicdisplays.nms.v1_10_R1.NmsManagerImpl(); nmsManager = new com.gmail.filoghost.holographicdisplays.nms.v1_10_R1.NmsManagerImpl();
} else { } else {
printWarnAndDisable( printWarnAndDisable(
@ -177,14 +181,18 @@ public class HolographicDisplays extends JavaPlugin {
if (Bukkit.getPluginManager().isPluginEnabled("ProtocolLib")) { if (Bukkit.getPluginManager().isPluginEnabled("ProtocolLib")) {
ProtocolLibHook protocolLibHook; ProtocolLibHook protocolLibHook;
if (is19orGreater) { if (VersionUtils.classExists("com.comphenix.protocol.wrappers.WrappedDataWatcher$WrappedDataWatcherObject")) {
protocolLibHook = new com.gmail.filoghost.holographicdisplays.bridge.protocollib.current.ProtocolLibHookImpl(); // Only the new version contains this class
getLogger().info("Found ProtocolLib, using new version.");
protocolLibHook = new com.gmail.filoghost.holographicdisplays.bridge.protocollib.current.ProtocolLibHookImpl(is19orGreater, is110orGreater);
} else { } else {
protocolLibHook = new com.gmail.filoghost.holographicdisplays.bridge.protocollib.pre1_9.ProtocolLibHookImpl(is18orGreater); getLogger().info("Found ProtocolLib, using old version.");
protocolLibHook = new com.gmail.filoghost.holographicdisplays.bridge.protocollib.old.ProtocolLibHookImpl(is18orGreater);
} }
if (protocolLibHook.hook(this, nmsManager)) { if (protocolLibHook.hook(this, nmsManager)) {
HolographicDisplays.protocolLibHook = protocolLibHook; HolographicDisplays.protocolLibHook = protocolLibHook;
getLogger().info("Enabled player relative placeholders with ProtocolLib.");
} }
} }
} catch (Exception ex) { } catch (Exception ex) {
@ -279,6 +287,10 @@ public class HolographicDisplays extends JavaPlugin {
return is19orGreater; return is19orGreater;
} }
public static boolean is110orGreater() {
return is19orGreater;
}
private static void printWarnAndDisable(String... messages) { private static void printWarnAndDisable(String... messages) {
StringBuffer buffer = new StringBuffer("\n "); StringBuffer buffer = new StringBuffer("\n ");
for (String message : messages) { for (String message : messages) {

View File

@ -9,7 +9,6 @@ import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType; import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin; import org.bukkit.plugin.Plugin;
import com.comphenix.protocol.PacketType; import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.ProtocolLibrary; import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.events.ListenerPriority; import com.comphenix.protocol.events.ListenerPriority;
@ -36,22 +35,33 @@ import com.gmail.filoghost.holographicdisplays.object.line.CraftTouchableLine;
import com.gmail.filoghost.holographicdisplays.util.Utils; import com.gmail.filoghost.holographicdisplays.util.Utils;
import com.google.common.base.Optional; import com.google.common.base.Optional;
/**
* This is for the ProtocolLib versions containing the WrappedDataWatcher.WrappedDataWatcherObject class.
*
* These versions are only used from 1.8, there is no need to handle 1.7 entities.
*/
public class ProtocolLibHookImpl implements ProtocolLibHook { public class ProtocolLibHookImpl implements ProtocolLibHook {
private boolean is1_9orGreater;
private boolean is110orGreater;
private NMSManager nmsManager; private NMSManager nmsManager;
private boolean initSerializers;
private Serializer private Serializer
itemSerializer, itemSerializer,
intSerializer, intSerializer,
byteSerializer; byteSerializer;
public ProtocolLibHookImpl(boolean is19orGreater, boolean is110orGreater) {
this.is1_9orGreater = is19orGreater;
this.is110orGreater = is110orGreater;
}
@Override @Override
public boolean hook(Plugin plugin, NMSManager nmsManager) { public boolean hook(Plugin plugin, NMSManager nmsManager) {
this.nmsManager = nmsManager; this.nmsManager = nmsManager;
if (Bukkit.getPluginManager().isPluginEnabled("ProtocolLib")) {
String version = Bukkit.getPluginManager().getPlugin("ProtocolLib").getDescription().getVersion(); String version = Bukkit.getPluginManager().getPlugin("ProtocolLib").getDescription().getVersion();
if (version.matches(Pattern.quote("3.7-SNAPSHOT") + ".+")) { if (version.matches(Pattern.quote("3.7-SNAPSHOT") + ".+")) {
Bukkit.getConsoleSender().sendMessage( Bukkit.getConsoleSender().sendMessage(
@ -62,12 +72,6 @@ public class ProtocolLibHookImpl implements ProtocolLibHook {
return false; return false;
} }
itemSerializer = Registry.get(MinecraftReflection.getItemStackClass());
intSerializer = Registry.get(Integer.class);
byteSerializer = Registry.get(Byte.class);
plugin.getLogger().info("Found ProtocolLib, adding support for player relative variables.");
AdapterParameteters params = PacketAdapter AdapterParameteters params = PacketAdapter
.params() .params()
.plugin(plugin) .plugin(plugin)
@ -167,7 +171,7 @@ public class ProtocolLibHookImpl implements ProtocolLibHook {
for (int i = 0; i < dataWatcherValues.size(); i++) { for (int i = 0; i < dataWatcherValues.size(); i++) {
WrappedWatchableObject watchableObject = dataWatcherValues.get(i); WrappedWatchableObject watchableObject = dataWatcherValues.get(i);
if (watchableObject.getIndex() == 2) { if (watchableObject.getIndex() == 2) { // Custom name index
Object customNameObject = watchableObject.getValue(); Object customNameObject = watchableObject.getValue();
if (!(customNameObject instanceof String)) { if (!(customNameObject instanceof String)) {
@ -176,9 +180,17 @@ public class ProtocolLibHookImpl implements ProtocolLibHook {
String customName = (String) customNameObject; String customName = (String) customNameObject;
if (customName.contains("{player}") || customName.contains("{displayname}")) { if (customName.contains("{player}") || customName.contains("{displayname}")) {
String replacement = customName.replace("{player}", player.getName()).replace("{displayname}", player.getDisplayName()); String replacement = customName.replace("{player}", player.getName()).replace("{displayname}", player.getDisplayName());
dataWatcherValues.set(i, new WrappedWatchableObject(watchableObject.getWatcherObject(), replacement));
WrappedWatchableObject newWatchableObject;
if (is1_9orGreater) {
// The other constructor does not work in 1.9+.
newWatchableObject = new WrappedWatchableObject(watchableObject.getWatcherObject(), replacement);
} else {
newWatchableObject = new WrappedWatchableObject(watchableObject.getIndex(), replacement);
}
dataWatcherValues.set(i, newWatchableObject);
PacketContainer clone = packet.shallowClone(); PacketContainer clone = packet.shallowClone();
clone.getWatchableCollectionModifier().write(0, dataWatcherValues); clone.getWatchableCollectionModifier().write(0, dataWatcherValues);
event.setPacket(clone); event.setPacket(clone);
@ -193,9 +205,6 @@ public class ProtocolLibHookImpl implements ProtocolLibHook {
return true; return true;
} }
return false;
}
@Override @Override
public void sendDestroyEntitiesPacket(Player player, CraftHologram hologram) { public void sendDestroyEntitiesPacket(Player player, CraftHologram hologram) {
@ -240,17 +249,28 @@ public class ProtocolLibHookImpl implements ProtocolLibHook {
AbstractPacket vehiclePacket = new WrapperPlayServerSpawnEntityLiving(itemLine.getNmsVehicle().getBukkitEntityNMS()); AbstractPacket vehiclePacket = new WrapperPlayServerSpawnEntityLiving(itemLine.getNmsVehicle().getBukkitEntityNMS());
vehiclePacket.sendPacket(player); vehiclePacket.sendPacket(player);
WrapperPlayServerMount attachPacket = new WrapperPlayServerMount(); AbstractPacket attachPacket = getAttachPacket(itemLine.getNmsVehicle().getIdNMS(), itemLine.getNmsItem().getIdNMS());
attachPacket.setVehicleId(itemLine.getNmsVehicle().getIdNMS());
attachPacket.setPassengers(new int[] {itemLine.getNmsItem().getIdNMS()});
attachPacket.sendPacket(player); attachPacket.sendPacket(player);
WrapperPlayServerEntityMetadata itemDataPacket = new WrapperPlayServerEntityMetadata(); WrapperPlayServerEntityMetadata itemDataPacket = new WrapperPlayServerEntityMetadata();
WrappedDataWatcher dataWatcher = new WrappedDataWatcher(); WrappedDataWatcher dataWatcher = new WrappedDataWatcher();
dataWatcher.setObject(new WrappedDataWatcherObject(5, itemSerializer), Optional.of(itemLine.getNmsItem().getRawItemStack())); if (is1_9orGreater) {
if (!initSerializers) {
itemSerializer = Registry.get(MinecraftReflection.getItemStackClass());
intSerializer = Registry.get(Integer.class);
byteSerializer = Registry.get(Byte.class);
initSerializers = true;
}
dataWatcher.setObject(new WrappedDataWatcherObject(is110orGreater ? 6 : 5, itemSerializer), Optional.of(itemLine.getNmsItem().getRawItemStack()));
dataWatcher.setObject(new WrappedDataWatcherObject(1, intSerializer), 300); dataWatcher.setObject(new WrappedDataWatcherObject(1, intSerializer), 300);
dataWatcher.setObject(new WrappedDataWatcherObject(0, byteSerializer), (byte) 0); dataWatcher.setObject(new WrappedDataWatcherObject(0, byteSerializer), (byte) 0);
} else {
dataWatcher.setObject(10, itemLine.getNmsItem().getRawItemStack());
dataWatcher.setObject(1, 300);
dataWatcher.setObject(0, (byte) 0);
}
itemDataPacket.setEntityMetadata(dataWatcher.getWatchableObjects()); itemDataPacket.setEntityMetadata(dataWatcher.getWatchableObjects());
itemDataPacket.setEntityId(itemLine.getNmsItem().getIdNMS()); itemDataPacket.setEntityId(itemLine.getNmsItem().getIdNMS());
@ -272,9 +292,7 @@ public class ProtocolLibHookImpl implements ProtocolLibHook {
AbstractPacket slimePacket = new WrapperPlayServerSpawnEntityLiving(touchSlime.getNmsSlime().getBukkitEntityNMS()); AbstractPacket slimePacket = new WrapperPlayServerSpawnEntityLiving(touchSlime.getNmsSlime().getBukkitEntityNMS());
slimePacket.sendPacket(player); slimePacket.sendPacket(player);
WrapperPlayServerMount attachPacket = new WrapperPlayServerMount(); AbstractPacket attachPacket = getAttachPacket(touchSlime.getNmsVehicle().getIdNMS(), touchSlime.getNmsSlime().getIdNMS());
attachPacket.setVehicleId(touchSlime.getNmsVehicle().getIdNMS());
attachPacket.setPassengers(new int[] {touchSlime.getNmsSlime().getIdNMS()});
attachPacket.sendPacket(player); attachPacket.sendPacket(player);
} }
} }
@ -283,6 +301,21 @@ public class ProtocolLibHookImpl implements ProtocolLibHook {
} }
private AbstractPacket getAttachPacket(int vehicleId, int passengerId) {
if (is1_9orGreater) {
WrapperPlayServerMount attachPacket = new WrapperPlayServerMount();
attachPacket.setVehicleId(vehicleId);
attachPacket.setPassengers(new int[] {passengerId});
return attachPacket;
} else {
WrapperPlayServerAttachEntity attachPacket = new WrapperPlayServerAttachEntity();
attachPacket.setVehicleId(vehicleId);
attachPacket.setEntityId(passengerId);
return attachPacket;
}
}
private boolean isHologramType(EntityType type) { private boolean isHologramType(EntityType type) {
return type == EntityType.ARMOR_STAND || type == EntityType.DROPPED_ITEM || type == EntityType.SLIME; return type == EntityType.ARMOR_STAND || type == EntityType.DROPPED_ITEM || type == EntityType.SLIME;
} }

View File

@ -0,0 +1,122 @@
/*
* PacketWrapper - Contains wrappers for each packet in Minecraft.
* 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 Lesser 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.gmail.filoghost.holographicdisplays.bridge.protocollib.current;
import org.bukkit.World;
import org.bukkit.entity.Entity;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.events.PacketEvent;
public class WrapperPlayServerAttachEntity extends AbstractPacket {
public static final PacketType TYPE = PacketType.Play.Server.ATTACH_ENTITY;
public WrapperPlayServerAttachEntity() {
super(new PacketContainer(TYPE), TYPE);
handle.getModifier().writeDefaults();
}
public WrapperPlayServerAttachEntity(PacketContainer packet) {
super(packet, TYPE);
}
/**
* Retrieve whether or not the entity is leached onto the vehicle.
* @return TRUE if it is, FALSE otherwise.
*/
public boolean getLeached() {
return handle.getIntegers().read(0) != 0;
}
/**
* Set whether or not the entity is leached onto the vehicle.
* @param value - TRUE if it is leached, FALSE otherwise.
*/
public void setLeached(boolean value) {
handle.getIntegers().write(0, value ? 1 : 0);
}
/**
* Retrieve the player entity ID being attached.
* @return The current Entity ID
*/
public int getEntityId() {
return handle.getIntegers().read(1);
}
/**
* Set the player entity ID being attached.
* @param value - new value.
*/
public void setEntityId(int value) {
handle.getIntegers().write(1, value);
}
/**
* Retrieve the entity being attached.
* @param world - the current world of the entity.
* @return The entity.
*/
public Entity getEntity(World world) {
return handle.getEntityModifier(world).read(1);
}
/**
* Retrieve the entity being attached.
* @param event - the packet event.
* @return The entity.
*/
public Entity getEntity(PacketEvent event) {
return getEntity(event.getPlayer().getWorld());
}
/**
* Retrieve the vehicle entity ID attached to (-1 for unattaching).
* @return The current Vehicle ID
*/
public int getVehicleId() {
return handle.getIntegers().read(2);
}
/**
* Set the vehicle entity ID attached to (-1 for unattaching).
* @param value - new value.
*/
public void setVehicleId(int value) {
handle.getIntegers().write(2, value);
}
/**
* Retrieve the vehicle entity attached to (NULL for unattaching).
* @param world - the current world of the entity.
* @return The vehicle.
*/
public Entity getVehicle(World world) {
return handle.getEntityModifier(world).read(2);
}
/**
* Retrieve the vehicle entity attached to (NULL for unattaching).
* @param event - the packet event.
* @return The vehicle.
*/
public Entity getVehicle(PacketEvent event) {
return getVehicle(event.getPlayer().getWorld());
}
}

View File

@ -15,7 +15,7 @@
* 02111-1307 USA * 02111-1307 USA
*/ */
package com.gmail.filoghost.holographicdisplays.bridge.protocollib.pre1_9; package com.gmail.filoghost.holographicdisplays.bridge.protocollib.old;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;

View File

@ -1,9 +1,8 @@
package com.gmail.filoghost.holographicdisplays.bridge.protocollib.pre1_9; package com.gmail.filoghost.holographicdisplays.bridge.protocollib.old;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.util.List; import java.util.List;
import org.bukkit.Bukkit;
import org.bukkit.entity.Entity; import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType; import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
@ -19,7 +18,7 @@ import com.comphenix.protocol.wrappers.WrappedDataWatcher;
import com.comphenix.protocol.wrappers.WrappedWatchableObject; import com.comphenix.protocol.wrappers.WrappedWatchableObject;
import com.gmail.filoghost.holographicdisplays.HolographicDisplays; import com.gmail.filoghost.holographicdisplays.HolographicDisplays;
import com.gmail.filoghost.holographicdisplays.bridge.protocollib.ProtocolLibHook; import com.gmail.filoghost.holographicdisplays.bridge.protocollib.ProtocolLibHook;
import com.gmail.filoghost.holographicdisplays.bridge.protocollib.pre1_9.WrapperPlayServerSpawnEntity.ObjectTypes; import com.gmail.filoghost.holographicdisplays.bridge.protocollib.old.WrapperPlayServerSpawnEntity.ObjectTypes;
import com.gmail.filoghost.holographicdisplays.nms.interfaces.NMSManager; import com.gmail.filoghost.holographicdisplays.nms.interfaces.NMSManager;
import com.gmail.filoghost.holographicdisplays.nms.interfaces.entity.NMSEntityBase; import com.gmail.filoghost.holographicdisplays.nms.interfaces.entity.NMSEntityBase;
import com.gmail.filoghost.holographicdisplays.object.CraftHologram; import com.gmail.filoghost.holographicdisplays.object.CraftHologram;
@ -31,26 +30,28 @@ import com.gmail.filoghost.holographicdisplays.object.line.CraftTouchableLine;
import com.gmail.filoghost.holographicdisplays.util.Utils; import com.gmail.filoghost.holographicdisplays.util.Utils;
import com.gmail.filoghost.holographicdisplays.util.VersionUtils; import com.gmail.filoghost.holographicdisplays.util.VersionUtils;
/**
* This is for the ProtocolLib versions without the WrappedDataWatcher.WrappedDataWatcherObject class.
*
* These versions are only used for 1.7 and 1.8.
*/
public class ProtocolLibHookImpl implements ProtocolLibHook { public class ProtocolLibHookImpl implements ProtocolLibHook {
private boolean is1_8; private boolean is18orGreater;
private NMSManager nmsManager; private NMSManager nmsManager;
private int customNameWatcherIndex; private int customNameWatcherIndex;
public ProtocolLibHookImpl(boolean is1_8) { public ProtocolLibHookImpl(boolean is18orGreater) {
this.is1_8 = is1_8; this.is18orGreater = is18orGreater;
} }
@Override @Override
public boolean hook(Plugin plugin, NMSManager nmsManager) { public boolean hook(Plugin plugin, NMSManager nmsManager) {
this.nmsManager = nmsManager; this.nmsManager = nmsManager;
if (Bukkit.getPluginManager().isPluginEnabled("ProtocolLib")) { if (is18orGreater) {
plugin.getLogger().info("Found ProtocolLib, adding support for player relative variables.");
if (is1_8) {
customNameWatcherIndex = 2; customNameWatcherIndex = 2;
} else { } else {
customNameWatcherIndex = 10; customNameWatcherIndex = 10;
@ -58,7 +59,6 @@ public class ProtocolLibHookImpl implements ProtocolLibHook {
ProtocolLibrary.getProtocolManager().addPacketListener(new PacketAdapter(plugin, ListenerPriority.NORMAL, PacketType.Play.Server.SPAWN_ENTITY_LIVING, PacketType.Play.Server.SPAWN_ENTITY, PacketType.Play.Server.ENTITY_METADATA) { ProtocolLibrary.getProtocolManager().addPacketListener(new PacketAdapter(plugin, ListenerPriority.NORMAL, PacketType.Play.Server.SPAWN_ENTITY_LIVING, PacketType.Play.Server.SPAWN_ENTITY, PacketType.Play.Server.ENTITY_METADATA) {
@SuppressWarnings("deprecation")
@Override @Override
public void onPacketSending(PacketEvent event) { public void onPacketSending(PacketEvent event) {
@ -153,10 +153,11 @@ public class ProtocolLibHookImpl implements ProtocolLibHook {
List<WrappedWatchableObject> dataWatcherValues = entityMetadataPacket.getEntityMetadata(); List<WrappedWatchableObject> dataWatcherValues = entityMetadataPacket.getEntityMetadata();
for (int i = 0; i < dataWatcherValues.size(); i++) { for (int i = 0; i < dataWatcherValues.size(); i++) {
WrappedWatchableObject dataWatcherValue = dataWatcherValues.get(i);
if (dataWatcherValues.get(i).getIndex() == customNameWatcherIndex && dataWatcherValues.get(i).getValue() != null) { if (dataWatcherValue.getIndex() == customNameWatcherIndex && dataWatcherValue.getValue() != null) {
Object customNameObject = dataWatcherValues.get(i).getValue(); Object customNameObject = dataWatcherValue.getValue();
if (customNameObject == null || customNameObject instanceof String == false) { if (customNameObject == null || customNameObject instanceof String == false) {
return; return;
} }
@ -183,9 +184,6 @@ public class ProtocolLibHookImpl implements ProtocolLibHook {
return true; return true;
} }
return false;
}
public void sendDestroyEntitiesPacket(Player player, CraftHologram hologram) { public void sendDestroyEntitiesPacket(Player player, CraftHologram hologram) {
List<Integer> ids = Utils.newList(); List<Integer> ids = Utils.newList();
for (CraftHologramLine line : hologram.getLinesUnsafe()) { for (CraftHologramLine line : hologram.getLinesUnsafe()) {

View File

@ -15,7 +15,7 @@
* 02111-1307 USA * 02111-1307 USA
*/ */
package com.gmail.filoghost.holographicdisplays.bridge.protocollib.pre1_9; package com.gmail.filoghost.holographicdisplays.bridge.protocollib.old;
import org.bukkit.World; import org.bukkit.World;
import org.bukkit.entity.Entity; import org.bukkit.entity.Entity;

View File

@ -15,7 +15,7 @@
* 02111-1307 USA * 02111-1307 USA
*/ */
package com.gmail.filoghost.holographicdisplays.bridge.protocollib.pre1_9; package com.gmail.filoghost.holographicdisplays.bridge.protocollib.old;
import java.util.List; import java.util.List;

View File

@ -15,7 +15,7 @@
* 02111-1307 USA * 02111-1307 USA
*/ */
package com.gmail.filoghost.holographicdisplays.bridge.protocollib.pre1_9; package com.gmail.filoghost.holographicdisplays.bridge.protocollib.old;
import java.util.List; import java.util.List;

View File

@ -15,7 +15,7 @@
* 02111-1307 USA * 02111-1307 USA
*/ */
package com.gmail.filoghost.holographicdisplays.bridge.protocollib.pre1_9; package com.gmail.filoghost.holographicdisplays.bridge.protocollib.old;
import org.bukkit.World; import org.bukkit.World;
import org.bukkit.entity.Entity; import org.bukkit.entity.Entity;

View File

@ -15,7 +15,7 @@
* 02111-1307 USA * 02111-1307 USA
*/ */
package com.gmail.filoghost.holographicdisplays.bridge.protocollib.pre1_9; package com.gmail.filoghost.holographicdisplays.bridge.protocollib.old;
import org.bukkit.World; import org.bukkit.World;
import org.bukkit.entity.Entity; import org.bukkit.entity.Entity;

View File

@ -85,4 +85,15 @@ public class VersionUtils {
return Collections.emptyList(); return Collections.emptyList();
} }
} }
public static boolean classExists(String className) {
try {
Class.forName(className);
return true;
} catch (Throwable t) {
return false;
}
}
} }