1
mirror of https://github.com/CarmJos/ScriptItems synced 2024-09-19 21:35:50 +00:00

完成物品基本逻辑与配置文件读取

This commit is contained in:
Carm Jos 2022-03-11 08:16:22 +08:00
parent e77c7e2804
commit fb1ee145f0
19 changed files with 866 additions and 15 deletions

View File

@ -14,10 +14,11 @@
物品指令绑定插件给予玩家可执行对应指令的消耗物品基于EasyPlugin实现。
## 插件功能与优势
> 加 * 的功能仍在开发中。
- 物品指令绑定给予玩家可执行对应指令的消耗物品支持PlaceholderAPI变量。
- **允许限定。** 允许给物品对应的指令组设定“领取次数”、“每日领取次数”与“领取时间”限定。
- **详细记录。** 每个物品均有独立ID并对使用的玩家与执行结果进行详细记录便于追踪查询。
- **\*详细记录。** 每个物品均有独立ID并对使用的玩家与执行结果进行详细记录便于追踪查询。
- **异步存取。** 数据读取与存储均为异步操作,不影响服务器性能。
- **轻量插件。** 适合小型服务器使用,配置简单方便。
- **规范开发。** 插件架构符合开发规范,适合新手开发者学习。

View File

@ -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;
}
}

View File

@ -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("已禁用检查更新,跳过。");

View File

@ -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<Boolean> ENABLE = new ConfigValue<>("custom-storage.enable", Boolean.class, false);
public static ConfigValue<String> PATH = new ConfigValue<>("custom-storage.path", String.class, "items/");
}
}

View File

@ -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<String> 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<String> 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`)"
};
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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());
}
}

View File

@ -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<ItemAction> actions;
public ItemActionGroup(List<ItemAction> actions) {
this.actions = actions;
}
public static @NotNull ItemActionGroup read(@NotNull List<String> actionsString) {
List<ItemAction> actions = actionsString.stream()
.map(ItemAction::read).filter(Objects::nonNull).collect(Collectors.toList());
return new ItemActionGroup(actions);
}
}

View File

@ -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<String> 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<String> 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;
}),
/**
* 向玩家发送声音
* 允许配置音量与音调
* <ul>
* <li>SOUND_NAME</li>
* <li>SOUND_NAME:VOLUME</li>
* <li>SOUND_NAME:VOLUME:PITCH</li>
* </ul>
*/
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<Player, String, Boolean> 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);
}
}

View File

@ -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;
}
}

View File

@ -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"))
);
}
}

View File

@ -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<String, String> permissions;
@NotNull Map<String, ItemActionGroup> actions;
public ItemSettings(@NotNull String identifier, @Nullable String name,
@Nullable ItemStackConfig item, @NotNull ItemRestrictions restrictions,
@Nullable ItemActionGroup defaultActions,
@NotNull Map<String, String> permissions,
@NotNull Map<String, ItemActionGroup> 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<String, String> getPermissions() {
return ImmutableSortedMap.copyOf(permissions);
}
@Unmodifiable
public @NotNull Map<String, ItemActionGroup> 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)
);
}
}

View File

@ -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<String> lore;
public ItemStackConfig() {
}
public ItemStackConfig(@Nullable Material material, @Nullable String displayName, @Nullable List<String> 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<String> 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<String> getLore() {
return lore;
}
public ItemStackConfig setLore(@Nullable List<String> 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;
}
}

View File

@ -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) {
}
}

View File

@ -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 <V> Map<String, V> readStringMap(@Nullable ConfigurationSection section,
@NotNull Function<String, V> valueCast) {
if (section == null) return new LinkedHashMap<>();
Map<String, V> 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 <V> Map<String, V> readSectionMap(@Nullable ConfigurationSection section,
@NotNull Function<ConfigurationSection, V> valueCast) {
if (section == null) return new LinkedHashMap<>();
Map<String, V> 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 <V> Map<String, V> readListMap(@Nullable ConfigurationSection section,
@NotNull Function<@NotNull List<String>, V> valueCast) {
if (section == null) return new LinkedHashMap<>();
Map<String, V> 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;
}
}

View File

@ -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<String, ItemSettings> 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<File> files = Arrays.stream(filesList)
.map(s -> new File(prefixDataFolder, s))
.filter(File::isFile)
.collect(Collectors.toList());
HashMap<String, ItemSettings> 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<String, ItemSettings> 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);
}
}

View File

@ -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"
tables:
give: "log_item_give"
take: "log_item_take"

View File

@ -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您已经拥有了该前缀无法重复领取"