Fix setSleeping for player disguise in 1.8

This commit is contained in:
libraryaddict 2014-09-15 02:22:29 +12:00
parent 14267f5b61
commit 3d0a5fc113
6 changed files with 282 additions and 52 deletions

10
pom.xml
View File

@ -86,17 +86,17 @@
<artifactId>spigot-api</artifactId>
<version>1.7.8-R0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot</artifactId>
<version>1.7.8-R0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot</artifactId>
<version>1.7.10-R0.1-SNAPSHOT</version>
</dependency>
</dependencies>
<distributionManagement>

View File

@ -1,5 +1,6 @@
package me.libraryaddict.disguise;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Random;
@ -9,8 +10,8 @@ import me.libraryaddict.disguise.disguisetypes.DisguiseType;
import me.libraryaddict.disguise.disguisetypes.PlayerDisguise;
import me.libraryaddict.disguise.disguisetypes.TargetedDisguise;
import me.libraryaddict.disguise.disguisetypes.watchers.LivingWatcher;
import me.libraryaddict.disguise.disguisetypes.watchers.PlayerWatcher;
import me.libraryaddict.disguise.utilities.DisguiseUtilities;
import me.libraryaddict.disguise.utilities.ReflectionManager;
import me.libraryaddict.disguise.utilities.UpdateChecker;
import org.bukkit.Bukkit;
@ -24,15 +25,20 @@ import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.EntityDamageByEntityEvent;
import org.bukkit.event.entity.EntityTargetEvent;
import org.bukkit.event.player.PlayerChangedWorldEvent;
import org.bukkit.event.player.PlayerInteractEntityEvent;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerMoveEvent;
import org.bukkit.event.player.PlayerPortalEvent;
import org.bukkit.event.player.PlayerRespawnEvent;
import org.bukkit.event.player.PlayerTeleportEvent;
import org.bukkit.event.vehicle.VehicleEnterEvent;
import org.bukkit.event.vehicle.VehicleExitEvent;
import org.bukkit.scheduler.BukkitRunnable;
import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.events.PacketContainer;
public class DisguiseListener implements Listener {
private String currentVersion;
@ -86,6 +92,39 @@ public class DisguiseListener implements Listener {
}
}
private void chunkMove(Player player, Location newLoc, Location oldLoc) {
try {
if (ReflectionManager.is1_8(player)) {
for (PacketContainer packet : DisguiseUtilities.getBedChunkPacket(player, newLoc, oldLoc)) {
ProtocolLibrary.getProtocolManager().sendServerPacket(player, packet, false);
}
}
if (newLoc != null) {
for (HashSet<TargetedDisguise> list : DisguiseUtilities.getDisguises().values()) {
for (TargetedDisguise disguise : list) {
if (disguise.getType() == DisguiseType.PLAYER && disguise.canSee(player)
&& ((PlayerDisguise) disguise).getWatcher().isSleeping()
&& DisguiseUtilities.getPerverts(disguise).contains(player)) {
PacketContainer[] packets = DisguiseUtilities.getBedPackets(player,
disguise.getEntity() == player ? newLoc : disguise.getEntity().getLocation(), newLoc,
(PlayerDisguise) disguise);
if (disguise.getEntity() == player) {
for (PacketContainer packet : packets) {
packet.getIntegers().write(0, DisguiseAPI.getSelfDisguiseId());
}
}
for (PacketContainer packet : packets) {
ProtocolLibrary.getProtocolManager().sendServerPacket(player, packet);
}
}
}
}
}
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
@EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
public void onAttack(EntityDamageByEntityEvent event) {
if (DisguiseConfig.isDisguiseBlownOnAttack()) {
@ -104,6 +143,33 @@ public class DisguiseListener implements Listener {
if (latestVersion != null && p.hasPermission(updateNotifyPermission)) {
p.sendMessage(String.format(updateMessage, currentVersion, latestVersion));
}
if (DisguiseConfig.isBedPacketsEnabled()) {
chunkMove(p, p.getLocation(), null);
}
}
/**
* Most likely faster if we don't bother doing checks if he sees a player disguise
*/
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onMove(PlayerMoveEvent event) {
if (DisguiseConfig.isBedPacketsEnabled()) {
Location to = event.getTo();
Location from = event.getFrom();
if (Math.floor(to.getBlockX() / 160D) != Math.floor(from.getBlockX() / 160D)
|| Math.floor(to.getBlockZ() / 160D) != Math.floor(from.getBlockZ() / 160D)) {
chunkMove(event.getPlayer(), to, from);
}
}
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onPortalEnter(PlayerPortalEvent event) {
if (DisguiseConfig.isUndisguiseOnWorldChange() && event.getFrom().getWorld() != event.getTo().getWorld()) {
for (Disguise disguise : DisguiseAPI.getDisguises(event.getPlayer())) {
disguise.removeDisguise();
}
}
}
@EventHandler
@ -216,6 +282,27 @@ public class DisguiseListener implements Listener {
}
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onTeleport(final PlayerTeleportEvent event) {
if (DisguiseConfig.isBedPacketsEnabled()) {
Location to = event.getTo();
Location from = event.getFrom();
if (Math.floor(to.getBlockX() / 160D) != Math.floor(from.getBlockX() / 160D)
|| Math.floor(to.getBlockZ() / 160D) != Math.floor(from.getBlockZ() / 160D)) {
chunkMove(event.getPlayer(), null, from);
Bukkit.getScheduler().scheduleSyncDelayedTask(plugin, new Runnable() {
public void run() {
if (!event.isCancelled()) {
chunkMove(event.getPlayer(), event.getTo(), null);
} else {
chunkMove(event.getPlayer(), event.getPlayer().getLocation(), null);
}
}
});
}
}
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onVehicleEnter(VehicleEnterEvent event) {
if (event.getEntered() instanceof Player && DisguiseAPI.isDisguised((Player) event.getEntered(), event.getEntered())) {
@ -240,11 +327,9 @@ public class DisguiseListener implements Listener {
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onWorldSwitch(PlayerPortalEvent event) {
if (DisguiseConfig.isUndisguiseOnWorldChange() && event.getFrom().getWorld() != event.getTo().getWorld()) {
for (Disguise disguise : DisguiseAPI.getDisguises(event.getPlayer())) {
disguise.removeDisguise();
}
public void onWorldSwitch(final PlayerChangedWorldEvent event) {
if (DisguiseConfig.isBedPacketsEnabled()) {
chunkMove(event.getPlayer(), event.getPlayer().getLocation(), null);
}
}

View File

@ -1,6 +1,6 @@
package me.libraryaddict.disguise.disguisetypes.watchers;
import org.bukkit.Location;
import org.bukkit.block.BlockFace;
import org.bukkit.entity.Player;
import com.comphenix.protocol.PacketType;
@ -8,6 +8,7 @@ import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.reflect.StructureModifier;
import me.libraryaddict.disguise.DisguiseAPI;
import me.libraryaddict.disguise.DisguiseConfig;
import me.libraryaddict.disguise.disguisetypes.Disguise;
import me.libraryaddict.disguise.disguisetypes.PlayerDisguise;
@ -16,6 +17,7 @@ import me.libraryaddict.disguise.utilities.ReflectionManager.LibVersion;
public class PlayerWatcher extends LivingWatcher {
private boolean isInBed;
private BlockFace sleepingDirection;
public PlayerWatcher(Disguise disguise) {
super(disguise);
@ -31,6 +33,10 @@ public class PlayerWatcher extends LivingWatcher {
return (Byte) getValue(9, (byte) 0);
}
public BlockFace getSleepingDirection() {
return sleepingDirection;
}
private boolean getValue16(int i) {
return ((Byte) getValue(16, (byte) 0) & 1 << i) != 0;
}
@ -57,31 +63,57 @@ public class PlayerWatcher extends LivingWatcher {
((PlayerDisguise) getDisguise()).setSkin(playerName);
}
/**
* The facing direction for the bed is the block metadata. 0 - 90 degrees. 1 - 0 degrees. 2 - 270 degrees. 3 - 180 degrees.
*/
public void setSleeping(BlockFace sleepingDirection) {
setSleeping(true, sleepingDirection);
}
public void setSleeping(boolean sleep) {
if (sleep != isSleeping()) {
isInBed = sleep;
setSleeping(sleep, null);
}
/**
* If no BlockFace is supplied. It grabs it from the entities facing direction if applicable.
*/
public void setSleeping(boolean sleeping, BlockFace sleepingDirection) {
if (sleepingDirection != null) {
this.sleepingDirection = BlockFace.values()[sleepingDirection.ordinal() % 4];
} else if (sleeping) {
if (this.getDisguise().getEntity() != null) {
this.sleepingDirection = BlockFace.values()[Math
.round(this.getDisguise().getEntity().getLocation().getYaw() / 90F) & 0x3];
} else {
this.sleepingDirection = BlockFace.EAST;
}
}
if (sleeping != isSleeping()) {
isInBed = sleeping;
if (DisguiseConfig.isBedPacketsEnabled() && DisguiseUtilities.isDisguiseInUse(getDisguise())) {
PacketContainer packet;
if (isSleeping()) {
packet = new PacketContainer(PacketType.Play.Server.BED);
StructureModifier<Integer> mods = packet.getIntegers();
mods.write(0, getDisguise().getEntity().getEntityId());
Location loc = getDisguise().getEntity().getLocation();
mods.write(1, loc.getBlockX());
mods.write(2, loc.getBlockY());
mods.write(3, loc.getBlockZ());
} else {
packet = new PacketContainer(PacketType.Play.Server.ANIMATION);
StructureModifier<Integer> mods = packet.getIntegers();
mods.write(0, getDisguise().getEntity().getEntityId());
mods.write(1, LibVersion.is1_7() ? 3 : 2);
}
try {
for (Player player : DisguiseUtilities.getPerverts(getDisguise())) {
ProtocolLibrary.getProtocolManager().sendServerPacket(player, packet);
if (isSleeping()) {
for (Player player : DisguiseUtilities.getPerverts(getDisguise())) {
PacketContainer[] packets = DisguiseUtilities.getBedPackets(player, this.getDisguise().getEntity()
.getLocation(), player.getLocation(), (PlayerDisguise) this.getDisguise());
if (getDisguise().getEntity() == player) {
for (PacketContainer packet : packets) {
packet = packet.shallowClone();
packet.getIntegers().write(0, DisguiseAPI.getSelfDisguiseId());
ProtocolLibrary.getProtocolManager().sendServerPacket(player, packet);
}
} else {
for (PacketContainer packet : packets) {
ProtocolLibrary.getProtocolManager().sendServerPacket(player, packet);
}
}
}
} else {
PacketContainer packet = new PacketContainer(PacketType.Play.Server.ANIMATION);
StructureModifier<Integer> mods = packet.getIntegers();
mods.write(0, getDisguise().getEntity().getEntityId());
mods.write(1, LibVersion.is1_7() ? 3 : 2);
for (Player player : DisguiseUtilities.getPerverts(getDisguise())) {
ProtocolLibrary.getProtocolManager().sendServerPacket(player, packet);
}
}
} catch (Exception ex) {
ex.printStackTrace();

View File

@ -1,9 +1,11 @@
package me.libraryaddict.disguise.utilities;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
@ -23,6 +25,7 @@ import me.libraryaddict.disguise.disguisetypes.TargetedDisguise;
import me.libraryaddict.disguise.disguisetypes.watchers.AgeableWatcher;
import me.libraryaddict.disguise.disguisetypes.watchers.EndermanWatcher;
import me.libraryaddict.disguise.disguisetypes.watchers.ItemFrameWatcher;
import me.libraryaddict.disguise.disguisetypes.watchers.PlayerWatcher;
import me.libraryaddict.disguise.disguisetypes.watchers.ZombieWatcher;
import me.libraryaddict.disguise.disguisetypes.TargetedDisguise.TargetType;
import me.libraryaddict.disguise.utilities.ReflectionManager.LibVersion;
@ -31,6 +34,7 @@ import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.block.BlockFace;
import org.bukkit.entity.Ageable;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
@ -43,6 +47,7 @@ import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.ProtocolManager;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.wrappers.WrappedDataWatcher;
import com.comphenix.protocol.wrappers.WrappedGameProfile;
import com.comphenix.protocol.wrappers.WrappedWatchableObject;
@ -53,6 +58,7 @@ public class DisguiseUtilities {
* the plugin to do that.
*/
private static HashSet<String> addedByPlugins = new HashSet<String>();
private static Object bedChunk;
private static LinkedHashMap<String, Disguise> clonedDisguises = new LinkedHashMap<String, Disguise>();
/**
* A hashmap of the uuid's of entitys, alive and dead. And their disguises in use
@ -70,6 +76,43 @@ public class DisguiseUtilities {
private static LibsDisguises libsDisguises;
private static HashMap<String, ArrayList<Object>> runnables = new HashMap<String, ArrayList<Object>>();
private static HashSet<UUID> selfDisguised = new HashSet<UUID>();
private static Field xChunk, zChunk;
static {
try {
bedChunk = ReflectionManager.getNmsClass("Chunk")
.getConstructor(ReflectionManager.getNmsClass("World"), int.class, int.class).newInstance(null, 0, 0);
Field cSection = bedChunk.getClass().getDeclaredField("sections");
cSection.setAccessible(true);
Object chunkSection = ReflectionManager.getNmsClass("ChunkSection").getConstructor(int.class, boolean.class)
.newInstance(0, false);
Object block = ReflectionManager.getNmsClass("Block").getMethod("getById", int.class)
.invoke(null, Material.BED_BLOCK.getId()); // TODO Method name exists in older versions?
Method setId = chunkSection.getClass().getMethod("setTypeId", int.class, int.class, int.class,
ReflectionManager.getNmsClass("Block"));
Method setData = chunkSection.getClass().getMethod("setData", int.class, int.class, int.class, int.class);
Method setSky = chunkSection.getClass().getMethod("setSkyLight", int.class, int.class, int.class, int.class);
Method setEmitted = chunkSection.getClass().getMethod("setEmittedLight", int.class, int.class, int.class, int.class);
for (BlockFace face : new BlockFace[] { BlockFace.EAST, BlockFace.WEST, BlockFace.NORTH, BlockFace.SOUTH }) {
setId.invoke(chunkSection, 1 + face.getModX(), 0, 1 + face.getModZ(), block);
setData.invoke(chunkSection, 1 + face.getModX(), 0, 1 + face.getModZ(), face.ordinal());
setSky.invoke(chunkSection, 1 + face.getModX(), 0, 1 + face.getModZ(), 0);
setEmitted.invoke(chunkSection, 1 + face.getModX(), 0, 1 + face.getModZ(), 0);
}
Object[] array = (Object[]) Array.newInstance(chunkSection.getClass(), 16);
array[0] = chunkSection;
cSection.set(bedChunk, array);
Object server = ReflectionManager.getNmsMethod("MinecraftServer", "getServer").invoke(null);
Object world = ((List) server.getClass().getField("worlds").get(server)).get(0);
bedChunk.getClass().getField("world").set(bedChunk, world);
xChunk = bedChunk.getClass().getField("locX");
xChunk.setAccessible(true);
zChunk = bedChunk.getClass().getField("locZ");
zChunk.setAccessible(true);
} catch (Exception ex) {
ex.printStackTrace();
}
}
public static boolean addClonedDisguise(String key, Disguise disguise) {
if (DisguiseConfig.getMaxClonedDisguises() > 0) {
@ -257,6 +300,74 @@ public class DisguiseUtilities {
return addedByPlugins;
}
public static PacketContainer[] getBedChunkPacket(Player player, Location newLoc, Location oldLoc) {
int i = 0;
PacketContainer[] packets = new PacketContainer[newLoc != null ? 2 + (oldLoc != null ? 1 : 0) : 1];
for (Location loc : new Location[] { oldLoc, newLoc }) {
if (loc == null) {
continue;
}
try {
int chunkX = (int) Math.floor(loc.getBlockX() / 16D) + 20, chunkZ = (int) Math.floor(loc.getBlockZ() / 16D) + 20;
chunkX -= chunkX % 10;
chunkZ -= chunkZ % 10;
xChunk.set(bedChunk, chunkX);
zChunk.set(bedChunk, chunkZ);
} catch (Exception ex) {
ex.printStackTrace();
}
try {
packets[i++] = ProtocolLibrary.getProtocolManager()
.createPacketConstructor(PacketType.Play.Server.MAP_CHUNK, bedChunk, true, 0, 40)
.createPacket(bedChunk, true, 0, ReflectionManager.is1_8(player) ? 48 : 0);
} catch (IllegalArgumentException ex) {
packets[i++] = ProtocolLibrary.getProtocolManager()
.createPacketConstructor(PacketType.Play.Server.MAP_CHUNK, bedChunk, true, 0)
.createPacket(bedChunk, true, 0);
}
if (oldLoc == null || i > 1) {
try {
packets[i++] = ProtocolLibrary.getProtocolManager()
.createPacketConstructor(PacketType.Play.Server.MAP_CHUNK_BULK, Arrays.asList(bedChunk), 40)
.createPacket(Arrays.asList(bedChunk), ReflectionManager.is1_8(player) ? 48 : 0);
} catch (IllegalArgumentException ex) {
packets[i++] = ProtocolLibrary.getProtocolManager()
.createPacketConstructor(PacketType.Play.Server.MAP_CHUNK_BULK, Arrays.asList(bedChunk))
.createPacket(Arrays.asList(bedChunk));
}
}
}
return packets;
}
public static PacketContainer[] getBedPackets(Player player, Location loc, Location playerLocation, PlayerDisguise disguise) {
Entity entity = disguise.getEntity();
PacketContainer setBed = new PacketContainer(PacketType.Play.Server.BED);
StructureModifier<Integer> bedInts = setBed.getIntegers();
bedInts.write(0, entity.getEntityId());
if (ReflectionManager.is1_8(player)) {
PlayerWatcher watcher = disguise.getWatcher();
int chunkX = (int) Math.floor(playerLocation.getBlockX() / 16D) + 20, chunkZ = (int) Math.floor(playerLocation
.getBlockZ() / 16D) + 20;
chunkX -= chunkX % 10;
chunkZ -= chunkZ % 10;
bedInts.write(1, (chunkX * 16) + 1 + watcher.getSleepingDirection().getModX());
bedInts.write(3, (chunkZ * 16) + 1 + watcher.getSleepingDirection().getModZ());
} else {
bedInts.write(1, loc.getBlockX());
bedInts.write(2, loc.getBlockY());
bedInts.write(3, loc.getBlockZ());
}
PacketContainer teleport = new PacketContainer(PacketType.Play.Server.ENTITY_TELEPORT);
StructureModifier<Integer> ints = teleport.getIntegers();
ints.write(0, entity.getEntityId());
ints.write(1, (int) Math.floor(loc.getX() * 32));
ints.write(2, (int) Math.floor((PacketsManager.getYModifier(disguise.getEntity(), disguise) + loc.getY()) * 32));
ints.write(3, (int) Math.floor(loc.getZ() * 32));
return new PacketContainer[] { setBed, teleport };
}
public static Disguise getClonedDisguise(String key) {
if (clonedDisguises.containsKey(key)) {
return clonedDisguises.get(key).clone();
@ -264,6 +375,12 @@ public class DisguiseUtilities {
return null;
}
public static PacketContainer getDestroyPacket(int... ids) {
PacketContainer destroyPacket = new PacketContainer(PacketType.Play.Server.ENTITY_DESTROY);
destroyPacket.getIntegerArrays().write(0, ids);
return destroyPacket;
}
public static TargetedDisguise getDisguise(Player observer, Entity entity) {
UUID entityId = entity.getUniqueId();
if (futureDisguises.containsKey(entity.getEntityId())) {
@ -671,12 +788,6 @@ public class DisguiseUtilities {
}
}
public static PacketContainer getDestroyPacket(int... ids) {
PacketContainer destroyPacket = new PacketContainer(PacketType.Play.Server.ENTITY_DESTROY);
destroyPacket.getIntegerArrays().write(0, ids);
return destroyPacket;
}
public static boolean removeDisguise(TargetedDisguise disguise) {
UUID entityId = disguise.getEntity().getUniqueId();
if (getDisguises().containsKey(entityId) && getDisguises().get(entityId).remove(disguise)) {

View File

@ -2,6 +2,7 @@ package me.libraryaddict.disguise.utilities;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
@ -250,12 +251,13 @@ public class PacketsManager {
createDataWatcher(player, WrappedDataWatcher.getEntityWatcher(disguisedEntity), disguise.getWatcher()));
if (((PlayerWatcher) disguise.getWatcher()).isSleeping() && DisguiseConfig.isBedPacketsEnabled()) {
spawnPackets[1] = new PacketContainer(PacketType.Play.Server.BED);
StructureModifier<Integer> mods = spawnPackets[1].getIntegers();
mods.write(0, disguisedEntity.getEntityId());
mods.write(1, loc.getBlockX());
mods.write(2, loc.getBlockY());
mods.write(3, loc.getBlockZ());
spawnPackets = Arrays.copyOf(spawnPackets, spawnPackets.length);
PacketContainer[] bedPackets = DisguiseUtilities.getBedPackets(player,
loc.clone().subtract(0, PacketsManager.getYModifier(disguisedEntity, disguise), 0), player.getLocation(),
((PlayerDisguise) disguise));
for (int i = 0; i < 2; i++) {
spawnPackets[i + 1] = bedPackets[i];
}
}
} else if (disguise.getType().isMob()) {
@ -439,7 +441,7 @@ public class PacketsManager {
/**
* Get the Y level to add to the disguise for realism.
*/
private static double getYModifier(Entity entity, Disguise disguise) {
public static double getYModifier(Entity entity, Disguise disguise) {
double yMod = 0;
if ((disguise.getType() != DisguiseType.PLAYER || !((PlayerWatcher) disguise.getWatcher()).isSleeping())
&& entity.getType() == EntityType.DROPPED_ITEM) {

View File

@ -29,7 +29,7 @@ import com.comphenix.protocol.wrappers.WrappedGameProfile;
public class ReflectionManager {
public enum LibVersion {
V1_6, V1_7, V1_7_6, V1_7_10, V1_8;
V1_6, V1_7, V1_7_10, V1_7_6, V1_8;
private static LibVersion currentVersion = LibVersion.V1_7;
static {
String mcVersion = Bukkit.getVersion().split("MC: ")[1].replace(")", "");
@ -58,13 +58,13 @@ public class ReflectionManager {
return getGameVersion() == V1_7 || is1_7_6();
}
public static boolean is1_7_6() {
return getGameVersion() == V1_7_6 || is1_7_10();
}
public static boolean is1_7_10() {
return getGameVersion() == V1_7_10;
}
public static boolean is1_7_6() {
return getGameVersion() == V1_7_6 || is1_7_10();
}
}
private static final String bukkitVersion = Bukkit.getServer().getClass().getName().split("\\.")[3];