From 53ad7a73d6c19038e6980630b9f8f17d56a6cd44 Mon Sep 17 00:00:00 2001 From: Pyrbu Date: Mon, 8 May 2023 12:17:25 +0100 Subject: [PATCH] start work on persistence --- api/build.gradle | 1 + .../znpcsplus/api/entity/EntityProperty.java | 46 +----- .../lol/pyr/znpcsplus/api/npc/NpcType.java | 112 +-------------- .../java/lol/pyr/znpcsplus/ZNpcsPlus.java | 20 +-- .../pyr/znpcsplus/config/MessageConfig.java | 10 +- .../znpcsplus/entity/EntityPropertyImpl.java | 90 ++++++++++++ .../pyr/znpcsplus/hologram/HologramImpl.java | 4 + .../pyr/znpcsplus/hologram/HologramLine.java | 9 +- .../pyr/znpcsplus/interaction/NpcAction.java | 6 + .../types/ConsoleCommandAction.java | 6 + .../interaction/types/MessageAction.java | 6 + .../types/PlayerCommandAction.java | 6 + .../interaction/types/SwitchServerAction.java | 6 + .../lol/pyr/znpcsplus/npc/NpcEntryImpl.java | 8 +- .../java/lol/pyr/znpcsplus/npc/NpcImpl.java | 35 +++-- .../pyr/znpcsplus/npc/NpcRegistryImpl.java | 13 +- .../lol/pyr/znpcsplus/npc/NpcTypeImpl.java | 114 +++++++++++++++ .../pyr/znpcsplus/packets/V1_14Factory.java | 4 +- .../pyr/znpcsplus/packets/V1_8Factory.java | 18 +-- .../pyr/znpcsplus/packets/V1_9Factory.java | 14 +- .../znpcsplus/skin/BaseSkinDescriptor.java | 24 +++- .../java/lol/pyr/znpcsplus/skin/Skin.java | 11 +- .../skin/descriptor/FetchingDescriptor.java | 9 ++ .../skin/descriptor/MirrorDescriptor.java | 5 + .../skin/descriptor/PrefetchedDescriptor.java | 17 +++ .../lol/pyr/znpcsplus/storage/NpcStorage.java | 10 ++ .../pyr/znpcsplus/storage/NpcStorageType.java | 14 ++ .../storage/SerializationException.java | 7 + .../znpcsplus/storage/yaml/YamlStorage.java | 133 ++++++++++++++++++ 29 files changed, 548 insertions(+), 210 deletions(-) create mode 100644 plugin/src/main/java/lol/pyr/znpcsplus/entity/EntityPropertyImpl.java create mode 100644 plugin/src/main/java/lol/pyr/znpcsplus/npc/NpcTypeImpl.java create mode 100644 plugin/src/main/java/lol/pyr/znpcsplus/storage/NpcStorage.java create mode 100644 plugin/src/main/java/lol/pyr/znpcsplus/storage/NpcStorageType.java create mode 100644 plugin/src/main/java/lol/pyr/znpcsplus/storage/SerializationException.java create mode 100644 plugin/src/main/java/lol/pyr/znpcsplus/storage/yaml/YamlStorage.java diff --git a/api/build.gradle b/api/build.gradle index f6b0d20..30944ca 100644 --- a/api/build.gradle +++ b/api/build.gradle @@ -6,6 +6,7 @@ plugins { dependencies { compileOnly "org.spigotmc:spigot-api:1.19.4-R0.1-SNAPSHOT" compileOnly "net.kyori:adventure-platform-bukkit:4.3.0" + compileOnly "net.kyori:adventure-text-minimessage:4.13.1" compileOnly "com.github.retrooper.packetevents:spigot:2.0.0-SNAPSHOT" } diff --git a/api/src/main/java/lol/pyr/znpcsplus/api/entity/EntityProperty.java b/api/src/main/java/lol/pyr/znpcsplus/api/entity/EntityProperty.java index b467915..b5c20b0 100644 --- a/api/src/main/java/lol/pyr/znpcsplus/api/entity/EntityProperty.java +++ b/api/src/main/java/lol/pyr/znpcsplus/api/entity/EntityProperty.java @@ -1,45 +1,5 @@ package lol.pyr.znpcsplus.api.entity; -import lol.pyr.znpcsplus.api.skin.SkinDescriptor; -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.format.NamedTextColor; - -import java.util.HashMap; -import java.util.Map; - -public class EntityProperty { - private final String name; - private final T defaultValue; - - public EntityProperty(String name) { - this(name, null); - } - - public EntityProperty(String name, T defaultValue) { - this.name = name.toUpperCase(); - this.defaultValue = defaultValue; - BY_NAME.put(this.name, this); - } - - public String name() { - return name; - } - - public T getDefaultValue() { - return defaultValue; - } - - private final static Map> BY_NAME = new HashMap<>(); - - public static EntityProperty getByName(String name) { - return BY_NAME.get(name.toUpperCase()); - } - - public static EntityProperty SKIN_LAYERS = new EntityProperty<>("skin_layers", true); - public static EntityProperty SKIN = new EntityProperty<>("skin"); - public static EntityProperty GLOW = new EntityProperty<>("glow"); - public static EntityProperty FIRE = new EntityProperty<>("fire", false); - public static EntityProperty INVISIBLE = new EntityProperty<>("invisible", false); - public static EntityProperty SILENT = new EntityProperty<>("silent", false); - public static EntityProperty NAME = new EntityProperty<>("name"); -} \ No newline at end of file +public interface EntityProperty { + T getDefaultValue(); +} diff --git a/api/src/main/java/lol/pyr/znpcsplus/api/npc/NpcType.java b/api/src/main/java/lol/pyr/znpcsplus/api/npc/NpcType.java index 855251e..166969d 100644 --- a/api/src/main/java/lol/pyr/znpcsplus/api/npc/NpcType.java +++ b/api/src/main/java/lol/pyr/znpcsplus/api/npc/NpcType.java @@ -1,114 +1,4 @@ package lol.pyr.znpcsplus.api.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 java.util.*; - -public class NpcType { - private final static Map BY_NAME = new HashMap<>(); - - public static Collection values() { - return BY_NAME.values(); - } - - public static NpcType byName(String name) { - return BY_NAME.get(name.toUpperCase()); - } - - private final EntityType type; - private final Set> allowedProperties; - private final String name; - private final double hologramOffset; - - private NpcType(String name, EntityType type, double hologramOffset, Set> allowedProperties) { - this.name = name.toUpperCase(); - this.type = type; - this.hologramOffset = hologramOffset; - this.allowedProperties = allowedProperties; - } - - public String getName() { - return name; - } - - public EntityType getType() { - return type; - } - - public double getHologramOffset() { - return hologramOffset; - } - - public Set> getAllowedProperties() { - return allowedProperties; - } - - private static NpcType define(Builder builder) { - return define(builder.build()); - } - - private static NpcType define(NpcType type) { - BY_NAME.put(type.getName(), type); - return type; - } - - public static final NpcType PLAYER = define( - new Builder("player", EntityTypes.PLAYER) - .addProperties(EntityProperty.SKIN, EntityProperty.SKIN_LAYERS) - .setHologramOffset(-0.45D)); - - public static final NpcType CREEPER = define( - new Builder("creeper", EntityTypes.CREEPER) - .setHologramOffset(-0.6D)); - - public static final NpcType ZOMBIE = define( - new Builder("zombie", EntityTypes.ZOMBIE) - .setHologramOffset(-0.3D)); - - public static final NpcType SKELETON = define( - new Builder("skeleton", EntityTypes.SKELETON) - .setHologramOffset(-0.3D)); - - private static final class Builder { - private final String name; - private final EntityType type; - private final List> allowedProperties = new ArrayList<>(); - private boolean globalProperties = true; - private double hologramOffset = 0; - - private Builder(String name, EntityType type) { - this.name = name; - this.type = type; - } - - public Builder addProperties(EntityProperty... properties) { - allowedProperties.addAll(Arrays.asList(properties)); - return this; - } - - public Builder setEnableGlobalProperties(boolean enabled) { - globalProperties = enabled; - return this; - } - - public Builder setHologramOffset(double hologramOffset) { - this.hologramOffset = hologramOffset; - return this; - } - - public NpcType build() { - if (globalProperties) { - allowedProperties.add(EntityProperty.FIRE); - allowedProperties.add(EntityProperty.INVISIBLE); - allowedProperties.add(EntityProperty.SILENT); - if (PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_9)) - allowedProperties.add(EntityProperty.GLOW); - } - return new NpcType(name, type, hologramOffset, new HashSet<>(allowedProperties)); - } - } +public interface NpcType { } diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/ZNpcsPlus.java b/plugin/src/main/java/lol/pyr/znpcsplus/ZNpcsPlus.java index e82dcaf..d573d16 100644 --- a/plugin/src/main/java/lol/pyr/znpcsplus/ZNpcsPlus.java +++ b/plugin/src/main/java/lol/pyr/znpcsplus/ZNpcsPlus.java @@ -7,8 +7,8 @@ import io.github.retrooper.packetevents.factory.spigot.SpigotPacketEventsBuilder import lol.pyr.director.adventure.command.CommandManager; import lol.pyr.director.adventure.command.MultiCommand; import lol.pyr.znpcsplus.api.ZApiProvider; -import lol.pyr.znpcsplus.api.entity.EntityProperty; -import lol.pyr.znpcsplus.api.npc.NpcType; +import lol.pyr.znpcsplus.entity.EntityPropertyImpl; +import lol.pyr.znpcsplus.npc.NpcTypeImpl; import lol.pyr.znpcsplus.config.Configs; import lol.pyr.znpcsplus.interaction.InteractionPacketListener; import lol.pyr.znpcsplus.interaction.types.ConsoleCommandAction; @@ -143,15 +143,15 @@ public class ZNpcsPlus extends JavaPlugin { int z = 0; World world = Bukkit.getWorld("world"); if (world == null) world = Bukkit.getWorlds().get(0); - for (NpcType type : NpcType.values()) { + for (NpcTypeImpl type : NpcTypeImpl.values()) { NpcEntryImpl entry = NpcRegistryImpl.get().create("debug_npc" + (z * wrap + x), world, type, new ZLocation(x * 3, 200, z * 3, 0, 0)); entry.setProcessed(true); NpcImpl npc = entry.getNpc(); if (type.getType() == EntityTypes.PLAYER) { - SkinCache.fetchByName("Notch").thenAccept(skin -> npc.setProperty(EntityProperty.SKIN, new PrefetchedDescriptor(skin))); - npc.setProperty(EntityProperty.INVISIBLE, true); + SkinCache.fetchByName("Notch").thenAccept(skin -> npc.setProperty(EntityPropertyImpl.SKIN, new PrefetchedDescriptor(skin))); + npc.setProperty(EntityPropertyImpl.INVISIBLE, true); } - npc.setProperty(EntityProperty.GLOW, NamedTextColor.RED); + npc.setProperty(EntityPropertyImpl.GLOW, NamedTextColor.RED); // npc.setProperty(EntityProperty.FIRE, true); npc.getHologram().addLine(Component.text("Hello, World!")); if (x++ > wrap) { @@ -159,16 +159,16 @@ public class ZNpcsPlus extends JavaPlugin { z++; } } - NpcEntryImpl entry = NpcRegistryImpl.get().create("debug_npc" + (z * wrap + x), world, NpcType.byName("player"), new ZLocation(x * 3, 200, z * 3, 0, 0)); + NpcEntryImpl entry = NpcRegistryImpl.get().create("debug_npc" + (z * wrap + x), world, NpcTypeImpl.byName("player"), new ZLocation(x * 3, 200, z * 3, 0, 0)); entry.setProcessed(true); NpcImpl npc = entry.getNpc(); - npc.setProperty(EntityProperty.SKIN, new FetchingDescriptor("jeb_")); + npc.setProperty(EntityPropertyImpl.SKIN, new FetchingDescriptor("jeb_")); npc.addAction(new MessageAction(1000L, "Hi, I'm jeb!")); x++; - entry = NpcRegistryImpl.get().create("debug_npc" + (z * wrap + x), world, NpcType.byName("player"), new ZLocation(x * 3, 200, z * 3, 0, 0)); + entry = NpcRegistryImpl.get().create("debug_npc" + (z * wrap + x), world, NpcTypeImpl.byName("player"), new ZLocation(x * 3, 200, z * 3, 0, 0)); entry.setProcessed(true); npc = entry.getNpc(); - npc.setProperty(EntityProperty.SKIN, new MirrorDescriptor()); + npc.setProperty(EntityPropertyImpl.SKIN, new MirrorDescriptor()); npc.addAction(new ConsoleCommandAction(1000L, "kick {player}")); } } diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/config/MessageConfig.java b/plugin/src/main/java/lol/pyr/znpcsplus/config/MessageConfig.java index 757f6af..666a59e 100644 --- a/plugin/src/main/java/lol/pyr/znpcsplus/config/MessageConfig.java +++ b/plugin/src/main/java/lol/pyr/znpcsplus/config/MessageConfig.java @@ -12,24 +12,24 @@ package lol.pyr.znpcsplus.config; * TOO_FEW_ARGUMENTS("messages", "&cThis command does not contain enough arguments. Type &f/znpcs&c or view our documentation for a list/examples of existing arguments.", String.class), * PATH_START("messages", "&aSuccess! Move to create a path for your NPC. When finished, type &f/znpcs path exit&c to exit path creation.", String.class), * EXIT_PATH("messages", "&cYou have exited path creation.", String.class), - * PATH_FOUND("messages", "&cThere is already a path with this name.", String.class), + * PATH_FOUND("messages", "&cThere is already a path with this getName.", String.class), * NPC_FOUND("messages", "&cThere is already an NPC with this ID.", String.class), * NO_PATH_FOUND("messages", "&cThe path you have specified does not exist.", String.class), * NO_SKIN_FOUND("messages", "&cThe skin username/URL you have specified does not exist or is invalid.", String.class), * NO_NPC_FOUND("messages", "&cThe NPC you have specified does not exist.", String.class), * NO_ACTION_FOUND("messages", "&cThis action does not exist! Type &f/znpcs&c or view our documentation for a list/examples of existing action types.", String.class), * METHOD_NOT_FOUND("messages", "&cThis method does not exist! Type &f/znpcs&c or view our documentation for a list/examples of existing methods.", String.class), - * INVALID_NAME_LENGTH("messages", "&cThe name you specified either too short or long. Please enter a positive integer of (3 to 16) characters.", String.class), + * INVALID_NAME_LENGTH("messages", "&cThe getName you specified either too short or long. Please enter a positive integer of (3 to 16) characters.", String.class), * UNSUPPORTED_ENTITY("messages", "&cThis entity type not available in your current server version.", String.class), * PATH_SET_INCORRECT_USAGE("messages", "&eUsage: &aset ", String.class), * ACTION_ADD_INCORRECT_USAGE("messages", "&eUsage: &a ", String.class), * ACTION_DELAY_INCORRECT_USAGE("messages", "&eUsage: &a ", String.class), * CONVERSATION_SET_INCORRECT_USAGE("messages", "&cUsage: ", String.class), * NO_CONVERSATION_FOUND("messages", "&cThe conversation you have specified does not exist!", String.class), - * CONVERSATION_FOUND("messages", "&cThere is already a conversation with this name.", String.class), + * CONVERSATION_FOUND("messages", "&cThere is already a conversation with this getName.", String.class), * INVALID_SIZE("messages", "&cThe position you have specified cannot exceed the limit.", String.class), - * FETCHING_SKIN("messages", "&aFetching skin for name: &f%s&a. Please wait...", String.class), - * CANT_GET_SKIN("messages", "&cCould not fetch skin for name: %s.", String.class), + * FETCHING_SKIN("messages", "&aFetching skin for getName: &f%s&a. Please wait...", String.class), + * CANT_GET_SKIN("messages", "&cCould not fetch skin for getName: %s.", String.class), * GET_SKIN("messages", "&aSkin successfully fetched!", String.class), * NOT_SUPPORTED_NPC_TYPE("messages", "&cThis NPC type doesn't exists or is not supported in your current server version.", String.class); */ diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/entity/EntityPropertyImpl.java b/plugin/src/main/java/lol/pyr/znpcsplus/entity/EntityPropertyImpl.java new file mode 100644 index 0000000..1cd654c --- /dev/null +++ b/plugin/src/main/java/lol/pyr/znpcsplus/entity/EntityPropertyImpl.java @@ -0,0 +1,90 @@ +package lol.pyr.znpcsplus.entity; + +import lol.pyr.znpcsplus.api.entity.EntityProperty; +import lol.pyr.znpcsplus.api.entity.PropertyHolder; +import lol.pyr.znpcsplus.api.skin.SkinDescriptor; +import lol.pyr.znpcsplus.skin.BaseSkinDescriptor; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.minimessage.MiniMessage; + +import java.util.HashMap; +import java.util.Map; + +public class EntityPropertyImpl implements EntityProperty { + private final String name; + private final T defaultValue; + + private final PropertySerializer serializer; + private final PropertyDeserializer deserializer; + + public EntityPropertyImpl(String name, PropertySerializer serializer, PropertyDeserializer deserializer) { + this(name, null, serializer, deserializer); + } + + public EntityPropertyImpl(String name, T defaultValue, PropertySerializer serializer, PropertyDeserializer deserializer) { + this.name = name.toUpperCase(); + this.defaultValue = defaultValue; + this.serializer = serializer; + this.deserializer = deserializer; + BY_NAME.put(this.name, this); + } + + public String getName() { + 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 deserializer.deserialize(str); + } + + @Override + public T getDefaultValue() { + return defaultValue; + } + + private final static Map> BY_NAME = new HashMap<>(); + + public static EntityPropertyImpl getByName(String name) { + return BY_NAME.get(name.toUpperCase()); + } + + @FunctionalInterface + private interface PropertySerializer { + String serialize(T property); + } + + @FunctionalInterface + private interface PropertyDeserializer { + T deserialize(String property); + } + + + private final static PropertySerializer BOOLEAN_SERIALIZER = Object::toString; + private final static PropertyDeserializer BOOLEAN_DESERIALIZER = Boolean::valueOf; + + private final static PropertySerializer COLOR_SERIALIZER = color -> String.valueOf(color.value()); + private final static PropertyDeserializer COLOR_DESERIALIZER = str -> NamedTextColor.namedColor(Integer.parseInt(str)); + + private final static PropertySerializer COMPONENT_SERIALIZER = component -> MiniMessage.miniMessage().serialize(component); + private final static PropertyDeserializer COMPONENT_DESERIALIZER = str -> MiniMessage.miniMessage().deserialize(str); + + private final static PropertySerializer DESCRIPTOR_SERIALIZER = descriptor -> ((BaseSkinDescriptor) descriptor).serialize(); + private final static PropertyDeserializer DESCRIPTOR_DESERIALIZER = BaseSkinDescriptor::deserialize; + + public static EntityPropertyImpl SKIN_LAYERS = new EntityPropertyImpl<>("skin_layers", true, BOOLEAN_SERIALIZER, BOOLEAN_DESERIALIZER); + public static EntityPropertyImpl SKIN = new EntityPropertyImpl<>("skin", DESCRIPTOR_SERIALIZER, DESCRIPTOR_DESERIALIZER); + public static EntityPropertyImpl GLOW = new EntityPropertyImpl<>("glow", COLOR_SERIALIZER, COLOR_DESERIALIZER); + public static EntityPropertyImpl FIRE = new EntityPropertyImpl<>("fire", false, BOOLEAN_SERIALIZER, BOOLEAN_DESERIALIZER); + public static EntityPropertyImpl INVISIBLE = new EntityPropertyImpl<>("invisible", false, BOOLEAN_SERIALIZER, BOOLEAN_DESERIALIZER); + public static EntityPropertyImpl SILENT = new EntityPropertyImpl<>("silent", false, BOOLEAN_SERIALIZER, BOOLEAN_DESERIALIZER); + public static EntityPropertyImpl NAME = new EntityPropertyImpl<>("name", COMPONENT_SERIALIZER, COMPONENT_DESERIALIZER); +} \ No newline at end of file diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/hologram/HologramImpl.java b/plugin/src/main/java/lol/pyr/znpcsplus/hologram/HologramImpl.java index 8320665..3b748a0 100644 --- a/plugin/src/main/java/lol/pyr/znpcsplus/hologram/HologramImpl.java +++ b/plugin/src/main/java/lol/pyr/znpcsplus/hologram/HologramImpl.java @@ -36,6 +36,10 @@ public class HologramImpl extends Viewable implements Hologram { relocateLines(); } + public List getLines() { + return Collections.unmodifiableList(lines); + } + public void clearLines() { UNSAFE_hideAll(); lines.clear(); diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/hologram/HologramLine.java b/plugin/src/main/java/lol/pyr/znpcsplus/hologram/HologramLine.java index f8576c9..d3c680b 100644 --- a/plugin/src/main/java/lol/pyr/znpcsplus/hologram/HologramLine.java +++ b/plugin/src/main/java/lol/pyr/znpcsplus/hologram/HologramLine.java @@ -1,8 +1,9 @@ package lol.pyr.znpcsplus.hologram; import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes; -import lol.pyr.znpcsplus.api.entity.PropertyHolder; import lol.pyr.znpcsplus.api.entity.EntityProperty; +import lol.pyr.znpcsplus.api.entity.PropertyHolder; +import lol.pyr.znpcsplus.entity.EntityPropertyImpl; import lol.pyr.znpcsplus.entity.PacketEntity; import lol.pyr.znpcsplus.util.ZLocation; import net.kyori.adventure.text.Component; @@ -42,13 +43,13 @@ public class HologramLine implements PropertyHolder { @SuppressWarnings("unchecked") @Override public T getProperty(EntityProperty key) { - if (key == EntityProperty.INVISIBLE) return (T) Boolean.TRUE; - if (key == EntityProperty.NAME) return (T) text; + if (key == EntityPropertyImpl.INVISIBLE) return (T) Boolean.TRUE; + if (key == EntityPropertyImpl.NAME) return (T) text; return key.getDefaultValue(); } @Override public boolean hasProperty(EntityProperty key) { - return key == EntityProperty.NAME || key == EntityProperty.INVISIBLE; + return key == EntityPropertyImpl.NAME || key == EntityPropertyImpl.INVISIBLE; } } diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/interaction/NpcAction.java b/plugin/src/main/java/lol/pyr/znpcsplus/interaction/NpcAction.java index eb2e0ba..88d73d7 100644 --- a/plugin/src/main/java/lol/pyr/znpcsplus/interaction/NpcAction.java +++ b/plugin/src/main/java/lol/pyr/znpcsplus/interaction/NpcAction.java @@ -23,5 +23,11 @@ public abstract class NpcAction { return delay; } + public String getArgument() { + return argument; + } + public abstract void run(Player player); + + public abstract NpcActionType getType(); } diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/interaction/types/ConsoleCommandAction.java b/plugin/src/main/java/lol/pyr/znpcsplus/interaction/types/ConsoleCommandAction.java index d466bb9..2945e43 100644 --- a/plugin/src/main/java/lol/pyr/znpcsplus/interaction/types/ConsoleCommandAction.java +++ b/plugin/src/main/java/lol/pyr/znpcsplus/interaction/types/ConsoleCommandAction.java @@ -2,6 +2,7 @@ package lol.pyr.znpcsplus.interaction.types; import lol.pyr.znpcsplus.ZNpcsPlus; import lol.pyr.znpcsplus.interaction.NpcAction; +import lol.pyr.znpcsplus.interaction.NpcActionType; import me.clip.placeholderapi.PlaceholderAPI; import org.bukkit.Bukkit; import org.bukkit.entity.Player; @@ -16,4 +17,9 @@ public class ConsoleCommandAction extends NpcAction { String cmd = argument.replace("{player}", player.getName()).replace("{uuid}", player.getUniqueId().toString()); Bukkit.dispatchCommand(Bukkit.getConsoleSender(), ZNpcsPlus.PLACEHOLDERS_SUPPORTED ? PlaceholderAPI.setPlaceholders(player, cmd) : cmd); } + + @Override + public NpcActionType getType() { + return NpcActionType.CONSOLE_CMD; + } } diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/interaction/types/MessageAction.java b/plugin/src/main/java/lol/pyr/znpcsplus/interaction/types/MessageAction.java index b19d03e..1f8f545 100644 --- a/plugin/src/main/java/lol/pyr/znpcsplus/interaction/types/MessageAction.java +++ b/plugin/src/main/java/lol/pyr/znpcsplus/interaction/types/MessageAction.java @@ -2,6 +2,7 @@ package lol.pyr.znpcsplus.interaction.types; import lol.pyr.znpcsplus.ZNpcsPlus; import lol.pyr.znpcsplus.interaction.NpcAction; +import lol.pyr.znpcsplus.interaction.NpcActionType; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.minimessage.MiniMessage; import org.bukkit.entity.Player; @@ -18,4 +19,9 @@ public class MessageAction extends NpcAction { public void run(Player player) { ZNpcsPlus.ADVENTURE.player(player).sendMessage(message); } + + @Override + public NpcActionType getType() { + return NpcActionType.MESSAGE; + } } diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/interaction/types/PlayerCommandAction.java b/plugin/src/main/java/lol/pyr/znpcsplus/interaction/types/PlayerCommandAction.java index c928bad..1ed2cfd 100644 --- a/plugin/src/main/java/lol/pyr/znpcsplus/interaction/types/PlayerCommandAction.java +++ b/plugin/src/main/java/lol/pyr/znpcsplus/interaction/types/PlayerCommandAction.java @@ -2,6 +2,7 @@ package lol.pyr.znpcsplus.interaction.types; import lol.pyr.znpcsplus.ZNpcsPlus; import lol.pyr.znpcsplus.interaction.NpcAction; +import lol.pyr.znpcsplus.interaction.NpcActionType; import me.clip.placeholderapi.PlaceholderAPI; import org.bukkit.Bukkit; import org.bukkit.entity.Player; @@ -16,4 +17,9 @@ public class PlayerCommandAction extends NpcAction { String cmd = argument.replace("{player}", player.getName()).replace("{uuid}", player.getUniqueId().toString()); Bukkit.dispatchCommand(player, ZNpcsPlus.PLACEHOLDERS_SUPPORTED ? PlaceholderAPI.setPlaceholders(player, cmd) : cmd); } + + @Override + public NpcActionType getType() { + return NpcActionType.PLAYER_CMD; + } } diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/interaction/types/SwitchServerAction.java b/plugin/src/main/java/lol/pyr/znpcsplus/interaction/types/SwitchServerAction.java index c872a08..ad2c234 100644 --- a/plugin/src/main/java/lol/pyr/znpcsplus/interaction/types/SwitchServerAction.java +++ b/plugin/src/main/java/lol/pyr/znpcsplus/interaction/types/SwitchServerAction.java @@ -2,6 +2,7 @@ package lol.pyr.znpcsplus.interaction.types; import lol.pyr.znpcsplus.ZNpcsPlus; import lol.pyr.znpcsplus.interaction.NpcAction; +import lol.pyr.znpcsplus.interaction.NpcActionType; import org.bukkit.entity.Player; public class SwitchServerAction extends NpcAction { @@ -13,4 +14,9 @@ public class SwitchServerAction extends NpcAction { public void run(Player player) { ZNpcsPlus.BUNGEE_UTILS.sendPlayerToServer(player, argument); } + + @Override + public NpcActionType getType() { + return NpcActionType.SERVER; + } } diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/npc/NpcEntryImpl.java b/plugin/src/main/java/lol/pyr/znpcsplus/npc/NpcEntryImpl.java index 4c1deb4..e944dc0 100644 --- a/plugin/src/main/java/lol/pyr/znpcsplus/npc/NpcEntryImpl.java +++ b/plugin/src/main/java/lol/pyr/znpcsplus/npc/NpcEntryImpl.java @@ -3,13 +3,15 @@ package lol.pyr.znpcsplus.npc; import lol.pyr.znpcsplus.api.npc.NpcEntry; public class NpcEntryImpl implements NpcEntry { + private final String id; private final NpcImpl npc; private boolean process = false; private boolean save = false; private boolean modify = false; - public NpcEntryImpl(NpcImpl npc) { + public NpcEntryImpl(String id, NpcImpl npc) { + this.id = id.toLowerCase(); this.npc = npc; } @@ -54,4 +56,8 @@ public class NpcEntryImpl implements NpcEntry { setProcessed(true); setAllowCommandModification(true); } + + public String getId() { + return id; + } } diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/npc/NpcImpl.java b/plugin/src/main/java/lol/pyr/znpcsplus/npc/NpcImpl.java index c15b919..0da9cb3 100644 --- a/plugin/src/main/java/lol/pyr/znpcsplus/npc/NpcImpl.java +++ b/plugin/src/main/java/lol/pyr/znpcsplus/npc/NpcImpl.java @@ -1,8 +1,8 @@ package lol.pyr.znpcsplus.npc; import lol.pyr.znpcsplus.api.entity.EntityProperty; -import lol.pyr.znpcsplus.api.npc.NpcType; import lol.pyr.znpcsplus.api.npc.Npc; +import lol.pyr.znpcsplus.entity.EntityPropertyImpl; import lol.pyr.znpcsplus.entity.PacketEntity; import lol.pyr.znpcsplus.hologram.HologramImpl; import lol.pyr.znpcsplus.interaction.NpcAction; @@ -19,28 +19,33 @@ public class NpcImpl extends Viewable implements Npc { private final String worldName; private PacketEntity entity; private ZLocation location; - private NpcType type; + private NpcTypeImpl type; private final HologramImpl hologram; - private final Map, Object> propertyMap = new HashMap<>(); + private final Map, Object> propertyMap = new HashMap<>(); private final Set actions = new HashSet<>(); - protected NpcImpl(World world, NpcType type, ZLocation location) { - this.worldName = world.getName(); + protected NpcImpl(World world, NpcTypeImpl type, ZLocation location) { + this(world.getName(), type, location); + } + + public NpcImpl(String world, NpcTypeImpl type, ZLocation location) { + this.worldName = world; this.type = type; this.location = location; entity = new PacketEntity(this, type.getType(), location); hologram = new HologramImpl(location.withY(location.getY() + type.getHologramOffset())); } - public void setType(NpcType type) { + + public void setType(NpcTypeImpl type) { UNSAFE_hideAll(); this.type = type; entity = new PacketEntity(this, type.getType(), entity.getLocation()); UNSAFE_showAll(); } - public NpcType getType() { + public NpcTypeImpl getType() { return type; } @@ -66,6 +71,10 @@ public class NpcImpl extends Viewable implements Npc { return Bukkit.getWorld(worldName); } + public String getWorldName() { + return worldName; + } + @Override protected void _show(Player player) { entity.spawn(player); @@ -84,14 +93,14 @@ public class NpcImpl extends Viewable implements Npc { @SuppressWarnings("unchecked") public T getProperty(EntityProperty key) { - return hasProperty(key) ? (T) propertyMap.get(key) : key.getDefaultValue(); + return hasProperty(key) ? (T) propertyMap.get((EntityPropertyImpl) key) : key.getDefaultValue(); } public boolean hasProperty(EntityProperty key) { - return propertyMap.containsKey(key); + return propertyMap.containsKey((EntityPropertyImpl) key); } - public void setProperty(EntityProperty key, T value) { + public void setProperty(EntityPropertyImpl key, T value) { if (value.equals(key.getDefaultValue())) removeProperty(key); else { propertyMap.put(key, value); @@ -99,11 +108,15 @@ public class NpcImpl extends Viewable implements Npc { } } - public void removeProperty(EntityProperty key) { + public void removeProperty(EntityPropertyImpl key) { propertyMap.remove(key); _refreshMeta(); } + public Set> getAppliedProperties() { + return Collections.unmodifiableSet(propertyMap.keySet()); + } + public Collection getActions() { return Collections.unmodifiableSet(actions); } diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/npc/NpcRegistryImpl.java b/plugin/src/main/java/lol/pyr/znpcsplus/npc/NpcRegistryImpl.java index 0948c80..4392949 100644 --- a/plugin/src/main/java/lol/pyr/znpcsplus/npc/NpcRegistryImpl.java +++ b/plugin/src/main/java/lol/pyr/znpcsplus/npc/NpcRegistryImpl.java @@ -24,7 +24,7 @@ public class NpcRegistryImpl implements NpcRegistry { private final Map npcMap = new HashMap<>(); public NpcEntryImpl get(String id) { - return npcMap.get(id.toUpperCase()); + return npcMap.get(id.toLowerCase()); } public Collection all() { @@ -39,19 +39,22 @@ public class NpcRegistryImpl implements NpcRegistry { return Collections.unmodifiableSet(npcMap.keySet()); } - @Override public NpcEntryImpl create(String id, World world, NpcType type, ZLocation location) { - id = id.toUpperCase(); + return create(id, world, (NpcTypeImpl) type, location); + } + + public NpcEntryImpl create(String id, World world, NpcTypeImpl type, ZLocation location) { + id = id.toLowerCase(); if (npcMap.containsKey(id)) throw new IllegalArgumentException("An npc with the id " + id + " already exists!"); NpcImpl npc = new NpcImpl(world, type, location); - NpcEntryImpl entry = new NpcEntryImpl(npc); + NpcEntryImpl entry = new NpcEntryImpl(id, npc); npcMap.put(id, entry); return entry; } @Override public void delete(String id) { - id = id.toUpperCase(); + id = id.toLowerCase(); if (!npcMap.containsKey(id)) return; npcMap.remove(id).getNpc().delete(); } diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/npc/NpcTypeImpl.java b/plugin/src/main/java/lol/pyr/znpcsplus/npc/NpcTypeImpl.java new file mode 100644 index 0000000..db4db94 --- /dev/null +++ b/plugin/src/main/java/lol/pyr/znpcsplus/npc/NpcTypeImpl.java @@ -0,0 +1,114 @@ +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.entity.EntityPropertyImpl; + +import java.util.*; + +public class NpcTypeImpl { + private final static Map BY_NAME = new HashMap<>(); + + public static Collection values() { + return BY_NAME.values(); + } + + public static NpcTypeImpl byName(String name) { + return BY_NAME.get(name.toUpperCase()); + } + + private final EntityType type; + private final Set> allowedProperties; + private final String name; + private final double hologramOffset; + + private NpcTypeImpl(String name, EntityType type, double hologramOffset, Set> allowedProperties) { + this.name = name.toUpperCase(); + this.type = type; + this.hologramOffset = hologramOffset; + this.allowedProperties = allowedProperties; + } + + public String getName() { + return name; + } + + public EntityType getType() { + return type; + } + + public double getHologramOffset() { + return hologramOffset; + } + + public Set> getAllowedProperties() { + return allowedProperties; + } + + private static NpcTypeImpl define(Builder builder) { + return define(builder.build()); + } + + private static NpcTypeImpl define(NpcTypeImpl type) { + BY_NAME.put(type.getName(), type); + return type; + } + + public static final NpcTypeImpl PLAYER = define( + new Builder("player", EntityTypes.PLAYER) + .addProperties(EntityPropertyImpl.SKIN, EntityPropertyImpl.SKIN_LAYERS) + .setHologramOffset(-0.45D)); + + public static final NpcTypeImpl CREEPER = define( + new Builder("creeper", EntityTypes.CREEPER) + .setHologramOffset(-0.6D)); + + public static final NpcTypeImpl ZOMBIE = define( + new Builder("zombie", EntityTypes.ZOMBIE) + .setHologramOffset(-0.3D)); + + public static final NpcTypeImpl SKELETON = define( + new Builder("skeleton", EntityTypes.SKELETON) + .setHologramOffset(-0.3D)); + + private static final class Builder { + private final String name; + private final EntityType type; + private final List> allowedProperties = new ArrayList<>(); + private boolean globalProperties = true; + private double hologramOffset = 0; + + private Builder(String name, EntityType type) { + this.name = name; + this.type = type; + } + + public Builder addProperties(EntityPropertyImpl... properties) { + allowedProperties.addAll(Arrays.asList(properties)); + return this; + } + + public Builder setEnableGlobalProperties(boolean enabled) { + globalProperties = enabled; + return this; + } + + public Builder setHologramOffset(double hologramOffset) { + this.hologramOffset = hologramOffset; + return this; + } + + public NpcTypeImpl build() { + if (globalProperties) { + allowedProperties.add(EntityPropertyImpl.FIRE); + allowedProperties.add(EntityPropertyImpl.INVISIBLE); + allowedProperties.add(EntityPropertyImpl.SILENT); + if (PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_9)) + allowedProperties.add(EntityPropertyImpl.GLOW); + } + return new NpcTypeImpl(name, type, hologramOffset, new HashSet<>(allowedProperties)); + } + } +} diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/packets/V1_14Factory.java b/plugin/src/main/java/lol/pyr/znpcsplus/packets/V1_14Factory.java index c74f1e4..bcee102 100644 --- a/plugin/src/main/java/lol/pyr/znpcsplus/packets/V1_14Factory.java +++ b/plugin/src/main/java/lol/pyr/znpcsplus/packets/V1_14Factory.java @@ -3,7 +3,7 @@ package lol.pyr.znpcsplus.packets; import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerSpawnEntity; import lol.pyr.znpcsplus.entity.PacketEntity; import lol.pyr.znpcsplus.util.ZLocation; -import lol.pyr.znpcsplus.api.entity.EntityProperty; +import lol.pyr.znpcsplus.entity.EntityPropertyImpl; import lol.pyr.znpcsplus.api.entity.PropertyHolder; import org.bukkit.entity.Player; @@ -15,7 +15,7 @@ public class V1_14Factory extends V1_9Factory { ZLocation location = entity.getLocation(); sendPacket(player, new WrapperPlayServerSpawnEntity(entity.getEntityId(), Optional.of(entity.getUuid()), entity.getType(), location.toVector3d(), location.getPitch(), location.getYaw(), location.getYaw(), 0, Optional.empty())); - if (properties.hasProperty(EntityProperty.GLOW)) createTeam(player, entity, properties); + if (properties.hasProperty(EntityPropertyImpl.GLOW)) createTeam(player, entity, properties); sendAllMetadata(player, entity, properties); } } diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/packets/V1_8Factory.java b/plugin/src/main/java/lol/pyr/znpcsplus/packets/V1_8Factory.java index cd4c0a6..8d6ab58 100644 --- a/plugin/src/main/java/lol/pyr/znpcsplus/packets/V1_8Factory.java +++ b/plugin/src/main/java/lol/pyr/znpcsplus/packets/V1_8Factory.java @@ -12,7 +12,7 @@ import com.github.retrooper.packetevents.wrapper.PacketWrapper; import com.github.retrooper.packetevents.wrapper.play.server.*; import lol.pyr.znpcsplus.ZNpcsPlus; import lol.pyr.znpcsplus.api.entity.PropertyHolder; -import lol.pyr.znpcsplus.api.entity.EntityProperty; +import lol.pyr.znpcsplus.entity.EntityPropertyImpl; import lol.pyr.znpcsplus.entity.PacketEntity; import lol.pyr.znpcsplus.metadata.MetadataFactory; import lol.pyr.znpcsplus.skin.BaseSkinDescriptor; @@ -94,7 +94,7 @@ public class V1_8Factory implements PacketFactory { Component.empty(), Component.empty(), Component.empty(), WrapperPlayServerTeams.NameTagVisibility.NEVER, WrapperPlayServerTeams.CollisionRule.NEVER, - properties.hasProperty(EntityProperty.GLOW) ? properties.getProperty(EntityProperty.GLOW) : NamedTextColor.WHITE, + properties.hasProperty(EntityPropertyImpl.GLOW) ? properties.getProperty(EntityPropertyImpl.GLOW) : NamedTextColor.WHITE, WrapperPlayServerTeams.OptionData.NONE ))); sendPacket(player, new WrapperPlayServerTeams("npc_team_" + entity.getEntityId(), WrapperPlayServerTeams.TeamMode.ADD_ENTITIES, (WrapperPlayServerTeams.ScoreBoardTeamInfo) null, @@ -109,12 +109,12 @@ public class V1_8Factory implements PacketFactory { @Override public void sendAllMetadata(Player player, PacketEntity entity, PropertyHolder properties) { ArrayList data = new ArrayList<>(); - if (entity.getType() == EntityTypes.PLAYER && properties.getProperty(EntityProperty.SKIN_LAYERS)) data.add(MetadataFactory.get().skinLayers()); - boolean fire = properties.getProperty(EntityProperty.FIRE); - boolean invisible = properties.getProperty(EntityProperty.INVISIBLE); + if (entity.getType() == EntityTypes.PLAYER && properties.getProperty(EntityPropertyImpl.SKIN_LAYERS)) data.add(MetadataFactory.get().skinLayers()); + boolean fire = properties.getProperty(EntityPropertyImpl.FIRE); + boolean invisible = properties.getProperty(EntityPropertyImpl.INVISIBLE); if (fire || invisible) data.add(MetadataFactory.get().effects(fire, false, invisible)); - if (properties.getProperty(EntityProperty.SILENT)) data.add(MetadataFactory.get().silent()); - if (properties.hasProperty(EntityProperty.NAME)) data.addAll(MetadataFactory.get().name(properties.getProperty(EntityProperty.NAME))); + if (properties.getProperty(EntityPropertyImpl.SILENT)) data.add(MetadataFactory.get().silent()); + if (properties.hasProperty(EntityPropertyImpl.NAME)) data.addAll(MetadataFactory.get().name(properties.getProperty(EntityPropertyImpl.NAME))); sendMetadata(player, entity, data); } @@ -128,8 +128,8 @@ public class V1_8Factory implements PacketFactory { } protected CompletableFuture skinned(Player player, PropertyHolder properties, UserProfile profile) { - if (!properties.hasProperty(EntityProperty.SKIN)) return CompletableFuture.completedFuture(profile); - BaseSkinDescriptor descriptor = (BaseSkinDescriptor) properties.getProperty(EntityProperty.SKIN); + if (!properties.hasProperty(EntityPropertyImpl.SKIN)) return CompletableFuture.completedFuture(profile); + BaseSkinDescriptor descriptor = (BaseSkinDescriptor) properties.getProperty(EntityPropertyImpl.SKIN); if (descriptor.supportsInstant(player)) { descriptor.fetchInstant(player).apply(profile); return CompletableFuture.completedFuture(profile); diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/packets/V1_9Factory.java b/plugin/src/main/java/lol/pyr/znpcsplus/packets/V1_9Factory.java index 2f5fb41..2006095 100644 --- a/plugin/src/main/java/lol/pyr/znpcsplus/packets/V1_9Factory.java +++ b/plugin/src/main/java/lol/pyr/znpcsplus/packets/V1_9Factory.java @@ -4,7 +4,7 @@ import com.github.retrooper.packetevents.protocol.entity.data.EntityData; import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes; import lol.pyr.znpcsplus.entity.PacketEntity; import lol.pyr.znpcsplus.metadata.MetadataFactory; -import lol.pyr.znpcsplus.api.entity.EntityProperty; +import lol.pyr.znpcsplus.entity.EntityPropertyImpl; import lol.pyr.znpcsplus.api.entity.PropertyHolder; import org.bukkit.entity.Player; @@ -14,13 +14,13 @@ public class V1_9Factory extends V1_8Factory { @Override public void sendAllMetadata(Player player, PacketEntity entity, PropertyHolder properties) { ArrayList data = new ArrayList<>(); - if (entity.getType() == EntityTypes.PLAYER && properties.getProperty(EntityProperty.SKIN_LAYERS)) data.add(MetadataFactory.get().skinLayers()); - boolean glow = properties.hasProperty(EntityProperty.GLOW); - boolean fire = properties.getProperty(EntityProperty.FIRE); - boolean invisible = properties.getProperty(EntityProperty.INVISIBLE); + if (entity.getType() == EntityTypes.PLAYER && properties.getProperty(EntityPropertyImpl.SKIN_LAYERS)) data.add(MetadataFactory.get().skinLayers()); + boolean glow = properties.hasProperty(EntityPropertyImpl.GLOW); + boolean fire = properties.getProperty(EntityPropertyImpl.FIRE); + boolean invisible = properties.getProperty(EntityPropertyImpl.INVISIBLE); if (glow || fire || invisible) data.add(MetadataFactory.get().effects(fire, glow, invisible)); - if (properties.getProperty(EntityProperty.SILENT)) data.add(MetadataFactory.get().silent()); - if (properties.hasProperty(EntityProperty.NAME)) data.addAll(MetadataFactory.get().name(properties.getProperty(EntityProperty.NAME))); + if (properties.getProperty(EntityPropertyImpl.SILENT)) data.add(MetadataFactory.get().silent()); + if (properties.hasProperty(EntityPropertyImpl.NAME)) data.addAll(MetadataFactory.get().name(properties.getProperty(EntityPropertyImpl.NAME))); sendMetadata(player, entity, data); } } diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/skin/BaseSkinDescriptor.java b/plugin/src/main/java/lol/pyr/znpcsplus/skin/BaseSkinDescriptor.java index 3749d35..b3bc55e 100644 --- a/plugin/src/main/java/lol/pyr/znpcsplus/skin/BaseSkinDescriptor.java +++ b/plugin/src/main/java/lol/pyr/znpcsplus/skin/BaseSkinDescriptor.java @@ -1,11 +1,33 @@ package lol.pyr.znpcsplus.skin; +import com.github.retrooper.packetevents.protocol.player.TextureProperty; +import lol.pyr.znpcsplus.api.skin.SkinDescriptor; +import lol.pyr.znpcsplus.skin.descriptor.FetchingDescriptor; +import lol.pyr.znpcsplus.skin.descriptor.MirrorDescriptor; +import lol.pyr.znpcsplus.skin.descriptor.PrefetchedDescriptor; import org.bukkit.entity.Player; +import java.util.ArrayList; +import java.util.List; import java.util.concurrent.CompletableFuture; -public interface BaseSkinDescriptor { +public interface BaseSkinDescriptor extends SkinDescriptor { CompletableFuture fetch(Player player); Skin fetchInstant(Player player); boolean supportsInstant(Player player); + String serialize(); + + static BaseSkinDescriptor deserialize(String str) { + String[] arr = str.split(";"); + if (arr[0].equalsIgnoreCase("mirror")) return new MirrorDescriptor(); + else if (arr[0].equalsIgnoreCase("fetching")) return new FetchingDescriptor(arr[1]); + else if (arr[0].equalsIgnoreCase("prefetched")) { + List properties = new ArrayList<>(); + for (int i = 0; i < (arr.length - 1) / 3; i++) { + properties.add(new TextureProperty(arr[i + 1], arr[i + 2], arr[i + 3])); + } + return new PrefetchedDescriptor(new Skin(properties)); + } + throw new IllegalArgumentException("Unknown SkinDescriptor type!"); + } } diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/skin/Skin.java b/plugin/src/main/java/lol/pyr/znpcsplus/skin/Skin.java index ff0ed16..b0be451 100644 --- a/plugin/src/main/java/lol/pyr/znpcsplus/skin/Skin.java +++ b/plugin/src/main/java/lol/pyr/znpcsplus/skin/Skin.java @@ -8,6 +8,7 @@ import com.mojang.authlib.properties.PropertyMap; import lol.pyr.znpcsplus.util.list.ListUtil; import java.util.ArrayList; +import java.util.Collection; import java.util.List; import java.util.stream.Collectors; @@ -23,6 +24,10 @@ public class Skin { this.properties.addAll(ListUtil.immutableList(properties)); } + public Skin(Collection properties) { + this.properties.addAll(properties); + } + public Skin(PropertyMap properties) { this.properties.addAll(properties.values().stream() .map(property -> new TextureProperty(property.getName(), property.getValue(), property.getSignature())) @@ -32,7 +37,7 @@ public class Skin { public Skin(JsonObject obj) { for (JsonElement e : obj.get("properties").getAsJsonArray()) { JsonObject o = e.getAsJsonObject(); - properties.add(new TextureProperty(o.get("name").getAsString(), o.get("value").getAsString(), o.has("signature") ? o.get("signature").getAsString() : null)); + properties.add(new TextureProperty(o.get("getName").getAsString(), o.get("value").getAsString(), o.has("signature") ? o.get("signature").getAsString() : null)); } } @@ -41,6 +46,10 @@ public class Skin { return profile; } + public List getProperties() { + return properties; + } + public boolean isExpired() { return System.currentTimeMillis() - timestamp > 60000L; } diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/skin/descriptor/FetchingDescriptor.java b/plugin/src/main/java/lol/pyr/znpcsplus/skin/descriptor/FetchingDescriptor.java index 616c2bb..e53cbd0 100644 --- a/plugin/src/main/java/lol/pyr/znpcsplus/skin/descriptor/FetchingDescriptor.java +++ b/plugin/src/main/java/lol/pyr/znpcsplus/skin/descriptor/FetchingDescriptor.java @@ -36,4 +36,13 @@ public class FetchingDescriptor implements BaseSkinDescriptor, SkinDescriptor { if (ZNpcsPlus.PLACEHOLDERS_SUPPORTED) return PlaceholderAPI.setPlaceholders(player, name); return name; } + + public String getName() { + return name; + } + + @Override + public String serialize() { + return "fetching;" + name; + } } diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/skin/descriptor/MirrorDescriptor.java b/plugin/src/main/java/lol/pyr/znpcsplus/skin/descriptor/MirrorDescriptor.java index 81379a7..f44ab4c 100644 --- a/plugin/src/main/java/lol/pyr/znpcsplus/skin/descriptor/MirrorDescriptor.java +++ b/plugin/src/main/java/lol/pyr/znpcsplus/skin/descriptor/MirrorDescriptor.java @@ -26,4 +26,9 @@ public class MirrorDescriptor implements BaseSkinDescriptor, SkinDescriptor { public boolean supportsInstant(Player player) { return true; } + + @Override + public String serialize() { + return "mirror"; + } } diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/skin/descriptor/PrefetchedDescriptor.java b/plugin/src/main/java/lol/pyr/znpcsplus/skin/descriptor/PrefetchedDescriptor.java index b388051..954ed80 100644 --- a/plugin/src/main/java/lol/pyr/znpcsplus/skin/descriptor/PrefetchedDescriptor.java +++ b/plugin/src/main/java/lol/pyr/znpcsplus/skin/descriptor/PrefetchedDescriptor.java @@ -1,5 +1,6 @@ package lol.pyr.znpcsplus.skin.descriptor; +import com.github.retrooper.packetevents.protocol.player.TextureProperty; import lol.pyr.znpcsplus.api.skin.SkinDescriptor; import lol.pyr.znpcsplus.skin.BaseSkinDescriptor; import lol.pyr.znpcsplus.skin.Skin; @@ -28,4 +29,20 @@ public class PrefetchedDescriptor implements BaseSkinDescriptor, SkinDescriptor public boolean supportsInstant(Player player) { return true; } + + public Skin getSkin() { + return skin; + } + + @Override + public String serialize() { + StringBuilder sb = new StringBuilder(); + sb.append("prefetched;"); + for (TextureProperty property : skin.getProperties()) { + sb.append(property.getName()).append(";"); + sb.append(property.getValue()).append(";"); + sb.append(property.getSignature()).append(";"); + } + return sb.toString(); + } } diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/storage/NpcStorage.java b/plugin/src/main/java/lol/pyr/znpcsplus/storage/NpcStorage.java new file mode 100644 index 0000000..9866b4b --- /dev/null +++ b/plugin/src/main/java/lol/pyr/znpcsplus/storage/NpcStorage.java @@ -0,0 +1,10 @@ +package lol.pyr.znpcsplus.storage; + +import lol.pyr.znpcsplus.npc.NpcEntryImpl; + +import java.util.Collection; + +public interface NpcStorage { + Collection loadNpcs(); + void saveNpcs(Collection npcs); +} diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/storage/NpcStorageType.java b/plugin/src/main/java/lol/pyr/znpcsplus/storage/NpcStorageType.java new file mode 100644 index 0000000..9e1bbaa --- /dev/null +++ b/plugin/src/main/java/lol/pyr/znpcsplus/storage/NpcStorageType.java @@ -0,0 +1,14 @@ +package lol.pyr.znpcsplus.storage; + +import lol.pyr.znpcsplus.storage.yaml.YamlStorage; + +public enum NpcStorageType { + YAML { + @Override + public NpcStorage create() { + return new YamlStorage(); + } + }; + + public abstract NpcStorage create(); +} diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/storage/SerializationException.java b/plugin/src/main/java/lol/pyr/znpcsplus/storage/SerializationException.java new file mode 100644 index 0000000..278b7ee --- /dev/null +++ b/plugin/src/main/java/lol/pyr/znpcsplus/storage/SerializationException.java @@ -0,0 +1,7 @@ +package lol.pyr.znpcsplus.storage; + +public class SerializationException extends RuntimeException { + public SerializationException(String message) { + super(message); + } +} diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/storage/yaml/YamlStorage.java b/plugin/src/main/java/lol/pyr/znpcsplus/storage/yaml/YamlStorage.java new file mode 100644 index 0000000..75fb678 --- /dev/null +++ b/plugin/src/main/java/lol/pyr/znpcsplus/storage/yaml/YamlStorage.java @@ -0,0 +1,133 @@ +package lol.pyr.znpcsplus.storage.yaml; + +import lol.pyr.znpcsplus.ZNpcsPlus; +import lol.pyr.znpcsplus.entity.EntityPropertyImpl; +import lol.pyr.znpcsplus.hologram.HologramLine; +import lol.pyr.znpcsplus.interaction.NpcAction; +import lol.pyr.znpcsplus.interaction.NpcActionType; +import lol.pyr.znpcsplus.npc.NpcEntryImpl; +import lol.pyr.znpcsplus.npc.NpcImpl; +import lol.pyr.znpcsplus.npc.NpcTypeImpl; +import lol.pyr.znpcsplus.storage.NpcStorage; +import lol.pyr.znpcsplus.util.ZLocation; +import net.kyori.adventure.text.minimessage.MiniMessage; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.file.YamlConfiguration; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +public class YamlStorage implements NpcStorage { + private final File npcsFolder; + + public YamlStorage() { + npcsFolder = new File(ZNpcsPlus.PLUGIN_FOLDER, "npcs"); + if (npcsFolder.exists()) npcsFolder.mkdirs(); + } + + @SuppressWarnings("ConstantConditions") + @Override + public Collection loadNpcs() { + File[] files = npcsFolder.listFiles(); + if (files == null || files.length == 0) return Collections.emptyList(); + List npcs = new ArrayList<>(); + for (File file : files) if (file.isFile() && file.getName().toLowerCase().endsWith(".yml")) { + YamlConfiguration config = YamlConfiguration.loadConfiguration(file); + NpcImpl npc = new NpcImpl(config.getString("world"), NpcTypeImpl.byName(config.getString("type")), + deserializeLocation(config.getConfigurationSection("location"))); + + ConfigurationSection properties = config.getConfigurationSection("properties"); + for (String key : properties.getKeys(false)) { + EntityPropertyImpl property = EntityPropertyImpl.getByName(key); + _setProperty(npc, property, property.deserialize(properties.getString(key))); + } + + for (String line : config.getStringList("hologram")) { + npc.getHologram().addLine(MiniMessage.miniMessage().deserialize(line)); + } + + int amt = config.getInt("action-amount"); + for (int i = 1; i <= amt; i++) { + String key = "actions." + i; + npc.addAction(NpcActionType.valueOf(config.getString(key + ".type")) + .deserialize(config.getInt(key + ".cooldown"), config.getString(key + ".argument"))); + } + + NpcEntryImpl entry = new NpcEntryImpl(config.getString("id"), npc); + entry.setProcessed(config.getBoolean("is-processed")); + entry.setAllowCommandModification(config.getBoolean("allow-commands")); + entry.setSave(true); + + npcs.add(entry); + } + return npcs; + } + + @SuppressWarnings("unchecked") + private void _setProperty(NpcImpl npc, EntityPropertyImpl property, Object value) { + npc.setProperty((EntityPropertyImpl) property, (T) value); + } + + + @Override + public void saveNpcs(Collection npcs) { + for (NpcEntryImpl entry : npcs) if (entry.isSave()) try { + YamlConfiguration config = new YamlConfiguration(); + config.set("id", entry.getId()); + config.set("is-processed", entry.isProcessed()); + config.set("allow-commands", entry.isAllowCommandModification()); + + NpcImpl npc = entry.getNpc(); + config.set("world", npc.getWorldName()); + config.set("location", serializeLocation(npc.getLocation())); + config.set("type", npc.getType().getName()); + + for (EntityPropertyImpl property : npc.getAppliedProperties()) { + config.set("properties." + property.getName(), property.serialize(npc)); + } + + List lines = new ArrayList<>(); + for (HologramLine line : npc.getHologram().getLines()) { + lines.add(MiniMessage.miniMessage().serialize(line.getText())); + } + config.set("hologram", lines); + + int i = 0; + for (NpcAction action : npc.getActions()) { i++; + String key = "actions." + i; + config.set(key + ".type", action.getType().name()); + config.set(key + ".cooldown", action.getCooldown()); + config.set(key + ".argument", action.getArgument()); + } + config.set("action-amount", i); + + config.save(new File(entry.getId() + ".yml")); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public ZLocation deserializeLocation(ConfigurationSection section) { + return new ZLocation( + section.getDouble("x"), + section.getDouble("y"), + section.getDouble("z"), + (float) section.getDouble("yaw"), + (float) section.getDouble("pitch") + ); + } + + public YamlConfiguration serializeLocation(ZLocation location) { + YamlConfiguration config = new YamlConfiguration(); + config.set("x", location.getX()); + config.set("y", location.getY()); + config.set("z", location.getZ()); + config.set("yaw", location.getYaw()); + config.set("pitch", location.getPitch()); + return config; + } +}