Merge branch 'modular-property-system' into 2.X

# Conflicts:
#	plugin/src/main/java/lol/pyr/znpcsplus/ZNpcsPlus.java
#	plugin/src/main/java/lol/pyr/znpcsplus/conversion/citizens/CitizensImporter.java
#	plugin/src/main/java/lol/pyr/znpcsplus/entity/EntityPropertyRegistryImpl.java
#	plugin/src/main/java/lol/pyr/znpcsplus/storage/yaml/YamlStorage.java
#	plugin/src/main/java/lol/pyr/znpcsplus/util/Viewable.java
This commit is contained in:
Pyrbu 2023-09-13 02:09:15 +02:00
commit f30bb1a6df
109 changed files with 2559 additions and 1866 deletions

@ -3,10 +3,6 @@ plugins {
id "maven-publish"
}
dependencies {
compileOnly "org.spigotmc:spigot-api:1.19.4-R0.1-SNAPSHOT"
}
publishing {
publications {
mavenJava(MavenPublication) {

@ -3,4 +3,5 @@ package lol.pyr.znpcsplus.api.entity;
public interface EntityProperty<T> {
T getDefaultValue();
String getName();
boolean isPlayerModifiable();
}

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

@ -1,8 +1,12 @@
package lol.pyr.znpcsplus.api.skin;
import java.net.URL;
public interface SkinDescriptorFactory {
SkinDescriptor createMirrorDescriptor();
SkinDescriptor createRefreshingDescriptor(String playerName);
SkinDescriptor createStaticDescriptor(String playerName);
SkinDescriptor createStaticDescriptor(String texture, String signature);
SkinDescriptor createUrlDescriptor(String url, String variant);
SkinDescriptor createUrlDescriptor(URL url, String variant);
}

@ -0,0 +1,9 @@
package lol.pyr.znpcsplus.util;
public enum AxolotlVariant {
LUCY,
WILD,
GOLD,
CYAN,
BLUE
}

@ -1,25 +1,15 @@
package lol.pyr.znpcsplus.util;
public enum CatVariant {
TABBY(0),
BLACK(1),
RED(2),
SIAMESE(3),
BRITISH_SHORTHAIR(4),
CALICO(5),
PERSIAN(6),
RAGDOLL(7),
WHITE(8),
JELLIE(9),
ALL_BLACK(10);
private final int id;
CatVariant(int id) {
this.id = id;
}
public int getId() {
return id;
}
TABBY,
BLACK,
RED,
SIAMESE,
BRITISH_SHORTHAIR,
CALICO,
PERSIAN,
RAGDOLL,
WHITE,
JELLIE,
ALL_BLACK
}

@ -0,0 +1,8 @@
package lol.pyr.znpcsplus.util;
public enum HorseArmor {
NONE,
IRON,
GOLD,
DIAMOND
}

@ -0,0 +1,11 @@
package lol.pyr.znpcsplus.util;
public enum HorseColor {
WHITE,
CREAMY,
CHESTNUT,
BROWN,
BLACK,
GRAY,
DARK_BROWN
}

@ -0,0 +1,9 @@
package lol.pyr.znpcsplus.util;
public enum HorseStyle {
NONE,
WHITE,
WHITEFIELD,
WHITE_DOTS,
BLACK_DOTS
}

@ -0,0 +1,9 @@
package lol.pyr.znpcsplus.util;
public enum HorseType {
HORSE,
DONKEY,
MULE,
ZOMBIE,
SKELETON
}

@ -0,0 +1,8 @@
package lol.pyr.znpcsplus.util;
public enum LlamaVariant {
CREAMY,
WHITE,
BROWN,
GRAY
}

@ -0,0 +1,10 @@
package lol.pyr.znpcsplus.util;
public enum MooshroomVariant {
RED,
BROWN;
public static String getVariantName(MooshroomVariant variant) {
return variant.name().toLowerCase();
}
}

@ -0,0 +1,8 @@
package lol.pyr.znpcsplus.util;
public enum OcelotType {
OCELOT,
TUXEDO,
TABBY,
SIAMESE,
}

@ -0,0 +1,11 @@
package lol.pyr.znpcsplus.util;
public enum PandaGene {
NORMAL,
LAZY,
WORRIED,
PLAYFUL,
BROWN,
WEAK,
AGGRESSIVE
}

@ -5,6 +5,5 @@ public enum ParrotVariant {
BLUE,
GREEN,
YELLOW_BLUE,
GRAY,
NONE // only used to set empty nbt compound
GRAY
}

@ -0,0 +1,7 @@
package lol.pyr.znpcsplus.util;
public enum PuffState {
DEFLATED,
HALF_INFLATED,
FULLY_INFLATED,
}

@ -2,28 +2,28 @@ package lol.pyr.znpcsplus.util;
public enum VillagerProfession {
NONE(0),
ARMORER(3),
BUTCHER(4),
CARTOGRAPHER(1),
CLERIC(2),
FARMER(0),
FISHERMAN(0),
FLETCHER(0),
LEATHER_WORKER(4),
LIBRARIAN(1),
MASON(-1),
NITWIT(5),
SHEPHERD(0),
TOOL_SMITH(3),
WEAPON_SMITH(3);
ARMORER(1),
BUTCHER(2),
CARTOGRAPHER(3),
CLERIC(4),
FARMER(5),
FISHERMAN(6),
FLETCHER(7),
LEATHER_WORKER(8),
LIBRARIAN(9),
MASON(10),
NITWIT(11),
SHEPHERD(12),
TOOL_SMITH(13),
WEAPON_SMITH(14);
private final int legacyId;
private final int id;
VillagerProfession(int legacyId) {
this.legacyId = legacyId;
VillagerProfession(int id) {
this.id = id;
}
public int getLegacyId() {
return legacyId;
public int getId() {
return id;
}
}

@ -1,11 +1,20 @@
package lol.pyr.znpcsplus.util;
public enum VillagerType {
DESERT,
JUNGLE,
PLAINS,
SAVANNA,
SNOW,
SWAMP,
TAIGA
DESERT(0),
JUNGLE(1),
PLAINS(2),
SAVANNA(3),
SNOW(4),
SWAMP(5),
TAIGA(6);
private final int id;
VillagerType(int id) {
this.id = id;
}
public int getId() {
return id;
}
}

@ -8,6 +8,10 @@ subprojects {
toolchain.languageVersion.set(JavaLanguageVersion.of(8))
}
dependencies {
compileOnly "org.spigotmc:spigot-api:1.8.8-R0.1-SNAPSHOT"
}
repositories {
mavenCentral()
maven {

@ -16,8 +16,6 @@ processResources {
}
dependencies {
compileOnly "org.spigotmc:spigot-api:1.8.8-R0.1-SNAPSHOT"
compileOnly "me.clip:placeholderapi:2.11.3" // Placeholder support
implementation "com.google.code.gson:gson:2.10.1" // JSON parsing
implementation "org.bstats:bstats-bukkit:3.0.2" // Plugin stats

@ -28,9 +28,11 @@ import lol.pyr.znpcsplus.entity.EntityPropertyImpl;
import lol.pyr.znpcsplus.entity.EntityPropertyRegistryImpl;
import lol.pyr.znpcsplus.interaction.ActionRegistry;
import lol.pyr.znpcsplus.interaction.InteractionPacketListener;
import lol.pyr.znpcsplus.metadata.*;
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.scheduling.FoliaScheduler;
import lol.pyr.znpcsplus.scheduling.SpigotScheduler;
@ -126,8 +128,8 @@ public class ZNpcsPlus extends JavaPlugin {
ConfigManager configManager = new ConfigManager(getDataFolder());
MojangSkinCache skinCache = new MojangSkinCache(configManager);
EntityPropertyRegistryImpl propertyRegistry = new EntityPropertyRegistryImpl(skinCache);
MetadataFactory metadataFactory = setupMetadataFactory();
PacketFactory packetFactory = setupPacketFactory(scheduler, metadataFactory, propertyRegistry);
PacketFactory packetFactory = setupPacketFactory(scheduler, propertyRegistry);
propertyRegistry.registerTypes(packetFactory);
ActionRegistry actionRegistry = new ActionRegistry();
NpcTypeRegistryImpl typeRegistry = new NpcTypeRegistryImpl();
@ -140,7 +142,7 @@ public class ZNpcsPlus extends JavaPlugin {
DataImporterRegistry importerRegistry = new DataImporterRegistry(configManager, adventure,
scheduler, packetFactory, textSerializer, typeRegistry, getDataFolder().getParentFile(),
propertyRegistry, skinCache);
propertyRegistry, skinCache, npcRegistry);
log(ChatColor.WHITE + " * Registerring components...");
@ -197,10 +199,9 @@ public class ZNpcsPlus extends JavaPlugin {
NpcEntryImpl entry = npcRegistry.create("debug_npc_" + i, world, type, new NpcLocation(i * 3, 200, 0, 0, 0));
entry.setProcessed(true);
NpcImpl npc = entry.getNpc();
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, 0, 255)));
npc.setProperty(propertyRegistry.getByName("look", Boolean.class), true);
npc.getHologram().addTextLineComponent(Component.text("Hello, World!", TextColor.color(255, 0, 0)));
npc.getHologram().addTextLineComponent(Component.text("Hello, World!", TextColor.color(0, 255, 0)));
npc.getHologram().addTextLineComponent(Component.text("Hello, World!", TextColor.color(0, 0, 255)));
i++;
}
}
@ -210,18 +211,15 @@ public class ZNpcsPlus extends JavaPlugin {
public void onDisable() {
NpcApiProvider.unregister();
for (Runnable runnable : shutdownTasks) runnable.run();
shutdownTasks.clear();
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<>();
versions.put(ServerVersion.V_1_8, LazyLoader.of(() -> new V1_8PacketFactory(scheduler, metadataFactory, packetEvents, propertyRegistry, textSerializer)));
versions.put(ServerVersion.V_1_9, LazyLoader.of(() -> new V1_9PacketFactory(scheduler, metadataFactory, packetEvents, propertyRegistry, textSerializer)));
versions.put(ServerVersion.V_1_10, LazyLoader.of(() -> new V1_10PacketFactory(scheduler, metadataFactory, 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)));
versions.put(ServerVersion.V_1_8, LazyLoader.of(() -> new V1_8PacketFactory(scheduler, packetEvents, propertyRegistry, textSerializer)));
versions.put(ServerVersion.V_1_17, LazyLoader.of(() -> new V1_17PacketFactory(scheduler, packetEvents, propertyRegistry, textSerializer)));
versions.put(ServerVersion.V_1_19, LazyLoader.of(() -> new V1_19PacketFactory(scheduler, packetEvents, propertyRegistry, textSerializer)));
ServerVersion version = packetEvents.getServerManager().getVersion();
if (versions.containsKey(version)) return versions.get(version).get();
@ -233,31 +231,6 @@ public class ZNpcsPlus extends JavaPlugin {
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,
ActionRegistry actionRegistry, NpcTypeRegistryImpl typeRegistry,
EntityPropertyRegistryImpl propertyRegistry, DataImporterRegistry importerRegistry,
@ -277,6 +250,7 @@ public class ZNpcsPlus extends JavaPlugin {
manager.registerParser(Color.class, new ColorParser(incorrectUsageMessage));
manager.registerParser(Vector3f.class, new Vector3fParser(incorrectUsageMessage));
// TODO: Need to find a better way to do this
registerEnumParser(manager, NpcPose.class, incorrectUsageMessage);
registerEnumParser(manager, DyeColor.class, incorrectUsageMessage);
registerEnumParser(manager, CatVariant.class, incorrectUsageMessage);
@ -288,6 +262,16 @@ public class ZNpcsPlus extends JavaPlugin {
registerEnumParser(manager, VillagerType.class, incorrectUsageMessage);
registerEnumParser(manager, VillagerProfession.class, incorrectUsageMessage);
registerEnumParser(manager, VillagerLevel.class, incorrectUsageMessage);
registerEnumParser(manager, AxolotlVariant.class, incorrectUsageMessage);
registerEnumParser(manager, HorseType.class, incorrectUsageMessage);
registerEnumParser(manager, HorseStyle.class, incorrectUsageMessage);
registerEnumParser(manager, HorseColor.class, incorrectUsageMessage);
registerEnumParser(manager, HorseArmor.class, incorrectUsageMessage);
registerEnumParser(manager, LlamaVariant.class, incorrectUsageMessage);
registerEnumParser(manager, MooshroomVariant.class, incorrectUsageMessage);
registerEnumParser(manager, OcelotType.class, incorrectUsageMessage);
registerEnumParser(manager, PandaGene.class, incorrectUsageMessage);
registerEnumParser(manager, PuffState.class, incorrectUsageMessage);
manager.registerCommand("npc", new MultiCommand(loadHelpMessage("root"))
.addSubcommand("center", new CenterCommand(npcRegistry))
@ -309,11 +293,14 @@ public class ZNpcsPlus extends JavaPlugin {
.addSubcommand("reload", new LoadAllCommand(npcRegistry))
.addSubcommand("import", new ImportCommand(npcRegistry, importerRegistry)))
.addSubcommand("holo", new MultiCommand(loadHelpMessage("holo"))
.addSubcommand("add", new HoloAddCommand(npcRegistry, textSerializer))
.addSubcommand("add", new HoloAddCommand(npcRegistry))
.addSubcommand("additem", new HoloAddItemCommand(npcRegistry))
.addSubcommand("delete", new HoloDeleteCommand(npcRegistry))
.addSubcommand("info", new HoloInfoCommand(npcRegistry))
.addSubcommand("insert", new HoloInsertCommand(npcRegistry, textSerializer))
.addSubcommand("set", new HoloSetCommand(npcRegistry, textSerializer))
.addSubcommand("insert", new HoloInsertCommand(npcRegistry))
.addSubcommand("insertitem", new HoloInsertItemCommand(npcRegistry))
.addSubcommand("set", new HoloSetCommand(npcRegistry))
.addSubcommand("setitem", new HoloSetItemCommand(npcRegistry))
.addSubcommand("offset", new HoloOffsetCommand(npcRegistry))
.addSubcommand("refreshdelay", new HoloRefreshDelayCommand(npcRegistry)))
.addSubcommand("action", new MultiCommand(loadHelpMessage("action"))

@ -17,6 +17,8 @@ import lol.pyr.znpcsplus.skin.descriptor.PrefetchedDescriptor;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Collections;
import java.util.List;
@ -44,9 +46,7 @@ public class SkinCommand implements CommandHandler {
npc.setProperty(propertyRegistry.getByName("skin", SkinDescriptor.class), new MirrorDescriptor(skinCache));
npc.respawn();
context.halt(Component.text("The NPC's skin will now mirror the player that it's being displayed to", NamedTextColor.GREEN));
}
if (type.equalsIgnoreCase("static")) {
} else if (type.equalsIgnoreCase("static")) {
context.ensureArgsNotEmpty();
String name = context.dumpAllArgs();
context.send(Component.text("Fetching skin \"" + name + "\"...", NamedTextColor.GREEN));
@ -57,26 +57,51 @@ public class SkinCommand implements CommandHandler {
}
npc.setProperty(propertyRegistry.getByName("skin", SkinDescriptor.class), skin);
npc.respawn();
context.send(Component.text("The NPC's skin has been set to \"" + name + "\""));
context.send(Component.text("The NPC's skin has been set to \"" + name + "\"", NamedTextColor.GREEN));
});
return;
}
if (type.equalsIgnoreCase("dynamic")) {
} else if (type.equalsIgnoreCase("dynamic")) {
context.ensureArgsNotEmpty();
String name = context.dumpAllArgs();
npc.setProperty(propertyRegistry.getByName("skin", SkinDescriptor.class), new FetchingDescriptor(skinCache, name));
npc.respawn();
context.halt(Component.text("The NPC's skin will now be resolved per-player from \"" + name + "\""));
} else if (type.equalsIgnoreCase("url")) {
context.ensureArgsNotEmpty();
String variant = context.popString().toLowerCase();
if (!variant.equalsIgnoreCase("slim") && !variant.equalsIgnoreCase("classic")) {
context.send(Component.text("Invalid skin variant! Please use one of the following: slim, classic", NamedTextColor.RED));
return;
}
context.send(Component.text("Unknown skin type! Please use one of the following: mirror, static, dynamic"));
String urlString = context.dumpAllArgs();
try {
URL url = new URL(urlString);
context.send(Component.text("Fetching skin from url \"" + urlString + "\"...", NamedTextColor.GREEN));
PrefetchedDescriptor.fromUrl(skinCache, url , variant).thenAccept(skin -> {
if (skin.getSkin() == null) {
context.send(Component.text("Failed to fetch skin, are you sure the url is valid?", NamedTextColor.RED));
return;
}
npc.setProperty(propertyRegistry.getByName("skin", SkinDescriptor.class), skin);
npc.respawn();
context.send(Component.text("The NPC's skin has been set.", NamedTextColor.GREEN));
});
} catch (MalformedURLException e) {
context.send(Component.text("Invalid url!", NamedTextColor.RED));
}
return;
}
context.send(Component.text("Unknown skin type! Please use one of the following: mirror, static, dynamic, url"));
}
@Override
public List<String> suggest(CommandContext context) throws CommandExecutionException {
if (context.argSize() == 1) return context.suggestCollection(npcRegistry.getModifiableIds());
if (context.argSize() == 2) return context.suggestLiteral("mirror", "static", "dynamic");
if (context.argSize() == 2) return context.suggestLiteral("mirror", "static", "dynamic", "url");
if (context.matchSuggestion("*", "static")) return context.suggestPlayers();
if (context.argSize() == 3 && context.matchSuggestion("*", "url")) {
return context.suggestLiteral("slim", "classic");
}
return Collections.emptyList();
}
}

@ -4,22 +4,20 @@ import lol.pyr.director.adventure.command.CommandContext;
import lol.pyr.director.adventure.command.CommandHandler;
import lol.pyr.director.common.command.CommandExecutionException;
import lol.pyr.znpcsplus.hologram.HologramImpl;
import lol.pyr.znpcsplus.hologram.HologramItem;
import lol.pyr.znpcsplus.npc.NpcEntryImpl;
import lol.pyr.znpcsplus.npc.NpcRegistryImpl;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import java.util.Collections;
import java.util.List;
public class HoloAddCommand implements CommandHandler {
private final NpcRegistryImpl registry;
private final LegacyComponentSerializer textSerializer;
public HoloAddCommand(NpcRegistryImpl registry, LegacyComponentSerializer textSerializer) {
public HoloAddCommand(NpcRegistryImpl registry) {
this.registry = registry;
this.textSerializer = textSerializer;
}
@Override
@ -27,7 +25,13 @@ public class HoloAddCommand implements CommandHandler {
context.setUsage(context.getLabel() + " holo add <id> <text>");
HologramImpl hologram = context.parse(NpcEntryImpl.class).getNpc().getHologram();
context.ensureArgsNotEmpty();
hologram.addLineComponent(textSerializer.deserialize(context.dumpAllArgs()));
String in = context.dumpAllArgs();
if (in.toLowerCase().startsWith("item:")) {
if (!HologramItem.ensureValidItemInput(in.substring(5))) {
context.halt(Component.text("The item input is invalid!", NamedTextColor.RED));
}
}
hologram.addLine(in);
context.send(Component.text("NPC line added!", NamedTextColor.GREEN));
}

@ -0,0 +1,39 @@
package lol.pyr.znpcsplus.commands.hologram;
import lol.pyr.director.adventure.command.CommandContext;
import lol.pyr.director.adventure.command.CommandHandler;
import lol.pyr.director.common.command.CommandExecutionException;
import lol.pyr.znpcsplus.hologram.HologramImpl;
import lol.pyr.znpcsplus.npc.NpcEntryImpl;
import lol.pyr.znpcsplus.npc.NpcRegistryImpl;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import org.bukkit.entity.Player;
import java.util.Collections;
import java.util.List;
public class HoloAddItemCommand implements CommandHandler {
private final NpcRegistryImpl registry;
public HoloAddItemCommand(NpcRegistryImpl registry) {
this.registry = registry;
}
@Override
public void run(CommandContext context) throws CommandExecutionException {
context.setUsage(context.getLabel() + " holo additem <id>");
Player player = context.ensureSenderIsPlayer();
org.bukkit.inventory.ItemStack itemStack = player.getInventory().getItemInHand();
if (itemStack == null) context.halt(Component.text("You must be holding an item!", NamedTextColor.RED));
HologramImpl hologram = context.parse(NpcEntryImpl.class).getNpc().getHologram();
hologram.addItemLineStack(itemStack);
context.send(Component.text("NPC item line added!", NamedTextColor.GREEN));
}
@Override
public List<String> suggest(CommandContext context) throws CommandExecutionException {
if (context.argSize() == 1) return context.suggestCollection(registry.getModifiableIds());
return Collections.emptyList();
}
}

@ -4,7 +4,6 @@ import lol.pyr.director.adventure.command.CommandContext;
import lol.pyr.director.adventure.command.CommandHandler;
import lol.pyr.director.common.command.CommandExecutionException;
import lol.pyr.znpcsplus.hologram.HologramImpl;
import lol.pyr.znpcsplus.hologram.HologramLine;
import lol.pyr.znpcsplus.npc.NpcEntryImpl;
import lol.pyr.znpcsplus.npc.NpcRegistryImpl;
import net.kyori.adventure.text.Component;
@ -26,7 +25,11 @@ public class HoloInfoCommand implements CommandHandler {
NpcEntryImpl entry = context.parse(NpcEntryImpl.class);
HologramImpl hologram = entry.getNpc().getHologram();
Component component = Component.text("NPC Hologram Info of ID " + entry.getId() + ":", NamedTextColor.GREEN).appendNewline();
for (HologramLine line : hologram.getLines()) component = component.append(line.getText()).appendNewline();
for (int i = 0; i < hologram.getLines().size(); i++) {
component = component.append(Component.text(i + ") ", NamedTextColor.GREEN))
.append(Component.text(hologram.getLine(i), NamedTextColor.WHITE))
.appendNewline();
}
context.send(component);
}

@ -4,11 +4,11 @@ import lol.pyr.director.adventure.command.CommandContext;
import lol.pyr.director.adventure.command.CommandHandler;
import lol.pyr.director.common.command.CommandExecutionException;
import lol.pyr.znpcsplus.hologram.HologramImpl;
import lol.pyr.znpcsplus.hologram.HologramItem;
import lol.pyr.znpcsplus.npc.NpcEntryImpl;
import lol.pyr.znpcsplus.npc.NpcRegistryImpl;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import java.util.Collections;
import java.util.List;
@ -16,11 +16,9 @@ import java.util.stream.Stream;
public class HoloInsertCommand implements CommandHandler {
private final NpcRegistryImpl npcRegistry;
private final LegacyComponentSerializer componentSerializer;
public HoloInsertCommand(NpcRegistryImpl npcRegistry, LegacyComponentSerializer componentSerializer) {
public HoloInsertCommand(NpcRegistryImpl npcRegistry) {
this.npcRegistry = npcRegistry;
this.componentSerializer = componentSerializer;
}
@Override
@ -30,7 +28,13 @@ public class HoloInsertCommand implements CommandHandler {
int line = context.parse(Integer.class);
if (line < 0 || line >= hologram.getLines().size()) context.halt(Component.text("Invalid line number!", NamedTextColor.RED));
context.ensureArgsNotEmpty();
hologram.insertLineComponent(line, componentSerializer.deserialize(context.dumpAllArgs()));
String in = context.dumpAllArgs();
if (in.toLowerCase().startsWith("item:")) {
if (!HologramItem.ensureValidItemInput(in.substring(5))) {
context.halt(Component.text("The item input is invalid!", NamedTextColor.RED));
}
}
hologram.insertLine(line, in);
context.send(Component.text("NPC line inserted!", NamedTextColor.GREEN));
}

@ -0,0 +1,45 @@
package lol.pyr.znpcsplus.commands.hologram;
import lol.pyr.director.adventure.command.CommandContext;
import lol.pyr.director.adventure.command.CommandHandler;
import lol.pyr.director.common.command.CommandExecutionException;
import lol.pyr.znpcsplus.hologram.HologramImpl;
import lol.pyr.znpcsplus.npc.NpcEntryImpl;
import lol.pyr.znpcsplus.npc.NpcRegistryImpl;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import org.bukkit.entity.Player;
import java.util.Collections;
import java.util.List;
import java.util.stream.Stream;
public class HoloInsertItemCommand implements CommandHandler {
private final NpcRegistryImpl npcRegistry;
public HoloInsertItemCommand(NpcRegistryImpl npcRegistry) {
this.npcRegistry = npcRegistry;
}
@Override
public void run(CommandContext context) throws CommandExecutionException {
context.setUsage(context.getLabel() + " holo insertitem <id> <line>");
HologramImpl hologram = context.parse(NpcEntryImpl.class).getNpc().getHologram();
int line = context.parse(Integer.class);
if (line < 0 || line >= hologram.getLines().size()) context.halt(Component.text("Invalid line number!", NamedTextColor.RED));
Player player = context.ensureSenderIsPlayer();
org.bukkit.inventory.ItemStack itemStack = player.getInventory().getItemInHand();
if (itemStack == null) context.halt(Component.text("You must be holding an item!", NamedTextColor.RED));
hologram.insertItemLineStack(line, itemStack);
context.send(Component.text("NPC item line inserted!", NamedTextColor.GREEN));
}
@Override
public List<String> suggest(CommandContext context) throws CommandExecutionException {
if (context.argSize() == 1) return context.suggestCollection(npcRegistry.getModifiableIds());
if (context.argSize() == 2) return context.suggestStream(Stream.iterate(0, n -> n + 1)
.limit(context.suggestionParse(0, NpcEntryImpl.class).getNpc().getHologram().getLines().size())
.map(String::valueOf));
return Collections.emptyList();
}
}

@ -8,7 +8,6 @@ import lol.pyr.znpcsplus.npc.NpcEntryImpl;
import lol.pyr.znpcsplus.npc.NpcRegistryImpl;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import java.util.Collections;
import java.util.List;
@ -16,11 +15,9 @@ import java.util.stream.Stream;
public class HoloSetCommand implements CommandHandler {
private final NpcRegistryImpl npcRegistry;
private final LegacyComponentSerializer componentSerializer;
public HoloSetCommand(NpcRegistryImpl npcRegistry, LegacyComponentSerializer componentSerializer) {
public HoloSetCommand(NpcRegistryImpl npcRegistry) {
this.npcRegistry = npcRegistry;
this.componentSerializer = componentSerializer;
}
@Override
@ -31,7 +28,7 @@ public class HoloSetCommand implements CommandHandler {
if (line < 0 || line >= hologram.getLines().size()) context.halt(Component.text("Invalid line number!", NamedTextColor.RED));
context.ensureArgsNotEmpty();
hologram.removeLine(line);
hologram.insertLineComponent(line, componentSerializer.deserialize(context.dumpAllArgs()));
hologram.insertLine(line, context.dumpAllArgs());
context.send(Component.text("NPC line set!", NamedTextColor.GREEN));
}
@ -42,8 +39,7 @@ public class HoloSetCommand implements CommandHandler {
HologramImpl hologram = context.suggestionParse(0, NpcEntryImpl.class).getNpc().getHologram();
if (context.argSize() == 2) return context.suggestStream(Stream.iterate(0, n -> n + 1)
.limit(hologram.getLines().size()).map(String::valueOf));
if (context.argSize() == 3) return context.suggestLiteral(componentSerializer.serialize(
hologram.getLineComponent(context.suggestionParse(1, Integer.class))));
if (context.argSize() == 3) return context.suggestLiteral(hologram.getLine(context.suggestionParse(1, Integer.class)));
}
return Collections.emptyList();
}

@ -0,0 +1,48 @@
package lol.pyr.znpcsplus.commands.hologram;
import lol.pyr.director.adventure.command.CommandContext;
import lol.pyr.director.adventure.command.CommandHandler;
import lol.pyr.director.common.command.CommandExecutionException;
import lol.pyr.znpcsplus.hologram.HologramImpl;
import lol.pyr.znpcsplus.npc.NpcEntryImpl;
import lol.pyr.znpcsplus.npc.NpcRegistryImpl;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import org.bukkit.entity.Player;
import java.util.Collections;
import java.util.List;
import java.util.stream.Stream;
public class HoloSetItemCommand implements CommandHandler {
private final NpcRegistryImpl npcRegistry;
public HoloSetItemCommand(NpcRegistryImpl npcRegistry) {
this.npcRegistry = npcRegistry;
}
@Override
public void run(CommandContext context) throws CommandExecutionException {
context.setUsage(context.getLabel() + " holo setitem <id> <line>");
HologramImpl hologram = context.parse(NpcEntryImpl.class).getNpc().getHologram();
int line = context.parse(Integer.class);
if (line < 0 || line >= hologram.getLines().size()) context.halt(Component.text("Invalid line number!", NamedTextColor.RED));
Player player = context.ensureSenderIsPlayer();
org.bukkit.inventory.ItemStack itemStack = player.getInventory().getItemInHand();
if (itemStack == null) context.halt(Component.text("You must be holding an item!", NamedTextColor.RED));
hologram.removeLine(line);
hologram.insertItemLineStack(line, itemStack);
context.send(Component.text("NPC item line set!", NamedTextColor.GREEN));
}
@Override
public List<String> suggest(CommandContext context) throws CommandExecutionException {
if (context.argSize() == 1) return context.suggestCollection(npcRegistry.getModifiableIds());
if (context.argSize() >= 2) {
HologramImpl hologram = context.suggestionParse(0, NpcEntryImpl.class).getNpc().getHologram();
if (context.argSize() == 2) return context.suggestStream(Stream.iterate(0, n -> n + 1)
.limit(hologram.getLines().size()).map(String::valueOf));
}
return Collections.emptyList();
}
}

@ -28,7 +28,8 @@ public class PropertyRemoveCommand implements CommandHandler {
NpcImpl npc = entry.getNpc();
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));
npc.removeProperty(property);
if (!property.isPlayerModifiable()) context.halt(Component.text("This property is not modifiable by players", NamedTextColor.RED));
npc.setProperty(property, null);
context.send(Component.text("Removed property " + property.getName() + " from NPC " + entry.getId(), NamedTextColor.GREEN));
}
@ -36,7 +37,7 @@ public class PropertyRemoveCommand implements CommandHandler {
public List<String> suggest(CommandContext context) throws CommandExecutionException {
if (context.argSize() == 1) return context.suggestCollection(npcRegistry.getModifiableIds());
if (context.argSize() == 2) return context.suggestStream(context.suggestionParse(0, NpcEntryImpl.class)
.getNpc().getAppliedProperties().stream().map(EntityProperty::getName));
.getNpc().getAppliedProperties().stream().filter(EntityProperty::isPlayerModifiable).map(EntityProperty::getName));
return Collections.emptyList();
}
}

@ -1,8 +1,11 @@
package lol.pyr.znpcsplus.commands.property;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.protocol.world.states.WrappedBlockState;
import com.github.retrooper.packetevents.protocol.world.states.type.StateType;
import com.github.retrooper.packetevents.protocol.world.states.type.StateTypes;
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;
@ -15,9 +18,9 @@ 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.inventory.ItemStack;
import com.github.retrooper.packetevents.protocol.item.ItemStack;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@ -42,12 +45,12 @@ public class PropertySetCommand implements CommandHandler {
Object value;
String valueName;
if (type == ItemStack.class) {
ItemStack bukkitStack = context.ensureSenderIsPlayer().getInventory().getItemInHand();
org.bukkit.inventory.ItemStack bukkitStack = context.ensureSenderIsPlayer().getInventory().getItemInHand();
if (bukkitStack.getAmount() == 0) {
value = null;
valueName = "EMPTY";
} else {
value = bukkitStack;
value = SpigotConversionUtil.fromBukkitItemStack(bukkitStack);
valueName = bukkitStack.toString();
}
}
@ -60,7 +63,7 @@ public class PropertySetCommand implements CommandHandler {
valueName = "NONE";
}
else if (type == ParrotVariant.class && context.argSize() < 1 && npc.getProperty(property) != null) {
value = ParrotVariant.NONE;
value = null;
valueName = "NONE";
}
else if (type == BlockState.class) {
@ -96,6 +99,24 @@ public class PropertySetCommand implements CommandHandler {
return;
}
}
else if (type == SpellType.class) {
if (PacketEvents.getAPI().getServerManager().getVersion().isOlderThan(ServerVersion.V_1_13)) {
value = context.parse(type);
valueName = String.valueOf(value);
if (((SpellType) value).ordinal() > 3) {
context.send(Component.text("Spell type " + valueName + " is not supported on this version", NamedTextColor.RED));
return;
}
}
else {
value = context.parse(type);
valueName = String.valueOf(value);
}
}
else if (type == NpcEntryImpl.class) {
value = context.parse(type);
valueName = value == null ? "NONE" : ((NpcEntryImpl) value).getId();
}
else {
value = context.parse(type);
valueName = String.valueOf(value);
@ -117,19 +138,16 @@ public class PropertySetCommand implements CommandHandler {
if (context.argSize() == 3) {
if (type == Boolean.class) return context.suggestLiteral("true", "false");
if (type == NamedTextColor.class) return context.suggestCollection(NamedTextColor.NAMES.keys());
if (type == NpcPose.class) return context.suggestEnum(NpcPose.values());
if (type == Color.class) return context.suggestLiteral("0x0F00FF", "#FFFFFF");
if (type == DyeColor.class) return context.suggestEnum(DyeColor.values());
if (type == CatVariant.class) return context.suggestEnum(CatVariant.values());
if (type == CreeperState.class) return context.suggestEnum(CreeperState.values());
if (type == ParrotVariant.class) return context.suggestEnum(ParrotVariant.values());
if (type == BlockState.class) return context.suggestLiteral("hand", "looking_at", "block");
if (type == SpellType.class) return context.suggestEnum(SpellType.values());
if (type == FoxVariant.class) return context.suggestEnum(FoxVariant.values());
if (type == FrogVariant.class) return context.suggestEnum(FrogVariant.values());
if (type == VillagerType.class) return context.suggestEnum(VillagerType.values());
if (type == VillagerProfession.class) return context.suggestEnum(VillagerProfession.values());
if (type == VillagerLevel.class) return context.suggestEnum(VillagerLevel.values());
if (type == SpellType.class) return PacketEvents.getAPI().getServerManager().getVersion().isOlderThan(ServerVersion.V_1_13) ?
context.suggestEnum(Arrays.stream(SpellType.values()).filter(spellType -> spellType.ordinal() <= 3).toArray(SpellType[]::new)) :
context.suggestEnum(SpellType.values());
// Suggest enum values directly
if (type.isEnum()) {
return context.suggestEnum((Enum<?>[]) type.getEnumConstants());
}
}
else if (context.argSize() == 4) {
if (type == BlockState.class) {

@ -1,8 +1,10 @@
package lol.pyr.znpcsplus.conversion;
import lol.pyr.znpcsplus.config.ConfigManager;
import lol.pyr.znpcsplus.conversion.citizens.CitizensImporter;
import lol.pyr.znpcsplus.conversion.znpcs.ZNpcImporter;
import lol.pyr.znpcsplus.entity.EntityPropertyRegistryImpl;
import lol.pyr.znpcsplus.npc.NpcRegistryImpl;
import lol.pyr.znpcsplus.npc.NpcTypeRegistryImpl;
import lol.pyr.znpcsplus.packets.PacketFactory;
import lol.pyr.znpcsplus.scheduling.TaskScheduler;
@ -23,14 +25,14 @@ public class DataImporterRegistry {
public DataImporterRegistry(ConfigManager configManager, BukkitAudiences adventure,
TaskScheduler taskScheduler, PacketFactory packetFactory, LegacyComponentSerializer textSerializer,
NpcTypeRegistryImpl typeRegistry, File pluginsFolder, EntityPropertyRegistryImpl propertyRegistry,
MojangSkinCache skinCache) {
MojangSkinCache skinCache, NpcRegistryImpl npcRegistry) {
register("znpcs", LazyLoader.of(() -> new ZNpcImporter(configManager, adventure, taskScheduler,
packetFactory, textSerializer, typeRegistry, propertyRegistry, skinCache, new File(pluginsFolder, "ServersNPC/data.json"))));
register("znpcsplus_legacy", LazyLoader.of(() -> new ZNpcImporter(configManager, adventure, taskScheduler,
packetFactory, textSerializer, typeRegistry, propertyRegistry, skinCache, new File(pluginsFolder, "ZNPCsPlusLegacy/data.json"))));
/* register("citizens", LazyLoader.of(() -> new CitizensImporter(configManager, adventure, bungeeConnector, taskScheduler,
packetFactory, textSerializer, typeRegistry, propertyRegistry, skinCache, new File(pluginsFolder, "Citizens/saves.yml")))); */
register("citizens", LazyLoader.of(() -> new CitizensImporter(configManager, adventure, taskScheduler,
packetFactory, textSerializer, typeRegistry, propertyRegistry, skinCache, new File(pluginsFolder, "Citizens/saves.yml"), npcRegistry)));
}
private void register(String id, LazyLoader<DataImporter> loader) {

@ -2,19 +2,28 @@ package lol.pyr.znpcsplus.conversion.citizens;
import lol.pyr.znpcsplus.config.ConfigManager;
import lol.pyr.znpcsplus.conversion.DataImporter;
import lol.pyr.znpcsplus.conversion.citizens.model.CitizensTrait;
import lol.pyr.znpcsplus.conversion.citizens.model.CitizensTraitsRegistry;
import lol.pyr.znpcsplus.entity.EntityPropertyRegistryImpl;
import lol.pyr.znpcsplus.npc.NpcEntryImpl;
import lol.pyr.znpcsplus.npc.NpcImpl;
import lol.pyr.znpcsplus.npc.NpcRegistryImpl;
import lol.pyr.znpcsplus.npc.NpcTypeRegistryImpl;
import lol.pyr.znpcsplus.packets.PacketFactory;
import lol.pyr.znpcsplus.scheduling.TaskScheduler;
import lol.pyr.znpcsplus.skin.cache.MojangSkinCache;
import lol.pyr.znpcsplus.util.NpcLocation;
import net.kyori.adventure.platform.bukkit.BukkitAudiences;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import org.bukkit.Bukkit;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.YamlConfiguration;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.UUID;
@SuppressWarnings("FieldCanBeLocal")
public class CitizensImporter implements DataImporter {
@ -27,11 +36,13 @@ public class CitizensImporter implements DataImporter {
private final EntityPropertyRegistryImpl propertyRegistry;
private final MojangSkinCache skinCache;
private final File dataFile;
private final CitizensTraitsRegistry traitsRegistry;
private final NpcRegistryImpl npcRegistry;
public CitizensImporter(ConfigManager configManager, BukkitAudiences adventure,
TaskScheduler taskScheduler, PacketFactory packetFactory, LegacyComponentSerializer textSerializer,
NpcTypeRegistryImpl typeRegistry, EntityPropertyRegistryImpl propertyRegistry, MojangSkinCache skinCache,
File dataFile) {
File dataFile, NpcRegistryImpl npcRegistry) {
this.configManager = configManager;
this.adventure = adventure;
this.scheduler = taskScheduler;
@ -41,14 +52,58 @@ public class CitizensImporter implements DataImporter {
this.propertyRegistry = propertyRegistry;
this.skinCache = skinCache;
this.dataFile = dataFile;
this.traitsRegistry = new CitizensTraitsRegistry(typeRegistry, propertyRegistry, skinCache);
this.npcRegistry = npcRegistry;
}
@Override
public Collection<NpcEntryImpl> importData() {
YamlConfiguration config = YamlConfiguration.loadConfiguration(dataFile);
// TODO
ConfigurationSection npcsSection = config.getConfigurationSection("npc");
if (npcsSection == null) {
return Collections.emptyList();
}
ArrayList<NpcEntryImpl> entries = new ArrayList<>();
npcsSection.getKeys(false).forEach(key -> {
ConfigurationSection npcSection = npcsSection.getConfigurationSection(key);
if (npcSection == null) {
return;
}
String name = npcSection.getString("name", "Citizens NPC");
UUID uuid;
try {
uuid = UUID.fromString(npcSection.getString("uuid"));
} catch (IllegalArgumentException e) {
uuid = UUID.randomUUID();
}
String world = npcSection.getString("traits.location.world");
if (world == null) {
world = Bukkit.getWorlds().get(0).getName();
}
NpcImpl npc = new NpcImpl(uuid, propertyRegistry, configManager, packetFactory, textSerializer, world, typeRegistry.getByName("armor_stand"), new NpcLocation(0, 0, 0, 0, 0));
npc.getType().applyDefaultProperties(npc);
npc.getHologram().addTextLineComponent(textSerializer.deserialize(name));
ConfigurationSection traits = npcSection.getConfigurationSection("traits");
if (traits != null) {
for (String traitName : traits.getKeys(false)) {
Object trait = traits.get(traitName);
CitizensTrait citizensTrait = traitsRegistry.getByName(traitName);
if (citizensTrait != null) {
npc = citizensTrait.apply(npc, trait);
}
}
}
String id = key.toLowerCase();
while (npcRegistry.getById(id) != null) {
id += "_"; // TODO: make a backup of the old npc instead
}
NpcEntryImpl entry = new NpcEntryImpl(id, npc);
entry.enableEverything();
entries.add(entry);
});
return entries;
}
@Override
public boolean isValid() {

@ -0,0 +1,19 @@
package lol.pyr.znpcsplus.conversion.citizens.model;
import lol.pyr.znpcsplus.npc.NpcImpl;
import org.jetbrains.annotations.NotNull;
public abstract class CitizensTrait {
private final String identifier;
public CitizensTrait(String identifier) {
this.identifier = identifier;
}
public String getIdentifier() {
return identifier;
}
public abstract @NotNull NpcImpl apply(NpcImpl npc, Object value);
}

@ -0,0 +1,31 @@
package lol.pyr.znpcsplus.conversion.citizens.model;
import lol.pyr.znpcsplus.api.entity.EntityPropertyRegistry;
import lol.pyr.znpcsplus.api.npc.NpcTypeRegistry;
import lol.pyr.znpcsplus.conversion.citizens.model.traits.*;
import lol.pyr.znpcsplus.skin.cache.MojangSkinCache;
import java.util.HashMap;
public class CitizensTraitsRegistry {
private final HashMap<String, CitizensTrait> traitMap = new HashMap<>();
public CitizensTraitsRegistry(NpcTypeRegistry typeRegistry, EntityPropertyRegistry propertyRegistry, MojangSkinCache skinCache) {
register(new LocationTrait());
register(new TypeTrait(typeRegistry));
register(new ProfessionTrait(propertyRegistry));
register(new VillagerTrait(propertyRegistry));
register(new SkinTrait(propertyRegistry));
register(new MirrorTrait(propertyRegistry, skinCache));
register(new SkinLayersTrait(propertyRegistry));
register(new LookTrait(propertyRegistry));
}
public CitizensTrait getByName(String name) {
return traitMap.get(name);
}
public void register(CitizensTrait trait) {
traitMap.put(trait.getIdentifier(), trait);
}
}

@ -0,0 +1,19 @@
package lol.pyr.znpcsplus.conversion.citizens.model;
import lol.pyr.znpcsplus.npc.NpcImpl;
import org.bukkit.configuration.ConfigurationSection;
import org.jetbrains.annotations.NotNull;
public abstract class SectionCitizensTrait extends CitizensTrait {
public SectionCitizensTrait(String identifier) {
super(identifier);
}
@Override
public @NotNull NpcImpl apply(NpcImpl npc, Object value) {
if (!(value instanceof ConfigurationSection)) return npc;
return apply(npc, (ConfigurationSection) value);
}
public abstract @NotNull NpcImpl apply(NpcImpl npc, ConfigurationSection section);
}

@ -0,0 +1,18 @@
package lol.pyr.znpcsplus.conversion.citizens.model;
import lol.pyr.znpcsplus.npc.NpcImpl;
import org.jetbrains.annotations.NotNull;
public abstract class StringCitizensTrait extends CitizensTrait {
public StringCitizensTrait(String identifier) {
super(identifier);
}
@Override
public @NotNull NpcImpl apply(NpcImpl npc, Object value) {
if (!(value instanceof String)) return npc;
return apply(npc, (String) value);
}
public abstract @NotNull NpcImpl apply(NpcImpl npc, String string);
}

@ -0,0 +1,25 @@
package lol.pyr.znpcsplus.conversion.citizens.model.traits;
import lol.pyr.znpcsplus.conversion.citizens.model.SectionCitizensTrait;
import lol.pyr.znpcsplus.npc.NpcImpl;
import lol.pyr.znpcsplus.util.NpcLocation;
import org.bukkit.configuration.ConfigurationSection;
import org.jetbrains.annotations.NotNull;
public class LocationTrait extends SectionCitizensTrait {
public LocationTrait() {
super("location");
}
@Override
public @NotNull NpcImpl apply(NpcImpl npc, ConfigurationSection section) {
double x = Double.parseDouble(section.getString("x"));
double y = Double.parseDouble(section.getString("y"));
double z = Double.parseDouble(section.getString("z"));
float yaw = Float.parseFloat(section.getString("yaw"));
float pitch = Float.parseFloat(section.getString("pitch"));
NpcLocation location = new NpcLocation(x, y, z, yaw, pitch);
npc.setLocation(location);
return npc;
}
}

@ -0,0 +1,22 @@
package lol.pyr.znpcsplus.conversion.citizens.model.traits;
import lol.pyr.znpcsplus.api.entity.EntityPropertyRegistry;
import lol.pyr.znpcsplus.conversion.citizens.model.SectionCitizensTrait;
import lol.pyr.znpcsplus.npc.NpcImpl;
import org.bukkit.configuration.ConfigurationSection;
import org.jetbrains.annotations.NotNull;
public class LookTrait extends SectionCitizensTrait {
private final EntityPropertyRegistry registry;
public LookTrait(EntityPropertyRegistry registry) {
super("lookclose");
this.registry = registry;
}
@Override
public @NotNull NpcImpl apply(NpcImpl npc, ConfigurationSection section) {
if (section.getBoolean("enabled")) npc.setProperty(registry.getByName("look", Boolean.class), true);
return npc;
}
}

@ -0,0 +1,27 @@
package lol.pyr.znpcsplus.conversion.citizens.model.traits;
import lol.pyr.znpcsplus.api.entity.EntityPropertyRegistry;
import lol.pyr.znpcsplus.api.skin.SkinDescriptor;
import lol.pyr.znpcsplus.conversion.citizens.model.SectionCitizensTrait;
import lol.pyr.znpcsplus.npc.NpcImpl;
import lol.pyr.znpcsplus.skin.cache.MojangSkinCache;
import lol.pyr.znpcsplus.skin.descriptor.MirrorDescriptor;
import org.bukkit.configuration.ConfigurationSection;
import org.jetbrains.annotations.NotNull;
public class MirrorTrait extends SectionCitizensTrait {
private final EntityPropertyRegistry registry;
private final MojangSkinCache skinCache;
public MirrorTrait(EntityPropertyRegistry registry, MojangSkinCache skinCache) {
super("mirrortrait");
this.registry = registry;
this.skinCache = skinCache;
}
@Override
public @NotNull NpcImpl apply(NpcImpl npc, ConfigurationSection section) {
if (section.getBoolean("enabled")) npc.setProperty(registry.getByName("skin", SkinDescriptor.class), new MirrorDescriptor(skinCache));
return npc;
}
}

@ -0,0 +1,28 @@
package lol.pyr.znpcsplus.conversion.citizens.model.traits;
import lol.pyr.znpcsplus.api.entity.EntityPropertyRegistry;
import lol.pyr.znpcsplus.conversion.citizens.model.StringCitizensTrait;
import lol.pyr.znpcsplus.npc.NpcImpl;
import lol.pyr.znpcsplus.util.VillagerProfession;
import org.jetbrains.annotations.NotNull;
public class ProfessionTrait extends StringCitizensTrait {
private final EntityPropertyRegistry registry;
public ProfessionTrait(EntityPropertyRegistry registry) {
super("profession");
this.registry = registry;
}
@Override
public @NotNull NpcImpl apply(NpcImpl npc, String string) {
VillagerProfession profession;
try {
profession = VillagerProfession.valueOf(string.toUpperCase());
} catch (IllegalArgumentException ignored) {
profession = VillagerProfession.NONE;
}
npc.setProperty(registry.getByName("villager_profession", VillagerProfession.class), profession);
return npc;
}
}

@ -0,0 +1,38 @@
package lol.pyr.znpcsplus.conversion.citizens.model.traits;
import lol.pyr.znpcsplus.api.entity.EntityPropertyRegistry;
import lol.pyr.znpcsplus.conversion.citizens.model.SectionCitizensTrait;
import lol.pyr.znpcsplus.npc.NpcImpl;
import org.bukkit.configuration.ConfigurationSection;
import org.jetbrains.annotations.NotNull;
import java.util.HashMap;
import java.util.Map;
public class SkinLayersTrait extends SectionCitizensTrait {
private final EntityPropertyRegistry registry;
private final Map<String, String> skinLayers;
public SkinLayersTrait(EntityPropertyRegistry registry) {
super("skinlayers");
this.registry = registry;
this.skinLayers = new HashMap<>();
this.skinLayers.put("cape", "skin_cape");
this.skinLayers.put("hat", "skin_hat");
this.skinLayers.put("jacket", "skin_jacket");
this.skinLayers.put("left_sleeve", "skin_left_sleeve");
this.skinLayers.put("left_pants", "skin_left_leg");
this.skinLayers.put("right_sleeve", "skin_right_sleeve");
this.skinLayers.put("right_pants", "skin_right_leg");
}
@Override
public @NotNull NpcImpl apply(NpcImpl npc, ConfigurationSection section) {
for (Map.Entry<String, String> entry : this.skinLayers.entrySet()) {
String key = entry.getKey();
String property = entry.getValue();
if (section.contains(key)) npc.setProperty(registry.getByName(property, Boolean.class), section.getBoolean(key));
}
return npc;
}
}

@ -0,0 +1,27 @@
package lol.pyr.znpcsplus.conversion.citizens.model.traits;
import lol.pyr.znpcsplus.api.entity.EntityPropertyRegistry;
import lol.pyr.znpcsplus.api.skin.SkinDescriptor;
import lol.pyr.znpcsplus.conversion.citizens.model.SectionCitizensTrait;
import lol.pyr.znpcsplus.npc.NpcImpl;
import lol.pyr.znpcsplus.skin.Skin;
import lol.pyr.znpcsplus.skin.descriptor.PrefetchedDescriptor;
import org.bukkit.configuration.ConfigurationSection;
import org.jetbrains.annotations.NotNull;
public class SkinTrait extends SectionCitizensTrait {
private final EntityPropertyRegistry registry;
public SkinTrait(EntityPropertyRegistry registry) {
super("skintrait");
this.registry = registry;
}
@Override
public @NotNull NpcImpl apply(NpcImpl npc, ConfigurationSection section) {
String texture = section.getString("textureRaw");
String signature = section.getString("signature");
if (texture != null && signature != null) npc.setProperty(registry.getByName("skin", SkinDescriptor.class), new PrefetchedDescriptor(new Skin(texture, signature)));
return npc;
}
}

@ -0,0 +1,31 @@
package lol.pyr.znpcsplus.conversion.citizens.model.traits;
import lol.pyr.znpcsplus.api.npc.NpcTypeRegistry;
import lol.pyr.znpcsplus.conversion.citizens.model.StringCitizensTrait;
import lol.pyr.znpcsplus.npc.NpcImpl;
import lol.pyr.znpcsplus.npc.NpcTypeImpl;
import org.jetbrains.annotations.NotNull;
public class TypeTrait extends StringCitizensTrait {
private final NpcTypeRegistry registry;
public TypeTrait(NpcTypeRegistry registry) {
super("type");
this.registry = registry;
}
@Override
public @NotNull NpcImpl apply(NpcImpl npc, String string) {
NpcTypeImpl type = warpNpcType(string);
if (type == null) return npc;
npc.setType(type);
return npc;
}
private NpcTypeImpl warpNpcType(String name) {
name = name.toLowerCase();
// if (name.equals("player")) name = "human";
// else if (name.equals("zombievillager")) name = "zombie_villager";
return (NpcTypeImpl) registry.getByName(name);
}
}

@ -0,0 +1,39 @@
package lol.pyr.znpcsplus.conversion.citizens.model.traits;
import lol.pyr.znpcsplus.api.entity.EntityPropertyRegistry;
import lol.pyr.znpcsplus.conversion.citizens.model.SectionCitizensTrait;
import lol.pyr.znpcsplus.npc.NpcImpl;
import lol.pyr.znpcsplus.util.VillagerLevel;
import lol.pyr.znpcsplus.util.VillagerType;
import org.bukkit.configuration.ConfigurationSection;
import org.jetbrains.annotations.NotNull;
public class VillagerTrait extends SectionCitizensTrait {
private final EntityPropertyRegistry registry;
public VillagerTrait(EntityPropertyRegistry registry) {
super("villagertrait");
this.registry = registry;
}
@Override
public @NotNull NpcImpl apply(NpcImpl npc, ConfigurationSection section) {
int level = section.getInt("level");
String type = section.getString("type", "plains");
VillagerLevel villagerLevel;
try {
villagerLevel = VillagerLevel.values()[level];
} catch (ArrayIndexOutOfBoundsException ignored) {
villagerLevel = VillagerLevel.STONE;
}
VillagerType villagerType;
try {
villagerType = VillagerType.valueOf(type.toUpperCase());
} catch (IllegalArgumentException ignored) {
villagerType = VillagerType.PLAINS;
}
npc.setProperty(registry.getByName("villager_level", VillagerLevel.class), villagerLevel);
npc.setProperty(registry.getByName("villager_type", VillagerType.class), villagerType);
return npc;
}
}

@ -98,13 +98,14 @@ public class ZNpcImporter implements DataImporter {
ZNpcsLocation oldLoc = model.getLocation();
NpcLocation location = new NpcLocation(oldLoc.getX(), oldLoc.getY(), oldLoc.getZ(), oldLoc.getYaw(), oldLoc.getPitch());
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);
npc.getType().applyDefaultProperties(npc);
HologramImpl hologram = npc.getHologram();
hologram.setOffset(model.getHologramHeight());
for (String raw : model.getHologramLines()) {
Component line = textSerializer.deserialize(raw);
hologram.addLineComponent(line);
hologram.addTextLineComponent(line);
}
for (ZNpcsAction action : model.getClickActions()) {

@ -1,19 +1,23 @@
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.PropertyHolder;
import org.bukkit.entity.Player;
public class EntityPropertyImpl<T> implements EntityProperty<T> {
import java.util.*;
public abstract class EntityPropertyImpl<T> implements EntityProperty<T> {
private final String name;
private final T defaultValue;
private final Class<T> clazz;
private final PropertySerializer<T> serializer;
private final Set<EntityPropertyImpl<?>> dependencies = new HashSet<>();
private boolean playerModifiable = true;
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.defaultValue = defaultValue;
this.clazz = clazz;
this.serializer = serializer;
}
@Override
@ -21,24 +25,38 @@ public class EntityPropertyImpl<T> implements EntityProperty<T> {
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
public T getDefaultValue() {
return defaultValue;
}
@Override
public boolean isPlayerModifiable() {
return playerModifiable;
}
public void setPlayerModifiable(boolean playerModifiable) {
this.playerModifiable = playerModifiable;
}
public Class<T> getType() {
return clazz;
}
public void addDependency(EntityPropertyImpl<?> property) {
dependencies.add(property);
}
protected static <V> EntityData newEntityData(int index, EntityDataType<V> type, V value) {
return new EntityData(index, type, value);
}
public List<EntityData> applyStandalone(Player player, PacketEntity packetEntity, boolean isSpawned) {
Map<Integer, EntityData> map = new HashMap<>();
apply(player, packetEntity, isSpawned, map);
for (EntityPropertyImpl<?> property : dependencies) property.apply(player, packetEntity, isSpawned, map);
return new ArrayList<>(map.values());
}
abstract public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData> properties);
}

@ -1,23 +1,43 @@
package lol.pyr.znpcsplus.entity;
import org.bukkit.inventory.ItemStack;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.protocol.entity.data.EntityDataTypes;
import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
import com.github.retrooper.packetevents.protocol.nbt.NBTInt;
import com.github.retrooper.packetevents.protocol.nbt.NBTString;
import com.github.retrooper.packetevents.protocol.player.EquipmentSlot;
import lol.pyr.znpcsplus.api.entity.EntityProperty;
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.properties.villager.VillagerLevelProperty;
import lol.pyr.znpcsplus.entity.properties.villager.VillagerProfessionProperty;
import lol.pyr.znpcsplus.entity.properties.villager.VillagerTypeProperty;
import lol.pyr.znpcsplus.entity.serializers.*;
import lol.pyr.znpcsplus.packets.PacketFactory;
import lol.pyr.znpcsplus.skin.cache.MojangSkinCache;
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 java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.*;
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")
public class EntityPropertyRegistryImpl implements EntityPropertyRegistry {
private final Map<Class<?>, PropertySerializer<?>> serializerMap = new HashMap<>();
@ -32,6 +52,7 @@ public class EntityPropertyRegistryImpl implements EntityPropertyRegistry {
registerSerializer(new ColorPropertySerializer());
registerSerializer(new Vector3fPropertySerializer());
registerSerializer(new BlockStatePropertySerializer());
registerSerializer(new IntegerPropertySerializer());
registerEnumSerializer(NpcPose.class);
registerEnumSerializer(DyeColor.class);
@ -44,140 +65,41 @@ public class EntityPropertyRegistryImpl implements EntityPropertyRegistry {
registerEnumSerializer(VillagerType.class);
registerEnumSerializer(VillagerProfession.class);
registerEnumSerializer(VillagerLevel.class);
registerEnumSerializer(AxolotlVariant.class);
registerEnumSerializer(HorseType.class);
registerEnumSerializer(HorseColor.class);
registerEnumSerializer(HorseStyle.class);
registerEnumSerializer(HorseArmor.class);
registerEnumSerializer(LlamaVariant.class);
registerEnumSerializer(MooshroomVariant.class);
registerEnumSerializer(OcelotType.class);
registerEnumSerializer(PandaGene.class);
registerEnumSerializer(PuffState.class);
registerType("glow", NamedTextColor.class);
registerType("fire", false);
registerType("invisible", false);
registerType("silent", false);
registerType("skin", SkinDescriptor.class);
registerType("name", Component.class);
registerType("look", false);
registerType("dinnerbone", 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);
/*
registerType("using_item", false); // TODO: fix it for 1.8 and add new property to use offhand item and riptide animation
registerType("potion_color", Color.BLACK);
registerType("potion_ambient", false);
registerType("shaking", false);
registerType("baby", false); // TODO
registerType("pose", NpcPose.STANDING);
// Player
registerType("skin_cape", true);
registerType("skin_jacket", true);
registerType("skin_left_sleeve", true);
registerType("skin_right_sleeve", true);
registerType("skin_left_leg", true);
registerType("skin_right_leg", true);
registerType("skin_hat", true);
registerType("shoulder_entity_left", ParrotVariant.NONE);
registerType("shoulder_entity_right", ParrotVariant.NONE);
// End Crystal
registerType("beam_target", null); // TODO: Make a block pos class for this
registerType("show_base", true); // TODO
// Armor Stand
registerType("small", false);
registerType("arms", false);
registerType("base_plate", true);
registerType("head_rotation", Vector3f.zero());
registerType("body_rotation", Vector3f.zero());
registerType("left_arm_rotation", new Vector3f(-10, 0, -10));
registerType("right_arm_rotation", new Vector3f(-15, 0, 10));
registerType("left_leg_rotation", new Vector3f(-1 , 0, -1));
registerType("right_leg_rotation", new Vector3f(1, 0, 1));
// Axolotl
registerType("axolotl_variant", 0);
registerType("playing_dead", false); // TODO fix disabling
// Bat
registerType("hanging", false);
// Bee
registerType("angry", false);
registerType("has_nectar", false);
// Blaze
registerType("blaze_on_fire", false);
// Cat
registerType("cat_variant", CatVariant.BLACK);
registerType("cat_lying", false);
registerType("cat_collar_color", DyeColor.RED);
// Creeper
registerType("creeper_state", CreeperState.IDLE);
registerType("creeper_charged", false);
// Enderman
registerType("enderman_held_block", new BlockState(0)); // TODO: figure out the type on this
registerType("enderman_screaming", false); // TODO
registerType("enderman_staring", false); // TODO
// Evoker
registerType("evoker_spell", SpellType.NONE);
// Fox
registerType("fox_variant", FoxVariant.RED);
registerType("fox_sitting", false);
registerType("fox_crouching", false);
registerType("fox_sleeping", false);
registerType("fox_faceplanted", false);
// Frog
registerType("frog_variant", FrogVariant.TEMPERATE);
// Ghast
registerType("attacking", false);
// Guardian
registerType("is_elder", false); // TODO: ensure it only works till 1.10. Note: index is wrong on wiki.vg
// Piglin / Hoglin
registerType("immune_to_zombification", true);
// Pufferfish
registerType("puff_state", null); // TODO: Make a puff state enum class
// Tropical Fish
registerType("tropical_fish_variant", null); // TODO: Maybe make an enum class for this? its just an int on wiki.vg
// Sniffer
registerType("sniffer_state", null); // TODO: Nothing on wiki.vg, look in mc source
// Horse
registerType("horse_style", 0); // TODO: Figure this out
registerType("horse_chest", false); // TODO
registerType("horse_saddle", false); // TODO
// LLama
registerType("carpet_color", DyeColor.class); // TODO
registerType("llama_variant", 0); // TODO
// Panda
registerType("panda_sneezing", false); // TODO
registerType("panda_rolling", false); // TODO
registerType("panda_sitting", false); // TODO
registerType("panda_on_back", false); // TODO
// Pig
registerType("pig_saddle", false); // TODO
// Rabbit
registerType("rabbit_type", 0); // TODO: Figure this out
// Polar Bear
registerType("polar_bear_standing", false); // TODO
// Sheep
registerType("sheep_color", DyeColor.WHITE); // TODO: Figure this out
registerType("sheep_sheared", false); // TODO
@ -190,14 +112,6 @@ public class EntityPropertyRegistryImpl implements EntityPropertyRegistry {
registerType("wolf_collar_color", DyeColor.RED); // TODO
registerType("wolf_angry", false); // TODO
// Parrot
registerType("parrot_variant", 0); // TODO
// Villager
registerType("villager_type", VillagerType.PLAINS);
registerType("villager_profession", VillagerProfession.NONE);
registerType("villager_level", VillagerLevel.STONE);
// Show Golem
registerType("pumpkin", true); // TODO
@ -206,25 +120,391 @@ public class EntityPropertyRegistryImpl implements EntityPropertyRegistry {
registerType("shield_height", 0); // TODO: figure this out
registerType("shulker_color", DyeColor.RED); // TODO
// Piglin
registerType("piglin_dancing", false); // TODO
registerType("piglin_charging_crossbow", false); // TODO
// Goat
registerType("has_left_horn", true);
registerType("has_right_horn", true);
// Vindicator
registerType("celebrating", false); // TODO
// Wither
registerType("invulnerable_time", 0); // TODO
// Phantom
registerType("phantom_size", 0); // TODO
*/
}
// Slime
registerType("slime_size", 0); // TODO
public void registerTypes(PacketFactory packetFactory) {
ServerVersion ver = PacketEvents.getAPI().getServerManager().getVersion();
boolean legacyBooleans = ver.isOlderThan(ServerVersion.V_1_9);
boolean legacyNames = ver.isOlderThan(ServerVersion.V_1_9);
boolean optionalComponents = ver.isNewerThanOrEquals(ServerVersion.V_1_13);
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(legacyNames, optionalComponents));
register(new DinnerboneProperty(legacyNames, optionalComponents));
register(new DummyProperty<>("look", false));
register(new GlowProperty(packetFactory));
register(new BitsetProperty("fire", 0, 0x01));
register(new BitsetProperty("invisible", 0, 0x20));
register(new HologramItemProperty());
linkProperties("glow", "fire", "invisible");
register(new BooleanProperty("silent", 4, false, legacyBooleans));
final int tamedIndex;
if (ver.isNewerThanOrEquals(ServerVersion.V_1_17)) tamedIndex = 17;
else if (ver.isNewerThanOrEquals(ServerVersion.V_1_15)) tamedIndex = 16;
else if (ver.isNewerThanOrEquals(ServerVersion.V_1_14)) tamedIndex = 15;
else if (ver.isNewerThanOrEquals(ServerVersion.V_1_10)) tamedIndex = 13;
else if (ver.isNewerThanOrEquals(ServerVersion.V_1_9)) tamedIndex = 12;
else tamedIndex = 16;
register(new BitsetProperty("tamed", tamedIndex, 0x04));
int potionIndex;
if (ver.isNewerThanOrEquals(ServerVersion.V_1_17)) potionIndex = 10;
else if (ver.isNewerThanOrEquals(ServerVersion.V_1_14)) potionIndex = 9;
else if (ver.isNewerThanOrEquals(ServerVersion.V_1_10)) potionIndex = 8;
else potionIndex = 7;
register(new EncodedIntegerProperty<>("potion_color", Color.class, potionIndex++, Color::asRGB));
register(new BooleanProperty("potion_ambient", potionIndex, false, legacyBooleans));
int babyIndex;
if (ver.isNewerThanOrEquals(ServerVersion.V_1_17)) babyIndex = 16;
else if (ver.isNewerThanOrEquals(ServerVersion.V_1_15)) babyIndex = 15;
else if (ver.isNewerThanOrEquals(ServerVersion.V_1_14)) babyIndex = 14;
else if (ver.isNewerThanOrEquals(ServerVersion.V_1_10)) babyIndex = 12;
else if (ver.isNewerThanOrEquals(ServerVersion.V_1_9)) babyIndex = 11;
else babyIndex = 12;
register(new BooleanProperty("baby", babyIndex, false, legacyBooleans));
// Player
register(new DummyProperty<>("skin", SkinDescriptor.class, false));
final int skinLayersIndex;
if (ver.isNewerThanOrEquals(ServerVersion.V_1_17)) skinLayersIndex = 17;
else if (ver.isNewerThanOrEquals(ServerVersion.V_1_16)) skinLayersIndex = 16;
else if (ver.isNewerThanOrEquals(ServerVersion.V_1_14)) skinLayersIndex = 15;
else if (ver.isNewerThanOrEquals(ServerVersion.V_1_10)) skinLayersIndex = 13;
else if (ver.isNewerThanOrEquals(ServerVersion.V_1_9)) skinLayersIndex = 12;
else skinLayersIndex = 10;
register(new BitsetProperty("skin_cape", skinLayersIndex, 0x01));
register(new BitsetProperty("skin_jacket", skinLayersIndex, 0x02));
register(new BitsetProperty("skin_left_sleeve", skinLayersIndex, 0x04));
register(new BitsetProperty("skin_right_sleeve", skinLayersIndex, 0x08));
register(new BitsetProperty("skin_left_leg", skinLayersIndex, 0x10));
register(new BitsetProperty("skin_right_leg", skinLayersIndex, 0x20));
register(new BitsetProperty("skin_hat", skinLayersIndex, 0x40));
linkProperties("skin_cape", "skin_jacket", "skin_left_sleeve", "skin_right_sleeve", "skin_left_leg", "skin_right_leg", "skin_hat");
// Armor Stand
int armorStandIndex;
if (ver.isNewerThanOrEquals(ServerVersion.V_1_17)) armorStandIndex = 15;
else if (ver.isNewerThanOrEquals(ServerVersion.V_1_15)) armorStandIndex = 14;
else if (ver.isNewerThanOrEquals(ServerVersion.V_1_14)) armorStandIndex = 13;
else if (ver.isNewerThanOrEquals(ServerVersion.V_1_10)) armorStandIndex = 11;
else armorStandIndex = 10;
register(new BitsetProperty("small", armorStandIndex, 0x01));
register(new BitsetProperty("arms", armorStandIndex, 0x04));
register(new BitsetProperty("base_plate", armorStandIndex++, 0x08, true));
linkProperties("small", "arms", "base_plate");
register(new RotationProperty("head_rotation", armorStandIndex++, Vector3f.zero()));
register(new RotationProperty("body_rotation", armorStandIndex++, Vector3f.zero()));
register(new RotationProperty("left_arm_rotation", armorStandIndex++, new Vector3f(-10, 0, -10)));
register(new RotationProperty("right_arm_rotation", armorStandIndex++, new Vector3f(-15, 0, 10)));
register(new RotationProperty("left_leg_rotation", armorStandIndex++, new Vector3f(-1, 0, -1)));
register(new RotationProperty("right_leg_rotation", armorStandIndex, new Vector3f(1, 0, 1)));
// Ghast
final int ghastAttackingIndex;
if (ver.isNewerThanOrEquals(ServerVersion.V_1_17)) ghastAttackingIndex = 16;
else if (ver.isNewerThanOrEquals(ServerVersion.V_1_15)) ghastAttackingIndex = 15;
else if (ver.isNewerThanOrEquals(ServerVersion.V_1_14)) ghastAttackingIndex = 14;
else if (ver.isNewerThanOrEquals(ServerVersion.V_1_10)) ghastAttackingIndex = 12;
else if (ver.isNewerThanOrEquals(ServerVersion.V_1_9)) ghastAttackingIndex = 11;
else ghastAttackingIndex = 16;
register(new BooleanProperty("attacking", ghastAttackingIndex, false, legacyBooleans));
// Bat
final int batIndex;
if (ver.isNewerThanOrEquals(ServerVersion.V_1_17)) batIndex = 16;
else if (ver.isNewerThanOrEquals(ServerVersion.V_1_15)) batIndex = 15;
else if (ver.isNewerThanOrEquals(ServerVersion.V_1_14)) batIndex = 14;
else if (ver.isNewerThanOrEquals(ServerVersion.V_1_10)) batIndex = 12;
else if (ver.isNewerThanOrEquals(ServerVersion.V_1_9)) batIndex = 11;
else batIndex = 16;
register(new BooleanProperty("hanging", batIndex, false, true /* This isn't a mistake */));
// Blaze
final int blazeIndex;
if (ver.isNewerThanOrEquals(ServerVersion.V_1_17)) blazeIndex = 16;
else if (ver.isNewerThanOrEquals(ServerVersion.V_1_15)) blazeIndex = 15;
else if (ver.isNewerThanOrEquals(ServerVersion.V_1_14)) blazeIndex = 14;
else if (ver.isNewerThanOrEquals(ServerVersion.V_1_10)) blazeIndex = 12;
else if (ver.isNewerThanOrEquals(ServerVersion.V_1_9)) blazeIndex = 11;
else blazeIndex = 16;
register(new BitsetProperty("blaze_on_fire", blazeIndex, 0x01));
// Creeper
int creeperIndex;
if (ver.isNewerThanOrEquals(ServerVersion.V_1_17)) creeperIndex = 16;
else if (ver.isNewerThanOrEquals(ServerVersion.V_1_15)) creeperIndex = 15;
else if (ver.isNewerThanOrEquals(ServerVersion.V_1_14)) creeperIndex = 14;
else if (ver.isNewerThanOrEquals(ServerVersion.V_1_10)) creeperIndex = 12;
else if (ver.isNewerThanOrEquals(ServerVersion.V_1_9)) creeperIndex = 11;
else creeperIndex= 16;
register(new EncodedIntegerProperty<>("creeper_state", CreeperState.IDLE, creeperIndex++, CreeperState::getState));
register(new BooleanProperty("creeper_charged", creeperIndex, false, legacyBooleans));
// Abstract Horse
int horseIndex;
if (ver.isNewerThanOrEquals(ServerVersion.V_1_17)) horseIndex = 17;
else if (ver.isNewerThanOrEquals(ServerVersion.V_1_15)) horseIndex = 16;
else if (ver.isNewerThanOrEquals(ServerVersion.V_1_14)) horseIndex = 15;
else if (ver.isNewerThanOrEquals(ServerVersion.V_1_10)) horseIndex = 13;
else if (ver.isNewerThanOrEquals(ServerVersion.V_1_9)) horseIndex = 12;
else horseIndex = 16;
int horseEating = ver.isNewerThanOrEquals(ServerVersion.V_1_12) ? 0x10 : 0x20;
register(new BitsetProperty("is_tame", horseIndex, 0x02, false, legacyBooleans));
register(new BitsetProperty("is_saddled", horseIndex, 0x04, false, legacyBooleans));
register(new BitsetProperty("is_eating", horseIndex, horseEating, false, legacyBooleans));
register(new BitsetProperty("is_rearing", horseIndex, horseEating << 1, false, legacyBooleans));
register(new BitsetProperty("has_mouth_open", horseIndex, horseEating << 2, false, legacyBooleans));
// Horse
if (ver.isNewerThanOrEquals(ServerVersion.V_1_8) && ver.isOlderThan(ServerVersion.V_1_9)) {
register(new EncodedByteProperty<>("horse_type", HorseType.HORSE, 19, obj -> (byte) obj.ordinal()));
} else if (ver.isOlderThan(ServerVersion.V_1_11)) {
int horseTypeIndex = 14;
if (ver.isOlderThan(ServerVersion.V_1_10)) horseTypeIndex = 13;
register(new EncodedIntegerProperty<>("horse_type", HorseType.HORSE, horseTypeIndex, Enum::ordinal));
}
int horseVariantIndex;
if (ver.isNewerThanOrEquals(ServerVersion.V_1_18)) horseVariantIndex = 18;
else if (ver.isNewerThanOrEquals(ServerVersion.V_1_17)) horseVariantIndex = 19;
else if (ver.isNewerThanOrEquals(ServerVersion.V_1_15)) horseVariantIndex = 18;
else if (ver.isNewerThanOrEquals(ServerVersion.V_1_14)) horseVariantIndex = 17;
else if (ver.isNewerThanOrEquals(ServerVersion.V_1_10)) horseVariantIndex = 15;
else if (ver.isNewerThanOrEquals(ServerVersion.V_1_9)) horseVariantIndex = 14;
else horseVariantIndex = 20;
register(new HorseStyleProperty(horseVariantIndex));
register(new HorseColorProperty(horseVariantIndex));
linkProperties("horse_style", "horse_color");
// Use chesteplate property for 1.14 and above
if (ver.isOlderThan(ServerVersion.V_1_14)) {
register(new EncodedIntegerProperty<>("horse_armor", HorseArmor.NONE, horseVariantIndex + 2, Enum::ordinal));
}
// Chested Horse
if (ver.isOlderThan(ServerVersion.V_1_11)) {
register(new BitsetProperty("has_chest", horseIndex, 0x08, false, legacyBooleans));
linkProperties("is_saddled", "has_chest", "is_eating", "is_rearing", "has_mouth_open");
} else {
register(new BooleanProperty("has_chest", horseVariantIndex, false, legacyBooleans));
linkProperties("is_saddled", "is_eating", "is_rearing", "has_mouth_open");
}
// Slime, Magma Cube and Phantom
int sizeIndex;
if (ver.isNewerThanOrEquals(ServerVersion.V_1_17)) sizeIndex = 16;
else if (ver.isNewerThanOrEquals(ServerVersion.V_1_15)) sizeIndex = 15;
else if (ver.isNewerThanOrEquals(ServerVersion.V_1_14)) sizeIndex = 14;
else if (ver.isNewerThanOrEquals(ServerVersion.V_1_10)) sizeIndex = 12;
else if (ver.isNewerThanOrEquals(ServerVersion.V_1_9)) sizeIndex = 11;
else sizeIndex = 16;
register(new IntegerProperty("size", sizeIndex, 1, legacyBooleans));
// Ocelot
if (ver.isOlderThan(ServerVersion.V_1_14)) {
int ocelotIndex;
if (ver.isNewerThanOrEquals(ServerVersion.V_1_10)) ocelotIndex = 15;
else if (ver.isNewerThanOrEquals(ServerVersion.V_1_9)) ocelotIndex = 14;
else ocelotIndex = 18;
if (legacyBooleans) register(new EncodedByteProperty<>("ocelot_type", OcelotType.OCELOT, ocelotIndex, obj -> (byte) obj.ordinal()));
else register(new EncodedIntegerProperty<>("ocelot_type", OcelotType.OCELOT, ocelotIndex, Enum::ordinal));
}
// Pig
int pigIndex;
if (ver.isNewerThanOrEquals(ServerVersion.V_1_17)) pigIndex = 17;
else if (ver.isNewerThanOrEquals(ServerVersion.V_1_15)) pigIndex = 16;
else if (ver.isNewerThanOrEquals(ServerVersion.V_1_14)) pigIndex = 15;
else if (ver.isNewerThanOrEquals(ServerVersion.V_1_10)) pigIndex = 13;
else if (ver.isNewerThanOrEquals(ServerVersion.V_1_9)) pigIndex = 12;
else pigIndex = 16;
register(new BooleanProperty("pig_saddled", pigIndex, false, legacyBooleans));
if (!ver.isNewerThanOrEquals(ServerVersion.V_1_10)) return;
// Polar Bear
int polarBearIndex;
if (ver.isNewerThanOrEquals(ServerVersion.V_1_17)) polarBearIndex = 17;
else if (ver.isNewerThanOrEquals(ServerVersion.V_1_15)) polarBearIndex = 16;
else if (ver.isNewerThanOrEquals(ServerVersion.V_1_14)) polarBearIndex = 15;
else polarBearIndex = 13;
register(new BooleanProperty("polar_bear_standing", polarBearIndex, false, false));
if (!ver.isNewerThanOrEquals(ServerVersion.V_1_11)) return;
// Spellcaster Illager
int spellIndex = 12;
if (ver.isNewerThanOrEquals(ServerVersion.V_1_17)) spellIndex = 17;
else if (ver.isNewerThanOrEquals(ServerVersion.V_1_15)) spellIndex = 16;
else if (ver.isNewerThanOrEquals(ServerVersion.V_1_14)) spellIndex = 15;
else if (ver.isNewerThanOrEquals(ServerVersion.V_1_12)) spellIndex = 13;
register(new EncodedByteProperty<>("spell", SpellType.NONE, spellIndex, obj -> (byte) Math.min(obj.ordinal(), ver.isOlderThan(ServerVersion.V_1_13) ? 3 : 5)));
// Llama
int llamaIndex;
if (ver.isNewerThanOrEquals(ServerVersion.V_1_18)) llamaIndex = 20;
else if (ver.isNewerThanOrEquals(ServerVersion.V_1_17)) llamaIndex = 21;
else if (ver.isNewerThanOrEquals(ServerVersion.V_1_15)) llamaIndex = 20;
else if (ver.isNewerThanOrEquals(ServerVersion.V_1_14)) llamaIndex = 19;
else llamaIndex = 17;
register(new EncodedIntegerProperty<DyeColor>("carpet_color", DyeColor.class, llamaIndex++, obj -> obj == null ? -1 : obj.ordinal()));
register(new EncodedIntegerProperty<>("llama_variant", LlamaVariant.CREAMY, llamaIndex, Enum::ordinal));
if (!ver.isNewerThanOrEquals(ServerVersion.V_1_12)) return;
// Parrot
int parrotIndex;
if (ver.isNewerThanOrEquals(ServerVersion.V_1_17)) parrotIndex = 19;
else if (ver.isNewerThanOrEquals(ServerVersion.V_1_15)) parrotIndex = 18;
else if (ver.isNewerThanOrEquals(ServerVersion.V_1_14)) parrotIndex = 17;
else parrotIndex = 15;
register(new EncodedIntegerProperty<>("parrot_variant", ParrotVariant.RED_BLUE, parrotIndex, Enum::ordinal));
// Player
NBTProperty.NBTDecoder<ParrotVariant> parrotVariantDecoder = (variant) -> {
NBTCompound compound = new NBTCompound();
compound.setTag("id", new NBTString("minecraft:parrot"));
compound.setTag("Variant", new NBTInt(variant.ordinal()));
return compound;
};
int shoulderIndex = skinLayersIndex+2;
register(new NBTProperty<>("shoulder_entity_left", ParrotVariant.class, shoulderIndex++, parrotVariantDecoder));
register(new NBTProperty<>("shoulder_entity_right", ParrotVariant.class, shoulderIndex, parrotVariantDecoder));
if (!ver.isNewerThanOrEquals(ServerVersion.V_1_13)) return;
// Pufferfish
int pufferfishIndex;
if (ver.isNewerThanOrEquals(ServerVersion.V_1_17)) pufferfishIndex = 17;
else if (ver.isNewerThanOrEquals(ServerVersion.V_1_15)) pufferfishIndex = 16;
else if (ver.isNewerThanOrEquals(ServerVersion.V_1_14)) pufferfishIndex = 15;
else pufferfishIndex = 13;
register(new EncodedIntegerProperty<>("puff_state", PuffState.DEFLATED, pufferfishIndex, Enum::ordinal));
if (!ver.isNewerThanOrEquals(ServerVersion.V_1_14)) return;
// Pose
register(new NpcPoseProperty());
// Villager
final int villagerIndex;
if (ver.isNewerThanOrEquals(ServerVersion.V_1_17)) villagerIndex = 18;
else if (ver.isNewerThanOrEquals(ServerVersion.V_1_15)) villagerIndex = 17;
else if (ver.isNewerThanOrEquals(ServerVersion.V_1_14)) villagerIndex = 16;
else if (ver.isNewerThanOrEquals(ServerVersion.V_1_10)) villagerIndex = 13;
else if (ver.isNewerThanOrEquals(ServerVersion.V_1_9)) villagerIndex = 12;
else villagerIndex = 16;
register(new VillagerTypeProperty("villager_type", villagerIndex, VillagerType.PLAINS));
register(new VillagerProfessionProperty("villager_profession", villagerIndex, VillagerProfession.NONE));
register(new VillagerLevelProperty("villager_level", villagerIndex, VillagerLevel.STONE));
linkProperties("villager_type", "villager_profession", "villager_level");
// Cat
int catIndex;
if (ver.isNewerThanOrEquals(ServerVersion.V_1_17)) catIndex = 19;
else if (ver.isNewerThanOrEquals(ServerVersion.V_1_15)) catIndex = 18;
else catIndex = 17;
register(new EncodedIntegerProperty<>("cat_variant", CatVariant.BLACK, catIndex++, Enum::ordinal, EntityDataTypes.CAT_VARIANT));
register(new BooleanProperty("cat_laying", catIndex++, false, legacyBooleans));
register(new BooleanProperty("cat_relaxed", catIndex++, false, legacyBooleans));
register(new EncodedIntegerProperty<>("cat_collar", DyeColor.RED, catIndex, Enum::ordinal));
// Fox
int foxIndex;
if (ver.isNewerThanOrEquals(ServerVersion.V_1_17)) foxIndex = 17;
else if (ver.isNewerThanOrEquals(ServerVersion.V_1_15)) foxIndex = 16;
else foxIndex = 15;
register(new EncodedIntegerProperty<>("fox_variant", FoxVariant.RED, foxIndex++, Enum::ordinal));
register(new BitsetProperty("fox_sitting", foxIndex, 0x01));
register(new BitsetProperty("fox_crouching", foxIndex, 0x04));
register(new BitsetProperty("fox_sleeping", foxIndex, 0x20));
linkProperties("fox_sitting", "fox_crouching", "fox_sleeping");
int mooshroomIndex;
if (ver.isNewerThanOrEquals(ServerVersion.V_1_17)) mooshroomIndex = 17;
else if (ver.isNewerThanOrEquals(ServerVersion.V_1_15)) mooshroomIndex = 16;
else mooshroomIndex = 15;
register(new EncodedStringProperty<>("mooshroom_variant", MooshroomVariant.RED, mooshroomIndex, MooshroomVariant::getVariantName));
// Panda
int pandaIndex;
if (ver.isNewerThanOrEquals(ServerVersion.V_1_17)) pandaIndex = 20;
else if (ver.isNewerThanOrEquals(ServerVersion.V_1_15)) pandaIndex = 19;
else pandaIndex = 18;
register(new EncodedByteProperty<>("panda_main_gene", PandaGene.NORMAL, pandaIndex++, obj -> (byte) obj.ordinal()));
register(new EncodedByteProperty<>("panda_hidden_gene", PandaGene.NORMAL, pandaIndex++, obj -> (byte) obj.ordinal()));
if (ver.isNewerThanOrEquals(ServerVersion.V_1_15)) {
register(new BitsetProperty("panda_sneezing", pandaIndex, 0x02));
register(new BitsetProperty("panda_rolling", pandaIndex, 0x04));
register(new BitsetProperty("panda_sitting", pandaIndex, 0x08));
register(new BitsetProperty("panda_on_back", pandaIndex, 0x10));
linkProperties("panda_sneezing", "panda_rolling", "panda_sitting", "panda_on_back");
} else {
register(new BitsetProperty("panda_sneezing", pandaIndex, 0x02));
register(new BitsetProperty("panda_eating", pandaIndex, 0x04));
linkProperties("panda_sneezing", "panda_eating");
}
if (!ver.isNewerThanOrEquals(ServerVersion.V_1_15)) return;
register(new BitsetProperty("fox_faceplanted", foxIndex, 0x40));
linkProperties("fox_sitting", "fox_crouching", "fox_sleeping", "fox_faceplanted");
// Bee
int beeIndex;
if (ver.isNewerThanOrEquals(ServerVersion.V_1_17)) beeIndex = 17;
else beeIndex = 18;
register(new BitsetProperty("has_nectar", beeIndex++, 0x08));
register(new EncodedIntegerProperty<>("angry", false, beeIndex, enabled -> enabled ? 1 : 0));
if (!ver.isNewerThanOrEquals(ServerVersion.V_1_16)) return;
// Hoglin and Piglin Zombification
final int zombificationIndex;
if (ver.isNewerThanOrEquals(ServerVersion.V_1_17)) zombificationIndex = 17; // Change piglinIndex and pillagerIndex if you change this
else zombificationIndex = 16;
register(new BooleanProperty("hoglin_immune_to_zombification", zombificationIndex, false, legacyBooleans));
register(new BooleanProperty("piglin_immune_to_zombification", zombificationIndex-1, false, legacyBooleans));
// Piglin
int piglinIndex = zombificationIndex;
register(new BooleanProperty("piglin_baby", piglinIndex++, false, legacyBooleans));
register(new BooleanProperty("piglin_charging_crossbow", piglinIndex++, false, legacyBooleans));
register(new BooleanProperty("piglin_dancing", piglinIndex, false, legacyBooleans));
// Pillager
register(new BooleanProperty("pillager_charging", zombificationIndex, false, legacyBooleans));
// Vindicator
int vindicatorIndex = zombificationIndex -1;
register(new BooleanProperty("celebrating", vindicatorIndex, false, legacyBooleans));
if (!ver.isNewerThanOrEquals(ServerVersion.V_1_17)) return;
// Axolotl
register(new EncodedIntegerProperty<>("axolotl_variant", AxolotlVariant.LUCY, 17, Enum::ordinal));
register(new BooleanProperty("playing_dead", 18, false, legacyBooleans));
// Goat
register(new BooleanProperty("has_left_horn", 18, true, legacyBooleans));
register(new BooleanProperty("has_right_horn", 19, true, legacyBooleans));
register(new EncodedIntegerProperty<>("shaking", false,7, enabled -> enabled ? 140 : 0));
if (!ver.isNewerThanOrEquals(ServerVersion.V_1_19)) return;
// Frog
register(new EncodedIntegerProperty<>("frog_variant", FrogVariant.TEMPERATE, 17, Enum::ordinal, EntityDataTypes.FROG_VARIANT));
if (!ver.isNewerThanOrEquals(ServerVersion.V_1_20)) return;
// Camel
register(new BooleanProperty("bashing", 18, false, legacyBooleans));
}
private void registerSerializer(PropertySerializer<?> serializer) {
@ -235,18 +515,27 @@ public class EntityPropertyRegistryImpl implements EntityPropertyRegistry {
serializerMap.put(clazz, new EnumPropertySerializer<>(clazz));
}
private <T> void registerType(String name, Class<T> type) {
registerType(name, null, type);
private <T> void register(EntityPropertyImpl<?> property) {
if (byName.containsKey(property.getName()))
throw new IllegalArgumentException("Duplicate property name: " + property.getName());
byName.put(property.getName(), property);
}
private <T> void registerType(String name, T defaultValue) {
registerType(name, defaultValue, (Class<T>) defaultValue.getClass());
private void linkProperties(String... names) {
linkProperties(Arrays.stream(names)
.map(this::getByName)
.collect(Collectors.toSet()));
}
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);
private void linkProperties(Collection<EntityPropertyImpl<?>> properties) {
for (EntityPropertyImpl<?> property : properties) for (EntityPropertyImpl<?> dependency : properties) {
if (property.equals(dependency)) continue;
property.addDependency(dependency);
}
}
public <V> PropertySerializer<V> getSerializer(Class<V> type) {
return (PropertySerializer<V>) serializerMap.get(type);
}
@Override

@ -15,7 +15,11 @@ public class EnumPropertySerializer<T extends Enum<T>> implements PropertySerial
@Override
public T deserialize(String property) {
try {
return Enum.valueOf(enumClass, property.toUpperCase());
} catch (IllegalArgumentException e) {
return null;
}
}
@Override

@ -4,6 +4,7 @@ import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.protocol.entity.type.EntityType;
import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes;
import lol.pyr.znpcsplus.api.entity.EntityProperty;
import lol.pyr.znpcsplus.api.entity.PropertyHolder;
import lol.pyr.znpcsplus.packets.PacketFactory;
import lol.pyr.znpcsplus.reflection.Reflections;
@ -11,9 +12,10 @@ import lol.pyr.znpcsplus.util.NpcLocation;
import org.bukkit.entity.Player;
import java.util.Collection;
import java.util.Set;
import java.util.UUID;
public class PacketEntity {
public class PacketEntity implements PropertyHolder {
private final PacketFactory packetFactory;
private final PropertyHolder properties;
@ -66,11 +68,6 @@ public class PacketEntity {
packetFactory.sendAllMetadata(player, this, properties);
}
public void remakeTeam(Player player) {
packetFactory.removeTeam(player, this);
packetFactory.createTeam(player, this, properties);
}
private static int reserveEntityID() {
if (PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_14)) {
return Reflections.ATOMIC_ENTITY_ID_FIELD.get().incrementAndGet();
@ -80,4 +77,24 @@ public class PacketEntity {
return id;
}
}
@Override
public <T> T getProperty(EntityProperty<T> key) {
return properties.getProperty(key);
}
@Override
public boolean hasProperty(EntityProperty<?> key) {
return properties.hasProperty(key);
}
@Override
public <T> void setProperty(EntityProperty<T> key, T value) {
properties.setProperty(key, value);
}
@Override
public Set<EntityProperty<?>> getAppliedProperties() {
return properties.getAppliedProperties();
}
}

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

@ -0,0 +1,43 @@
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 BitsetProperty extends EntityPropertyImpl<Boolean> {
private final int index;
private final int bitmask;
private final boolean inverted;
private boolean integer = false;
public BitsetProperty(String name, int index, int bitmask, boolean inverted, boolean integer) {
this(name, index, bitmask, inverted);
this.integer = integer;
}
public BitsetProperty(String name, int index, int bitmask, boolean inverted) {
super(name, inverted, Boolean.class);
this.index = index;
this.bitmask = bitmask;
this.inverted = inverted;
}
public BitsetProperty(String name, int index, int bitmask) {
this(name, index, bitmask, false);
}
@Override
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData> properties) {
EntityData oldData = properties.get(index);
boolean enabled = entity.getProperty(this);
if (inverted) enabled = !enabled;
properties.put(index,
integer ? newEntityData(index, EntityDataTypes.INT, (oldData == null ? 0 : (int) oldData.getValue()) | (enabled ? bitmask : 0)) :
newEntityData(index, EntityDataTypes.BYTE, (byte) ((oldData == null ? 0 : (byte) oldData.getValue()) | (enabled ? bitmask : 0))));
}
}

@ -0,0 +1,34 @@
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 BooleanProperty extends EntityPropertyImpl<Boolean> {
private final int index;
private final boolean legacy;
private final boolean inverted;
public BooleanProperty(String name, int index, boolean defaultValue, boolean legacy) {
this(name, index, defaultValue, legacy, false);
}
public BooleanProperty(String name, int index, boolean defaultValue, boolean legacy, boolean inverted) {
super(name, defaultValue, Boolean.class);
this.index = index;
this.legacy = legacy;
this.inverted = inverted;
}
@Override
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData> properties) {
boolean enabled = entity.getProperty(this);
if (inverted) enabled = !enabled;
if (legacy) properties.put(index, newEntityData(index, EntityDataTypes.BYTE, (byte) (enabled ? 1 : 0)));
else properties.put(index, newEntityData(index, EntityDataTypes.BOOLEAN, enabled));
}
}

@ -0,0 +1,33 @@
package lol.pyr.znpcsplus.entity.properties;
import com.github.retrooper.packetevents.protocol.entity.data.EntityData;
import com.github.retrooper.packetevents.protocol.entity.data.EntityDataType;
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 DinnerboneProperty extends EntityPropertyImpl<Boolean> {
private final Object serialized;
private final EntityDataType<?> type;
public DinnerboneProperty(boolean legacy, boolean optional) {
super("dinnerbone", false, Boolean.class);
Component name = Component.text("Dinnerbone");
String serialized = legacy ?
AdventureSerializer.getLegacyGsonSerializer().serialize(name) :
AdventureSerializer.getGsonSerializer().serialize(name);
this.serialized = optional ? Optional.of(serialized) : serialized;
this.type = optional ? EntityDataTypes.OPTIONAL_COMPONENT : EntityDataTypes.STRING;
}
@Override
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData> properties) {
properties.put(2, new EntityData(2, type, entity.getProperty(this) ? serialized : null));
}
}

@ -0,0 +1,33 @@
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 DummyProperty<T> extends EntityPropertyImpl<T> {
public DummyProperty(String name, T defaultValue) {
this(name, defaultValue, true);
}
public DummyProperty(String name, Class<T> clazz) {
this(name, clazz, true);
}
@SuppressWarnings("unchecked")
public DummyProperty(String name, T defaultValue, boolean playerModifiable) {
super(name, defaultValue, (Class<T>) defaultValue.getClass());
setPlayerModifiable(playerModifiable);
}
public DummyProperty(String name, Class<T> clazz, boolean playerModifiable) {
super(name, null, clazz);
setPlayerModifiable(playerModifiable);
}
@Override
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData> properties) {
}
}

@ -0,0 +1,48 @@
package lol.pyr.znpcsplus.entity.properties;
import com.github.retrooper.packetevents.protocol.entity.data.EntityData;
import com.github.retrooper.packetevents.protocol.entity.data.EntityDataType;
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 EncodedByteProperty<T> extends EntityPropertyImpl<T> {
private final EntityDataType<Byte> type;
private final ByteDecoder<T> decoder;
private final int index;
protected EncodedByteProperty(String name, T defaultValue, Class<T> clazz, int index, ByteDecoder<T> decoder, EntityDataType<Byte> type) {
super(name, defaultValue, clazz);
this.decoder = decoder;
this.index = index;
this.type = type;
}
@SuppressWarnings("unchecked")
public EncodedByteProperty(String name, T defaultValue, int index, ByteDecoder<T> decoder) {
this(name, defaultValue, (Class<T>) defaultValue.getClass(), index, decoder, EntityDataTypes.BYTE);
}
@SuppressWarnings("unchecked")
public EncodedByteProperty(String name, T defaultValue, int index, ByteDecoder<T> decoder, EntityDataType<Byte> type) {
this(name, defaultValue, (Class<T>) defaultValue.getClass(), index, decoder, type);
}
public EncodedByteProperty(String name, Class<T> clazz, int index, ByteDecoder<T> decoder) {
this(name, null, clazz, index, decoder, EntityDataTypes.BYTE);
}
@Override
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData> properties) {
T value = entity.getProperty(this);
if (value == null) return;
properties.put(index, newEntityData(index, type, decoder.decode(value)));
}
public interface ByteDecoder<T> {
byte decode(T obj);
}
}

@ -0,0 +1,48 @@
package lol.pyr.znpcsplus.entity.properties;
import com.github.retrooper.packetevents.protocol.entity.data.EntityData;
import com.github.retrooper.packetevents.protocol.entity.data.EntityDataType;
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 EncodedIntegerProperty<T> extends EntityPropertyImpl<T> {
private final EntityDataType<Integer> type;
private final IntegerDecoder<T> decoder;
private final int index;
protected EncodedIntegerProperty(String name, T defaultValue, Class<T> clazz, int index, IntegerDecoder<T> decoder, EntityDataType<Integer> type) {
super(name, defaultValue, clazz);
this.decoder = decoder;
this.index = index;
this.type = type;
}
@SuppressWarnings("unchecked")
public EncodedIntegerProperty(String name, T defaultValue, int index, IntegerDecoder<T> decoder) {
this(name, defaultValue, (Class<T>) defaultValue.getClass(), index, decoder, EntityDataTypes.INT);
}
@SuppressWarnings("unchecked")
public EncodedIntegerProperty(String name, T defaultValue, int index, IntegerDecoder<T> decoder, EntityDataType<Integer> type) {
this(name, defaultValue, (Class<T>) defaultValue.getClass(), index, decoder, type);
}
public EncodedIntegerProperty(String name, Class<T> clazz, int index, IntegerDecoder<T> decoder) {
this(name, null, clazz, index, decoder, EntityDataTypes.INT);
}
@Override
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData> properties) {
T value = entity.getProperty(this);
if (value == null) return;
properties.put(index, newEntityData(index, type, decoder.decode(value)));
}
public interface IntegerDecoder<T> {
int decode(T obj);
}
}

@ -0,0 +1,48 @@
package lol.pyr.znpcsplus.entity.properties;
import com.github.retrooper.packetevents.protocol.entity.data.EntityData;
import com.github.retrooper.packetevents.protocol.entity.data.EntityDataType;
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 EncodedStringProperty<T> extends EntityPropertyImpl<T> {
private final EntityDataType<String> type;
private final EncodedStringProperty.StringDecoder<T> decoder;
private final int index;
public EncodedStringProperty(String name, T defaultValue, Class<T> clazz, int index, StringDecoder<T> decoder, EntityDataType<String> type) {
super(name, defaultValue, clazz);
this.decoder = decoder;
this.index = index;
this.type = type;
}
@SuppressWarnings("unchecked")
public EncodedStringProperty(String name, T defaultValue, int index, StringDecoder<T> decoder) {
this(name, defaultValue, (Class<T>) defaultValue.getClass(), index, decoder, EntityDataTypes.STRING);
}
@SuppressWarnings("unchecked")
public EncodedStringProperty(String name, T defaultValue, int index, StringDecoder<T> decoder, EntityDataType<String> type) {
this(name, defaultValue, (Class<T>) defaultValue.getClass(), index, decoder, type);
}
public EncodedStringProperty(String name, Class<T> clazz, int index, StringDecoder<T> decoder) {
this(name, null, clazz, index, decoder, EntityDataTypes.STRING);
}
@Override
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData> properties) {
T value = entity.getProperty(this);
if (value == null) return;
properties.put(index, newEntityData(index, type, decoder.decode(value)));
}
public interface StringDecoder<T> {
String decode(T obj);
}
}

@ -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(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData> properties) {
packetFactory.sendEquipment(player, entity, new Equipment(slot, entity.getProperty(this)));
}
}

@ -0,0 +1,30 @@
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(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData> properties) {
NamedTextColor value = entity.getProperty(this);
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,24 @@
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 com.github.retrooper.packetevents.protocol.item.ItemStack;
import lol.pyr.znpcsplus.entity.EntityPropertyImpl;
import lol.pyr.znpcsplus.entity.PacketEntity;
import org.bukkit.entity.Player;
import java.util.Map;
public class HologramItemProperty extends EntityPropertyImpl<ItemStack> {
public HologramItemProperty() {
super("holo_item", null, ItemStack.class);
setPlayerModifiable(false);
}
@Override
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData> properties) {
properties.put(8, newEntityData(8, EntityDataTypes.ITEMSTACK, entity.getProperty(this)));
properties.put(5, newEntityData(5, EntityDataTypes.BOOLEAN, true));
}
}

@ -0,0 +1,26 @@
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.util.HorseColor;
import org.bukkit.entity.Player;
import java.util.Map;
public class HorseColorProperty extends EntityPropertyImpl<HorseColor> {
private final int index;
public HorseColorProperty(int index) {
super("horse_color", HorseColor.WHITE, HorseColor.class);
this.index = index;
}
@Override
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData> properties) {
EntityData oldData = properties.get(index);
HorseColor value = entity.getProperty(this);
properties.put(index, newEntityData(index, EntityDataTypes.INT, value.ordinal() | (oldData == null ? 0 : ((int) oldData.getValue() & 0xFF00))));
}
}

@ -0,0 +1,26 @@
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.util.HorseStyle;
import org.bukkit.entity.Player;
import java.util.Map;
public class HorseStyleProperty extends EntityPropertyImpl<HorseStyle> {
private final int index;
public HorseStyleProperty(int index) {
super("horse_style", HorseStyle.NONE, HorseStyle.class);
this.index = index;
}
@Override
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData> properties) {
EntityData oldData = properties.get(index);
HorseStyle value = entity.getProperty(this);
properties.put(index, newEntityData(index, EntityDataTypes.INT, (oldData == null ? 0 : ((int) oldData.getValue() & 0x00FF)) | (value.ordinal() << 8)));
}
}

@ -0,0 +1,31 @@
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 IntegerProperty extends EntityPropertyImpl<Integer> {
private final int index;
private final boolean legacy;
public IntegerProperty(String name, int index, Integer defaultValue) {
this(name, index, defaultValue, false);
}
public IntegerProperty(String name, int index, Integer defaultValue, boolean legacy) {
super(name, defaultValue, Integer.class);
this.index = index;
this.legacy = legacy;
}
@Override
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData> properties) {
properties.put(index, legacy ?
newEntityData(index, EntityDataTypes.BYTE, (byte) entity.getProperty(this).intValue()) :
newEntityData(index, EntityDataTypes.INT, entity.getProperty(this)));
}
}

@ -0,0 +1,49 @@
package lol.pyr.znpcsplus.entity.properties;
import com.github.retrooper.packetevents.protocol.entity.data.EntityData;
import com.github.retrooper.packetevents.protocol.entity.data.EntityDataType;
import com.github.retrooper.packetevents.protocol.entity.data.EntityDataTypes;
import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
import lol.pyr.znpcsplus.entity.EntityPropertyImpl;
import lol.pyr.znpcsplus.entity.PacketEntity;
import org.bukkit.entity.Player;
import java.util.Map;
public class NBTProperty<T> extends EntityPropertyImpl<T> {
private final EntityDataType<NBTCompound> type;
private final NBTDecoder<T> decoder;
private final int index;
public NBTProperty(String name, T defaultValue, Class<T> clazz, int index, NBTDecoder<T> decoder, EntityDataType<NBTCompound> type) {
super(name, defaultValue, clazz);
this.decoder = decoder;
this.index = index;
this.type = type;
}
@SuppressWarnings("unchecked")
public NBTProperty(String name, T defaultValue, int index, NBTDecoder<T> decoder) {
this(name, defaultValue, (Class<T>) defaultValue.getClass(), index, decoder, EntityDataTypes.NBT);
}
@SuppressWarnings("unchecked")
public NBTProperty(String name, T defaultValue, int index, NBTDecoder<T> decoder, EntityDataType<NBTCompound> type) {
this(name, defaultValue, (Class<T>) defaultValue.getClass(), index, decoder, type);
}
public NBTProperty(String name, Class<T> clazz, int index, NBTDecoder<T> decoder) {
this(name, null, clazz, index, decoder, EntityDataTypes.NBT);
}
@Override
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData> properties) {
T value = entity.getProperty(this);
if (value == null) return;
properties.put(index, newEntityData(index, type, decoder.decode(value)));
}
public interface NBTDecoder<T> {
NBTCompound decode(T obj);
}
}

@ -0,0 +1,41 @@
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 com.github.retrooper.packetevents.util.adventure.AdventureSerializer;
import lol.pyr.znpcsplus.entity.EntityPropertyImpl;
import lol.pyr.znpcsplus.entity.PacketEntity;
import lol.pyr.znpcsplus.util.PapiUtil;
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 optional;
public NameProperty(boolean legacy, boolean optional) {
super("name", null, Component.class);
this.legacy = legacy;
this.optional = optional;
}
@Override
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData> properties) {
Component value = entity.getProperty(this);
if (value != null) {
String serialized = legacy ?
AdventureSerializer.getLegacyGsonSerializer().serialize(value) :
AdventureSerializer.getGsonSerializer().serialize(value);
serialized = PapiUtil.set(player, serialized);
if (optional) 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));
}
}

@ -0,0 +1,23 @@
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 com.github.retrooper.packetevents.protocol.entity.pose.EntityPose;
import lol.pyr.znpcsplus.entity.EntityPropertyImpl;
import lol.pyr.znpcsplus.entity.PacketEntity;
import lol.pyr.znpcsplus.util.NpcPose;
import org.bukkit.entity.Player;
import java.util.Map;
public class NpcPoseProperty extends EntityPropertyImpl<NpcPose> {
public NpcPoseProperty() {
super("pose", NpcPose.STANDING, NpcPose.class);
}
@Override
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData> properties) {
properties.put(6, newEntityData(6, EntityDataTypes.ENTITY_POSE, EntityPose.valueOf(entity.getProperty(this).name())));
}
}

@ -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 lol.pyr.znpcsplus.util.Vector3f;
import org.bukkit.entity.Player;
import java.util.Map;
public class RotationProperty extends EntityPropertyImpl<Vector3f> {
private final int index;
public RotationProperty(String name, int index, Vector3f defaultValue) {
super(name, defaultValue, Vector3f.class);
this.index = index;
}
@Override
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData> properties) {
Vector3f vec = entity.getProperty(this);
properties.put(index, newEntityData(index, EntityDataTypes.ROTATION, new com.github.retrooper.packetevents.util.Vector3f(vec.getX(), vec.getY(), vec.getZ())));
}
}

@ -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.npc.NpcEntryImpl;
import org.bukkit.entity.Player;
import java.util.Map;
public class TargetNpcProperty extends EntityPropertyImpl<NpcEntryImpl> {
private final int index;
public TargetNpcProperty(String name, int index, NpcEntryImpl defaultValue) {
super(name, defaultValue, NpcEntryImpl.class);
this.index = index;
}
@Override
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData> properties) {
NpcEntryImpl value = entity.getProperty(this);
if (value == null) return;
if (value.getNpc().getEntity().getEntityId() == entity.getEntityId()) return;
if (value.getNpc().isVisibleTo(player)) {
properties.put(index, newEntityData(index, EntityDataTypes.INT, value.getNpc().getEntity().getEntityId()));
}
}
}

@ -0,0 +1,31 @@
package lol.pyr.znpcsplus.entity.properties.villager;
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.villager.VillagerData;
import com.github.retrooper.packetevents.protocol.entity.villager.profession.VillagerProfessions;
import com.github.retrooper.packetevents.protocol.entity.villager.type.VillagerTypes;
import lol.pyr.znpcsplus.entity.EntityPropertyImpl;
import lol.pyr.znpcsplus.entity.PacketEntity;
import org.bukkit.entity.Player;
import java.util.Map;
public abstract class VillagerDataProperty<T> extends EntityPropertyImpl<T> {
private final int index;
@SuppressWarnings("unchecked")
public VillagerDataProperty(String name, int index, T def) {
super(name, def, (Class<T>) def.getClass());
this.index = index;
}
@Override
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData> properties) {
EntityData oldData = properties.get(index);
VillagerData old = oldData == null ? new VillagerData(VillagerTypes.PLAINS, VillagerProfessions.NONE, 1) : (VillagerData) oldData.getValue();
properties.put(index, newEntityData(index, EntityDataTypes.VILLAGER_DATA, apply(old, entity.getProperty(this))));
}
protected abstract VillagerData apply(VillagerData data, T value);
}

@ -0,0 +1,16 @@
package lol.pyr.znpcsplus.entity.properties.villager;
import com.github.retrooper.packetevents.protocol.entity.villager.VillagerData;
import lol.pyr.znpcsplus.util.VillagerLevel;
public class VillagerLevelProperty extends VillagerDataProperty<VillagerLevel> {
public VillagerLevelProperty(String name, int index, VillagerLevel def) {
super(name, index, def);
}
@Override
protected VillagerData apply(VillagerData data, VillagerLevel value) {
data.setLevel(value.ordinal() + 1);
return data;
}
}

@ -0,0 +1,17 @@
package lol.pyr.znpcsplus.entity.properties.villager;
import com.github.retrooper.packetevents.protocol.entity.villager.VillagerData;
import com.github.retrooper.packetevents.protocol.entity.villager.profession.VillagerProfessions;
import lol.pyr.znpcsplus.util.VillagerProfession;
public class VillagerProfessionProperty extends VillagerDataProperty<VillagerProfession> {
public VillagerProfessionProperty(String name, int index, VillagerProfession def) {
super(name, index, def);
}
@Override
protected VillagerData apply(VillagerData data, VillagerProfession value) {
data.setProfession(VillagerProfessions.getById(value.getId()));
return data;
}
}

@ -0,0 +1,17 @@
package lol.pyr.znpcsplus.entity.properties.villager;
import com.github.retrooper.packetevents.protocol.entity.villager.VillagerData;
import com.github.retrooper.packetevents.protocol.entity.villager.type.VillagerTypes;
import lol.pyr.znpcsplus.util.VillagerType;
public class VillagerTypeProperty extends VillagerDataProperty<VillagerType> {
public VillagerTypeProperty(String name, int index, VillagerType def) {
super(name, index, def);
}
@Override
protected VillagerData apply(VillagerData data, VillagerType value) {
data.setType(VillagerTypes.getById(value.getId()));
return data;
}
}

@ -0,0 +1,25 @@
package lol.pyr.znpcsplus.entity.serializers;
import lol.pyr.znpcsplus.entity.PropertySerializer;
public class IntegerPropertySerializer implements PropertySerializer<Integer> {
@Override
public String serialize(Integer property) {
return String.valueOf(property);
}
@Override
public Integer deserialize(String property) {
try {
return Integer.parseInt(property);
} catch (NumberFormatException e) {
e.printStackTrace();
}
return null;
}
@Override
public Class<Integer> getTypeClass() {
return Integer.class;
}
}

@ -1,18 +1,19 @@
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;
import org.bukkit.inventory.ItemStack;
public class ItemStackPropertySerializer implements PropertySerializer<ItemStack> {
@Override
public String serialize(ItemStack property) {
return ItemSerializationUtil.itemToB64(property);
return ItemSerializationUtil.itemToB64(SpigotConversionUtil.toBukkitItemStack(property));
}
@Override
public ItemStack deserialize(String property) {
return ItemSerializationUtil.itemFromB64(property);
return SpigotConversionUtil.fromBukkitItemStack(ItemSerializationUtil.itemFromB64(property));
}
@Override

@ -0,0 +1,21 @@
package lol.pyr.znpcsplus.entity.serializers;
import lol.pyr.znpcsplus.entity.PropertySerializer;
import lol.pyr.znpcsplus.npc.NpcEntryImpl;
public class TargetNpcPropertySerializer implements PropertySerializer<NpcEntryImpl> {
@Override
public String serialize(NpcEntryImpl property) {
return property.getId();
}
@Override
public NpcEntryImpl deserialize(String property) {
return null; // TODO: find a way to do this
}
@Override
public Class<NpcEntryImpl> getTypeClass() {
return NpcEntryImpl.class;
}
}

@ -1,10 +1,13 @@
package lol.pyr.znpcsplus.hologram;
import com.github.retrooper.packetevents.protocol.item.ItemStack;
import io.github.retrooper.packetevents.util.SpigotConversionUtil;
import lol.pyr.znpcsplus.api.hologram.Hologram;
import lol.pyr.znpcsplus.config.ConfigManager;
import lol.pyr.znpcsplus.entity.EntityPropertyRegistryImpl;
import lol.pyr.znpcsplus.packets.PacketFactory;
import lol.pyr.znpcsplus.util.Viewable;
import lol.pyr.znpcsplus.util.NpcLocation;
import lol.pyr.znpcsplus.util.Viewable;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import org.bukkit.entity.Player;
@ -17,46 +20,75 @@ public class HologramImpl extends Viewable implements Hologram {
private final ConfigManager configManager;
private final PacketFactory packetFactory;
private final LegacyComponentSerializer textSerializer;
private final EntityPropertyRegistryImpl propertyRegistry;
private double offset = 0.0;
private long refreshDelay = -1;
private long lastRefresh = System.currentTimeMillis();
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.packetFactory = packetFactory;
this.textSerializer = textSerializer;
this.location = location;
}
public void addLineComponent(Component line) {
HologramLine newLine = new HologramLine(packetFactory, null, line);
public void addTextLineComponent(Component line) {
HologramText newLine = new HologramText(propertyRegistry, packetFactory, null, line);
lines.add(newLine);
relocateLines(newLine);
for (Player viewer : getViewers()) newLine.show(viewer.getPlayer());
}
public void addTextLine(String line) {
addTextLineComponent(textSerializer.deserialize(line));
}
public void addItemLineStack(org.bukkit.inventory.ItemStack item) {
addItemLinePEStack(SpigotConversionUtil.fromBukkitItemStack(item));
}
public void addItemLine(String serializedItem) {
addItemLinePEStack(HologramItem.deserialize(serializedItem));
}
public void addItemLinePEStack(ItemStack item) {
HologramItem newLine = new HologramItem(propertyRegistry, packetFactory, null, item);
lines.add(newLine);
relocateLines(newLine);
for (Player viewer : getViewers()) newLine.show(viewer.getPlayer());
}
public void addLine(String line) {
addLineComponent(textSerializer.deserialize(line));
if (line.toLowerCase().startsWith("item:")) {
addItemLine(line.substring(5));
} else {
addTextLine(line);
}
}
public Component getLineComponent(int index) {
return lines.get(index).getText();
public Component getLineTextComponent(int index) {
return ((HologramText) lines.get(index)).getValue();
}
public String getLine(int index) {
return textSerializer.serialize(getLineComponent(index));
if (lines.get(index) instanceof HologramItem) {
return ((HologramItem) lines.get(index)).serialize();
} else {
return textSerializer.serialize(getLineTextComponent(index));
}
}
public void removeLine(int index) {
HologramLine line = lines.remove(index);
HologramLine<?> line = lines.remove(index);
for (Player viewer : getViewers()) line.hide(viewer);
relocateLines();
}
public List<HologramLine> getLines() {
public List<HologramLine<?>> getLines() {
return Collections.unmodifiableList(lines);
}
@ -65,25 +97,48 @@ public class HologramImpl extends Viewable implements Hologram {
lines.clear();
}
public void insertLineComponent(int index, Component line) {
HologramLine newLine = new HologramLine(packetFactory, null, line);
public void insertTextLineComponent(int index, Component line) {
HologramText newLine = new HologramText(propertyRegistry, packetFactory, null, line);
lines.add(index, newLine);
relocateLines(newLine);
for (Player viewer : getViewers()) newLine.show(viewer.getPlayer());
}
public void insertTextLine(int index, String line) {
insertTextLineComponent(index, textSerializer.deserialize(line));
}
public void insertItemLineStack(int index, org.bukkit.inventory.ItemStack item) {
insertItemLinePEStack(index, SpigotConversionUtil.fromBukkitItemStack(item));
}
public void insertItemLinePEStack(int index, ItemStack item) {
HologramItem newLine = new HologramItem(propertyRegistry, packetFactory, null, item);
lines.add(index, newLine);
relocateLines(newLine);
for (Player viewer : getViewers()) newLine.show(viewer.getPlayer());
}
public void insertItemLine(int index, String item) {
insertItemLinePEStack(index, HologramItem.deserialize(item));
}
public void insertLine(int index, String line) {
insertLineComponent(index, textSerializer.deserialize(line));
if (line.toLowerCase().startsWith("item:")) {
insertItemLine(index, line.substring(5));
} else {
insertTextLine(index, line);
}
}
@Override
protected void UNSAFE_show(Player player) {
for (HologramLine line : lines) line.show(player);
for (HologramLine<?> line : lines) line.show(player);
}
@Override
protected void UNSAFE_hide(Player player) {
for (HologramLine line : lines) line.hide(player);
for (HologramLine<?> line : lines) line.hide(player);
}
public long getRefreshDelay() {
@ -100,7 +155,7 @@ public class HologramImpl extends Viewable implements Hologram {
public void refresh() {
lastRefresh = System.currentTimeMillis();
for (HologramLine line : lines) for (Player viewer : getViewers()) line.refreshMeta(viewer);
for (HologramLine<?> line : lines) for (Player viewer : getViewers()) line.refreshMeta(viewer);
}
public void setLocation(NpcLocation location) {
@ -112,10 +167,10 @@ public class HologramImpl extends Viewable implements Hologram {
relocateLines(null);
}
private void relocateLines(HologramLine newLine) {
private void relocateLines(HologramLine<?> newLine) {
final double lineSpacing = configManager.getConfig().lineSpacing();
double height = location.getY() + (lines.size() - 1) * lineSpacing + getOffset();
for (HologramLine line : lines) {
for (HologramLine<?> line : lines) {
line.setLocation(location.withY(height), line == newLine ? Collections.emptySet() : getViewers());
height -= lineSpacing;
}

@ -0,0 +1,111 @@
package lol.pyr.znpcsplus.hologram;
import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes;
import com.github.retrooper.packetevents.protocol.item.ItemStack;
import com.github.retrooper.packetevents.protocol.item.type.ItemType;
import com.github.retrooper.packetevents.protocol.item.type.ItemTypes;
import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
import com.github.retrooper.packetevents.protocol.nbt.NBTInt;
import com.github.retrooper.packetevents.protocol.nbt.NBTNumber;
import com.github.retrooper.packetevents.protocol.nbt.codec.NBTCodec;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
import com.google.gson.JsonSyntaxException;
import lol.pyr.znpcsplus.api.entity.EntityProperty;
import lol.pyr.znpcsplus.entity.EntityPropertyRegistryImpl;
import lol.pyr.znpcsplus.packets.PacketFactory;
import lol.pyr.znpcsplus.util.NpcLocation;
import org.bukkit.entity.Player;
import java.util.Collection;
public class HologramItem extends HologramLine<ItemStack> {
public HologramItem(EntityPropertyRegistryImpl propertyRegistry, PacketFactory packetFactory, NpcLocation location, ItemStack item) {
super(item, packetFactory, EntityTypes.ITEM, location);
addProperty(propertyRegistry.getByName("holo_item"));
}
@SuppressWarnings("unchecked")
@Override
public <T> T getProperty(EntityProperty<T> key) {
if (key.getName().equalsIgnoreCase("holo_item")) return (T) getValue();
return super.getProperty(key);
}
@Override
public void setLocation(NpcLocation location, Collection<Player> viewers) {
super.setLocation(location.withY(location.getY() + 2.05), viewers);
}
public static boolean ensureValidItemInput(String in) {
if (in == null || in.isEmpty()) {
return false;
}
int indexOfNbt = in.indexOf("{");
if (indexOfNbt != -1) {
String typeName = in.substring(0, indexOfNbt);
ItemType type = ItemTypes.getByName("minecraft:" + typeName.toLowerCase());
if (type == null) {
return false;
}
String nbtString = in.substring(indexOfNbt);
return ensureValidNbt(nbtString);
} else {
ItemType type = ItemTypes.getByName("minecraft:" + in.toLowerCase());
return type != null;
}
}
private static boolean ensureValidNbt(String nbtString) {
JsonElement nbtJson;
try {
nbtJson = JsonParser.parseString(nbtString);
} catch (JsonSyntaxException e) {
return false;
}
try {
NBTCodec.jsonToNBT(nbtJson);
} catch (Exception ignored) {
return false;
}
return true;
}
public static ItemStack deserialize(String serializedItem) {
int indexOfNbt = serializedItem.indexOf("{");
String typeName = serializedItem;
int amount = 1;
NBTCompound nbt = new NBTCompound();
if (indexOfNbt != -1) {
typeName = serializedItem.substring(0, indexOfNbt);
String nbtString = serializedItem.substring(indexOfNbt);
JsonElement nbtJson = null;
try {
nbtJson = JsonParser.parseString(nbtString);
} catch (Exception ignored) {
}
if (nbtJson != null) {
nbt = (NBTCompound) NBTCodec.jsonToNBT(nbtJson);
NBTNumber nbtAmount = nbt.getNumberTagOrNull("Count");
if (nbtAmount != null) {
nbt.removeTag("Count");
amount = nbtAmount.getAsInt();
if (amount <= 0) amount = 1;
if (amount > 127) amount = 127;
}
}
}
ItemType type = ItemTypes.getByName("minecraft:" + typeName.toLowerCase());
if (type == null) type = ItemTypes.STONE;
return ItemStack.builder().type(type).amount(amount).nbt(nbt).build();
}
public String serialize() {
NBTCompound nbt = getValue().getNBT();
if (nbt == null) nbt = new NBTCompound();
if (getValue().getAmount() > 1) nbt.setTag("Count", new NBTInt(getValue().getAmount()));
if (nbt.isEmpty()) return "item:" + getValue().getType().getName().toString().replace("minecraft:", "");
return "item:" + getValue().getType().getName().toString().replace("minecraft:", "") + NBTCodec.nbtToJson(nbt, true);
}
}

@ -1,64 +1,77 @@
package lol.pyr.znpcsplus.hologram;
import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes;
import com.github.retrooper.packetevents.protocol.entity.type.EntityType;
import lol.pyr.znpcsplus.api.entity.EntityProperty;
import lol.pyr.znpcsplus.api.entity.PropertyHolder;
import lol.pyr.znpcsplus.entity.PacketEntity;
import lol.pyr.znpcsplus.packets.PacketFactory;
import lol.pyr.znpcsplus.util.NpcLocation;
import net.kyori.adventure.text.Component;
import org.bukkit.entity.Player;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
public class HologramLine implements PropertyHolder {
private Component text;
private final PacketEntity armorStand;
public class HologramLine<M> implements PropertyHolder {
private M value;
private final PacketEntity entity;
private final Set<EntityProperty<?>> properties;
public HologramLine(PacketFactory packetFactory, NpcLocation location, Component text) {
this.text = text;
armorStand = new PacketEntity(packetFactory, this, EntityTypes.ARMOR_STAND, location);
public HologramLine(M value, PacketFactory packetFactory, EntityType type, NpcLocation location) {
this.value = value;
this.entity = new PacketEntity(packetFactory, this, type, location);
this.properties = new HashSet<>();
}
public Component getText() {
return text;
public M getValue() {
return value;
}
public void setText(Component text) {
this.text = text;
public void setValue(M value) {
this.value = value;
}
public void refreshMeta(Player player) {
armorStand.refreshMeta(player);
entity.refreshMeta(player);
}
protected void show(Player player) {
armorStand.spawn(player);
entity.spawn(player);
}
protected void hide(Player player) {
armorStand.despawn(player);
entity.despawn(player);
}
public void setLocation(NpcLocation location, Collection<Player> viewers) {
armorStand.setLocation(location, viewers);
entity.setLocation(location, viewers);
}
public int getEntityId() {
return entity.getEntityId();
}
public <T> void addProperty(EntityProperty<T> property) {
properties.add(property);
}
@SuppressWarnings("unchecked")
@Override
public <T> T getProperty(EntityProperty<T> key) {
if (key.getName().equalsIgnoreCase("invisible")) return (T) Boolean.TRUE;
if (key.getName().equalsIgnoreCase("name")) return (T) text;
return key.getDefaultValue();
}
@Override
public boolean hasProperty(EntityProperty<?> key) {
return key.getName().equalsIgnoreCase("name") || key.getName().equalsIgnoreCase("invisible");
return properties.contains(key);
}
@Override
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 line");
}
@Override
public Set<EntityProperty<?>> getAppliedProperties() {
return properties;
}
}

@ -0,0 +1,30 @@
package lol.pyr.znpcsplus.hologram;
import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes;
import lol.pyr.znpcsplus.api.entity.EntityProperty;
import lol.pyr.znpcsplus.entity.EntityPropertyRegistryImpl;
import lol.pyr.znpcsplus.packets.PacketFactory;
import lol.pyr.znpcsplus.util.NpcLocation;
import net.kyori.adventure.text.Component;
public class HologramText extends HologramLine<Component> {
public HologramText(EntityPropertyRegistryImpl propertyRegistry, PacketFactory packetFactory, NpcLocation location, Component text) {
super(text, packetFactory, EntityTypes.ARMOR_STAND, location);
addProperty(propertyRegistry.getByName("name"));
addProperty(propertyRegistry.getByName("invisible"));
}
@SuppressWarnings("unchecked")
@Override
public <T> T getProperty(EntityProperty<T> key) {
if (key.getName().equalsIgnoreCase("invisible")) return (T) Boolean.TRUE;
if (key.getName().equalsIgnoreCase("name")) return (T) getValue();
return super.getProperty(key);
}
@Override
public boolean hasProperty(EntityProperty<?> key) {
return key.getName().equalsIgnoreCase("name") || key.getName().equalsIgnoreCase("invisible");
}
}

@ -1,102 +0,0 @@
package lol.pyr.znpcsplus.metadata;
import com.github.retrooper.packetevents.protocol.entity.data.EntityData;
import com.github.retrooper.packetevents.protocol.entity.pose.EntityPose;
import lol.pyr.znpcsplus.util.*;
import net.kyori.adventure.text.Component;
import org.bukkit.DyeColor;
/**
* 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>
*/
public interface MetadataFactory {
EntityData effects(boolean onFire, boolean glowing, boolean invisible, boolean usingElytra, boolean usingItemLegacy);
EntityData silent(boolean enabled);
EntityData name(Component name);
EntityData nameShown();
EntityData noGravity();
EntityData pose(EntityPose pose);
EntityData shaking(boolean enabled);
EntityData usingItem(boolean enabled, boolean offhand, boolean riptide);
EntityData potionColor(int color);
EntityData potionAmbient(boolean ambient);
// Player
EntityData skinLayers(boolean cape, boolean jacket, boolean leftSleeve, boolean rightSleeve, boolean leftLeg, boolean rightLeg, boolean hat);
EntityData shoulderEntityLeft(ParrotVariant variant);
EntityData shoulderEntityRight(ParrotVariant variant);
// Armor Stand
EntityData armorStandProperties(boolean small, boolean arms, boolean noBasePlate);
EntityData armorStandHeadRotation(Vector3f headRotation);
EntityData armorStandBodyRotation(Vector3f bodyRotation);
EntityData armorStandLeftArmRotation(Vector3f leftArmRotation);
EntityData armorStandRightArmRotation(Vector3f rightArmRotation);
EntityData armorStandLeftLegRotation(Vector3f leftLegRotation);
EntityData armorStandRightLegRotation(Vector3f rightLegRotation);
// Axolotl
EntityData axolotlVariant(int variant);
EntityData playingDead(boolean playingDead);
// Bat
EntityData batHanging(boolean hanging);
// Bee
EntityData beeAngry(boolean angry);
EntityData beeHasNectar(boolean hasNectar);
// Blaze
EntityData blazeOnFire(boolean onFire);
// Cat
EntityData catVariant(CatVariant variant);
EntityData catLying(boolean lying);
EntityData catTamed(boolean tamed);
EntityData catCollarColor(DyeColor collarColor);
// Creeper
EntityData creeperState(CreeperState state);
EntityData creeperCharged(boolean charged);
// Enderman
EntityData endermanHeldBlock(int heldBlock);
EntityData endermanScreaming(boolean screaming);
EntityData endermanStaring(boolean staring);
// Evoker
EntityData evokerSpell(int spell);
// Fox
EntityData foxVariant(int variant);
EntityData foxProperties(boolean sitting, boolean crouching, boolean sleeping, boolean facePlanted);
// Frog
EntityData frogVariant(int variant);
// Ghast
EntityData ghastAttacking(boolean attacking);
// Goat
EntityData goatHasLeftHorn(boolean hasLeftHorn);
EntityData goatHasRightHorn(boolean hasRightHorn);
// Guardian
// Hoglin
EntityData hoglinImmuneToZombification(boolean immuneToZombification);
// Villager
EntityData villagerData(int type, int profession, int level);
}

@ -1,93 +0,0 @@
package lol.pyr.znpcsplus.metadata;
import com.github.retrooper.packetevents.protocol.entity.data.EntityData;
import com.github.retrooper.packetevents.protocol.entity.data.EntityDataTypes;
import lol.pyr.znpcsplus.util.CreeperState;
import lol.pyr.znpcsplus.util.Vector3f;
public class V1_10MetadataFactory extends V1_9MetadataFactory {
@Override
public EntityData skinLayers(boolean cape, boolean jacket, boolean leftSleeve, boolean rightSleeve, boolean leftLeg, boolean rightLeg, boolean hat) {
return createSkinLayers(13, cape, jacket, leftSleeve, rightSleeve, leftLeg, rightLeg, hat);
}
@Override
public EntityData noGravity() {
return newEntityData(5, EntityDataTypes.BOOLEAN, true);
}
@Override
public EntityData potionColor(int color) {
return newEntityData(8, EntityDataTypes.INT, color);
}
@Override
public EntityData potionAmbient(boolean ambient) {
return newEntityData(9, EntityDataTypes.BOOLEAN, ambient);
}
@Override
public EntityData armorStandProperties(boolean small, boolean arms, boolean noBasePlate) {
return newEntityData(11, EntityDataTypes.BYTE, (byte) ((small ? 0x01 : 0) | (arms ? 0x04 : 0) | (noBasePlate ? 0x08 : 0)));
}
@Override
public EntityData armorStandHeadRotation(Vector3f headRotation) {
return createRotations(12, headRotation);
}
@Override
public EntityData armorStandBodyRotation(Vector3f bodyRotation) {
return createRotations(13, bodyRotation);
}
@Override
public EntityData armorStandLeftArmRotation(Vector3f leftArmRotation) {
return createRotations(14, leftArmRotation);
}
@Override
public EntityData armorStandRightArmRotation(Vector3f rightArmRotation) {
return createRotations(15, rightArmRotation);
}
@Override
public EntityData armorStandLeftLegRotation(Vector3f leftLegRotation) {
return createRotations(16, leftLegRotation);
}
@Override
public EntityData armorStandRightLegRotation(Vector3f rightLegRotation) {
return createRotations(17, rightLegRotation);
}
@Override
public EntityData batHanging(boolean hanging) {
return newEntityData(12, EntityDataTypes.BYTE, (byte) (hanging ? 0x01 : 0));
}
@Override
public EntityData blazeOnFire(boolean onFire) {
return newEntityData(12, EntityDataTypes.BYTE, (byte) (onFire ? 0x01 : 0));
}
@Override
public EntityData creeperState(CreeperState state) {
return newEntityData(12, EntityDataTypes.INT, state.getState());
}
@Override
public EntityData creeperCharged(boolean charged) {
return newEntityData(13, EntityDataTypes.BOOLEAN, charged);
}
@Override
public EntityData ghastAttacking(boolean attacking) {
return newEntityData(12, EntityDataTypes.BOOLEAN, attacking);
}
@Override
public EntityData villagerData(int type, int profession, int level) {
return newEntityData(13, EntityDataTypes.INT, profession);
}
}

@ -1,21 +0,0 @@
package lol.pyr.znpcsplus.metadata;
import com.github.retrooper.packetevents.protocol.entity.data.EntityData;
import com.github.retrooper.packetevents.protocol.entity.data.EntityDataTypes;
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
public EntityData usingItem(boolean usingItem, boolean offHand, boolean riptide) {
return newEntityData(6, EntityDataTypes.BYTE, (byte) ((usingItem ? 0x01 : 0) | (offHand ? 0x02 : 0)));
}
@Override
public EntityData evokerSpell(int spell) {
return newEntityData(12, EntityDataTypes.BYTE, (byte) spell);
}
}

@ -1,32 +0,0 @@
package lol.pyr.znpcsplus.metadata;
import com.github.retrooper.packetevents.protocol.entity.data.EntityData;
import com.github.retrooper.packetevents.protocol.entity.data.EntityDataTypes;
import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
import lol.pyr.znpcsplus.entity.ParrotNBTCompound;
import lol.pyr.znpcsplus.util.ParrotVariant;
public class V1_12MetadataFactory extends V1_11MetadataFactory {
@Override
public EntityData shoulderEntityLeft(ParrotVariant variant) {
return createShoulderEntityLeft(15, variant);
}
public EntityData createShoulderEntityLeft(int index, ParrotVariant variant) {
return newEntityData(index, EntityDataTypes.NBT, variant == ParrotVariant.NONE ? new NBTCompound() : new ParrotNBTCompound(variant).getTag());
}
@Override
public EntityData shoulderEntityRight(ParrotVariant variant) {
return createShoulderEntityRight(16, variant);
}
public EntityData createShoulderEntityRight(int index, ParrotVariant variant) {
return newEntityData(index, EntityDataTypes.NBT, variant == ParrotVariant.NONE ? new NBTCompound() : new ParrotNBTCompound(variant).getTag());
}
@Override
public EntityData evokerSpell(int spell) {
return newEntityData(13, EntityDataTypes.BYTE, (byte) spell);
}
}

@ -1,20 +0,0 @@
package lol.pyr.znpcsplus.metadata;
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 net.kyori.adventure.text.Component;
import java.util.Optional;
public class V1_13MetadataFactory extends V1_12MetadataFactory {
@Override
public EntityData name(Component name) {
return newEntityData(2, EntityDataTypes.OPTIONAL_COMPONENT, Optional.of(AdventureSerializer.getGsonSerializer().serialize(name)));
}
@Override
public EntityData usingItem(boolean usingItem, boolean offHand, boolean riptide) {
return newEntityData(6, EntityDataTypes.BYTE, (byte) ((usingItem ? 0x01 : 0) | (offHand ? 0x02 : 0) | (riptide ? 0x04 : 0)));
}
}

@ -1,143 +0,0 @@
package lol.pyr.znpcsplus.metadata;
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.pose.EntityPose;
import com.github.retrooper.packetevents.protocol.entity.villager.VillagerData;
import lol.pyr.znpcsplus.util.CatVariant;
import lol.pyr.znpcsplus.util.CreeperState;
import lol.pyr.znpcsplus.util.ParrotVariant;
import lol.pyr.znpcsplus.util.Vector3f;
import org.bukkit.DyeColor;
public class V1_14MetadataFactory extends V1_13MetadataFactory {
@Override
public EntityData skinLayers(boolean cape, boolean jacket, boolean leftSleeve, boolean rightSleeve, boolean leftLeg, boolean rightLeg, boolean hat) {
return createSkinLayers(15, cape, jacket, leftSleeve, rightSleeve, leftLeg, rightLeg, hat);
}
@Override
public EntityData pose(EntityPose pose) {
return newEntityData(6, EntityDataTypes.ENTITY_POSE, pose);
}
@Override
public EntityData usingItem(boolean usingItem, boolean offHand, boolean riptide) {
return newEntityData(7, EntityDataTypes.BYTE, (byte) ((usingItem ? 0x01 : 0) | (offHand ? 0x02 : 0) | (riptide ? 0x04 : 0)));
}
@Override
public EntityData potionColor(int color) {
return newEntityData(9, EntityDataTypes.INT, color);
}
@Override
public EntityData potionAmbient(boolean ambient) {
return newEntityData(10, EntityDataTypes.BOOLEAN, ambient);
}
@Override
public EntityData shoulderEntityLeft(ParrotVariant variant) {
return createShoulderEntityLeft(17, variant);
}
@Override
public EntityData shoulderEntityRight(ParrotVariant variant) {
return createShoulderEntityRight(18, variant);
}
@Override
public EntityData armorStandProperties(boolean small, boolean arms, boolean noBasePlate) {
return newEntityData(13, EntityDataTypes.BYTE, (byte) ((small ? 0x01 : 0) | (arms ? 0x04 : 0) | (noBasePlate ? 0x08 : 0)));
}
@Override
public EntityData armorStandHeadRotation(Vector3f headRotation) {
return createRotations(14, headRotation);
}
@Override
public EntityData armorStandBodyRotation(Vector3f bodyRotation) {
return createRotations(15, bodyRotation);
}
@Override
public EntityData armorStandLeftArmRotation(Vector3f leftArmRotation) {
return createRotations(16, leftArmRotation);
}
@Override
public EntityData armorStandRightArmRotation(Vector3f rightArmRotation) {
return createRotations(17, rightArmRotation);
}
@Override
public EntityData armorStandLeftLegRotation(Vector3f leftLegRotation) {
return createRotations(18, leftLegRotation);
}
@Override
public EntityData armorStandRightLegRotation(Vector3f rightLegRotation) {
return createRotations(19, rightLegRotation);
}
@Override
public EntityData batHanging(boolean hanging) {
return newEntityData(14, EntityDataTypes.BYTE, (byte) (hanging ? 0x01 : 0));
}
@Override
public EntityData blazeOnFire(boolean onFire) {
return newEntityData(14, EntityDataTypes.BYTE, (byte) (onFire ? 0x01 : 0));
}
@Override
public EntityData catVariant(CatVariant variant) {
return newEntityData(17, EntityDataTypes.CAT_VARIANT, variant.getId());
}
@Override
public EntityData catLying(boolean lying) {
throw new UnsupportedOperationException("The cat lying entity data isn't supported on this version");
}
@Override
public EntityData catCollarColor(DyeColor collarColor) {
return newEntityData(20, EntityDataTypes.INT, collarColor.ordinal());
}
@Override
public EntityData creeperState(CreeperState state) {
return newEntityData(14, EntityDataTypes.INT, state.getState());
}
@Override
public EntityData creeperCharged(boolean charged) {
return newEntityData(15, EntityDataTypes.BOOLEAN, charged);
}
@Override
public EntityData evokerSpell(int spell) {
return newEntityData(15, EntityDataTypes.BYTE, (byte) spell);
}
@Override
public EntityData foxVariant(int variant) {
return newEntityData(15, EntityDataTypes.INT, variant);
}
@Override
public EntityData foxProperties(boolean sitting, boolean crouching, boolean sleeping, boolean facePlanted) {
return newEntityData(16, EntityDataTypes.BYTE, (byte) ((sitting ? 0x01 : 0) | (crouching ? 0x04 : 0) | (sleeping ? 0x20 : 0)));
}
@Override
public EntityData ghastAttacking(boolean attacking) {
return newEntityData(14, EntityDataTypes.BOOLEAN, attacking);
}
@Override
public EntityData villagerData(int type, int profession, int level) {
return newEntityData(16, EntityDataTypes.VILLAGER_DATA, new VillagerData(type, profession, level));
}
}

@ -1,127 +0,0 @@
package lol.pyr.znpcsplus.metadata;
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.villager.VillagerData;
import lol.pyr.znpcsplus.util.CatVariant;
import lol.pyr.znpcsplus.util.CreeperState;
import lol.pyr.znpcsplus.util.ParrotVariant;
import lol.pyr.znpcsplus.util.Vector3f;
import org.bukkit.DyeColor;
public class V1_15MetadataFactory extends V1_14MetadataFactory {
@Override
public EntityData shoulderEntityLeft(ParrotVariant variant) {
return createShoulderEntityLeft(18, variant);
}
@Override
public EntityData shoulderEntityRight(ParrotVariant variant) {
return createShoulderEntityRight(19, variant);
}
@Override
public EntityData armorStandProperties(boolean small, boolean arms, boolean noBasePlate) {
return newEntityData(14, EntityDataTypes.BYTE, (byte) ((small ? 0x01 : 0) | (arms ? 0x04 : 0) | (!noBasePlate ? 0x08 : 0)));
}
@Override
public EntityData armorStandHeadRotation(Vector3f headRotation) {
return createRotations(15, headRotation);
}
@Override
public EntityData armorStandBodyRotation(Vector3f bodyRotation) {
return createRotations(16, bodyRotation);
}
@Override
public EntityData armorStandLeftArmRotation(Vector3f leftArmRotation) {
return createRotations(17, leftArmRotation);
}
@Override
public EntityData armorStandRightArmRotation(Vector3f rightArmRotation) {
return createRotations(18, rightArmRotation);
}
@Override
public EntityData armorStandLeftLegRotation(Vector3f leftLegRotation) {
return createRotations(19, leftLegRotation);
}
@Override
public EntityData armorStandRightLegRotation(Vector3f rightLegRotation) {
return createRotations(20, rightLegRotation);
}
@Override
public EntityData batHanging(boolean hanging) {
return newEntityData(15, EntityDataTypes.BYTE, (byte) (hanging ? 0x01 : 0));
}
@Override
public EntityData beeAngry(boolean angry) {
return newEntityData(17, EntityDataTypes.INT, angry ? 1 : 0);
}
@Override
public EntityData beeHasNectar(boolean hasNectar) {
return newEntityData(16, EntityDataTypes.BYTE, (byte) (hasNectar ? 0x08 : 0));
}
@Override
public EntityData blazeOnFire(boolean onFire) {
return newEntityData(15, EntityDataTypes.BYTE, (byte) (onFire ? 0x01 : 0));
}
@Override
public EntityData catVariant(CatVariant variant) {
return newEntityData(18, EntityDataTypes.CAT_VARIANT, variant.getId());
}
@Override
public EntityData catLying(boolean lying) {
return newEntityData(19, EntityDataTypes.BOOLEAN, lying);
}
@Override
public EntityData catCollarColor(DyeColor collarColor) {
return newEntityData(21, EntityDataTypes.INT, collarColor.ordinal());
}
@Override
public EntityData creeperState(CreeperState state) {
return newEntityData(15, EntityDataTypes.INT, state.getState());
}
@Override
public EntityData creeperCharged(boolean charged) {
return newEntityData(16, EntityDataTypes.BOOLEAN, charged);
}
@Override
public EntityData evokerSpell(int spell) {
return newEntityData(16, EntityDataTypes.BYTE, (byte) spell);
}
@Override
public EntityData foxVariant(int variant) {
return newEntityData(16, EntityDataTypes.INT, variant);
}
@Override
public EntityData foxProperties(boolean sitting, boolean crouching, boolean sleeping, boolean facePlanted) {
return newEntityData(17, EntityDataTypes.BYTE, (byte) ((sitting ? 0x01 : 0) | (crouching ? 0x04 : 0) | (sleeping ? 0x20 : 0) | (facePlanted ? 0x40 : 0)));
}
@Override
public EntityData ghastAttacking(boolean attacking) {
return newEntityData(15, EntityDataTypes.BOOLEAN, attacking);
}
@Override
public EntityData villagerData(int type, int profession, int level) {
return newEntityData(17, EntityDataTypes.VILLAGER_DATA, new VillagerData(type, profession, level));
}
}

@ -1,16 +0,0 @@
package lol.pyr.znpcsplus.metadata;
import com.github.retrooper.packetevents.protocol.entity.data.EntityData;
import com.github.retrooper.packetevents.protocol.entity.data.EntityDataTypes;
public class V1_16MetadataFactory extends V1_15MetadataFactory {
@Override
public EntityData skinLayers(boolean cape, boolean jacket, boolean leftSleeve, boolean rightSleeve, boolean leftLeg, boolean rightLeg, boolean hat) {
return createSkinLayers(16, cape, jacket, leftSleeve, rightSleeve, leftLeg, rightLeg, hat);
}
@Override
public EntityData hoglinImmuneToZombification(boolean immuneToZombification) {
return newEntityData(16, EntityDataTypes.BOOLEAN, immuneToZombification);
}
}

@ -1,202 +0,0 @@
package lol.pyr.znpcsplus.metadata;
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.villager.VillagerData;
import lol.pyr.znpcsplus.util.CatVariant;
import lol.pyr.znpcsplus.util.CreeperState;
import lol.pyr.znpcsplus.util.ParrotVariant;
import lol.pyr.znpcsplus.util.Vector3f;
import org.bukkit.DyeColor;
public class V1_17MetadataFactory extends V1_16MetadataFactory {
@Override
public EntityData skinLayers(boolean cape, boolean jacket, boolean leftSleeve, boolean rightSleeve, boolean leftLeg, boolean rightLeg, boolean hat) {
return createSkinLayers(17, cape, jacket, leftSleeve, rightSleeve, leftLeg, rightLeg, hat);
}
@Override
public EntityData effects(boolean onFire, boolean glowing, boolean invisible, boolean usingElytra, boolean usingItemLegacy) {
return newEntityData(0, EntityDataTypes.BYTE, (byte) ((onFire ? 0x01 : 0) | (invisible ? 0x20 : 0) | (glowing ? 0x40 : 0) | (usingElytra ? 0x80 : 0)));
}
@Override
public EntityData shaking(boolean enabled) {
return newEntityData(7, EntityDataTypes.INT, enabled ? 140 : 0);
}
@Override
public EntityData usingItem(boolean usingItem, boolean offHand, boolean riptide) {
return newEntityData(8, EntityDataTypes.BYTE, (byte) ((usingItem ? 0x01 : 0) | (offHand ? 0x02 : 0) | (riptide ? 0x04 : 0)));
}
@Override
public EntityData potionColor(int color) {
return newEntityData(10, EntityDataTypes.INT, color);
}
@Override
public EntityData potionAmbient(boolean ambient) {
return newEntityData(11, EntityDataTypes.BOOLEAN, ambient);
}
@Override
public EntityData shoulderEntityLeft(ParrotVariant variant) {
return createShoulderEntityLeft(19, variant);
}
@Override
public EntityData shoulderEntityRight(ParrotVariant variant) {
return createShoulderEntityRight(20, variant);
}
@Override
public EntityData armorStandProperties(boolean small, boolean arms, boolean noBasePlate) {
return newEntityData(15, EntityDataTypes.BYTE, (byte) ((small ? 0x01 : 0) | (arms ? 0x04 : 0) | (noBasePlate ? 0x08 : 0)));
}
@Override
public EntityData armorStandHeadRotation(Vector3f headRotation) {
return createRotations(16, headRotation);
}
@Override
public EntityData armorStandBodyRotation(Vector3f bodyRotation) {
return createRotations(17, bodyRotation);
}
@Override
public EntityData armorStandLeftArmRotation(Vector3f leftArmRotation) {
return createRotations(18, leftArmRotation);
}
@Override
public EntityData armorStandRightArmRotation(Vector3f rightArmRotation) {
return createRotations(19, rightArmRotation);
}
@Override
public EntityData armorStandLeftLegRotation(Vector3f leftLegRotation) {
return createRotations(20, leftLegRotation);
}
@Override
public EntityData armorStandRightLegRotation(Vector3f rightLegRotation) {
return createRotations(21, rightLegRotation);
}
@Override
public EntityData axolotlVariant(int variant) {
return newEntityData(17, EntityDataTypes.INT, variant);
}
@Override
public EntityData playingDead(boolean playingDead) {
return newEntityData(18, EntityDataTypes.BOOLEAN, playingDead);
}
@Override
public EntityData batHanging(boolean hanging) {
return newEntityData(16, EntityDataTypes.BYTE, (byte) (hanging ? 0x01 : 0));
}
@Override
public EntityData beeAngry(boolean angry) {
return newEntityData(18, EntityDataTypes.INT, angry ? 1 : 0);
}
@Override
public EntityData beeHasNectar(boolean hasNectar) {
return newEntityData(17, EntityDataTypes.BYTE, (byte) (hasNectar ? 0x08 : 0));
}
@Override
public EntityData blazeOnFire(boolean onFire) {
return newEntityData(16, EntityDataTypes.BYTE, (byte) (onFire ? 0x01 : 0));
}
@Override
public EntityData catVariant(CatVariant variant) {
return newEntityData(19, EntityDataTypes.CAT_VARIANT, variant.getId());
}
@Override
public EntityData catLying(boolean lying) {
return newEntityData(20, EntityDataTypes.BOOLEAN, lying);
}
@Override
public EntityData catTamed(boolean tamed) {
return newEntityData(17, EntityDataTypes.BYTE, (byte) (tamed ? 0x04 : 0));
}
@Override
public EntityData catCollarColor(DyeColor collarColor) {
return newEntityData(22, EntityDataTypes.INT, collarColor.ordinal());
}
@Override
public EntityData creeperState(CreeperState state) {
return newEntityData(16, EntityDataTypes.INT, state.getState());
}
@Override
public EntityData creeperCharged(boolean charged) {
return newEntityData(17, EntityDataTypes.BOOLEAN, charged);
}
@Override
public EntityData endermanHeldBlock(int carriedBlock) {
return newEntityData(16, EntityDataTypes.INT, carriedBlock);
}
@Override
public EntityData endermanScreaming(boolean screaming) {
return newEntityData(17, EntityDataTypes.BOOLEAN, screaming);
}
@Override
public EntityData endermanStaring(boolean staring) {
return newEntityData(18, EntityDataTypes.BOOLEAN, staring);
}
@Override
public EntityData evokerSpell(int spell) {
return newEntityData(17, EntityDataTypes.BYTE, (byte) spell);
}
@Override
public EntityData foxVariant(int variant) {
return newEntityData(17, EntityDataTypes.INT, variant);
}
@Override
public EntityData foxProperties(boolean sitting, boolean crouching, boolean sleeping, boolean facePlanted) {
return newEntityData(18, EntityDataTypes.BYTE, (byte) ((sitting ? 0x01 : 0) | (crouching ? 0x04 : 0) | (sleeping ? 0x20 : 0) | (facePlanted ? 0x40 : 0)));
}
@Override
public EntityData ghastAttacking(boolean attacking) {
return newEntityData(16, EntityDataTypes.BOOLEAN, attacking);
}
@Override
public EntityData goatHasLeftHorn(boolean hasLeftHorn) {
return newEntityData(18, EntityDataTypes.BOOLEAN, hasLeftHorn);
}
@Override
public EntityData goatHasRightHorn(boolean hasRightHorn) {
return newEntityData(19, EntityDataTypes.BOOLEAN, hasRightHorn);
}
@Override
public EntityData hoglinImmuneToZombification(boolean immuneToZombification) {
return newEntityData(17, EntityDataTypes.BOOLEAN, immuneToZombification);
}
@Override
public EntityData villagerData(int type, int profession, int level) {
return newEntityData(18, EntityDataTypes.VILLAGER_DATA, new VillagerData(type, profession, level));
}
}

@ -1,12 +0,0 @@
package lol.pyr.znpcsplus.metadata;
import com.github.retrooper.packetevents.protocol.entity.data.EntityData;
import com.github.retrooper.packetevents.protocol.entity.data.EntityDataTypes;
import lol.pyr.znpcsplus.util.FrogVariant;
public class V1_19MetadataFactory extends V1_17MetadataFactory {
@Override
public EntityData frogVariant(int variant) {
return newEntityData(17, EntityDataTypes.FROG_VARIANT, variant);
}
}

@ -1,252 +0,0 @@
package lol.pyr.znpcsplus.metadata;
import com.github.retrooper.packetevents.protocol.entity.data.EntityData;
import com.github.retrooper.packetevents.protocol.entity.data.EntityDataType;
import com.github.retrooper.packetevents.protocol.entity.data.EntityDataTypes;
import com.github.retrooper.packetevents.protocol.entity.pose.EntityPose;
import com.github.retrooper.packetevents.util.adventure.AdventureSerializer;
import lol.pyr.znpcsplus.util.*;
import net.kyori.adventure.text.Component;
import org.bukkit.DyeColor;
public class V1_8MetadataFactory implements MetadataFactory {
@Override
public EntityData skinLayers(boolean cape, boolean jacket, boolean leftSleeve, boolean rightSleeve, boolean leftLeg, boolean rightLeg, boolean hat) {
return createSkinLayers(10, cape, jacket, leftSleeve, rightSleeve, leftLeg, rightLeg, hat);
}
@Override
public EntityData effects(boolean onFire, boolean glowing, boolean invisible, boolean usingElytra, boolean usingItemLegacy) {
return newEntityData(0, EntityDataTypes.BYTE, (byte) ((onFire ? 0x01 : 0) | (usingItemLegacy ? 0x10 : 0) | (invisible ? 0x20 : 0)));
}
@Override
public EntityData name(Component name) {
return newEntityData(2, EntityDataTypes.STRING, AdventureSerializer.getLegacyGsonSerializer().serialize(name));
}
@Override
public EntityData nameShown() {
return newEntityData(3, EntityDataTypes.BYTE, (byte) 1);
}
@Override
public EntityData noGravity() {
throw new UnsupportedOperationException("The gravity entity data isn't supported on this version");
}
@Override
public EntityData pose(EntityPose pose) {
throw new UnsupportedOperationException("The pose entity data isn't supported on this version");
}
@Override
public EntityData shaking(boolean enabled) {
throw new UnsupportedOperationException("The shaking entity data isn't supported on this version");
}
@Override
public EntityData usingItem(boolean enabled, boolean offHand, boolean riptide) {
throw new UnsupportedOperationException("The standalone using item data isn't supported on this version");
}
@Override
public EntityData potionColor(int color) {
return newEntityData(7, EntityDataTypes.INT, color);
}
@Override
public EntityData potionAmbient(boolean ambient) {
return newEntityData(8, EntityDataTypes.BYTE, (byte) (ambient ? 1 : 0));
}
@Override
public EntityData shoulderEntityLeft(ParrotVariant variant) {
throw new UnsupportedOperationException("The shoulder entity data isn't supported on this version");
}
@Override
public EntityData shoulderEntityRight(ParrotVariant variant) {
throw new UnsupportedOperationException("The shoulder entity data isn't supported on this version");
}
@Override
public EntityData armorStandProperties(boolean small, boolean arms, boolean noBasePlate) {
return newEntityData(10, EntityDataTypes.BYTE, (byte) ((small ? 0x01 : 0) | (arms ? 0x04 : 0) | (noBasePlate ? 0x08 : 0)));
}
@Override
public EntityData armorStandHeadRotation(Vector3f headRotation) {
return createRotations(11, headRotation);
}
@Override
public EntityData armorStandBodyRotation(Vector3f bodyRotation) {
return createRotations(12, bodyRotation);
}
@Override
public EntityData armorStandLeftArmRotation(Vector3f leftArmRotation) {
return createRotations(13, leftArmRotation);
}
@Override
public EntityData armorStandRightArmRotation(Vector3f rightArmRotation) {
return createRotations(14, rightArmRotation);
}
@Override
public EntityData armorStandLeftLegRotation(Vector3f leftLegRotation) {
return createRotations(15, leftLegRotation);
}
@Override
public EntityData armorStandRightLegRotation(Vector3f rightLegRotation) {
return createRotations(16, rightLegRotation);
}
@Override
public EntityData axolotlVariant(int variant) {
throw new UnsupportedOperationException("The axolotl variant entity data isn't supported on this version");
}
@Override
public EntityData playingDead(boolean playingDead) {
throw new UnsupportedOperationException("The playing dead entity data isn't supported on this version");
}
@Override
public EntityData batHanging(boolean hanging) {
return newEntityData(16, EntityDataTypes.BYTE, (byte) (hanging ? 1 : 0));
}
@Override
public EntityData beeAngry(boolean angry) {
throw new UnsupportedOperationException("The bee properties entity data isn't supported on this version");
}
@Override
public EntityData beeHasNectar(boolean hasNectar) {
throw new UnsupportedOperationException("The bee properties entity data isn't supported on this version");
}
@Override
public EntityData blazeOnFire(boolean onFire) {
return newEntityData(16, EntityDataTypes.BYTE, (byte) (onFire ? 1 : 0));
}
@Override
public EntityData catVariant(CatVariant variant) {
throw new UnsupportedOperationException("The cat variant entity data isn't supported on this version");
}
@Override
public EntityData catLying(boolean lying) {
throw new UnsupportedOperationException("The cat lying entity data isn't supported on this version");
}
@Override
public EntityData catTamed(boolean tamed) {
throw new UnsupportedOperationException("The cat tamed entity data isn't supported on this version");
}
@Override
public EntityData catCollarColor(DyeColor collarColor) {
throw new UnsupportedOperationException("The cat collar color entity data isn't supported on this version");
}
@Override
public EntityData creeperState(CreeperState state) {
return newEntityData(16, EntityDataTypes.BYTE, (byte) state.getState());
}
@Override
public EntityData creeperCharged(boolean charged) {
return newEntityData(17, EntityDataTypes.BYTE, (byte) (charged ? 1 : 0));
}
@Override
public EntityData endermanHeldBlock(int carriedBlock) {
throw new UnsupportedOperationException("The enderman carried block entity data isn't supported on this version");
}
@Override
public EntityData endermanScreaming(boolean screaming) {
throw new UnsupportedOperationException("The enderman screaming entity data isn't supported on this version");
}
@Override
public EntityData endermanStaring(boolean staring) {
return newEntityData(18, EntityDataTypes.BOOLEAN, staring);
}
@Override
public EntityData evokerSpell(int spell) {
throw new UnsupportedOperationException("The evoker spell entity data isn't supported on this version");
}
@Override
public EntityData foxVariant(int variant) {
throw new UnsupportedOperationException("The fox variant entity data isn't supported on this version");
}
@Override
public EntityData foxProperties(boolean sitting, boolean crouching, boolean sleeping, boolean facePlanted) {
throw new UnsupportedOperationException("The fox properties entity data isn't supported on this version");
}
@Override
public EntityData frogVariant(int variant) {
throw new UnsupportedOperationException("The frog variant entity data isn't supported on this version");
}
@Override
public EntityData ghastAttacking(boolean attacking) {
return newEntityData(16, EntityDataTypes.BYTE, (byte) (attacking ? 1 : 0));
}
@Override
public EntityData goatHasLeftHorn(boolean hasLeftHorn) {
throw new UnsupportedOperationException("The goat horn entity data isn't supported on this version");
}
@Override
public EntityData goatHasRightHorn(boolean hasRightHorn) {
throw new UnsupportedOperationException("The goat horn entity data isn't supported on this version");
}
@Override
public EntityData hoglinImmuneToZombification(boolean immuneToZombification) {
throw new UnsupportedOperationException("The hoglin zombification entity data isn't supported on this version");
}
@Override
public EntityData villagerData(int type, int profession, int level) {
return newEntityData(16, EntityDataTypes.INT, profession);
}
@Override
public EntityData silent(boolean enabled) {
return newEntityData(4, EntityDataTypes.BYTE, (byte) (enabled ? 1 : 0));
}
protected EntityData createSkinLayers(int index, boolean cape, boolean jacket, boolean leftSleeve, boolean rightSleeve, boolean leftLeg, boolean rightLeg, boolean hat) {
return newEntityData(index, EntityDataTypes.BYTE, (byte) (
(cape ? 0x01 : 0) |
(jacket ? 0x02 : 0) |
(leftSleeve ? 0x04 : 0) |
(rightSleeve ? 0x08 : 0) |
(leftLeg ? 0x10 : 0) |
(rightLeg ? 0x20 : 0) |
(hat ? 0x40 : 0))
);
}
protected <T> EntityData newEntityData(int index, EntityDataType<T> type, T value) {
return new EntityData(index, type, value);
}
protected EntityData createRotations(int index, Vector3f rotations) {
return newEntityData(index, EntityDataTypes.ROTATION, new com.github.retrooper.packetevents.util.Vector3f(rotations.getX(), rotations.getY(), rotations.getZ()));
}
}

@ -1,75 +0,0 @@
package lol.pyr.znpcsplus.metadata;
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.util.CreeperState;
import net.kyori.adventure.text.Component;
public class V1_9MetadataFactory extends V1_8MetadataFactory {
@Override
public EntityData skinLayers(boolean cape, boolean jacket, boolean leftSleeve, boolean rightSleeve, boolean leftLeg, boolean rightLeg, boolean hat) {
return createSkinLayers(12, cape, jacket, leftSleeve, rightSleeve, leftLeg, rightLeg, hat);
}
@Override
public EntityData effects(boolean onFire, boolean glowing, boolean invisible, boolean usingElytra, boolean usingItemLegacy) {
return newEntityData(0, EntityDataTypes.BYTE, (byte) (
(onFire ? 0x01 : 0) |
(usingItemLegacy ? 0x10 : 0) |
(invisible ? 0x20 : 0) |
(glowing ? 0x40 : 0) |
(usingElytra ? 0x80 : 0)
));
}
@Override
public EntityData potionAmbient(boolean ambient) {
return newEntityData(8, EntityDataTypes.BOOLEAN, ambient);
}
@Override
public EntityData batHanging(boolean hanging) {
return newEntityData(11, EntityDataTypes.BYTE, (byte) (hanging ? 0x01 : 0));
}
@Override
public EntityData blazeOnFire(boolean onFire) {
return newEntityData(16, EntityDataTypes.BYTE, (byte) (onFire ? 1 : 0));
}
@Override
public EntityData creeperState(CreeperState state) {
return newEntityData(11, EntityDataTypes.INT, state.getState());
}
@Override
public EntityData creeperCharged(boolean charged) {
return newEntityData(12, EntityDataTypes.BOOLEAN, charged);
}
@Override
public EntityData name(Component name) {
return newEntityData(2, EntityDataTypes.STRING, AdventureSerializer.getGsonSerializer().serialize(name));
}
@Override
public EntityData nameShown() {
return newEntityData(3, EntityDataTypes.BOOLEAN, true);
}
@Override
public EntityData silent(boolean enabled) {
return newEntityData(4, EntityDataTypes.BOOLEAN, enabled);
}
@Override
public EntityData ghastAttacking(boolean attacking) {
return newEntityData(11, EntityDataTypes.BOOLEAN, attacking);
}
@Override
public EntityData villagerData(int type, int profession, int level) {
return newEntityData(12, EntityDataTypes.INT, profession);
}
}

@ -1,10 +1,12 @@
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.npc.Npc;
import lol.pyr.znpcsplus.api.npc.NpcType;
import lol.pyr.znpcsplus.config.ConfigManager;
import lol.pyr.znpcsplus.entity.EntityPropertyImpl;
import lol.pyr.znpcsplus.entity.EntityPropertyRegistryImpl;
import lol.pyr.znpcsplus.entity.PacketEntity;
import lol.pyr.znpcsplus.hologram.HologramImpl;
import lol.pyr.znpcsplus.interaction.InteractionActionImpl;
@ -32,18 +34,18 @@ public class NpcImpl extends Viewable implements Npc {
private final Map<EntityPropertyImpl<?>, Object> propertyMap = new HashMap<>();
private final List<InteractionActionImpl> actions = new ArrayList<>();
protected NpcImpl(UUID uuid, ConfigManager configManager, LegacyComponentSerializer textSerializer, World world, NpcTypeImpl type, NpcLocation location, PacketFactory packetFactory) {
this(uuid, configManager, packetFactory, textSerializer, world.getName(), type, location);
protected NpcImpl(UUID uuid, EntityPropertyRegistryImpl propertyRegistry, ConfigManager configManager, LegacyComponentSerializer textSerializer, World world, NpcTypeImpl type, NpcLocation location, PacketFactory packetFactory) {
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.worldName = world;
this.type = type;
this.location = location;
this.uuid = uuid;
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()));
}
@ -118,12 +120,11 @@ public class NpcImpl extends Viewable implements Npc {
hologram.hide(player);
}
private void UNSAFE_refreshMeta() {
for (Player viewer : getViewers()) entity.refreshMeta(viewer);
private <T> void UNSAFE_refreshProperty(EntityPropertyImpl<T> property) {
for (Player viewer : getViewers()) {
List<EntityData> data = property.applyStandalone(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")
@ -141,10 +142,10 @@ public class NpcImpl extends Viewable implements Npc {
}
public <T> void setProperty(EntityPropertyImpl<T> key, T value) {
if (value == null || value.equals(key.getDefaultValue())) removeProperty(key);
if (key == null) return;
if (value == null || value.equals(key.getDefaultValue())) propertyMap.remove(key);
else propertyMap.put(key, value);
UNSAFE_refreshMeta();
if (key.getName().equalsIgnoreCase("glow")) UNSAFE_remakeTeam();
UNSAFE_refreshProperty(key);
}
@SuppressWarnings("unchecked")
@ -152,14 +153,8 @@ public class NpcImpl extends Viewable implements Npc {
setProperty((EntityPropertyImpl<T>) property, (T) value);
}
public void removeProperty(EntityPropertyImpl<?> key) {
propertyMap.remove(key);
UNSAFE_refreshMeta();
if (key.getName().equalsIgnoreCase("glow")) UNSAFE_remakeTeam();
else if (key.getName().equalsIgnoreCase("dinnerbone")) respawn();
}
public Set<EntityPropertyImpl<?>> getAppliedProperties() {
@Override
public Set<EntityProperty<?>> getAppliedProperties() {
return Collections.unmodifiableSet(propertyMap.keySet());
}

@ -22,6 +22,7 @@ public class NpcRegistryImpl implements NpcRegistry {
private final PacketFactory packetFactory;
private final ConfigManager configManager;
private final LegacyComponentSerializer textSerializer;
private final EntityPropertyRegistryImpl propertyRegistry;
private final List<NpcEntryImpl> npcList = new ArrayList<>();
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) {
this.textSerializer = textSerializer;
this.propertyRegistry = propertyRegistry;
storage = configManager.getConfig().storageType().create(configManager, plugin, packetFactory, actionRegistry, typeRegistry, propertyRegistry, textSerializer);
this.packetFactory = packetFactory;
this.configManager = configManager;
@ -101,7 +103,9 @@ public class NpcRegistryImpl implements NpcRegistry {
}
public NpcEntryImpl getByEntityId(int id) {
return npcList.stream().filter(entry -> entry.getNpc().getEntity().getEntityId() == id).findFirst().orElse(null);
return npcList.stream().filter(entry -> entry.getNpc().getEntity().getEntityId() == id ||
entry.getNpc().getHologram().getLines().stream().anyMatch(line -> line.getEntityId() == id)) // Also match the holograms of npcs
.findFirst().orElse(null);
}
public Collection<String> getAllIds() {
@ -134,7 +138,8 @@ public class NpcRegistryImpl implements NpcRegistry {
public NpcEntryImpl create(String id, World world, NpcTypeImpl type, NpcLocation location) {
id = id.toLowerCase();
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);
type.applyDefaultProperties(npc);
NpcEntryImpl entry = new NpcEntryImpl(id, npc);
register(entry);
return entry;

@ -3,25 +3,29 @@ package lol.pyr.znpcsplus.npc;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.protocol.entity.type.EntityType;
import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes;
import lol.pyr.znpcsplus.api.entity.EntityProperty;
import lol.pyr.znpcsplus.api.npc.NpcType;
import lol.pyr.znpcsplus.entity.EntityPropertyImpl;
import lol.pyr.znpcsplus.entity.EntityPropertyRegistryImpl;
import java.util.*;
import java.util.logging.Logger;
import java.util.stream.Collectors;
public class NpcTypeImpl implements NpcType {
private final EntityType type;
private final Set<EntityPropertyImpl<?>> allowedProperties;
private final Map<EntityPropertyImpl<?>, Object> defaultProperties;
private final String name;
private final double hologramOffset;
private NpcTypeImpl(String name, EntityType type, double hologramOffset, Set<EntityPropertyImpl<?>> allowedProperties) {
private NpcTypeImpl(String name, EntityType type, double hologramOffset, Set<EntityPropertyImpl<?>> allowedProperties, Map<EntityPropertyImpl<?>, Object> defaultProperties) {
this.name = name.toLowerCase();
this.type = type;
this.hologramOffset = hologramOffset;
this.allowedProperties = allowedProperties;
this.defaultProperties = defaultProperties;
}
public String getName() {
@ -40,11 +44,20 @@ public class NpcTypeImpl implements NpcType {
return allowedProperties.stream().map(property -> (EntityProperty<?>) property).collect(Collectors.toSet());
}
public void applyDefaultProperties(NpcImpl npc) {
for (Map.Entry<EntityPropertyImpl<?>, Object> entry : defaultProperties.entrySet()) {
npc.UNSAFE_setProperty(entry.getKey(), entry.getValue());
}
}
protected static final class Builder {
private final static Logger logger = Logger.getLogger("NpcTypeBuilder");
private final EntityPropertyRegistryImpl propertyRegistry;
private final String name;
private final EntityType type;
private final List<EntityPropertyImpl<?>> allowedProperties = new ArrayList<>();
private final Map<EntityPropertyImpl<?>, Object> defaultProperties = new HashMap<>();
private double hologramOffset = 0;
Builder(EntityPropertyRegistryImpl propertyRegistry, String name, EntityType type) {
@ -67,7 +80,26 @@ public class NpcTypeImpl implements NpcType {
}
public Builder addProperties(String... names) {
for (String name : names) allowedProperties.add(propertyRegistry.getByName(name));
for (String name : names) {
if (propertyRegistry.getByName(name) == null) {
// Only for use in development, please comment this out in production because some properties are version-dependent
// logger.warning("Tried to register the non-existent \"" + name + "\" property to the \"" + this.name + "\" npc type");
continue;
}
allowedProperties.add(propertyRegistry.getByName(name));
}
return this;
}
@SuppressWarnings("unchecked")
public <T> Builder addDefaultProperty(String name, T value) {
EntityPropertyImpl<T> property = (EntityPropertyImpl<T>) propertyRegistry.getByName(name);
if (property == null) {
// Only for use in development, please comment this out in production because some properties are version-dependent
// logger.warning("Tried to register the non-existent \"" + name + "\" default property to the \"" + this.name + "\" npc type");
return this;
}
defaultProperties.put(property, value);
return this;
}
@ -77,22 +109,51 @@ public class NpcTypeImpl implements NpcType {
}
public NpcTypeImpl build() {
allowedProperties.add(propertyRegistry.getByName("fire"));
allowedProperties.add(propertyRegistry.getByName("invisible"));
allowedProperties.add(propertyRegistry.getByName("silent"));
allowedProperties.add(propertyRegistry.getByName("look"));
allowedProperties.add(propertyRegistry.getByName("skin_cape"));
allowedProperties.add(propertyRegistry.getByName("using_item"));
allowedProperties.add(propertyRegistry.getByName("potion_color"));
allowedProperties.add(propertyRegistry.getByName("potion_ambient"));
allowedProperties.add(propertyRegistry.getByName("dinnerbone"));
if (PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_9))
allowedProperties.add(propertyRegistry.getByName("glow"));
if (PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_14))
allowedProperties.add(propertyRegistry.getByName("pose"));
if (PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_17))
allowedProperties.add(propertyRegistry.getByName("shaking"));
return new NpcTypeImpl(name, type, hologramOffset, new HashSet<>(allowedProperties));
ServerVersion version = PacketEvents.getAPI().getServerManager().getVersion();
addProperties("fire", "invisible", "silent", "look",
"potion_color", "potion_ambient", "dinnerbone");
// TODO: make this look nicer after completing the rest of the properties
if (version.isNewerThanOrEquals(ServerVersion.V_1_9)) addProperties("glow");
if (version.isNewerThanOrEquals(ServerVersion.V_1_14)) {
addProperties("pose");
if (EntityTypes.isTypeInstanceOf(type, EntityTypes.HORSE)) {
addProperties("chestplate");
}
}
if (version.isNewerThanOrEquals(ServerVersion.V_1_17)) addProperties("shaking");
if (EntityTypes.isTypeInstanceOf(type, EntityTypes.ABSTRACT_AGEABLE) || EntityTypes.isTypeInstanceOf(type, EntityTypes.ZOMBIE) || EntityTypes.isTypeInstanceOf(type, EntityTypes.ZOGLIN)) {
addProperties("baby");
}
if (EntityTypes.isTypeInstanceOf(type, EntityTypes.ABSTRACT_HORSE)) {
addProperties("is_saddled", "is_eating", "is_rearing", "has_mouth_open");
}
if (EntityTypes.isTypeInstanceOf(type, EntityTypes.CHESTED_HORSE)) {
addProperties("has_chest");
} else if (version.isOlderThan(ServerVersion.V_1_11) && type.equals(EntityTypes.HORSE)) {
addProperties("has_chest");
}
if (EntityTypes.isTypeInstanceOf(type, EntityTypes.ABSTRACT_EVO_ILLU_ILLAGER)) {
addProperties("spell");
}
if (EntityTypes.isTypeInstanceOf(type, EntityTypes.ABSTRACT_PIGLIN)) {
addProperties("piglin_immune_to_zombification");
}
if (EntityTypes.isTypeInstanceOf(type, EntityTypes.SLIME) || EntityTypes.isTypeInstanceOf(type, EntityTypes.PHANTOM)) {
addProperties("size");
}
if (version.isOlderThan(ServerVersion.V_1_14)) {
if (EntityTypes.isTypeInstanceOf(type, EntityTypes.OCELOT)) {
addProperties("ocelot_type");
}
}
if (EntityTypes.isTypeInstanceOf(type, EntityTypes.PANDA)) {
if (version.isNewerThanOrEquals(ServerVersion.V_1_15)) {
addProperties("panda_rolling", "panda_sitting", "panda_on_back");
} else {
addProperties("panda_eating");
}
}
return new NpcTypeImpl(name, type, hologramOffset, new HashSet<>(allowedProperties), defaultProperties);
}
}
}

@ -36,7 +36,14 @@ public class NpcTypeRegistryImpl implements NpcTypeRegistry {
register(builder(p, "player", EntityTypes.PLAYER)
.setHologramOffset(-0.15D)
.addEquipmentProperties()
.addProperties("skin_cape", "skin_jacket", "skin_left_sleeve", "skin_right_sleeve", "skin_left_leg", "skin_right_leg", "skin_hat", "shoulder_entity_left", "shoulder_entity_right"));
.addProperties("skin_cape", "skin_jacket", "skin_left_sleeve", "skin_right_sleeve", "skin_left_leg", "skin_right_leg", "skin_hat", "shoulder_entity_left", "shoulder_entity_right")
.addDefaultProperty("skin_cape", true)
.addDefaultProperty("skin_jacket", true)
.addDefaultProperty("skin_left_sleeve", true)
.addDefaultProperty("skin_right_sleeve", true)
.addDefaultProperty("skin_left_leg", true)
.addDefaultProperty("skin_right_leg", true)
.addDefaultProperty("skin_hat", true));
// Most hologram offsets generated using Entity#getHeight() in 1.19.4
@ -88,7 +95,8 @@ public class NpcTypeRegistryImpl implements NpcTypeRegistry {
.setHologramOffset(-1.125));
register(builder(p, "horse", EntityTypes.HORSE)
.setHologramOffset(-0.375));
.setHologramOffset(-0.375)
.addProperties("horse_type", "horse_style", "horse_color", "horse_armor"));
register(builder(p, "iron_golem", EntityTypes.IRON_GOLEM)
.setHologramOffset(0.725));
@ -96,13 +104,15 @@ public class NpcTypeRegistryImpl implements NpcTypeRegistry {
register(builder(p, "magma_cube", EntityTypes.MAGMA_CUBE)); // TODO: Hologram offset scaling with size property
register(builder(p, "mooshroom", EntityTypes.MOOSHROOM)
.setHologramOffset(-0.575));
.setHologramOffset(-0.575)
.addProperties("mooshroom_variant"));
register(builder(p, "ocelot", EntityTypes.OCELOT)
.setHologramOffset(-1.275));
register(builder(p, "pig", EntityTypes.PIG)
.setHologramOffset(-1.075));
.setHologramOffset(-1.075)
.addProperties("pig_saddled"));
register(builder(p, "rabbit", EntityTypes.RABBIT)
.setHologramOffset(-1.475));
@ -143,7 +153,8 @@ public class NpcTypeRegistryImpl implements NpcTypeRegistry {
.setHologramOffset(1.525));
register(builder(p, "wolf", EntityTypes.WOLF)
.setHologramOffset(-1.125));
.setHologramOffset(-1.125)
.addProperties("tamed"));
register(builder(p, "zombie", EntityTypes.ZOMBIE)
.setHologramOffset(-0.025)
@ -164,7 +175,8 @@ public class NpcTypeRegistryImpl implements NpcTypeRegistry {
if (!version.isNewerThanOrEquals(ServerVersion.V_1_10)) return;
register(builder(p, "polar_bear", EntityTypes.POLAR_BEAR)
.setHologramOffset(-0.575));
.setHologramOffset(-0.575)
.addProperties("polar_bear_standing"));
if (!version.isNewerThanOrEquals(ServerVersion.V_1_11)) return;
@ -193,14 +205,16 @@ public class NpcTypeRegistryImpl implements NpcTypeRegistry {
.addProperties("evoker_spell"));
register(builder(p, "llama", EntityTypes.LLAMA)
.setHologramOffset(-0.105));
.setHologramOffset(-0.105)
.addProperties("carpet_color", "llama_variant"));
register(builder(p, "vex", EntityTypes.VEX)
.setHologramOffset(-1.175)
.addHandProperties());
register(builder(p, "vindicator", EntityTypes.VINDICATOR)
.setHologramOffset(-0.025));
.setHologramOffset(-0.025)
.addProperties("celebrating"));
register(builder(p, "wither_skeleton", EntityTypes.WITHER_SKELETON)
.setHologramOffset(0.425)
@ -216,7 +230,8 @@ public class NpcTypeRegistryImpl implements NpcTypeRegistry {
.setHologramOffset(-0.025));
register(builder(p, "parrot", EntityTypes.PARROT)
.setHologramOffset(-1.075));
.setHologramOffset(-1.075)
.addProperties("parrot_variant"));
if (!version.isNewerThanOrEquals(ServerVersion.V_1_13)) return;
@ -235,7 +250,8 @@ public class NpcTypeRegistryImpl implements NpcTypeRegistry {
.setHologramOffset(-1.475));
register(builder(p, "pufferfish", EntityTypes.PUFFERFISH)
.setHologramOffset(-1.625));
.setHologramOffset(-1.625)
.addProperties("puff_state"));
register(builder(p, "salmon", EntityTypes.SALMON)
.setHologramOffset(-1.575));
@ -250,24 +266,27 @@ public class NpcTypeRegistryImpl implements NpcTypeRegistry {
register(builder(p, "cat", EntityTypes.CAT)
.setHologramOffset(-1.275)
.addProperties("cat_variant", "cat_lying", "cat_collar_color"));
.addProperties("cat_variant", "cat_laying", "cat_relaxed", "cat_collar", "tamed"));
register(builder(p, "fox", EntityTypes.FOX)
.setHologramOffset(-1.275)
.addProperties("hand", "fox_variant", "fox_sitting", "fox_crouching", "fox_sleeping", "fox_faceplanted"));
register(builder(p, "panda", EntityTypes.PANDA)
.setHologramOffset(-0.725));
.setHologramOffset(-0.725)
.addProperties("panda_main_gene", "panda_hidden_gene", "panda_sneezing"));
register(builder(p, "pillager", EntityTypes.PILLAGER)
.setHologramOffset(-0.025)
.addHandProperties());
.addHandProperties()
.addProperties("pillager_charging"));
register(builder(p, "ravager", EntityTypes.RAVAGER)
.setHologramOffset(0.225));
register(builder(p, "trader_llama", EntityTypes.TRADER_LLAMA)
.setHologramOffset(-0.105));
.setHologramOffset(-0.105)
.addProperties("llama_variant"));
register(builder(p, "wandering_trader", EntityTypes.WANDERING_TRADER)
.setHologramOffset(-0.025)
@ -283,11 +302,12 @@ public class NpcTypeRegistryImpl implements NpcTypeRegistry {
register(builder(p, "hoglin", EntityTypes.HOGLIN)
.setHologramOffset(-0.575)
.addProperties("immune_to_zombification"));
.addProperties("hoglin_immune_to_zombification"));
register(builder(p, "piglin", EntityTypes.PIGLIN)
.setHologramOffset(-1.0)
.addEquipmentProperties());
.addEquipmentProperties()
.addProperties("piglin_baby", "piglin_charging_crossbow", "piglin_dancing"));
register(builder(p, "piglin_brute", EntityTypes.PIGLIN_BRUTE)
.setHologramOffset(-0.025)
@ -320,7 +340,7 @@ public class NpcTypeRegistryImpl implements NpcTypeRegistry {
register(builder(p, "frog", EntityTypes.FROG)
.setHologramOffset(-1.475)
.addProperties("frog_variant"));
.addProperties("frog_variant", "frog_target_npc"));
register(builder(p, "tadpole", EntityTypes.TADPOLE)
.setHologramOffset(-1.675));
@ -334,7 +354,8 @@ public class NpcTypeRegistryImpl implements NpcTypeRegistry {
.setHologramOffset(0.125));
register(builder(p, "camel", EntityTypes.CAMEL)
.setHologramOffset(0.25));
.setHologramOffset(0.25)
.addProperties("bashing"));
}
public Collection<NpcType> getAll() {

@ -1,12 +1,13 @@
package lol.pyr.znpcsplus.packets;
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.entity.PacketEntity;
import net.kyori.adventure.text.format.NamedTextColor;
import org.bukkit.entity.Player;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
public interface PacketFactory {
@ -16,10 +17,9 @@ public interface PacketFactory {
void teleportEntity(Player player, PacketEntity entity);
CompletableFuture<Void> addTabPlayer(Player player, PacketEntity entity, PropertyHolder properties);
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);
Map<Integer, EntityData> generateMetadata(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 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());
}
}

Some files were not shown because too many files have changed in this diff Show More