diff --git a/Plugins/Mineplex.Core/src/mineplex/core/disguise/DisguiseManager.java b/Plugins/Mineplex.Core/src/mineplex/core/disguise/DisguiseManager.java index 44dc988fc..59783273d 100644 --- a/Plugins/Mineplex.Core/src/mineplex/core/disguise/DisguiseManager.java +++ b/Plugins/Mineplex.Core/src/mineplex/core/disguise/DisguiseManager.java @@ -228,6 +228,9 @@ public class DisguiseManager extends MiniPlugin implements IPacketHandler if (!spawnedIn) { refreshTrackers(disguise.getEntity().getBukkitEntity()); + } else + { + disguise.markSpawnedIn(); } disguise.onDisguise(true); diff --git a/Plugins/Mineplex.Core/src/mineplex/core/disguise/disguises/DisguiseBase.java b/Plugins/Mineplex.Core/src/mineplex/core/disguise/disguises/DisguiseBase.java index b9e6cdd9c..e6e02767b 100644 --- a/Plugins/Mineplex.Core/src/mineplex/core/disguise/disguises/DisguiseBase.java +++ b/Plugins/Mineplex.Core/src/mineplex/core/disguise/disguises/DisguiseBase.java @@ -1,7 +1,11 @@ package mineplex.core.disguise.disguises; -import mineplex.core.common.DummyEntity; -import mineplex.core.common.util.UtilPlayer; +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.function.Predicate; +import java.util.function.Supplier; import net.minecraft.server.v1_8_R3.*; @@ -10,12 +14,8 @@ import org.bukkit.entity.EntityType; import org.bukkit.entity.Player; import org.bukkit.event.entity.CreatureSpawnEvent; -import java.lang.ref.WeakReference; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.function.Consumer; -import java.util.function.Supplier; +import mineplex.core.common.DummyEntity; +import mineplex.core.common.util.UtilPlayer; public abstract class DisguiseBase { @@ -32,6 +32,8 @@ public abstract class DisguiseBase */ private boolean _hideIfNotDisguised = false; + protected boolean _spawnedIn = false; + public DisguiseBase(EntityType entityType, org.bukkit.entity.Entity entity) { if (entity == null) @@ -67,7 +69,7 @@ public abstract class DisguiseBase DataWatcher.watch(1, getEntity().getDataWatcher().getShort(1), net.minecraft.server.v1_8_R3.Entity.META_AIR, (int) getEntity().getDataWatcher().getShort(1)); } - public void sendToWatchers(Supplier supplier) + protected void sendToWatchers(Predicate protocolPredicate, Supplier supplier) { if (getEntity() == null || !getEntity().getBukkitEntity().isValid() || !(getEntity().world instanceof WorldServer)) return; @@ -83,12 +85,16 @@ public abstract class DisguiseBase for (EntityPlayer player : tracker.get(getEntity().getId()).trackedPlayers) { + int protocol = player.getProtocol(); + if (!protocolPredicate.test(protocol)) + continue; + if (packet instanceof PacketPlayOutEntityMetadata) { - player.playerConnection.sendPacket(modifyMetaPacket(player.getProtocol(), packet)); + player.playerConnection.sendPacket(modifyMetaPacket(protocol, packet)); } else if (packet instanceof PacketPlayOutSpawnEntityLiving) { - player.playerConnection.sendPacket(modifySpawnPacket(player.getProtocol(), packet)); + player.playerConnection.sendPacket(modifySpawnPacket(protocol, packet)); } else { player.playerConnection.sendPacket(packet); @@ -96,6 +102,11 @@ public abstract class DisguiseBase } } + protected void sendToWatchers(Supplier supplier) + { + sendToWatchers(x -> true, supplier); + } + public abstract Packet getSpawnPacket(); public Packet modifySpawnPacket(int protocol, Packet packet) @@ -203,6 +214,11 @@ public abstract class DisguiseBase } + public void markSpawnedIn() + { + _spawnedIn = true; + } + public void setHideIfNotDisguised(boolean hideIfNotDisguised) { this._hideIfNotDisguised = hideIfNotDisguised; diff --git a/Plugins/Mineplex.Core/src/mineplex/core/disguise/disguises/DisguiseCreature.java b/Plugins/Mineplex.Core/src/mineplex/core/disguise/disguises/DisguiseCreature.java index 5e8eaaf8c..65b1f1174 100644 --- a/Plugins/Mineplex.Core/src/mineplex/core/disguise/disguises/DisguiseCreature.java +++ b/Plugins/Mineplex.Core/src/mineplex/core/disguise/disguises/DisguiseCreature.java @@ -1,10 +1,10 @@ package mineplex.core.disguise.disguises; import java.util.ArrayList; +import java.util.Iterator; import java.util.List; -import java.util.stream.Collectors; -import com.mineplex.MetaWrapper; +import com.mineplex.MetadataRewriter; import com.mineplex.ProtocolVersion; import net.minecraft.server.v1_8_R3.*; @@ -78,45 +78,112 @@ public abstract class DisguiseCreature extends DisguiseInsentient return packet; } + // ---- Metadata processing + + // This WON'T be post-processed by the Spigot metadata processor + @Override public Packet modifySpawnPacket(int protocol, Packet packet) { if (protocol >= ProtocolVersion.v1_10_PRE) { PacketPlayOutSpawnEntityLiving newSpawn = (PacketPlayOutSpawnEntityLiving) getSpawnPacket(); - newSpawn.m = processSpawnMeta(protocol, DataWatcher.c()); + + // Allow the entity type to be changed (needed on 1.11+) + newSpawn.b = getTypeId(protocol >= ProtocolVersion.v1_11); + + boolean hasArms = false; + List meta = DataWatcher.b(); + + if (meta != null) + { + // Run the meta through our Spigot rewriter + meta = MetadataRewriter.rewrite(getTypeId(false), protocol, meta).objects; + + // Remove indexes >= 12 on 1.11+ + if (protocol >= ProtocolVersion.v1_11) + { + Iterator iter = meta.iterator(); + while (iter.hasNext()) + { + WatchableObject next = iter.next(); + if (next.getIndex().a() == 6) + { + hasArms = true; + } else if (next.getIndex().a() >= 12) + { + iter.remove(); + } + } + } + } else + { + meta = new ArrayList<>(); + } + + if (!hasArms) + { + WatchableObject arms = new WatchableObject<>(0, 0, null, + new DataIndex<>(6, DataType.BYTE), (byte) 0); + meta.add(arms); + } + + newSpawn.m = meta; return newSpawn; } return packet; } - private List processSpawnMeta(int protocol, List list) + protected int getTypeId(boolean separate) { - List newMeta = new ArrayList<>(); - for (WatchableObject meta : list) + return getDisguiseType().getTypeId(); + } + + // This WILL be post-processed by Spigot's metadata processor + + @Override + public Packet modifyMetaPacket(int protocol, Packet packet) + { + if (protocol >= ProtocolVersion.v1_10_PRE) { - MetaWrapper wrapper = new MetaWrapper(meta); - if (wrapper.getIndex() >= 5) // 1.10 + PacketPlayOutEntityMetadata newMeta = new PacketPlayOutEntityMetadata(); + newMeta.a = getEntityId(); + + boolean hasArms = false; + List meta = DataWatcher.b(); + List adjusted = new ArrayList<>(); + + if (meta != null) { - wrapper.setIndex(wrapper.getIndex() + 1); + // Process the meta while we know the entity id + adjusted = MetadataRewriter.rewrite(getTypeId(false), protocol, meta).objects; + hasArms = meta.stream().anyMatch(obj -> obj.getIndex().a() == 6); } - if (protocol < ProtocolVersion.v1_11) + for (int i = 0; i < adjusted.size(); i++) { - newMeta.add(wrapper); - continue; + WatchableObject object = adjusted.get(i); + int index = object.getIndex().a(); + if (index >= 6) + { + index--; + adjusted.set(i, new WatchableObject(0, 0, null, + new DataIndex(index, object.getIndex().b()), object.getValue())); + } } - if (getEntity() instanceof EntityArmorStand && wrapper.getIndex() >= 12) + if (!hasArms) { - // Armor stand meta conflicts with a lot of entities on 1.11+ - continue; + WatchableObject arms = new WatchableObject<>(0, 0, null, + new DataIndex<>(5, DataType.BYTE), (byte) 0); + adjusted.add(arms); } - newMeta.add(wrapper); + newMeta.b = adjusted; + return newMeta; } - return newMeta.stream().map(MetaWrapper::toWatchableObject).collect(Collectors.toList()); + return packet; } } diff --git a/Plugins/Mineplex.Core/src/mineplex/core/disguise/disguises/DisguiseGuardian.java b/Plugins/Mineplex.Core/src/mineplex/core/disguise/disguises/DisguiseGuardian.java index 31222e6da..1f40da543 100644 --- a/Plugins/Mineplex.Core/src/mineplex/core/disguise/disguises/DisguiseGuardian.java +++ b/Plugins/Mineplex.Core/src/mineplex/core/disguise/disguises/DisguiseGuardian.java @@ -1,20 +1,14 @@ package mineplex.core.disguise.disguises; -import java.util.ArrayList; -import java.util.List; -import java.util.stream.Collectors; +import net.minecraft.server.v1_8_R3.EntityGuardian; -import com.mineplex.MetaWrapper; -import com.mineplex.ProtocolVersion; - -import net.minecraft.server.v1_8_R3.*; -import net.minecraft.server.v1_8_R3.DataWatcher.WatchableObject; - -import org.bukkit.entity.ArmorStand; import org.bukkit.entity.EntityType; -public class DisguiseGuardian extends DisguiseCreature +public class DisguiseGuardian extends DisguiseMutable { + private static final int GUARDIAN_ID = 68; + private static final int ELDER_GUARDIAN_ID = 4; + private int target = 0; private boolean elder = false; @@ -41,9 +35,7 @@ public class DisguiseGuardian extends DisguiseCreature DataWatcher.watch(16, Integer.valueOf(newValue), EntityGuardian.META_ELDER, (byte) newValue); - sendToWatchers(() -> new PacketPlayOutEntityDestroy(new int[]{getEntityId()})); - sendToWatchers(this::getSpawnPacket); - sendToWatchers(this::getMetadataPacket); + mutate(); } public boolean isElder() @@ -66,49 +58,9 @@ public class DisguiseGuardian extends DisguiseCreature return "mob.guardian.hit"; } - // ---- Packet modification for 1.11 and up - @Override - public Packet modifySpawnPacket(int protocol, Packet packet) + protected int getTypeId(boolean separate) { - PacketPlayOutSpawnEntityLiving newSpawn = (PacketPlayOutSpawnEntityLiving) super.modifySpawnPacket(protocol, packet); - if (protocol >= ProtocolVersion.v1_11 && isElder()) - { - newSpawn.b = 4; - } - - return newSpawn; - } - - @Override - public Packet modifyMetaPacket(int protocol, Packet packet) - { - if (protocol >= ProtocolVersion.v1_11) - { - PacketPlayOutEntityMetadata newPacket = (PacketPlayOutEntityMetadata) getMetadataPacket(); - newPacket.b = processMeta(newPacket.b); - return newPacket; - } - - return packet; - } - - private List processMeta(List list) - { - List newMeta = new ArrayList<>(); - for (WatchableObject meta : list) - { - MetaWrapper wrapper = new MetaWrapper(meta); - if (wrapper.getIndex() == 11) - { - byte value = (byte) wrapper.getValue(); - newMeta.add(new MetaWrapper(11, DataType.BOOLEAN, (value & 0x02) != 0)); - } else - { - newMeta.add(wrapper); - } - } - - return newMeta.stream().map(MetaWrapper::toWatchableObject).collect(Collectors.toList()); + return separate && isElder() ? ELDER_GUARDIAN_ID : GUARDIAN_ID; } } diff --git a/Plugins/Mineplex.Core/src/mineplex/core/disguise/disguises/DisguiseHorse.java b/Plugins/Mineplex.Core/src/mineplex/core/disguise/disguises/DisguiseHorse.java index cbd7ee097..b306a76b6 100644 --- a/Plugins/Mineplex.Core/src/mineplex/core/disguise/disguises/DisguiseHorse.java +++ b/Plugins/Mineplex.Core/src/mineplex/core/disguise/disguises/DisguiseHorse.java @@ -2,14 +2,24 @@ package mineplex.core.disguise.disguises; import java.util.UUID; -import net.minecraft.server.v1_8_R3.EntityHorse; - -import org.bukkit.entity.*; - import com.google.common.base.Optional; -public class DisguiseHorse extends DisguiseAnimal +import net.minecraft.server.v1_8_R3.EntityAgeable; +import net.minecraft.server.v1_8_R3.EntityHorse; + +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Horse; + +public class DisguiseHorse extends DisguiseMutable { + private static final int HORSE_ID = 100; + private static final int DONKEY_ID = 31; + private static final int MULE_ID = 32; + private static final int ZOMBIE_HORSE_ID = 29; + private static final int SKELETON_HORSE_ID = 28; + + private Horse.Variant variant = Horse.Variant.HORSE; + public DisguiseHorse(org.bukkit.entity.Entity entity) { super(EntityType.HORSE, entity); @@ -19,11 +29,25 @@ public class DisguiseHorse extends DisguiseAnimal DataWatcher.a(20, Integer.valueOf(0), EntityHorse.META_VARIANT, 0); DataWatcher.a(21, String.valueOf(""), EntityHorse.META_OWNER, Optional. absent()); DataWatcher.a(22, Integer.valueOf(0), EntityHorse.META_ARMOR, 0); + + DataWatcher.a(12, new Byte((byte)0), EntityAgeable.META_BABY, false); + } + + public boolean isBaby() + { + return DataWatcher.getByte(12) < 0; + } + + public void setBaby() + { + DataWatcher.watch(12, new Byte((byte) ( -1 )), EntityAgeable.META_BABY, true); } public void setType(Horse.Variant horseType) { DataWatcher.watch(19, Byte.valueOf((byte) horseType.ordinal()), EntityHorse.META_TYPE, horseType.ordinal()); + this.variant = horseType; + mutate(); } public Horse.Variant getType() @@ -72,4 +96,23 @@ public class DisguiseHorse extends DisguiseAnimal { DataWatcher.watch(22, Integer.valueOf(i), EntityHorse.META_ARMOR, i); } + + // 1.11 and up require separate entity ids + @Override + protected int getTypeId(boolean separate) + { + if (separate && variant != Horse.Variant.HORSE) + { + switch (variant) + { + case DONKEY: return DONKEY_ID; + case MULE: return MULE_ID; + case UNDEAD_HORSE: return ZOMBIE_HORSE_ID; + case SKELETON_HORSE: return SKELETON_HORSE_ID; + default: return HORSE_ID; + } + } + + return HORSE_ID; + } } diff --git a/Plugins/Mineplex.Core/src/mineplex/core/disguise/disguises/DisguiseMutable.java b/Plugins/Mineplex.Core/src/mineplex/core/disguise/disguises/DisguiseMutable.java new file mode 100644 index 000000000..d3ff2216c --- /dev/null +++ b/Plugins/Mineplex.Core/src/mineplex/core/disguise/disguises/DisguiseMutable.java @@ -0,0 +1,40 @@ +package mineplex.core.disguise.disguises; + +import java.util.function.Predicate; + +import com.mineplex.ProtocolVersion; + +import net.minecraft.server.v1_8_R3.Packet; +import net.minecraft.server.v1_8_R3.PacketPlayOutEntityDestroy; + +import org.bukkit.entity.Entity; +import org.bukkit.entity.EntityType; + +/** + * Represents a disguise that can "mutate" from one entity type to another. + */ +public abstract class DisguiseMutable extends DisguiseCreature +{ + public DisguiseMutable(EntityType disguiseType, Entity entity) + { + super(disguiseType, entity); + } + + protected void mutate() + { +// if (!_spawnedIn) +// return; + + Predicate pred = v -> v >= ProtocolVersion.v1_11; + sendToWatchers(pred, this::getDestroyPacket); + sendToWatchers(pred, this::getSpawnPacket); + sendToWatchers(pred, this::getMetadataPacket); + } + + private Packet getDestroyPacket() + { + return new PacketPlayOutEntityDestroy(new int[] { getEntityId() }); + } + + protected abstract int getTypeId(boolean separate); +} diff --git a/Plugins/Mineplex.Core/src/mineplex/core/disguise/disguises/DisguiseSkeleton.java b/Plugins/Mineplex.Core/src/mineplex/core/disguise/disguises/DisguiseSkeleton.java index 122490073..34dd47cc0 100644 --- a/Plugins/Mineplex.Core/src/mineplex/core/disguise/disguises/DisguiseSkeleton.java +++ b/Plugins/Mineplex.Core/src/mineplex/core/disguise/disguises/DisguiseSkeleton.java @@ -2,11 +2,16 @@ package mineplex.core.disguise.disguises; import net.minecraft.server.v1_8_R3.EntitySkeleton; -import org.bukkit.entity.*; +import org.bukkit.entity.EntityType; import org.bukkit.entity.Skeleton.SkeletonType; -public class DisguiseSkeleton extends DisguiseMonster +public class DisguiseSkeleton extends DisguiseMutable { + private static final int SKELETON_ID = 51; + private static final int WITHER_SKELETON_ID = 5; + + private SkeletonType type = SkeletonType.NORMAL; + public DisguiseSkeleton(org.bukkit.entity.Entity entity) { super(EntityType.SKELETON, entity); @@ -17,15 +22,24 @@ public class DisguiseSkeleton extends DisguiseMonster public void SetSkeletonType(SkeletonType skeletonType) { DataWatcher.watch(13, Byte.valueOf((byte) skeletonType.getId()), EntitySkeleton.META_TYPE, skeletonType.getId()); + this.type = skeletonType; + mutate(); } - public int GetSkeletonType() + public SkeletonType getSkeletonType() { - return DataWatcher.getByte(13); + return type; } protected String getHurtSound() { return "mob.skeleton.hurt"; } + + // 1.11 and up require separate entity ids + @Override + protected int getTypeId(boolean separate) + { + return separate && type == SkeletonType.WITHER ? WITHER_SKELETON_ID : SKELETON_ID; + } }