diff --git a/config.yml b/config.yml index 47f46da3..df61000e 100644 --- a/config.yml +++ b/config.yml @@ -62,6 +62,8 @@ DisguiseEntityExpire: 10 # Another option to choose the same thing for DisguiseClone command DisguiseCloneExpire: 10 +# Max disguises to store at a time with the DisguiseClone command +DisguiseCloneSize: 3 # This I don't really recommend turning on as it can make a memory leak.. # These disguises, as normal will not persist after a server restart. diff --git a/src/me/libraryaddict/disguise/DisguiseConfig.java b/src/me/libraryaddict/disguise/DisguiseConfig.java index a9ae4bbc..9b9f3fa6 100644 --- a/src/me/libraryaddict/disguise/DisguiseConfig.java +++ b/src/me/libraryaddict/disguise/DisguiseConfig.java @@ -19,6 +19,7 @@ public class DisguiseConfig { private static boolean keepDisguiseEntityDespawn; private static boolean keepDisguisePlayerDeath; private static boolean keepDisguisePlayerLogout; + private static int maxClonedDisguises; private static boolean maxHealthIsDisguisedEntity; private static boolean miscDisguisesForLivingEnabled; private static boolean modifyBoundingBox; @@ -44,6 +45,10 @@ public class DisguiseConfig { return disguiseEntityExpire; } + public static int getMaxClonedDisguises() { + return maxClonedDisguises; + } + public static boolean isAnimationPacketsEnabled() { return animationEnabled; } @@ -263,6 +268,10 @@ public class DisguiseConfig { keepDisguisePlayerLogout = keepDisguise; } + public static void setMaxClonedDisguises(int newMax) { + maxClonedDisguises = newMax; + } + public static void setMaxHealthDeterminedByDisguisedEntity(boolean isDetermined) { maxHealthIsDisguisedEntity = isDetermined; } diff --git a/src/me/libraryaddict/disguise/DisguiseListener.java b/src/me/libraryaddict/disguise/DisguiseListener.java index 07916db3..4cd5e728 100644 --- a/src/me/libraryaddict/disguise/DisguiseListener.java +++ b/src/me/libraryaddict/disguise/DisguiseListener.java @@ -1,6 +1,7 @@ package me.libraryaddict.disguise; import java.util.HashMap; +import java.util.Random; import me.libraryaddict.disguise.disguisetypes.Disguise; import me.libraryaddict.disguise.disguisetypes.PlayerDisguise; @@ -127,7 +128,7 @@ public class DisguiseListener implements Listener { disguiseRunnable.remove(p.getName()).cancel(); Entity entity = event.getRightClicked(); String entityName = ""; - if (entity instanceof Player) { + if (entity instanceof Player && !disguiseClone.containsKey(p.getName())) { entityName = ((Player) entity).getName(); } else { String[] split = entity.getType().name().split("_"); @@ -138,62 +139,73 @@ public class DisguiseListener implements Listener { } } } - Disguise disguise = null; - Entity disguiseTarget = null; if (disguiseClone.containsKey(p.getName())) { Boolean[] options = disguiseClone.remove(p.getName()); - disguiseTarget = p; - disguise = DisguiseAPI.getDisguise(p, entity); + Disguise disguise = DisguiseAPI.getDisguise(p, entity); if (disguise == null) { disguise = DisguiseAPI.constructDisguise(entity, options[0], options[1], options[2]); } else { disguise = disguise.clone(); } - } else if (disguiseEntity.containsKey(p.getName())) { - disguiseTarget = entity; - disguise = disguiseEntity.remove(p.getName()); - } - if (disguise != null) { - if (disguise.isMiscDisguise() && !DisguiseConfig.isMiscDisguisesForLivingEnabled() - && disguiseTarget instanceof LivingEntity) { - p.sendMessage(ChatColor.RED - + "Can't disguise a living entity as a misc disguise. This has been disabled in the config!"); + char[] alphabet = "abcdefghijklmnopqrstuvwxyz".toCharArray(); + String reference = null; + int referenceLength = Math.max(2, (int) Math.ceil((0.1D + DisguiseConfig.getMaxClonedDisguises()) / 26D)); + int attempts = 0; + while (reference == null && attempts++ < 1000) { + reference = "@"; + for (int i = 0; i < referenceLength; i++) { + reference += alphabet[new Random().nextInt(alphabet.length)]; + } + if (DisguiseUtilities.getClonedDisguise(reference) != null) { + reference = null; + } + } + if (reference != null && DisguiseUtilities.addClonedDisguise(reference, disguise)) { + p.sendMessage(ChatColor.RED + "Constructed a " + entityName + " disguise! Your reference is " + reference); + p.sendMessage(ChatColor.RED + "Example usage: /disguise " + reference); } else { - if (disguiseTarget instanceof Player && DisguiseConfig.isNameOfPlayerShownAboveDisguise()) { - if (disguise.getWatcher() instanceof LivingWatcher) { - ((LivingWatcher) disguise.getWatcher()).setCustomName(((Player) disguiseTarget).getDisplayName()); - if (DisguiseConfig.isNameAboveHeadAlwaysVisible()) { - ((LivingWatcher) disguise.getWatcher()).setCustomNameVisible(true); + p.sendMessage(ChatColor.RED + + "Failed to store the reference due to lack of size. Please set this in the config"); + } + } else if (disguiseEntity.containsKey(p.getName())) { + Disguise disguise = disguiseEntity.remove(p.getName()); + if (disguise != null) { + if (disguise.isMiscDisguise() && !DisguiseConfig.isMiscDisguisesForLivingEnabled() + && entity instanceof LivingEntity) { + p.sendMessage(ChatColor.RED + + "Can't disguise a living entity as a misc disguise. This has been disabled in the config!"); + } else { + if (entity instanceof Player && DisguiseConfig.isNameOfPlayerShownAboveDisguise()) { + if (disguise.getWatcher() instanceof LivingWatcher) { + ((LivingWatcher) disguise.getWatcher()).setCustomName(((Player) entity).getDisplayName()); + if (DisguiseConfig.isNameAboveHeadAlwaysVisible()) { + ((LivingWatcher) disguise.getWatcher()).setCustomNameVisible(true); + } } } - } - DisguiseAPI.disguiseToAll(disguiseTarget, disguise); - String disguiseName = "a "; - if (disguise instanceof PlayerDisguise) { - disguiseName = "the player " + ((PlayerDisguise) disguise).getName(); - } else { - String[] split = disguise.getType().name().split("_"); - for (int i = 0; i < split.length; i++) { - disguiseName += split[0].substring(0, 1) + split[0].substring(1).toLowerCase(); - if (i + 1 < split.length) { - disguiseName += " "; + DisguiseAPI.disguiseToAll(entity, disguise); + String disguiseName = "a "; + if (disguise instanceof PlayerDisguise) { + disguiseName = "the player " + ((PlayerDisguise) disguise).getName(); + } else { + String[] split = disguise.getType().name().split("_"); + for (int i = 0; i < split.length; i++) { + disguiseName += split[0].substring(0, 1) + split[0].substring(1).toLowerCase(); + if (i + 1 < split.length) { + disguiseName += " "; + } } } - } - if (disguiseTarget == p) { - p.sendMessage(ChatColor.RED + "Disguised yourself" + " as " + (entity instanceof Player ? "" : "a ") - + entityName + "!"); - } else { p.sendMessage(ChatColor.RED + "Disguised " + (entity instanceof Player ? "" : "the ") + entityName + " as " + disguiseName + "!"); } + } else { + if (DisguiseAPI.isDisguised(entity)) { + DisguiseAPI.undisguiseToAll(entity); + p.sendMessage(ChatColor.RED + "Undisguised " + (entity instanceof Player ? "" : "the ") + entityName); + } else + p.sendMessage(ChatColor.RED + (entity instanceof Player ? "" : "the") + entityName + " isn't disguised!"); } - } else { - if (DisguiseAPI.isDisguised(entity)) { - DisguiseAPI.undisguiseToAll(entity); - p.sendMessage(ChatColor.RED + "Undisguised " + (entity instanceof Player ? "" : "the ") + entityName); - } else - p.sendMessage(ChatColor.RED + (entity instanceof Player ? "" : "the") + entityName + " isn't disguised!"); } } } diff --git a/src/me/libraryaddict/disguise/LibsDisguises.java b/src/me/libraryaddict/disguise/LibsDisguises.java index f36a227f..95ea0688 100644 --- a/src/me/libraryaddict/disguise/LibsDisguises.java +++ b/src/me/libraryaddict/disguise/LibsDisguises.java @@ -119,6 +119,7 @@ public class LibsDisguises extends JavaPlugin { DisguiseConfig.setMaxHealthDeterminedByDisguisedEntity(getConfig().getBoolean("MaxHealthDeterminedByEntity")); DisguiseConfig.setDisguiseEntityExpire(getConfig().getInt("DisguiseEntityExpire")); DisguiseConfig.setDisguiseCloneExpire(getConfig().getInt("DisguiseCloneExpire")); + DisguiseConfig.setMaxClonedDisguises(getConfig().getInt("DisguiseCloneSize")); try { // Here I use reflection to set the plugin for Disguise.. // Kind of stupid but I don't want open API calls for a commonly used object. diff --git a/src/me/libraryaddict/disguise/commands/DisguiseCloneCommand.java b/src/me/libraryaddict/disguise/commands/DisguiseCloneCommand.java index 6603a02e..13722134 100644 --- a/src/me/libraryaddict/disguise/commands/DisguiseCloneCommand.java +++ b/src/me/libraryaddict/disguise/commands/DisguiseCloneCommand.java @@ -45,7 +45,7 @@ public class DisguiseCloneCommand extends BaseDisguiseCommand { } listener.setDisguiseClone(sender.getName(), new Boolean[] { doEnquipment, doSneak, doSprint }); sender.sendMessage(ChatColor.RED + "Right click a entity in the next " + DisguiseConfig.getDisguiseCloneExpire() - + " seconds to disguise as it!"); + + " seconds to grab the disguise reference!"); } else { sender.sendMessage(ChatColor.RED + "You are forbidden to use this command."); } @@ -56,7 +56,9 @@ public class DisguiseCloneCommand extends BaseDisguiseCommand { * Send the player the information */ protected void sendCommandUsage(CommandSender sender) { - sender.sendMessage(ChatColor.DARK_GREEN + "Disguise as the entity you right click! Or as their disguise!"); + sender.sendMessage(ChatColor.DARK_GREEN + + "Right click a entity to get a disguise reference you can pass to other disguise commands!"); + sender.sendMessage(ChatColor.DARK_GREEN + "Beware however, the reference bypasses all permissions checks"); sender.sendMessage(ChatColor.DARK_GREEN + "/disguiseclone IgnoreEnquipment" + ChatColor.DARK_GREEN + "(" + ChatColor.GREEN + "Optional" + ChatColor.DARK_GREEN + ")"); } diff --git a/src/me/libraryaddict/disguise/utilities/BaseDisguiseCommand.java b/src/me/libraryaddict/disguise/utilities/BaseDisguiseCommand.java index 170a024d..483e6bc5 100644 --- a/src/me/libraryaddict/disguise/utilities/BaseDisguiseCommand.java +++ b/src/me/libraryaddict/disguise/utilities/BaseDisguiseCommand.java @@ -166,89 +166,101 @@ public abstract class BaseDisguiseCommand implements CommandExecutor { sendCommandUsage(sender); throw new Exception(); } - DisguiseType disguiseType = null; - if (args[0].equalsIgnoreCase("p")) { - disguiseType = DisguiseType.PLAYER; - } else { - for (DisguiseType type : DisguiseType.values()) { - if (type.getEntityType() == null) { - continue; - } - if (args[0].equalsIgnoreCase(type.name()) || args[0].equalsIgnoreCase(type.name().replace("_", ""))) { - disguiseType = type; - break; - } - } - } - if (disguiseType == null) { - throw new Exception(ChatColor.RED + "Error! The disguise " + ChatColor.GREEN + args[0] + ChatColor.RED - + " doesn't exist!"); - } - if (!allowedDisguises.contains(disguiseType.name().toLowerCase())) { - throw new Exception(ChatColor.RED + "You are forbidden to use this disguise."); - } - ArrayList usedOptions = new ArrayList(); - Disguise disguise = null; // How many args to skip due to the disugise being constructed - int toSkip = 1; // Time to start constructing the disguise. // We will need to check between all 3 kinds of disguises - - HashSet> optionPermissions = this.getPermissions(sender, disguiseType.name().toLowerCase()); - if (disguiseType.isPlayer()) {// If he is doing a player disguise - if (args.length == 1) { - // He needs to give the player name - throw new Exception(ChatColor.RED + "Error! You need to give a player name!"); + int toSkip = 1; + ArrayList usedOptions = new ArrayList(); + Disguise disguise = null; + HashSet> optionPermissions; + if (args[0].startsWith("@")) { + if (sender.hasPermission("libsdisguises.disguise.disguiseclone")) { + disguise = DisguiseUtilities.getClonedDisguise(args[0].toLowerCase()); + if (disguise == null) { + throw new Exception(ChatColor.RED + "Cannot find a disguise under the reference " + args[0]); + } } else { - // Construct the player disguise - disguise = new PlayerDisguise(ChatColor.translateAlternateColorCodes('&', args[1])); - toSkip++; + throw new Exception(ChatColor.RED + "You do not have perimssion to use disguise references!"); } + optionPermissions = this.getPermissions(sender, disguise.getType().name().toLowerCase()); } else { - if (disguiseType.isMob()) { // Its a mob, use the mob constructor - boolean adult = true; - if (args.length > 1) { - if (args[1].equalsIgnoreCase("baby") || args[1].equalsIgnoreCase("adult")) { - usedOptions.add("setbaby"); - doCheck(optionPermissions, usedOptions); - adult = args[1].equalsIgnoreCase("adult"); - toSkip++; + DisguiseType disguiseType = null; + if (args[0].equalsIgnoreCase("p")) { + disguiseType = DisguiseType.PLAYER; + } else { + for (DisguiseType type : DisguiseType.values()) { + if (type.getEntityType() == null) { + continue; + } + if (args[0].equalsIgnoreCase(type.name()) || args[0].equalsIgnoreCase(type.name().replace("_", ""))) { + disguiseType = type; + break; } } - disguise = new MobDisguise(disguiseType, adult); - } else if (disguiseType.isMisc()) { - // Its a misc, we are going to use the MiscDisguise constructor. - int miscId = -1; - int miscData = -1; - if (args.length > 1) { - // They have defined more arguements! - // If the first arg is a number - if (isNumeric(args[1])) { - miscId = Integer.parseInt(args[1]); - toSkip++; - // If they also defined a data value - if (args.length > 2) { - if (isNumeric(args[2])) { - miscData = Integer.parseInt(args[2]); - toSkip++; + } + if (disguiseType == null) { + throw new Exception(ChatColor.RED + "Error! The disguise " + ChatColor.GREEN + args[0] + ChatColor.RED + + " doesn't exist!"); + } + if (!allowedDisguises.contains(disguiseType.name().toLowerCase())) { + throw new Exception(ChatColor.RED + "You are forbidden to use this disguise."); + } + optionPermissions = this.getPermissions(sender, disguiseType.name().toLowerCase()); + if (disguiseType.isPlayer()) {// If he is doing a player disguise + if (args.length == 1) { + // He needs to give the player name + throw new Exception(ChatColor.RED + "Error! You need to give a player name!"); + } else { + // Construct the player disguise + disguise = new PlayerDisguise(ChatColor.translateAlternateColorCodes('&', args[1])); + toSkip++; + } + } else { + if (disguiseType.isMob()) { // Its a mob, use the mob constructor + boolean adult = true; + if (args.length > 1) { + if (args[1].equalsIgnoreCase("baby") || args[1].equalsIgnoreCase("adult")) { + usedOptions.add("setbaby"); + doCheck(optionPermissions, usedOptions); + adult = args[1].equalsIgnoreCase("adult"); + toSkip++; + } + } + disguise = new MobDisguise(disguiseType, adult); + } else if (disguiseType.isMisc()) { + // Its a misc, we are going to use the MiscDisguise constructor. + int miscId = -1; + int miscData = -1; + if (args.length > 1) { + // They have defined more arguements! + // If the first arg is a number + if (isNumeric(args[1])) { + miscId = Integer.parseInt(args[1]); + toSkip++; + // If they also defined a data value + if (args.length > 2) { + if (isNumeric(args[2])) { + miscData = Integer.parseInt(args[2]); + toSkip++; + } } } } - } - if (miscId != -1) { - if (disguiseType == DisguiseType.FALLING_BLOCK) { - usedOptions.add("setblock"); - doCheck(optionPermissions, usedOptions); - } else if (disguiseType == DisguiseType.PAINTING) { - usedOptions.add("setpainting"); - doCheck(optionPermissions, usedOptions); - } else if (disguiseType == DisguiseType.SPLASH_POTION) { - usedOptions.add("setpotionid"); - doCheck(optionPermissions, usedOptions); + if (miscId != -1) { + if (disguiseType == DisguiseType.FALLING_BLOCK) { + usedOptions.add("setblock"); + doCheck(optionPermissions, usedOptions); + } else if (disguiseType == DisguiseType.PAINTING) { + usedOptions.add("setpainting"); + doCheck(optionPermissions, usedOptions); + } else if (disguiseType == DisguiseType.SPLASH_POTION) { + usedOptions.add("setpotionid"); + doCheck(optionPermissions, usedOptions); + } } + // Construct the disguise + disguise = new MiscDisguise(disguiseType, miscId, miscData); } - // Construct the disguise - disguise = new MiscDisguise(disguiseType, miscId, miscData); } } // Copy strings to their new range diff --git a/src/me/libraryaddict/disguise/utilities/DisguiseUtilities.java b/src/me/libraryaddict/disguise/utilities/DisguiseUtilities.java index dfff3b14..34ee097e 100644 --- a/src/me/libraryaddict/disguise/utilities/DisguiseUtilities.java +++ b/src/me/libraryaddict/disguise/utilities/DisguiseUtilities.java @@ -7,10 +7,12 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; +import java.util.LinkedHashMap; import java.util.List; import java.util.UUID; import me.libraryaddict.disguise.DisguiseAPI; +import me.libraryaddict.disguise.DisguiseConfig; import me.libraryaddict.disguise.LibsDisguises; import me.libraryaddict.disguise.disguisetypes.Disguise; import me.libraryaddict.disguise.disguisetypes.DisguiseType; @@ -19,6 +21,7 @@ import me.libraryaddict.disguise.disguisetypes.TargetedDisguise; import me.libraryaddict.disguise.disguisetypes.watchers.AgeableWatcher; import me.libraryaddict.disguise.disguisetypes.watchers.ZombieWatcher; import me.libraryaddict.disguise.disguisetypes.TargetedDisguise.TargetType; + import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.Material; @@ -42,6 +45,7 @@ public class DisguiseUtilities { * the plugin to do that. */ private static HashSet addedByPlugins = new HashSet(); + private static LinkedHashMap clonedDisguises = new LinkedHashMap(); /** * A hashmap of the uuid's of entitys, alive and dead. And their disguises in use **/ @@ -63,6 +67,21 @@ public class DisguiseUtilities { **/ private static HashMap selfDisguisesIds = new HashMap(); + public static boolean addClonedDisguise(String key, Disguise disguise) { + if (DisguiseConfig.getMaxClonedDisguises() > 0) { + if (clonedDisguises.containsKey(key)) { + clonedDisguises.remove(key); + } else if (DisguiseConfig.getMaxClonedDisguises() == clonedDisguises.size()) { + clonedDisguises.remove(clonedDisguises.keySet().iterator().next()); + } + if (DisguiseConfig.getMaxClonedDisguises() > clonedDisguises.size()) { + clonedDisguises.put(key, disguise); + return true; + } + } + return false; + } + public static void addDisguise(UUID entityId, TargetedDisguise disguise) { if (!getDisguises().containsKey(entityId)) { getDisguises().put(entityId, new HashSet()); @@ -217,6 +236,13 @@ public class DisguiseUtilities { return addedByPlugins; } + public static Disguise getClonedDisguise(String key) { + if (clonedDisguises.containsKey(key)) { + return clonedDisguises.get(key); + } + return null; + } + public static TargetedDisguise getDisguise(Player observer, Entity entity) { UUID entityId = entity.getUniqueId(); if (futureDisguises.containsKey(entity.getEntityId())) {