move to a more modular property/metadata system

This commit is contained in:
Pyrbu 2023-07-10 01:01:12 +02:00
parent 80683f81bc
commit acd953e460
39 changed files with 357 additions and 365 deletions

@ -1,7 +1,10 @@
package lol.pyr.znpcsplus.api.entity; package lol.pyr.znpcsplus.api.entity;
import java.util.Set;
public interface PropertyHolder { public interface PropertyHolder {
<T> T getProperty(EntityProperty<T> key); <T> T getProperty(EntityProperty<T> key);
boolean hasProperty(EntityProperty<?> key); boolean hasProperty(EntityProperty<?> key);
<T> void setProperty(EntityProperty<T> key, T value); <T> void setProperty(EntityProperty<T> key, T value);
Set<EntityProperty<?>> getAppliedProperties();
} }

@ -31,9 +31,11 @@ import lol.pyr.znpcsplus.entity.EntityPropertyImpl;
import lol.pyr.znpcsplus.entity.EntityPropertyRegistryImpl; import lol.pyr.znpcsplus.entity.EntityPropertyRegistryImpl;
import lol.pyr.znpcsplus.interaction.ActionRegistry; import lol.pyr.znpcsplus.interaction.ActionRegistry;
import lol.pyr.znpcsplus.interaction.InteractionPacketListener; import lol.pyr.znpcsplus.interaction.InteractionPacketListener;
import lol.pyr.znpcsplus.metadata.*;
import lol.pyr.znpcsplus.npc.*; import lol.pyr.znpcsplus.npc.*;
import lol.pyr.znpcsplus.packets.*; import lol.pyr.znpcsplus.packets.PacketFactory;
import lol.pyr.znpcsplus.packets.V1_17PacketFactory;
import lol.pyr.znpcsplus.packets.V1_19PacketFactory;
import lol.pyr.znpcsplus.packets.V1_8PacketFactory;
import lol.pyr.znpcsplus.parsers.*; import lol.pyr.znpcsplus.parsers.*;
import lol.pyr.znpcsplus.scheduling.FoliaScheduler; import lol.pyr.znpcsplus.scheduling.FoliaScheduler;
import lol.pyr.znpcsplus.scheduling.SpigotScheduler; import lol.pyr.znpcsplus.scheduling.SpigotScheduler;
@ -128,8 +130,8 @@ public class ZNpcsPlus extends JavaPlugin {
ConfigManager configManager = new ConfigManager(getDataFolder()); ConfigManager configManager = new ConfigManager(getDataFolder());
MojangSkinCache skinCache = new MojangSkinCache(configManager); MojangSkinCache skinCache = new MojangSkinCache(configManager);
EntityPropertyRegistryImpl propertyRegistry = new EntityPropertyRegistryImpl(skinCache); EntityPropertyRegistryImpl propertyRegistry = new EntityPropertyRegistryImpl(skinCache);
MetadataFactory metadataFactory = setupMetadataFactory(); PacketFactory packetFactory = setupPacketFactory(scheduler, propertyRegistry);
PacketFactory packetFactory = setupPacketFactory(scheduler, metadataFactory, propertyRegistry); propertyRegistry.registerTypes(packetFactory);
BungeeConnector bungeeConnector = new BungeeConnector(this); BungeeConnector bungeeConnector = new BungeeConnector(this);
ActionRegistry actionRegistry = new ActionRegistry(); ActionRegistry actionRegistry = new ActionRegistry();
@ -203,7 +205,6 @@ public class ZNpcsPlus extends JavaPlugin {
npc.getHologram().addLineComponent(Component.text("Hello, World!", TextColor.color(255, 0, 0))); npc.getHologram().addLineComponent(Component.text("Hello, World!", TextColor.color(255, 0, 0)));
npc.getHologram().addLineComponent(Component.text("Hello, World!", TextColor.color(0, 255, 0))); npc.getHologram().addLineComponent(Component.text("Hello, World!", TextColor.color(0, 255, 0)));
npc.getHologram().addLineComponent(Component.text("Hello, World!", TextColor.color(0, 0, 255))); npc.getHologram().addLineComponent(Component.text("Hello, World!", TextColor.color(0, 0, 255)));
npc.setProperty(propertyRegistry.getByName("look", Boolean.class), true);
i++; i++;
} }
} }
@ -216,15 +217,11 @@ public class ZNpcsPlus extends JavaPlugin {
PacketEvents.getAPI().terminate(); PacketEvents.getAPI().terminate();
} }
private PacketFactory setupPacketFactory(TaskScheduler scheduler, MetadataFactory metadataFactory, EntityPropertyRegistryImpl propertyRegistry) { private PacketFactory setupPacketFactory(TaskScheduler scheduler, EntityPropertyRegistryImpl propertyRegistry) {
HashMap<ServerVersion, LazyLoader<? extends PacketFactory>> versions = new HashMap<>(); HashMap<ServerVersion, LazyLoader<? extends PacketFactory>> versions = new HashMap<>();
versions.put(ServerVersion.V_1_8, LazyLoader.of(() -> new V1_8PacketFactory(scheduler, metadataFactory, packetEvents, propertyRegistry, textSerializer))); versions.put(ServerVersion.V_1_8, LazyLoader.of(() -> new V1_8PacketFactory(scheduler, packetEvents, propertyRegistry, textSerializer)));
versions.put(ServerVersion.V_1_9, LazyLoader.of(() -> new V1_9PacketFactory(scheduler, metadataFactory, packetEvents, propertyRegistry, textSerializer))); versions.put(ServerVersion.V_1_17, LazyLoader.of(() -> new V1_17PacketFactory(scheduler, packetEvents, propertyRegistry, textSerializer)));
versions.put(ServerVersion.V_1_10, LazyLoader.of(() -> new V1_10PacketFactory(scheduler, metadataFactory, packetEvents, propertyRegistry, textSerializer))); versions.put(ServerVersion.V_1_19, LazyLoader.of(() -> new V1_19PacketFactory(scheduler, packetEvents, propertyRegistry, textSerializer)));
versions.put(ServerVersion.V_1_14, LazyLoader.of(() -> new V1_14PacketFactory(scheduler, metadataFactory, packetEvents, propertyRegistry, textSerializer)));
versions.put(ServerVersion.V_1_16, LazyLoader.of(() -> new V1_16PacketFactory(scheduler, metadataFactory, packetEvents, propertyRegistry, textSerializer)));
versions.put(ServerVersion.V_1_17, LazyLoader.of(() -> new V1_17PacketFactory(scheduler, metadataFactory, packetEvents, propertyRegistry, textSerializer)));
versions.put(ServerVersion.V_1_19, LazyLoader.of(() -> new V1_19PacketFactory(scheduler, metadataFactory, packetEvents, propertyRegistry, textSerializer)));
ServerVersion version = packetEvents.getServerManager().getVersion(); ServerVersion version = packetEvents.getServerManager().getVersion();
if (versions.containsKey(version)) return versions.get(version).get(); if (versions.containsKey(version)) return versions.get(version).get();
@ -236,31 +233,6 @@ public class ZNpcsPlus extends JavaPlugin {
throw new RuntimeException("Unsupported version!"); throw new RuntimeException("Unsupported version!");
} }
private MetadataFactory setupMetadataFactory() {
HashMap<ServerVersion, LazyLoader<? extends MetadataFactory>> versions = new HashMap<>();
versions.put(ServerVersion.V_1_8, LazyLoader.of(V1_8MetadataFactory::new));
versions.put(ServerVersion.V_1_9, LazyLoader.of(V1_9MetadataFactory::new));
versions.put(ServerVersion.V_1_10, LazyLoader.of(V1_10MetadataFactory::new));
versions.put(ServerVersion.V_1_11, LazyLoader.of(V1_11MetadataFactory::new));
versions.put(ServerVersion.V_1_12, LazyLoader.of(V1_12MetadataFactory::new));
versions.put(ServerVersion.V_1_13, LazyLoader.of(V1_13MetadataFactory::new));
versions.put(ServerVersion.V_1_14, LazyLoader.of(V1_14MetadataFactory::new));
versions.put(ServerVersion.V_1_15, LazyLoader.of(V1_15MetadataFactory::new));
versions.put(ServerVersion.V_1_16, LazyLoader.of(V1_16MetadataFactory::new));
versions.put(ServerVersion.V_1_17, LazyLoader.of(V1_17MetadataFactory::new));
versions.put(ServerVersion.V_1_19, LazyLoader.of(V1_19MetadataFactory::new));
ServerVersion version = packetEvents.getServerManager().getVersion();
if (versions.containsKey(version)) return versions.get(version).get();
for (ServerVersion v : ServerVersion.reversedValues()) {
if (v.isNewerThan(version)) continue;
if (!versions.containsKey(v)) continue;
return versions.get(v).get();
}
throw new RuntimeException("Unsupported version!");
}
private void registerCommands(NpcRegistryImpl npcRegistry, MojangSkinCache skinCache, BukkitAudiences adventure, private void registerCommands(NpcRegistryImpl npcRegistry, MojangSkinCache skinCache, BukkitAudiences adventure,
ActionRegistry actionRegistry, NpcTypeRegistryImpl typeRegistry, ActionRegistry actionRegistry, NpcTypeRegistryImpl typeRegistry,
EntityPropertyRegistryImpl propertyRegistry, DataImporterRegistry importerRegistry, EntityPropertyRegistryImpl propertyRegistry, DataImporterRegistry importerRegistry,

@ -28,7 +28,7 @@ public class PropertyRemoveCommand implements CommandHandler {
NpcImpl npc = entry.getNpc(); NpcImpl npc = entry.getNpc();
EntityPropertyImpl<?> property = context.parse(EntityPropertyImpl.class); EntityPropertyImpl<?> property = context.parse(EntityPropertyImpl.class);
if (!npc.hasProperty(property)) context.halt(Component.text("This npc doesn't have the " + property.getName() + " property set", NamedTextColor.RED)); if (!npc.hasProperty(property)) context.halt(Component.text("This npc doesn't have the " + property.getName() + " property set", NamedTextColor.RED));
npc.removeProperty(property); npc.setProperty(property, null);
context.send(Component.text("Removed property " + property.getName() + " from NPC " + entry.getId(), NamedTextColor.GREEN)); context.send(Component.text("Removed property " + property.getName() + " from NPC " + entry.getId(), NamedTextColor.GREEN));
} }

@ -100,7 +100,7 @@ public class ZNpcImporter implements DataImporter {
ZNpcsLocation oldLoc = model.getLocation(); ZNpcsLocation oldLoc = model.getLocation();
NpcLocation location = new NpcLocation(oldLoc.getX(), oldLoc.getY(), oldLoc.getZ(), oldLoc.getYaw(), oldLoc.getPitch()); NpcLocation location = new NpcLocation(oldLoc.getX(), oldLoc.getY(), oldLoc.getZ(), oldLoc.getYaw(), oldLoc.getPitch());
UUID uuid = model.getUuid() == null ? UUID.randomUUID() : model.getUuid(); UUID uuid = model.getUuid() == null ? UUID.randomUUID() : model.getUuid();
NpcImpl npc = new NpcImpl(uuid, configManager, packetFactory, textSerializer, oldLoc.getWorld(), typeRegistry.getByName(type), location); NpcImpl npc = new NpcImpl(uuid, propertyRegistry, configManager, packetFactory, textSerializer, oldLoc.getWorld(), typeRegistry.getByName(type), location);
HologramImpl hologram = npc.getHologram(); HologramImpl hologram = npc.getHologram();
hologram.setOffset(model.getHologramHeight()); hologram.setOffset(model.getHologramHeight());

@ -1,19 +1,24 @@
package lol.pyr.znpcsplus.entity; package lol.pyr.znpcsplus.entity;
import com.github.retrooper.packetevents.protocol.entity.data.EntityData;
import com.github.retrooper.packetevents.protocol.entity.data.EntityDataType;
import lol.pyr.znpcsplus.api.entity.EntityProperty; import lol.pyr.znpcsplus.api.entity.EntityProperty;
import lol.pyr.znpcsplus.api.entity.PropertyHolder; import org.bukkit.entity.Player;
public class EntityPropertyImpl<T> implements EntityProperty<T> { import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public abstract class EntityPropertyImpl<T> implements EntityProperty<T> {
private final String name; private final String name;
private final T defaultValue; private final T defaultValue;
private final Class<T> clazz; private final Class<T> clazz;
private final PropertySerializer<T> serializer;
protected EntityPropertyImpl(String name, T defaultValue, Class<T> clazz, PropertySerializer<T> serializer) { protected EntityPropertyImpl(String name, T defaultValue, Class<T> clazz) {
this.name = name.toLowerCase(); this.name = name.toLowerCase();
this.defaultValue = defaultValue; this.defaultValue = defaultValue;
this.clazz = clazz; this.clazz = clazz;
this.serializer = serializer;
} }
@Override @Override
@ -21,18 +26,6 @@ public class EntityPropertyImpl<T> implements EntityProperty<T> {
return name; return name;
} }
public String serialize(PropertyHolder holder) {
return serialize(holder.getProperty(this));
}
public String serialize(T value) {
return serializer.serialize(value);
}
public T deserialize(String str) {
return serializer.deserialize(str);
}
@Override @Override
public T getDefaultValue() { public T getDefaultValue() {
return defaultValue; return defaultValue;
@ -41,4 +34,21 @@ public class EntityPropertyImpl<T> implements EntityProperty<T> {
public Class<T> getType() { public Class<T> getType() {
return clazz; return clazz;
} }
protected static <V> EntityData newEntityData(int index, EntityDataType<V> type, V value) {
return new EntityData(index, type, value);
}
public List<EntityData> makeStandaloneData(T value, Player player, PacketEntity packetEntity, boolean isSpawned) {
Map<Integer, EntityData> map = new HashMap<>();
apply(value, player, packetEntity, isSpawned, map);
return new ArrayList<>(map.values());
}
abstract public void apply(T value, Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData> properties);
@SuppressWarnings("unchecked")
public void UNSAFE_update(Object value, Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData> properties) {
apply((T) value, player, entity, isSpawned, properties);
}
} }

@ -1,15 +1,13 @@
package lol.pyr.znpcsplus.entity; package lol.pyr.znpcsplus.entity;
import com.github.retrooper.packetevents.protocol.item.ItemStack; import com.github.retrooper.packetevents.protocol.player.EquipmentSlot;
import lol.pyr.znpcsplus.api.entity.EntityProperty; import lol.pyr.znpcsplus.api.entity.EntityProperty;
import lol.pyr.znpcsplus.api.entity.EntityPropertyRegistry; import lol.pyr.znpcsplus.api.entity.EntityPropertyRegistry;
import lol.pyr.znpcsplus.api.skin.SkinDescriptor; import lol.pyr.znpcsplus.entity.properties.*;
import lol.pyr.znpcsplus.entity.serializers.*; import lol.pyr.znpcsplus.entity.serializers.*;
import lol.pyr.znpcsplus.packets.PacketFactory;
import lol.pyr.znpcsplus.skin.cache.MojangSkinCache; import lol.pyr.znpcsplus.skin.cache.MojangSkinCache;
import lol.pyr.znpcsplus.util.*; import lol.pyr.znpcsplus.util.*;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import org.bukkit.Color;
import org.bukkit.DyeColor; import org.bukkit.DyeColor;
import java.util.Collection; import java.util.Collection;
@ -18,6 +16,20 @@ import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/**
* 1.8 <a href="https://wiki.vg/index.php?title=Entity_metadata&oldid=7415">...</a>
* 1.9 <a href="https://wiki.vg/index.php?title=Entity_metadata&oldid=7968">...</a>
* 1.10 <a href="https://wiki.vg/index.php?title=Entity_metadata&oldid=8241">...</a>
* 1.11 <a href="https://wiki.vg/index.php?title=Entity_metadata&oldid=8534">...</a>
* 1.12 <a href="https://wiki.vg/index.php?title=Entity_metadata&oldid=14048">...</a>
* 1.13 <a href="https://wiki.vg/index.php?title=Entity_metadata&oldid=14800">...</a>
* 1.14 <a href="https://wiki.vg/index.php?title=Entity_metadata&oldid=15240">...</a>
* 1.15 <a href="https://wiki.vg/index.php?title=Entity_metadata&oldid=15991">...</a>
* 1.16 <a href="https://wiki.vg/index.php?title=Entity_metadata&oldid=16539">...</a>
* 1.17 <a href="https://wiki.vg/index.php?title=Entity_metadata&oldid=17521">...</a>
* 1.18-1.19 <a href="https://wiki.vg/index.php?title=Entity_metadata&oldid=18191">...</a>
* 1.20 <a href="https://wiki.vg/index.php?title=Entity_metadata">...</a>
*/
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public class EntityPropertyRegistryImpl implements EntityPropertyRegistry { public class EntityPropertyRegistryImpl implements EntityPropertyRegistry {
private final Map<Class<?>, PropertySerializer<?>> serializerMap = new HashMap<>(); private final Map<Class<?>, PropertySerializer<?>> serializerMap = new HashMap<>();
@ -44,7 +56,7 @@ public class EntityPropertyRegistryImpl implements EntityPropertyRegistry {
registerEnumSerializer(VillagerType.class); registerEnumSerializer(VillagerType.class);
registerEnumSerializer(VillagerProfession.class); registerEnumSerializer(VillagerProfession.class);
registerEnumSerializer(VillagerLevel.class); registerEnumSerializer(VillagerLevel.class);
/*
registerType("glow", NamedTextColor.class); registerType("glow", NamedTextColor.class);
registerType("fire", false); registerType("fire", false);
registerType("invisible", false); registerType("invisible", false);
@ -226,6 +238,22 @@ public class EntityPropertyRegistryImpl implements EntityPropertyRegistry {
// Slime // Slime
registerType("slime_size", 0); // TODO registerType("slime_size", 0); // TODO
*/
}
public void registerTypes(PacketFactory packetFactory) {
register(new EquipmentProperty(packetFactory, "helmet", EquipmentSlot.HELMET));
register(new EquipmentProperty(packetFactory, "chestplate", EquipmentSlot.CHEST_PLATE));
register(new EquipmentProperty(packetFactory, "leggings", EquipmentSlot.LEGGINGS));
register(new EquipmentProperty(packetFactory, "boots", EquipmentSlot.BOOTS));
register(new EquipmentProperty(packetFactory, "hand", EquipmentSlot.MAIN_HAND));
register(new EquipmentProperty(packetFactory, "offhand", EquipmentSlot.OFF_HAND));
register(new NameProperty());
register(new DummyBooleanProperty("look", false));
register(new GlowProperty(packetFactory));
register(new EffectsProperty("fire", 0x01));
register(new EffectsProperty("invisible", 0x20));
} }
private void registerSerializer(PropertySerializer<?> serializer) { private void registerSerializer(PropertySerializer<?> serializer) {
@ -236,18 +264,12 @@ public class EntityPropertyRegistryImpl implements EntityPropertyRegistry {
serializerMap.put(clazz, new EnumPropertySerializer<>(clazz)); serializerMap.put(clazz, new EnumPropertySerializer<>(clazz));
} }
private <T> void registerType(String name, Class<T> type) { private <T> void register(EntityPropertyImpl<?> property) {
registerType(name, null, type); byName.put(property.getName(), property);
} }
private <T> void registerType(String name, T defaultValue) { public <V> PropertySerializer<V> getSerializer(Class<V> type) {
registerType(name, defaultValue, (Class<T>) defaultValue.getClass()); return (PropertySerializer<V>) serializerMap.get(type);
}
private <T> void registerType(String name, T defaultValue, Class<T> clazz) {
if (clazz == null) return;
EntityPropertyImpl<T> property = new EntityPropertyImpl<>(name, defaultValue, clazz, (PropertySerializer<T>) serializerMap.get(clazz));
byName.put(name.toLowerCase(), property);
} }
@Override @Override

@ -66,11 +66,6 @@ public class PacketEntity {
packetFactory.sendAllMetadata(player, this, properties); packetFactory.sendAllMetadata(player, this, properties);
} }
public void remakeTeam(Player player) {
packetFactory.removeTeam(player, this);
packetFactory.createTeam(player, this, properties);
}
private static int reserveEntityID() { private static int reserveEntityID() {
if (PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_14)) { if (PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_14)) {
return Reflections.ATOMIC_ENTITY_ID_FIELD.get().incrementAndGet(); return Reflections.ATOMIC_ENTITY_ID_FIELD.get().incrementAndGet();

@ -4,4 +4,9 @@ public interface PropertySerializer<T> {
String serialize(T property); String serialize(T property);
T deserialize(String property); T deserialize(String property);
Class<T> getTypeClass(); Class<T> getTypeClass();
@SuppressWarnings("unchecked")
default String UNSAFE_serialize(Object property) {
return serialize((T) property);
}
} }

@ -0,0 +1,18 @@
package lol.pyr.znpcsplus.entity.properties;
import com.github.retrooper.packetevents.protocol.entity.data.EntityData;
import lol.pyr.znpcsplus.entity.EntityPropertyImpl;
import lol.pyr.znpcsplus.entity.PacketEntity;
import org.bukkit.entity.Player;
import java.util.Map;
public class DummyBooleanProperty extends EntityPropertyImpl<Boolean> {
public DummyBooleanProperty(String name, boolean def) {
super(name, def, Boolean.class);
}
@Override
public void apply(Boolean value, Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData> properties) {
}
}

@ -0,0 +1,25 @@
package lol.pyr.znpcsplus.entity.properties;
import com.github.retrooper.packetevents.protocol.entity.data.EntityData;
import com.github.retrooper.packetevents.protocol.entity.data.EntityDataTypes;
import lol.pyr.znpcsplus.entity.EntityPropertyImpl;
import lol.pyr.znpcsplus.entity.PacketEntity;
import org.bukkit.entity.Player;
import java.util.Map;
public class EffectsProperty extends EntityPropertyImpl<Boolean> {
private final int bitmask;
public EffectsProperty(String name, int bitmask) {
super(name, false, Boolean.class);
this.bitmask = bitmask;
}
@Override
public void apply(Boolean enabled, Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData> properties) {
EntityData oldData = properties.get(0);
byte oldValue = oldData == null ? 0 : (byte) oldData.getValue();
properties.put(0, newEntityData(0, EntityDataTypes.BYTE, (byte) (oldValue | (enabled ? bitmask : 0))));
}
}

@ -0,0 +1,28 @@
package lol.pyr.znpcsplus.entity.properties;
import com.github.retrooper.packetevents.protocol.entity.data.EntityData;
import com.github.retrooper.packetevents.protocol.item.ItemStack;
import com.github.retrooper.packetevents.protocol.player.Equipment;
import com.github.retrooper.packetevents.protocol.player.EquipmentSlot;
import lol.pyr.znpcsplus.entity.EntityPropertyImpl;
import lol.pyr.znpcsplus.entity.PacketEntity;
import lol.pyr.znpcsplus.packets.PacketFactory;
import org.bukkit.entity.Player;
import java.util.Map;
public class EquipmentProperty extends EntityPropertyImpl<ItemStack> {
private final PacketFactory packetFactory;
private final EquipmentSlot slot;
public EquipmentProperty(PacketFactory packetFactory, String name, EquipmentSlot slot) {
super(name, null, ItemStack.class);
this.packetFactory = packetFactory;
this.slot = slot;
}
@Override
public void apply(ItemStack value, Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData> properties) {
packetFactory.sendEquipment(player, entity, new Equipment(slot, value));
}
}

@ -0,0 +1,29 @@
package lol.pyr.znpcsplus.entity.properties;
import com.github.retrooper.packetevents.protocol.entity.data.EntityData;
import com.github.retrooper.packetevents.protocol.entity.data.EntityDataTypes;
import lol.pyr.znpcsplus.entity.EntityPropertyImpl;
import lol.pyr.znpcsplus.entity.PacketEntity;
import lol.pyr.znpcsplus.packets.PacketFactory;
import net.kyori.adventure.text.format.NamedTextColor;
import org.bukkit.entity.Player;
import java.util.Map;
public class GlowProperty extends EntityPropertyImpl<NamedTextColor> {
private final PacketFactory packetFactory;
public GlowProperty(PacketFactory packetFactory) {
super("glow", null, NamedTextColor.class);
this.packetFactory = packetFactory;
}
@Override
public void apply(NamedTextColor value, Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData> properties) {
EntityData oldData = properties.get(0);
byte oldValue = oldData == null ? 0 : (byte) oldData.getValue();
properties.put(0, newEntityData(0, EntityDataTypes.BYTE, (byte) (oldValue | (value == null ? 0 : 0x40))));
if (isSpawned) packetFactory.removeTeam(player, entity);
packetFactory.createTeam(player, entity, value);
}
}

@ -0,0 +1,41 @@
package lol.pyr.znpcsplus.entity.properties;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.protocol.entity.data.EntityData;
import com.github.retrooper.packetevents.protocol.entity.data.EntityDataTypes;
import com.github.retrooper.packetevents.util.adventure.AdventureSerializer;
import lol.pyr.znpcsplus.entity.EntityPropertyImpl;
import lol.pyr.znpcsplus.entity.PacketEntity;
import net.kyori.adventure.text.Component;
import org.bukkit.entity.Player;
import java.util.Map;
import java.util.Optional;
public class NameProperty extends EntityPropertyImpl<Component> {
private final boolean legacy;
private final boolean optionalComponent;
public NameProperty() {
super("name", null, Component.class);
ServerVersion version = PacketEvents.getAPI().getServerManager().getVersion();
legacy = version.isOlderThan(ServerVersion.V_1_9);
optionalComponent = version.isNewerThanOrEquals(ServerVersion.V_1_13);
}
@Override
public void apply(Component value, Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData> properties) {
if (value != null) {
String serialized = legacy ?
AdventureSerializer.getLegacyGsonSerializer().serialize(value) :
AdventureSerializer.getGsonSerializer().serialize(value);
if (optionalComponent) properties.put(2, newEntityData(2, EntityDataTypes.OPTIONAL_COMPONENT, Optional.of(serialized)));
else properties.put(2, newEntityData(2, EntityDataTypes.STRING, serialized));
}
if (legacy) properties.put(3, newEntityData(3, EntityDataTypes.BYTE, (byte) (value != null ? 1 : 0)));
else properties.put(3, newEntityData(3, EntityDataTypes.BOOLEAN, value != null));
}
}

@ -2,6 +2,7 @@ package lol.pyr.znpcsplus.hologram;
import lol.pyr.znpcsplus.api.hologram.Hologram; import lol.pyr.znpcsplus.api.hologram.Hologram;
import lol.pyr.znpcsplus.config.ConfigManager; import lol.pyr.znpcsplus.config.ConfigManager;
import lol.pyr.znpcsplus.entity.EntityPropertyRegistryImpl;
import lol.pyr.znpcsplus.packets.PacketFactory; import lol.pyr.znpcsplus.packets.PacketFactory;
import lol.pyr.znpcsplus.util.Viewable; import lol.pyr.znpcsplus.util.Viewable;
import lol.pyr.znpcsplus.util.NpcLocation; import lol.pyr.znpcsplus.util.NpcLocation;
@ -17,6 +18,7 @@ public class HologramImpl extends Viewable implements Hologram {
private final ConfigManager configManager; private final ConfigManager configManager;
private final PacketFactory packetFactory; private final PacketFactory packetFactory;
private final LegacyComponentSerializer textSerializer; private final LegacyComponentSerializer textSerializer;
private final EntityPropertyRegistryImpl propertyRegistry;
private double offset = 0.0; private double offset = 0.0;
private long refreshDelay = -1; private long refreshDelay = -1;
@ -24,7 +26,8 @@ public class HologramImpl extends Viewable implements Hologram {
private NpcLocation location; private NpcLocation location;
private final List<HologramLine> lines = new ArrayList<>(); private final List<HologramLine> lines = new ArrayList<>();
public HologramImpl(ConfigManager configManager, PacketFactory packetFactory, LegacyComponentSerializer textSerializer, NpcLocation location) { public HologramImpl(EntityPropertyRegistryImpl propertyRegistry, ConfigManager configManager, PacketFactory packetFactory, LegacyComponentSerializer textSerializer, NpcLocation location) {
this.propertyRegistry = propertyRegistry;
this.configManager = configManager; this.configManager = configManager;
this.packetFactory = packetFactory; this.packetFactory = packetFactory;
this.textSerializer = textSerializer; this.textSerializer = textSerializer;
@ -32,7 +35,7 @@ public class HologramImpl extends Viewable implements Hologram {
} }
public void addLineComponent(Component line) { public void addLineComponent(Component line) {
HologramLine newLine = new HologramLine(packetFactory, null, line); HologramLine newLine = new HologramLine(propertyRegistry, packetFactory, null, line);
lines.add(newLine); lines.add(newLine);
relocateLines(newLine); relocateLines(newLine);
for (Player viewer : getViewers()) newLine.show(viewer.getPlayer()); for (Player viewer : getViewers()) newLine.show(viewer.getPlayer());
@ -66,7 +69,7 @@ public class HologramImpl extends Viewable implements Hologram {
} }
public void insertLineComponent(int index, Component line) { public void insertLineComponent(int index, Component line) {
HologramLine newLine = new HologramLine(packetFactory, null, line); HologramLine newLine = new HologramLine(propertyRegistry, packetFactory, null, line);
lines.add(index, newLine); lines.add(index, newLine);
relocateLines(newLine); relocateLines(newLine);
for (Player viewer : getViewers()) newLine.show(viewer.getPlayer()); for (Player viewer : getViewers()) newLine.show(viewer.getPlayer());

@ -3,6 +3,7 @@ package lol.pyr.znpcsplus.hologram;
import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes; import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes;
import lol.pyr.znpcsplus.api.entity.EntityProperty; import lol.pyr.znpcsplus.api.entity.EntityProperty;
import lol.pyr.znpcsplus.api.entity.PropertyHolder; import lol.pyr.znpcsplus.api.entity.PropertyHolder;
import lol.pyr.znpcsplus.entity.EntityPropertyRegistryImpl;
import lol.pyr.znpcsplus.entity.PacketEntity; import lol.pyr.znpcsplus.entity.PacketEntity;
import lol.pyr.znpcsplus.packets.PacketFactory; import lol.pyr.znpcsplus.packets.PacketFactory;
import lol.pyr.znpcsplus.util.NpcLocation; import lol.pyr.znpcsplus.util.NpcLocation;
@ -10,13 +11,19 @@ import net.kyori.adventure.text.Component;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import java.util.Collection; import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
public class HologramLine implements PropertyHolder { public class HologramLine implements PropertyHolder {
private Component text; private Component text;
private final PacketEntity armorStand; private final PacketEntity armorStand;
private final Set<EntityProperty<?>> properties;
public HologramLine(PacketFactory packetFactory, NpcLocation location, Component text) { public HologramLine(EntityPropertyRegistryImpl propertyRegistry, PacketFactory packetFactory, NpcLocation location, Component text) {
this.text = text; this.text = text;
this.properties = new HashSet<>();
this.properties.add(propertyRegistry.getByName("name"));
this.properties.add(propertyRegistry.getByName("invisible"));
armorStand = new PacketEntity(packetFactory, this, EntityTypes.ARMOR_STAND, location); armorStand = new PacketEntity(packetFactory, this, EntityTypes.ARMOR_STAND, location);
} }
@ -61,4 +68,9 @@ public class HologramLine implements PropertyHolder {
public <T> void setProperty(EntityProperty<T> key, T value) { public <T> void setProperty(EntityProperty<T> key, T value) {
throw new UnsupportedOperationException("Can't set properties on a hologram"); throw new UnsupportedOperationException("Can't set properties on a hologram");
} }
@Override
public Set<EntityProperty<?>> getAppliedProperties() {
return properties;
}
} }

@ -20,6 +20,7 @@ import org.bukkit.DyeColor;
* 1.18-1.19 <a href="https://wiki.vg/index.php?title=Entity_metadata&oldid=18191">...</a> * 1.18-1.19 <a href="https://wiki.vg/index.php?title=Entity_metadata&oldid=18191">...</a>
* 1.20 <a href="https://wiki.vg/index.php?title=Entity_metadata">...</a> * 1.20 <a href="https://wiki.vg/index.php?title=Entity_metadata">...</a>
*/ */
@Deprecated
public interface MetadataFactory { public interface MetadataFactory {
EntityData effects(boolean onFire, boolean glowing, boolean invisible, boolean usingElytra, boolean usingItemLegacy); EntityData effects(boolean onFire, boolean glowing, boolean invisible, boolean usingElytra, boolean usingItemLegacy);
EntityData silent(boolean enabled); EntityData silent(boolean enabled);

@ -5,6 +5,7 @@ import com.github.retrooper.packetevents.protocol.entity.data.EntityDataTypes;
import lol.pyr.znpcsplus.util.CreeperState; import lol.pyr.znpcsplus.util.CreeperState;
import lol.pyr.znpcsplus.util.Vector3f; import lol.pyr.znpcsplus.util.Vector3f;
@Deprecated
public class V1_10MetadataFactory extends V1_9MetadataFactory { public class V1_10MetadataFactory extends V1_9MetadataFactory {
@Override @Override
public EntityData skinLayers(boolean cape, boolean jacket, boolean leftSleeve, boolean rightSleeve, boolean leftLeg, boolean rightLeg, boolean hat) { public EntityData skinLayers(boolean cape, boolean jacket, boolean leftSleeve, boolean rightSleeve, boolean leftLeg, boolean rightLeg, boolean hat) {

@ -3,12 +3,8 @@ package lol.pyr.znpcsplus.metadata;
import com.github.retrooper.packetevents.protocol.entity.data.EntityData; import com.github.retrooper.packetevents.protocol.entity.data.EntityData;
import com.github.retrooper.packetevents.protocol.entity.data.EntityDataTypes; import com.github.retrooper.packetevents.protocol.entity.data.EntityDataTypes;
@Deprecated
public class V1_11MetadataFactory extends V1_10MetadataFactory { public class V1_11MetadataFactory extends V1_10MetadataFactory {
@Override
public EntityData effects(boolean onFire, boolean glowing, boolean invisible, boolean usingElytra, boolean usingItemLegacy) {
return super.effects(onFire, glowing, invisible, usingElytra, false);
}
@Override @Override
public EntityData usingItem(boolean usingItem, boolean offHand, boolean riptide) { public EntityData usingItem(boolean usingItem, boolean offHand, boolean riptide) {
return newEntityData(6, EntityDataTypes.BYTE, (byte) ((usingItem ? 0x01 : 0) | (offHand ? 0x02 : 0))); return newEntityData(6, EntityDataTypes.BYTE, (byte) ((usingItem ? 0x01 : 0) | (offHand ? 0x02 : 0)));

@ -6,6 +6,7 @@ import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
import lol.pyr.znpcsplus.entity.ParrotNBTCompound; import lol.pyr.znpcsplus.entity.ParrotNBTCompound;
import lol.pyr.znpcsplus.util.ParrotVariant; import lol.pyr.znpcsplus.util.ParrotVariant;
@Deprecated
public class V1_12MetadataFactory extends V1_11MetadataFactory { public class V1_12MetadataFactory extends V1_11MetadataFactory {
@Override @Override
public EntityData shoulderEntityLeft(ParrotVariant variant) { public EntityData shoulderEntityLeft(ParrotVariant variant) {

@ -7,6 +7,7 @@ import net.kyori.adventure.text.Component;
import java.util.Optional; import java.util.Optional;
@Deprecated
public class V1_13MetadataFactory extends V1_12MetadataFactory { public class V1_13MetadataFactory extends V1_12MetadataFactory {
@Override @Override
public EntityData name(Component name) { public EntityData name(Component name) {

@ -10,6 +10,7 @@ import lol.pyr.znpcsplus.util.ParrotVariant;
import lol.pyr.znpcsplus.util.Vector3f; import lol.pyr.znpcsplus.util.Vector3f;
import org.bukkit.DyeColor; import org.bukkit.DyeColor;
@Deprecated
public class V1_14MetadataFactory extends V1_13MetadataFactory { public class V1_14MetadataFactory extends V1_13MetadataFactory {
@Override @Override
public EntityData skinLayers(boolean cape, boolean jacket, boolean leftSleeve, boolean rightSleeve, boolean leftLeg, boolean rightLeg, boolean hat) { public EntityData skinLayers(boolean cape, boolean jacket, boolean leftSleeve, boolean rightSleeve, boolean leftLeg, boolean rightLeg, boolean hat) {

@ -9,6 +9,7 @@ import lol.pyr.znpcsplus.util.ParrotVariant;
import lol.pyr.znpcsplus.util.Vector3f; import lol.pyr.znpcsplus.util.Vector3f;
import org.bukkit.DyeColor; import org.bukkit.DyeColor;
@Deprecated
public class V1_15MetadataFactory extends V1_14MetadataFactory { public class V1_15MetadataFactory extends V1_14MetadataFactory {
@Override @Override
public EntityData shoulderEntityLeft(ParrotVariant variant) { public EntityData shoulderEntityLeft(ParrotVariant variant) {

@ -3,6 +3,7 @@ package lol.pyr.znpcsplus.metadata;
import com.github.retrooper.packetevents.protocol.entity.data.EntityData; import com.github.retrooper.packetevents.protocol.entity.data.EntityData;
import com.github.retrooper.packetevents.protocol.entity.data.EntityDataTypes; import com.github.retrooper.packetevents.protocol.entity.data.EntityDataTypes;
@Deprecated
public class V1_16MetadataFactory extends V1_15MetadataFactory { public class V1_16MetadataFactory extends V1_15MetadataFactory {
@Override @Override
public EntityData skinLayers(boolean cape, boolean jacket, boolean leftSleeve, boolean rightSleeve, boolean leftLeg, boolean rightLeg, boolean hat) { public EntityData skinLayers(boolean cape, boolean jacket, boolean leftSleeve, boolean rightSleeve, boolean leftLeg, boolean rightLeg, boolean hat) {

@ -9,6 +9,7 @@ import lol.pyr.znpcsplus.util.ParrotVariant;
import lol.pyr.znpcsplus.util.Vector3f; import lol.pyr.znpcsplus.util.Vector3f;
import org.bukkit.DyeColor; import org.bukkit.DyeColor;
@Deprecated
public class V1_17MetadataFactory extends V1_16MetadataFactory { public class V1_17MetadataFactory extends V1_16MetadataFactory {
@Override @Override
public EntityData skinLayers(boolean cape, boolean jacket, boolean leftSleeve, boolean rightSleeve, boolean leftLeg, boolean rightLeg, boolean hat) { public EntityData skinLayers(boolean cape, boolean jacket, boolean leftSleeve, boolean rightSleeve, boolean leftLeg, boolean rightLeg, boolean hat) {

@ -2,8 +2,8 @@ package lol.pyr.znpcsplus.metadata;
import com.github.retrooper.packetevents.protocol.entity.data.EntityData; import com.github.retrooper.packetevents.protocol.entity.data.EntityData;
import com.github.retrooper.packetevents.protocol.entity.data.EntityDataTypes; import com.github.retrooper.packetevents.protocol.entity.data.EntityDataTypes;
import lol.pyr.znpcsplus.util.FrogVariant;
@Deprecated
public class V1_19MetadataFactory extends V1_17MetadataFactory { public class V1_19MetadataFactory extends V1_17MetadataFactory {
@Override @Override
public EntityData frogVariant(int variant) { public EntityData frogVariant(int variant) {

@ -9,6 +9,7 @@ import lol.pyr.znpcsplus.util.*;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import org.bukkit.DyeColor; import org.bukkit.DyeColor;
@Deprecated
public class V1_8MetadataFactory implements MetadataFactory { public class V1_8MetadataFactory implements MetadataFactory {
@Override @Override
public EntityData skinLayers(boolean cape, boolean jacket, boolean leftSleeve, boolean rightSleeve, boolean leftLeg, boolean rightLeg, boolean hat) { public EntityData skinLayers(boolean cape, boolean jacket, boolean leftSleeve, boolean rightSleeve, boolean leftLeg, boolean rightLeg, boolean hat) {

@ -6,6 +6,7 @@ import com.github.retrooper.packetevents.util.adventure.AdventureSerializer;
import lol.pyr.znpcsplus.util.CreeperState; import lol.pyr.znpcsplus.util.CreeperState;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
@Deprecated
public class V1_9MetadataFactory extends V1_8MetadataFactory { public class V1_9MetadataFactory extends V1_8MetadataFactory {
@Override @Override
public EntityData skinLayers(boolean cape, boolean jacket, boolean leftSleeve, boolean rightSleeve, boolean leftLeg, boolean rightLeg, boolean hat) { public EntityData skinLayers(boolean cape, boolean jacket, boolean leftSleeve, boolean rightSleeve, boolean leftLeg, boolean rightLeg, boolean hat) {

@ -1,9 +1,11 @@
package lol.pyr.znpcsplus.npc; package lol.pyr.znpcsplus.npc;
import com.github.retrooper.packetevents.protocol.entity.data.EntityData;
import lol.pyr.znpcsplus.api.entity.EntityProperty; import lol.pyr.znpcsplus.api.entity.EntityProperty;
import lol.pyr.znpcsplus.api.npc.Npc; import lol.pyr.znpcsplus.api.npc.Npc;
import lol.pyr.znpcsplus.config.ConfigManager; import lol.pyr.znpcsplus.config.ConfigManager;
import lol.pyr.znpcsplus.entity.EntityPropertyImpl; import lol.pyr.znpcsplus.entity.EntityPropertyImpl;
import lol.pyr.znpcsplus.entity.EntityPropertyRegistryImpl;
import lol.pyr.znpcsplus.entity.PacketEntity; import lol.pyr.znpcsplus.entity.PacketEntity;
import lol.pyr.znpcsplus.hologram.HologramImpl; import lol.pyr.znpcsplus.hologram.HologramImpl;
import lol.pyr.znpcsplus.interaction.InteractionAction; import lol.pyr.znpcsplus.interaction.InteractionAction;
@ -31,18 +33,18 @@ public class NpcImpl extends Viewable implements Npc {
private final Map<EntityPropertyImpl<?>, Object> propertyMap = new HashMap<>(); private final Map<EntityPropertyImpl<?>, Object> propertyMap = new HashMap<>();
private final List<InteractionAction> actions = new ArrayList<>(); private final List<InteractionAction> actions = new ArrayList<>();
protected NpcImpl(UUID uuid, ConfigManager configManager, LegacyComponentSerializer textSerializer, World world, NpcTypeImpl type, NpcLocation location, PacketFactory packetFactory) { protected NpcImpl(UUID uuid, EntityPropertyRegistryImpl propertyRegistry, ConfigManager configManager, LegacyComponentSerializer textSerializer, World world, NpcTypeImpl type, NpcLocation location, PacketFactory packetFactory) {
this(uuid, configManager, packetFactory, textSerializer, world.getName(), type, location); this(uuid, propertyRegistry, configManager, packetFactory, textSerializer, world.getName(), type, location);
} }
public NpcImpl(UUID uuid, ConfigManager configManager, PacketFactory packetFactory, LegacyComponentSerializer textSerializer, String world, NpcTypeImpl type, NpcLocation location) { public NpcImpl(UUID uuid, EntityPropertyRegistryImpl propertyRegistry, ConfigManager configManager, PacketFactory packetFactory, LegacyComponentSerializer textSerializer, String world, NpcTypeImpl type, NpcLocation location) {
this.packetFactory = packetFactory; this.packetFactory = packetFactory;
this.worldName = world; this.worldName = world;
this.type = type; this.type = type;
this.location = location; this.location = location;
this.uuid = uuid; this.uuid = uuid;
entity = new PacketEntity(packetFactory, this, type.getType(), location); entity = new PacketEntity(packetFactory, this, type.getType(), location);
hologram = new HologramImpl(configManager, packetFactory, textSerializer, location.withY(location.getY() + type.getHologramOffset())); hologram = new HologramImpl(propertyRegistry, configManager, packetFactory, textSerializer, location.withY(location.getY() + type.getHologramOffset()));
} }
@ -112,12 +114,11 @@ public class NpcImpl extends Viewable implements Npc {
hologram.hide(player); hologram.hide(player);
} }
private void UNSAFE_refreshMeta() { private <T> void UNSAFE_refreshProperty(EntityPropertyImpl<T> property) {
for (Player viewer : getViewers()) entity.refreshMeta(viewer); for (Player viewer : getViewers()) {
} List<EntityData> data = property.makeStandaloneData(property.getDefaultValue(), viewer, entity, true);
if (data.size() > 0) packetFactory.sendMetadata(viewer, entity, data);
private void UNSAFE_remakeTeam() { }
for (Player viewer : getViewers()) entity.remakeTeam(viewer);
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@ -135,10 +136,9 @@ public class NpcImpl extends Viewable implements Npc {
} }
public <T> void setProperty(EntityPropertyImpl<T> key, T value) { public <T> void setProperty(EntityPropertyImpl<T> key, T value) {
if (value == null || value.equals(key.getDefaultValue())) removeProperty(key); if (value == null || value.equals(key.getDefaultValue())) propertyMap.remove(key);
else propertyMap.put(key, value); else propertyMap.put(key, value);
UNSAFE_refreshMeta(); UNSAFE_refreshProperty(key);
if (key.getName().equalsIgnoreCase("glow")) UNSAFE_remakeTeam();
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@ -146,14 +146,8 @@ public class NpcImpl extends Viewable implements Npc {
setProperty((EntityPropertyImpl<T>) property, (T) value); setProperty((EntityPropertyImpl<T>) property, (T) value);
} }
public void removeProperty(EntityPropertyImpl<?> key) { @Override
propertyMap.remove(key); public Set<EntityProperty<?>> getAppliedProperties() {
UNSAFE_refreshMeta();
if (key.getName().equalsIgnoreCase("glow")) UNSAFE_remakeTeam();
else if (key.getName().equalsIgnoreCase("dinnerbone")) respawn();
}
public Set<EntityPropertyImpl<?>> getAppliedProperties() {
return Collections.unmodifiableSet(propertyMap.keySet()); return Collections.unmodifiableSet(propertyMap.keySet());
} }

@ -22,6 +22,7 @@ public class NpcRegistryImpl implements NpcRegistry {
private final PacketFactory packetFactory; private final PacketFactory packetFactory;
private final ConfigManager configManager; private final ConfigManager configManager;
private final LegacyComponentSerializer textSerializer; private final LegacyComponentSerializer textSerializer;
private final EntityPropertyRegistryImpl propertyRegistry;
private final List<NpcEntryImpl> npcList = new ArrayList<>(); private final List<NpcEntryImpl> npcList = new ArrayList<>();
private final Map<String, NpcEntryImpl> npcIdLookupMap = new HashMap<>(); private final Map<String, NpcEntryImpl> npcIdLookupMap = new HashMap<>();
@ -29,6 +30,7 @@ public class NpcRegistryImpl implements NpcRegistry {
public NpcRegistryImpl(ConfigManager configManager, ZNpcsPlus plugin, PacketFactory packetFactory, ActionRegistry actionRegistry, TaskScheduler scheduler, NpcTypeRegistryImpl typeRegistry, EntityPropertyRegistryImpl propertyRegistry, LegacyComponentSerializer textSerializer) { public NpcRegistryImpl(ConfigManager configManager, ZNpcsPlus plugin, PacketFactory packetFactory, ActionRegistry actionRegistry, TaskScheduler scheduler, NpcTypeRegistryImpl typeRegistry, EntityPropertyRegistryImpl propertyRegistry, LegacyComponentSerializer textSerializer) {
this.textSerializer = textSerializer; this.textSerializer = textSerializer;
this.propertyRegistry = propertyRegistry;
storage = configManager.getConfig().storageType().create(configManager, plugin, packetFactory, actionRegistry, typeRegistry, propertyRegistry, textSerializer); storage = configManager.getConfig().storageType().create(configManager, plugin, packetFactory, actionRegistry, typeRegistry, propertyRegistry, textSerializer);
this.packetFactory = packetFactory; this.packetFactory = packetFactory;
this.configManager = configManager; this.configManager = configManager;
@ -134,7 +136,7 @@ public class NpcRegistryImpl implements NpcRegistry {
public NpcEntryImpl create(String id, World world, NpcTypeImpl type, NpcLocation location) { public NpcEntryImpl create(String id, World world, NpcTypeImpl type, NpcLocation location) {
id = id.toLowerCase(); id = id.toLowerCase();
if (npcIdLookupMap.containsKey(id)) throw new IllegalArgumentException("An npc with the id " + id + " already exists!"); if (npcIdLookupMap.containsKey(id)) throw new IllegalArgumentException("An npc with the id " + id + " already exists!");
NpcImpl npc = new NpcImpl(UUID.randomUUID(), configManager, textSerializer, world, type, location, packetFactory); NpcImpl npc = new NpcImpl(UUID.randomUUID(), propertyRegistry, configManager, textSerializer, world, type, location, packetFactory);
NpcEntryImpl entry = new NpcEntryImpl(id, npc); NpcEntryImpl entry = new NpcEntryImpl(id, npc);
register(entry); register(entry);
return entry; return entry;

@ -1,12 +1,13 @@
package lol.pyr.znpcsplus.packets; package lol.pyr.znpcsplus.packets;
import com.github.retrooper.packetevents.protocol.entity.data.EntityData; import com.github.retrooper.packetevents.protocol.entity.data.EntityData;
import com.github.retrooper.packetevents.protocol.player.Equipment;
import lol.pyr.znpcsplus.api.entity.PropertyHolder; import lol.pyr.znpcsplus.api.entity.PropertyHolder;
import lol.pyr.znpcsplus.entity.PacketEntity; import lol.pyr.znpcsplus.entity.PacketEntity;
import net.kyori.adventure.text.format.NamedTextColor;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
public interface PacketFactory { public interface PacketFactory {
@ -16,10 +17,9 @@ public interface PacketFactory {
void teleportEntity(Player player, PacketEntity entity); void teleportEntity(Player player, PacketEntity entity);
CompletableFuture<Void> addTabPlayer(Player player, PacketEntity entity, PropertyHolder properties); CompletableFuture<Void> addTabPlayer(Player player, PacketEntity entity, PropertyHolder properties);
void removeTabPlayer(Player player, PacketEntity entity); void removeTabPlayer(Player player, PacketEntity entity);
void createTeam(Player player, PacketEntity entity, PropertyHolder properties); void createTeam(Player player, PacketEntity entity, NamedTextColor glowColor);
void removeTeam(Player player, PacketEntity entity); void removeTeam(Player player, PacketEntity entity);
Map<Integer, EntityData> generateMetadata(Player player, PacketEntity entity, PropertyHolder properties);
void sendAllMetadata(Player player, PacketEntity entity, PropertyHolder properties); void sendAllMetadata(Player player, PacketEntity entity, PropertyHolder properties);
void sendEquipment(Player player, PacketEntity entity, Equipment equipment);
void sendMetadata(Player player, PacketEntity entity, List<EntityData> data); void sendMetadata(Player player, PacketEntity entity, List<EntityData> data);
void sendEquipment(Player player, PacketEntity entity, PropertyHolder properties);
} }

@ -1,27 +0,0 @@
package lol.pyr.znpcsplus.packets;
import com.github.retrooper.packetevents.PacketEventsAPI;
import com.github.retrooper.packetevents.protocol.entity.data.EntityData;
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 net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import java.util.Map;
public class V1_10PacketFactory extends V1_9PacketFactory {
public V1_10PacketFactory(TaskScheduler scheduler, MetadataFactory metadataFactory, PacketEventsAPI<Plugin> packetEvents, EntityPropertyRegistryImpl propertyRegistry, LegacyComponentSerializer textSerializer) {
super(scheduler, metadataFactory, packetEvents, propertyRegistry, textSerializer);
}
@Override
public Map<Integer, EntityData> generateMetadata(Player player, PacketEntity entity, PropertyHolder properties) {
Map<Integer, EntityData> data = super.generateMetadata(player, entity, properties);
add(data, metadataFactory.noGravity());
return data;
}
}

@ -1,27 +0,0 @@
package lol.pyr.znpcsplus.packets;
import com.github.retrooper.packetevents.PacketEventsAPI;
import com.github.retrooper.packetevents.protocol.entity.data.EntityData;
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 net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import java.util.Map;
public class V1_11PacketFactory extends V1_10PacketFactory {
public V1_11PacketFactory(TaskScheduler scheduler, MetadataFactory metadataFactory, PacketEventsAPI<Plugin> packetEvents, EntityPropertyRegistryImpl propertyRegistry, LegacyComponentSerializer textSerializer) {
super(scheduler, metadataFactory, packetEvents, propertyRegistry, textSerializer);
}
@Override
public Map<Integer, EntityData> generateMetadata(Player player, PacketEntity entity, PropertyHolder properties) {
Map<Integer, EntityData> data = super.generateMetadata(player, entity, properties);
add(data, metadataFactory.usingItem(properties.getProperty(propertyRegistry.getByName("using_item", Boolean.class)), false, false));
return data;
}
}

@ -1,33 +0,0 @@
package lol.pyr.znpcsplus.packets;
import com.github.retrooper.packetevents.PacketEventsAPI;
import com.github.retrooper.packetevents.protocol.entity.data.EntityData;
import com.github.retrooper.packetevents.protocol.entity.pose.EntityPose;
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 lol.pyr.znpcsplus.util.NpcPose;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import java.util.Map;
public class V1_14PacketFactory extends V1_11PacketFactory {
public V1_14PacketFactory(TaskScheduler scheduler, MetadataFactory metadataFactory, PacketEventsAPI<Plugin> packetEvents, EntityPropertyRegistryImpl propertyRegistry, LegacyComponentSerializer textSerializer) {
super(scheduler, metadataFactory, packetEvents, propertyRegistry, textSerializer);
}
@Override
public Map<Integer, EntityData> generateMetadata(Player player, PacketEntity entity, PropertyHolder properties) {
Map<Integer, EntityData> data = super.generateMetadata(player, entity, properties);
add(data, metadataFactory.pose(adaptNpcPose(properties.getProperty(propertyRegistry.getByName("pose", NpcPose.class)))));
return data;
}
protected EntityPose adaptNpcPose(NpcPose pose) {
return EntityPose.valueOf(pose.name());
}
}

@ -1,27 +0,0 @@
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 net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
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, LegacyComponentSerializer textSerializer) {
super(scheduler, metadataFactory, packetEvents, propertyRegistry, textSerializer);
}
@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));
}
}

@ -1,25 +1,23 @@
package lol.pyr.znpcsplus.packets; package lol.pyr.znpcsplus.packets;
import com.github.retrooper.packetevents.PacketEventsAPI; import com.github.retrooper.packetevents.PacketEventsAPI;
import com.github.retrooper.packetevents.protocol.entity.data.EntityData;
import com.github.retrooper.packetevents.util.Vector3d; import com.github.retrooper.packetevents.util.Vector3d;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerSpawnEntity; import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerSpawnEntity;
import lol.pyr.znpcsplus.api.entity.PropertyHolder; import lol.pyr.znpcsplus.api.entity.PropertyHolder;
import lol.pyr.znpcsplus.entity.EntityPropertyRegistryImpl; import lol.pyr.znpcsplus.entity.EntityPropertyRegistryImpl;
import lol.pyr.znpcsplus.entity.PacketEntity; import lol.pyr.znpcsplus.entity.PacketEntity;
import lol.pyr.znpcsplus.metadata.MetadataFactory;
import lol.pyr.znpcsplus.scheduling.TaskScheduler; import lol.pyr.znpcsplus.scheduling.TaskScheduler;
import lol.pyr.znpcsplus.util.NpcLocation; import lol.pyr.znpcsplus.util.NpcLocation;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin; import org.bukkit.plugin.Plugin;
import java.util.Map;
import java.util.Optional; import java.util.Optional;
public class V1_17PacketFactory extends V1_16PacketFactory { public class V1_17PacketFactory extends V1_8PacketFactory {
public V1_17PacketFactory(TaskScheduler scheduler, MetadataFactory metadataFactory, PacketEventsAPI<Plugin> packetEvents, EntityPropertyRegistryImpl propertyRegistry, LegacyComponentSerializer textSerializer) { public V1_17PacketFactory(TaskScheduler scheduler, PacketEventsAPI<Plugin> packetEvents, EntityPropertyRegistryImpl propertyRegistry, LegacyComponentSerializer textSerializer) {
super(scheduler, metadataFactory, packetEvents, propertyRegistry, textSerializer); super(scheduler, packetEvents, propertyRegistry, textSerializer);
} }
@Override @Override
@ -28,13 +26,6 @@ public class V1_17PacketFactory extends V1_16PacketFactory {
sendPacket(player, new WrapperPlayServerSpawnEntity(entity.getEntityId(), Optional.of(entity.getUuid()), entity.getType(), sendPacket(player, new WrapperPlayServerSpawnEntity(entity.getEntityId(), Optional.of(entity.getUuid()), entity.getType(),
npcLocationToVector(location), location.getPitch(), location.getYaw(), location.getYaw(), 0, Optional.of(new Vector3d()))); npcLocationToVector(location), location.getPitch(), location.getYaw(), location.getYaw(), 0, Optional.of(new Vector3d())));
sendAllMetadata(player, entity, properties); sendAllMetadata(player, entity, properties);
createTeam(player, entity, properties); createTeam(player, entity, properties.getProperty(propertyRegistry.getByName("glow", NamedTextColor.class)));
}
@Override
public Map<Integer, EntityData> generateMetadata(Player player, PacketEntity entity, PropertyHolder properties) {
Map<Integer, EntityData> data = super.generateMetadata(player, entity, properties);
add(data, metadataFactory.shaking(properties.getProperty(propertyRegistry.getByName("shaking", Boolean.class))));
return data;
} }
} }

@ -7,10 +7,9 @@ import com.github.retrooper.packetevents.protocol.player.GameMode;
import com.github.retrooper.packetevents.protocol.player.UserProfile; import com.github.retrooper.packetevents.protocol.player.UserProfile;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerPlayerInfoRemove; import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerPlayerInfoRemove;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerPlayerInfoUpdate; import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerPlayerInfoUpdate;
import lol.pyr.znpcsplus.api.entity.PropertyHolder;
import lol.pyr.znpcsplus.entity.EntityPropertyRegistryImpl; import lol.pyr.znpcsplus.entity.EntityPropertyRegistryImpl;
import lol.pyr.znpcsplus.entity.PacketEntity; import lol.pyr.znpcsplus.entity.PacketEntity;
import lol.pyr.znpcsplus.api.entity.PropertyHolder;
import lol.pyr.znpcsplus.metadata.MetadataFactory;
import lol.pyr.znpcsplus.scheduling.TaskScheduler; import lol.pyr.znpcsplus.scheduling.TaskScheduler;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
@ -22,8 +21,8 @@ import java.util.concurrent.CompletableFuture;
public class V1_19PacketFactory extends V1_17PacketFactory { public class V1_19PacketFactory extends V1_17PacketFactory {
private final boolean oldTabPackets; private final boolean oldTabPackets;
public V1_19PacketFactory(TaskScheduler scheduler, MetadataFactory metadataFactory, PacketEventsAPI<Plugin> packetEvents, EntityPropertyRegistryImpl propertyRegistry, LegacyComponentSerializer textSerializer) { public V1_19PacketFactory(TaskScheduler scheduler, PacketEventsAPI<Plugin> packetEvents, EntityPropertyRegistryImpl propertyRegistry, LegacyComponentSerializer textSerializer) {
super(scheduler, metadataFactory, packetEvents, propertyRegistry, textSerializer); super(scheduler, packetEvents, propertyRegistry, textSerializer);
oldTabPackets = packetEvents.getServerManager().getVersion().isOlderThanOrEquals(ServerVersion.V_1_19_2); oldTabPackets = packetEvents.getServerManager().getVersion().isOlderThanOrEquals(ServerVersion.V_1_19_2);
} }

@ -1,29 +1,28 @@
package lol.pyr.znpcsplus.packets; package lol.pyr.znpcsplus.packets;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.PacketEventsAPI; import com.github.retrooper.packetevents.PacketEventsAPI;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.protocol.entity.data.EntityData; 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.EntityType;
import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes; import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes;
import com.github.retrooper.packetevents.protocol.item.ItemStack; import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.protocol.player.*; import com.github.retrooper.packetevents.protocol.player.Equipment;
import com.github.retrooper.packetevents.protocol.player.GameMode;
import com.github.retrooper.packetevents.protocol.player.UserProfile;
import com.github.retrooper.packetevents.util.Vector3d; import com.github.retrooper.packetevents.util.Vector3d;
import com.github.retrooper.packetevents.wrapper.PacketWrapper; import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import com.github.retrooper.packetevents.wrapper.play.server.*; import com.github.retrooper.packetevents.wrapper.play.server.*;
import lol.pyr.znpcsplus.api.entity.EntityProperty;
import lol.pyr.znpcsplus.api.entity.PropertyHolder; import lol.pyr.znpcsplus.api.entity.PropertyHolder;
import lol.pyr.znpcsplus.api.skin.SkinDescriptor; import lol.pyr.znpcsplus.api.skin.SkinDescriptor;
import lol.pyr.znpcsplus.entity.EntityPropertyImpl;
import lol.pyr.znpcsplus.entity.EntityPropertyRegistryImpl; import lol.pyr.znpcsplus.entity.EntityPropertyRegistryImpl;
import lol.pyr.znpcsplus.entity.PacketEntity; import lol.pyr.znpcsplus.entity.PacketEntity;
import lol.pyr.znpcsplus.metadata.MetadataFactory;
import lol.pyr.znpcsplus.scheduling.TaskScheduler; import lol.pyr.znpcsplus.scheduling.TaskScheduler;
import lol.pyr.znpcsplus.skin.BaseSkinDescriptor; import lol.pyr.znpcsplus.skin.BaseSkinDescriptor;
import lol.pyr.znpcsplus.util.*; import lol.pyr.znpcsplus.util.NpcLocation;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import org.bukkit.Color;
import org.bukkit.DyeColor;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin; import org.bukkit.plugin.Plugin;
@ -32,14 +31,12 @@ import java.util.concurrent.CompletableFuture;
public class V1_8PacketFactory implements PacketFactory { public class V1_8PacketFactory implements PacketFactory {
protected final TaskScheduler scheduler; protected final TaskScheduler scheduler;
protected final MetadataFactory metadataFactory;
protected final PacketEventsAPI<Plugin> packetEvents; protected final PacketEventsAPI<Plugin> packetEvents;
protected final EntityPropertyRegistryImpl propertyRegistry; protected final EntityPropertyRegistryImpl propertyRegistry;
protected final LegacyComponentSerializer textSerializer; protected final LegacyComponentSerializer textSerializer;
public V1_8PacketFactory(TaskScheduler scheduler, MetadataFactory metadataFactory, PacketEventsAPI<Plugin> packetEvents, EntityPropertyRegistryImpl propertyRegistry, LegacyComponentSerializer textSerializer) { public V1_8PacketFactory(TaskScheduler scheduler, PacketEventsAPI<Plugin> packetEvents, EntityPropertyRegistryImpl propertyRegistry, LegacyComponentSerializer textSerializer) {
this.scheduler = scheduler; this.scheduler = scheduler;
this.metadataFactory = metadataFactory;
this.packetEvents = packetEvents; this.packetEvents = packetEvents;
this.propertyRegistry = propertyRegistry; this.propertyRegistry = propertyRegistry;
this.textSerializer = textSerializer; this.textSerializer = textSerializer;
@ -48,7 +45,7 @@ public class V1_8PacketFactory implements PacketFactory {
@Override @Override
public void spawnPlayer(Player player, PacketEntity entity, PropertyHolder properties) { public void spawnPlayer(Player player, PacketEntity entity, PropertyHolder properties) {
addTabPlayer(player, entity, properties).thenAccept(ignored -> { addTabPlayer(player, entity, properties).thenAccept(ignored -> {
createTeam(player, entity, properties); createTeam(player, entity, properties.getProperty(propertyRegistry.getByName("glow", NamedTextColor.class)));
NpcLocation location = entity.getLocation(); NpcLocation location = entity.getLocation();
sendPacket(player, new WrapperPlayServerSpawnPlayer(entity.getEntityId(), sendPacket(player, new WrapperPlayServerSpawnPlayer(entity.getEntityId(),
entity.getUuid(), npcLocationToVector(location), location.getYaw(), location.getPitch(), Collections.emptyList())); entity.getUuid(), npcLocationToVector(location), location.getYaw(), location.getPitch(), Collections.emptyList()));
@ -69,7 +66,7 @@ public class V1_8PacketFactory implements PacketFactory {
new WrapperPlayServerSpawnEntity(entity.getEntityId(), Optional.of(entity.getUuid()), entity.getType(), npcLocationToVector(location), new WrapperPlayServerSpawnEntity(entity.getEntityId(), Optional.of(entity.getUuid()), entity.getType(), npcLocationToVector(location),
location.getPitch(), location.getYaw(), location.getYaw(), 0, Optional.empty())); location.getPitch(), location.getYaw(), location.getYaw(), 0, Optional.empty()));
sendAllMetadata(player, entity, properties); sendAllMetadata(player, entity, properties);
createTeam(player, entity, properties); createTeam(player, entity, properties.getProperty(propertyRegistry.getByName("glow", NamedTextColor.class)));
} }
protected Vector3d npcLocationToVector(NpcLocation location) { protected Vector3d npcLocationToVector(NpcLocation location) {
@ -111,12 +108,12 @@ public class V1_8PacketFactory implements PacketFactory {
} }
@Override @Override
public void createTeam(Player player, PacketEntity entity, PropertyHolder properties) { public void createTeam(Player player, PacketEntity entity, NamedTextColor glowColor) {
sendPacket(player, new WrapperPlayServerTeams("npc_team_" + entity.getEntityId(), WrapperPlayServerTeams.TeamMode.CREATE, new WrapperPlayServerTeams.ScoreBoardTeamInfo( sendPacket(player, new WrapperPlayServerTeams("npc_team_" + entity.getEntityId(), WrapperPlayServerTeams.TeamMode.CREATE, new WrapperPlayServerTeams.ScoreBoardTeamInfo(
Component.empty(), Component.empty(), Component.empty(), Component.empty(), Component.empty(), Component.empty(),
WrapperPlayServerTeams.NameTagVisibility.NEVER, WrapperPlayServerTeams.NameTagVisibility.NEVER,
WrapperPlayServerTeams.CollisionRule.NEVER, WrapperPlayServerTeams.CollisionRule.NEVER,
properties.hasProperty(propertyRegistry.getByName("glow")) ? properties.getProperty(propertyRegistry.getByName("glow", NamedTextColor.class)) : NamedTextColor.WHITE, glowColor == null ? NamedTextColor.WHITE : glowColor,
WrapperPlayServerTeams.OptionData.NONE WrapperPlayServerTeams.OptionData.NONE
))); )));
sendPacket(player, new WrapperPlayServerTeams("npc_team_" + entity.getEntityId(), WrapperPlayServerTeams.TeamMode.ADD_ENTITIES, (WrapperPlayServerTeams.ScoreBoardTeamInfo) null, sendPacket(player, new WrapperPlayServerTeams("npc_team_" + entity.getEntityId(), WrapperPlayServerTeams.TeamMode.ADD_ENTITIES, (WrapperPlayServerTeams.ScoreBoardTeamInfo) null,
@ -128,21 +125,20 @@ public class V1_8PacketFactory implements PacketFactory {
sendPacket(player, new WrapperPlayServerTeams("npc_team_" + entity.getEntityId(), WrapperPlayServerTeams.TeamMode.REMOVE, (WrapperPlayServerTeams.ScoreBoardTeamInfo) null)); sendPacket(player, new WrapperPlayServerTeams("npc_team_" + entity.getEntityId(), WrapperPlayServerTeams.TeamMode.REMOVE, (WrapperPlayServerTeams.ScoreBoardTeamInfo) null));
} }
@Override /*
public Map<Integer, EntityData> generateMetadata(Player player, PacketEntity entity, PropertyHolder properties) { List<EntityData> data = new ArrayList<>();
HashMap<Integer, EntityData> data = new HashMap<>(); data.add(metadataFactory.effects(
add(data, metadataFactory.effects(
properties.getProperty(propertyRegistry.getByName("fire", Boolean.class)), properties.getProperty(propertyRegistry.getByName("fire", Boolean.class)),
false, false,
properties.getProperty(propertyRegistry.getByName("invisible", Boolean.class)), properties.getProperty(propertyRegistry.getByName("invisible", Boolean.class)),
false, false,
properties.getProperty(propertyRegistry.getByName("using_item", Boolean.class)) properties.getProperty(propertyRegistry.getByName("using_item", Boolean.class))
)); ));
add(data, metadataFactory.silent(properties.getProperty(propertyRegistry.getByName("silent", Boolean.class)))); data.add(metadataFactory.silent(properties.getProperty(propertyRegistry.getByName("silent", Boolean.class))));
add(data, metadataFactory.potionColor(properties.getProperty(propertyRegistry.getByName("potion_color", Color.class)).asRGB())); data.add(metadataFactory.potionColor(properties.getProperty(propertyRegistry.getByName("potion_color", Color.class)).asRGB()));
add(data, metadataFactory.potionAmbient(properties.getProperty(propertyRegistry.getByName("potion_ambient", Boolean.class)))); data.add(metadataFactory.potionAmbient(properties.getProperty(propertyRegistry.getByName("potion_ambient", Boolean.class))));
if (entity.getType().equals(EntityTypes.PLAYER)) { if (entity.getType().equals(EntityTypes.PLAYER)) {
add(data, metadataFactory.skinLayers( data.add(metadataFactory.skinLayers(
properties.getProperty(propertyRegistry.getByName("skin_cape", Boolean.class)), properties.getProperty(propertyRegistry.getByName("skin_cape", Boolean.class)),
properties.getProperty(propertyRegistry.getByName("skin_jacket", Boolean.class)), properties.getProperty(propertyRegistry.getByName("skin_jacket", Boolean.class)),
properties.getProperty(propertyRegistry.getByName("skin_left_sleeve", Boolean.class)), properties.getProperty(propertyRegistry.getByName("skin_left_sleeve", Boolean.class)),
@ -151,60 +147,60 @@ public class V1_8PacketFactory implements PacketFactory {
properties.getProperty(propertyRegistry.getByName("skin_right_leg", Boolean.class)), properties.getProperty(propertyRegistry.getByName("skin_right_leg", Boolean.class)),
properties.getProperty(propertyRegistry.getByName("skin_hat", Boolean.class)) properties.getProperty(propertyRegistry.getByName("skin_hat", Boolean.class))
)); ));
add(data, metadataFactory.shoulderEntityLeft(properties.getProperty(propertyRegistry.getByName("shoulder_entity_left", ParrotVariant.class)))); data.add(metadataFactory.shoulderEntityLeft(properties.getProperty(propertyRegistry.getByName("shoulder_entity_left", ParrotVariant.class))));
add(data, metadataFactory.shoulderEntityRight(properties.getProperty(propertyRegistry.getByName("shoulder_entity_right", ParrotVariant.class)))); data.add(metadataFactory.shoulderEntityRight(properties.getProperty(propertyRegistry.getByName("shoulder_entity_right", ParrotVariant.class))));
} }
else if (entity.getType().equals(EntityTypes.ARMOR_STAND)) { else if (entity.getType().equals(EntityTypes.ARMOR_STAND)) {
add(data, metadataFactory.armorStandProperties( data.add(metadataFactory.armorStandProperties(
properties.getProperty(propertyRegistry.getByName("small", Boolean.class)), properties.getProperty(propertyRegistry.getByName("small", Boolean.class)),
properties.getProperty(propertyRegistry.getByName("arms", Boolean.class)), properties.getProperty(propertyRegistry.getByName("arms", Boolean.class)),
!properties.getProperty(propertyRegistry.getByName("base_plate", Boolean.class)) !properties.getProperty(propertyRegistry.getByName("base_plate", Boolean.class))
)); ));
add(data, metadataFactory.armorStandHeadRotation(properties.getProperty(propertyRegistry.getByName("head_rotation", Vector3f.class)))); data.add(metadataFactory.armorStandHeadRotation(properties.getProperty(propertyRegistry.getByName("head_rotation", Vector3f.class))));
add(data, metadataFactory.armorStandBodyRotation(properties.getProperty(propertyRegistry.getByName("body_rotation", Vector3f.class)))); data.add(metadataFactory.armorStandBodyRotation(properties.getProperty(propertyRegistry.getByName("body_rotation", Vector3f.class))));
add(data, metadataFactory.armorStandLeftArmRotation(properties.getProperty(propertyRegistry.getByName("left_arm_rotation", Vector3f.class)))); data.add(metadataFactory.armorStandLeftArmRotation(properties.getProperty(propertyRegistry.getByName("left_arm_rotation", Vector3f.class))));
add(data, metadataFactory.armorStandRightArmRotation(properties.getProperty(propertyRegistry.getByName("right_arm_rotation", Vector3f.class)))); data.add(metadataFactory.armorStandRightArmRotation(properties.getProperty(propertyRegistry.getByName("right_arm_rotation", Vector3f.class))));
add(data, metadataFactory.armorStandLeftLegRotation(properties.getProperty(propertyRegistry.getByName("left_leg_rotation", Vector3f.class)))); data.add(metadataFactory.armorStandLeftLegRotation(properties.getProperty(propertyRegistry.getByName("left_leg_rotation", Vector3f.class))));
add(data, metadataFactory.armorStandRightLegRotation(properties.getProperty(propertyRegistry.getByName("right_leg_rotation", Vector3f.class)))); data.add(metadataFactory.armorStandRightLegRotation(properties.getProperty(propertyRegistry.getByName("right_leg_rotation", Vector3f.class))));
} }
else if (entity.getType().equals(EntityTypes.AXOLOTL)) { else if (entity.getType().equals(EntityTypes.AXOLOTL)) {
add(data, metadataFactory.axolotlVariant(properties.getProperty(propertyRegistry.getByName("axolotl_variant", Integer.class)))); data.add(metadataFactory.axolotlVariant(properties.getProperty(propertyRegistry.getByName("axolotl_variant", Integer.class))));
add(data, metadataFactory.playingDead(properties.getProperty(propertyRegistry.getByName("playing_dead", Boolean.class)))); data.add(metadataFactory.playingDead(properties.getProperty(propertyRegistry.getByName("playing_dead", Boolean.class))));
} }
else if (entity.getType().equals(EntityTypes.BAT)) { else if (entity.getType().equals(EntityTypes.BAT)) {
add(data, metadataFactory.batHanging(properties.getProperty(propertyRegistry.getByName("hanging", Boolean.class)))); data.add(metadataFactory.batHanging(properties.getProperty(propertyRegistry.getByName("hanging", Boolean.class))));
} }
else if (entity.getType().equals(EntityTypes.BEE)) { else if (entity.getType().equals(EntityTypes.BEE)) {
add(data, metadataFactory.beeAngry(properties.getProperty(propertyRegistry.getByName("angry", Boolean.class)))); data.add(metadataFactory.beeAngry(properties.getProperty(propertyRegistry.getByName("angry", Boolean.class))));
add(data, metadataFactory.beeHasNectar(properties.getProperty(propertyRegistry.getByName("has_nectar", Boolean.class)))); data.add(metadataFactory.beeHasNectar(properties.getProperty(propertyRegistry.getByName("has_nectar", Boolean.class))));
} }
else if (entity.getType().equals(EntityTypes.BLAZE)) { else if (entity.getType().equals(EntityTypes.BLAZE)) {
add(data, metadataFactory.blazeOnFire(properties.getProperty(propertyRegistry.getByName("blaze_on_fire", Boolean.class)))); data.add(metadataFactory.blazeOnFire(properties.getProperty(propertyRegistry.getByName("blaze_on_fire", Boolean.class))));
} }
else if (entity.getType().equals(EntityTypes.CAT)) { else if (entity.getType().equals(EntityTypes.CAT)) {
add(data, metadataFactory.catVariant(properties.getProperty(propertyRegistry.getByName("cat_variant", CatVariant.class)))); data.add(metadataFactory.catVariant(properties.getProperty(propertyRegistry.getByName("cat_variant", CatVariant.class))));
add(data, metadataFactory.catLying(properties.getProperty(propertyRegistry.getByName("cat_lying", Boolean.class)))); data.add(metadataFactory.catLying(properties.getProperty(propertyRegistry.getByName("cat_lying", Boolean.class))));
add(data, metadataFactory.catCollarColor(properties.getProperty(propertyRegistry.getByName("cat_collar_color", DyeColor.class)))); data.add(metadataFactory.catCollarColor(properties.getProperty(propertyRegistry.getByName("cat_collar_color", DyeColor.class))));
add(data, metadataFactory.catTamed(properties.hasProperty(propertyRegistry.getByName("cat_collar_color", DyeColor.class)))); data.add(metadataFactory.catTamed(properties.hasProperty(propertyRegistry.getByName("cat_collar_color", DyeColor.class))));
} }
else if (entity.getType().equals(EntityTypes.CREEPER)) { else if (entity.getType().equals(EntityTypes.CREEPER)) {
add(data, metadataFactory.creeperState(properties.getProperty(propertyRegistry.getByName("creeper_state", CreeperState.class)))); data.add(metadataFactory.creeperState(properties.getProperty(propertyRegistry.getByName("creeper_state", CreeperState.class))));
add(data, metadataFactory.creeperCharged(properties.getProperty(propertyRegistry.getByName("creeper_charged", Boolean.class)))); data.add(metadataFactory.creeperCharged(properties.getProperty(propertyRegistry.getByName("creeper_charged", Boolean.class))));
} }
else if (entity.getType().equals(EntityTypes.ENDERMAN)) { else if (entity.getType().equals(EntityTypes.ENDERMAN)) {
add(data, metadataFactory.endermanHeldBlock( data.add(metadataFactory.endermanHeldBlock(
properties.getProperty(propertyRegistry.getByName("enderman_held_block", BlockState.class)).getGlobalId()) properties.getProperty(propertyRegistry.getByName("enderman_held_block", BlockState.class)).getGlobalId())
); );
add(data, metadataFactory.endermanScreaming(properties.getProperty(propertyRegistry.getByName("enderman_screaming", Boolean.class)))); data.add(metadataFactory.endermanScreaming(properties.getProperty(propertyRegistry.getByName("enderman_screaming", Boolean.class))));
add(data, metadataFactory.endermanStaring(properties.getProperty(propertyRegistry.getByName("enderman_staring", Boolean.class)))); data.add(metadataFactory.endermanStaring(properties.getProperty(propertyRegistry.getByName("enderman_staring", Boolean.class))));
} }
else if (entity.getType().equals(EntityTypes.EVOKER)) { else if (entity.getType().equals(EntityTypes.EVOKER)) {
add(data, metadataFactory.evokerSpell(properties.getProperty(propertyRegistry.getByName("evoker_spell", SpellType.class)).ordinal())); data.add(metadataFactory.evokerSpell(properties.getProperty(propertyRegistry.getByName("evoker_spell", SpellType.class)).ordinal()));
} }
else if (entity.getType().equals(EntityTypes.FOX)) { else if (entity.getType().equals(EntityTypes.FOX)) {
// Not sure if this should be in here or in 1.14 PacketFactory // Not sure if this should be in here or in 1.14 PacketFactory
add(data, metadataFactory.foxVariant(properties.getProperty(propertyRegistry.getByName("fox_variant", FoxVariant.class)).ordinal())); data.add(metadataFactory.foxVariant(properties.getProperty(propertyRegistry.getByName("fox_variant", FoxVariant.class)).ordinal()));
add(data, metadataFactory.foxProperties( data.add(metadataFactory.foxProperties(
properties.getProperty(propertyRegistry.getByName("fox_sitting", Boolean.class)), properties.getProperty(propertyRegistry.getByName("fox_sitting", Boolean.class)),
properties.getProperty(propertyRegistry.getByName("fox_crouching", Boolean.class)), properties.getProperty(propertyRegistry.getByName("fox_crouching", Boolean.class)),
properties.getProperty(propertyRegistry.getByName("fox_sleeping", Boolean.class)), properties.getProperty(propertyRegistry.getByName("fox_sleeping", Boolean.class)),
@ -212,20 +208,20 @@ public class V1_8PacketFactory implements PacketFactory {
)); ));
} }
else if (entity.getType().equals(EntityTypes.FROG)) { else if (entity.getType().equals(EntityTypes.FROG)) {
add(data, metadataFactory.frogVariant(properties.getProperty(propertyRegistry.getByName("frog_variant", FrogVariant.class)).ordinal())); data.add(metadataFactory.frogVariant(properties.getProperty(propertyRegistry.getByName("frog_variant", FrogVariant.class)).ordinal()));
} }
else if (entity.getType().equals(EntityTypes.GHAST)) { else if (entity.getType().equals(EntityTypes.GHAST)) {
add(data, metadataFactory.ghastAttacking(properties.getProperty(propertyRegistry.getByName("attacking", Boolean.class)))); data.add(metadataFactory.ghastAttacking(properties.getProperty(propertyRegistry.getByName("attacking", Boolean.class))));
} }
else if (entity.getType().equals(EntityTypes.GOAT)) { else if (entity.getType().equals(EntityTypes.GOAT)) {
add(data, metadataFactory.goatHasLeftHorn(properties.getProperty(propertyRegistry.getByName("has_left_horn", Boolean.class)))); data.add(metadataFactory.goatHasLeftHorn(properties.getProperty(propertyRegistry.getByName("has_left_horn", Boolean.class))));
add(data, metadataFactory.goatHasRightHorn(properties.getProperty(propertyRegistry.getByName("has_right_horn", Boolean.class)))); data.add(metadataFactory.goatHasRightHorn(properties.getProperty(propertyRegistry.getByName("has_right_horn", Boolean.class))));
} }
else if (entity.getType().equals(EntityTypes.GUARDIAN)) { else if (entity.getType().equals(EntityTypes.GUARDIAN)) {
// TODO // TODO
} }
else if (entity.getType().equals(EntityTypes.HOGLIN)) { else if (entity.getType().equals(EntityTypes.HOGLIN)) {
add(data, metadataFactory.hoglinImmuneToZombification(properties.getProperty(propertyRegistry.getByName("immune_to_zombification", Boolean.class)))); data.add(metadataFactory.hoglinImmuneToZombification(properties.getProperty(propertyRegistry.getByName("immune_to_zombification", Boolean.class))));
} }
else if (entity.getType().equals(EntityTypes.VILLAGER)) { else if (entity.getType().equals(EntityTypes.VILLAGER)) {
VillagerProfession profession = properties.getProperty(propertyRegistry.getByName("villager_profession", VillagerProfession.class)); VillagerProfession profession = properties.getProperty(propertyRegistry.getByName("villager_profession", VillagerProfession.class));
@ -233,7 +229,7 @@ public class V1_8PacketFactory implements PacketFactory {
if (PacketEvents.getAPI().getServerManager().getVersion().isOlderThan(ServerVersion.V_1_14)) { if (PacketEvents.getAPI().getServerManager().getVersion().isOlderThan(ServerVersion.V_1_14)) {
professionId = profession.getLegacyId(); professionId = profession.getLegacyId();
} }
add(data, metadataFactory.villagerData( data.add(metadataFactory.villagerData(
properties.getProperty(propertyRegistry.getByName("villager_type", VillagerType.class)).ordinal(), properties.getProperty(propertyRegistry.getByName("villager_type", VillagerType.class)).ordinal(),
professionId, professionId,
properties.getProperty(propertyRegistry.getByName("villager_level", VillagerLevel.class)).ordinal() + 1 properties.getProperty(propertyRegistry.getByName("villager_level", VillagerLevel.class)).ordinal() + 1
@ -241,47 +237,31 @@ public class V1_8PacketFactory implements PacketFactory {
} }
if (properties.getProperty(propertyRegistry.getByName("dinnerbone", Boolean.class))) { if (properties.getProperty(propertyRegistry.getByName("dinnerbone", Boolean.class))) {
add(data, metadataFactory.name(Component.text("Dinnerbone"))); data.add(metadataFactory.name(Component.text("Dinnerbone")));
} }
else if (properties.hasProperty(propertyRegistry.getByName("name"))) { else if (properties.hasProperty(propertyRegistry.getByName("name"))) {
add(data, metadataFactory.name(PapiUtil.set(textSerializer, player, properties.getProperty(propertyRegistry.getByName("name", Component.class))))); data.add(metadataFactory.name(PapiUtil.set(textSerializer, player, properties.getProperty(propertyRegistry.getByName("name", Component.class)))));
add(data, metadataFactory.nameShown()); data.add(metadataFactory.nameShown());
} }
return data; return data;
} */
@Override @Override
public void sendAllMetadata(Player player, PacketEntity entity, PropertyHolder properties) { public void sendAllMetadata(Player player, PacketEntity entity, PropertyHolder properties) {
sendMetadata(player, entity, new ArrayList<>(generateMetadata(player, entity, properties).values())); Map<Integer, EntityData> datas = new HashMap<>();
sendEquipment(player, entity, properties); for (EntityProperty<?> property : properties.getAppliedProperties())
((EntityPropertyImpl<?>) property).UNSAFE_update(properties.getProperty(property), player, entity, false, datas);
sendMetadata(player, entity, new ArrayList<>(datas.values()));
} }
@Override @Override
public void sendMetadata(Player player, PacketEntity entity, List<EntityData> data) { public void sendMetadata(Player player, PacketEntity entity, List<EntityData> data) {
packetEvents.getPlayerManager().sendPacket(player, new WrapperPlayServerEntityMetadata(entity.getEntityId(), data)); sendPacket(player, new WrapperPlayServerEntityMetadata(entity.getEntityId(), data));
} }
@Override @Override
public void sendEquipment(Player player, PacketEntity entity, PropertyHolder properties) { public void sendEquipment(Player player, PacketEntity entity, Equipment equipment) {
for (Equipment equipment : generateEquipments(properties)) sendPacket(player, new WrapperPlayServerEntityEquipment(entity.getEntityId(), Collections.singletonList(equipment)));
sendPacket(player, new WrapperPlayServerEntityEquipment(entity.getEntityId(), Collections.singletonList(equipment)));
}
protected List<Equipment> generateEquipments(PropertyHolder properties) {
HashMap<String, EquipmentSlot> equipmentSlotMap = new HashMap<>();
equipmentSlotMap.put("helmet", EquipmentSlot.HELMET);
equipmentSlotMap.put("chestplate", EquipmentSlot.CHEST_PLATE);
equipmentSlotMap.put("leggings", EquipmentSlot.LEGGINGS);
equipmentSlotMap.put("boots", EquipmentSlot.BOOTS);
equipmentSlotMap.put("hand", EquipmentSlot.MAIN_HAND);
equipmentSlotMap.put("offhand", EquipmentSlot.OFF_HAND);
List<Equipment> equipements = new ArrayList<>();
for (Map.Entry<String, EquipmentSlot> entry : equipmentSlotMap.entrySet()) {
if (!properties.hasProperty(propertyRegistry.getByName(entry.getKey()))) continue;
equipements.add(new Equipment(entry.getValue(), properties.getProperty(propertyRegistry.getByName(entry.getKey(), ItemStack.class))));
}
return equipements;
} }
protected void sendPacket(Player player, PacketWrapper<?> packet) { protected void sendPacket(Player player, PacketWrapper<?> packet) {

@ -1,32 +0,0 @@
package lol.pyr.znpcsplus.packets;
import com.github.retrooper.packetevents.PacketEventsAPI;
import com.github.retrooper.packetevents.protocol.entity.data.EntityData;
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 net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import java.util.Map;
public class V1_9PacketFactory extends V1_8PacketFactory {
public V1_9PacketFactory(TaskScheduler scheduler, MetadataFactory metadataFactory, PacketEventsAPI<Plugin> packetEvents, EntityPropertyRegistryImpl propertyRegistry, LegacyComponentSerializer textSerializer) {
super(scheduler, metadataFactory, packetEvents, propertyRegistry, textSerializer);
}
@Override
public Map<Integer, EntityData> generateMetadata(Player player, PacketEntity entity, PropertyHolder properties) {
Map<Integer, EntityData> data = super.generateMetadata(player, entity, properties);
add(data, metadataFactory.effects(properties.getProperty(propertyRegistry.getByName("fire", Boolean.class)),
properties.hasProperty(propertyRegistry.getByName("glow", Boolean.class)),
properties.getProperty(propertyRegistry.getByName("invisible", Boolean.class)),
false,
properties.getProperty(propertyRegistry.getByName("using_item", Boolean.class))
));
return data;
}
}

@ -1,8 +1,10 @@
package lol.pyr.znpcsplus.storage.yaml; package lol.pyr.znpcsplus.storage.yaml;
import lol.pyr.znpcsplus.api.entity.EntityProperty;
import lol.pyr.znpcsplus.config.ConfigManager; import lol.pyr.znpcsplus.config.ConfigManager;
import lol.pyr.znpcsplus.entity.EntityPropertyImpl; import lol.pyr.znpcsplus.entity.EntityPropertyImpl;
import lol.pyr.znpcsplus.entity.EntityPropertyRegistryImpl; import lol.pyr.znpcsplus.entity.EntityPropertyRegistryImpl;
import lol.pyr.znpcsplus.entity.PropertySerializer;
import lol.pyr.znpcsplus.hologram.HologramImpl; import lol.pyr.znpcsplus.hologram.HologramImpl;
import lol.pyr.znpcsplus.hologram.HologramLine; import lol.pyr.znpcsplus.hologram.HologramLine;
import lol.pyr.znpcsplus.interaction.ActionRegistry; import lol.pyr.znpcsplus.interaction.ActionRegistry;
@ -52,7 +54,7 @@ public class YamlStorage implements NpcStorage {
for (File file : files) if (file.isFile() && file.getName().toLowerCase().endsWith(".yml")) { for (File file : files) if (file.isFile() && file.getName().toLowerCase().endsWith(".yml")) {
YamlConfiguration config = YamlConfiguration.loadConfiguration(file); YamlConfiguration config = YamlConfiguration.loadConfiguration(file);
UUID uuid = config.contains("uuid") ? UUID.fromString(config.getString("uuid")) : UUID.randomUUID(); UUID uuid = config.contains("uuid") ? UUID.fromString(config.getString("uuid")) : UUID.randomUUID();
NpcImpl npc = new NpcImpl(uuid, configManager, packetFactory, textSerializer, config.getString("world"), NpcImpl npc = new NpcImpl(uuid, propertyRegistry, configManager, packetFactory, textSerializer, config.getString("world"),
typeRegistry.getByName(config.getString("type")), deserializeLocation(config.getConfigurationSection("location"))); typeRegistry.getByName(config.getString("type")), deserializeLocation(config.getConfigurationSection("location")));
if (config.isBoolean("enabled")) npc.setEnabled(config.getBoolean("enabled")); if (config.isBoolean("enabled")) npc.setEnabled(config.getBoolean("enabled"));
@ -65,7 +67,7 @@ public class YamlStorage implements NpcStorage {
Bukkit.getLogger().log(Level.WARNING, "Unknown property '" + key + "' for npc '" + config.getString("id") + "'. skipping ..."); Bukkit.getLogger().log(Level.WARNING, "Unknown property '" + key + "' for npc '" + config.getString("id") + "'. skipping ...");
continue; continue;
} }
npc.UNSAFE_setProperty(property, property.deserialize(properties.getString(key))); npc.UNSAFE_setProperty(property, propertyRegistry.getSerializer(property.getType()).deserialize(properties.getString(key)));
} }
} }
HologramImpl hologram = npc.getHologram(); HologramImpl hologram = npc.getHologram();
@ -101,8 +103,9 @@ public class YamlStorage implements NpcStorage {
config.set("location", serializeLocation(npc.getLocation())); config.set("location", serializeLocation(npc.getLocation()));
config.set("type", npc.getType().getName()); config.set("type", npc.getType().getName());
for (EntityPropertyImpl<?> property : npc.getAppliedProperties()) { for (EntityProperty<?> property : npc.getAppliedProperties()) {
config.set("properties." + property.getName(), property.serialize(npc)); PropertySerializer<?> serializer = propertyRegistry.getSerializer(((EntityPropertyImpl<?>) property).getType());
config.set("properties." + property.getName(), serializer.UNSAFE_serialize(npc.getProperty(property)));
} }
HologramImpl hologram = npc.getHologram(); HologramImpl hologram = npc.getHologram();