Added ability to set the max health of a disguise using attributes

This commit is contained in:
libraryaddict 2014-05-23 14:52:21 +12:00
parent 0c305a8151
commit 26ccaabe83
7 changed files with 89 additions and 9 deletions

View File

@ -65,6 +65,12 @@ KeepDisguises:
PlayerDeath: false PlayerDeath: false
PlayerLogout: false PlayerLogout: false
# This controls if a entitys max health is determined by the entity, or by the disguise.
# Wither is 200, a player is 20. With this enabled, a player disguised as a wither will have the boss bar health accurate to the players health.
# Else it will be 1/20 of the boss bar when he is full health.
# Setting this in LivingWatcher overrides both values.
MaxHealthDeterminedByEntity: true
# This here is a option to turn off misc disguises. # This here is a option to turn off misc disguises.
# This means you can not have a living entity disguise as a non-living entity. # This means you can not have a living entity disguise as a non-living entity.
# This disables the Attributes packet, Non-living entities can still disguise as other non-living # This disables the Attributes packet, Non-living entities can still disguise as other non-living
@ -91,6 +97,7 @@ PacketsEnabled:
# This is good if performance is extremely in need. # This is good if performance is extremely in need.
# This is bad to disable unless you are ONLY going to use the disguises for decorations. # This is bad to disable unless you are ONLY going to use the disguises for decorations.
# To be honest. This is basically "Disable entity animations". That option is called 'AddEntityAnimations' in the config but unlike that, this is always in effect. # To be honest. This is basically "Disable entity animations". That option is called 'AddEntityAnimations' in the config but unlike that, this is always in effect.
# Animations set by use of the api or through the disguise command are still in effect.
Metadata: true Metadata: true
# Movement packets are the biggest cpu hit. These are majorly used to ensure that the disguises facing direction isn't bugged up # Movement packets are the biggest cpu hit. These are majorly used to ensure that the disguises facing direction isn't bugged up
Movement: true Movement: true

15
pom.xml
View File

@ -75,16 +75,21 @@
</repositories> </repositories>
<dependencies> <dependencies>
<dependency>
<groupId>org.bukkit</groupId>
<artifactId>craftbukkit</artifactId>
<version>1.7.8-R0.1-SNAPSHOT</version>
</dependency>
<dependency> <dependency>
<groupId>com.comphenix.protocol</groupId> <groupId>com.comphenix.protocol</groupId>
<artifactId>ProtocolLib</artifactId> <artifactId>ProtocolLib</artifactId>
<version>3.1.0</version> <version>3.1.0</version>
</dependency> </dependency>
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>1.7.8-R0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot</artifactId>
<version>1.7.8-R0.1-SNAPSHOT</version>
</dependency>
</dependencies> </dependencies>
<distributionManagement> <distributionManagement>

View File

@ -17,6 +17,7 @@ public class DisguiseConfig {
private static boolean keepDisguiseEntityDespawn; private static boolean keepDisguiseEntityDespawn;
private static boolean keepDisguisePlayerDeath; private static boolean keepDisguisePlayerDeath;
private static boolean keepDisguisePlayerLogout; private static boolean keepDisguisePlayerLogout;
private static boolean maxHealthIsDisguisedEntity;
private static boolean miscDisguisesForLivingEnabled; private static boolean miscDisguisesForLivingEnabled;
private static boolean modifyBoundingBox; private static boolean modifyBoundingBox;
private static boolean movementEnabled; private static boolean movementEnabled;
@ -87,6 +88,10 @@ public class DisguiseConfig {
return keepDisguisePlayerLogout; return keepDisguisePlayerLogout;
} }
public static boolean isMaxHealthDeterminedByDisguisedEntity() {
return maxHealthIsDisguisedEntity;
}
public static boolean isMetadataPacketsEnabled() { public static boolean isMetadataPacketsEnabled() {
return sendsEntityMetadata; return sendsEntityMetadata;
} }
@ -240,6 +245,10 @@ public class DisguiseConfig {
keepDisguisePlayerLogout = keepDisguise; keepDisguisePlayerLogout = keepDisguise;
} }
public static void setMaxHealthDeterminedByDisguisedEntity(boolean isDetermined) {
maxHealthIsDisguisedEntity = isDetermined;
}
public static void setMetadataPacketsEnabled(boolean enabled) { public static void setMetadataPacketsEnabled(boolean enabled) {
sendsEntityMetadata = enabled; sendsEntityMetadata = enabled;
} }

View File

@ -27,6 +27,7 @@ import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Ageable; import org.bukkit.entity.Ageable;
import org.bukkit.entity.Damageable;
import org.bukkit.entity.Entity; import org.bukkit.entity.Entity;
import org.bukkit.entity.LivingEntity; import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Zombie; import org.bukkit.entity.Zombie;
@ -107,6 +108,7 @@ public class LibsDisguises extends JavaPlugin {
DisguiseConfig.setEntityStatusPacketsEnabled(getConfig().getBoolean("PacketsEnabled.EntityStatus")); DisguiseConfig.setEntityStatusPacketsEnabled(getConfig().getBoolean("PacketsEnabled.EntityStatus"));
DisguiseConfig.setCollectPacketsEnabled(getConfig().getBoolean("PacketsEnabled.Collect")); DisguiseConfig.setCollectPacketsEnabled(getConfig().getBoolean("PacketsEnabled.Collect"));
DisguiseConfig.setMetadataPacketsEnabled(getConfig().getBoolean("PacketsEnabled.Metadata")); DisguiseConfig.setMetadataPacketsEnabled(getConfig().getBoolean("PacketsEnabled.Metadata"));
DisguiseConfig.setMaxHealthDeterminedByDisguisedEntity(getConfig().getBoolean("MaxHealthDeterminedByEntity"));
try { try {
// Here I use reflection to set the plugin for Disguise.. // 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. // Kind of stupid but I don't want open API calls for a commonly used object.
@ -234,7 +236,8 @@ public class LibsDisguises extends JavaPlugin {
break; break;
} }
} }
DisguiseValues disguiseValues = new DisguiseValues(disguiseType, nmsEntity.getClass(), entitySize); DisguiseValues disguiseValues = new DisguiseValues(disguiseType, nmsEntity.getClass(), entitySize,
bukkitEntity instanceof Damageable ? ((Damageable) bukkitEntity).getMaxHealth() : 0);
for (WrappedWatchableObject watch : WrappedDataWatcher.getEntityWatcher(bukkitEntity).getWatchableObjects()) { for (WrappedWatchableObject watch : WrappedDataWatcher.getEntityWatcher(bukkitEntity).getWatchableObjects()) {
disguiseValues.setMetaValue(watch.getIndex(), watch.getValue()); disguiseValues.setMetaValue(watch.getIndex(), watch.getValue());
// Uncomment when I need to find the new datawatcher values for a class.. // Uncomment when I need to find the new datawatcher values for a class..

View File

@ -31,6 +31,8 @@ public class LivingWatcher extends FlagWatcher {
ex.printStackTrace(); ex.printStackTrace();
} }
} }
private double maxHealth;
private boolean maxHealthSet;
private HashSet<Integer> potionEffects = new HashSet<Integer>(); private HashSet<Integer> potionEffects = new HashSet<Integer>();
public LivingWatcher(Disguise disguise) { public LivingWatcher(Disguise disguise) {
@ -48,6 +50,8 @@ public class LivingWatcher extends FlagWatcher {
public LivingWatcher clone(Disguise disguise) { public LivingWatcher clone(Disguise disguise) {
LivingWatcher clone = (LivingWatcher) super.clone(disguise); LivingWatcher clone = (LivingWatcher) super.clone(disguise);
clone.potionEffects = (HashSet<Integer>) potionEffects.clone(); clone.potionEffects = (HashSet<Integer>) potionEffects.clone();
clone.maxHealth = maxHealth;
clone.maxHealthSet = maxHealthSet;
return clone; return clone;
} }
@ -59,6 +63,10 @@ public class LivingWatcher extends FlagWatcher {
return (Float) getValue(6, 0F); return (Float) getValue(6, 0F);
} }
public double getMaxHealth() {
return maxHealth;
}
public boolean getPotionParticlesRemoved() { public boolean getPotionParticlesRemoved() {
return (Byte) getValue(8, (byte) 0) == 1; return (Byte) getValue(8, (byte) 0) == 1;
} }
@ -105,6 +113,10 @@ public class LivingWatcher extends FlagWatcher {
return (Byte) getValue(11, (byte) 0) == 1; return (Byte) getValue(11, (byte) 0) == 1;
} }
public boolean isMaxHealthSet() {
return maxHealthSet;
}
public void removePotionEffect(PotionEffectType type) { public void removePotionEffect(PotionEffectType type) {
if (potionEffects.contains(type.getId())) { if (potionEffects.contains(type.getId())) {
potionEffects.remove(type.getId()); potionEffects.remove(type.getId());
@ -139,4 +151,10 @@ public class LivingWatcher extends FlagWatcher {
sendData(6); sendData(6);
} }
public void setMaxHealth(double newHealth) {
this.maxHealth = newHealth;
maxHealthSet = true;
// TODO Send packet
}
} }

View File

@ -47,13 +47,15 @@ public class DisguiseValues {
private FakeBoundingBox babyBox; private FakeBoundingBox babyBox;
private float[] entitySize; private float[] entitySize;
private int enumEntitySize; private int enumEntitySize;
private double maxHealth;
private HashMap<Integer, Object> metaValues = new HashMap<Integer, Object>(); private HashMap<Integer, Object> metaValues = new HashMap<Integer, Object>();
private Class nmsEntityClass; private Class nmsEntityClass;
public DisguiseValues(DisguiseType type, Class classType, int entitySize) { public DisguiseValues(DisguiseType type, Class classType, int entitySize, double maxHealth) {
values.put(type, this); values.put(type, this);
enumEntitySize = entitySize; enumEntitySize = entitySize;
nmsEntityClass = classType; nmsEntityClass = classType;
this.maxHealth = maxHealth;
} }
public FakeBoundingBox getAdultBox() { public FakeBoundingBox getAdultBox() {
@ -112,6 +114,10 @@ public class DisguiseValues {
return (int) Math.floor(paramDouble * 32.0D); return (int) Math.floor(paramDouble * 32.0D);
} }
public double getMaxHealth() {
return maxHealth;
}
public HashMap<Integer, Object> getMetaValues() { public HashMap<Integer, Object> getMetaValues() {
return metaValues; return metaValues;
} }

View File

@ -15,6 +15,7 @@ import me.libraryaddict.disguise.disguisetypes.FlagWatcher;
import me.libraryaddict.disguise.disguisetypes.MiscDisguise; import me.libraryaddict.disguise.disguisetypes.MiscDisguise;
import me.libraryaddict.disguise.disguisetypes.MobDisguise; import me.libraryaddict.disguise.disguisetypes.MobDisguise;
import me.libraryaddict.disguise.disguisetypes.PlayerDisguise; import me.libraryaddict.disguise.disguisetypes.PlayerDisguise;
import me.libraryaddict.disguise.disguisetypes.watchers.LivingWatcher;
import me.libraryaddict.disguise.disguisetypes.watchers.PlayerWatcher; import me.libraryaddict.disguise.disguisetypes.watchers.PlayerWatcher;
import me.libraryaddict.disguise.utilities.DisguiseSound.SoundType; import me.libraryaddict.disguise.utilities.DisguiseSound.SoundType;
import me.libraryaddict.disguise.utilities.ReflectionManager.LibVersion; import me.libraryaddict.disguise.utilities.ReflectionManager.LibVersion;
@ -43,6 +44,8 @@ import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.events.PacketEvent; import com.comphenix.protocol.events.PacketEvent;
import com.comphenix.protocol.events.PacketListener; import com.comphenix.protocol.events.PacketListener;
import com.comphenix.protocol.reflect.StructureModifier; import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.wrappers.WrappedAttribute;
import com.comphenix.protocol.wrappers.WrappedAttribute.Builder;
import com.comphenix.protocol.wrappers.WrappedDataWatcher; import com.comphenix.protocol.wrappers.WrappedDataWatcher;
import com.comphenix.protocol.wrappers.WrappedWatchableObject; import com.comphenix.protocol.wrappers.WrappedWatchableObject;
@ -1173,10 +1176,39 @@ public class PacketsManager {
if (disguise != null) { if (disguise != null) {
packets = new PacketContainer[] { sentPacket }; packets = new PacketContainer[] { sentPacket };
// If packet is PacketType.Play.Server.UPDATE_ATTRIBUTES
// This packet sends attributes // This packet sends attributes
if (sentPacket.getType() == PacketType.Play.Server.UPDATE_ATTRIBUTES) { if (sentPacket.getType() == PacketType.Play.Server.UPDATE_ATTRIBUTES) {
packets = new PacketContainer[0]; if (disguise.isMiscDisguise()) {
packets = new PacketContainer[0];
} else {
List<WrappedAttribute> attributes = new ArrayList<WrappedAttribute>();
for (WrappedAttribute attribute : sentPacket.getAttributeCollectionModifier().read(0)) {
if (attribute.getAttributeKey().equals("generic.maxHealth")) {
packets[0] = new PacketContainer(PacketType.Play.Server.UPDATE_ATTRIBUTES);
Builder builder;
if (((LivingWatcher) disguise.getWatcher()).isMaxHealthSet()) {
builder = WrappedAttribute.newBuilder();
builder.attributeKey("generic.maxHealth");
builder.baseValue(((LivingWatcher) disguise.getWatcher()).getMaxHealth());
} else if (DisguiseConfig.isMaxHealthDeterminedByDisguisedEntity()) {
builder = WrappedAttribute.newBuilder(attribute);
} else {
builder = WrappedAttribute.newBuilder();
builder.attributeKey("generic.maxHealth");
builder.baseValue(DisguiseValues.getDisguiseValues(disguise.getType()).getMaxHealth());
}
builder.packet(packets[0]);
attributes.add(builder.build());
break;
}
}
if (!attributes.isEmpty()) {
packets[0].getIntegers().write(0, entity.getEntityId());
packets[0].getAttributeCollectionModifier().write(0, attributes);
} else {
packets = new PacketContainer[0];
}
}
} }
else if (sentPacket.getType() == PacketType.Play.Server.ATTACH_ENTITY) { else if (sentPacket.getType() == PacketType.Play.Server.ATTACH_ENTITY) {