add support for entity equipment
This commit is contained in:
parent
1bbee6681a
commit
5ae5ca200d
11 changed files with 167 additions and 14 deletions
|
@ -1,5 +1,6 @@
|
|||
package lol.pyr.znpcsplus.commands;
|
||||
|
||||
import io.github.retrooper.packetevents.util.SpigotConversionUtil;
|
||||
import lol.pyr.director.adventure.command.CommandContext;
|
||||
import lol.pyr.director.adventure.command.CommandHandler;
|
||||
import lol.pyr.director.common.command.CommandExecutionException;
|
||||
|
@ -10,6 +11,7 @@ import lol.pyr.znpcsplus.npc.NpcImpl;
|
|||
import lol.pyr.znpcsplus.npc.NpcRegistryImpl;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import com.github.retrooper.packetevents.protocol.item.ItemStack;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
@ -23,15 +25,32 @@ public class PropertiesCommand implements CommandHandler {
|
|||
|
||||
@Override
|
||||
public void run(CommandContext context) throws CommandExecutionException {
|
||||
context.setUsage(context.getLabel() + " properties <id> <property> <value>");
|
||||
NpcEntryImpl entry = context.parse(NpcEntryImpl.class);
|
||||
NpcImpl npc = entry.getNpc();
|
||||
EntityPropertyImpl<?> property = context.parse(EntityPropertyImpl.class);
|
||||
|
||||
if (!npc.getType().getAllowedProperties().contains(property)) context.halt(Component.text("Property " + property.getName() + " not allowed for npc type " + npc.getType().getName()));
|
||||
if (!npc.getType().getAllowedProperties().contains(property)) context.halt(Component.text("Property " + property.getName() + " not allowed for npc type " + npc.getType().getName(), NamedTextColor.RED));
|
||||
Class<?> type = property.getType();
|
||||
Object value;
|
||||
String valueName;
|
||||
if (type == ItemStack.class) {
|
||||
org.bukkit.inventory.ItemStack bukkitStack = context.ensureSenderIsPlayer().getInventory().getItemInMainHand();
|
||||
if (bukkitStack.getType().isAir()) {
|
||||
value = null;
|
||||
valueName = "EMPTY";
|
||||
} else {
|
||||
value = SpigotConversionUtil.fromBukkitItemStack(bukkitStack);
|
||||
valueName = bukkitStack.toString();
|
||||
}
|
||||
}
|
||||
else {
|
||||
value = context.parse(property.getType());
|
||||
valueName = String.valueOf(value);
|
||||
}
|
||||
|
||||
Object value = context.parse(property.getType());
|
||||
npc.UNSAFE_setProperty(property, value);
|
||||
context.send(Component.text("Set property " + property.getName() + " for NPC " + entry.getId() + " to " + value.toString(), NamedTextColor.GREEN));
|
||||
context.send(Component.text("Set property " + property.getName() + " for NPC " + entry.getId() + " to " + valueName, NamedTextColor.GREEN));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
package lol.pyr.znpcsplus.entity;
|
||||
|
||||
import com.github.retrooper.packetevents.protocol.item.ItemStack;
|
||||
import lol.pyr.znpcsplus.api.entity.EntityPropertyRegistry;
|
||||
import lol.pyr.znpcsplus.api.skin.SkinDescriptor;
|
||||
import lol.pyr.znpcsplus.entity.serializers.BooleanPropertySerializer;
|
||||
import lol.pyr.znpcsplus.entity.serializers.ComponentPropertySerializer;
|
||||
import lol.pyr.znpcsplus.entity.serializers.NamedTextColorPropertySerializer;
|
||||
import lol.pyr.znpcsplus.entity.serializers.SkinDescriptorSerializer;
|
||||
import lol.pyr.znpcsplus.entity.serializers.*;
|
||||
import lol.pyr.znpcsplus.skin.cache.SkinCache;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
|
@ -23,6 +21,7 @@ public class EntityPropertyRegistryImpl implements EntityPropertyRegistry {
|
|||
registerSerializer(new ComponentPropertySerializer());
|
||||
registerSerializer(new NamedTextColorPropertySerializer());
|
||||
registerSerializer(new SkinDescriptorSerializer(skinCache));
|
||||
registerSerializer(new ItemStackPropertySerializer());
|
||||
|
||||
registerType("glow", NamedTextColor.class);
|
||||
registerType("skin_layers", true);
|
||||
|
@ -32,6 +31,13 @@ public class EntityPropertyRegistryImpl implements EntityPropertyRegistry {
|
|||
registerType("skin", SkinDescriptor.class);
|
||||
registerType("name", Component.class);
|
||||
registerType("look", false);
|
||||
|
||||
registerType("helmet", ItemStack.class);
|
||||
registerType("chestplate", ItemStack.class);
|
||||
registerType("leggings", ItemStack.class);
|
||||
registerType("boots", ItemStack.class);
|
||||
registerType("hand", ItemStack.class);
|
||||
registerType("offhand", ItemStack.class);
|
||||
}
|
||||
|
||||
private void registerSerializer(PropertySerializer<?> serializer) {
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
package lol.pyr.znpcsplus.entity.serializers;
|
||||
|
||||
import com.github.retrooper.packetevents.protocol.item.ItemStack;
|
||||
import io.github.retrooper.packetevents.util.SpigotConversionUtil;
|
||||
import lol.pyr.znpcsplus.entity.PropertySerializer;
|
||||
import lol.pyr.znpcsplus.util.ItemSerializationUtil;
|
||||
|
||||
public class ItemStackPropertySerializer implements PropertySerializer<ItemStack> {
|
||||
@Override
|
||||
public String serialize(ItemStack property) {
|
||||
return ItemSerializationUtil.itemToB64(SpigotConversionUtil.toBukkitItemStack(property));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack deserialize(String property) {
|
||||
return SpigotConversionUtil.fromBukkitItemStack(ItemSerializationUtil.itemFromB64(property));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<ItemStack> getTypeClass() {
|
||||
return ItemStack.class;
|
||||
}
|
||||
}
|
|
@ -114,8 +114,7 @@ public class NpcImpl extends Viewable implements Npc {
|
|||
}
|
||||
|
||||
public <T> void setProperty(EntityPropertyImpl<T> key, T value) {
|
||||
if (value == null) return;
|
||||
if (value.equals(key.getDefaultValue())) removeProperty(key);
|
||||
if (value == null || value.equals(key.getDefaultValue())) removeProperty(key);
|
||||
else propertyMap.put(key, value);
|
||||
UNSAFE_refreshMeta();
|
||||
if (key.getName().equalsIgnoreCase("glow")) UNSAFE_remakeTeam();
|
||||
|
|
|
@ -59,6 +59,11 @@ public class NpcTypeImpl implements NpcType {
|
|||
return this;
|
||||
}
|
||||
|
||||
public Builder addProperties(String... names) {
|
||||
for (String name : names) allowedProperties.add(propertyRegistry.getByName(name));
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setEnableGlobalProperties(boolean enabled) {
|
||||
globalProperties = enabled;
|
||||
return this;
|
||||
|
|
|
@ -29,7 +29,7 @@ public class NpcTypeRegistryImpl implements NpcTypeRegistry {
|
|||
ServerVersion version = packetEvents.getServerManager().getVersion();
|
||||
|
||||
register(new NpcTypeImpl.Builder(propertyRegistry, "player", EntityTypes.PLAYER).setHologramOffset(-0.15D)
|
||||
.addProperties(propertyRegistry.getByName("skin"), propertyRegistry.getByName("skin_layers")));
|
||||
.addProperties("skin", "skin_layers", "helmet", "chestplate", "leggings", "boots", "hand", "offhand"));
|
||||
|
||||
register(new NpcTypeImpl.Builder(propertyRegistry, "armor_stand", EntityTypes.ARMOR_STAND));
|
||||
register(new NpcTypeImpl.Builder(propertyRegistry, "bat", EntityTypes.BAT).setHologramOffset(-1.365));
|
||||
|
|
|
@ -21,4 +21,5 @@ public interface PacketFactory {
|
|||
Map<Integer, EntityData> generateMetadata(Player player, PacketEntity entity, PropertyHolder properties);
|
||||
void sendAllMetadata(Player player, PacketEntity entity, PropertyHolder properties);
|
||||
void sendMetadata(Player player, PacketEntity entity, List<EntityData> data);
|
||||
void sendEquipment(Player player, PacketEntity entity, PropertyHolder properties);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
package lol.pyr.znpcsplus.packets;
|
||||
|
||||
import com.github.retrooper.packetevents.PacketEventsAPI;
|
||||
import com.github.retrooper.packetevents.protocol.player.Equipment;
|
||||
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerEntityEquipment;
|
||||
import lol.pyr.znpcsplus.api.entity.PropertyHolder;
|
||||
import lol.pyr.znpcsplus.entity.EntityPropertyRegistryImpl;
|
||||
import lol.pyr.znpcsplus.entity.PacketEntity;
|
||||
import lol.pyr.znpcsplus.metadata.MetadataFactory;
|
||||
import lol.pyr.znpcsplus.scheduling.TaskScheduler;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class V1_16PacketFactory extends V1_14PacketFactory {
|
||||
public V1_16PacketFactory(TaskScheduler scheduler, MetadataFactory metadataFactory, PacketEventsAPI<Plugin> packetEvents, EntityPropertyRegistryImpl propertyRegistry) {
|
||||
super(scheduler, metadataFactory, packetEvents, propertyRegistry);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendEquipment(Player player, PacketEntity entity, PropertyHolder properties) {
|
||||
List<Equipment> equipments = generateEquipments(properties);
|
||||
if (equipments.size() > 0) sendPacket(player, new WrapperPlayServerEntityEquipment(entity.getEntityId(), equipments));
|
||||
}
|
||||
}
|
|
@ -18,7 +18,7 @@ import org.bukkit.plugin.Plugin;
|
|||
import java.util.EnumSet;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class V1_19PacketFactory extends V1_14PacketFactory {
|
||||
public class V1_19PacketFactory extends V1_16PacketFactory {
|
||||
public V1_19PacketFactory(TaskScheduler scheduler, MetadataFactory metadataFactory, PacketEventsAPI<Plugin> packetEvents, EntityPropertyRegistryImpl propertyRegistry) {
|
||||
super(scheduler, metadataFactory, packetEvents, propertyRegistry);
|
||||
}
|
||||
|
|
|
@ -4,14 +4,15 @@ import com.github.retrooper.packetevents.PacketEventsAPI;
|
|||
import com.github.retrooper.packetevents.protocol.entity.data.EntityData;
|
||||
import com.github.retrooper.packetevents.protocol.entity.type.EntityType;
|
||||
import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes;
|
||||
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
|
||||
import com.github.retrooper.packetevents.protocol.player.GameMode;
|
||||
import com.github.retrooper.packetevents.protocol.player.UserProfile;
|
||||
import com.github.retrooper.packetevents.protocol.item.ItemStack;
|
||||
import com.github.retrooper.packetevents.protocol.item.type.ItemTypes;
|
||||
import com.github.retrooper.packetevents.protocol.player.*;
|
||||
import com.github.retrooper.packetevents.util.Vector3d;
|
||||
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
|
||||
import com.github.retrooper.packetevents.wrapper.play.server.*;
|
||||
import lol.pyr.znpcsplus.api.entity.PropertyHolder;
|
||||
import lol.pyr.znpcsplus.api.skin.SkinDescriptor;
|
||||
import lol.pyr.znpcsplus.entity.EntityPropertyImpl;
|
||||
import lol.pyr.znpcsplus.entity.EntityPropertyRegistryImpl;
|
||||
import lol.pyr.znpcsplus.entity.PacketEntity;
|
||||
import lol.pyr.znpcsplus.metadata.MetadataFactory;
|
||||
|
@ -135,6 +136,7 @@ public class V1_8PacketFactory implements PacketFactory {
|
|||
@Override
|
||||
public void sendAllMetadata(Player player, PacketEntity entity, PropertyHolder properties) {
|
||||
sendMetadata(player, entity, new ArrayList<>(generateMetadata(player, entity, properties).values()));
|
||||
sendEquipment(player, entity, properties);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -142,6 +144,37 @@ public class V1_8PacketFactory implements PacketFactory {
|
|||
packetEvents.getPlayerManager().sendPacket(player, new WrapperPlayServerEntityMetadata(entity.getEntityId(), data));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendEquipment(Player player, PacketEntity entity, PropertyHolder properties) {
|
||||
for (Equipment equipment : generateEquipments(properties))
|
||||
sendPacket(player, new WrapperPlayServerEntityEquipment(entity.getEntityId(), Collections.singletonList(equipment)));
|
||||
}
|
||||
|
||||
protected List<Equipment> generateEquipments(PropertyHolder properties) {
|
||||
List<Equipment> equipements = new ArrayList<>();
|
||||
ItemStack air = new ItemStack.Builder().type(ItemTypes.AIR).build();
|
||||
|
||||
EntityPropertyImpl<ItemStack> helmet = propertyRegistry.getByName("helmet", ItemStack.class);
|
||||
equipements.add(new Equipment(EquipmentSlot.HELMET, properties.hasProperty(helmet) ? properties.getProperty(helmet) : air));
|
||||
|
||||
EntityPropertyImpl<ItemStack> chestplate = propertyRegistry.getByName("chestplate", ItemStack.class);
|
||||
equipements.add(new Equipment(EquipmentSlot.CHEST_PLATE, properties.hasProperty(chestplate) ? properties.getProperty(chestplate) : air));
|
||||
|
||||
EntityPropertyImpl<ItemStack> leggings = propertyRegistry.getByName("leggings", ItemStack.class);
|
||||
equipements.add(new Equipment(EquipmentSlot.LEGGINGS, properties.hasProperty(leggings) ? properties.getProperty(leggings) : air));
|
||||
|
||||
EntityPropertyImpl<ItemStack> boots = propertyRegistry.getByName("boots", ItemStack.class);
|
||||
equipements.add(new Equipment(EquipmentSlot.BOOTS, properties.hasProperty(boots) ? properties.getProperty(boots) : air));
|
||||
|
||||
EntityPropertyImpl<ItemStack> hand = propertyRegistry.getByName("hand", ItemStack.class);
|
||||
equipements.add(new Equipment(EquipmentSlot.MAIN_HAND, properties.hasProperty(hand) ? properties.getProperty(hand) : air));
|
||||
|
||||
EntityPropertyImpl<ItemStack> offhand = propertyRegistry.getByName("offhand", ItemStack.class);
|
||||
equipements.add(new Equipment(EquipmentSlot.OFF_HAND, properties.hasProperty(offhand) ? properties.getProperty(offhand) : air));
|
||||
|
||||
return equipements;
|
||||
}
|
||||
|
||||
protected void sendPacket(Player player, PacketWrapper<?> packet) {
|
||||
packetEvents.getPlayerManager().sendPacket(player, packet);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
package lol.pyr.znpcsplus.util;
|
||||
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.util.io.BukkitObjectInputStream;
|
||||
import org.bukkit.util.io.BukkitObjectOutputStream;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Base64;
|
||||
|
||||
public class ItemSerializationUtil {
|
||||
public static byte[] itemToBytes(ItemStack item) {
|
||||
ByteArrayOutputStream bout = new ByteArrayOutputStream();
|
||||
try {
|
||||
new BukkitObjectOutputStream(bout).writeObject(item);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return bout.toByteArray();
|
||||
}
|
||||
|
||||
public static ItemStack itemFromBytes(byte[] bytes) {
|
||||
ByteArrayInputStream bin = new ByteArrayInputStream(bytes);
|
||||
try {
|
||||
return (ItemStack) new BukkitObjectInputStream(bin).readObject();
|
||||
} catch (IOException | ClassNotFoundException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static String itemToB64(ItemStack item) {
|
||||
if (item == null) return null;
|
||||
return Base64.getEncoder().encodeToString(itemToBytes(item));
|
||||
}
|
||||
|
||||
public static ItemStack itemFromB64(String str) {
|
||||
if (str == null) return null;
|
||||
return itemFromBytes(Base64.getDecoder().decode(str));
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue