From 888bbd9521c84d554a84148969de4beaa19f6380 Mon Sep 17 00:00:00 2001 From: libraryaddict Date: Thu, 7 May 2020 21:27:07 +1200 Subject: [PATCH] Add new armorstand option for player disguise names, clean up config cos of the player name options Added ability to set multiple names on a player disguise, also normal mob but that is untested and has no command accessibility Permission to do this is libsdisguises.multiname Renamed "Now disguised as a %s" to "Now disguised as %s" for messages --- .../disguise/DisguiseConfig.java | 43 +++-- .../commands/libsdisguises/LDScoreboard.java | 2 +- .../disguise/disguisetypes/Disguise.java | 81 ++++++++-- .../disguisetypes/PlayerDisguise.java | 142 ++++++++++------- .../watchers/DroppedItemWatcher.java | 2 +- .../watchers/FallingBlockWatcher.java | 2 +- .../disguise/utilities/DisguiseUtilities.java | 148 +++++++++++++++++- .../utilities/listeners/DisguiseListener.java | 17 +- .../utilities/packets/PacketsManager.java | 5 +- .../packethandlers/PacketHandlerMovement.java | 32 ++++ .../packethandlers/PacketHandlerSpawn.java | 4 +- .../PacketListenerViewSelfDisguise.java | 3 +- .../utilities/parser/DisguiseParser.java | 8 + .../utilities/translations/LibsMsg.java | 4 +- src/main/resources/config.yml | 15 +- src/main/resources/plugin.yml | 2 + 16 files changed, 406 insertions(+), 104 deletions(-) diff --git a/src/main/java/me/libraryaddict/disguise/DisguiseConfig.java b/src/main/java/me/libraryaddict/disguise/DisguiseConfig.java index 62a13f3f..2a91c974 100644 --- a/src/main/java/me/libraryaddict/disguise/DisguiseConfig.java +++ b/src/main/java/me/libraryaddict/disguise/DisguiseConfig.java @@ -95,9 +95,6 @@ public class DisguiseConfig { private static boolean explicitDisguisePermissions; @Getter @Setter - private static boolean extendedDisguiseNames; - @Getter - @Setter private static boolean hideDisguisedPlayers; @Getter @Setter @@ -228,9 +225,6 @@ public class DisguiseConfig { private static PermissionDefault commandVisibility = PermissionDefault.TRUE; @Getter @Setter - private static boolean scoreboardDisguiseNames; - @Getter - @Setter private static int tablistRemoveDelay; @Getter private static boolean usingReleaseBuild = true; @@ -246,6 +240,17 @@ public class DisguiseConfig { @Getter @Setter private static boolean hideTallSelfDisguises; + @Getter + @Setter + private static PlayerNameType playerNameType = PlayerNameType.ARMORSTANDS; + + public static boolean isArmorstandsName() { + return getPlayerNameType() == PlayerNameType.ARMORSTANDS; + } + + public static boolean isExtendedNames() { + return getPlayerNameType() == PlayerNameType.TEAMS_EXTENDED; + } public static void setAutoUpdate(boolean update) { if (isAutoUpdate() == update) { @@ -447,6 +452,10 @@ public class DisguiseConfig { return new HashMap.SimpleEntry(entry.getKey(), DisguiseParser.parseDisguise(invoker, target, entry.getValue())); } + public static boolean isScoreboardNames() { + return getPlayerNameType() != PlayerNameType.VANILLA; + } + public static void removeCustomDisguise(String disguise) { for (DisguisePerm entry : customDisguises.keySet()) { String name = entry.toReadable(); @@ -596,8 +605,6 @@ public class DisguiseConfig { setEntityStatusPacketsEnabled(config.getBoolean("PacketsEnabled.EntityStatus")); setEquipmentPacketsEnabled(config.getBoolean("PacketsEnabled.Equipment")); setExplicitDisguisePermissions(config.getBoolean("Permissions.ExplicitDisguises")); - // The default value shall be false if you don't update config - setExtendedDisguiseNames(config.contains("ScoreboardNames") && config.getBoolean("ExtendedNames")); setHideArmorFromSelf(config.getBoolean("RemoveArmor")); setHideDisguisedPlayers(config.getBoolean("HideDisguisedPlayersFromTab")); setHideHeldItemFromSelf(config.getBoolean("RemoveHeldItem")); @@ -634,7 +641,6 @@ public class DisguiseConfig { setWarnScoreboardConflict(config.getBoolean("Scoreboard.WarnConflict")); setWitherSkullPacketsEnabled(config.getBoolean("PacketsEnabled.WitherSkull")); setWolfDyeable(config.getBoolean("DyeableWolf")); - setScoreboardDisguiseNames(config.getBoolean("ScoreboardNames")); setTablistRemoveDelay(config.getInt("TablistRemoveDelay")); setAutoUpdate(config.getBoolean("AutoUpdate")); setHideTallSelfDisguises(config.getBoolean("HideTallSelfDisguises")); @@ -643,6 +649,14 @@ public class DisguiseConfig { DisguiseUtilities.getLogger().warning("You must purchase the plugin to use saved disguises!"); } + try { + setPlayerNameType(PlayerNameType.valueOf(config.getString("PlayerNames").toUpperCase())); + } + catch (Exception ex) { + DisguiseUtilities.getLogger() + .warning("Cannot parse '" + config.getString("PlayerNames") + "' to a valid option for PlayerNames"); + } + try { setNotifyBar(NotifyBar.valueOf(config.getString("NotifyBar").toUpperCase())); @@ -1077,6 +1091,17 @@ public class DisguiseConfig { CREATE_SCOREBOARD } + public enum PlayerNameType { + VANILLA, + TEAMS, + TEAMS_EXTENDED, + ARMORSTANDS; + + public boolean isTeams() { + return this == TEAMS || this == TEAMS_EXTENDED; + } + } + public enum UpdatesBranch { SAME_BUILDS, SNAPSHOTS, diff --git a/src/main/java/me/libraryaddict/disguise/commands/libsdisguises/LDScoreboard.java b/src/main/java/me/libraryaddict/disguise/commands/libsdisguises/LDScoreboard.java index 9aaf6f2a..fa6761d5 100644 --- a/src/main/java/me/libraryaddict/disguise/commands/libsdisguises/LDScoreboard.java +++ b/src/main/java/me/libraryaddict/disguise/commands/libsdisguises/LDScoreboard.java @@ -36,7 +36,7 @@ public class LDScoreboard implements LDCommand { @Override public void onCommand(CommandSender sender, String[] args) { - if (DisguiseConfig.isScoreboardDisguiseNames()) { + if (DisguiseConfig.isScoreboardNames()) { int issuesFound = 0; int unexpected = 0; diff --git a/src/main/java/me/libraryaddict/disguise/disguisetypes/Disguise.java b/src/main/java/me/libraryaddict/disguise/disguisetypes/Disguise.java index ceffd522..119af303 100644 --- a/src/main/java/me/libraryaddict/disguise/disguisetypes/Disguise.java +++ b/src/main/java/me/libraryaddict/disguise/disguisetypes/Disguise.java @@ -96,16 +96,59 @@ public abstract class Disguise { */ @Getter @Setter - private boolean customName = true; + private boolean customDisguiseName = true; @Getter @Setter private boolean tallDisguisesVisible = !DisguiseConfig.isHideTallSelfDisguises(); + @Getter + private String[] multiName = new String[0]; + private transient int[] armorstandIds = new int[0]; public Disguise(DisguiseType disguiseType) { this.disguiseType = disguiseType; this.disguiseName = disguiseType.toReadable(); } + public void setMultiName(String... name) { + String[] oldName = getMultiName(); + multiName = name; + + if (!isDisguiseInUse()) { + return; + } + + sendArmorStands(oldName); + } + + protected void sendArmorStands(String[] oldName) { + ArrayList packets = DisguiseUtilities.getNamePackets(this, oldName); + + try { + for (Player player : DisguiseUtilities.getPerverts(this)) { + for (PacketContainer packet : packets) { + ProtocolLibrary.getProtocolManager().sendServerPacket(player, packet); + } + } + } + catch (InvocationTargetException e) { + e.printStackTrace(); + } + } + + public int[] getArmorstandIds() { + if (getMultiName().length > armorstandIds.length) { + int oldLen = armorstandIds.length; + + armorstandIds = Arrays.copyOf(armorstandIds, getMultiName().length); + + for (int i = oldLen; i < armorstandIds.length; i++) { + armorstandIds[i] = ReflectionManager.getNewEntityId(); + } + } + + return armorstandIds; + } + public void addCustomData(String key, Object data) { customData.put(key, data); } @@ -123,7 +166,7 @@ public abstract class Disguise { protected void clone(Disguise disguise) { disguise.setDisguiseName(getDisguiseName()); - disguise.setCustomName(isCustomName()); + disguise.setCustomDisguiseName(isCustomDisguiseName()); disguise.setTallDisguisesVisible(isTallDisguisesVisible()); disguise.setReplaceSounds(isSoundsReplaced()); @@ -133,6 +176,7 @@ public abstract class Disguise { disguise.setHideHeldItemFromSelf(isHidingHeldItemFromSelf()); disguise.setVelocitySent(isVelocitySent()); disguise.setModifyBoundingBox(isModifyBoundingBox()); + disguise.multiName = Arrays.copyOf(multiName, multiName.length); if (getWatcher() != null) { disguise.setWatcher(getWatcher().clone(disguise)); @@ -795,20 +839,13 @@ public abstract class Disguise { task = null; } - // If this disguise has a entity set + // If this disguise hasn't a entity set if (getEntity() == null) { // Loop through the disguises because it could be used with a unknown entity id. HashMap> future = DisguiseUtilities.getFutureDisguises(); - Iterator itel = DisguiseUtilities.getFutureDisguises().keySet().iterator(); - - while (itel.hasNext()) { - int id = itel.next(); - - if (future.get(id).remove(this) && future.get(id).isEmpty()) { - itel.remove(); - } - } + DisguiseUtilities.getFutureDisguises().keySet() + .removeIf(id -> future.get(id).remove(this) && future.get(id).isEmpty()); return true; } @@ -842,7 +879,7 @@ public abstract class Disguise { // Remove the disguise from the current disguises. if (DisguiseUtilities.removeDisguise((TargetedDisguise) this)) { if (getEntity() instanceof Player) { - DisguiseUtilities.removeSelfDisguise((Player) getEntity()); + DisguiseUtilities.removeSelfDisguise(this); } // Better refresh the entity to undisguise it @@ -893,6 +930,20 @@ public abstract class Disguise { } } + if (getMultiName().length > 0) { + PacketContainer packet = new PacketContainer(Server.ENTITY_DESTROY); + packet.getIntegerArrays().write(0, Arrays.copyOf(getArmorstandIds(), getMultiName().length)); + + try { + for (Player player : DisguiseUtilities.getPerverts(this)) { + ProtocolLibrary.getProtocolManager().sendServerPacket(player, packet); + } + } + catch (InvocationTargetException e) { + e.printStackTrace(); + } + } + return true; } @@ -970,7 +1021,7 @@ public abstract class Disguise { if (isSelfDisguiseVisible()) { DisguiseUtilities.setupFakeDisguise(this); } else { - DisguiseUtilities.removeSelfDisguise((Player) getEntity()); + DisguiseUtilities.removeSelfDisguise(this); } } } @@ -1053,7 +1104,7 @@ public abstract class Disguise { DisguiseUtilities.addDisguise(entity.getUniqueId(), (TargetedDisguise) this); if (isSelfDisguiseVisible() && getEntity() instanceof Player) { - DisguiseUtilities.removeSelfDisguise((Player) getEntity()); + DisguiseUtilities.removeSelfDisguise(this); } // Resend the disguised entity's packet diff --git a/src/main/java/me/libraryaddict/disguise/disguisetypes/PlayerDisguise.java b/src/main/java/me/libraryaddict/disguise/disguisetypes/PlayerDisguise.java index 4c546407..1b64ba2b 100644 --- a/src/main/java/me/libraryaddict/disguise/disguisetypes/PlayerDisguise.java +++ b/src/main/java/me/libraryaddict/disguise/disguisetypes/PlayerDisguise.java @@ -12,7 +12,6 @@ import me.libraryaddict.disguise.disguisetypes.watchers.PlayerWatcher; import me.libraryaddict.disguise.utilities.DisguiseUtilities; import me.libraryaddict.disguise.utilities.reflection.LibsProfileLookup; import me.libraryaddict.disguise.utilities.reflection.ReflectionManager; -import org.apache.commons.lang.Validate; import org.bukkit.Bukkit; import org.bukkit.entity.Entity; import org.bukkit.entity.Player; @@ -27,6 +26,10 @@ public class PlayerDisguise extends TargetedDisguise { private String playerName = "Herobrine"; private String skinToUse; private boolean nameVisible = true; + /** + * Has someone set name visible explicitly? + */ + private boolean explicitNameVisible = false; private UUID uuid = UUID.randomUUID(); @Getter @Setter @@ -82,8 +85,7 @@ public class PlayerDisguise extends TargetedDisguise { @Deprecated public DisguiseUtilities.DScoreTeam getScoreboardName() { - if (getName().length() <= 32 ? !DisguiseConfig.isScoreboardDisguiseNames() : - !DisguiseConfig.isExtendedDisguiseNames()) { + if (!DisguiseConfig.isScoreboardNames()) { throw new IllegalStateException("Cannot use this method when it's been disabled in config!"); } @@ -103,11 +105,11 @@ public class PlayerDisguise extends TargetedDisguise { } public boolean hasScoreboardName() { - if (isStaticName(getName())) { + if (!DisguiseConfig.isArmorstandsName() && isStaticName(getName())) { return false; } - return scoreboardName != null || DisguiseConfig.isScoreboardDisguiseNames() || getName().length() > 16; + return DisguiseConfig.isScoreboardNames(); } public String getProfileName() { @@ -123,12 +125,23 @@ public class PlayerDisguise extends TargetedDisguise { } public PlayerDisguise setNameVisible(boolean nameVisible) { - if (isNameVisible() == nameVisible) { + return setNameVisible(nameVisible, false); + } + + private PlayerDisguise setNameVisible(boolean nameVisible, boolean setInternally) { + if (isNameVisible() == nameVisible || (setInternally && explicitNameVisible)) { return this; } + if (!setInternally) { + explicitNameVisible = true; + } + if (isDisguiseInUse()) { - if (!DisguiseConfig.isScoreboardDisguiseNames()) { + if (DisguiseConfig.isArmorstandsName()) { + this.nameVisible = nameVisible; + sendArmorStands(isNameVisible() ? getMultiName() : new String[0]); + } else if (!DisguiseConfig.isScoreboardNames()) { if (stopDisguise()) { this.nameVisible = nameVisible; @@ -172,7 +185,8 @@ public class PlayerDisguise extends TargetedDisguise { } disguise.setName(getName()); - disguise.setNameVisible(isNameVisible()); + disguise.nameVisible = isNameVisible(); + disguise.explicitNameVisible = explicitNameVisible; disguise.setDynamicName(isDynamicName()); clone(disguise); @@ -214,58 +228,79 @@ public class PlayerDisguise extends TargetedDisguise { return; } - int cLimit = DisguiseConfig.isExtendedDisguiseNames() ? 16 * 3 : - DisguiseConfig.isScoreboardDisguiseNames() ? 16 * 2 : 16; + int cLimit; + + switch (DisguiseConfig.getPlayerNameType()) { + case TEAMS: + cLimit = 16 * 2; + break; + case TEAMS_EXTENDED: + cLimit = 16 * 3; + break; + case ARMORSTANDS: + cLimit = 256; + break; + default: + cLimit = 16; + break; + } + if (name.length() > cLimit) { name = name.substring(0, cLimit); } if (isDisguiseInUse()) { - boolean resendDisguise = false; + if (DisguiseConfig.isArmorstandsName()) { + playerName = name; - if (DisguiseConfig.isScoreboardDisguiseNames() && !isStaticName(name)) { - DisguiseUtilities.DScoreTeam team = getScoreboardName(); - String[] split = DisguiseUtilities.getExtendedNameSplit(team.getPlayer(), name); + setNameVisible(!name.isEmpty(), true); + setMultiName(DisguiseUtilities.splitNewLine(name)); + } else { + boolean resendDisguise = false; - resendDisguise = !split[1].equals(team.getPlayer()); - setScoreboardName(split); - } + if (DisguiseConfig.isScoreboardNames() && !isStaticName(name)) { + DisguiseUtilities.DScoreTeam team = getScoreboardName(); + String[] split = DisguiseUtilities.getExtendedNameSplit(team.getPlayer(), name); - resendDisguise = - !DisguiseConfig.isScoreboardDisguiseNames() || isStaticName(name) || isStaticName(getName()) || - resendDisguise; + resendDisguise = !split[1].equals(team.getPlayer()); + setScoreboardName(split); + } - if (resendDisguise) { - if (stopDisguise()) { - if (getName().isEmpty() && !name.isEmpty()) { - setNameVisible(true); - } else if (!getName().isEmpty() && name.isEmpty()) { - setNameVisible(false); - } + resendDisguise = !DisguiseConfig.isScoreboardNames() || isStaticName(name) || isStaticName(getName()) || + resendDisguise; - playerName = name; + if (resendDisguise) { + if (stopDisguise()) { + if (getName().isEmpty() && !name.isEmpty()) { + setNameVisible(true, true); + } else if (!getName().isEmpty() && name.isEmpty()) { + setNameVisible(false, true); + } - if (gameProfile != null) { - gameProfile = ReflectionManager - .getGameProfileWithThisSkin(uuid, getProfileName(), getGameProfile()); - } + playerName = name; - if (!startDisguise()) { + if (gameProfile != null) { + gameProfile = ReflectionManager + .getGameProfileWithThisSkin(uuid, getProfileName(), getGameProfile()); + } + + if (!startDisguise()) { + throw new IllegalStateException("Unable to restart disguise"); + } + } else { throw new IllegalStateException("Unable to restart disguise"); } } else { - throw new IllegalStateException("Unable to restart disguise"); - } - } else { - if (getName().isEmpty() && !name.isEmpty() && !isNameVisible()) { - setNameVisible(true); - } else if (!getName().isEmpty() && name.isEmpty() && isNameVisible()) { - setNameVisible(false); - } else { - DisguiseUtilities.updateExtendedName(this); - } + if (getName().isEmpty() && !name.isEmpty() && !isNameVisible()) { + setNameVisible(true, true); + } else if (!getName().isEmpty() && name.isEmpty() && isNameVisible()) { + setNameVisible(false, true); + } else { + DisguiseUtilities.updateExtendedName(this); + } - playerName = name; + playerName = name; + } } if (isDisplayedInTab()) { @@ -291,12 +326,11 @@ public class PlayerDisguise extends TargetedDisguise { setScoreboardName(split); } - if (name.isEmpty()) { - setNameVisible(false); - } else { - setNameVisible(true); + if (DisguiseConfig.isScoreboardNames()) { + setMultiName(DisguiseUtilities.splitNewLine(name)); } + setNameVisible(!name.isEmpty(), true); playerName = name; if (gameProfile != null) { @@ -310,19 +344,23 @@ public class PlayerDisguise extends TargetedDisguise { } public PlayerDisguise setSkin(String newSkin) { - if (newSkin != null && newSkin.length() > 70) { + if (newSkin != null && newSkin.length() > 70 && newSkin.startsWith("{") && newSkin.endsWith("}")) { try { return setSkin(DisguiseUtilities.getGson().fromJson(newSkin, WrappedGameProfile.class)); } catch (Exception ex) { if (!"12345".equals("%%__USER__%%")) { throw new IllegalArgumentException(String.format( - "The skin %s is too long to be a playername, but cannot be parsed to a GameProfile!", - newSkin)); + "The skin %s is too long to normally be a playername, but cannot be parsed to a " + + "GameProfile!", newSkin)); } } } + if (newSkin != null && newSkin.length() > 16) { + newSkin = null; + } + String oldSkin = skinToUse; skinToUse = newSkin; @@ -376,8 +414,6 @@ public class PlayerDisguise extends TargetedDisguise { return this; } - Validate.notEmpty(gameProfile.getName(), "Name must be set"); - currentLookup = null; this.skinToUse = gameProfile.getName(); diff --git a/src/main/java/me/libraryaddict/disguise/disguisetypes/watchers/DroppedItemWatcher.java b/src/main/java/me/libraryaddict/disguise/disguisetypes/watchers/DroppedItemWatcher.java index e75e0fcf..11c32a7d 100644 --- a/src/main/java/me/libraryaddict/disguise/disguisetypes/watchers/DroppedItemWatcher.java +++ b/src/main/java/me/libraryaddict/disguise/disguisetypes/watchers/DroppedItemWatcher.java @@ -22,7 +22,7 @@ public class DroppedItemWatcher extends FlagWatcher { setData(MetaIndex.DROPPED_ITEM, item); sendData(MetaIndex.DROPPED_ITEM); - if (!getDisguise().isCustomName()) { + if (!getDisguise().isCustomDisguiseName()) { getDisguise().setDisguiseName(TranslateType.DISGUISES.get(DisguiseType.DROPPED_ITEM.toReadable()) + " " + TranslateType.DISGUISE_OPTIONS_PARAMETERS .get(ReflectionManager.toReadable((item == null ? Material.AIR : item.getType()).name()))); diff --git a/src/main/java/me/libraryaddict/disguise/disguisetypes/watchers/FallingBlockWatcher.java b/src/main/java/me/libraryaddict/disguise/disguisetypes/watchers/FallingBlockWatcher.java index 2a809053..9b37d88f 100644 --- a/src/main/java/me/libraryaddict/disguise/disguisetypes/watchers/FallingBlockWatcher.java +++ b/src/main/java/me/libraryaddict/disguise/disguisetypes/watchers/FallingBlockWatcher.java @@ -87,7 +87,7 @@ public class FallingBlockWatcher extends FlagWatcher { this.block = block; - if (!getDisguise().isCustomName()) { + if (!getDisguise().isCustomDisguiseName()) { getDisguise().setDisguiseName(TranslateType.DISGUISE_OPTIONS_PARAMETERS.get("Block") + " " + TranslateType.DISGUISE_OPTIONS_PARAMETERS .get(ReflectionManager.toReadable(block.getType().name()))); diff --git a/src/main/java/me/libraryaddict/disguise/utilities/DisguiseUtilities.java b/src/main/java/me/libraryaddict/disguise/utilities/DisguiseUtilities.java index b2007cc9..111ba41d 100644 --- a/src/main/java/me/libraryaddict/disguise/utilities/DisguiseUtilities.java +++ b/src/main/java/me/libraryaddict/disguise/utilities/DisguiseUtilities.java @@ -22,6 +22,7 @@ import me.libraryaddict.disguise.LibsDisguises; import me.libraryaddict.disguise.disguisetypes.*; import me.libraryaddict.disguise.disguisetypes.TargetedDisguise.TargetType; import me.libraryaddict.disguise.disguisetypes.watchers.AgeableWatcher; +import me.libraryaddict.disguise.disguisetypes.watchers.LivingWatcher; import me.libraryaddict.disguise.disguisetypes.watchers.ZombieWatcher; import me.libraryaddict.disguise.utilities.json.*; import me.libraryaddict.disguise.utilities.mineskin.MineSkinAPI; @@ -84,6 +85,7 @@ public class DisguiseUtilities { } public void handleTeam(Scoreboard board, boolean nameVisible) { + nameVisible = !DisguiseConfig.isArmorstandsName() && nameVisible; Team team = board.getTeam(getTeamName()); if (team == null) { @@ -1037,7 +1039,7 @@ public class DisguiseUtilities { if (disguise.isDisguiseInUse() && disguise.getEntity() instanceof Player && disguise.getEntity().getName().equalsIgnoreCase(player)) { - removeSelfDisguise((Player) disguise.getEntity()); + removeSelfDisguise(disguise); if (disguise.isSelfDisguiseVisible()) { selfDisguised.add(disguise.getEntity().getUniqueId()); @@ -1167,7 +1169,7 @@ public class DisguiseUtilities { try { if (selfDisguised.contains(disguise.getEntity().getUniqueId()) && disguise.isDisguiseInUse()) { - removeSelfDisguise((Player) disguise.getEntity()); + removeSelfDisguise(disguise); selfDisguised.add(disguise.getEntity().getUniqueId()); @@ -1257,16 +1259,21 @@ public class DisguiseUtilities { file.delete(); } - public static void removeSelfDisguise(Player player) { + public static void removeSelfDisguise(Disguise disguise) { if (!Bukkit.isPrimaryThread()) throw new IllegalStateException("Cannot modify disguises on an async thread"); + Player player = (Player) disguise.getEntity(); + if (!selfDisguised.contains(player.getUniqueId())) { return; } + int[] ids = Arrays.copyOf(disguise.getArmorstandIds(), 1 + disguise.getMultiName().length); + ids[ids.length - 1] = DisguiseAPI.getSelfDisguiseId(); + // Send a packet to destroy the fake entity - PacketContainer packet = getDestroyPacket(DisguiseAPI.getSelfDisguiseId()); + PacketContainer packet = getDestroyPacket(ids); try { ProtocolLibrary.getProtocolManager().sendServerPacket(player, packet); @@ -1439,7 +1446,7 @@ public class DisguiseUtilities { } public static String[] getExtendedNameSplit(String playerName, String name) { - if (name.length() <= 16 && !DisguiseConfig.isScoreboardDisguiseNames()) { + if (name.length() <= 16 && !DisguiseConfig.isScoreboardNames()) { throw new IllegalStateException("This can only be used for names longer than 16 characters!"); } @@ -1450,7 +1457,7 @@ public class DisguiseUtilities { Scoreboard board = Bukkit.getScoreboardManager().getMainScoreboard(); // If name is short enough to be used outside of player name - if (DisguiseConfig.isScoreboardDisguiseNames() && name.length() <= 32) { + if (DisguiseConfig.isScoreboardNames() && name.length() <= 32) { String[] newName = new String[]{name, playerName, ""}; if (name.length() > 16) { @@ -1749,6 +1756,39 @@ public class DisguiseUtilities { "\""; } + public static String quoteNewLine(String string) { + return string.replaceAll("\\\\(?=\\\\+n)", "\\\\\\\\"); + } + + public static String[] splitNewLine(String string) { + Pattern regex = Pattern.compile("\\\\+n"); + Matcher result = regex.matcher(string); + + ArrayList lines = new ArrayList<>(); + StringBuilder builder = new StringBuilder(); + int last = 0; + + while (result.find()) { + builder.append(string, last, result.start()); + last = result.end(); + + if (result.group().matches("(\\\\\\\\)+n")) { + builder.append(result.group().replace("\\\\", "\\")); + } else { + String group = result.group().replace("\\\\", "\\"); + + builder.append(group, 0, group.length() - 2); + + lines.add(builder.toString()); + builder = new StringBuilder(); + } + } + + lines.add(builder.toString() + string.substring(last)); + + return lines.toArray(new String[0]); + } + public static String[] split(String string) { // Regex where we first match any character that isn't a slash, if it is a slash then it must not have more // slashes until it hits the quote @@ -2020,7 +2060,7 @@ public class DisguiseUtilities { } // Remove the old disguise, else we have weird disguises around the place - DisguiseUtilities.removeSelfDisguise(player); + DisguiseUtilities.removeSelfDisguise(disguise); // If the disguised player can't see himself. Return if (!disguise.isSelfDisguiseVisible() || !PacketsManager.isViewDisguisesListenerEnabled() || @@ -2237,6 +2277,100 @@ public class DisguiseUtilities { } } + public static ArrayList getNamePackets(Disguise disguise, String[] oldNames) { + ArrayList packets = new ArrayList<>(); + String[] newNames = + (disguise instanceof PlayerDisguise && !((PlayerDisguise) disguise).isNameVisible()) ? new String[0] : + disguise.getMultiName(); + int[] standIds = disguise.getArmorstandIds(); + int[] destroyIds = new int[0]; + + if (oldNames.length > newNames.length) { + // Destroy packet + destroyIds = Arrays.copyOfRange(standIds, newNames.length, oldNames.length); + } + + for (int i = 0; i < newNames.length; i++) { + if (i < oldNames.length) { + if (newNames[i].equals(oldNames[i]) || newNames[i].isEmpty()) { + continue; + } + + WrappedDataWatcher watcher = new WrappedDataWatcher(); + + Object name = NmsVersion.v1_13.isSupported() ? Optional.of(WrappedChatComponent.fromText(newNames[i])) : + newNames[i]; + + WrappedDataWatcher.WrappedDataWatcherObject obj = ReflectionManager.createDataWatcherObject( + NmsVersion.v1_13.isSupported() ? MetaIndex.ENTITY_CUSTOM_NAME : + MetaIndex.ENTITY_CUSTOM_NAME_OLD, name); + + watcher.setObject(obj, ReflectionManager.convertInvalidMeta(name)); + + PacketContainer metaPacket = ProtocolLibrary.getProtocolManager() + .createPacketConstructor(PacketType.Play.Server.ENTITY_METADATA, 0, watcher, true) + .createPacket(standIds[i], watcher, true); + + packets.add(metaPacket); + } else if (newNames[i].isEmpty()) { + destroyIds = Arrays.copyOf(destroyIds, destroyIds.length + 1); + destroyIds[destroyIds.length - 1] = standIds[i]; + } else { + PacketContainer packet = new PacketContainer(Server.SPAWN_ENTITY_LIVING); + packet.getIntegers().write(0, standIds[i]); + packet.getIntegers().write(1, DisguiseType.ARMOR_STAND.getTypeId()); + + packet.getUUIDs().write(0, UUID.randomUUID()); + + Location loc = disguise.getEntity().getLocation(); + + packet.getDoubles().write(0, loc.getX()); + packet.getDoubles().write(1, loc.getY() + -0.175 + (0.28 * i)); + packet.getDoubles().write(2, loc.getZ()); + packets.add(packet); + + WrappedDataWatcher watcher = new WrappedDataWatcher(); + + for (MetaIndex index : MetaIndex.getMetaIndexes(LivingWatcher.class)) { + Object val = index.getDefault(); + + if (index == MetaIndex.ENTITY_META) { + val = (byte) 32; + } else if (index == MetaIndex.ARMORSTAND_META) { + val = (byte) 17; + } else if (index == MetaIndex.ENTITY_CUSTOM_NAME) { + val = Optional.of(WrappedChatComponent.fromText(newNames[i])); + } else if (index == MetaIndex.ENTITY_CUSTOM_NAME_OLD) { + val = newNames[i]; + } else if (index == MetaIndex.ENTITY_CUSTOM_NAME_VISIBLE) { + val = true; + } + + WrappedDataWatcher.WrappedDataWatcherObject obj = ReflectionManager + .createDataWatcherObject(index, val); + + watcher.setObject(obj, ReflectionManager.convertInvalidMeta(val)); + } + + if (NmsVersion.v1_15.isSupported()) { + PacketContainer metaPacket = ProtocolLibrary.getProtocolManager() + .createPacketConstructor(PacketType.Play.Server.ENTITY_METADATA, 0, watcher, true) + .createPacket(standIds[i], watcher, true); + + packets.add(metaPacket); + } else { + packet.getDataWatcherModifier().write(0, watcher); + } + } + } + + if (destroyIds.length > 0) { + packets.add(getDestroyPacket(destroyIds)); + } + + return packets; + } + public static Disguise getDisguise(Player observer, int entityId) { // If the entity ID is the same as self disguises id, then it needs to be set to the observers id if (entityId == DisguiseAPI.getSelfDisguiseId()) { diff --git a/src/main/java/me/libraryaddict/disguise/utilities/listeners/DisguiseListener.java b/src/main/java/me/libraryaddict/disguise/utilities/listeners/DisguiseListener.java index 1c84996a..1fc23738 100644 --- a/src/main/java/me/libraryaddict/disguise/utilities/listeners/DisguiseListener.java +++ b/src/main/java/me/libraryaddict/disguise/utilities/listeners/DisguiseListener.java @@ -583,12 +583,19 @@ public class DisguiseListener implements Listener { @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) public void onVehicleEnter(VehicleEnterEvent event) { - if (event.getEntered() instanceof Player && - DisguiseAPI.isDisguised((Player) event.getEntered(), event.getEntered())) { - DisguiseUtilities.removeSelfDisguise((Player) event.getEntered()); - - ((Player) event.getEntered()).updateInventory(); + if (!(event.getEntered() instanceof Player)) { + return; } + + Disguise disguise = DisguiseAPI.getDisguise((Player) event.getEntered(), event.getEntered()); + + if (disguise == null) { + return; + } + + DisguiseUtilities.removeSelfDisguise(disguise); + + ((Player) event.getEntered()).updateInventory(); } @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) diff --git a/src/main/java/me/libraryaddict/disguise/utilities/packets/PacketsManager.java b/src/main/java/me/libraryaddict/disguise/utilities/packets/PacketsManager.java index 678108e9..72b53ec2 100644 --- a/src/main/java/me/libraryaddict/disguise/utilities/packets/PacketsManager.java +++ b/src/main/java/me/libraryaddict/disguise/utilities/packets/PacketsManager.java @@ -25,6 +25,7 @@ public class PacketsManager { private static PacketListener viewDisguisesListener; private static boolean viewDisguisesListenerEnabled; private static PacketsHandler packetsHandler; + private static PacketListener destroyListener; public static void addPacketListeners() { // Add a client listener to cancel them interacting with uninteractable disguised entitys. @@ -160,8 +161,10 @@ public class PacketsManager { } mainListener = new PacketListenerMain(LibsDisguises.getInstance(), packetsToListen); + destroyListener = new PacketListenerDestroyEntity(LibsDisguises.getInstance()); ProtocolLibrary.getProtocolManager().addPacketListener(mainListener); + ProtocolLibrary.getProtocolManager().addPacketListener(destroyListener); } } @@ -183,7 +186,7 @@ public class PacketsManager { if (enabled) { DisguiseUtilities.setupFakeDisguise(disguise); } else { - DisguiseUtilities.removeSelfDisguise(player); + DisguiseUtilities.removeSelfDisguise(disguise); } if (inventoryModifierEnabled && diff --git a/src/main/java/me/libraryaddict/disguise/utilities/packets/packethandlers/PacketHandlerMovement.java b/src/main/java/me/libraryaddict/disguise/utilities/packets/packethandlers/PacketHandlerMovement.java index f54eff1e..6ccf215e 100644 --- a/src/main/java/me/libraryaddict/disguise/utilities/packets/packethandlers/PacketHandlerMovement.java +++ b/src/main/java/me/libraryaddict/disguise/utilities/packets/packethandlers/PacketHandlerMovement.java @@ -18,6 +18,8 @@ import org.bukkit.entity.Player; import org.bukkit.metadata.FixedMetadataValue; import org.bukkit.util.Vector; +import java.util.ArrayList; + /** * Created by libraryaddict on 3/01/2019. */ @@ -38,6 +40,36 @@ public class PacketHandlerMovement implements IPacketHandler { @Override public void handle(Disguise disguise, PacketContainer sentPacket, LibsPackets packets, Player observer, Entity entity) { + handle2(disguise, sentPacket, packets, observer, entity); + + int len = disguise.getMultiName().length; + + if (len == 0) { + return; + } + + ArrayList toAdd = new ArrayList<>(); + + for (PacketContainer packet : packets.getPackets()) { + for (int i = 0; i < len; i++) { + for (int standId : disguise.getArmorstandIds()) { + PacketContainer packet2 = packet.shallowClone(); + packet2.getIntegers().write(0, standId); + + if (packet2.getType() == PacketType.Play.Server.ENTITY_TELEPORT) { + packet2.getDoubles().write(1, packet2.getDoubles().read(1) + -0.175 + (0.28 * i)); + } + + toAdd.add(packet2); + } + } + } + + packets.getPackets().addAll(toAdd); + } + + public void handle2(Disguise disguise, PacketContainer sentPacket, LibsPackets packets, Player observer, + Entity entity) { if (invalid && RandomUtils.nextDouble() < 0.1) { packets.clear(); return; diff --git a/src/main/java/me/libraryaddict/disguise/utilities/packets/packethandlers/PacketHandlerSpawn.java b/src/main/java/me/libraryaddict/disguise/utilities/packets/packethandlers/PacketHandlerSpawn.java index 9ae797c9..19d40118 100644 --- a/src/main/java/me/libraryaddict/disguise/utilities/packets/packethandlers/PacketHandlerSpawn.java +++ b/src/main/java/me/libraryaddict/disguise/utilities/packets/packethandlers/PacketHandlerSpawn.java @@ -151,7 +151,7 @@ public class PacketHandlerSpawn implements IPacketHandler { mods.write(5, pitch); } else if (disguise.getType().isPlayer()) { PlayerDisguise playerDisguise = (PlayerDisguise) disguise; - boolean visibleOrNewCompat = playerDisguise.isNameVisible() || DisguiseConfig.isScoreboardDisguiseNames(); + boolean visibleOrNewCompat = playerDisguise.isNameVisible() || DisguiseConfig.isScoreboardNames(); WrappedGameProfile spawnProfile = visibleOrNewCompat ? playerDisguise.getGameProfile() : ReflectionManager .getGameProfileWithThisSkin(UUID.randomUUID(), visibleOrNewCompat ? playerDisguise.getName() : "", @@ -387,6 +387,8 @@ public class PacketHandlerSpawn implements IPacketHandler { packets.addPacket(newPacket); } + DisguiseUtilities.getNamePackets(disguise, new String[0]).forEach(packets::addPacket); + // If armor must be sent because its currently not displayed and would've been sent normally // This sends the armor packets so that the player isn't naked. diff --git a/src/main/java/me/libraryaddict/disguise/utilities/packets/packetlisteners/PacketListenerViewSelfDisguise.java b/src/main/java/me/libraryaddict/disguise/utilities/packets/packetlisteners/PacketListenerViewSelfDisguise.java index ab85777c..5d4c17aa 100644 --- a/src/main/java/me/libraryaddict/disguise/utilities/packets/packetlisteners/PacketListenerViewSelfDisguise.java +++ b/src/main/java/me/libraryaddict/disguise/utilities/packets/packetlisteners/PacketListenerViewSelfDisguise.java @@ -78,7 +78,8 @@ public class PacketListenerViewSelfDisguise extends PacketAdapter { } for (PacketContainer newPacket : transformed.getPackets()) { - if (newPacket.getType() != Server.PLAYER_INFO) { + if (newPacket.getType() != Server.PLAYER_INFO && + newPacket.getIntegers().read(0) == observer.getEntityId()) { if (newPacket == packet) { newPacket = newPacket.shallowClone(); } diff --git a/src/main/java/me/libraryaddict/disguise/utilities/parser/DisguiseParser.java b/src/main/java/me/libraryaddict/disguise/utilities/parser/DisguiseParser.java index 54dc91b7..3150068c 100644 --- a/src/main/java/me/libraryaddict/disguise/utilities/parser/DisguiseParser.java +++ b/src/main/java/me/libraryaddict/disguise/utilities/parser/DisguiseParser.java @@ -698,6 +698,10 @@ public class DisguiseParser { args[1] = args[1].replace("\\_", " "); + if (DisguiseConfig.isArmorstandsName() && !sender.hasPermission("libsdisguises.multiname")) { + args[1] = DisguiseUtilities.quoteNewLine(args[1]); + } + // Construct the player disguise disguise = new PlayerDisguise(ChatColor.translateAlternateColorCodes('&', args[1])); @@ -904,6 +908,10 @@ public class DisguiseParser { } } + if (DisguiseConfig.isArmorstandsName() && methodToUse.getName().equals("setName") && !sender.hasPermission("libsdisguises.multiname")) { + valueToSet = DisguiseUtilities.quoteNewLine((String) valueToSet); + } + if (FlagWatcher.class.isAssignableFrom(methodToUse.getDeclaringClass())) { methodToUse.invoke(disguise.getWatcher(), valueToSet); } else { diff --git a/src/main/java/me/libraryaddict/disguise/utilities/translations/LibsMsg.java b/src/main/java/me/libraryaddict/disguise/utilities/translations/LibsMsg.java index 84a279b1..763f86dd 100644 --- a/src/main/java/me/libraryaddict/disguise/utilities/translations/LibsMsg.java +++ b/src/main/java/me/libraryaddict/disguise/utilities/translations/LibsMsg.java @@ -59,7 +59,7 @@ public enum LibsMsg { DISG_HELP4(ChatColor.DARK_GREEN + "/disguise "), DISG_PLAYER_AS_DISG(ChatColor.RED + "Successfully disguised %s as a %s!"), DISG_PLAYER_AS_DISG_FAIL(ChatColor.RED + "Failed to disguise %s as a %s!"), - DISGUISED(ChatColor.RED + "Now disguised as a %s"), + DISGUISED(ChatColor.RED + "Now disguised as %s"), DISRADIUS(ChatColor.RED + "Successfully disguised %s entities!"), DISRADIUS_FAIL(ChatColor.RED + "Couldn't find any entities to disguise!"), DMODENT_HELP1(ChatColor.DARK_GREEN + "Choose the options for a disguise then right click a entity to modify it!"), @@ -116,7 +116,7 @@ public enum LibsMsg { "disabled in the config"), DRADIUS_NEEDOPTIONS(ChatColor.RED + "You need to supply a disguise as well as the radius"), DRADIUS_NEEDOPTIONS_ENTITY(ChatColor.RED + "You need to supply a disguise as well as the radius and EntityType"), - FAILED_DISGIUSE(ChatColor.RED + "Failed to disguise as a %s"), + FAILED_DISGIUSE(ChatColor.RED + "Failed to disguise as %s"), GRABBED_SKIN(ChatColor.GOLD + "Grabbed skin and saved as %s!"), PLEASE_WAIT(ChatColor.GRAY + "Please wait..."), INVALID_CLONE(ChatColor.DARK_RED + "Unknown method '%s' - Valid methods are 'IgnoreEquipment' 'DoSneakSprint' " + diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index adbcdb71..082dd4b3 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -66,13 +66,14 @@ SaveDisguises: Players: false Entities: false -# Do names go beyond the 16 char limit for player disguises? -ExtendedNames: false -# Do names resolve into scoreboard teams so changing the player name is flawless? -# This will only work properly for names 28 chars or less in length. -# Notch [DragonSlayer lvl: 28] -# It's recommended to leave this on unless you need it off -ScoreboardNames: true +# There are four options you can use +# VANILLA - Names are limited to 16 chars but can't be changed without resending disguise +# TEAMS - Names are limited to 32 chars but can be changed willy nilly +# EXTENDED - Names are limited to 48 chars but can't be changed without resending disguise +# ARMORSTANDS - Names are limited to 256 chars, uses a mix of armorstands and teams to do this. Slightly hacky. +# Downside of armorstand names is that there's a chance of it becoming desynced from the player disguise +PlayerNames: TEAMS + # How many ticks before tab packet is sent to remove from tablist. This shouldn't need to be touched TablistRemoveDelay: 2 diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index fe84bd7d..f81772ff 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -121,6 +121,8 @@ permissions: libsdisguises.noactionbar: description: Hides the action bar even if enabled in config default: false + libsdisguises.multiname: + description: Allows the command user to set names on different heights libsdisguises.seecmd: description: See all commands in tab-completion default: true