diff --git a/src/main/java/me/libraryaddict/disguise/DisguiseConfig.java b/src/main/java/me/libraryaddict/disguise/DisguiseConfig.java index 1eb6da06..25e0f9d8 100644 --- a/src/main/java/me/libraryaddict/disguise/DisguiseConfig.java +++ b/src/main/java/me/libraryaddict/disguise/DisguiseConfig.java @@ -82,6 +82,15 @@ public class DisguiseConfig { private static UpdatesBranch updatesBranch = UpdatesBranch.SAME_BUILDS; private static int playerDisguisesTablistExpires; private static boolean dynamicExpiry; + private static boolean playerHideArmor; + + public static boolean isPlayerHideArmor() { + return playerHideArmor; + } + + public static void setPlayerHideArmor(boolean playerHiddenArmor) { + playerHideArmor = playerHiddenArmor; + } public static boolean isDynamicExpiry() { return dynamicExpiry; @@ -336,6 +345,7 @@ public class DisguiseConfig { setUUIDGeneratedVersion(config.getInt("UUIDVersion")); setPlayerDisguisesTablistExpires(config.getInt("PlayerDisguisesTablistExpires")); setDynamicExpiry(config.getBoolean("DynamicExpiry")); + setPlayerHideArmor(config.getBoolean("PlayerHideArmor")); if (!LibsPremium.isPremium() && (isSavePlayerDisguises() || isSaveEntityDisguises())) { DisguiseUtilities.getLogger().warning("You must purchase the plugin to use saved disguises!"); diff --git a/src/main/java/me/libraryaddict/disguise/utilities/packets/LibsPackets.java b/src/main/java/me/libraryaddict/disguise/utilities/packets/LibsPackets.java index 108ec6b7..e32e8b12 100644 --- a/src/main/java/me/libraryaddict/disguise/utilities/packets/LibsPackets.java +++ b/src/main/java/me/libraryaddict/disguise/utilities/packets/LibsPackets.java @@ -5,6 +5,7 @@ import com.comphenix.protocol.ProtocolLibrary; import com.comphenix.protocol.events.PacketContainer; import me.libraryaddict.disguise.LibsDisguises; import me.libraryaddict.disguise.disguisetypes.Disguise; +import me.libraryaddict.disguise.utilities.DisguiseUtilities; import org.bukkit.Bukkit; import org.bukkit.entity.Player; @@ -20,6 +21,7 @@ public class LibsPackets { private boolean isSpawnPacket; private Disguise disguise; private boolean doNothing; + private int removeMetaAt = -1; public LibsPackets(Disguise disguise) { this.disguise = disguise; @@ -29,6 +31,10 @@ public class LibsPackets { doNothing = true; } + public void setRemoveMetaAt(int tick) { + removeMetaAt = tick; + } + public boolean isUnhandled() { return doNothing; } @@ -70,18 +76,17 @@ public class LibsPackets { public void sendDelayed(final Player observer) { Iterator>> itel = delayedPackets.entrySet().iterator(); - Optional largestTick = delayedPackets.keySet().stream().max(Integer::compare); - - if (!largestTick.isPresent()) { - return; - } while (itel.hasNext()) { Map.Entry> entry = itel.next(); // If this is the last delayed packet - final boolean isRemoveCancel = isSpawnPacket && largestTick.get().equals(entry.getKey()); + final boolean isRemoveCancel = isSpawnPacket && entry.getKey() >= removeMetaAt && removeMetaAt >= 0; Bukkit.getScheduler().scheduleSyncDelayedTask(LibsDisguises.getInstance(), () -> { + if (isRemoveCancel) { + PacketsManager.getPacketsHandler().removeCancel(disguise, observer); + } + try { for (PacketContainer packet : entry.getValue()) { ProtocolLibrary.getProtocolManager().sendServerPacket(observer, packet, false); @@ -90,10 +95,6 @@ public class LibsPackets { catch (InvocationTargetException e) { e.printStackTrace(); } - - if (isRemoveCancel) { - PacketsManager.getPacketsHandler().removeCancel(disguise, observer); - } }, entry.getKey()); } } diff --git a/src/main/java/me/libraryaddict/disguise/utilities/packets/PacketsHandler.java b/src/main/java/me/libraryaddict/disguise/utilities/packets/PacketsHandler.java index dd1b913a..e51abe92 100644 --- a/src/main/java/me/libraryaddict/disguise/utilities/packets/PacketsHandler.java +++ b/src/main/java/me/libraryaddict/disguise/utilities/packets/PacketsHandler.java @@ -31,7 +31,7 @@ public class PacketsHandler { packetHandlers.add(new PacketHandlerBed()); packetHandlers.add(new PacketHandlerCollect()); packetHandlers.add(new PacketHandlerEntityStatus()); - packetHandlers.add(new PacketHandlerEquipment()); + packetHandlers.add(new PacketHandlerEquipment(this)); packetHandlers.add(new PacketHandlerHeadRotation()); packetHandlers.add(new PacketHandlerMetadata(this)); packetHandlers.add(new PacketHandlerMovement()); diff --git a/src/main/java/me/libraryaddict/disguise/utilities/packets/packethandlers/PacketHandlerEquipment.java b/src/main/java/me/libraryaddict/disguise/utilities/packets/packethandlers/PacketHandlerEquipment.java index 22d9b103..6b0a0085 100644 --- a/src/main/java/me/libraryaddict/disguise/utilities/packets/packethandlers/PacketHandlerEquipment.java +++ b/src/main/java/me/libraryaddict/disguise/utilities/packets/packethandlers/PacketHandlerEquipment.java @@ -6,8 +6,11 @@ import com.comphenix.protocol.wrappers.WrappedDataWatcher; import com.comphenix.protocol.wrappers.WrappedWatchableObject; import me.libraryaddict.disguise.DisguiseConfig; import me.libraryaddict.disguise.disguisetypes.Disguise; +import me.libraryaddict.disguise.disguisetypes.MetaIndex; +import me.libraryaddict.disguise.utilities.DisguiseUtilities; import me.libraryaddict.disguise.utilities.packets.IPacketHandler; import me.libraryaddict.disguise.utilities.packets.LibsPackets; +import me.libraryaddict.disguise.utilities.packets.PacketsHandler; import me.libraryaddict.disguise.utilities.reflection.ReflectionManager; import org.bukkit.Material; import org.bukkit.entity.Entity; @@ -22,6 +25,12 @@ import java.util.List; * Created by libraryaddict on 3/01/2019. */ public class PacketHandlerEquipment implements IPacketHandler { + private PacketsHandler packetsHandler; + + public PacketHandlerEquipment(PacketsHandler packetsHandler) { + this.packetsHandler = packetsHandler; + } + @Override public PacketType[] getHandledPackets() { return new PacketType[]{PacketType.Play.Server.ENTITY_EQUIPMENT}; @@ -30,6 +39,17 @@ public class PacketHandlerEquipment implements IPacketHandler { @Override public void handle(Disguise disguise, PacketContainer sentPacket, LibsPackets packets, Player observer, Entity entity) { + if (DisguiseConfig.isPlayerHideArmor() && packetsHandler.isCancelMeta(disguise, observer)) { + packets.clear(); + + PacketContainer equipPacket = sentPacket.shallowClone(); + + packets.addPacket(equipPacket); + + equipPacket.getModifier().write(2, ReflectionManager.getNmsItem(new ItemStack(Material.AIR))); + return; + } + // Else if the disguise is updating equipment EquipmentSlot slot = ReflectionManager.createEquipmentSlot(packets.getPackets().get(0).getModifier().read(1)); @@ -55,8 +75,8 @@ public class PacketHandlerEquipment implements IPacketHandler { List list = new ArrayList<>(); if (DisguiseConfig.isMetadataPacketsEnabled()) { - WrappedWatchableObject watch = ReflectionManager - .createWatchable(0, WrappedDataWatcher.getEntityWatcher(entity).getByte(0)); + WrappedWatchableObject watch = ReflectionManager.createWatchable(MetaIndex.ENTITY_META.getIndex(), + WrappedDataWatcher.getEntityWatcher(entity).getByte(0)); if (watch != null) list.add(watch); 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 bc41f93b..b9a8210c 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 @@ -11,8 +11,8 @@ import me.libraryaddict.disguise.DisguiseConfig; import me.libraryaddict.disguise.disguisetypes.*; import me.libraryaddict.disguise.disguisetypes.watchers.FallingBlockWatcher; 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.LibsPremium; import me.libraryaddict.disguise.utilities.packets.IPacketHandler; import me.libraryaddict.disguise.utilities.packets.LibsPackets; import me.libraryaddict.disguise.utilities.packets.PacketsHandler; @@ -32,6 +32,7 @@ import org.bukkit.util.Vector; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.UUID; /** * Created by libraryaddict on 3/01/2019. @@ -56,6 +57,10 @@ public class PacketHandlerSpawn implements IPacketHandler { packets.clear(); + if (disguise.getType() == DisguiseType.UNKNOWN) { + return; + } + constructSpawnPackets(observer, packets, entity); } @@ -69,36 +74,6 @@ public class PacketHandlerSpawn implements IPacketHandler { disguise.setEntity(disguisedEntity); } - // This sends the armor packets so that the player isn't naked. - // Please note it only sends the packets that wouldn't be sent normally - if (DisguiseConfig.isEquipmentPacketsEnabled()) { - for (EquipmentSlot slot : EquipmentSlot.values()) { - ItemStack itemstack = disguise.getWatcher().getItemStack(slot); - - if (itemstack == null || itemstack.getType() == Material.AIR) { - continue; - } - - if (disguisedEntity instanceof LivingEntity) { - ItemStack item = ReflectionManager.getEquipment(slot, disguisedEntity); - - if (item != null && item.getType() != Material.AIR) { - continue; - } - } - - PacketContainer packet = new PacketContainer(PacketType.Play.Server.ENTITY_EQUIPMENT); - - StructureModifier mods = packet.getModifier(); - - mods.write(0, disguisedEntity.getEntityId()); - mods.write(1, ReflectionManager.createEnumItemSlot(slot)); - mods.write(2, ReflectionManager.getNmsItem(itemstack)); - - packets.addDelayedPacket(packet); - } - } - if (DisguiseConfig.isMiscDisguisesForLivingEnabled()) { if (disguise.getWatcher() instanceof LivingWatcher) { @@ -180,30 +155,35 @@ public class PacketHandlerSpawn implements IPacketHandler { } else if (disguise.getType().isPlayer()) { PlayerDisguise playerDisguise = (PlayerDisguise) disguise; - String name = playerDisguise.getName(); - WrappedGameProfile gameProfile = playerDisguise.getGameProfile(); + WrappedGameProfile spawnProfile = playerDisguise.getGameProfile(); int entityId = disguisedEntity.getEntityId(); - // Send player info along with the disguise - PacketContainer sendTab = new PacketContainer(PacketType.Play.Server.PLAYER_INFO); + if (!playerDisguise.isDisplayedInTab()) { + // Send player info along with the disguise + PacketContainer sendTab = new PacketContainer(PacketType.Play.Server.PLAYER_INFO); - if (!((PlayerDisguise) disguise).isDisplayedInTab()) { // Add player to the list, necessary to spawn them sendTab.getModifier().write(0, ReflectionManager.getEnumPlayerInfoAction(0)); List playerList = Collections - .singletonList(ReflectionManager.getPlayerInfoData(sendTab.getHandle(), gameProfile)); + .singletonList(ReflectionManager.getPlayerInfoData(sendTab.getHandle(), spawnProfile)); sendTab.getModifier().write(1, playerList); packets.addPacket(sendTab); + + // Remove player from the list + PacketContainer deleteTab = sendTab.shallowClone(); + deleteTab.getModifier().write(0, ReflectionManager.getEnumPlayerInfoAction(4)); + + packets.addDelayedPacket(deleteTab, DisguiseConfig.getPlayerDisguisesTablistExpires()); } // Spawn the player PacketContainer spawnPlayer = new PacketContainer(PacketType.Play.Server.NAMED_ENTITY_SPAWN); spawnPlayer.getIntegers().write(0, entityId); // Id - spawnPlayer.getModifier().write(1, gameProfile.getUUID()); + spawnPlayer.getModifier().write(1, spawnProfile.getUUID()); Location spawnAt = disguisedEntity.getLocation(); @@ -240,15 +220,7 @@ public class PacketHandlerSpawn implements IPacketHandler { packets.addPacket(spawnPlayer); - if (DisguiseConfig.isBedPacketsEnabled() && ((PlayerWatcher) disguise.getWatcher()).isSleeping()) { - PacketContainer[] bedPackets = DisguiseUtilities.getBedPackets( - loc.clone().subtract(0, DisguiseUtilities.getYModifier(disguisedEntity, disguise), 0), - observer.getLocation(), ((PlayerDisguise) disguise)); - - for (PacketContainer packet : bedPackets) { - packets.addPacket(packet); - } - } else if (!selfDisguise) { + if (!selfDisguise) { // Teleport the player back to where he's supposed to be PacketContainer teleportPacket = new PacketContainer(PacketType.Play.Server.ENTITY_TELEPORT); @@ -263,10 +235,8 @@ public class PacketHandlerSpawn implements IPacketHandler { bytes.write(0, yaw); bytes.write(1, pitch); - packets.addPacket(teleportPacket); - } + packets.addDelayedPacket(teleportPacket, 3); - if (!selfDisguise) { // Send a metadata packet PacketContainer metaPacket = new PacketContainer(PacketType.Play.Server.ENTITY_METADATA); @@ -281,15 +251,8 @@ public class PacketHandlerSpawn implements IPacketHandler { // Add a delay to remove the entry from 'cancelMeta' - packets.addDelayedPacket(metaPacket, 4); - } - - // Remove player from the list - PacketContainer deleteTab = sendTab.shallowClone(); - deleteTab.getModifier().write(0, ReflectionManager.getEnumPlayerInfoAction(4)); - - if (!((PlayerDisguise) disguise).isDisplayedInTab()) { - packets.addDelayedPacket(deleteTab, DisguiseConfig.getPlayerDisguisesTablistExpires()); + packets.addDelayedPacket(metaPacket, 7); + packets.setRemoveMetaAt(7); } } else if (disguise.getType().isMob() || disguise.getType() == DisguiseType.ARMOR_STAND) { Vector vec = disguisedEntity.getVelocity(); @@ -336,8 +299,10 @@ public class PacketHandlerSpawn implements IPacketHandler { .createSanitizedDataWatcher(WrappedDataWatcher.getEntityWatcher(disguisedEntity), disguise.getWatcher())); } else if (disguise.getType().isMisc()) { - int objectId = disguise.getType().getObjectId(); int data = ((MiscDisguise) disguise).getData(); + double x = loc.getX(); + double y = loc.getY(); + double z = loc.getZ(); if (disguise.getType() == DisguiseType.FALLING_BLOCK) { ItemStack block = ((FallingBlockWatcher) disguise.getWatcher()).getBlock(); @@ -351,11 +316,14 @@ public class PacketHandlerSpawn implements IPacketHandler { data = ((((int) loc.getYaw() % 360) + 720 + 45) / 90) % 4; } - Object nmsEntity = ReflectionManager.getNmsEntity(disguisedEntity); + Object entityType = ReflectionManager.getEntityType(disguise.getType().getEntityType()); + + Object[] params = new Object[]{disguisedEntity.getEntityId(), disguisedEntity.getUniqueId(), x, y, z, + loc.getPitch(), loc.getYaw(), entityType, data, + ReflectionManager.getVec3D(disguisedEntity.getVelocity())}; PacketContainer spawnEntity = ProtocolLibrary.getProtocolManager() - .createPacketConstructor(PacketType.Play.Server.SPAWN_ENTITY, nmsEntity, objectId, data) - .createPacket(nmsEntity, objectId, data); + .createPacketConstructor(PacketType.Play.Server.SPAWN_ENTITY, params).createPacket(params); packets.addPacket(spawnEntity); // If it's not the same type, then highly likely they have different velocity settings which we'd want to @@ -399,5 +367,55 @@ public class PacketHandlerSpawn implements IPacketHandler { packets.addPacket(newPacket); } + + // If armor must be sent because its currently not displayed and would've been sent normally + boolean delayedArmor = + DisguiseConfig.isPlayerHideArmor() && (disguise.isPlayerDisguise() && disguisedEntity != observer) && + disguisedEntity instanceof LivingEntity; + // This sends the armor packets so that the player isn't naked. + if (DisguiseConfig.isEquipmentPacketsEnabled() || delayedArmor) { + for (EquipmentSlot slot : EquipmentSlot.values()) { + // Get what the disguise wants to show for its armor + ItemStack itemToSend = disguise.getWatcher().getItemStack(slot); + + // If the disguise armor isn't visible + if (itemToSend == null || itemToSend.getType() == Material.AIR) { + // If we need to send the natural armor if possible + if (delayedArmor) { + itemToSend = ReflectionManager.getEquipment(slot, disguisedEntity); + + // If natural armor isn't sent either + if (itemToSend == null || itemToSend.getType() == Material.AIR) { + continue; + } + } else { + // We don't need to send natural armor and disguise armor isn't visible + continue; + } + } else if (!delayedArmor && disguisedEntity instanceof LivingEntity) { + ItemStack item = ReflectionManager.getEquipment(slot, disguisedEntity); + + // If the item was going to be sent anyways + if (item != null && item.getType() != Material.AIR) { + continue; + } + } + + PacketContainer packet = new PacketContainer(PacketType.Play.Server.ENTITY_EQUIPMENT); + + StructureModifier mods = packet.getModifier(); + + mods.write(0, disguisedEntity.getEntityId()); + mods.write(1, ReflectionManager.createEnumItemSlot(slot)); + mods.write(2, ReflectionManager.getNmsItem(itemToSend)); + + if (delayedArmor) { + packets.addDelayedPacket(packet, 7); + packets.setRemoveMetaAt(7); + } else { + packets.addDelayedPacket(packet); + } + } + } } } diff --git a/src/main/java/me/libraryaddict/disguise/utilities/reflection/ReflectionManager.java b/src/main/java/me/libraryaddict/disguise/utilities/reflection/ReflectionManager.java index 16518c29..6d4e849d 100644 --- a/src/main/java/me/libraryaddict/disguise/utilities/reflection/ReflectionManager.java +++ b/src/main/java/me/libraryaddict/disguise/utilities/reflection/ReflectionManager.java @@ -16,6 +16,7 @@ import org.bukkit.entity.*; import org.bukkit.inventory.EquipmentSlot; import org.bukkit.inventory.ItemStack; import org.bukkit.potion.PotionEffect; +import org.bukkit.util.Vector; import java.io.IOException; import java.io.InputStream; @@ -976,6 +977,19 @@ public class ReflectionManager { return null; } + public static Object getVec3D(Vector vector) { + try { + Constructor c = getNmsConstructor("Vec3D", double.class, double.class, double.class); + + return c.newInstance(vector.getX(), vector.getY(), vector.getZ()); + } + catch (Exception ex) { + ex.printStackTrace(); + } + + return null; + } + public static int getEntityType(Object nmsEntity) { try { Field entityTypesField = null; diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 967dc123..a707c4d4 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -197,7 +197,7 @@ ShowPlayerDisguisesInTab: false # 2 seconds is normally long enough to load the skin properly, but sometimes the server needs longer # This is in ticks, there are 20 ticks in every second. 40 ticks = 2 seconds. 200 ticks = 10 seconds. # This option is ignored if 'ShowPlayerDisguisesInTab' is enabled. -PlayerDisguisesTablistExpires: 40 +PlayerDisguisesTablistExpires: 60 # Don't like players able to set themselves invisible when using the disguise commands? Toggle this to true and no one can use setInvisible! Plugins can still use this however. DisableInvisibility: false @@ -208,6 +208,11 @@ DisableInvisibility: false # Expired message can be hidden with an empty translation message DynamicExpiry: false +# Some players have issues with conflicting plugins where disguised entities will show the wrong armor +# This should be left alone unless you're trying to solve this issue. Such as MM and stone blocks. +# When true, the plugin will hide player disguises armor to prevent a minor visual bug for half a second +PlayerHideArmor: true + # This will help performance, especially with CPU # Due to safety reasons, self disguises can never have their packets disabled. PacketsEnabled: