diff --git a/pom.xml b/pom.xml index 2506636..999b8c3 100755 --- a/pom.xml +++ b/pom.xml @@ -2,7 +2,7 @@ 4.0.0 com.trc202 combattag - 6.4.0-SNAPSHOT + 6.4.0 CombatTag 1.6 diff --git a/src/main/java/com/trc202/CombatTag/CombatTag.java b/src/main/java/com/trc202/CombatTag/CombatTag.java index aac69f0..c620d2a 100755 --- a/src/main/java/com/trc202/CombatTag/CombatTag.java +++ b/src/main/java/com/trc202/CombatTag/CombatTag.java @@ -2,6 +2,7 @@ import java.io.File; import java.lang.reflect.Field; +import java.lang.reflect.Method; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -18,6 +19,7 @@ import org.bukkit.command.CommandSender; import org.bukkit.entity.Entity; import org.bukkit.entity.HumanEntity; +import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.PlayerInventory; @@ -37,6 +39,8 @@ import com.trc202.settings.SettingsHelper; import com.trc202.settings.SettingsLoader; +import static com.trc202.CombatTag.Reflection.*; + public class CombatTag extends JavaPlugin { private final SettingsHelper settingsHelper; @@ -87,11 +91,13 @@ public CombatTag() { */ @Override public void onDisable() { - for (NPC npc : npcm.getNPCs()) { - UUID uuid = npcm.getNPCIdFromEntity(npc.getEntity()); - despawnNPC(uuid, NpcDespawnReason.PLUGIN_DISABLED); - if (isDebugEnabled()) { - log.info("[CombatTag] Disabling npc with ID of: " + uuid); + if (npcm != null) { + for (NPC npc : npcm.getNPCs()) { + UUID uuid = npcm.getNPCIdFromEntity(npc.getEntity()); + despawnNPC(uuid, NpcDespawnReason.PLUGIN_DISABLED); + if (isDebugEnabled()) { + log.info("[CombatTag] Disabling npc with ID of: " + uuid); + } } } //Just in case... @@ -367,7 +373,7 @@ public boolean onCommand(CommandSender sender, Command command, String commandLa sender.sendMessage("Please specify a player to force into combat"); return true; } - if (isInCombat(toForce.getUniqueId())) { + if (!isInCombat(toForce.getUniqueId())) { tagged.put(toForce.getUniqueId(), PvPTimeout(60)); if (!toForce.equals(sender)) sender.sendMessage("You have been forced into combat for one minute"); sender.sendMessage("Sucessfuly forced " + toForce.getName() + " into combat."); @@ -457,7 +463,7 @@ public void updatePlayerData(NPC npc, UUID playerUUID) { emptyArmorStack[x] = airItem; } target.getInventory().setArmorContents(emptyArmorStack); - target.setHealth(0); + setHealth(target, healthCheck(source.getHealth())); } else { copyTo(target, source); } @@ -480,13 +486,7 @@ public void copyTo(Player target, Player source) { target.setExhaustion(source.getExhaustion()); target.setSaturation(source.getSaturation()); target.setFireTicks(source.getFireTicks()); - if (target instanceof HumanEntity) { - HumanEntity humanTarget = (HumanEntity) target; - double healthSet = healthCheck(source.getHealth()); - humanTarget.setHealth((float) healthSet); - } else { - log.info("[CombatTag] An error has occurred! Target is not a HumanEntity!"); - } + setHealth(target, healthCheck(source.getHealth())); } public double healthCheck(double health) { @@ -506,13 +506,22 @@ public SettingsHelper getSettingsHelper() { public static boolean isVersionSupported() { return NPCLib.isSupported(); } - - public static final Field ENTITY_PLAYER_INVULNERABLE_TICKS_FIELD = Reflection.makeField(Reflection.getNmsClass("EntityPlayer"), "invulnerableTicks"); - + + public static final Field ENTITY_PLAYER_INVULNERABLE_TICKS_FIELD = Reflection.makeField(getNmsClass("EntityPlayer"), "invulnerableTicks"); + public static final Method ENTITY_LIVING_SET_HEALTH_METHOD = Reflection.makeMethod(getNmsClass("EntityLiving"), "setHealth", float.class); + public static void setInvulnerableTicks(Entity bukkitEntity, int invulnerableTicks) { //Entity.setNoDamageTicks() doesn't set EntityPlayer.invulnerableTicks Object entity = Reflection.getHandle(bukkitEntity); - if (Reflection.getNmsClass("EntityPlayer").isInstance(entity)) { + if (getNmsClass("EntityPlayer").isInstance(entity)) { Reflection.setField(ENTITY_PLAYER_INVULNERABLE_TICKS_FIELD, entity, invulnerableTicks); } } + /* + * EntityLiving.setHealth() calls die() if the entity is a player. + * EntityPlayer.die() tries to close any open inventories, causing the offline player to throw an exception. + */ + public static void setHealth(LivingEntity bukkitEntity, double health) { + Object entity = Reflection.getHandle(bukkitEntity); + Reflection.callMethod(ENTITY_LIVING_SET_HEALTH_METHOD, entity, (float)health); + } } diff --git a/src/main/java/com/trc202/CombatTag/NPCManager.java b/src/main/java/com/trc202/CombatTag/NPCManager.java index 5bde27f..2d81c95 100755 --- a/src/main/java/com/trc202/CombatTag/NPCManager.java +++ b/src/main/java/com/trc202/CombatTag/NPCManager.java @@ -16,6 +16,7 @@ public class NPCManager { public NPCManager(CombatTag plugin) { this.plugin = plugin; + getRegistry(); } private final CombatTag plugin; diff --git a/src/main/java/com/trc202/CombatTag/OfflinePlayerLoader.java b/src/main/java/com/trc202/CombatTag/OfflinePlayerLoader.java index c1c934b..4042d2d 100755 --- a/src/main/java/com/trc202/CombatTag/OfflinePlayerLoader.java +++ b/src/main/java/com/trc202/CombatTag/OfflinePlayerLoader.java @@ -1,209 +1,156 @@ -/** -* The MIT License -* Copyright (c) 2015 Techcable -* -* 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. -*/ - -package com.trc202.CombatTag; - -import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.UUID; - -import org.bukkit.Bukkit; -import org.bukkit.OfflinePlayer; -import org.bukkit.entity.Entity; -import org.bukkit.entity.Player; - -/** -* Loads the data of offline players -* -* Should be compatible with versions later than 1.6.4 -* -* @author Techcable -*/ -public class OfflinePlayerLoader { -/** -* Returns the given players data -* -* Loads the player's data from its file if it is offline -* If the player is online the online version is returned -* -* Players returned by this method may or may not be spawned and should only be used to access data -* -* @param name the player's name -* -* @return a player's data -* -* @throws RuntimeException if the loading failed -*/ -public static Player loadPlayer(String name) { -return loadPlayer(Bukkit.getOfflinePlayer(name)); -} -/** -* Returns the given players data -* -* Loads the player's data from its file if it is offline -* If the player is online the online version is returned -* -* Players returned by this method may or may not be spawned and should only be used to access data -* -* @param id the player's uuid -* -* @return a player's data -* -* @throws RuntimeException if the loading failed -*/ -public static Player loadPlayer(UUID id) { -return loadPlayer(Bukkit.getOfflinePlayer(id)); -} -/** -* Returns the given players data -* -* Loads the player's data from its file if it is offline -* If the player is online the online version is returned -* -* Players returned by this method may or may not be spawned and should only be used to access data -* -* @param player the player -* -* @return a player's data -* -* @throws RuntimeException if the loading failed -*/ -public static Player loadPlayer(OfflinePlayer player) { -if (player == null) return null; -if (player instanceof Player) { -return (Player) player; -} -return loadPlayer(player.getUniqueId(), player.getName()); -} -private static Player loadPlayer(UUID id, String name) { -Object server = getHandle(Bukkit.getServer()); -Object interactManager = newPlayerInteractManager(); -Object worldServer = getWorldServer(); -Object profile = newGameProfile(id, name); -Class entityPlayerClass = getNmsClass("EntityPlayer"); -Constructor entityPlayerConstructor = makeConstructor(entityPlayerClass, server.getClass(), worldServer.getClass(), profile.getClass(), interactManager.getClass()); -Object entityPlayer = callConstructor(entityPlayerConstructor, server, worldServer, profile, interactManager); -Player player = (Player) getBukkitEntity(entityPlayer); -return player; -} -private static Object newGameProfile(UUID id, String name) { -Class gameProfileClass = getUtilClass("com.mojang.authlib.GameProfile"); -if (gameProfileClass == null) { //Before uuids -return name; -} -Constructor gameProfileConstructor = null; -gameProfileConstructor = makeConstructor(gameProfileClass, UUID.class, String.class); -if (gameProfileConstructor == null) { //Verson has string constructor -gameProfileConstructor = makeConstructor(gameProfileClass, String.class, String.class); -return callConstructor(gameProfileConstructor, id.toString(), name); -} else { //Version has uuid constructor -return callConstructor(gameProfileConstructor, id, name); -} -} -private static Object newPlayerInteractManager() { -Object worldServer = getWorldServer(); -Class playerInteractClass = getNmsClass("PlayerInteractManager"); -Constructor c = makeConstructor(playerInteractClass, worldServer.getClass()); -return callConstructor(c, worldServer); -} -private static Object getWorldServer() { -Object server = getHandle(Bukkit.getServer()); -Method getWorldServer = makeMethod(server.getClass(), "getWorldServer", int.class); -return callMethod(getWorldServer, server, 0); -} -//NMS Utils -private static Entity getBukkitEntity(Object o) { -Method getBukkitEntity = makeMethod(o.getClass(), "getBukkitEntity"); -return callMethod(getBukkitEntity, o); -} -private static Class getNmsClass(String name) { -String className = "net.minecraft.server" + getVersion() + "." + name; -try { -return Class.forName(className); -} catch (ClassNotFoundException ex) { -return null; -} -} -private static Class getUtilClass(String name) { -try { -return Class.forName(name); //Try before 1.8 first -} catch (ClassNotFoundException ex) { -try { -return Class.forName("net.minecraft.util." + name); //Not 1.8 -} catch (ClassNotFoundException ex2) { -return null; -} -} -} -private static String getVersion() { -Bukkit.getServer(); -String packageName = Bukkit.getServer().getClass().getPackage().getName(); -return packageName.substring(packageName.lastIndexOf('.') + 1); -} -private static Object getHandle(Object wrapper) { -Method getHandle = makeMethod(wrapper.getClass(), "getHandle"); -return callMethod(getHandle, wrapper); -} -//Utils -private static Method makeMethod(Class clazz, String methodName, Class... paramaters) { -try { -return clazz.getDeclaredMethod(methodName, paramaters); -} catch (NoSuchMethodException ex) { -return null; -} catch (Exception ex) { -throw new RuntimeException(ex); -} -} -private static T callMethod(Method method, Object instance, Object... paramaters) { -if (method == null) throw new RuntimeException("No such method"); -method.setAccessible(true); -try { -return (T) method.invoke(instance, paramaters); -} catch (InvocationTargetException ex) { -throw new RuntimeException(ex.getCause()); -} catch (Exception ex) { -throw new RuntimeException(ex); -} -} -private static Constructor makeConstructor(Class clazz, Class... paramaterTypes) { -try { -return (Constructor) clazz.getConstructor(paramaterTypes); -} catch (NoSuchMethodException ex) { -return null; -} catch (Exception ex) { -throw new RuntimeException(ex); -} -} -private static T callConstructor(Constructor constructor, Object... paramaters) { -if (constructor == null) throw new RuntimeException("No such constructor"); -constructor.setAccessible(true); -try { -return (T) constructor.newInstance(paramaters); -} catch (InvocationTargetException ex) { -throw new RuntimeException(ex.getCause()); -} catch (Exception ex) { -throw new RuntimeException(ex); -} -} -} \ No newline at end of file +/** + * The MIT License + * Copyright (c) 2014-2015 Techcable + * + * 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. + */ +package com.trc202.CombatTag; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.UUID; + +import org.bukkit.Bukkit; +import org.bukkit.OfflinePlayer; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; + +import static com.trc202.CombatTag.Reflection.*; + +/** + * Loads the data of offline players + * + * Should be compatible with versions later than 1.6.4 + * + * @author Techcable + */ +class OfflinePlayerLoader { + + /** + * Returns the given players data + * + * Loads the player's data from its file if it is offline + * If the player is online the online version is returned + * + * Players returned by this method may or may not be spawned and should only be used to access data + * + * @param name the player's name + * + * @return a player's data + * + * @throws RuntimeException if the loading failed + */ + public static Player loadPlayer(String name) { + return loadPlayer(Bukkit.getOfflinePlayer(name)); + } + + /** + * Returns the given players data + * + * Loads the player's data from its file if it is offline + * If the player is online the online version is returned + * + * Players returned by this method may or may not be spawned and should only be used to access data + * + * @param id the player's uuid + * + * @return a player's data + * + * @throws RuntimeException if the loading failed + */ + public static Player loadPlayer(UUID id) { + return loadPlayer(Bukkit.getOfflinePlayer(id)); + } + + /** + * Returns the given players data + * + * Loads the player's data from its file if it is offline + * If the player is online the online version is returned + * + * Players returned by this method may or may not be spawned and should only be used to access data + * + * @param player the player + * + * @return a player's data + * + * @throws RuntimeException if the loading failed + */ + public static Player loadPlayer(OfflinePlayer player) { + if (player == null) return null; + if (player instanceof Player) { + return (Player) player; + } + return loadPlayer(player.getUniqueId(), player.getName()); + } + + private static Player loadPlayer(UUID id, String name) { + Object server = getMinecraftServer(); + Object interactManager = newPlayerInteractManager(); + Object worldServer = getWorldServer(); + Object profile = newGameProfile(id, name); + Class entityPlayerClass = getNmsClass("EntityPlayer"); + Constructor entityPlayerConstructor = makeConstructor(entityPlayerClass, getNmsClass("MinecraftServer"), getNmsClass("WorldServer"), getUtilClass("com.mojang.authlib.GameProfile"), getNmsClass("PlayerInteractManager")); + Object entityPlayer = callConstructor(entityPlayerConstructor, server, worldServer, profile, interactManager); + Player player = (Player) getBukkitEntity(entityPlayer); + return player; + } + + private static Object newGameProfile(UUID id, String name) { + Class gameProfileClass = getUtilClass("com.mojang.authlib.GameProfile"); + if (gameProfileClass == null) { //Before uuids + return name; + } + Constructor gameProfileConstructor = null; + gameProfileConstructor = makeConstructor(gameProfileClass, UUID.class, String.class); + if (gameProfileConstructor == null) { //Verson has string constructor + gameProfileConstructor = makeConstructor(gameProfileClass, String.class, String.class); + return callConstructor(gameProfileConstructor, id.toString(), name); + } else { //Version has uuid constructor + return callConstructor(gameProfileConstructor, id, name); + } + } + + private static Object newPlayerInteractManager() { + Object worldServer = getWorldServer(); + Class playerInteractClass = getNmsClass("PlayerInteractManager"); + Class worldClass = getNmsClass("World"); + Constructor c = makeConstructor(playerInteractClass, worldClass); + return callConstructor(c, worldServer); + } + + private static Object getWorldServer() { + Object server = getMinecraftServer(); + Class minecraftServerClass = getNmsClass("MinecraftServer"); + Method getWorldServer = makeMethod(minecraftServerClass, "getWorldServer", int.class); + return callMethod(getWorldServer, server, 0); + } + + //NMS Utils + + private static Object getMinecraftServer() { + return callMethod(makeMethod(getCbClass("CraftServer"), "getServer"), Bukkit.getServer()); + } + + private static Entity getBukkitEntity(Object o) { + Method getBukkitEntity = makeMethod(o.getClass(), "getBukkitEntity"); + return callMethod(getBukkitEntity, o); + } +} \ No newline at end of file diff --git a/src/main/java/com/trc202/CombatTag/Reflection.java b/src/main/java/com/trc202/CombatTag/Reflection.java old mode 100644 new mode 100755 index 318e33a..571c470 --- a/src/main/java/com/trc202/CombatTag/Reflection.java +++ b/src/main/java/com/trc202/CombatTag/Reflection.java @@ -12,7 +12,7 @@ public static Class getNmsClass(String name) { String className = "net.minecraft.server." + getVersion() + "." + name; return getClass(className); } - + public static Class getCbClass(String name) { String className = "org.bukkit.craftbukkit." + getVersion() + "." + name; return getClass(className); diff --git a/src/main/java/com/trc202/CombatTagListeners/NoPvpPlayerListener.java b/src/main/java/com/trc202/CombatTagListeners/NoPvpPlayerListener.java index 9b1c7e4..3ba7374 100755 --- a/src/main/java/com/trc202/CombatTagListeners/NoPvpPlayerListener.java +++ b/src/main/java/com/trc202/CombatTagListeners/NoPvpPlayerListener.java @@ -81,7 +81,7 @@ public void onPlayerQuit(PlayerQuitEvent e) { NPC npc = plugin.spawnNpc(quitPlr, quitPlr.getLocation()); Player npcPlayer = (Player) npc.getEntity(); plugin.copyContentsNpc(npc, quitPlr); - npcPlayer.setHealth(plugin.healthCheck(quitPlr.getHealth())); + CombatTag.setHealth(npcPlayer, plugin.healthCheck(quitPlr.getHealth())); quitPlr.getWorld().createExplosion(quitPlr.getLocation(), -1); //Create the smoke effect CombatTag.setInvulnerableTicks(npcPlayer, 0); if (plugin.settings.getNpcDespawnTime() > 0) {