diff --git a/.github/znpc.png b/.github/znpc.png new file mode 100644 index 0000000..06a8686 Binary files /dev/null and b/.github/znpc.png differ diff --git a/README.md b/README.md index d947365..0d0eeb0 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ that players can interact with to perform actions like switching servers on a network or executing commands. This plugin is a remake of a plugin called ZNPCs, we originally started because the maintainer of ZNPCs decided to announce that he was -[dropping support for the plugin](https://media.discordapp.net/attachments/1093914615873806477/1098409384855474237/znpc.png). +[dropping support for the plugin](https://github.com/Pyrbu/ZNPCsPlus/blob/2.X/.github/znpc.png?raw=true). Looking for up-to-date builds of the plugin? Check out our [Jenkins](https://ci.pyr.lol/job/ZNPCsPlus/) @@ -19,7 +19,7 @@ Looking for up-to-date builds of the plugin? Check out our [Jenkins](https://ci. ### Requirements, Extensions & Supported Software Requirements: - Java 8+ -- Minecraft 1.8 - 1.20.4 +- Minecraft 1.8 - 1.21 Supported Softwares: - Spigot ([Website](https://www.spigotmc.org/)) @@ -43,7 +43,6 @@ Open an issue in the GitHub [issue tracker](https://github.com/Pyrbu/ZNPCsPlus/i - [wiki.vg](https://wiki.vg/Main_Page) - Minecraft protocol documentation - [gson](https://github.com/google/gson) - JSON parsing library made by Google - [Mineskin.org](https://mineskin.org/) - Website for raw skin file uploads -- [SpigotResourcesAPI](https://github.com/robertlit/SpigotResourcesAPI/) - Spigot API wrapper used for updater - [adventure](https://docs.advntr.dev/) - Minecraft text api - [DazzleConf](https://github.com/A248/DazzleConf) - Configuration library - [Director](https://github.com/Pyrbu/Director) - Command library diff --git a/api/src/main/java/lol/pyr/znpcsplus/api/NpcPropertyRegistryProvider.java b/api/src/main/java/lol/pyr/znpcsplus/api/NpcPropertyRegistryProvider.java new file mode 100644 index 0000000..83b3e2c --- /dev/null +++ b/api/src/main/java/lol/pyr/znpcsplus/api/NpcPropertyRegistryProvider.java @@ -0,0 +1,46 @@ +package lol.pyr.znpcsplus.api; + +import lol.pyr.znpcsplus.api.entity.EntityPropertyRegistry; +import org.bukkit.Bukkit; + +/** + * Provider for the registered entity property registry instance + */ +public class NpcPropertyRegistryProvider { + private static EntityPropertyRegistry registry = null; + + private NpcPropertyRegistryProvider() { + throw new UnsupportedOperationException(); + } + + /** + * Static method that returns the entity property registry instance of the plugin + * + * @return The instance of the entity property registry for the ZNPCsPlus plugin + */ + public static EntityPropertyRegistry get() { + if (registry == null) throw new IllegalStateException( + "ZNPCsPlus plugin isn't loaded yet!\n" + + "Please add it to your plugin.yml as a depend or softdepend." + ); + return registry; + } + + /** + * Internal method used to register the main instance of the plugin as the entity property registry provider + * You probably shouldn't call this method under any circumstances + * + * @param api Instance of the ZNPCsPlus entity property registry + */ + public static void register(EntityPropertyRegistry api) { + NpcPropertyRegistryProvider.registry = api; + } + + /** + * Internal method used to unregister the plugin from the provider when the plugin shuts down + * You probably shouldn't call this method under any circumstances + */ + public static void unregister() { + Bukkit.getServicesManager().unregister(registry); + } +} diff --git a/api/src/main/java/lol/pyr/znpcsplus/api/npc/Npc.java b/api/src/main/java/lol/pyr/znpcsplus/api/npc/Npc.java index 4c3f61d..6081060 100644 --- a/api/src/main/java/lol/pyr/znpcsplus/api/npc/Npc.java +++ b/api/src/main/java/lol/pyr/znpcsplus/api/npc/Npc.java @@ -38,6 +38,18 @@ public interface Npc extends PropertyHolder { */ void setLocation(NpcLocation location); + /** + * Sets the world of this NPC + * @param world The bukkit world to set + */ + void setWorld(World world); + + /** + * Sets the world of this NPC + * @param name The name world to set + */ + void setWorld(String name); + /** * Gets the hologram of this NPC * @return The {@link Hologram} of this NPC @@ -69,6 +81,13 @@ public interface Npc extends PropertyHolder { */ World getWorld(); + /** + * Gets the name of the world this NPC is in + * Unlike {@link Npc#getWorld()} this will never be null + * @return The name of the world this NPC is in + */ + String getWorldName(); + /** * Gets the list of actions for this NPC * @return The {@link List} of {@link InteractionAction}s for this NPC diff --git a/api/src/main/java/lol/pyr/znpcsplus/util/NpcPose.java b/api/src/main/java/lol/pyr/znpcsplus/util/NpcPose.java index 4036e94..605702e 100644 --- a/api/src/main/java/lol/pyr/znpcsplus/util/NpcPose.java +++ b/api/src/main/java/lol/pyr/znpcsplus/util/NpcPose.java @@ -20,5 +20,8 @@ public enum NpcPose { ROARING, SNIFFING, EMERGING, - DIGGING + DIGGING, + SLIDING, + SHOOTING, + INHALING, } diff --git a/plugin/build.gradle b/plugin/build.gradle index eb428a9..9d389db 100644 --- a/plugin/build.gradle +++ b/plugin/build.gradle @@ -4,23 +4,15 @@ plugins { id "xyz.jpenilla.run-paper" version "2.2.0" } -processResources { - expand("version": version) +runServer { + javaLauncher = javaToolchains.launcherFor { + languageVersion = JavaLanguageVersion.of(21) + } + minecraftVersion "1.20.6" } -publishing { - publications { - mavenJava(MavenPublication) { - from components.java - artifactId = "znpcsplus-plugin" - - pom { - name.set("znpcsplus-plugin") - description.set("The ZNPCsPlus plugin") - url.set("https://github.com/Pyrbu/ZNPCsPlus") - } - } - } +processResources { + expand("version": version) } dependencies { @@ -28,12 +20,11 @@ dependencies { compileOnly "com.github.retrooper:packetevents-spigot:2.6.0" // Packets implementation "com.google.code.gson:gson:2.10.1" // JSON parsing implementation "org.bstats:bstats-bukkit:3.0.2" // Plugin stats - implementation "me.robertlit:SpigotResourcesAPI:2.0" // Spigot API wrapper for update checker implementation "space.arim.dazzleconf:dazzleconf-ext-snakeyaml:1.2.1" // Configs implementation "lol.pyr:director-adventure:2.1.2" // Commands // Fancy text library - implementation "net.kyori:adventure-platform-bukkit:4.3.3" + implementation "net.kyori:adventure-platform-bukkit:4.3.4" implementation "net.kyori:adventure-text-minimessage:4.17.0" implementation project(":api") @@ -52,6 +43,7 @@ shadowJar { relocate "net.kyori", "lol.pyr.znpcsplus.libraries.kyori" relocate "org.checkerframework", "lol.pyr.znpcsplus.libraries.checkerframework" relocate "com.google", "lol.pyr.znpcsplus.libraries.google" + relocate "com.google.gson", "lol.pyr.znpcsplus.libraries.gson" relocate "org.yaml.snakeyaml", "lol.pyr.znpcsplus.libraries.snakeyaml" relocate "space.arim.dazzleconf", "lol.pyr.znpcsplus.libraries.dazzleconf" relocate "lol.pyr.director", "lol.pyr.znpcsplus.libraries.command" diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/ZNpcsPlus.java b/plugin/src/main/java/lol/pyr/znpcsplus/ZNpcsPlus.java index 4991b16..c4d1f9a 100644 --- a/plugin/src/main/java/lol/pyr/znpcsplus/ZNpcsPlus.java +++ b/plugin/src/main/java/lol/pyr/znpcsplus/ZNpcsPlus.java @@ -14,6 +14,7 @@ import lol.pyr.director.adventure.parse.primitive.FloatParser; import lol.pyr.director.adventure.parse.primitive.IntegerParser; import lol.pyr.director.common.message.Message; import lol.pyr.znpcsplus.api.NpcApiProvider; +import lol.pyr.znpcsplus.api.NpcPropertyRegistryProvider; import lol.pyr.znpcsplus.api.interaction.InteractionType; import lol.pyr.znpcsplus.commands.*; import lol.pyr.znpcsplus.commands.action.*; @@ -22,6 +23,7 @@ import lol.pyr.znpcsplus.commands.property.PropertyRemoveCommand; import lol.pyr.znpcsplus.commands.property.PropertySetCommand; import lol.pyr.znpcsplus.commands.storage.ImportCommand; import lol.pyr.znpcsplus.commands.storage.LoadAllCommand; +import lol.pyr.znpcsplus.commands.storage.MigrateCommand; import lol.pyr.znpcsplus.commands.storage.SaveAllCommand; import lol.pyr.znpcsplus.config.ConfigManager; import lol.pyr.znpcsplus.conversion.DataImporterRegistry; @@ -38,6 +40,7 @@ import lol.pyr.znpcsplus.scheduling.SpigotScheduler; import lol.pyr.znpcsplus.scheduling.TaskScheduler; import lol.pyr.znpcsplus.skin.cache.MojangSkinCache; import lol.pyr.znpcsplus.skin.cache.SkinCacheCleanTask; +import lol.pyr.znpcsplus.storage.NpcStorageType; import lol.pyr.znpcsplus.tasks.HologramRefreshTask; import lol.pyr.znpcsplus.tasks.NpcProcessorTask; import lol.pyr.znpcsplus.tasks.ViewableHideOnLeaveListener; @@ -78,12 +81,23 @@ public class ZNpcsPlus { private final PacketEventsAPI packetEvents; private final ZNpcsPlusBootstrap bootstrap; + private final ConfigManager configManager; + private final MojangSkinCache skinCache; + private final EntityPropertyRegistryImpl propertyRegistry; + public ZNpcsPlus(ZNpcsPlusBootstrap bootstrap) { this.bootstrap = bootstrap; packetEvents = SpigotPacketEventsBuilder.build(bootstrap); PacketEvents.setAPI(packetEvents); packetEvents.getSettings().checkForUpdates(false); packetEvents.load(); + + configManager = new ConfigManager(getDataFolder()); + skinCache = new MojangSkinCache(configManager); + propertyRegistry = new EntityPropertyRegistryImpl(skinCache, configManager); + + NpcPropertyRegistryProvider.register(propertyRegistry); + shutdownTasks.add(NpcPropertyRegistryProvider::unregister); } private void log(String str) { @@ -113,11 +127,9 @@ public class ZNpcsPlus { TaskScheduler scheduler = FoliaUtil.isFolia() ? new FoliaScheduler(bootstrap) : new SpigotScheduler(bootstrap); shutdownTasks.add(scheduler::cancelAll); - ConfigManager configManager = new ConfigManager(getDataFolder()); - MojangSkinCache skinCache = new MojangSkinCache(configManager); - EntityPropertyRegistryImpl propertyRegistry = new EntityPropertyRegistryImpl(skinCache, configManager); + PacketFactory packetFactory = setupPacketFactory(scheduler, propertyRegistry, configManager); - propertyRegistry.registerTypes(bootstrap, packetFactory, textSerializer); + propertyRegistry.registerTypes(bootstrap, packetFactory, textSerializer, scheduler); BungeeConnector bungeeConnector = new BungeeConnector(bootstrap); ActionRegistryImpl actionRegistry = new ActionRegistryImpl(); @@ -147,7 +159,7 @@ public class ZNpcsPlus { pluginManager.registerEvents(new UserListener(userManager), bootstrap); registerCommands(npcRegistry, skinCache, adventure, actionRegistry, - typeRegistry, propertyRegistry, importerRegistry, configManager); + typeRegistry, propertyRegistry, importerRegistry, configManager, packetFactory); log(ChatColor.WHITE + " * Starting tasks..."); if (configManager.getConfig().checkForUpdates()) { @@ -219,6 +231,7 @@ public class ZNpcsPlus { versions.put(ServerVersion.V_1_17, LazyLoader.of(() -> new V1_17PacketFactory(scheduler, packetEvents, propertyRegistry, textSerializer, configManager))); versions.put(ServerVersion.V_1_19_3, LazyLoader.of(() -> new V1_19_3PacketFactory(scheduler, packetEvents, propertyRegistry, textSerializer, configManager))); versions.put(ServerVersion.V_1_20_2, LazyLoader.of(() -> new V1_20_2PacketFactory(scheduler, packetEvents, propertyRegistry, textSerializer, configManager))); + versions.put(ServerVersion.V_1_21_3, LazyLoader.of(() -> new V1_21_3PacketFactory(scheduler, packetEvents, propertyRegistry, textSerializer, configManager))); ServerVersion version = packetEvents.getServerManager().getVersion(); if (versions.containsKey(version)) return versions.get(version).get(); @@ -233,7 +246,7 @@ public class ZNpcsPlus { private void registerCommands(NpcRegistryImpl npcRegistry, MojangSkinCache skinCache, BukkitAudiences adventure, ActionRegistryImpl actionRegistry, NpcTypeRegistryImpl typeRegistry, EntityPropertyRegistryImpl propertyRegistry, DataImporterRegistry importerRegistry, - ConfigManager configManager) { + ConfigManager configManager, PacketFactory packetFactory) { Message incorrectUsageMessage = context -> context.send(Component.text("Incorrect usage: /" + context.getUsage(), NamedTextColor.RED)); CommandManager manager = new CommandManager(bootstrap, adventure, incorrectUsageMessage); @@ -282,6 +295,7 @@ public class ZNpcsPlus { registerEnumParser(manager, Sound.class, incorrectUsageMessage); registerEnumParser(manager, ArmadilloState.class, incorrectUsageMessage); registerEnumParser(manager, WoldVariant.class, incorrectUsageMessage); + registerEnumParser(manager, NpcStorageType.class, incorrectUsageMessage); manager.registerCommand("npc", new MultiCommand(bootstrap.loadHelpMessage("root")) .addSubcommand("center", new CenterCommand(npcRegistry)) @@ -306,7 +320,8 @@ public class ZNpcsPlus { .addSubcommand("storage", new MultiCommand(bootstrap.loadHelpMessage("storage")) .addSubcommand("save", new SaveAllCommand(npcRegistry)) .addSubcommand("reload", new LoadAllCommand(npcRegistry)) - .addSubcommand("import", new ImportCommand(npcRegistry, importerRegistry))) + .addSubcommand("import", new ImportCommand(npcRegistry, importerRegistry)) + .addSubcommand("migrate", new MigrateCommand(configManager, this, packetFactory, actionRegistry, typeRegistry, propertyRegistry, textSerializer, npcRegistry.getStorage(), configManager.getConfig().storageType(), npcRegistry))) .addSubcommand("holo", new MultiCommand(bootstrap.loadHelpMessage("holo")) .addSubcommand("add", new HoloAddCommand(npcRegistry)) .addSubcommand("additem", new HoloAddItemCommand(npcRegistry)) diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/commands/storage/MigrateCommand.java b/plugin/src/main/java/lol/pyr/znpcsplus/commands/storage/MigrateCommand.java new file mode 100644 index 0000000..cda3b3c --- /dev/null +++ b/plugin/src/main/java/lol/pyr/znpcsplus/commands/storage/MigrateCommand.java @@ -0,0 +1,176 @@ +package lol.pyr.znpcsplus.commands.storage; + +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.ZNpcsPlus; +import lol.pyr.znpcsplus.config.ConfigManager; +import lol.pyr.znpcsplus.entity.EntityPropertyRegistryImpl; +import lol.pyr.znpcsplus.interaction.ActionRegistryImpl; +import lol.pyr.znpcsplus.npc.NpcEntryImpl; +import lol.pyr.znpcsplus.npc.NpcRegistryImpl; +import lol.pyr.znpcsplus.npc.NpcTypeRegistryImpl; +import lol.pyr.znpcsplus.packets.PacketFactory; +import lol.pyr.znpcsplus.storage.NpcStorage; +import lol.pyr.znpcsplus.storage.NpcStorageType; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.atomic.AtomicReference; +import java.util.stream.Collectors; + +public class MigrateCommand implements CommandHandler { + private final ConfigManager configManager; + private final ZNpcsPlus plugin; + private final PacketFactory packetFactory; + private final ActionRegistryImpl actionRegistry; + private final NpcTypeRegistryImpl typeRegistry; + private final EntityPropertyRegistryImpl propertyRegistry; + private final LegacyComponentSerializer textSerializer; + private final NpcStorage currentStorage; + private final NpcStorageType currentStorageType; + private final NpcRegistryImpl npcRegistry; + + public MigrateCommand(ConfigManager configManager, ZNpcsPlus plugin, PacketFactory packetFactory, ActionRegistryImpl actionRegistry, NpcTypeRegistryImpl typeRegistry, EntityPropertyRegistryImpl propertyRegistry, LegacyComponentSerializer textSerializer, NpcStorage currentStorage, NpcStorageType currentStorageType, NpcRegistryImpl npcRegistry) { + this.configManager = configManager; + this.plugin = plugin; + this.packetFactory = packetFactory; + this.actionRegistry = actionRegistry; + this.typeRegistry = typeRegistry; + this.propertyRegistry = propertyRegistry; + this.textSerializer = textSerializer; + this.currentStorage = currentStorage; + this.currentStorageType = currentStorageType; + this.npcRegistry = npcRegistry; + } + + @Override + public void run(CommandContext context) throws CommandExecutionException { + context.setUsage(context.getLabel() + " storage migrate [force]"); + NpcStorageType from = context.parse(NpcStorageType.class); + NpcStorageType to = context.parse(NpcStorageType.class); + boolean force = context.argSize() > 2 && context.parse(Boolean.class); + if (from.equals(to)) { + context.halt(Component.text("The storage types must be different.", NamedTextColor.RED)); + return; + } + NpcStorage fromStorage; + if (currentStorageType == from) { + fromStorage = currentStorage; + } else { + fromStorage = from.create(configManager, plugin, packetFactory, actionRegistry, typeRegistry, propertyRegistry, textSerializer); + if (fromStorage == null) { + context.halt(Component.text("Failed to initialize the source storage. Please check the console for more information.", NamedTextColor.RED)); + return; + } + } + Collection entries; + try { + entries = fromStorage.loadNpcs(); + } catch (Exception e) { + context.halt(Component.text("Failed to load NPCs from the source storage.", NamedTextColor.RED)); + return; + } + if (entries.isEmpty()) { + context.send(Component.text("No NPCs to migrate.", NamedTextColor.YELLOW)); + return; + } + NpcStorage toStorage; + if (currentStorageType == to) { + toStorage = currentStorage; + } else { + toStorage = to.create(configManager, plugin, packetFactory, actionRegistry, typeRegistry, propertyRegistry, textSerializer); + if (toStorage == null) { + context.halt(Component.text("Failed to initialize the destination storage. Please check the console for more information.", NamedTextColor.RED)); + return; + } + } + + Collection existingEntries; + try { + existingEntries = toStorage.loadNpcs(); + } catch (Exception e) { + context.halt(Component.text("Failed to load NPCs from the destination storage.", NamedTextColor.RED)); + return; + } + if (existingEntries.isEmpty()) { + toStorage.saveNpcs(entries); + context.send(Component.text("Migrated " + entries.size() + " NPCs from the source storage (", NamedTextColor.GREEN) + .append(Component.text(from.name(), NamedTextColor.GOLD)) + .append(Component.text(") to the destination storage (", NamedTextColor.GREEN)) + .append(Component.text(to.name(), NamedTextColor.GOLD)) + .append(Component.text(").", NamedTextColor.GREEN))); + if (currentStorageType == to) { + npcRegistry.reload(); + } else { + toStorage.close(); + } + return; + } + if (!force) { + Collection toSave = entries.stream().filter(e -> existingEntries.stream().noneMatch(e2 -> e2.getId().equals(e.getId()))).collect(Collectors.toList()); + Collection idExists = entries.stream().filter(e -> existingEntries.stream().anyMatch(e2 -> e2.getId().equals(e.getId()))).collect(Collectors.toList()); + if (toSave.isEmpty()) { + context.send(Component.text("No NPCs to migrate.", NamedTextColor.YELLOW)); + if (currentStorageType != to) { + toStorage.close(); + } + } else { + toStorage.saveNpcs(toSave); + context.send(Component.text("Migrated " + toSave.size() + " NPCs from the source storage (", NamedTextColor.GREEN) + .append(Component.text(from.name(), NamedTextColor.GOLD)) + .append(Component.text(") to the destination storage (", NamedTextColor.GREEN)) + .append(Component.text(to.name(), NamedTextColor.GOLD)) + .append(Component.text(").", NamedTextColor.GREEN))); + if (currentStorageType == to) { + npcRegistry.reload(); + } else { + toStorage.close(); + } + } + if (!idExists.isEmpty()) { + AtomicReference component = new AtomicReference<>(Component.text("The following NPCs were not migrated because their IDs already exist in the destination storage:").color(NamedTextColor.YELLOW)); + idExists.forEach(e -> { + component.set(component.get().append(Component.newline()).append(Component.text(e.getId(), NamedTextColor.RED))); + }); + component.set(component.get().append(Component.newline()) + .append(Component.text("Use the ", NamedTextColor.YELLOW)) + .append(Component.text("force", NamedTextColor.GOLD)) + .append(Component.text(" argument to overwrite them.", NamedTextColor.YELLOW))); + context.send(component.get()); + } + } else { + toStorage.saveNpcs(entries); + context.send(Component.text("Force migrated " + entries.size() + " NPCs from the source storage (", NamedTextColor.GREEN) + .append(Component.text(from.name(), NamedTextColor.GOLD)) + .append(Component.text(") to the destination storage (", NamedTextColor.GREEN)) + .append(Component.text(to.name(), NamedTextColor.GOLD)) + .append(Component.text(").", NamedTextColor.GREEN))); + if (currentStorageType == to) { + npcRegistry.reload(); + } else { + toStorage.close(); + } + } + } + + @Override + public List suggest(CommandContext context) throws CommandExecutionException { + if (context.argSize() == 1) { + return context.suggestEnum(NpcStorageType.values()); + } else if (context.argSize() == 2) { + NpcStorageType from = context.suggestionParse(0, NpcStorageType.class); + if (from == null) return Collections.emptyList(); + return context.suggestCollection(Arrays.stream(NpcStorageType.values()) + .filter(t -> t != from).map(Enum::name).collect(Collectors.toList())); + } else if (context.argSize() == 3) { + return context.suggestLiteral("true"); + } + return Collections.emptyList(); + } +} diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/config/DatabaseConfig.java b/plugin/src/main/java/lol/pyr/znpcsplus/config/DatabaseConfig.java index 16b110e..a62e983 100644 --- a/plugin/src/main/java/lol/pyr/znpcsplus/config/DatabaseConfig.java +++ b/plugin/src/main/java/lol/pyr/znpcsplus/config/DatabaseConfig.java @@ -30,9 +30,14 @@ public interface DatabaseConfig { @DefaultString("znpcsplus") String databaseName(); + @ConfKey("use-ssl") + @ConfComments("Should SSL be used when connecting to the database?") + @DefaultBoolean(false) + boolean useSSL(); + default String createConnectionURL(String dbType) { if (dbType.equalsIgnoreCase("mysql")) { - return "jdbc:mysql://" + host() + ":" + port() + "/" + databaseName() + "?useSSL=false&user=" + username() + "&password=" + password(); + return "jdbc:mysql://" + host() + ":" + port() + "/" + databaseName() + "?useSSL=" + useSSL(); } else { throw new IllegalArgumentException("Unsupported database type: " + dbType); } diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/entity/EntityPropertyRegistryImpl.java b/plugin/src/main/java/lol/pyr/znpcsplus/entity/EntityPropertyRegistryImpl.java index ba70aa3..6bb9b3c 100644 --- a/plugin/src/main/java/lol/pyr/znpcsplus/entity/EntityPropertyRegistryImpl.java +++ b/plugin/src/main/java/lol/pyr/znpcsplus/entity/EntityPropertyRegistryImpl.java @@ -20,6 +20,7 @@ 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.scheduling.TaskScheduler; import lol.pyr.znpcsplus.skin.cache.MojangSkinCache; import lol.pyr.znpcsplus.util.*; import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; @@ -104,7 +105,7 @@ public class EntityPropertyRegistryImpl implements EntityPropertyRegistry { */ } - public void registerTypes(ZNpcsPlusBootstrap plugin, PacketFactory packetFactory, LegacyComponentSerializer textSerializer) { + public void registerTypes(ZNpcsPlusBootstrap plugin, PacketFactory packetFactory, LegacyComponentSerializer textSerializer, TaskScheduler taskScheduler) { ServerVersion ver = PacketEvents.getAPI().getServerManager().getVersion(); boolean legacyBooleans = ver.isOlderThan(ServerVersion.V_1_9); boolean legacyNames = ver.isOlderThan(ServerVersion.V_1_9); @@ -127,7 +128,7 @@ public class EntityPropertyRegistryImpl implements EntityPropertyRegistry { register(new DummyProperty<>("permission_required", false)); - register(new ForceBodyRotationProperty(plugin)); + register(new ForceBodyRotationProperty(plugin, taskScheduler)); register(new DummyProperty<>("player_knockback", false)); register(new DummyProperty<>("player_knockback_exempt_permission", String.class)); @@ -659,6 +660,11 @@ public class EntityPropertyRegistryImpl implements EntityPropertyRegistry { // Wolf register(new EncodedIntegerProperty<>("wolf_variant", WoldVariant.PALE, wolfIndex, WoldVariant::getId, EntityDataTypes.WOLF_VARIANT)); + + if (!ver.isNewerThanOrEquals(ServerVersion.V_1_21)) return; + + // Bogged + register(new BooleanProperty("bogged_sheared", 16, false, legacyBooleans)); } private void registerSerializer(PropertySerializer serializer) { diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/entity/properties/ForceBodyRotationProperty.java b/plugin/src/main/java/lol/pyr/znpcsplus/entity/properties/ForceBodyRotationProperty.java index 4d568a7..b642193 100644 --- a/plugin/src/main/java/lol/pyr/znpcsplus/entity/properties/ForceBodyRotationProperty.java +++ b/plugin/src/main/java/lol/pyr/znpcsplus/entity/properties/ForceBodyRotationProperty.java @@ -3,22 +3,26 @@ package lol.pyr.znpcsplus.entity.properties; import com.github.retrooper.packetevents.protocol.entity.data.EntityData; import lol.pyr.znpcsplus.ZNpcsPlusBootstrap; import lol.pyr.znpcsplus.entity.PacketEntity; -import org.bukkit.Bukkit; +import lol.pyr.znpcsplus.scheduling.TaskScheduler; import org.bukkit.entity.Player; import java.util.Map; public class ForceBodyRotationProperty extends DummyProperty { private final ZNpcsPlusBootstrap plugin; + private final TaskScheduler scheduler; - public ForceBodyRotationProperty(ZNpcsPlusBootstrap plugin) { + public ForceBodyRotationProperty(ZNpcsPlusBootstrap plugin, TaskScheduler scheduler) { super("force_body_rotation", false); this.plugin = plugin; + this.scheduler = scheduler; } @Override public void apply(Player player, PacketEntity entity, boolean isSpawned, Map properties) { - Bukkit.getScheduler().runTaskLater(plugin, () -> entity.swingHand(player, false), 2L); - Bukkit.getScheduler().runTaskLater(plugin, () -> entity.swingHand(player, false), 6L); + if (entity.getProperty(this)) { + scheduler.runLaterAsync(() -> entity.swingHand(player, false), 2L); + scheduler.runLaterAsync(() -> entity.swingHand(player, false), 6L); + } } } diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/interaction/consolecommand/ConsoleCommandActionType.java b/plugin/src/main/java/lol/pyr/znpcsplus/interaction/consolecommand/ConsoleCommandActionType.java index 725f4fa..931256c 100644 --- a/plugin/src/main/java/lol/pyr/znpcsplus/interaction/consolecommand/ConsoleCommandActionType.java +++ b/plugin/src/main/java/lol/pyr/znpcsplus/interaction/consolecommand/ConsoleCommandActionType.java @@ -44,7 +44,7 @@ public class ConsoleCommandActionType implements InteractionActionType "); + context.setUsage(context.getUsage() + " " + getSubcommandName() + " "); } @Override diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/interaction/playerchat/PlayerChatActionType.java b/plugin/src/main/java/lol/pyr/znpcsplus/interaction/playerchat/PlayerChatActionType.java index a8ca987..99a292d 100644 --- a/plugin/src/main/java/lol/pyr/znpcsplus/interaction/playerchat/PlayerChatActionType.java +++ b/plugin/src/main/java/lol/pyr/znpcsplus/interaction/playerchat/PlayerChatActionType.java @@ -43,7 +43,7 @@ public class PlayerChatActionType implements InteractionActionType "); + context.setUsage(context.getUsage() + " " + getSubcommandName() + " "); } @Override 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 e7a2b7f..4fa6b67 100644 --- a/plugin/src/main/java/lol/pyr/znpcsplus/npc/NpcImpl.java +++ b/plugin/src/main/java/lol/pyr/znpcsplus/npc/NpcImpl.java @@ -56,6 +56,7 @@ public class NpcImpl extends Viewable implements Npc { UNSAFE_hideAll(); this.type = type; entity = new PacketEntity(packetFactory, this, type.getType(), entity.getLocation()); + hologram.setLocation(location.withY(location.getY() + type.getHologramOffset())); UNSAFE_showAll(); } @@ -231,10 +232,17 @@ public class NpcImpl extends Viewable implements Npc { } public void setWorld(World world) { + if (world == null) throw new IllegalArgumentException("world can not be null"); delete(); this.worldName = world.getName(); } + public void setWorld(String name) { + if (name == null) throw new IllegalArgumentException("world name can not be null"); + delete(); + this.worldName = name; + } + public void swingHand(boolean offHand) { for (Player viewer : getViewers()) entity.swingHand(viewer, offHand); } 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 45a50f9..e95cecd 100644 --- a/plugin/src/main/java/lol/pyr/znpcsplus/npc/NpcRegistryImpl.java +++ b/plugin/src/main/java/lol/pyr/znpcsplus/npc/NpcRegistryImpl.java @@ -207,5 +207,10 @@ public class NpcRegistryImpl implements NpcRegistry { public void unload() { npcList.forEach(npcEntry -> npcEntry.getNpc().delete()); + storage.close(); + } + + public NpcStorage getStorage() { + return storage; } } diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/npc/NpcTypeRegistryImpl.java b/plugin/src/main/java/lol/pyr/znpcsplus/npc/NpcTypeRegistryImpl.java index 8958ba3..47d8244 100644 --- a/plugin/src/main/java/lol/pyr/znpcsplus/npc/NpcTypeRegistryImpl.java +++ b/plugin/src/main/java/lol/pyr/znpcsplus/npc/NpcTypeRegistryImpl.java @@ -48,7 +48,7 @@ public class NpcTypeRegistryImpl implements NpcTypeRegistry { // Most hologram offsets generated using Entity#getHeight() in 1.19.4 register(builder(p, "armor_stand", EntityTypes.ARMOR_STAND) - .setHologramOffset(-0.15) + .setHologramOffset(0) .addEquipmentProperties() .addProperties("small", "arms", "base_plate", "head_rotation", "body_rotation", "left_arm_rotation", "right_arm_rotation", "left_leg_rotation", "right_leg_rotation")); @@ -106,7 +106,8 @@ public class NpcTypeRegistryImpl implements NpcTypeRegistry { register(builder(p, "iron_golem", EntityTypes.IRON_GOLEM) .setHologramOffset(0.725)); - register(builder(p, "magma_cube", EntityTypes.MAGMA_CUBE)); // TODO: Hologram offset scaling with size property + register(builder(p, "magma_cube", EntityTypes.MAGMA_CUBE) + .setHologramOffset(-1.455)); // TODO: Hologram offset scaling with size property register(builder(p, "mooshroom", EntityTypes.MOOSHROOM) .setHologramOffset(-0.575) @@ -137,7 +138,8 @@ public class NpcTypeRegistryImpl implements NpcTypeRegistry { register(builder(p, "skeleton_horse", EntityTypes.SKELETON_HORSE) .setHologramOffset(-0.375)); - register(builder(p, "slime", EntityTypes.SLIME)); // TODO: Hologram offset scaling with size property + register(builder(p, "slime", EntityTypes.SLIME) + .setHologramOffset(-1.455)); // TODO: Hologram offset scaling with size property register(builder(p, "snow_golem", EntityTypes.SNOW_GOLEM) .setHologramOffset(-0.075) @@ -230,7 +232,7 @@ public class NpcTypeRegistryImpl implements NpcTypeRegistry { .addEquipmentProperties()); register(builder(p, "zombie_villager", EntityTypes.ZOMBIE_VILLAGER) - .setHologramOffset(-1.0) + .setHologramOffset(-0.025) .addEquipmentProperties()); if (!version.isNewerThanOrEquals(ServerVersion.V_1_12)) return; @@ -315,7 +317,7 @@ public class NpcTypeRegistryImpl implements NpcTypeRegistry { .addProperties("hoglin_immune_to_zombification")); register(builder(p, "piglin", EntityTypes.PIGLIN) - .setHologramOffset(-1.0) + .setHologramOffset(-0.025) .addEquipmentProperties() .addProperties("piglin_baby", "piglin_charging_crossbow", "piglin_dancing")); @@ -363,19 +365,28 @@ public class NpcTypeRegistryImpl implements NpcTypeRegistry { if (!version.isNewerThanOrEquals(ServerVersion.V_1_20)) return; register(builder(p, "sniffer", EntityTypes.SNIFFER) - .setHologramOffset(0.125) + .setHologramOffset(0.075) .addProperties("sniffer_state")); register(builder(p, "camel", EntityTypes.CAMEL) - .setHologramOffset(0.25) + .setHologramOffset(0.4) .addProperties("bashing", "camel_sitting")); if (!version.isNewerThanOrEquals(ServerVersion.V_1_20_5)) return; register(builder(p, "armadillo", EntityTypes.ARMADILLO) - .setHologramOffset(-1.475) + .setHologramOffset(-1.325) .addProperties("armadillo_state")); + + if (!version.isNewerThanOrEquals(ServerVersion.V_1_21)) return; + + register(builder(p, "bogged", EntityTypes.BOGGED) + .setHologramOffset(0.015) + .addProperties("bogged_sheared")); + + register(builder(p, "breeze", EntityTypes.BREEZE) + .setHologramOffset(-0.205)); } public Collection getAll() { diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/packets/V1_21_3PacketFactory.java b/plugin/src/main/java/lol/pyr/znpcsplus/packets/V1_21_3PacketFactory.java new file mode 100644 index 0000000..7b7ddc5 --- /dev/null +++ b/plugin/src/main/java/lol/pyr/znpcsplus/packets/V1_21_3PacketFactory.java @@ -0,0 +1,29 @@ +package lol.pyr.znpcsplus.packets; + +import com.github.retrooper.packetevents.PacketEventsAPI; +import com.github.retrooper.packetevents.protocol.entity.EntityPositionData; +import com.github.retrooper.packetevents.protocol.teleport.RelativeFlag; +import com.github.retrooper.packetevents.util.Vector3d; +import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerEntityHeadLook; +import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerEntityTeleport; +import lol.pyr.znpcsplus.config.ConfigManager; +import lol.pyr.znpcsplus.entity.EntityPropertyRegistryImpl; +import lol.pyr.znpcsplus.entity.PacketEntity; +import lol.pyr.znpcsplus.scheduling.TaskScheduler; +import lol.pyr.znpcsplus.util.NpcLocation; +import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; +import org.bukkit.entity.Player; +import org.bukkit.plugin.Plugin; + +public class V1_21_3PacketFactory extends V1_20_2PacketFactory { + public V1_21_3PacketFactory(TaskScheduler scheduler, PacketEventsAPI packetEvents, EntityPropertyRegistryImpl propertyRegistry, LegacyComponentSerializer textSerializer, ConfigManager configManager) { + super(scheduler, packetEvents, propertyRegistry, textSerializer, configManager); + } + + @Override + public void teleportEntity(Player player, PacketEntity entity) { + NpcLocation location = entity.getLocation(); + sendPacket(player, new WrapperPlayServerEntityTeleport(entity.getEntityId(), new EntityPositionData(npcLocationToVector(location), new Vector3d(0, 0, 0), location.getYaw(), location.getPitch()), RelativeFlag.NONE, false)); + sendPacket(player, new WrapperPlayServerEntityHeadLook(entity.getEntityId(), location.getYaw())); + } +} 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 3c63364..65e6e7d 100644 --- a/plugin/src/main/java/lol/pyr/znpcsplus/skin/BaseSkinDescriptor.java +++ b/plugin/src/main/java/lol/pyr/znpcsplus/skin/BaseSkinDescriptor.java @@ -9,6 +9,7 @@ import lol.pyr.znpcsplus.skin.descriptor.PrefetchedDescriptor; import org.bukkit.entity.Player; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.concurrent.CompletableFuture; @@ -21,7 +22,7 @@ public interface BaseSkinDescriptor extends SkinDescriptor { static BaseSkinDescriptor deserialize(MojangSkinCache skinCache, String str) { String[] arr = str.split(";"); if (arr[0].equalsIgnoreCase("mirror")) return new MirrorDescriptor(skinCache); - else if (arr[0].equalsIgnoreCase("fetching")) return new FetchingDescriptor(skinCache, arr[1]); + else if (arr[0].equalsIgnoreCase("fetching")) return new FetchingDescriptor(skinCache, String.join(";", Arrays.copyOfRange(arr, 1, arr.length))); else if (arr[0].equalsIgnoreCase("prefetched")) { List properties = new ArrayList<>(); for (int i = 0; i < (arr.length - 1) / 3; i++) { diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/storage/NpcStorage.java b/plugin/src/main/java/lol/pyr/znpcsplus/storage/NpcStorage.java index 63c51fb..702d9ca 100644 --- a/plugin/src/main/java/lol/pyr/znpcsplus/storage/NpcStorage.java +++ b/plugin/src/main/java/lol/pyr/znpcsplus/storage/NpcStorage.java @@ -8,4 +8,7 @@ public interface NpcStorage { Collection loadNpcs(); void saveNpcs(Collection npcs); void deleteNpc(NpcEntryImpl npc); + default void close() { + + } } diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/storage/database/Database.java b/plugin/src/main/java/lol/pyr/znpcsplus/storage/database/Database.java index 1794abd..4e41696 100644 --- a/plugin/src/main/java/lol/pyr/znpcsplus/storage/database/Database.java +++ b/plugin/src/main/java/lol/pyr/znpcsplus/storage/database/Database.java @@ -13,4 +13,6 @@ public abstract class Database { public abstract Connection getSQLConnection(); public abstract void load(); + + public abstract void close(); } diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/storage/mysql/MySQL.java b/plugin/src/main/java/lol/pyr/znpcsplus/storage/mysql/MySQL.java index 7ffd709..ccdf79d 100644 --- a/plugin/src/main/java/lol/pyr/znpcsplus/storage/mysql/MySQL.java +++ b/plugin/src/main/java/lol/pyr/znpcsplus/storage/mysql/MySQL.java @@ -10,10 +10,14 @@ import java.util.logging.Logger; public class MySQL extends Database { private final String connectionURL; + private final String username; + private final String password; - public MySQL(String connectionURL, Logger logger) { + public MySQL(String connectionURL, String username, String password, Logger logger) { super(logger); this.connectionURL = connectionURL; + this.username = username; + this.password = password; } @Override @@ -25,7 +29,7 @@ public class MySQL extends Database { return connection; } Class.forName("com.mysql.jdbc.Driver"); - connection = java.sql.DriverManager.getConnection(connectionURL); + connection = java.sql.DriverManager.getConnection(connectionURL, username, password); return connection; } catch (ClassNotFoundException ex) { logger.severe("MySQL JDBC library not found" + ex); @@ -56,6 +60,18 @@ public class MySQL extends Database { connection = getSQLConnection(); } + @Override + public void close() { + try { + if (connection != null) { + connection.close(); + } + } catch (SQLException e) { + logger.severe("An error occurred while closing the connection"); + e.printStackTrace(); + } + } + public boolean tableExists(String tableName) { try { Statement s = connection.createStatement(); diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/storage/mysql/MySQLStorage.java b/plugin/src/main/java/lol/pyr/znpcsplus/storage/mysql/MySQLStorage.java index d1aa805..b7fe0cb 100644 --- a/plugin/src/main/java/lol/pyr/znpcsplus/storage/mysql/MySQLStorage.java +++ b/plugin/src/main/java/lol/pyr/znpcsplus/storage/mysql/MySQLStorage.java @@ -46,7 +46,8 @@ public class MySQLStorage implements NpcStorage { this.typeRegistry = typeRegistry; this.propertyRegistry = propertyRegistry; this.textSerializer = textSerializer; - this.database = new MySQL(configManager.getConfig().databaseConfig().createConnectionURL("mysql"), logger); + this.database = new MySQL(configManager.getConfig().databaseConfig().createConnectionURL("mysql"), + configManager.getConfig().databaseConfig().username(), configManager.getConfig().databaseConfig().password(), logger); database.load(); if (database.getSQLConnection() == null) { throw new RuntimeException("Failed to initialize MySQL Storage"); @@ -313,4 +314,9 @@ public class MySQLStorage implements NpcStorage { exception.printStackTrace(); } } + + @Override + public void close() { + database.close(); + } } diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/storage/sqlite/SQLite.java b/plugin/src/main/java/lol/pyr/znpcsplus/storage/sqlite/SQLite.java index f44944c..ff1a273 100644 --- a/plugin/src/main/java/lol/pyr/znpcsplus/storage/sqlite/SQLite.java +++ b/plugin/src/main/java/lol/pyr/znpcsplus/storage/sqlite/SQLite.java @@ -41,6 +41,18 @@ public class SQLite extends Database{ connection = getSQLConnection(); } + @Override + public void close() { + try { + if (connection != null) { + connection.close(); + } + } catch (SQLException e) { + logger.severe("An error occurred while closing the connection"); + e.printStackTrace(); + } + } + public boolean tableExists(String tableName) { try { Statement s = connection.createStatement(); diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/storage/sqlite/SQLiteStorage.java b/plugin/src/main/java/lol/pyr/znpcsplus/storage/sqlite/SQLiteStorage.java index 2eb004e..0dc7592 100644 --- a/plugin/src/main/java/lol/pyr/znpcsplus/storage/sqlite/SQLiteStorage.java +++ b/plugin/src/main/java/lol/pyr/znpcsplus/storage/sqlite/SQLiteStorage.java @@ -312,4 +312,9 @@ public class SQLiteStorage implements NpcStorage { exception.printStackTrace(); } } + + @Override + public void close() { + database.close(); + } } diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/updater/UpdateChecker.java b/plugin/src/main/java/lol/pyr/znpcsplus/updater/UpdateChecker.java index 29fce30..ef6ecc1 100644 --- a/plugin/src/main/java/lol/pyr/znpcsplus/updater/UpdateChecker.java +++ b/plugin/src/main/java/lol/pyr/znpcsplus/updater/UpdateChecker.java @@ -1,20 +1,21 @@ package lol.pyr.znpcsplus.updater; -import me.robertlit.spigotresources.api.Resource; -import me.robertlit.spigotresources.api.SpigotResourcesAPI; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; import org.bukkit.plugin.PluginDescriptionFile; import org.bukkit.scheduler.BukkitRunnable; -import java.util.concurrent.TimeUnit; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.URL; import java.util.logging.Logger; public class UpdateChecker extends BukkitRunnable { private final static Logger logger = Logger.getLogger("ZNPCsPlus Update Checker"); - private final static int RESOURCE_ID = 109380; + private final static String GET_RESOURCE = "https://api.spigotmc.org/simple/0.2/index.php?action=getResource&id=109380"; public final static String DOWNLOAD_LINK = "https://www.spigotmc.org/resources/znpcsplus.109380/"; - private final SpigotResourcesAPI api = new SpigotResourcesAPI(1, TimeUnit.MINUTES); - private final PluginDescriptionFile info; private Status status = Status.UNKNOWN; private String newestVersion = "N/A"; @@ -24,9 +25,29 @@ public class UpdateChecker extends BukkitRunnable { } public void run() { - Resource resource = api.getResource(RESOURCE_ID).join(); - if (resource == null) return; - newestVersion = resource.getVersion(); + String foundVersion = null; + try { + URL getResource = new URL(GET_RESOURCE); + HttpURLConnection httpRequest = ((HttpURLConnection) getResource.openConnection()); + httpRequest.setRequestMethod("GET"); + httpRequest.setConnectTimeout(5_000); + httpRequest.setReadTimeout(5_000); + + if (httpRequest.getResponseCode() == HttpURLConnection.HTTP_OK) { + try (InputStreamReader reader = new InputStreamReader(httpRequest.getInputStream())) { + JsonObject jsonObject = JsonParser.parseReader(reader).getAsJsonObject(); + foundVersion = jsonObject.get("current_version").getAsString(); + } + } else { + logger.warning("Failed to check for updates: HTTP response code " + httpRequest.getResponseCode()); + } + } catch (IOException e) { + logger.warning("Failed to check for updates: " + e.getMessage()); + return; + } + + if (foundVersion == null) return; + newestVersion = foundVersion; status = compareVersions(info.getVersion(), newestVersion); if (status == Status.UPDATE_NEEDED) notifyConsole(); diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/user/UserManager.java b/plugin/src/main/java/lol/pyr/znpcsplus/user/UserManager.java index 0b47e94..ee0c12b 100644 --- a/plugin/src/main/java/lol/pyr/znpcsplus/user/UserManager.java +++ b/plugin/src/main/java/lol/pyr/znpcsplus/user/UserManager.java @@ -3,12 +3,12 @@ package lol.pyr.znpcsplus.user; import org.bukkit.Bukkit; import org.bukkit.entity.Player; -import java.util.HashMap; import java.util.Map; import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; public class UserManager { - private final Map userMap = new HashMap<>(); + private final Map userMap = new ConcurrentHashMap<>(); public UserManager() { Bukkit.getOnlinePlayers().forEach(this::get); diff --git a/plugin/src/main/resources/messages/action-hover/add.txt b/plugin/src/main/resources/messages/action-hover/add.txt index e69de29..23b0083 100644 --- a/plugin/src/main/resources/messages/action-hover/add.txt +++ b/plugin/src/main/resources/messages/action-hover/add.txt @@ -0,0 +1,13 @@ +Examples: + * /npc action add consolecommand cool_npc1 ANY_CLICK 0 0 say {player} just clicked a cool npc! + * /npc action add playerchat dog LEFT_CLICK 0 100 It has been 5 seconds since i clicked the npc + * /npc action add message npc123 RIGHT_CLICK 1 0 You can only click this npc once per second + +Action Types: + * Console Command - Send a console command when a player interacts with the npc + * Message - Send a message to any player that interacts with the npc + * Player Chat - Make any player that interacts send something in the chat + * Player Command - Make any player that interacts send a command + * Switch Server - Send the player to a different server on the proxy using bungee messaging channel + +Command used to add actions to an npc \ No newline at end of file diff --git a/plugin/src/main/resources/messages/action-hover/clear.txt b/plugin/src/main/resources/messages/action-hover/clear.txt index e69de29..ef981f7 100644 --- a/plugin/src/main/resources/messages/action-hover/clear.txt +++ b/plugin/src/main/resources/messages/action-hover/clear.txt @@ -0,0 +1,3 @@ +Usage » /npc action clear + +Command used to clear all npc actions \ No newline at end of file diff --git a/plugin/src/main/resources/messages/action-hover/delete.txt b/plugin/src/main/resources/messages/action-hover/delete.txt index e69de29..5a6507e 100644 --- a/plugin/src/main/resources/messages/action-hover/delete.txt +++ b/plugin/src/main/resources/messages/action-hover/delete.txt @@ -0,0 +1,3 @@ +Usage » /npc action delete + +Command used to delete a specific action from an npc \ No newline at end of file diff --git a/plugin/src/main/resources/messages/action-hover/edit.txt b/plugin/src/main/resources/messages/action-hover/edit.txt index e69de29..1827560 100644 --- a/plugin/src/main/resources/messages/action-hover/edit.txt +++ b/plugin/src/main/resources/messages/action-hover/edit.txt @@ -0,0 +1,3 @@ +Usage » /npc action edit + +Command used to change a specific action on an npc \ No newline at end of file diff --git a/plugin/src/main/resources/messages/action-hover/list.txt b/plugin/src/main/resources/messages/action-hover/list.txt index e69de29..7397e5f 100644 --- a/plugin/src/main/resources/messages/action-hover/list.txt +++ b/plugin/src/main/resources/messages/action-hover/list.txt @@ -0,0 +1,3 @@ +Usage » /npc action list + +Command used to list all actions of an npc \ No newline at end of file diff --git a/plugin/src/main/resources/messages/holo-hover/additem.txt b/plugin/src/main/resources/messages/holo-hover/additem.txt new file mode 100644 index 0000000..e69de29 diff --git a/plugin/src/main/resources/messages/holo-hover/insertitem.txt b/plugin/src/main/resources/messages/holo-hover/insertitem.txt new file mode 100644 index 0000000..e69de29 diff --git a/plugin/src/main/resources/messages/holo-hover/refreshdelay.txt b/plugin/src/main/resources/messages/holo-hover/refreshdelay.txt new file mode 100644 index 0000000..e69de29 diff --git a/plugin/src/main/resources/messages/holo-hover/setitem.txt b/plugin/src/main/resources/messages/holo-hover/setitem.txt new file mode 100644 index 0000000..e69de29 diff --git a/plugin/src/main/resources/messages/holo.txt b/plugin/src/main/resources/messages/holo.txt index a0b858d..c4e0394 100644 --- a/plugin/src/main/resources/messages/holo.txt +++ b/plugin/src/main/resources/messages/holo.txt @@ -3,9 +3,16 @@ Hover over any command more info * /npc holo add - * /npc holo delete * /npc holo set * /npc holo insert + + * /npc holo additem + * /npc holo setitem + * /npc holo insertitem + + * /npc holo delete + * /npc holo offset + * /npc holo refreshdelay * /npc holo info diff --git a/plugin/src/main/resources/messages/storage-hover/migrate.txt b/plugin/src/main/resources/messages/storage-hover/migrate.txt new file mode 100644 index 0000000..371148c --- /dev/null +++ b/plugin/src/main/resources/messages/storage-hover/migrate.txt @@ -0,0 +1,16 @@ +Usage » /npc storage migrate [force] + +Storage Types: + * YAML - Npcs are stored in yaml files + * SQLite - Npcs are stored in a SQLite database + * MySQL - Npcs are stored in a MySQL database + +Command used to migrate npcs from one storage type to another. + +This command will NOT delete the original storage files or database, +but will copy the npcs to the new storage type. + +This will also not overwrite any existing npcs in the new storage +type, unless the force argument is set to true. +Warning: force will overwrite any existing npcs with the same id +in the new storage type and CANNOT be undone. \ No newline at end of file diff --git a/plugin/src/main/resources/messages/storage.txt b/plugin/src/main/resources/messages/storage.txt index b5721b3..6cfcc3f 100644 --- a/plugin/src/main/resources/messages/storage.txt +++ b/plugin/src/main/resources/messages/storage.txt @@ -5,4 +5,5 @@ * /npc storage save * /npc storage reload * /npc storage import + * /npc storage migrate [force]