From c184e107cd14f11ebf71c8ebc0aae91f8cad83c5 Mon Sep 17 00:00:00 2001 From: carm Date: Mon, 23 Aug 2021 03:12:37 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E6=88=90=E5=86=85=E5=AE=B9=EF=BC=8C?= =?UTF-8?q?=E9=A6=96=E6=AC=A1=E6=8F=90=E4=BA=A4=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 3 + README.md | 175 ++++++++ pom.xml | 173 ++++++++ .../java/cc/carm/plugin/userprefix/Main.java | 86 ++++ .../command/UserPrefixAdminCommand.java | 61 +++ .../userprefix/command/UserPrefixCommand.java | 22 + .../configuration/PrefixConfig.java | 37 ++ .../configuration/values/ConfigSound.java | 60 +++ .../configuration/values/ConfigValue.java | 37 ++ .../configuration/values/ConfigValueList.java | 45 +++ .../hooker/UserPrefixExpansion.java | 89 +++++ .../userprefix/listener/UserListener.java | 39 ++ .../processor/UserNodeUpdateProcessor.java | 29 ++ .../userprefix/manager/ConfigManager.java | 31 ++ .../userprefix/manager/PrefixManager.java | 129 ++++++ .../userprefix/manager/ServiceManager.java | 45 +++ .../userprefix/manager/UserManager.java | 222 ++++++++++ .../userprefix/model/ConfiguredPrefix.java | 97 +++++ .../userprefix/nametag/UserNameTag.java | 153 +++++++ .../plugin/userprefix/ui/PrefixSelectGUI.java | 94 +++++ .../plugin/userprefix/util/ColorParser.java | 10 + .../userprefix/util/ItemStackFactory.java | 141 +++++++ .../plugin/userprefix/util/MessageUtil.java | 63 +++ .../userprefix/util/gui/AutoPagedGUI.java | 105 +++++ .../userprefix/util/gui/CommonPagedGUI.java | 170 ++++++++ .../carm/plugin/userprefix/util/gui/GUI.java | 378 ++++++++++++++++++ .../plugin/userprefix/util/gui/GUIItem.java | 96 +++++ .../plugin/userprefix/util/gui/GUIType.java | 30 ++ .../plugin/userprefix/util/gui/PagedGUI.java | 75 ++++ src/main/resources/config.yml | 102 +++++ src/main/resources/plugin.yml | 20 + 31 files changed, 2817 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 pom.xml create mode 100644 src/main/java/cc/carm/plugin/userprefix/Main.java create mode 100644 src/main/java/cc/carm/plugin/userprefix/command/UserPrefixAdminCommand.java create mode 100644 src/main/java/cc/carm/plugin/userprefix/command/UserPrefixCommand.java create mode 100644 src/main/java/cc/carm/plugin/userprefix/configuration/PrefixConfig.java create mode 100644 src/main/java/cc/carm/plugin/userprefix/configuration/values/ConfigSound.java create mode 100644 src/main/java/cc/carm/plugin/userprefix/configuration/values/ConfigValue.java create mode 100644 src/main/java/cc/carm/plugin/userprefix/configuration/values/ConfigValueList.java create mode 100644 src/main/java/cc/carm/plugin/userprefix/hooker/UserPrefixExpansion.java create mode 100644 src/main/java/cc/carm/plugin/userprefix/listener/UserListener.java create mode 100644 src/main/java/cc/carm/plugin/userprefix/listener/processor/UserNodeUpdateProcessor.java create mode 100644 src/main/java/cc/carm/plugin/userprefix/manager/ConfigManager.java create mode 100644 src/main/java/cc/carm/plugin/userprefix/manager/PrefixManager.java create mode 100644 src/main/java/cc/carm/plugin/userprefix/manager/ServiceManager.java create mode 100644 src/main/java/cc/carm/plugin/userprefix/manager/UserManager.java create mode 100644 src/main/java/cc/carm/plugin/userprefix/model/ConfiguredPrefix.java create mode 100644 src/main/java/cc/carm/plugin/userprefix/nametag/UserNameTag.java create mode 100644 src/main/java/cc/carm/plugin/userprefix/ui/PrefixSelectGUI.java create mode 100644 src/main/java/cc/carm/plugin/userprefix/util/ColorParser.java create mode 100644 src/main/java/cc/carm/plugin/userprefix/util/ItemStackFactory.java create mode 100644 src/main/java/cc/carm/plugin/userprefix/util/MessageUtil.java create mode 100644 src/main/java/cc/carm/plugin/userprefix/util/gui/AutoPagedGUI.java create mode 100644 src/main/java/cc/carm/plugin/userprefix/util/gui/CommonPagedGUI.java create mode 100644 src/main/java/cc/carm/plugin/userprefix/util/gui/GUI.java create mode 100644 src/main/java/cc/carm/plugin/userprefix/util/gui/GUIItem.java create mode 100644 src/main/java/cc/carm/plugin/userprefix/util/gui/GUIType.java create mode 100644 src/main/java/cc/carm/plugin/userprefix/util/gui/PagedGUI.java create mode 100644 src/main/resources/config.yml create mode 100644 src/main/resources/plugin.yml diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2e965e1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/.idea/ +/target/ +./*.iml \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..2bd9fdc --- /dev/null +++ b/README.md @@ -0,0 +1,175 @@ +# 用户前缀系统插件 + +轻便、高效、实时的用户前缀系统。 + +数据部分基于 [LuckPerms](https://www.spigotmc.org/resources/luckperms.28140/) 实现。 + +## 特性 + +- 理论上全版本支持! +- 游戏内重载配置文件并实时更新到玩家! +- 当玩家权限变更时会实时监测前缀,若权限不足则自动更换前缀并提示! +- 可配置的声音、消息! +- 前缀图标可配置“选中”、“有权限”与“无权限”三种状态的物品 + - 物品的配置通过ItemStack原生配置,支持MC所有的设定! + - 具体的设定请参考其他文档哦~ +- TabList自动按照前缀的权重排序 (如有冲突可关掉) +- 玩家头顶前缀显示 (如有冲突可关掉) +- 自动排序,且可翻页的GUI +- 支持PlaceholderAPI变量 + +## 注意事项 + +### 1. 版本支持问题 + +本插件理论全版本支持,如果出现图标不加载、声音无法播放等问题请检查配置文件中物品与声音的type在当前版本是否存在。 + +以声音举例,村民表示可以的声音在低版本中为 “`VILLAGER_YES`”,而在高版本中则变为了“`ENTITY_VILLAGER_YES`”。 + +### 2. 计分板异常问题 + +头顶上前缀的显示与TabList的排序均使用到了计分板API。 + +如有冲突导致其他插件的计分板无法显示,请关掉配置文件中`functions.OnNamePrefix`。 + +### 3. 物品图标配置问题 +物品相关均通过Bukkit提供的ItemStack序列化方法读取,相关配置方式请参考其他文档。 + +## 指令 + +本插件指令部分较为简单。 + +```text +/UserPrefix 或 /prefix 打开前缀更换GUI +/UserPrefixAdmin 查看管理员指令帮助 +/UserPrefixAdmin reload 重载配置文件 +/UserPrefixAdmin list 查看已配置的前缀内容 +``` + +## 变量 (PlaceholderAPI) + +安装 [PlaceholderAPI](https://github.com/PlaceholderAPI/PlaceholderAPI) 后,可以输入 `/papi info UserPrefix` 查看相关变量。 + +变量内容如下 + +```text +# %UserPrefix_prefix% +- 得到当前正在使用的前缀 +# %UserPrefix_weight% +- 得到当前正在使用的前缀权重 +# %UserPrefix_identifier% +- 得到当前正在使用的前缀标识 +# %UserPrefix_name% +- 得到当前正在使用的前缀名 +# %UserPrefix_has_% +- 判断玩家是否拥有某个前缀(true/false) +``` + +## 配置文件示例 + +```yaml +version: 1.0.0-SNAPSHOT # 配置文件版本,一般不会动。 + +debug: false #debug输出,开发者用的 + +functions: + OnNamePrefix: true # 是否给头顶上添加前缀,该方法用到了头顶的那个计分板,如有冲突请关掉哦~ + autoUsePrefix: true # 自动前缀显示 当玩家没有自己选择一个前缀的时候,会自动使用所拥有的的前缀中权重最高的那一个 + +messages: + selected: + - "&7您选择了 &f%(name) &7作为当前显示的前缀。" + expired: + - "&7您先前使用的前缀 &f%(oldName) &7已到期。" + - "&7现在已为您重新调整为 &f%(newName) &7。" + help: + - "&7输入 &b/prefix &7打开前缀选择菜单。" + +Sounds: #相关的声音,注释掉则不播放声音 格式为 【声音名:音量:音调】 或 【声音名:音量】 或 【声音名】 + openGUI: "BLOCK_NOTE_BLOCK_PLING:1:1" + guiClick: "UI_BUTTON_CLICK" + prefixChange: "ENTITY_VILLAGER_YES" + prefixExpired: "ENTITY_VILLAGER_NO" + +# 默认前缀的配置 +# 默认前缀的权重为0哦 +defaultPrefix: + name: "默认前缀" + content: "&b" + itemNotUsing: + ==: org.bukkit.inventory.ItemStack + type: NAME_TAG + meta: + ==: ItemMeta + meta-type: UNSPECIFIC + display-name: "§f默认玩家前缀 §f(点击切换)" + lore: + - "" + - "§a➥ 点击切换到该前缀" + itemUsing: + ==: org.bukkit.inventory.ItemStack + type: NAME_TAG + meta: + ==: ItemMeta + meta-type: UNSPECIFIC + display-name: "§f默认玩家前缀" + lore: + - "" + - "§a✔ 您正在使用该前缀" + +prefixes: + VIP: + name: "&b&lPro&b" # [必须] 名字(切换的时候左下角会弹提示 用的就是这个名字) + content: "§b§lPro §b" # [必须] 显示在名字前面的内容 + weight: 1 # [必须] 权重,用于GUI里面的排序(越大显示在越后面)和自动前缀显示 + permission: "yc.pro" # [非必须] 检测的权限,如果没有就是人人都能用,也代表不用配置“itemNoPermission”了(因为压根不可能显示没权限时候的物品) + itemHasPermission: + # [必须] 当有权限的时候会显示这个Item + ==: org.bukkit.inventory.ItemStack + type: DIAMOND + meta: + ==: ItemMeta + meta-type: UNSPECIFIC + display-name: "§b§lPro §b会员前缀" + lore: + - "§7Pro会员专属称号" + - "" + - "§f尊贵的Pro会员专属称号。" + - "§f您将获得多种特权与更好的游戏体验。" + - "" + - "§a➥ 点击切换到该前缀" + itemUsing: + # [非必需] 当有权限的时候会显示这个Item,如果没有这个配置就自动显示“itemHasPermission”的。 + ==: org.bukkit.inventory.ItemStack + type: DIAMOND + meta: + ==: ItemMeta + meta-type: UNSPECIFIC + display-name: "§b§lPro §b会员前缀" + enchants: + PROTECTION_ENVIRONMENTAL: 1 #加一个附魔这样看上去就像是选中了的 + lore: + - "§7Pro会员专属称号" + - "" + - "§f尊贵的Pro会员专属称号。" + - "§f您将获得多种特权与更好的游戏体验。" + - "" + - "§a✔ 您正在使用该前缀" + itemNoPermission: + # [非必需] 如果没有权限就会显示这个item。如果不配置该物品,则玩家没有使用权限时不会显示在GUI里面。 + ==: org.bukkit.inventory.ItemStack + type: INK_SACK + damage: 8 + meta: + ==: ItemMeta + meta-type: UNSPECIFIC + display-name: "§b§lPro+ §b会员前缀 §c(未拥有)" + lore: + - "§7Pro+会员专属称号" + - "" + - "§f尊贵的Pro会员专属称号。" + - "§f您将获得多种特权与更好的游戏体验。" + - "§f您可以输入 §b/vip §f指令查看详细特权!" + - "" + - "§e✯ 加入Pro+会员以使用该前缀!" +``` \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..24110f4 --- /dev/null +++ b/pom.xml @@ -0,0 +1,173 @@ + + + 4.0.0 + + cc.carm.plugin + UserPrefix + 1.0.0-SNAPSHOT + + + 8 + 8 + true + UTF-8 + UTF-8 + + + + + + ycraft + https://maven.ycraft.cn/repository/maven-public/ + + + + luck-repo + https://repo.lucko.me/ + + + + + + + org.spigotmc + spigot + 1.17-R0.1-SNAPSHOT + provided + + + + me.clip + placeholderapi + 2.10.9 + provided + + + + net.luckperms + api + 5.3 + provided + + + + com.comphenix.protocol + ProtocolLib + 4.5.0 + provided + + + + com.comphenix.packetwrapper + PacketWrapper + 1.13-R0.1-SNAPSHOT + provided + + + + junit + junit + 4.13 + test + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.22.1 + + false + + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 3.2.0 + + javadoc + + https://javadoc.io/doc/org.jetbrains/annotations/ + + true + UTF-8 + + + + attach-javadocs + + jar + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + + 1.8 + 1.8 + UTF-8 + -parameters + + + + org.apache.maven.plugins + maven-jar-plugin + 3.2.0 + + + org.apache.maven.plugins + maven-shade-plugin + 3.2.3 + + + package + + shade + + + false + + + + + + + *:* + + META-INF/MANIFEST.MF + META-INF/*.txt + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.22.1 + + false + + + + + + + src/main/resources + true + + + + + + \ No newline at end of file diff --git a/src/main/java/cc/carm/plugin/userprefix/Main.java b/src/main/java/cc/carm/plugin/userprefix/Main.java new file mode 100644 index 0000000..7ab0fa5 --- /dev/null +++ b/src/main/java/cc/carm/plugin/userprefix/Main.java @@ -0,0 +1,86 @@ +package cc.carm.plugin.userprefix; + +import cc.carm.plugin.userprefix.command.UserPrefixAdminCommand; +import cc.carm.plugin.userprefix.command.UserPrefixCommand; +import cc.carm.plugin.userprefix.configuration.PrefixConfig; +import cc.carm.plugin.userprefix.hooker.UserPrefixExpansion; +import cc.carm.plugin.userprefix.listener.UserListener; +import cc.carm.plugin.userprefix.listener.processor.UserNodeUpdateProcessor; +import cc.carm.plugin.userprefix.manager.ServiceManager; +import net.luckperms.api.event.user.UserDataRecalculateEvent; +import cc.carm.plugin.userprefix.manager.ConfigManager; +import cc.carm.plugin.userprefix.manager.PrefixManager; +import cc.carm.plugin.userprefix.util.ColorParser; +import org.bukkit.Bukkit; +import org.bukkit.event.Listener; +import org.bukkit.plugin.java.JavaPlugin; + +public class Main extends JavaPlugin { + + private static Main instance; + + @Override + public void onEnable() { + instance = this; + + log(getName() + " " + getDescription().getVersion() + " &7开始加载..."); + long startTime = System.currentTimeMillis(); + + log("加载配置文件..."); + ConfigManager.initConfig(); + PrefixManager.init(); + + log("注册指令..."); + Bukkit.getPluginCommand("UserPrefix").setExecutor(new UserPrefixCommand()); + Bukkit.getPluginCommand("UserPrefixAdmin").setExecutor(new UserPrefixAdminCommand()); + + log("注册监听器..."); + regListener(new UserListener()); + ServiceManager.getService().getEventBus().subscribe(this, UserDataRecalculateEvent.class, UserNodeUpdateProcessor::process); + + if (Bukkit.getPluginManager().getPlugin("PlaceholderAPI") != null) { + log("注册变量..."); + new UserPrefixExpansion(getInstance()).register(); + } else { + log("未安装 PlaceholderAPI 放弃注册变量..."); + } + + + log("加载完成 ,共耗时 " + (System.currentTimeMillis() - startTime) + " ms 。"); + } + + @Override + public void onDisable() { + log(getName() + " " + getDescription().getVersion() + " 开始卸载..."); + long startTime = System.currentTimeMillis(); + + log("卸载监听器..."); + Bukkit.getServicesManager().unregisterAll(this); + + log("卸载完成 ,共耗时 " + (System.currentTimeMillis() - startTime) + " ms 。"); + } + + /** + * 注册监听器 + * + * @param listener 监听器 + */ + public static void regListener(Listener listener) { + Bukkit.getPluginManager().registerEvents(listener, getInstance()); + } + + public static void log(String message) { + Bukkit.getConsoleSender().sendMessage(ColorParser.parseColor("[" + getInstance().getName() + "] " + message)); + } + + public static void debug(String message) { + if (PrefixConfig.DEBUG.get()) { + log("[DEBUG] " + ColorParser.parseColor(message)); + } + } + + public static JavaPlugin getInstance() { + return instance; + } + +} diff --git a/src/main/java/cc/carm/plugin/userprefix/command/UserPrefixAdminCommand.java b/src/main/java/cc/carm/plugin/userprefix/command/UserPrefixAdminCommand.java new file mode 100644 index 0000000..6f55916 --- /dev/null +++ b/src/main/java/cc/carm/plugin/userprefix/command/UserPrefixAdminCommand.java @@ -0,0 +1,61 @@ +package cc.carm.plugin.userprefix.command; + +import cc.carm.plugin.userprefix.ui.PrefixSelectGUI; +import cc.carm.plugin.userprefix.manager.PrefixManager; +import cc.carm.plugin.userprefix.manager.UserManager; +import cc.carm.plugin.userprefix.model.ConfiguredPrefix; +import cc.carm.plugin.userprefix.util.ColorParser; +import org.bukkit.Bukkit; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; + +public class UserPrefixAdminCommand implements CommandExecutor { + + + @Override + public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String s, @NotNull String[] args) { + if (args.length == 1) { + String aim = args[0]; + if (aim.equalsIgnoreCase("list")) { + sender.sendMessage(ColorParser.parseColor("&3&l用户前缀系统 &f前缀列表")); + for (ConfiguredPrefix value : PrefixManager.getPrefixes().values()) { + sender.sendMessage(ColorParser.parseColor("&8#" + value.getWeight() + " &f" + value.getIdentifier())); + sender.sendMessage(ColorParser.parseColor("&8- &7显示名 &r" + value.getName() + " &7权限&r " + value.getPermission())); + sender.sendMessage(ColorParser.parseColor("&8- &7内容示例&r " + value.getContent() + sender.getName())); + } + return true; + } else if (aim.equalsIgnoreCase("reload")) { + long s1 = System.currentTimeMillis(); + PrefixSelectGUI.closeAll(); // 关掉所有正在显示的前缀列表 + PrefixManager.loadConfiguredPrefixes(); //重载配置文件 + for (Player onlinePlayer : Bukkit.getOnlinePlayers()) { + UserManager.checkPrefix(onlinePlayer, false); + /* + * 这里关掉loadOthers(为其他玩家更新)了。 + * 因为每个玩家更新的时候会为其他人更新自己, + * 全部走完一遍后,所有玩家都会加载最新的前缀内容。 + */ + UserManager.updatePrefixView(onlinePlayer, false); + } + sender.sendMessage(ColorParser.parseColor("&a&l重载完成!&7共耗时 &f" + (System.currentTimeMillis() - s1) + " ms&7。")); + return true; + } + return help(sender); + } + return help(sender); + } + + public static boolean help(CommandSender sender) { + sender.sendMessage(ColorParser.parseColor("&3&l用户前缀系统 &f帮助")); + sender.sendMessage(ColorParser.parseColor("&8#&f list")); + sender.sendMessage(ColorParser.parseColor("&8- &7查看当前前缀列表。")); + sender.sendMessage(ColorParser.parseColor("&8#&f reload")); + sender.sendMessage(ColorParser.parseColor("&8- &7重载前缀配置。")); + return true; + } + + +} diff --git a/src/main/java/cc/carm/plugin/userprefix/command/UserPrefixCommand.java b/src/main/java/cc/carm/plugin/userprefix/command/UserPrefixCommand.java new file mode 100644 index 0000000..a4980c4 --- /dev/null +++ b/src/main/java/cc/carm/plugin/userprefix/command/UserPrefixCommand.java @@ -0,0 +1,22 @@ +package cc.carm.plugin.userprefix.command; + +import cc.carm.plugin.userprefix.ui.PrefixSelectGUI; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; + +public class UserPrefixCommand implements CommandExecutor { + + + @Override + public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String s, @NotNull String[] strings) { + if (sender instanceof Player) { + PrefixSelectGUI.open((Player) sender); + } + return true; + } + + +} \ No newline at end of file diff --git a/src/main/java/cc/carm/plugin/userprefix/configuration/PrefixConfig.java b/src/main/java/cc/carm/plugin/userprefix/configuration/PrefixConfig.java new file mode 100644 index 0000000..dc0df53 --- /dev/null +++ b/src/main/java/cc/carm/plugin/userprefix/configuration/PrefixConfig.java @@ -0,0 +1,37 @@ +package cc.carm.plugin.userprefix.configuration; + +import cc.carm.plugin.userprefix.configuration.values.ConfigSound; +import cc.carm.plugin.userprefix.configuration.values.ConfigValue; +import cc.carm.plugin.userprefix.configuration.values.ConfigValueList; + +public class PrefixConfig { + + public static ConfigValue DEBUG = new ConfigValue<>("debug", Boolean.class, false); + + public static class Functions { + + public static ConfigValue NAME_PREFIX = new ConfigValue<>("functions.OnNamePrefix", Boolean.class, true); + public static ConfigValue AUTO_USE = new ConfigValue<>("functions.autoUsePrefix", Boolean.class, true); + + } + + public static class Messages { + + public static ConfigValueList SELECTED = new ConfigValueList<>("messages.selected", String.class); + public static ConfigValueList EXPIRED = new ConfigValueList<>("messages.expired", String.class); + public static ConfigValueList HELP = new ConfigValueList<>("messages.help", String.class); + + + } + + public static class Sounds { + + public static ConfigSound GUI_OPEN = new ConfigSound("Sounds.openGUI"); + public static ConfigSound GUI_CLICK = new ConfigSound("Sounds.guiClick"); + public static ConfigSound PREFIX_CHANGE = new ConfigSound("Sounds.prefixChange"); + public static ConfigSound PREFIX_EXPIRED = new ConfigSound("Sounds.prefixExpired"); + + } + + +} diff --git a/src/main/java/cc/carm/plugin/userprefix/configuration/values/ConfigSound.java b/src/main/java/cc/carm/plugin/userprefix/configuration/values/ConfigSound.java new file mode 100644 index 0000000..6fb3e2c --- /dev/null +++ b/src/main/java/cc/carm/plugin/userprefix/configuration/values/ConfigSound.java @@ -0,0 +1,60 @@ +package cc.carm.plugin.userprefix.configuration.values; + +import cc.carm.plugin.userprefix.Main; +import cc.carm.plugin.userprefix.manager.ConfigManager; +import org.bukkit.Sound; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.entity.Player; + +public class ConfigSound { + FileConfiguration source; + String configSection; + + Sound defaultValue; + + public ConfigSound(String configSection) { + this(configSection, null); + } + + public ConfigSound(String configSection, Sound defaultValue) { + this.source = ConfigManager.getConfig(); + this.configSection = configSection; + this.defaultValue = defaultValue; + } + + public void set(Sound value, float volume) { + this.source.set(this.configSection, value.name() + ":" + volume); + this.save(); + } + + public void set(Sound value, float volume, float pitch) { + this.source.set(this.configSection, value.name() + ":" + volume + ":" + pitch); + this.save(); + } + + public void play(Player player) { + Sound finalSound = defaultValue; + float pitch = 1; + float volume = 1; + String soundString = this.source.getString(this.configSection); + if (soundString != null) { + String[] args = soundString.contains(":") ? soundString.split(":") : new String[]{soundString}; + try { + if (args.length >= 1) finalSound = Sound.valueOf(args[0]); + if (args.length >= 2) volume = Float.parseFloat(args[1]); + if (args.length >= 3) volume = Float.parseFloat(args[2]); + } catch (Exception exception) { + Main.log("声音 " + this.configSection + " 配置错误,不存在 " + soundString + " ,请检查。"); + } + } + if (finalSound != null) { + player.playSound(player.getLocation(), finalSound, volume, pitch); + } + + } + + public void save() { + ConfigManager.saveConfig(); + } + +} diff --git a/src/main/java/cc/carm/plugin/userprefix/configuration/values/ConfigValue.java b/src/main/java/cc/carm/plugin/userprefix/configuration/values/ConfigValue.java new file mode 100644 index 0000000..14886fb --- /dev/null +++ b/src/main/java/cc/carm/plugin/userprefix/configuration/values/ConfigValue.java @@ -0,0 +1,37 @@ +package cc.carm.plugin.userprefix.configuration.values; + +import cc.carm.plugin.userprefix.manager.ConfigManager; +import org.bukkit.configuration.file.FileConfiguration; + +public class ConfigValue { + FileConfiguration source; + String configSection; + Class clazz; + V defaultValue; + + public ConfigValue(String configSection, Class clazz) { + this(configSection, clazz, null); + } + + public ConfigValue(String configSection, Class clazz, V defaultValue) { + this.source = ConfigManager.getConfig(); + this.configSection = configSection; + this.clazz = clazz; + this.defaultValue = defaultValue; + } + + public V get() { + Object val = this.source.get(this.configSection, this.defaultValue); + return this.clazz.isInstance(val) ? this.clazz.cast(val) : this.defaultValue; + } + + public void set(V value) { + this.source.set(this.configSection, value); + this.save(); + } + + public void save() { + ConfigManager.saveConfig(); + } + +} diff --git a/src/main/java/cc/carm/plugin/userprefix/configuration/values/ConfigValueList.java b/src/main/java/cc/carm/plugin/userprefix/configuration/values/ConfigValueList.java new file mode 100644 index 0000000..4ac8d3e --- /dev/null +++ b/src/main/java/cc/carm/plugin/userprefix/configuration/values/ConfigValueList.java @@ -0,0 +1,45 @@ +package cc.carm.plugin.userprefix.configuration.values; + +import cc.carm.plugin.userprefix.manager.ConfigManager; +import org.bukkit.configuration.file.FileConfiguration; + +import java.util.ArrayList; +import java.util.List; + +public class ConfigValueList { + FileConfiguration source; + String configSection; + Class clazz; + + public ConfigValueList(String configSection, Class clazz) { + this.source = ConfigManager.getConfig(); + this.configSection = configSection; + this.clazz = clazz; + } + + public ArrayList get() { + List list = this.source.getList(this.configSection); + if (list == null) { + return new ArrayList(0); + } else { + ArrayList result = new ArrayList(); + + for (Object object : list) { + if (this.clazz.isInstance(object)) { + result.add(this.clazz.cast(object)); + } + } + + return result; + } + } + + public void set(ArrayList value) { + this.source.set(this.configSection, value); + this.save(); + } + + public void save() { + ConfigManager.saveConfig(); + } +} diff --git a/src/main/java/cc/carm/plugin/userprefix/hooker/UserPrefixExpansion.java b/src/main/java/cc/carm/plugin/userprefix/hooker/UserPrefixExpansion.java new file mode 100644 index 0000000..c601787 --- /dev/null +++ b/src/main/java/cc/carm/plugin/userprefix/hooker/UserPrefixExpansion.java @@ -0,0 +1,89 @@ +package cc.carm.plugin.userprefix.hooker; + +import me.clip.placeholderapi.expansion.PlaceholderExpansion; +import cc.carm.plugin.userprefix.manager.PrefixManager; +import cc.carm.plugin.userprefix.manager.UserManager; +import cc.carm.plugin.userprefix.model.ConfiguredPrefix; +import org.bukkit.entity.Player; +import org.bukkit.plugin.java.JavaPlugin; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; + +public class UserPrefixExpansion extends PlaceholderExpansion { + + JavaPlugin plugin; + + public UserPrefixExpansion(JavaPlugin plugin) { + this.plugin = plugin; + } + + @Override + public @NotNull List getPlaceholders() { + List placeholders = new ArrayList<>(); + placeholders.add("%UserPrefix_prefix%"); + placeholders.add("%UserPrefix_weight%"); + placeholders.add("%UserPrefix_identifier%"); + placeholders.add("%UserPrefix_name%"); + placeholders.add("%UserPrefix_has_%"); + return placeholders; + } + + @Override + public boolean canRegister() { + return true; + } + + @Override + public String getAuthor() { + return plugin.getDescription().getAuthors().toString(); + } + + @Override + public String getIdentifier() { + return "UserPrefix"; + } + + @Override + public String getVersion() { + return plugin.getDescription().getVersion(); + } + + @Override + public String onPlaceholderRequest(Player player, @NotNull String identifier) { + if (player == null) return "加载中..."; + String[] args = identifier.split("_"); + + + if (args.length < 1) { + return "参数不足"; + } + + switch (args[0].toLowerCase()) { + case "identifier": { + return UserManager.getPrefix(player).getIdentifier(); + } + case "prefix": { + return UserManager.getPrefix(player).getContent(); + } + case "name": { + return UserManager.getPrefix(player).getName(); + } + case "weight": { + return Integer.toString(UserManager.getPrefix(player).getWeight()); + } + case "has": { + if (args.length < 2) return "参数不足"; + ConfiguredPrefix prefix = PrefixManager.getPrefix(args[1]); + if (prefix == null) return "该前缀不存在"; + return Boolean.toString(UserManager.isPrefixUsable(player, prefix)); + } + case "version": { + return getVersion().replace("-SNAPSHOT", ""); + } + } + return null; + } + +} diff --git a/src/main/java/cc/carm/plugin/userprefix/listener/UserListener.java b/src/main/java/cc/carm/plugin/userprefix/listener/UserListener.java new file mode 100644 index 0000000..356b214 --- /dev/null +++ b/src/main/java/cc/carm/plugin/userprefix/listener/UserListener.java @@ -0,0 +1,39 @@ +package cc.carm.plugin.userprefix.listener; + +import cc.carm.plugin.userprefix.configuration.PrefixConfig; +import cc.carm.plugin.userprefix.manager.UserManager; +import cc.carm.plugin.userprefix.ui.PrefixSelectGUI; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.event.player.PlayerQuitEvent; + +public class UserListener implements Listener { + + + @EventHandler + public void onJoin(PlayerJoinEvent event) { + Player player = event.getPlayer(); + + UserManager.checkPrefix(player, false); + + + if (PrefixConfig.Functions.NAME_PREFIX.get()) { + UserManager.createNameTag(event.getPlayer()); + UserManager.updatePrefixView(event.getPlayer(), true); + } + + + } + + + @EventHandler + public void onLeave(PlayerQuitEvent event) { + PrefixSelectGUI.removeOpening(event.getPlayer()); + UserManager.unloadNameTag(event.getPlayer().getUniqueId()); + UserManager.checkingPlayers.remove(event.getPlayer().getUniqueId()); + } + + +} diff --git a/src/main/java/cc/carm/plugin/userprefix/listener/processor/UserNodeUpdateProcessor.java b/src/main/java/cc/carm/plugin/userprefix/listener/processor/UserNodeUpdateProcessor.java new file mode 100644 index 0000000..62e27c0 --- /dev/null +++ b/src/main/java/cc/carm/plugin/userprefix/listener/processor/UserNodeUpdateProcessor.java @@ -0,0 +1,29 @@ +package cc.carm.plugin.userprefix.listener.processor; + +import net.luckperms.api.event.user.UserDataRecalculateEvent; +import net.luckperms.api.model.user.User; +import cc.carm.plugin.userprefix.manager.UserManager; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; + +public class UserNodeUpdateProcessor { + +// public static void process(NodeRemoveEvent event) { +// if (event.getTarget() instanceof User) { +// if (!(event.getNode() instanceof PermissionNode)) return; +// User user = (User) event.getTarget(); +// Player player = Bukkit.getPlayer(user.getUniqueId()); +// if (player == null) return; +// UserManager.checkPrefix(player, true); +// } +// } + + public static void process(UserDataRecalculateEvent event) { + User user = event.getUser(); + Player player = Bukkit.getPlayer(user.getUniqueId()); + if (player == null) return; + UserManager.checkPrefix(player, true); + + } + +} diff --git a/src/main/java/cc/carm/plugin/userprefix/manager/ConfigManager.java b/src/main/java/cc/carm/plugin/userprefix/manager/ConfigManager.java new file mode 100644 index 0000000..6f7affe --- /dev/null +++ b/src/main/java/cc/carm/plugin/userprefix/manager/ConfigManager.java @@ -0,0 +1,31 @@ +package cc.carm.plugin.userprefix.manager; + +import cc.carm.plugin.userprefix.Main; +import org.bukkit.configuration.file.FileConfiguration; + +public class ConfigManager { + + private static FileConfiguration config; + + public static void initConfig() { + Main.getInstance().saveDefaultConfig(); + Main.getInstance().reloadConfig(); + + config = Main.getInstance().getConfig(); + } + + public static FileConfiguration getConfig() { + return config; + } + + public static void reloadConfig() { + Main.getInstance().reloadConfig(); + config = Main.getInstance().getConfig(); + } + + public static void saveConfig() { + Main.getInstance().saveConfig(); + } + + +} diff --git a/src/main/java/cc/carm/plugin/userprefix/manager/PrefixManager.java b/src/main/java/cc/carm/plugin/userprefix/manager/PrefixManager.java new file mode 100644 index 0000000..f61198d --- /dev/null +++ b/src/main/java/cc/carm/plugin/userprefix/manager/PrefixManager.java @@ -0,0 +1,129 @@ +package cc.carm.plugin.userprefix.manager; + +import cc.carm.plugin.userprefix.Main; +import cc.carm.plugin.userprefix.model.ConfiguredPrefix; +import cc.carm.plugin.userprefix.util.ItemStackFactory; +import org.bukkit.Material; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.inventory.ItemFlag; +import org.bukkit.inventory.ItemStack; + +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.stream.Collectors; + +public class PrefixManager { + + public static ConfiguredPrefix defaultPrefix; + public static HashMap prefixes = new HashMap<>(); + + + public static void init() { + loadConfiguredPrefixes(); + Main.log("共加载了 " + prefixes.size() + " 个前缀。"); + } + + public static void loadConfiguredPrefixes() { + loadDefaultPrefix(); + + ConfigurationSection prefixesSection = ConfigManager.getConfig().getConfigurationSection("prefixes"); + if (prefixesSection == null || prefixesSection.getKeys(false).isEmpty()) { + Main.log("配置文件中暂无任何前缀配置,请检查。"); + return; + } + HashMap dataPrefixes = new HashMap<>(); + for (String prefixIdentifier : prefixesSection.getKeys(false)) { + ConfigurationSection configuredPrefixSection = prefixesSection.getConfigurationSection(prefixIdentifier); + if (configuredPrefixSection == null) continue; + + String name = configuredPrefixSection.getString("name", "前缀名配置错误"); + String content = configuredPrefixSection.getString("content", "&r"); + String permission = configuredPrefixSection.getString("permission"); + int weight = configuredPrefixSection.getInt("weight", 1); + + ItemStack itemHasPermission = configuredPrefixSection.getItemStack("itemHasPermission", + new ItemStackFactory(Material.STONE).setDisplayName(name).addLore(" ").addLore("§a➥ 点击切换到该前缀").toItemStack() + ); + ItemStack itemNoPermission = configuredPrefixSection.getItemStack("itemNoPermission", itemHasPermission); + ItemStack itemUsing = configuredPrefixSection.getItemStack("itemUsing", itemHasPermission); + + + Main.log("完成前缀加载 " + prefixIdentifier + " : " + name); + + dataPrefixes.put(prefixIdentifier, new ConfiguredPrefix(prefixIdentifier, name, content, weight, permission, itemHasPermission, itemNoPermission, itemUsing)); + } + + prefixes = dataPrefixes; + } + + public static void loadDefaultPrefix() { + ConfigurationSection defaultPrefixSection = ConfigManager.getConfig().getConfigurationSection("defaultPrefix"); + if (defaultPrefixSection != null) { + String name = defaultPrefixSection.getString("name", "默认前缀"); + String content = defaultPrefixSection.getString("content", "&r"); + ItemStack itemNotUsing = defaultPrefixSection.getItemStack( + "itemNotUsing", + new ItemStackFactory(Material.NAME_TAG) + .setDisplayName("&f默认前缀") + .addLore(" ") + .addLore("§a➥ 点击切换到该前缀") + .toItemStack() + ); + ItemStack itemUsing = defaultPrefixSection.getItemStack("itemUsing", + new ItemStackFactory(Material.NAME_TAG) + .setDisplayName("&f默认前缀") + .addLore(" ") + .addLore("§a✔ 您正在使用该前缀") + .addEnchant(Enchantment.DURABILITY, 1, false) + .addFlag(ItemFlag.HIDE_ENCHANTS) + .toItemStack() + ); + defaultPrefix = new ConfiguredPrefix("default", name, content, 0, null, itemNotUsing, null, itemUsing); + } else { + defaultPrefix = new ConfiguredPrefix("default", "默认前缀", "&r", 0, null, + new ItemStackFactory(Material.NAME_TAG) + .setDisplayName("&f默认前缀") + .addLore(" ") + .addLore("§a➥ 点击切换到该前缀") + .toItemStack(), + null, + new ItemStackFactory(Material.NAME_TAG) + .setDisplayName("&f默认前缀") + .addLore(" ") + .addLore("§a✔ 您正在使用该前缀") + .addEnchant(Enchantment.DURABILITY, 1, false) + .addFlag(ItemFlag.HIDE_ENCHANTS) + .toItemStack() + ); + } + + Main.log("完成默认前缀加载 " + defaultPrefix.getName()); + } + + public static List getVisiblePrefix() { + return PrefixManager.getPrefixes().values().stream() + .filter(ConfiguredPrefix::isVisibleNoPermission) + .sorted(Comparator.comparingInt(ConfiguredPrefix::getWeight)) + .collect(Collectors.toList()); + } + + public static ConfiguredPrefix getDefaultPrefix() { + return defaultPrefix; + } + + public static HashMap getPrefixes() { + return prefixes; + } + + public static ConfiguredPrefix getPrefix(String identifier) { + if (identifier == null || identifier.equalsIgnoreCase("default")) { + return getDefaultPrefix(); + } else { + return getPrefixes().get(identifier); + } + } + + +} diff --git a/src/main/java/cc/carm/plugin/userprefix/manager/ServiceManager.java b/src/main/java/cc/carm/plugin/userprefix/manager/ServiceManager.java new file mode 100644 index 0000000..f53e7a0 --- /dev/null +++ b/src/main/java/cc/carm/plugin/userprefix/manager/ServiceManager.java @@ -0,0 +1,45 @@ +package cc.carm.plugin.userprefix.manager; + +import net.luckperms.api.LuckPerms; +import net.luckperms.api.LuckPermsProvider; +import net.luckperms.api.model.user.User; +import net.luckperms.api.platform.PlayerAdapter; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.plugin.RegisteredServiceProvider; + +/** + * 服务管理器,旨在于LuckPerms互联,调用其原始接口。 + */ +public class ServiceManager { + + public static User getUser(Player player) { + return getAPI().getUser(player); + } + + public static LuckPerms getService() { + RegisteredServiceProvider provider = Bukkit.getServicesManager().getRegistration(LuckPerms.class); + if (provider != null) { + return provider.getProvider(); + } else { + return LuckPermsProvider.get(); + } + } + + public static PlayerAdapter getAPI() { + return getService().getPlayerAdapter(Player.class); + } + + + /** + * 通过LuckPermsAPI判断玩家是否有对应的权限 + * + * @param user 用户 + * @param permission 权限 + * @return true / false + */ + public static boolean hasPermission(User user, String permission) { + return user.getCachedData().getPermissionData().checkPermission(permission).asBoolean(); + } + +} diff --git a/src/main/java/cc/carm/plugin/userprefix/manager/UserManager.java b/src/main/java/cc/carm/plugin/userprefix/manager/UserManager.java new file mode 100644 index 0000000..bd64b45 --- /dev/null +++ b/src/main/java/cc/carm/plugin/userprefix/manager/UserManager.java @@ -0,0 +1,222 @@ +package cc.carm.plugin.userprefix.manager; + +import cc.carm.plugin.userprefix.Main; +import cc.carm.plugin.userprefix.configuration.PrefixConfig; +import cc.carm.plugin.userprefix.model.ConfiguredPrefix; +import cc.carm.plugin.userprefix.nametag.UserNameTag; +import cc.carm.plugin.userprefix.util.MessageUtil; +import net.luckperms.api.model.user.User; +import net.luckperms.api.node.NodeType; +import net.luckperms.api.node.types.MetaNode; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; + +import java.util.*; +import java.util.stream.Collectors; + +public class UserManager { + + public static HashMap nameTags = new HashMap<>(); + + public static HashSet checkingPlayers = new HashSet<>(); + + public static UserNameTag getNameTag(Player player) { + return nameTags.get(player.getUniqueId()); + } + + public static UserNameTag createNameTag(Player player) { + UserNameTag nameTag = new UserNameTag(player); + nameTags.put(player.getUniqueId(), nameTag); + return nameTag; + } + + /** + * 更新前缀显示效果 + * + * @param player 玩家 + * @param loadOthers 是否为玩家更新其他人的前缀(一般用于加入游戏) + */ + public static void updatePrefixView(Player player, boolean loadOthers) { + ConfiguredPrefix playerPrefix = UserManager.getPrefix(player); + + UserNameTag tag = getNameTag(player); + + tag.setPrefix(playerPrefix.getContent()); + tag.setOrder(playerPrefix.getWeight()); + + Main.debug("为玩家 " + player.getName() + " 设置了 " + player.getName() + "的前缀为 #" + playerPrefix.getWeight() + " " + playerPrefix.getName()); + + for (Player onlinePlayer : Bukkit.getOnlinePlayers()) { + if (onlinePlayer.equals(player)) continue; + UserNameTag onlinePlayerTag = getNameTag(onlinePlayer); + if (onlinePlayerTag != null) { + onlinePlayerTag.setPrefix(player, playerPrefix.getContent()); + onlinePlayerTag.setOrder(player, playerPrefix.getWeight()); + Main.debug("为玩家 " + onlinePlayer.getName() + " 设置了 " + player.getName() + "的前缀为 #" + playerPrefix.getWeight() + " " + playerPrefix.getName()); + + } + + if (loadOthers) { + ConfiguredPrefix onlinePlayerPrefix = UserManager.getPrefix(onlinePlayer); + if (onlinePlayerPrefix != null) { + tag.setPrefix(onlinePlayer, onlinePlayerPrefix.getContent()); + tag.setOrder(onlinePlayer, onlinePlayerPrefix.getWeight()); + Main.debug("为玩家 " + player.getName() + " 设置了 " + player.getName() + "的前缀为 #" + onlinePlayerPrefix.getWeight() + " " + onlinePlayerPrefix.getName()); + } + } + } + } + + /** + * 检查玩家的前缀的使用权 + * + * @param player 玩家 + * @param updateView 是否更新头顶与TabList中的前缀 + */ + public static void checkPrefix(Player player, boolean updateView) { + if (checkingPlayers.contains(player.getUniqueId())) return; + checkingPlayers.add(player.getUniqueId()); + String currentPrefixIdentifier = UserManager.getPrefixData(player); + ConfiguredPrefix currentPrefix = PrefixManager.getPrefix(currentPrefixIdentifier); + if (!UserManager.isPrefixUsable(player, currentPrefixIdentifier)) { + ConfiguredPrefix newPrefix = UserManager.getHighestPrefix(player); + // 更新前缀 + UserManager.setPrefix(player, newPrefix, updateView); + // 发送消息 + MessageUtil.sendWithPlaceholders(player, PrefixConfig.Messages.EXPIRED.get(), + new String[]{"%(newName)", "%(oldName)"}, + new Object[]{newPrefix.getName(), currentPrefix != null ? currentPrefix.getName() : currentPrefixIdentifier} + ); + // 播放声音 + PrefixConfig.Sounds.PREFIX_EXPIRED.play(player); + } + checkingPlayers.remove(player.getUniqueId()); + } + + public static void unloadNameTag(UUID uuid) { + nameTags.remove(uuid); + } + + /** + * 得到玩家的前缀。 + * 该方法会自动判断玩家当前的前缀是否可用,并返回最终可用的前缀。 + * + * @param player 玩家 + * @return 前缀配置 + */ + public static ConfiguredPrefix getPrefix(Player player) { + String identifier = getPrefixData(player); + if (identifier == null || !isPrefixUsable(player, identifier)) { + return getHighestPrefix(player); + } else { + return PrefixManager.getPrefix(identifier); + } + } + + /** + * 设定玩家前缀 + * + * @param player 玩家 + * @param prefix 前缀配置 + * @param updateView 是否更新头顶上、TabList的前缀 + */ + public static void setPrefix(Player player, ConfiguredPrefix prefix, boolean updateView) { + setPrefixData(player, prefix.getIdentifier()); + if (updateView) updatePrefixView(player, false); + } + + /** + * 得到玩家所有可用的前缀 + * + * @param player 玩家 + * @return 可用前缀列表 + */ + public static List getUsablePrefixes(Player player) { + return PrefixManager.getPrefixes().values().stream() + .filter(configuredPrefix -> isPrefixUsable(player, configuredPrefix)) + .sorted(Comparator.comparingInt(ConfiguredPrefix::getWeight)) + .collect(Collectors.toList()); + } + + + /** + * 得到玩家可使用的最高权重的权限 + * 注意:若配置文件中关闭了 “autoUsePrefix” ,则会返回默认前缀。 + * + * @param player 玩家 + * @return 权限内容 + */ + public static ConfiguredPrefix getHighestPrefix(Player player) { + if (PrefixConfig.Functions.AUTO_USE.get()) { + // 关闭了自动选择,就直接给默认的前缀,让玩家自己去设置吧 + return PrefixManager.getDefaultPrefix(); + } + List prefixes = getUsablePrefixes(player); + return prefixes.stream().max(Comparator.comparingInt(ConfiguredPrefix::getWeight)).orElseGet(PrefixManager::getDefaultPrefix); + } + + /** + * 判断一个前缀对某玩家是否可用 + * + * @param player 玩家 + * @param prefixIdentifier 前缀标识 + * @return 若前缀标识不存在,则返回false;若前缀为默认前缀,或该前缀无权限,或玩家有该前缀的权限,则返回true。 + */ + public static boolean isPrefixUsable(Player player, String prefixIdentifier) { + if (prefixIdentifier == null || prefixIdentifier.equalsIgnoreCase("default")) return true; + ConfiguredPrefix prefix = PrefixManager.getPrefix(prefixIdentifier); + if (prefix == null) return false; + return isPrefixUsable(player, prefix); + } + + /** + * 判断一个前缀对某玩家是否可用 + * + * @param player 玩家 + * @param configuredPrefix 前缀配置 + * @return 若前缀标识不存在,则返回false;若前缀为默认前缀,或该前缀无权限,或玩家有该前缀的权限,则返回true。 + */ + public static boolean isPrefixUsable(Player player, ConfiguredPrefix configuredPrefix) { + return configuredPrefix.getPermission() == null || ServiceManager.hasPermission(ServiceManager.getUser(player), configuredPrefix.getPermission()); + } + + /** + * 得到用户当前正在使用的前缀Identifier。 + * 该方法通过LuckPerms的MetaData实现,因此可以通过指令去操作。 + * + * @param player 玩家 + * @return 正在使用的前缀Identifier(若不存在则返回null) + */ + public static String getPrefixData(Player player) { + return ServiceManager.getAPI().getMetaData(player).getMetaValue("userprefix", String::valueOf).orElse(null); + } + + /** + * 设定用户所使用的的prefix。 + * 该方法通过LuckPerms的MetaData实现,因此可以通过指令去操作。 + * + * @param player 玩家 + * @param prefixIdentifier 前缀的标识 + */ + public static void setPrefixData(Player player, String prefixIdentifier) { + User user = ServiceManager.getUser(player); + clearPrefixData(player); + // LuckPerms竟然会把所有的metaKey全部转换为小写... 那我这里就直接写成小写吧~ + MetaNode node = MetaNode.builder("userprefix", prefixIdentifier).build(); + user.data().add(node); + ServiceManager.getService().getUserManager().saveUser(user); + } + + /** + * 清除玩家所选择的前缀数据 + * + * @param player 玩家 + */ + public static void clearPrefixData(Player player) { + User user = ServiceManager.getUser(player); + // LuckPerms竟然会把所有的metaKey全部转换为小写... 那我这里就直接写成小写吧~ + user.data().clear(NodeType.META.predicate(mn -> mn.getMetaKey().equals("userprefix"))); + } + + +} diff --git a/src/main/java/cc/carm/plugin/userprefix/model/ConfiguredPrefix.java b/src/main/java/cc/carm/plugin/userprefix/model/ConfiguredPrefix.java new file mode 100644 index 0000000..8e78fe1 --- /dev/null +++ b/src/main/java/cc/carm/plugin/userprefix/model/ConfiguredPrefix.java @@ -0,0 +1,97 @@ +package cc.carm.plugin.userprefix.model; + +import cc.carm.plugin.userprefix.util.ColorParser; +import org.bukkit.inventory.ItemStack; + +public class ConfiguredPrefix { + + String identifier; + + String name; + String content; + + int weight; + + String permission; + + ItemStack itemHasPermission; + ItemStack itemNoPermission; + ItemStack itemWhenUsing; + + public ConfiguredPrefix(String identifier, String name, String content, int weight, String permission, ItemStack itemHasPermission, ItemStack itemNoPermission, ItemStack itemWhenUsing) { + this.identifier = identifier; + this.name = name; + this.content = content; + this.weight = weight; + this.permission = permission; + this.itemHasPermission = itemHasPermission; + this.itemNoPermission = itemNoPermission; + this.itemWhenUsing = itemWhenUsing; + } + + public String getIdentifier() { + return identifier; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getContent() { + return ColorParser.parseColor(content); + } + + public void setContent(String content) { + this.content = content; + } + + public int getWeight() { + return weight; + } + + public void setWeight(int weight) { + this.weight = weight; + } + + public String getPermission() { + return permission; + } + + public void setPermission(String permission) { + this.permission = permission; + } + + public ItemStack getItemHasPermission() { + return itemHasPermission; + } + + public void setItemHasPermission(ItemStack itemHasPermission) { + this.itemHasPermission = itemHasPermission; + } + + public ItemStack getItemNoPermission() { + return itemNoPermission; + } + + public void setItemNoPermission(ItemStack itemNoPermission) { + this.itemNoPermission = itemNoPermission; + } + + public ItemStack getItemWhenUsing() { + return itemWhenUsing; + } + + public void setItemWhenUsing(ItemStack itemWhenUsing) { + this.itemWhenUsing = itemWhenUsing; + } + + public boolean isVisibleNoPermission() { + return this.itemNoPermission != null; + } + + +} diff --git a/src/main/java/cc/carm/plugin/userprefix/nametag/UserNameTag.java b/src/main/java/cc/carm/plugin/userprefix/nametag/UserNameTag.java new file mode 100644 index 0000000..b22686f --- /dev/null +++ b/src/main/java/cc/carm/plugin/userprefix/nametag/UserNameTag.java @@ -0,0 +1,153 @@ +package cc.carm.plugin.userprefix.nametag; + +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.scoreboard.Scoreboard; +import org.bukkit.scoreboard.Team; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.UUID; + +public class UserNameTag { + + private final Player viewer; + private Team team; + private final Scoreboard sb; + private int order = 99999; + private final Map targetOrders = new HashMap<>(); + private final Map previousTeamNames = new HashMap<>(); + + public UserNameTag(Player viewer) { + this.viewer = viewer; + Scoreboard sb = viewer.getScoreboard(); + if (sb == Bukkit.getServer().getScoreboardManager().getMainScoreboard()) { + sb = Bukkit.getScoreboardManager().getNewScoreboard(); + } + team = sb.registerNewTeam(order + viewer.getUniqueId().toString().substring(0, 10)); + team.setCanSeeFriendlyInvisibles(true); + team.addEntry(viewer.getName()); + this.sb = sb; + viewer.setScoreboard(sb); + } + + /** + * 设置自己的前缀 + * + * @param prefix + */ + public void setPrefix(String prefix) { + team.setPrefix(prefix); + update(viewer); + } + + /** + * 设置某个玩家的前缀 + * + * @param target + * @param prefix + */ + public void setPrefix(Player target, String prefix) { + if (target == viewer) { + setPrefix(prefix); + } else { + Team targetTeam = checkTeam(target); + targetTeam.setPrefix(prefix); + } + update(viewer); + if (viewer != target) + update(target); + } + + /** + * 设置名字在TabList中的顺序 + * + * @param order + */ + public void setOrder(int order) { + if (order < 0 || order > 99999) + throw new IllegalArgumentException("order must be in 0~99999"); + this.order = order; + targetOrders.put(viewer.getUniqueId(), order); + update(viewer); + } + + /** + * 设置名字在TabList中的顺序 + * + * @param order 顺序 + */ + public void setOrder(Player target, int order) { + if (order < 0 || order > 99999) + throw new IllegalArgumentException("order must be in 0~99999"); + Team targetTeam = checkTeam(target); + String teamName = order + UUID.randomUUID().toString().substring(0, 10); + targetTeam.setDisplayName(teamName); + targetOrders.put(target.getUniqueId(), order); + update(viewer); + if (viewer != target) + update(target); + } + + public void update(Player target) { + if (target == viewer) { + Set entries = team.getEntries(); + String name = order + viewer.getUniqueId().toString().substring(0, 10); + if (sb.getTeam(name) == null) { + Team newTeam = sb.registerNewTeam(name); + newTeam.setDisplayName(name); + entries.forEach(newTeam::addEntry); + team.getPrefix(); + if (!team.getPrefix().isEmpty()) + newTeam.setPrefix(team.getPrefix()); + team.getSuffix(); + if (!team.getSuffix().isEmpty()) + newTeam.setSuffix(team.getSuffix()); + newTeam.setNameTagVisibility(team.getNameTagVisibility()); + newTeam.setCanSeeFriendlyInvisibles(true); + team.unregister(); + team = newTeam; + } + } else { + int order = targetOrders.getOrDefault(target.getUniqueId(), 99999); + String previousTeamName = previousTeamNames.get(target.getUniqueId()); + if (previousTeamName == null) { + return; + } + String name = order + target.getUniqueId().toString().substring(0, 10); + Team targetTeam = this.sb.getTeam(previousTeamName); + if (targetTeam == null) { + return; + } + if (sb.getTeam(name) == null) { + Team newTeam = sb.registerNewTeam(name); + newTeam.setDisplayName(name); + newTeam.addEntry(target.getName()); + newTeam.setNameTagVisibility(targetTeam.getNameTagVisibility()); + newTeam.setCanSeeFriendlyInvisibles(true); + targetTeam.getPrefix(); + if (!targetTeam.getPrefix().isEmpty()) newTeam.setPrefix(targetTeam.getPrefix()); + targetTeam.getSuffix(); + if (!targetTeam.getSuffix().isEmpty()) newTeam.setSuffix(targetTeam.getSuffix()); + targetTeam.unregister(); + previousTeamNames.put(target.getUniqueId(), name); + } + } + + } + + private Team checkTeam(Player target) { + int order = targetOrders.getOrDefault(target.getUniqueId(), 99999); + String name = order + target.getUniqueId().toString().substring(0, 10); + Team targetTeam = this.sb.getTeam(name); + if (targetTeam == null) { + targetTeam = this.sb.registerNewTeam(name); + targetTeam.addEntry(target.getName()); + previousTeamNames.put(target.getUniqueId(), name); + } + return targetTeam; + } + + +} diff --git a/src/main/java/cc/carm/plugin/userprefix/ui/PrefixSelectGUI.java b/src/main/java/cc/carm/plugin/userprefix/ui/PrefixSelectGUI.java new file mode 100644 index 0000000..a01ffa6 --- /dev/null +++ b/src/main/java/cc/carm/plugin/userprefix/ui/PrefixSelectGUI.java @@ -0,0 +1,94 @@ +package cc.carm.plugin.userprefix.ui; + +import cc.carm.plugin.userprefix.configuration.PrefixConfig; +import cc.carm.plugin.userprefix.util.MessageUtil; +import cc.carm.plugin.userprefix.util.gui.GUIType; +import cc.carm.plugin.userprefix.manager.PrefixManager; +import cc.carm.plugin.userprefix.manager.UserManager; +import cc.carm.plugin.userprefix.model.ConfiguredPrefix; +import cc.carm.plugin.userprefix.util.gui.AutoPagedGUI; +import cc.carm.plugin.userprefix.util.gui.GUIItem; +import org.bukkit.entity.Player; +import org.bukkit.event.inventory.ClickType; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; + +public class PrefixSelectGUI extends AutoPagedGUI { + + public static HashSet openingUsers = new HashSet<>(); + + Player player; + + public PrefixSelectGUI(Player player) { + super(GUIType.SIXBYNINE, "&f&l我的前缀 &8| 列表", 10, 43); + this.player = player; + + setPreviousPageSlot(18); + setNextPageSlot(26); + + loadItems(); + } + + public Player getPlayer() { + return player; + } + + public void loadItems() { + List prefixList = new ArrayList<>(); + prefixList.add(PrefixManager.getDefaultPrefix()); + prefixList.addAll(PrefixManager.getVisiblePrefix()); + + ConfiguredPrefix usingPrefix = UserManager.getPrefix(getPlayer()); + + for (ConfiguredPrefix prefix : prefixList) { + if (prefix.getIdentifier().equals(usingPrefix.getIdentifier())) { + addItem(new GUIItem(prefix.getItemWhenUsing() != null ? prefix.getItemWhenUsing() : prefix.getItemHasPermission())); + } else if (UserManager.isPrefixUsable(player, prefix)) { + addItem(new GUIItem(prefix.getItemHasPermission()) { + @Override + public void onClick(ClickType type) { + if (UserManager.isPrefixUsable(player, prefix)) { //再次检查,防止打开GUI后、选择前的时间段内权限消失 + player.closeInventory(); + UserManager.setPrefix(player, prefix, true); + + PrefixConfig.Sounds.PREFIX_CHANGE.play(player); + MessageUtil.sendWithPlaceholders(player, PrefixConfig.Messages.SELECTED.get(), + new String[]{"%(name)"}, + new Object[]{prefix.getName()}); + + } + } + }); + } else { + addItem(new GUIItem(prefix.getItemNoPermission())); + } + } + + + } + + @Override + public void onClose() { + openingUsers.remove(player); + } + + public static void removeOpening(Player player) { + openingUsers.remove(player); + } + + public static void closeAll() { + for (Player player : new HashSet<>(openingUsers)) { + player.closeInventory(); + } + } + + public static void open(Player player) { + PrefixConfig.Sounds.GUI_OPEN.play(player); + new PrefixSelectGUI(player).openGUI(player); + openingUsers.add(player); + } + + +} diff --git a/src/main/java/cc/carm/plugin/userprefix/util/ColorParser.java b/src/main/java/cc/carm/plugin/userprefix/util/ColorParser.java new file mode 100644 index 0000000..b891d30 --- /dev/null +++ b/src/main/java/cc/carm/plugin/userprefix/util/ColorParser.java @@ -0,0 +1,10 @@ + +package cc.carm.plugin.userprefix.util; + +public class ColorParser { + + public static String parseColor(final String text) { + return text.replaceAll("&", "§").replace("§§", "&"); + } + +} diff --git a/src/main/java/cc/carm/plugin/userprefix/util/ItemStackFactory.java b/src/main/java/cc/carm/plugin/userprefix/util/ItemStackFactory.java new file mode 100644 index 0000000..1a3b6e7 --- /dev/null +++ b/src/main/java/cc/carm/plugin/userprefix/util/ItemStackFactory.java @@ -0,0 +1,141 @@ +package cc.carm.plugin.userprefix.util; + + +import org.bukkit.Material; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.inventory.ItemFlag; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.inventory.meta.SkullMeta; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +public class ItemStackFactory { + ItemStack item; + + private ItemStackFactory() { + } + + public ItemStackFactory(ItemStack is) { + this.item = is.clone(); + } + + public ItemStackFactory(Material type) { + this(type, 1); + } + + public ItemStackFactory(Material type, int amount) { + this(type, amount, (short) 0); + } + + public ItemStackFactory(Material type, int amount, short data) { + this.item = new ItemStack(type, amount, data); + } + + public ItemStackFactory(Material type, int amount, int data) { + this(type, amount, (short) data); + } + + public ItemStack toItemStack() { + return this.item; + } + + public ItemStackFactory setType(Material type) { + this.item.setType(type); + return this; + } + + public ItemStackFactory setDurability(int i) { + this.item.setDurability((short) i); + return this; + } + + public ItemStackFactory setAmount(int a) { + this.item.setAmount(a); + return this; + } + + public ItemStackFactory setDisplayName(String name) { + ItemMeta im = this.item.getItemMeta(); + im.setDisplayName(name.replace("&", "§").replace("§§", "&&")); + this.item.setItemMeta(im); + return this; + } + + public ItemStackFactory setLore(List lores) { + ItemMeta im = this.item.getItemMeta(); + List lores_ = new ArrayList(); + Iterator var4 = lores.iterator(); + + while (var4.hasNext()) { + String lore = (String) var4.next(); + lores_.add(lore.replace("&", "§").replace("§§", "&&")); + } + + im.setLore(lores_); + this.item.setItemMeta(im); + return this; + } + + public ItemStackFactory addLore(String name) { + ItemMeta im = this.item.getItemMeta(); + Object lores; + if (im.hasLore()) { + lores = im.getLore(); + } else { + lores = new ArrayList(); + } + + ((List) lores).add(name.replace("&", "§").replace("§§", "&&")); + im.setLore((List) lores); + this.item.setItemMeta(im); + return this; + } + + public ItemStackFactory addEnchant(Enchantment ench, int level, boolean ignoreLevelRestriction) { + ItemMeta im = this.item.getItemMeta(); + im.addEnchant(ench, level, ignoreLevelRestriction); + this.item.setItemMeta(im); + return this; + } + + public ItemStackFactory removeEnchant(Enchantment ench) { + ItemMeta im = this.item.getItemMeta(); + im.removeEnchant(ench); + this.item.setItemMeta(im); + return this; + } + + public ItemStackFactory addFlag(ItemFlag flag) { + ItemMeta im = this.item.getItemMeta(); + im.addItemFlags(new ItemFlag[]{flag}); + this.item.setItemMeta(im); + return this; + } + + public ItemStackFactory removeFlag(ItemFlag flag) { + ItemMeta im = this.item.getItemMeta(); + im.removeItemFlags(new ItemFlag[]{flag}); + this.item.setItemMeta(im); + return this; + } + + public ItemStackFactory setUnbreakable(boolean unbreakable) { + ItemMeta im = this.item.getItemMeta(); + im.setUnbreakable(unbreakable); + this.item.setItemMeta(im); + return this; + } + + public ItemStackFactory setSkullOwner(String name) { + if (this.item.getType() == Material.PLAYER_HEAD || this.item.getType() == Material.PLAYER_WALL_HEAD) { + SkullMeta im = (SkullMeta) this.item.getItemMeta(); + im.setOwner(name); + this.item.setItemMeta(im); + } + + return this; + } +} diff --git a/src/main/java/cc/carm/plugin/userprefix/util/MessageUtil.java b/src/main/java/cc/carm/plugin/userprefix/util/MessageUtil.java new file mode 100644 index 0000000..45f0974 --- /dev/null +++ b/src/main/java/cc/carm/plugin/userprefix/util/MessageUtil.java @@ -0,0 +1,63 @@ +package cc.carm.plugin.userprefix.util; + +import me.clip.placeholderapi.PlaceholderAPI; +import org.bukkit.entity.Player; + +import java.util.*; + +public class MessageUtil { + + public static void send(Player player, List messages) { + for (String s : messages) { + player.sendMessage(ColorParser.parseColor(s)); + } + } + + public static void send(Player player, String... messages) { + send(player, Arrays.asList(messages)); + } + + public static void sendWithPlaceholders(Player player, String... messages) { + sendWithPlaceholders(player, Arrays.asList(messages)); + } + + public static void sendWithPlaceholders(Player player, List messages) { + send(player, PlaceholderAPI.setPlaceholders(player, messages)); + } + + public static void sendWithPlaceholders(Player player, List messages, String param, Object value) { + sendWithPlaceholders(player, messages, new String[]{param}, new Object[]{value}); + } + + public static void sendWithPlaceholders(Player player, List messages, String[] params, Object[] values) { + sendWithPlaceholders(player, setCustomParams(messages, params, values)); + } + + public static List setCustomParams(List messages, String param, Object value) { + return setCustomParams(messages, new String[]{param}, new Object[]{value}); + } + + public static List setCustomParams(List messages, String[] params, Object[] values) { + if (params.length != values.length) return messages; + HashMap paramsMap = new HashMap<>(); + for (int i = 0; i < params.length; i++) { + paramsMap.put(params[i], values[i]); + } + return setCustomParams(messages, paramsMap); + } + + + public static List setCustomParams(List messages, HashMap params) { + List list = new ArrayList<>(); + for (String message : messages) { + String afterMessage = message; + for (Map.Entry entry : params.entrySet()) { + afterMessage = afterMessage.replace(entry.getKey(), entry.getValue().toString()); + } + list.add(afterMessage); + } + return list; + } + + +} diff --git a/src/main/java/cc/carm/plugin/userprefix/util/gui/AutoPagedGUI.java b/src/main/java/cc/carm/plugin/userprefix/util/gui/AutoPagedGUI.java new file mode 100644 index 0000000..aa3dca7 --- /dev/null +++ b/src/main/java/cc/carm/plugin/userprefix/util/gui/AutoPagedGUI.java @@ -0,0 +1,105 @@ +package cc.carm.plugin.userprefix.util.gui; + +import cc.carm.plugin.userprefix.configuration.PrefixConfig; +import cc.carm.plugin.userprefix.util.ItemStackFactory; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.event.inventory.ClickType; +import org.bukkit.inventory.ItemStack; + +public class AutoPagedGUI extends CommonPagedGUI { + + ItemStack previousPageUI; + ItemStack nextPageUI; + ItemStack noPreviousPageUI; + ItemStack noNextPageUI; + int previousPageSlot = -1; + int nextPageSlot = -1; + + public AutoPagedGUI(GUIType type, String name, int[] range) { + super(type, name, range); + } + + public AutoPagedGUI(GUIType type, String name, int a, int b) { + super(type, name, a, b); + } + + public void setPreviousPageUI(ItemStack lastPageUI) { + this.previousPageUI = lastPageUI; + } + + public void setNextPageUI(ItemStack nextPageUI) { + this.nextPageUI = nextPageUI; + } + + public void setNoPreviousPageUI(ItemStack noPreviousPageUI) { + this.noPreviousPageUI = noPreviousPageUI; + } + + public void setNoNextPageUI(ItemStack noNextPageUI) { + this.noNextPageUI = noNextPageUI; + } + + public void setPreviousPageSlot(int slot) { + this.previousPageSlot = slot; + } + + public void setNextPageSlot(int slot) { + this.nextPageSlot = slot; + } + + + @Override + public void openGUI(Player user) { + if (previousPageSlot >= 0) + if (hasPreviousPage()) { + setItem(previousPageSlot, new GUIItem(previousPageUI == null ? new ItemStackFactory(Material.ARROW) + .setDisplayName("&f上一页") + .addLore("&7&o右键可前往第一页哦") + .toItemStack() : previousPageUI) { + @Override + public void ClickAction(ClickType type, Player u) { + if (type == ClickType.RIGHT) { + goFirstPage(); + } else { + goPreviousPage(); + } + PrefixConfig.Sounds.GUI_CLICK.play(u); + openGUI(u); +// u.playSound(u.getLocation(), Sound.ENTITY_CHICKEN_EGG, 0.5f, 1); + } + }); + } else { +// setItem(previousPageSlot, new GUIItem(noPreviousPageUI == null ? new ItemStackFactory(Material.GRAY_STAINED_GLASS_PANE) +// .setDisplayName("已经是第一页啦") +// .toItemStack() : noPreviousPageUI)); + } + + if (previousPageSlot >= 0) + if (hasNextPage()) { + setItem(nextPageSlot, new GUIItem(nextPageUI == null ? new ItemStackFactory(Material.ARROW) + .setDisplayName("下一页") + .addLore("&7&o右键可前往最后一页哦") + .toItemStack() : nextPageUI) { + @Override + public void ClickAction(ClickType type, Player u) { + if (type == ClickType.RIGHT) { + goLastPage(); + } else { + goNextPage(); + } + PrefixConfig.Sounds.GUI_CLICK.play(u); + openGUI(u); +// u.playSound(u.getLocation(), Sound.ENTITY_CHICKEN_EGG, 0.5f, 1); + } + }); + } else { +// setItem(nextPageSlot, new GUIItem(noNextPageUI == null ? new ItemStackFactory(Material.GRAY_STAINED_GLASS_PANE) +// .setDisplayName("已经是最后一页啦") +// .toItemStack() : noNextPageUI)); + } + + super.openGUI(user); + } + +} diff --git a/src/main/java/cc/carm/plugin/userprefix/util/gui/CommonPagedGUI.java b/src/main/java/cc/carm/plugin/userprefix/util/gui/CommonPagedGUI.java new file mode 100644 index 0000000..bf51bfd --- /dev/null +++ b/src/main/java/cc/carm/plugin/userprefix/util/gui/CommonPagedGUI.java @@ -0,0 +1,170 @@ +package cc.carm.plugin.userprefix.util.gui; + + +import org.bukkit.entity.Player; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class CommonPagedGUI extends PagedGUI { + + private int[] range; + + int a; + int b; + + int lineA; + int columnA; + int lineB; + int columnB; + + private CommonPagedGUI(GUIType type, String name) { + super(type, name); + } + + public CommonPagedGUI(GUIType type, String Name, int[] range) { + super(type, Name); + Arrays.sort(range); + this.range = range; + + } + + public CommonPagedGUI(GUIType type, String Name, int a, int b) { + super(type, Name); + this.a = a; + this.b = b; + toRange(a, b); + } + + /* + int[] matrix = new int[]{ + 0, 1, 2, 3, 4, 5, 6, 7, 8, + 9, 10, 11, 12, 13, 14, 15, 16, 17, + 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35, + 36, 37, 38, 39, 40, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53 + } + */ + + private void toRange(int a, int b) { + if (a > b) { + a = a ^ b; + b = a ^ b; + a = a ^ b; + } + + lineA = getLine(a); + columnA = getColumn(a); + lineB = getLine(b); + columnB = getColumn(b); + + if (lineB > this.items.length / 9) + throw new IndexOutOfBoundsException("页面内容范围超过了GUI的大小"); + + int[] range = new int[(lineB - lineA + 1) * (columnB - columnA + 1)]; + + for (int i = 0, l = 0; i < this.items.length; i++) { + int li = getLine(i); + int ci = getColumn(i); + if (li >= lineA && li <= lineB && ci >= columnA && ci <= columnB) { + range[l] = i; + l++; + } + } + + this.range = range; + } + + int getLine(int i) { + return i / 9 + 1; + } + + int getColumn(int i) { + return i % 9 + 1; + } + + @Override + public boolean hasPreviousPage() { + return page > 1; + } + + @Override + public boolean hasNextPage() { + return page < getLastPageNumber(); + } + + + /** + * 前往第一页 + */ + public void goFirstPage() { + if (hasPreviousPage()) + this.page = 1; + else + throw new IndexOutOfBoundsException(); + } + + + /** + * 前往最后一页 + */ + public void goLastPage() { + if (hasNextPage()) + this.page = getLastPageNumber(); + else + throw new IndexOutOfBoundsException(); + } + + + /** + * 得到最后一页的页码 + * + * @return 最后一页的页码 + */ + public int getLastPageNumber() { + return (this.container.size() / range.length) + 1; + } + + /** + * 得到第一页的页码 + * + * @return 第一页页码(默认为1) + */ + public int getFirstPageNumber() { + return 1; + } + + + @Override + public void openGUI(Player player) { + if (container.isEmpty()) { + super.openGUI(player); + return; + } + List list = new ArrayList<>(); + int start = (page - 1) * range.length; + for (int i = start; i < start + range.length; i++) { + if (i < container.size()) { + list.add(container.get(i)); + } else { + break; + } + } + int i = 0; + for (int index : range) { + setItem(index, null); + } + for (int index : range) { + if (i < list.size()) { + setItem(index, list.get(i)); + i++; + } else { + break; + } + } + super.openGUI(player); + } + +} diff --git a/src/main/java/cc/carm/plugin/userprefix/util/gui/GUI.java b/src/main/java/cc/carm/plugin/userprefix/util/gui/GUI.java new file mode 100644 index 0000000..90a1601 --- /dev/null +++ b/src/main/java/cc/carm/plugin/userprefix/util/gui/GUI.java @@ -0,0 +1,378 @@ +package cc.carm.plugin.userprefix.util.gui; + +import cc.carm.plugin.userprefix.Main; +import cc.carm.plugin.userprefix.util.ColorParser; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.entity.HumanEntity; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.HandlerList; +import org.bukkit.event.Listener; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.inventory.InventoryCloseEvent; +import org.bukkit.event.inventory.InventoryDragEvent; +import org.bukkit.event.inventory.InventoryType; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class GUI { + + private static final HashMap openedGUIs = new HashMap<>(); + + GUIType type; + String name; + public GUIItem[] items; + public Inventory inv; + + boolean setCancelledIfClickOnTarget = true; + boolean setCancelledIfClickOnSelf = true; + boolean setCancelledIfClickOnOuter = true; + + Map flags; + + public Listener listener; + + public GUI(GUIType type, String name) { + this.type = type; + this.name = ColorParser.parseColor(name); + switch (type) { + case ONEBYNINE: + this.items = new GUIItem[9]; + break; + case TWOBYNINE: + this.items = new GUIItem[18]; + break; + case THREEBYNINE: + this.items = new GUIItem[27]; + break; + case FOURBYNINE: + this.items = new GUIItem[36]; + break; + case FIVEBYNINE: + this.items = new GUIItem[45]; + break; + default: + case SIXBYNINE: + this.items = new GUIItem[54]; + break; + + case HOPPER: + this.items = new GUIItem[InventoryType.HOPPER.getDefaultSize()]; + break; + case BEACON: + this.items = new GUIItem[InventoryType.BEACON.getDefaultSize()]; + break; + case DISPENSER: + this.items = new GUIItem[InventoryType.DISPENSER.getDefaultSize()]; + break; + case DROPPER: + this.items = new GUIItem[InventoryType.DROPPER.getDefaultSize()]; + break; + case FURNACE: + this.items = new GUIItem[InventoryType.FURNACE.getDefaultSize()]; + break; + case WORKBENCH: + this.items = new GUIItem[InventoryType.WORKBENCH.getDefaultSize()]; + break; + case CRAFTING: + this.items = new GUIItem[InventoryType.CRAFTING.getDefaultSize()]; + break; + case ENCHANTING: + this.items = new GUIItem[InventoryType.ENCHANTING.getDefaultSize()]; + break; + case BREWING: + this.items = new GUIItem[InventoryType.BREWING.getDefaultSize()]; + break; + case PLAYER: + this.items = new GUIItem[InventoryType.PLAYER.getDefaultSize()]; + break; + case MERCHANT: + this.items = new GUIItem[InventoryType.MERCHANT.getDefaultSize()]; + break; + case ENDER_CHEST: + this.items = new GUIItem[InventoryType.ENDER_CHEST.getDefaultSize()]; + break; + + case CREATIVE: + this.items = new GUIItem[InventoryType.CREATIVE.getDefaultSize()]; + break; + case CANCEL: + this.items = null; + } + } + + + public final void setItem(int index, GUIItem item) { + if (item == null) { + this.items[index] = new GUIItem(new ItemStack(Material.AIR)); + } else { + this.items[index] = item; + } + } + + /** + * 批量添加GUI Item + * + * @param item + * @param index + */ + public void setItem(GUIItem item, int... index) { + for (int i : index) { + setItem(i, item); + } + } + + public GUIItem getItem(int index) { + return this.items[index]; + } + + /** + * 更新玩家箱子的视图 + */ + public void updateView() { + if (this.inv != null) { + List viewers = this.inv.getViewers(); + for (int index = 0; index < this.items.length; index++) { + if (items[index] == null) { + inv.setItem(index, new ItemStack(Material.AIR)); + } else { + inv.setItem(index, items[index].display); + } + } + for (HumanEntity p : viewers) { + ((Player) p).updateInventory(); + } + } + } + + /** + * 设置是否取消点击GUI内物品的事件 + * 如果不取消,玩家可以从GUI中拿取物品。 + * + * @param b 是否取消 + */ + public void setCancelledIfClickOnTarget(boolean b) { + this.setCancelledIfClickOnTarget = b; + } + + /** + * 设置是否取消点击自己背包内物品的事件 + * 如果不取消,玩家可以从自己的背包中拿取物品。 + * + * @param b 是否取消 + */ + public void setCancelledIfClickOnSelf(boolean b) { + this.setCancelledIfClickOnSelf = b; + } + + /** + * 设置是否取消点击GUI外的事件 + * 如果不取消,玩家可以把物品从GUI或背包中丢出去 + * + * @param b 是否取消 + */ + public void setCancelledIfClickOnOuter(boolean b) { + this.setCancelledIfClickOnOuter = b; + } + + public void addFlag(String flag, Object obj) { + if (this.flags == null) this.flags = new HashMap<>(); + this.flags.put(flag, obj); + } + + public Object getFlag(String flag) { + if (this.flags == null) return null; + else + return this.flags.get(flag); + } + + public void setFlag(String flag, Object obj) { + if (this.flags == null) this.flags = new HashMap<>(); + this.flags.replace(flag, obj); + } + + public void removeFlag(String flag) { + if (this.flags == null) this.flags = new HashMap<>(); + this.flags.remove(flag); + } + + public void rawClickListener(InventoryClickEvent event) { + } + + public void openGUI(Player player) { + Inventory inv; + if (this.type == GUIType.CANCEL) { + throw new NullPointerException("被取消或不存在的GUI"); + } + switch (type) { + default: + case ONEBYNINE: + case TWOBYNINE: + case THREEBYNINE: + case FOURBYNINE: + case FIVEBYNINE: + case SIXBYNINE: + inv = Bukkit.createInventory(null, this.items.length, this.name); + break; + case HOPPER: + inv = Bukkit.createInventory(null, InventoryType.HOPPER, this.name); + break; + case BEACON: + inv = Bukkit.createInventory(null, InventoryType.BEACON, this.name); + break; + case DISPENSER: + inv = Bukkit.createInventory(null, InventoryType.DISPENSER, this.name); + break; + case DROPPER: + inv = Bukkit.createInventory(null, InventoryType.DROPPER, this.name); + break; + case FURNACE: + inv = Bukkit.createInventory(null, InventoryType.FURNACE, this.name); + break; + case WORKBENCH: + inv = Bukkit.createInventory(null, InventoryType.WORKBENCH, this.name); + break; + case CRAFTING: + inv = Bukkit.createInventory(null, InventoryType.CRAFTING, this.name); + break; + case ENCHANTING: + inv = Bukkit.createInventory(null, InventoryType.ENCHANTING, this.name); + break; + case BREWING: + inv = Bukkit.createInventory(null, InventoryType.BREWING, this.name); + break; + case PLAYER: + inv = Bukkit.createInventory(null, InventoryType.PLAYER, this.name); + break; + case CREATIVE: + inv = Bukkit.createInventory(null, InventoryType.CREATIVE, this.name); + break; + case MERCHANT: + inv = Bukkit.createInventory(null, InventoryType.MERCHANT, this.name); + break; + case ENDER_CHEST: + inv = Bukkit.createInventory(null, InventoryType.ENDER_CHEST, this.name); + break; + } + + for (int index = 0; index < this.items.length; index++) { + if (items[index] == null) { + inv.setItem(index, new ItemStack(Material.AIR)); + } else { + inv.setItem(index, items[index].display); + } + } + setOpenedGUI(player, this); + this.inv = inv; + player.openInventory(inv); + + if (listener == null) + Bukkit.getPluginManager().registerEvents(listener = new Listener() { + @EventHandler + public void onInventoryClickEvent(InventoryClickEvent event) { + rawClickListener(event); + if (!(event.getWhoClicked() instanceof Player)) { + return; + } + Player p = (Player) event.getWhoClicked(); + if (event.getSlot() != -999) { + try { + if (getOpenedGUI(p) == GUI.this && event.getClickedInventory() != null && event.getClickedInventory().equals(GUI.this.inv) && GUI.this.items[event.getSlot()] != null) + GUI.this.items[event.getSlot()].realRawClickAction(event); + } catch (ArrayIndexOutOfBoundsException e) { + e.printStackTrace(); + System.err.print("err cause by GUI(" + GUI.this.toString() + "), name=" + name); + return; + } + } else { + if (setCancelledIfClickOnOuter) event.setCancelled(true); + } + if (hasOpenedGUI(p) && /*player.openedGUI.inv.equals(event.getClickedInventory())*/ getOpenedGUI(p) == GUI.this && event.getClickedInventory() != null) { + if (event.getClickedInventory().equals(GUI.this.inv)) { + if (setCancelledIfClickOnTarget) event.setCancelled(true); + + if (event.getSlot() != -999 && GUI.this.items[event.getSlot()] != null) { + if (GUI.this.items[event.getSlot()].isActionActive()) { + GUI.this.items[event.getSlot()].onClick(event.getClick()); + GUI.this.items[event.getSlot()].ClickAction(event.getClick(), player); + GUI.this.items[event.getSlot()].rawClickAction(event); + if (!GUI.this.items[event.getSlot()].actions.isEmpty()) { + for (GUIItem.GUIClickAction action : GUI.this.items[event.getSlot()].actions) { + action.run(event.getClick(), player); + } + } + } + if (!GUI.this.items[event.getSlot()].actionsIgnoreActive.isEmpty()) { + for (GUIItem.GUIClickAction action : GUI.this.items[event.getSlot()].actionsIgnoreActive) { + action.run(event.getClick(), player); + } + } + } + } else if (event.getClickedInventory().equals(p.getInventory())) { + if (setCancelledIfClickOnSelf) event.setCancelled(true); + } + } + } + + @EventHandler + public void onDrag(InventoryDragEvent e) { + if (e.getWhoClicked() instanceof Player) { + Player p = (Player) e.getWhoClicked(); + if (e.getInventory().equals(inv) || e.getInventory().equals(p.getInventory())) { + GUI.this.onDrag(e); + } + } + } + + @EventHandler + public void onInventoryCloseEvent(InventoryCloseEvent event) { + if (event.getPlayer() instanceof Player && event.getInventory().equals(inv)) { + Player p = (Player) event.getPlayer(); + if (event.getInventory().equals(inv)) { + HandlerList.unregisterAll(this); + listener = null; + onClose(); + } + } + } + }, Main.getInstance()); + + } + + /** + * 拖动GUI内物品是执行的代码 + * + * @param event InventoryDragEvent + */ + public void onDrag(InventoryDragEvent event) { + } + + /** + * 关闭GUI时执行的代码 + */ + public void onClose() { + } + + + public static void setOpenedGUI(Player player, GUI gui) { + openedGUIs.put(player, gui); + } + + public static boolean hasOpenedGUI(Player player) { + return openedGUIs.containsKey(player); + } + + public static GUI getOpenedGUI(Player player) { + return openedGUIs.get(player); + } + + public static void removeOpenedGUI(Player player) { + openedGUIs.remove(player); + } + +} diff --git a/src/main/java/cc/carm/plugin/userprefix/util/gui/GUIItem.java b/src/main/java/cc/carm/plugin/userprefix/util/gui/GUIItem.java new file mode 100644 index 0000000..ea60de3 --- /dev/null +++ b/src/main/java/cc/carm/plugin/userprefix/util/gui/GUIItem.java @@ -0,0 +1,96 @@ +package cc.carm.plugin.userprefix.util.gui; + +import org.bukkit.entity.Player; +import org.bukkit.event.inventory.ClickType; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.inventory.ItemStack; + +import java.util.HashSet; +import java.util.Set; + +public class GUIItem { + + ItemStack display; + boolean actionActive = true; + + public Set actions = new HashSet<>(); + public Set actionsIgnoreActive = new HashSet<>(); + + public GUIItem(ItemStack display) { + this.display = display; + } + + public final ItemStack getDisplay() { + return this.display; + } + + public final void setDisplay(ItemStack display) { + this.display = display; + } + + public final boolean isActionActive() { + return this.actionActive; + } + + public final void setActionActive(boolean b) { + actionActive = b; + } + + /** + * 玩家点击GUI后执行的代码 + * + * @param type 点击的类型 + * @param player 点击GUI的玩家 + */ + @Deprecated + public void ClickAction(ClickType type, Player player) { + + } + + /** + * 玩家点击GUI后执行的代码 + * + * @param type 点击的类型 + */ + public void onClick(ClickType type) { + + } + + public void addClickAction(GUIClickAction action) { + actions.add(action); + } + + public void addActionIgnoreActive(GUIClickAction action) { + actionsIgnoreActive.add(action); + } + + public void customAction() { + + } + + public void rawClickAction(InventoryClickEvent event) { + + } + + public void realRawClickAction(InventoryClickEvent event) { + + } + + public void customAction(Object obj) { + + } + + /** + * 玩家点击GUI后执行的代码 + * + * @param player 点击GUI的玩家 + */ + public void customAction(Player player) { + + } + + public abstract static class GUIClickAction { + public abstract void run(ClickType type, Player player); + } + +} diff --git a/src/main/java/cc/carm/plugin/userprefix/util/gui/GUIType.java b/src/main/java/cc/carm/plugin/userprefix/util/gui/GUIType.java new file mode 100644 index 0000000..9ed6de9 --- /dev/null +++ b/src/main/java/cc/carm/plugin/userprefix/util/gui/GUIType.java @@ -0,0 +1,30 @@ +package cc.carm.plugin.userprefix.util.gui; + +/** + * @author LSeng + */ +public enum GUIType { + + ONEBYNINE, + TWOBYNINE, + THREEBYNINE, + FOURBYNINE, + FIVEBYNINE, + SIXBYNINE, + DISPENSER, + DROPPER, + FURNACE, + WORKBENCH, + CRAFTING, + ENCHANTING, + BREWING, + PLAYER, + CREATIVE, + MERCHANT, + ENDER_CHEST, + BEACON, + HOPPER, + UNKNOWN, + CANCEL; + +} diff --git a/src/main/java/cc/carm/plugin/userprefix/util/gui/PagedGUI.java b/src/main/java/cc/carm/plugin/userprefix/util/gui/PagedGUI.java new file mode 100644 index 0000000..42a423e --- /dev/null +++ b/src/main/java/cc/carm/plugin/userprefix/util/gui/PagedGUI.java @@ -0,0 +1,75 @@ +package cc.carm.plugin.userprefix.util.gui; + + +import java.util.ArrayList; +import java.util.List; + +public abstract class PagedGUI extends GUI { + + List container = new ArrayList<>(); + public int page = 1; + + public PagedGUI(GUIType type, String name) { + super(type, name); + } + + public int addItem(GUIItem i) { + container.add(i); + return container.size() - 1; + } + + /** + * 从GUI中移除一个物品 + * + * @param item 物品 + */ + public void removeItem(GUIItem item) { + container.remove(item); + } + + /** + * 从GUI中移除一个物品 + * + * @param slot 物品格子数 + */ + public void removeItem(int slot) { + container.remove(slot); + } + + public List getItemsContainer() { + return new ArrayList<>(container); + } + + /** + * 前往上一页 + */ + public void goPreviousPage() { + if (hasPreviousPage()) + page--; + else + throw new IndexOutOfBoundsException(); + } + + + /** + * 前往下一页 + */ + public void goNextPage() { + if (hasNextPage()) + page++; + else + throw new IndexOutOfBoundsException(); + } + + + /** + * @return 是否有上一页 + */ + public abstract boolean hasPreviousPage(); + + /** + * @return 是否有下一页 + */ + public abstract boolean hasNextPage(); + +} diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml new file mode 100644 index 0000000..a226473 --- /dev/null +++ b/src/main/resources/config.yml @@ -0,0 +1,102 @@ +version: ${project.version} + +debug: false + +functions: + OnNamePrefix: true # 是否给头顶上添加前缀,该方法用到了头顶的那个计分板,如有冲突请关掉哦~ + autoUsePrefix: true # 自动前缀显示 当玩家没有自己选择一个前缀的时候,会自动使用所拥有的的前缀中权重最高的那一个 + +messages: + selected: + - "&7您选择了 &f%(name) &7作为当前显示的前缀。" + expired: + - "&7您先前使用的前缀 &f%(oldName) &7已到期。" + - "&7现在已为您重新调整为 &f%(newName) &7。" + help: + - "&7输入 &b/prefix &7打开前缀选择菜单。" + +Sounds: #相关的声音,注释掉则不播放声音 格式为 【声音名:音量:音调】 或 【声音名:音量】 或 【声音名】 + openGUI: "BLOCK_NOTE_BLOCK_PLING:1:1" + guiClick: "UI_BUTTON_CLICK" + prefixChange: "ENTITY_VILLAGER_YES" + prefixExpired: "ENTITY_VILLAGER_NO" + +# 默认前缀的配置 +# 默认前缀的权重为0哦 +defaultPrefix: + name: "默认前缀" + content: "&b" + itemNotUsing: + ==: org.bukkit.inventory.ItemStack + type: NAME_TAG + meta: + ==: ItemMeta + meta-type: UNSPECIFIC + display-name: "§f默认玩家前缀 §f(点击切换)" + lore: + - "" + - "§a➥ 点击切换到该前缀" + itemUsing: + ==: org.bukkit.inventory.ItemStack + type: NAME_TAG + meta: + ==: ItemMeta + meta-type: UNSPECIFIC + display-name: "§f默认玩家前缀" + lore: + - "" + - "§a✔ 您正在使用该前缀" + +prefixes: + VIP: + name: "&b&lPro&b" # [必须] 名字(切换的时候左下角会弹提示 用的就是这个名字) + content: "§b§lPro §b" # [必须] 显示在名字前面的内容 + weight: 1 # [必须] 权重,用于GUI里面的排序(越大显示在越后面)和自动前缀显示 + permission: "yc.pro" # [非必须] 检测的权限,如果没有就是人人都能用,也代表不用配置“itemNoPermission”了(因为压根不可能显示没权限时候的物品) + itemHasPermission: # [必须] 当有权限的时候会显示这个Item + ==: org.bukkit.inventory.ItemStack + type: DIAMOND + meta: + ==: ItemMeta + meta-type: UNSPECIFIC + display-name: "§b§lPro §b会员前缀" + lore: + - "§7Pro会员专属称号" + - "" + - "§f尊贵的Pro会员专属称号。" + - "§f您将获得多种特权与更好的游戏体验。" + - "" + - "§a➥ 点击切换到该前缀" + itemUsing: # [非必需] 当有权限的时候会显示这个Item,如果没有这个配置就自动显示“itemHasPermission”的。 + ==: org.bukkit.inventory.ItemStack + type: DIAMOND + meta: + ==: ItemMeta + meta-type: UNSPECIFIC + display-name: "§b§lPro §b会员前缀" + enchants: + PROTECTION_ENVIRONMENTAL: 1 #加一个附魔这样看上去就像是选中了的 + lore: + - "§7Pro会员专属称号" + - "" + - "§f尊贵的Pro会员专属称号。" + - "§f您将获得多种特权与更好的游戏体验。" + - "" + - "§a✔ 您正在使用该前缀" + itemNoPermission: # [非必需] 如果没有权限就会显示这个item。如果不配置该物品,则玩家没有使用权限时不会显示在GUI里面。 + ==: org.bukkit.inventory.ItemStack + type: INK_SACK + damage: 8 + meta: + ==: ItemMeta + meta-type: UNSPECIFIC + display-name: "§b§lPro+ §b会员前缀 §c(未拥有)" + lore: + - "§7Pro+会员专属称号" + - "" + - "§f尊贵的Pro会员专属称号。" + - "§f您将获得多种特权与更好的游戏体验。" + - "§f您可以输入 §b/vip §f指令查看详细特权!" + - "" + - "§e✯ 加入Pro+会员以使用该前缀!" + diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml new file mode 100644 index 0000000..b1b190c --- /dev/null +++ b/src/main/resources/plugin.yml @@ -0,0 +1,20 @@ +main: cc.carm.plugin.userprefix.Main +name: UserPrefix +version: ${project.version} +authors: + - Carm +depend: + - LuckPerms +softdepend: + - PlaceholderAPI +commands: + UserPrefix: + aliases: + - prefix + description: "用户前缀系统玩家指令,用于打开前缀GUI。" + UserPrefixAdmin: + aliases: + - upa + - prefixAdmin + permission: "UserPrefix.admin" + description: "用户前缀系统管理指令,可以查看前缀列表与重载配置文件。" \ No newline at end of file