From fb1ee145f03b23a67594bc69ad257a2b494cc165 Mon Sep 17 00:00:00 2001 From: CarmJos Date: Fri, 11 Mar 2022 08:16:22 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E6=88=90=E7=89=A9=E5=93=81=E5=9F=BA?= =?UTF-8?q?=E6=9C=AC=E9=80=BB=E8=BE=91=E4=B8=8E=E9=85=8D=E7=BD=AE=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E8=AF=BB=E5=8F=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 3 +- .../plugin/commanditem/CommandItemAPI.java | 5 + .../java/cc/carm/plugin/commanditem/Main.java | 11 +- .../configuration/PluginConfig.java | 10 ++ .../plugin/commanditem/database/DBTables.java | 46 +++++-- .../commanditem/database/DataManager.java | 22 +++- .../plugin/commanditem/item/CommandItem.java | 34 +++++ .../plugin/commanditem/item/ItemAction.java | 43 ++++++ .../commanditem/item/ItemActionGroup.java | 24 ++++ .../commanditem/item/ItemActionType.java | 121 +++++++++++++++++ .../commanditem/item/ItemExecuteResult.java | 20 +++ .../commanditem/item/ItemRestrictions.java | 57 ++++++++ .../plugin/commanditem/item/ItemSettings.java | 105 +++++++++++++++ .../commanditem/item/ItemStackConfig.java | 103 +++++++++++++++ .../commanditem/listener/ItemListener.java | 43 ++++++ .../commanditem/manager/ConfigManager.java | 43 ++++++ .../commanditem/manager/ItemsManager.java | 124 ++++++++++++++++++ src/main/resources/config.yml | 12 +- src/main/resources/items/.example-item.yml | 55 ++++++++ 19 files changed, 866 insertions(+), 15 deletions(-) create mode 100644 src/main/java/cc/carm/plugin/commanditem/item/CommandItem.java create mode 100644 src/main/java/cc/carm/plugin/commanditem/item/ItemAction.java create mode 100644 src/main/java/cc/carm/plugin/commanditem/item/ItemActionGroup.java create mode 100644 src/main/java/cc/carm/plugin/commanditem/item/ItemActionType.java create mode 100644 src/main/java/cc/carm/plugin/commanditem/item/ItemExecuteResult.java create mode 100644 src/main/java/cc/carm/plugin/commanditem/item/ItemRestrictions.java create mode 100644 src/main/java/cc/carm/plugin/commanditem/item/ItemSettings.java create mode 100644 src/main/java/cc/carm/plugin/commanditem/item/ItemStackConfig.java create mode 100644 src/main/java/cc/carm/plugin/commanditem/listener/ItemListener.java create mode 100644 src/main/java/cc/carm/plugin/commanditem/manager/ItemsManager.java create mode 100644 src/main/resources/items/.example-item.yml diff --git a/README.md b/README.md index 2ad032a..5f02e4d 100644 --- a/README.md +++ b/README.md @@ -14,10 +14,11 @@ 物品指令绑定插件,给予玩家可执行对应指令的消耗物品,基于EasyPlugin实现。 ## 插件功能与优势 +> 加 * 的功能仍在开发中。 - 物品指令绑定,给予玩家可执行对应指令的消耗物品,支持PlaceholderAPI变量。 - **允许限定。** 允许给物品对应的指令组设定“领取次数”、“每日领取次数”与“领取时间”限定。 -- **详细记录。** 每个物品均有独立ID,并对使用的玩家与执行结果进行详细记录,便于追踪查询。 +- **\*详细记录。** 每个物品均有独立ID,并对使用的玩家与执行结果进行详细记录,便于追踪查询。 - **异步存取。** 数据读取与存储均为异步操作,不影响服务器性能。 - **轻量插件。** 适合小型服务器使用,配置简单方便。 - **规范开发。** 插件架构符合开发规范,适合新手开发者学习。 diff --git a/src/main/java/cc/carm/plugin/commanditem/CommandItemAPI.java b/src/main/java/cc/carm/plugin/commanditem/CommandItemAPI.java index 1b761f0..fe3d5a1 100644 --- a/src/main/java/cc/carm/plugin/commanditem/CommandItemAPI.java +++ b/src/main/java/cc/carm/plugin/commanditem/CommandItemAPI.java @@ -1,6 +1,11 @@ package cc.carm.plugin.commanditem; +import cc.carm.plugin.commanditem.manager.ItemsManager; + public class CommandItemAPI { + public static ItemsManager getItemsManager() { + return Main.getInstance().itemsManager; + } } diff --git a/src/main/java/cc/carm/plugin/commanditem/Main.java b/src/main/java/cc/carm/plugin/commanditem/Main.java index 43cf347..5cbf176 100644 --- a/src/main/java/cc/carm/plugin/commanditem/Main.java +++ b/src/main/java/cc/carm/plugin/commanditem/Main.java @@ -4,7 +4,9 @@ import cc.carm.lib.easyplugin.EasyPlugin; import cc.carm.lib.easyplugin.i18n.EasyPluginMessageProvider; import cc.carm.plugin.commanditem.configuration.PluginConfig; import cc.carm.plugin.commanditem.hooker.GHUpdateChecker; +import cc.carm.plugin.commanditem.listener.ItemListener; import cc.carm.plugin.commanditem.manager.ConfigManager; +import cc.carm.plugin.commanditem.manager.ItemsManager; import cc.carm.plugin.commanditem.util.JarResourceUtils; import org.bstats.bukkit.Metrics; import org.bukkit.Bukkit; @@ -24,6 +26,8 @@ public class Main extends EasyPlugin { return instance; } + protected ItemsManager itemsManager; + @Override protected boolean initialize() { @@ -34,6 +38,11 @@ public class Main extends EasyPlugin { return false; } + info("注册指令..."); + + info("注册监听器..."); + regListener(new ItemListener()); + if (PluginConfig.METRICS.get()) { info("启用统计数据..."); new Metrics(this, 14459); @@ -41,7 +50,7 @@ public class Main extends EasyPlugin { if (PluginConfig.CHECK_UPDATE.get()) { log("开始检查更新..."); - GHUpdateChecker checker = new GHUpdateChecker(getLogger(), "CarmJos", "ItemCommands"); + GHUpdateChecker checker = new GHUpdateChecker(getLogger(), "CarmJos", "CommandItem"); getScheduler().runAsync(() -> checker.checkUpdate(getDescription().getVersion())); } else { log("已禁用检查更新,跳过。"); diff --git a/src/main/java/cc/carm/plugin/commanditem/configuration/PluginConfig.java b/src/main/java/cc/carm/plugin/commanditem/configuration/PluginConfig.java index ea7cc3c..ef15abe 100644 --- a/src/main/java/cc/carm/plugin/commanditem/configuration/PluginConfig.java +++ b/src/main/java/cc/carm/plugin/commanditem/configuration/PluginConfig.java @@ -1,6 +1,7 @@ package cc.carm.plugin.commanditem.configuration; import cc.carm.lib.easyplugin.configuration.values.ConfigValue; +import cc.carm.lib.easyplugin.configuration.values.ConfigValueMap; public class PluginConfig { @@ -20,4 +21,13 @@ public class PluginConfig { "log-storage.enable", Boolean.class, true ); + public static class CustomStorage { + + public static ConfigValue ENABLE = new ConfigValue<>("custom-storage.enable", Boolean.class, false); + + public static ConfigValue PATH = new ConfigValue<>("custom-storage.path", String.class, "items/"); + + } + + } diff --git a/src/main/java/cc/carm/plugin/commanditem/database/DBTables.java b/src/main/java/cc/carm/plugin/commanditem/database/DBTables.java index 35ee795..c754d18 100644 --- a/src/main/java/cc/carm/plugin/commanditem/database/DBTables.java +++ b/src/main/java/cc/carm/plugin/commanditem/database/DBTables.java @@ -4,20 +4,48 @@ import cc.carm.lib.easyplugin.configuration.values.ConfigValue; public class DBTables { - public static class LogTable { + /** + * 物品发放记录表 + * 用于记录每个物品的发放情况,包含发放时间,发放人,发放数量以及发放给了谁。 + */ + public static class GiveTable { protected static final ConfigValue TABLE_NAME = new ConfigValue<>( - "log-storage.database.table-name", String.class, - "log_item_commands" + "log-storage.database.tables.give", String.class, + "log_item_give" ); protected static final String[] TABLE_COLUMNS = new String[]{ - "`uuid` VARCHAR(36) NOT NULL PRIMARY KEY COMMENT '用户UUID'", // 用户的UUID - "`time` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '用户在线秒数'",// 用户在线时间(秒) - "`update` DATETIME NOT NULL " + - "DEFAULT CURRENT_TIMESTAMP " + - "ON UPDATE CURRENT_TIMESTAMP " + - " COMMENT '最后更新时间'" + "`id` INT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE KEY", + "`uuid` VARCHAR(36) NOT NULL PRIMARY KEY", // ItemUUID + "`settings` VARCHAR(36) NOT NULL", // 该物品配置对应的Identifier + "`operator` VARCHAR(36)", "`operator_name` VARCHAR(32)", // 发放人的相关信息 + "`receiver` VARCHAR(36)", "`receiver_name` VARCHAR(32)", // 接受者的相关信息 + "`amount` INT UNSIGNED NOT NULL DEFAULT 1", // 同uuid物品的发放数量 + "`time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP" // 发放时间 + }; + + } + + /** + * 物品拿取记录表 + * 改表用于记录物品的使用情况,即谁在什么时候使用了哪个物品,以及领取时任务的执行情况。 + * 请注意:只有在发生物品拿取(即 take action )事件时才会记录! + */ + public static class TakeTable { + + protected static final ConfigValue TABLE_NAME = new ConfigValue<>( + "log-storage.database.tables.received", String.class, + "log_item_received" + ); + + protected static final String[] TABLE_COLUMNS = new String[]{ + "`id` INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY", + "`uuid` VARCHAR(36) NOT NULL", // ItemUUID + "`receiver` VARCHAR(36)", "`receiver_name` VARCHAR(32)", // 接受者的相关信息 + "`result` TINYINT(2) NOT NULL DEFAULT 0",// 领取结果 + "`time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP", + "INDEX `item`(`uuid`)" }; } diff --git a/src/main/java/cc/carm/plugin/commanditem/database/DataManager.java b/src/main/java/cc/carm/plugin/commanditem/database/DataManager.java index 45007b7..2354983 100644 --- a/src/main/java/cc/carm/plugin/commanditem/database/DataManager.java +++ b/src/main/java/cc/carm/plugin/commanditem/database/DataManager.java @@ -3,12 +3,16 @@ package cc.carm.plugin.commanditem.database; import cc.carm.lib.easysql.EasySQL; import cc.carm.lib.easysql.api.SQLManager; import cc.carm.plugin.commanditem.Main; +import cc.carm.plugin.commanditem.util.DatabaseTable; import java.sql.SQLException; public class DataManager { private SQLManager sqlManager; + private DatabaseTable givenTable; + private DatabaseTable receivedTable; + public boolean initialize() { try { Main.info(" 尝试连接到数据库..."); @@ -25,9 +29,12 @@ public class DataManager { try { Main.info(" 创建插件记录所需表..."); - getSQLManager().createTable(DBTables.LogTable.TABLE_NAME.get()) - .setColumns(DBTables.LogTable.TABLE_COLUMNS) - .build().execute(); + + this.givenTable = new DatabaseTable(DBTables.GiveTable.TABLE_NAME.get(), DBTables.GiveTable.TABLE_COLUMNS); + this.receivedTable = new DatabaseTable(DBTables.TakeTable.TABLE_NAME.get(), DBTables.TakeTable.TABLE_COLUMNS); + + this.givenTable.createTable(this.sqlManager); + this.receivedTable.createTable(this.sqlManager); } catch (SQLException exception) { Main.severe("无法创建插件所需的表,请检查数据库权限。"); @@ -48,6 +55,15 @@ public class DataManager { return sqlManager; } + public DatabaseTable getGivenTable() { + return givenTable; + } + + public DatabaseTable getReceivedTable() { + return receivedTable; + } + + } diff --git a/src/main/java/cc/carm/plugin/commanditem/item/CommandItem.java b/src/main/java/cc/carm/plugin/commanditem/item/CommandItem.java new file mode 100644 index 0000000..d3c08ca --- /dev/null +++ b/src/main/java/cc/carm/plugin/commanditem/item/CommandItem.java @@ -0,0 +1,34 @@ +package cc.carm.plugin.commanditem.item; + +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; + +import java.util.UUID; + +public class CommandItem { + + @NotNull UUID uuid; + + @NotNull ItemSettings configuration; + @NotNull ItemStack itemStack; + + public CommandItem(@NotNull UUID uuid, @NotNull ItemSettings configuration, @NotNull ItemStack itemStack) { + this.uuid = uuid; + this.configuration = configuration; + this.itemStack = itemStack; + } + + public @NotNull UUID getUUID() { + return uuid; + } + + public @NotNull ItemSettings getConfiguration() { + return configuration; + } + + public @NotNull ItemStack getItemStack() { + return itemStack; + } + + +} diff --git a/src/main/java/cc/carm/plugin/commanditem/item/ItemAction.java b/src/main/java/cc/carm/plugin/commanditem/item/ItemAction.java new file mode 100644 index 0000000..6aba250 --- /dev/null +++ b/src/main/java/cc/carm/plugin/commanditem/item/ItemAction.java @@ -0,0 +1,43 @@ +package cc.carm.plugin.commanditem.item; + +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class ItemAction { + + @NotNull ItemActionType type; + @Nullable String actionContent; + + public ItemAction(@NotNull ItemActionType type, @Nullable String actionContent) { + this.type = type; + this.actionContent = actionContent; + } + + public @NotNull ItemActionType getType() { + return type; + } + + public @Nullable String getActionContent() { + return actionContent; + } + + public boolean execute(Player player) { + return getType().execute(player, getActionContent()); + } + + public static @Nullable ItemAction read(@Nullable String actionString) { + if (actionString == null) return null; + int prefixStart = actionString.indexOf("["); + int prefixEnd = actionString.indexOf("]"); + if (prefixStart < 0 || prefixEnd < 0) return null; + + String prefix = actionString.substring(prefixStart + 1, prefixEnd); + ItemActionType actionType = ItemActionType.read(prefix); + if (actionType == null) return null; + + return new ItemAction(actionType, actionString.substring(prefixEnd + 1).trim()); + } + + +} diff --git a/src/main/java/cc/carm/plugin/commanditem/item/ItemActionGroup.java b/src/main/java/cc/carm/plugin/commanditem/item/ItemActionGroup.java new file mode 100644 index 0000000..4e49667 --- /dev/null +++ b/src/main/java/cc/carm/plugin/commanditem/item/ItemActionGroup.java @@ -0,0 +1,24 @@ +package cc.carm.plugin.commanditem.item; + +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +public class ItemActionGroup { + + + List actions; + + public ItemActionGroup(List actions) { + this.actions = actions; + } + + public static @NotNull ItemActionGroup read(@NotNull List actionsString) { + List actions = actionsString.stream() + .map(ItemAction::read).filter(Objects::nonNull).collect(Collectors.toList()); + return new ItemActionGroup(actions); + } + +} diff --git a/src/main/java/cc/carm/plugin/commanditem/item/ItemActionType.java b/src/main/java/cc/carm/plugin/commanditem/item/ItemActionType.java new file mode 100644 index 0000000..2155d69 --- /dev/null +++ b/src/main/java/cc/carm/plugin/commanditem/item/ItemActionType.java @@ -0,0 +1,121 @@ +package cc.carm.plugin.commanditem.item; + +import cc.carm.lib.easyplugin.utils.MessageUtils; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.Sound; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.function.BiFunction; + +public enum ItemActionType { + + /** + * 以玩家聊天的形式执行 + * 若内容以 “/" 开头,则会以玩家身份执行命令。 + */ + CHAT((player, string) -> { + if (string == null) return true; //没有需要执行的 + List finalContents = MessageUtils.setPlaceholders(player, Collections.singletonList(string)); + boolean success = true; + for (String finalContent : finalContents) { + try { + player.chat(finalContent); + } catch (Exception ex) { + success = false; + } + } + return success; + }), + + /** + * 以后台的形式执行指令 + * 指令内容不需要以“/”开头。 + */ + CONSOLE((player, string) -> { + if (string == null) return true; + List finalCommands = MessageUtils.setPlaceholders(player, Collections.singletonList(string)); + boolean success = true; + for (String finalCommand : finalCommands) { + try { + Bukkit.dispatchCommand(Bukkit.getConsoleSender(), finalCommand); + } catch (Exception ex) { + success = false; + } + } + return success; + }), + + /** + * 向玩家发送消息。 + */ + MESSAGE((sender, messages) -> { + MessageUtils.send(sender, messages); + return true; + }), + + /** + * 向玩家发送声音。 + * 允许配置音量与音调 + *
    + *
  • SOUND_NAME
  • + *
  • SOUND_NAME:VOLUME
  • + *
  • SOUND_NAME:VOLUME:PITCH
  • + *
+ */ + SOUND((player, string) -> { + if (string == null) return true; + try { + String[] args = string.contains(":") ? string.split(":") : new String[]{string}; + Sound sound = Arrays.stream(Sound.values()) + .filter(s -> s.name().equals(args[0])) + .findFirst().orElse(null); + + if (sound == null) return true; + float volume = args.length > 1 ? Float.parseFloat(args[1]) : 1F; + float pitch = args.length > 2 ? Float.parseFloat(args[2]) : 1F; + + player.playSound(player.getLocation(), sound, volume, pitch); + } catch (Exception ignored) { + } + return true; // 声音放不放无关紧要 + }), + + /** + * 拿取玩家手上的一个物品 + */ + TAKE((player, string) -> { + if (player.getInventory().getItemInMainHand().getType() != Material.AIR) { + int current = player.getInventory().getItemInMainHand().getAmount(); + player.getInventory().getItemInMainHand().setAmount(current - 1); + return true; + } + return false; + }); + + BiFunction<@NotNull Player, @Nullable String, @NotNull Boolean> executor; + + ItemActionType(BiFunction<@NotNull Player, @Nullable String, @NotNull Boolean> executor) { + this.executor = executor; + } + + public BiFunction getExecutor() { + return executor; + } + + public boolean execute(@NotNull Player player, @Nullable String content) { + return getExecutor().apply(player, content); + } + + public static ItemActionType read(String string) { + return Arrays.stream(ItemActionType.values()) + .filter(action -> action.name().equalsIgnoreCase(string)) + .findFirst().orElse(null); + } + +} \ No newline at end of file diff --git a/src/main/java/cc/carm/plugin/commanditem/item/ItemExecuteResult.java b/src/main/java/cc/carm/plugin/commanditem/item/ItemExecuteResult.java new file mode 100644 index 0000000..9a05f51 --- /dev/null +++ b/src/main/java/cc/carm/plugin/commanditem/item/ItemExecuteResult.java @@ -0,0 +1,20 @@ +package cc.carm.plugin.commanditem.item; + +public enum ItemExecuteResult { + + SUCCESS(0), + FAILED(1), + + ; + + int id; + + ItemExecuteResult(int id) { + this.id = id; + } + + public int getId() { + return id; + } + +} diff --git a/src/main/java/cc/carm/plugin/commanditem/item/ItemRestrictions.java b/src/main/java/cc/carm/plugin/commanditem/item/ItemRestrictions.java new file mode 100644 index 0000000..5631a16 --- /dev/null +++ b/src/main/java/cc/carm/plugin/commanditem/item/ItemRestrictions.java @@ -0,0 +1,57 @@ +package cc.carm.plugin.commanditem.item; + +import cc.carm.lib.easysql.api.util.TimeDateUtils; +import org.bukkit.configuration.ConfigurationSection; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class ItemRestrictions { + + long startTime; + long endTime; + + public ItemRestrictions() { + this(-1, -1); + } + + public ItemRestrictions(long startTime, long endTime) { + this.startTime = startTime; + this.endTime = endTime; + } + + /** + * @return 限定的开始时间,-1表示不限定。 + */ + public long getStartTime() { + return startTime; + } + + /** + * @return 限定的结束时间,-1表示不限定。 + */ + public long getEndTime() { + return endTime; + } + + public CheckResult check() { + if (startTime > 0 && startTime > System.currentTimeMillis()) return CheckResult.NOT_STARTED; + if (endTime > 0 && endTime < System.currentTimeMillis()) return CheckResult.EXPIRED; + return CheckResult.AVAILABLE; + } + + public enum CheckResult { + + AVAILABLE, + NOT_STARTED, + EXPIRED; + + } + + public static @NotNull ItemRestrictions read(@Nullable ConfigurationSection section) { + if (section == null) return new ItemRestrictions(); + return new ItemRestrictions( + TimeDateUtils.parseTimeMillis(section.getString("time.start")), + TimeDateUtils.parseTimeMillis(section.getString("time.end")) + ); + } +} diff --git a/src/main/java/cc/carm/plugin/commanditem/item/ItemSettings.java b/src/main/java/cc/carm/plugin/commanditem/item/ItemSettings.java new file mode 100644 index 0000000..cb1621c --- /dev/null +++ b/src/main/java/cc/carm/plugin/commanditem/item/ItemSettings.java @@ -0,0 +1,105 @@ +package cc.carm.plugin.commanditem.item; + +import cc.carm.plugin.commanditem.CommandItemAPI; +import cc.carm.plugin.commanditem.manager.ConfigManager; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSortedMap; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.Unmodifiable; + +import java.io.File; +import java.util.Map; +import java.util.UUID; + +public class ItemSettings { + + protected final @NotNull String identifier; + @Nullable String name; + + @Nullable ItemStackConfig item; + @NotNull ItemRestrictions restrictions; + + @Nullable ItemActionGroup defaultActions; + @NotNull Map permissions; + @NotNull Map actions; + + public ItemSettings(@NotNull String identifier, @Nullable String name, + @Nullable ItemStackConfig item, @NotNull ItemRestrictions restrictions, + @Nullable ItemActionGroup defaultActions, + @NotNull Map permissions, + @NotNull Map actions) { + this.identifier = identifier; + this.name = name; + this.item = item; + this.restrictions = restrictions; + this.defaultActions = defaultActions; + this.permissions = permissions; + this.actions = actions; + } + + public @NotNull String getIdentifier() { + return identifier; + } + + public @Nullable String getName() { + return name; + } + + public @Nullable ItemStack getItemStack(int amount) { + ItemStack originalItem = this.item == null ? null : this.item.getItemStack(amount); + if (originalItem == null) return null; + return applyItem(originalItem); + } + + public @Nullable ItemStack getItemStack() { + return getItemStack(1); + } + + @Unmodifiable + public @NotNull Map getPermissions() { + return ImmutableSortedMap.copyOf(permissions); + } + + @Unmodifiable + public @NotNull Map getActions() { + return ImmutableMap.copyOf(actions); + } + + public @Nullable ItemActionGroup getDefaultActions() { + return defaultActions; + } + + public @Nullable ItemActionGroup getPlayerActions(@NotNull Player player) { + String actionGroup = getPermissions().entrySet().stream() + .filter(entry -> player.hasPermission(entry.getValue())) + .map(Map.Entry::getKey).findFirst().orElse(null); + return getActions().getOrDefault(actionGroup, getDefaultActions()); + } + + public @NotNull ItemStack applyItem(ItemStack originalItem) { + return CommandItemAPI.getItemsManager().applyTag(originalItem, identifier, UUID.randomUUID()); + } + + public static @NotNull ItemSettings load(@NotNull File file) throws Exception { + return load(YamlConfiguration.loadConfiguration(file)); + } + + public static @NotNull ItemSettings load(@NotNull FileConfiguration config) throws Exception { + String identifier = config.getString("identifier"); + if (identifier == null) throw new Exception("identifier not provided."); + return new ItemSettings( + identifier, config.getString("name"), + ItemStackConfig.read(config.getConfigurationSection("item")), + ItemRestrictions.read(config.getConfigurationSection("restrictions")), + ItemActionGroup.read(config.getStringList("actions.default")), + ConfigManager.readStringMap(config.getConfigurationSection("permissions"), (s -> s)), + ConfigManager.readListMap(config.getConfigurationSection("actions"), ItemActionGroup::read) + ); + } + +} diff --git a/src/main/java/cc/carm/plugin/commanditem/item/ItemStackConfig.java b/src/main/java/cc/carm/plugin/commanditem/item/ItemStackConfig.java new file mode 100644 index 0000000..4ac9f17 --- /dev/null +++ b/src/main/java/cc/carm/plugin/commanditem/item/ItemStackConfig.java @@ -0,0 +1,103 @@ +package cc.carm.plugin.commanditem.item; + +import cc.carm.lib.easyplugin.utils.ItemStackFactory; +import org.bukkit.Material; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.Optional; + +@SuppressWarnings("UnusedReturnValue") +public class ItemStackConfig { + + protected @Nullable ItemStack original; + + protected @Nullable Material material; + protected @Nullable String displayName; + protected @Nullable List lore; + + public ItemStackConfig() { + } + + public ItemStackConfig(@Nullable Material material, @Nullable String displayName, @Nullable List lore) { + this(null, material, displayName, lore); + } + + public ItemStackConfig(@Nullable ItemStack original) { + this(original, null, null, null); + } + + public ItemStackConfig(@Nullable ItemStack original, + @Nullable Material material, @Nullable String displayName, @Nullable List lore) { + this.original = original; + this.material = material; + this.displayName = displayName; + this.lore = lore; + } + + public @Nullable ItemStack getItemStack(int amount) { + if (amount <= 0) return null; + if (original != null) return original.clone(); + if (material == null) return null; + ItemStackFactory factory = new ItemStackFactory(material, amount); + if (displayName != null) factory.setDisplayName(displayName); + if (lore != null && !lore.isEmpty()) factory.setLore(lore); + return factory.toItemStack(); + } + + public @Nullable ItemStack getItemStack() { + return getItemStack(1); + } + + public @Nullable ItemStack getOriginal() { + return original; + } + + public ItemStackConfig setOriginal(@Nullable ItemStack original) { + this.original = original; + return this; + } + + public @Nullable Material getMaterial() { + return material; + } + + public ItemStackConfig setMaterial(@Nullable Material material) { + this.material = material; + return this; + } + + public @Nullable String getDisplayName() { + return displayName; + } + + public ItemStackConfig setDisplayName(@Nullable String displayName) { + this.displayName = displayName; + return this; + } + + public @Nullable List getLore() { + return lore; + } + + public ItemStackConfig setLore(@Nullable List lore) { + this.lore = lore; + return this; + } + + public static @Nullable ItemStackConfig read(@Nullable ConfigurationSection section) { + if (section == null) return null; + ItemStackConfig config = new ItemStackConfig(); + if (section.contains("original") && section.isItemStack("original")) { + config.setOriginal(section.getItemStack("original")); + } + Optional.ofNullable(section.getString("material")).map(Material::matchMaterial).ifPresent(config::setMaterial); + Optional.ofNullable(section.getString("displayName")).ifPresent(config::setDisplayName); + Optional.of(section.getStringList("lore")).filter(l -> !l.isEmpty()).ifPresent(config::setLore); + return config; + } + + +} diff --git a/src/main/java/cc/carm/plugin/commanditem/listener/ItemListener.java b/src/main/java/cc/carm/plugin/commanditem/listener/ItemListener.java new file mode 100644 index 0000000..374d3f5 --- /dev/null +++ b/src/main/java/cc/carm/plugin/commanditem/listener/ItemListener.java @@ -0,0 +1,43 @@ +package cc.carm.plugin.commanditem.listener; + +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.entity.EntityPickupItemEvent; +import org.bukkit.event.inventory.CraftItemEvent; +import org.bukkit.event.player.PlayerInteractEvent; + +public class ItemListener implements Listener { + + + /** + * 监听玩家点击,并执行物品对应的操作。 + * + * @param event 玩家点击事件 + */ + @EventHandler + public void onClick(PlayerInteractEvent event) { + + } + + /** + * 监听玩家合成,阻止玩家将指令物品合成浪费掉。 + * + * @param event 合成事件 + */ + @EventHandler + public void onCraft(CraftItemEvent event) { + + } + + /** + * 阻止非玩家捡起指令物品 + * + * @param event 捡起事件 + */ + @EventHandler + public void onPickup(EntityPickupItemEvent event) { + + } + + +} diff --git a/src/main/java/cc/carm/plugin/commanditem/manager/ConfigManager.java b/src/main/java/cc/carm/plugin/commanditem/manager/ConfigManager.java index 8b790e6..015b329 100644 --- a/src/main/java/cc/carm/plugin/commanditem/manager/ConfigManager.java +++ b/src/main/java/cc/carm/plugin/commanditem/manager/ConfigManager.java @@ -5,8 +5,15 @@ import cc.carm.lib.easyplugin.configuration.language.MessagesConfig; import cc.carm.lib.easyplugin.configuration.language.MessagesInitializer; import cc.carm.plugin.commanditem.Main; import cc.carm.plugin.commanditem.configuration.PluginMessages; +import org.bukkit.configuration.ConfigurationSection; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.io.IOException; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Function; public class ConfigManager { @@ -53,5 +60,41 @@ public class ConfigManager { } } + public static Map readStringMap(@Nullable ConfigurationSection section, + @NotNull Function valueCast) { + if (section == null) return new LinkedHashMap<>(); + Map result = new LinkedHashMap<>(); + for (String key : section.getKeys(false)) { + V finalValue = valueCast.apply(section.getString(key)); + if (finalValue != null) result.put(key, finalValue); + } + return result; + } + + public static Map readSectionMap(@Nullable ConfigurationSection section, + @NotNull Function valueCast) { + if (section == null) return new LinkedHashMap<>(); + Map result = new LinkedHashMap<>(); + for (String key : section.getKeys(false)) { + if (!section.isConfigurationSection(key)) continue; + V finalValue = valueCast.apply(section.getConfigurationSection(key)); + if (finalValue != null) result.put(key, finalValue); + } + return result; + } + + + public static Map readListMap(@Nullable ConfigurationSection section, + @NotNull Function<@NotNull List, V> valueCast) { + if (section == null) return new LinkedHashMap<>(); + Map result = new LinkedHashMap<>(); + for (String key : section.getKeys(false)) { + if (!section.isConfigurationSection(key)) continue; + V finalValue = valueCast.apply(section.getStringList(key)); + if (finalValue != null) result.put(key, finalValue); + } + return result; + } + } diff --git a/src/main/java/cc/carm/plugin/commanditem/manager/ItemsManager.java b/src/main/java/cc/carm/plugin/commanditem/manager/ItemsManager.java new file mode 100644 index 0000000..98ca407 --- /dev/null +++ b/src/main/java/cc/carm/plugin/commanditem/manager/ItemsManager.java @@ -0,0 +1,124 @@ +package cc.carm.plugin.commanditem.manager; + +import cc.carm.plugin.commanditem.Main; +import cc.carm.plugin.commanditem.configuration.PluginConfig; +import cc.carm.plugin.commanditem.item.CommandItem; +import cc.carm.plugin.commanditem.item.ItemSettings; +import com.google.common.collect.ImmutableMap; +import org.bukkit.NamespacedKey; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.inventory.meta.tags.CustomItemTagContainer; +import org.bukkit.inventory.meta.tags.ItemTagType; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.Unmodifiable; + +import java.io.File; +import java.util.*; +import java.util.stream.Collectors; + +public class ItemsManager { + + private static final String FOLDER_NAME = "items"; + + public HashMap items = new HashMap<>(); + + protected NamespacedKey idKey; + protected NamespacedKey uuidKey; + + public void initialize() { + this.idKey = new NamespacedKey(Main.getInstance(), "id"); + this.uuidKey = new NamespacedKey(Main.getInstance(), "uuid"); + loadItems(); + Main.info("共加载了 " + items.size() + " 个前缀。"); + } + + public void loadItems() { + File prefixDataFolder = getStorageFolder(); + if (!prefixDataFolder.isDirectory() || !prefixDataFolder.exists()) { + boolean success = prefixDataFolder.mkdir(); + } + + String[] filesList = prefixDataFolder.list(); + if (filesList == null || filesList.length < 1) { + Main.severe("配置文件夹中暂无任何物品,请检查。"); + Main.severe("There's no configured items."); + Main.severe("Path: " + prefixDataFolder.getAbsolutePath()); + return; + } + + List files = Arrays.stream(filesList) + .map(s -> new File(prefixDataFolder, s)) + .filter(File::isFile) + .collect(Collectors.toList()); + + HashMap dataItems = new HashMap<>(); + + if (files.size() > 0) { + for (File file : files) { + try { + ItemSettings item = ItemSettings.load(file); + Main.info("完成物品加载 " + item.getIdentifier() + " : " + item.getName()); + Main.info("Successfully loaded " + item.getIdentifier() + " : " + item.getName()); + dataItems.put(item.getIdentifier(), item); + } catch (Exception ex) { + Main.severe("在加载物品 " + file.getAbsolutePath() + " 时出错,请检查配置!"); + Main.severe("Error occurred when loading item #" + file.getAbsolutePath() + " !"); + ex.printStackTrace(); + } + } + } + + items = dataItems; + } + + private static File getStorageFolder() { + if (PluginConfig.CustomStorage.ENABLE.get()) { + return new File(PluginConfig.CustomStorage.PATH.get()); + } else { + return new File(Main.getInstance().getDataFolder() + File.separator + FOLDER_NAME); + } + } + + @Unmodifiable + public Map listItemSettings() { + return ImmutableMap.copyOf(items); + } + + public @Nullable ItemSettings getItemSettings(@NotNull String identifier) { + return items.get(identifier); + } + + public ItemStack applyTag(@NotNull ItemStack originalItem, String identifier, UUID uuid) { + if (!originalItem.hasItemMeta()) return originalItem; + ItemMeta meta = originalItem.getItemMeta(); + if (meta == null) return originalItem; + CustomItemTagContainer container = meta.getCustomTagContainer(); + container.setCustomTag(idKey, ItemTagType.STRING, identifier); + container.setCustomTag(uuidKey, ItemTagType.STRING, uuid.toString()); + originalItem.setItemMeta(meta); + return originalItem; + } + + public @Nullable CommandItem parseCommandItem(@Nullable ItemStack item) { + if (item == null) return null; + if (!item.hasItemMeta()) return null; + ItemMeta meta = item.getItemMeta(); + if (meta == null) return null; + CustomItemTagContainer container = meta.getCustomTagContainer(); + String settingsID = container.getCustomTag(this.idKey, ItemTagType.STRING); + String itemUUID = container.getCustomTag(this.uuidKey, ItemTagType.STRING); + if (settingsID == null || itemUUID == null) return null; + ItemSettings settings = getItemSettings(settingsID); + if (settings == null) return null; + return new CommandItem(UUID.fromString(itemUUID), settings, item); + } + + public boolean isCommandItem(ItemStack item) { + return item.hasItemMeta() && item.getItemMeta() != null + && item.getItemMeta().getCustomTagContainer().hasCustomTag(idKey, ItemTagType.STRING); + } + + +} diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index ea538ee..489b36f 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -15,6 +15,14 @@ metrics: true # 检查更新为异步操作,绝不会影响性能与使用体验。 check-update: true +custom-storage: + # 自定义存储位置 + # 默认存储位置为 “插件文件夹”/items + # 可以规定到远程文件夹中去寻找前缀相关的设定 + # 支持绝对文件路径,如 "/etc/minecraft/configurations/items/" + enable: false # 是否启用 + path: "items/" # 一定要指向一个文件夹! + log-storage: # 是否启用日志记录存储 # 可用于追踪物品的发放、领取情况与执行记录。 @@ -32,4 +40,6 @@ log-storage: password: "password" extra: "?useSSL=false" # 插件相关表的名称 - table-name: "log_item_commands" \ No newline at end of file + tables: + give: "log_item_give" + take: "log_item_take" \ No newline at end of file diff --git a/src/main/resources/items/.example-item.yml b/src/main/resources/items/.example-item.yml new file mode 100644 index 0000000..0288de5 --- /dev/null +++ b/src/main/resources/items/.example-item.yml @@ -0,0 +1,55 @@ +# 唯一标识 [必须] +# 将用于判定一个物品对于的配置文件 +# 必须 必须 必须 保持唯一! +identifier: "pro" + +name: "Pro会员前缀" + +item: + # 使用原生 ItemStack 配置物品 可能引起配置无法加载而报错! + original: + ==: org.bukkit.inventory.ItemStack + type: DIAMOND + damage: 8 + meta: + ==: ItemMeta + meta-type: UNSPECIFIC + display-name: "&b&lPro+ &b会员前缀" + lore: + - "&7手持物品右键点击即可获得!" + + # 使用插件提供的方法配置物品,更简单 + type: DIAMOND + name: "&b&lPro+ &b会员前缀" + lore: + - "&7手持物品右键点击即可获得!" + +functions: + log-give: true # 是否为该物品记录发放日志 + log-take: false # 是否为该物品记录拿取日志 (即出现 [take] action 时记录) + +# 限定相关配置 +restrictions: + time: # 允许领取的时间范围 + start: 2021-12-21 15:33:21 # 开始时间,若无该选项则不限制开始时间 + end: 2022-01-21 15:33:21 # 结束时间,若无该选项则不限制结束时间 + +# 相关权限设定 +# 若玩家拥有配置的权限,则会执行权限对应的操作; +# 权限配置从上向下判断,若权限设定为空,则会使用default的操作。 +permissions: + normal: "prefix.item.use" + have: "prefix.pro" + +# 玩家右键物品时执行的操作 +actions: + default: # 默认执行的操作 + - "[message] &c您没有使用该物品的权限!" + normal: # 若有 normal 权限设定,则执行此操作 + - "[console] say %player_name% 获得了 %name% !" + - "[console] lp user %player_name% permission set prefix.pro true" + - "[chat] /prefix" # 以玩家聊天的形式发出消息,若以 "/" 开头则会被视为命令 + - "[sound] ENTITY_PLAYER_LEVELUP" # 播放声音 + - "[take]" # 拿走对应物品 (即数量-1) + have: + - "[message] &c您已经拥有了该前缀,无法重复领取!"