1
mirror of https://github.com/CarmJos/UserPrefix.git synced 2026-06-05 00:35:02 +08:00

Compare commits

...

157 Commits

Author SHA1 Message Date
renovate[bot] 3b2eaf2519 chore(deps): update dependency org.apache.maven.plugins:maven-surefire-plugin to v3.5.6 (#145)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-28 04:57:06 +00:00
carm 75ee308174 chore(deps): update XSeries dependency version to 13.8.0 2026-05-13 03:49:24 +08:00
renovate[bot] bed1651027 chore(deps): update actions/upload-artifact action to v7 2026-03-10 08:05:54 +08:00
renovate[bot] 7dde64e699 chore(deps): update dependency org.apache.maven.plugins:maven-shade-plugin to v3.6.2 (#142)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-05 17:38:09 +00:00
renovate[bot] 9b88142676 fix(deps): update dependency org.bstats:bstats-bukkit to v3.2.1 (#140)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-25 00:36:09 +00:00
renovate[bot] e0cfd87ac6 fix(deps): update dependency org.bstats:bstats-bukkit to v3.2.0 (#139)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-23 21:21:11 +00:00
renovate[bot] fd764fa348 chore(deps): update dependency org.apache.maven.plugins:maven-surefire-plugin to v3.5.5 (#138)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-21 16:38:46 +00:00
renovate[bot] 480cebd16a fix(deps): update dependency org.jetbrains:annotations to v26.1.0 (#137)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-18 22:30:57 +00:00
renovate[bot] 6cd25cf94a fix(deps): update dependency me.clip:placeholderapi to v2.12.2 (#136)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-09 01:37:34 +00:00
renovate[bot] 72071492b3 fix(deps): update dependency me.clip:placeholderapi to v2.12.1 (#135)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-03 18:09:02 +00:00
renovate[bot] b334c3476e fix(deps): update dependency me.clip:placeholderapi to v2.12.0 (#134)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-02 22:27:49 +00:00
renovate[bot] c94ed28b98 chore(deps): update dependency org.apache.maven.plugins:maven-compiler-plugin to v3.15.0 (#133)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-02 02:02:54 +00:00
carm 72aa4e3a82 ci(deploy): Add deployment to Modrinth 2025-12-25 14:53:04 +08:00
carm acc92d5fb4 ci(deploy): Add deployment to Modrinth 2025-12-25 14:50:52 +08:00
renovate[bot] bc002f9a07 chore(deps): update actions/upload-artifact action to v6 2025-12-25 14:36:04 +08:00
carm f11749d4da ci(deploy): Add deployment to Modrinth 2025-12-25 14:35:42 +08:00
renovate[bot] a52b703a75 chore(deps): update dependency com.github.cryptomorin:xseries to v13.6.0 (#130)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-13 20:37:53 +00:00
renovate[bot] 86775cdbcd chore(deps): update actions/checkout action to v6 2025-12-01 09:32:27 +08:00
renovate[bot] 8951b7ac90 chore(deps): update dependency org.apache.maven.plugins:maven-source-plugin to v3.4.0 (#128)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-29 05:34:56 +00:00
renovate[bot] 89b520b8d3 chore(deps): update dependency org.apache.maven.plugins:maven-jar-plugin to v3.5.0 (#126)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-15 12:53:53 +00:00
renovate[bot] b57ff5b2c5 chore(deps): update actions/upload-artifact action to v5 2025-11-11 03:30:53 +08:00
Creeam b4469f93d0 Fix Folia detection 2025-11-11 03:30:42 +08:00
renovate[bot] d91dd6c7bd fix(deps): update dependency org.spigotmc:spigot-api to v1.19-r0.1-20220725.090125-47 (#123)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-08 18:26:41 +00:00
renovate[bot] ebd9f9fbfc fix(deps): update dependency me.clip:placeholderapi to v2.11.7 (#121)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-03 19:39:27 +00:00
renovate[bot] f5d256b5c4 chore(deps): update github/codeql-action action to v4 2025-10-23 10:37:05 +08:00
renovate[bot] e43ce22551 chore(deps): update dependency com.github.cryptomorin:xseries to v13.5.1 (#119)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-08 21:08:58 +00:00
renovate[bot] f821ab88ed chore(deps): update dependency com.github.cryptomorin:xseries to v13.5.0 (#117)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-07 16:01:58 +00:00
renovate[bot] b03bb4a476 chore(deps): update dependency org.apache.maven.plugins:maven-compiler-plugin to v3.14.1 (#116)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-22 09:26:03 +00:00
renovate[bot] 86326a168b chore(deps): update dependency org.apache.maven.plugins:maven-javadoc-plugin to v3.12.0 (#115)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-21 08:34:12 +00:00
renovate[bot] 672641259e chore(deps): update dependency com.github.cryptomorin:xseries to v13.4.0 2025-09-14 17:02:04 +08:00
renovate[bot] 5fc46eed8f chore(deps): update dependency org.apache.maven.plugins:maven-surefire-plugin to v3.5.4 2025-09-14 16:56:42 +08:00
renovate[bot] 50ded3fa11 chore(deps): update dependency org.apache.maven.plugins:maven-shade-plugin to v3.6.1 (#112)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-14 03:07:59 +00:00
carm 951fe67117 fix(color): 修复前缀颜色显示的问题。 2025-09-04 01:27:52 +08:00
carm 442374da0a fix: 修复前缀内容显示错误的问题。 2025-09-04 01:21:13 +08:00
renovate[bot] 77a277ba58 fix(deps): update dependency org.jetbrains:annotations to v26.0.2-1 (#109)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-02 00:33:43 +00:00
renovate[bot] 1ead1a0692 chore(deps): update actions/checkout action to v5 2025-08-21 23:44:41 +08:00
renovate[bot] f519fa43df chore(deps): update actions/setup-java action to v5 2025-08-21 23:44:34 +08:00
renovate[bot] da63cf8b1c chore(deps): update dependency org.apache.maven.plugins:maven-javadoc-plugin to v3.11.3 (#105)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-16 16:26:54 +00:00
carm 9527e9fcbb fix: 修复颜色丢失的问题。 2025-08-06 19:05:26 +08:00
carm 5befd40706 feat: 新增prefix内容轮换。 2025-08-03 08:04:09 +08:00
carm e1bedba01b feat: 新增prefix内容轮换。 2025-08-03 08:03:50 +08:00
carm 5809659155 feat: 新增prefix内容轮换。 2025-08-03 07:58:53 +08:00
carm e5013b76e0 feat: 新增prefix变量支持 2025-07-22 16:05:22 +08:00
carm 6604cec136 feat: 添加 description 变量 2025-07-20 21:33:59 +08:00
renovate[bot] c7f3286d81 fix(deps): update dependency net.luckperms:api to v5.5 (#103)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-20 08:58:11 +00:00
renovate[bot] 5ed6f1a301 chore(deps): update dependency org.apache.maven.plugins:maven-clean-plugin to v3.5.0 (#102)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-20 04:43:17 +00:00
renovate[bot] 9864d5a75e chore(deps): update dependency com.github.cryptomorin:xseries to v13.3.3 (#101)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-20 02:05:27 +00:00
renovate[bot] 2b8a7e7545 chore(deps): update deps.easyplugin.version to v1.5.14 (#100)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-19 21:48:43 +00:00
renovate[bot] 25b961b913 chore(deps): update dependency cc.carm.lib:mineconfiguration-bukkit to v3.1.3 (#99)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-19 18:50:35 +00:00
carm 8e8427a937 feat: 为物品配置支持变量与描述插入。 2025-07-19 20:38:16 +08:00
renovate[bot] 748335a962 chore(deps): update dependency org.apache.maven.plugins:maven-surefire-plugin to v3.5.3 2025-05-12 18:15:10 +08:00
renovate[bot] 1e0fd2bcd4 chore(deps): update dependency com.github.cryptomorin:xseries to v13.2.0 2025-05-12 18:15:00 +08:00
flowerinsnow 04caf2df73 revert: change api-version to 1.13 2025-05-12 18:14:49 +08:00
flowerinsnow 0eba124f08 revert: no more mirror repo 2025-05-12 18:14:49 +08:00
flowerinsnow d54349da98 feat: use reflection instead folia dependencies, java 8 supported 2025-05-12 18:14:49 +08:00
flowerinsnow eab9b2385c feat: folia support 2025-05-12 18:14:49 +08:00
flowerinsnow 003884d772 fix: use 3.0 MineConfiguration 2025-05-12 18:14:49 +08:00
renovate[bot] 610242ff62 chore(deps): update dependency cc.carm.lib:mineconfiguration-bukkit to v3.1.1 2025-04-18 10:56:29 +08:00
renovate[bot] 289ab87273 chore(deps): update dependency com.github.cryptomorin:xseries to v13.1.0 2025-04-18 10:56:22 +08:00
renovate[bot] 46c1c03113 chore(deps): update dependency cc.carm.lib:mineconfiguration-bukkit to v3 2025-03-12 04:37:14 +08:00
renovate[bot] 5ddde2aa99 chore(deps): update dependency org.apache.maven.plugins:maven-compiler-plugin to v3.14.0 (#87)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-09 18:57:52 +00:00
renovate[bot] 38a9b0866c chore(deps): update dependency org.apache.maven.plugins:maven-clean-plugin to v3.4.1 (#86)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-09 15:55:02 +00:00
carm e7b5b78d66 ci(doc): 修复javadoc地址错误的问题 2025-02-05 00:24:46 +08:00
carm ca83cc63a5 feat: 小版本更新 2025-02-05 00:18:00 +08:00
carm 3c19c8165f feat(content): 为前缀内容支持PAPI。 2025-02-04 15:03:39 +08:00
carm 844f59af20 chore: 打开界面前不再强制关闭GUI 2025-02-04 12:50:57 +08:00
renovate[bot] 4d42982659 fix(deps): update dependency org.jetbrains:annotations to v26.0.2 (#82)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-24 09:46:22 +00:00
renovate[bot] f272456079 fix(deps): update dependency org.bstats:bstats-bukkit to v3.1.0 2025-01-18 02:18:26 +08:00
renovate[bot] 50be238b36 fix(deps): update dependency me.clip:placeholderapi to v2.11.6 2025-01-18 02:18:23 +08:00
renovate[bot] b7341290ec chore(deps): update dependency org.apache.maven.plugins:maven-source-plugin to v3.3.1 2025-01-18 02:18:20 +08:00
renovate[bot] af59a8cb4b chore(deps): update dependency org.apache.maven.plugins:maven-shade-plugin to v3.6.0 2025-01-18 02:18:16 +08:00
renovate[bot] 30f4ca9af1 chore(deps): update dependency org.apache.maven.plugins:maven-javadoc-plugin to v3.11.2 2025-01-18 02:18:12 +08:00
renovate[bot] 61350cde57 chore(deps): update dependency org.apache.maven.plugins:maven-jar-plugin to v3.4.2 2025-01-18 02:18:09 +08:00
renovate[bot] ba3c7d5c32 chore(deps): update dependency org.apache.maven.plugins:maven-compiler-plugin to v3.13.0 2025-01-18 02:18:06 +08:00
renovate[bot] 3b06ca8109 chore(deps): update dependency cc.carm.lib:mineconfiguration-bukkit to v2.9.3 2025-01-18 02:16:26 +08:00
renovate[bot] 5edde945d1 chore(deps): update actions/checkout action to v4 2025-01-18 02:03:01 +08:00
renovate[bot] 4de8863ab6 chore(deps): update actions/setup-java action to v4 2025-01-18 02:02:51 +08:00
renovate[bot] 3c40419437 chore(deps): update dependency com.github.cryptomorin:xseries to v13 2025-01-18 02:02:41 +08:00
renovate[bot] d18ce24841 chore(deps): update deps.easyplugin.version to v1.5.12 2025-01-18 02:02:27 +08:00
renovate[bot] 0b62bcd1ef chore(deps): update dependency org.apache.maven.plugins:maven-clean-plugin to v3 2025-01-18 02:02:15 +08:00
carm e2aadfde23 chore:Update renovate.json 2025-01-18 01:58:19 +08:00
renovate[bot] 4ca145dbef chore(deps): update github/codeql-action action to v3 2025-01-18 01:52:47 +08:00
renovate[bot] 1b8d499d28 chore(deps): update dependency org.apache.maven.plugins:maven-surefire-plugin to v3 2025-01-18 01:33:46 +08:00
renovate[bot] 0c182a8ed3 fix(deps): update dependency org.jetbrains:annotations to v26 2025-01-18 01:33:19 +08:00
carm 66e2302fd0 Update renovate.json 2025-01-18 01:31:34 +08:00
carm 824a65b21c chore: Update renovate.json 2025-01-18 01:29:33 +08:00
renovate[bot] 2c49377c33 Add renovate.json (#60)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-18 00:00:55 +08:00
carm a0f8382830 doc(color): 添加渐变色用法实例 2024-09-28 23:48:35 +08:00
carm 675bbe4cf2 chore(deps): 升级依赖版本 2024-09-26 22:40:43 +08:00
carm 3b8fd6febf chore(deps): 升级依赖版本 2024-09-26 22:35:24 +08:00
carm d8282b9641 chore(deps): 升级依赖版本 2024-09-26 21:46:20 +08:00
carm ad616e5470 chore(deps): 支持1.21版本 2024-07-20 19:17:56 +08:00
carm f741723204 chore(deps): 更新 XSeries 尝试解决 #51 2024-07-17 06:40:34 +08:00
carm 54f526300a chore(deps): 更新 XSeries 尝试解决 #51 2024-07-17 06:35:53 +08:00
carm 5a0e5b8e96 chore(deps): 更新 XSeries 尝试解决 #51 2024-07-17 06:35:44 +08:00
skylandnewmc c89a9c6ccc Update ejemplo-prefijo.yml (#49) 2023-12-21 15:06:02 +08:00
skylandnewmc e321a1ec66 Update ejemplo-prefijo.yml (#48) 2023-11-06 00:41:40 +08:00
carm 9054161135 fix(msg): 修复消息占位符顺序错误 #47 2023-10-23 13:00:59 +08:00
carm 53281b80e3 chore(item): 更换物品的配置逻辑,支持跨版本的物品读取、保存。
BREAKING-CHANGE: 旧的配置文件需要进行变更。
2023-10-12 20:15:30 +08:00
carm 12489fe1a8 chore(item): 更换物品的配置逻辑,支持跨版本的物品读取、保存。
BREAKING-CHANGE: 旧的配置文件需要进行变更。
2023-10-12 20:09:24 +08:00
carm 7d62f68662 chore(item): 更换物品的配置逻辑,支持跨版本的物品读取、保存。
BREAKING-CHANGE: 旧的配置文件需要进行变更。
2023-10-10 15:20:40 +08:00
carm 4cc50c21d5 chore(item): 更换物品的配置逻辑,支持跨版本的物品读取、保存。
BREAKING-CHANGE: 旧的配置文件需要进行变更。
2023-10-10 15:01:51 +08:00
skylandnewmc 0d25490d0f feat(i18n): example-prefix.yml translated to Spanish 2023-09-28 12:57:12 +08:00
carm a805aba42e fix(auto): 修复自动前缀判断错误的问题 2023-09-21 22:26:21 +08:00
Snowball_233 2a0d6b8582 fix(papi): 添加“持久”标签 避免卸载。
Co-authored-by: Carm <KarmunJ@outlook.com>
2023-07-16 23:22:18 +08:00
carm 1ea993a31b feat(gui): 支持配置更多GUI按钮。(#44) 2023-02-23 22:27:22 +08:00
carm 8ce82ec7fd chore(expansion): 适配新版EasyPlugin提供的解析器 2023-02-19 04:19:51 +08:00
carm 6fb9daa3d3 docs(donate): Thanks for Jetbrains! 2023-02-15 00:33:08 +08:00
carm da23eaa8fe fix(placeholder): 修复PlaceholderAPI重载后变量失效的问题 2023-02-01 15:32:08 +08:00
carm 36cc4bf7ce fix(conf): 修复玩家进服时无法读取配置文件而出现报错的问题。#42 2023-01-20 00:58:45 +08:00
carm 7cf9f3f9ee fix(view): 修复获取用户”可见前缀”列表时出现的判断错误的问题。 2023-01-18 22:03:00 +08:00
DongShaoNB 84bb41ab2f feat(placeholder): 添加用户所拥有前缀的数量变量。 (#41)
* 修正错误的ID

修正错误的ID,墨囊应为INK_SAC而非INK_SACK

* 增加PAPI变量: %UserPrefix_prefix_number% 玩家称号数量

* 增加PAPI变量: %UserPrefix_prefix_number% 玩家称号数量

* 增加PAPI变量: %UserPrefix_prefix_number% 玩家称号数量

* 增加PAPI变量: %UserPrefix_prefix_number% 玩家称号数量

* Update example-prefix.yml

* 增加PAPI变量: %UserPrefix_prefix_number% 玩家称号数量

* 增加PAPI变量: %UserPrefix_prefix_number% 玩家称号数量

* feat(placeholder): 添加用户所拥有前缀的数量变量
2023-01-18 21:58:05 +08:00
carm 2fc2474f89 feat(cmd): 使用命令为玩家更换称号 (#37)
* feat(cmd): 使用命令为玩家更换称号 #35
2022-10-02 15:44:24 +08:00
carm 01a83d5c9e feat(conf): 支持切换前缀时执行自定义操作 (#32) 2022-09-13 01:33:11 +08:00
carm f648e29ee2 fix(code): 移除重复代码 2022-09-10 02:01:13 +08:00
carm cc0122f9ba chore: 适配依赖更新,移除冗余代码 (#33)
* feat(actions): 为单个前缀的选择添加可配置的操作

* feat(actions): 实现在前缀选择时执行操作

* chore: 适配新接口,删除冗杂代码

* chore: 适配新接口,删除冗杂代码

* chore: 修改repo
2022-09-10 01:57:45 +08:00
carm e3c911d375 feat(actions): 支持切换前缀时执行自定义操作 (#32)
* feat(actions): 为单个前缀的选择添加可配置的操作
* feat(actions): 实现在前缀选择时执行操作
2022-09-09 21:28:15 +08:00
carm dc14b0cd2e chore(cmd): 移除空行 2022-08-11 18:22:57 +08:00
carm a8908b6dd4 chore(conf): 移除无用字段。 2022-08-06 18:36:46 +08:00
carm fa9f3bc1ed chore(cmd): 重构指令的实现方式。 2022-08-06 18:04:54 +08:00
carm 7c2391a3b0 chore(conf): 修改配置文件包名。 2022-08-06 17:43:36 +08:00
carm cb384ca201 build(javadoc): 添加 EasyPlugin 与 MineConfiguration 的Javadoc链接。 2022-07-01 21:11:26 +08:00
carm bb9d672016 build(javadoc): 临时移除annotations的Javadoc链接。 2022-07-01 21:05:45 +08:00
carm 13d3d56eea build(javadoc): 添加Spigot的Javadoc链接。 2022-07-01 20:45:04 +08:00
carm 71db4b663c fix(messages): 修复 list 指令前缀变量未被替换的问题。 2022-07-01 20:39:09 +08:00
carm 9d601b4f75 fix(messages): 修复 list 指令前缀变量未被替换的问题。 2022-07-01 20:36:56 +08:00
carm 9693bc6b8a fix(gui): 修复GUI物品无法初始化的问题 2022-07-01 20:36:32 +08:00
carm cbd2a4373d fix(event): 采用新Event唤起方式 2022-07-01 18:48:50 +08:00
carm 2beefb71d4 fix(plugin): 更新EasyPlugin版本以防止部分Bukkit版本中的方法冲突。 2022-07-01 18:45:11 +08:00
carm 23db43604c docs(README): 修改部分配置文件介绍 2022-07-01 15:45:13 +08:00
carm 899acbe2cc refactor(config): 修改前缀配置文件的配置路径
BREAKING CHANGE: 旧的配置文件需要进行变更。
2022-06-18 22:04:02 +08:00
carm d3a3529ccc build(repo): 添加 EasyPlugin 的github-repo库 2022-06-18 06:40:53 +08:00
carm 969844dae9 refactor(all): 重构项目代码,变更配置文件格式
BREAKING CHANGE: 所有API接口变更,大部分配置文件变更。
2022-06-18 06:12:24 +08:00
carm 6904f5a6d6 Merge pull request #18 from Yurinann/Yurinann-issue-fix
[ci skip] 修复 "Bug 反馈" Issue 模板无法使用
2022-03-11 22:29:30 +08:00
Yurinann b60cb89460 [ci skip] 修复 "Bug 反馈" Issue 模板无法使用 2022-03-11 22:09:06 +08:00
carm ff108d0d1c 修复 #9 中提到的后续问题 2022-03-09 04:19:44 +08:00
carm 734e5f526c 尝试支持旧版本物品的读取 2022-03-02 04:20:17 +08:00
carm 99f2b6ff28 补充英文介绍。 2022-02-27 23:26:24 +08:00
carm 5c5a57af5b Merge pull request #14 from zhangnelson/master
修复order-desc不生效的问题
2022-02-27 23:18:35 +08:00
Nelsoniwork 044cd1a84f 修复order-desc不生效的问题 2022-02-27 23:15:21 +08:00
carm 29796ec455 [#12] 修复order-desc不生效的问题 2022-02-27 22:34:40 +08:00
carm 898ba5bf27 [#12] 修复玩家前缀未生效order的问题 2022-02-27 18:27:38 +08:00
carm 2e46888019 [#12] 优化代码格式 2022-02-27 18:22:51 +08:00
carm ac55fc135c [2.4.3] 版本更新
- [F] 修复 #12 相关问题。
- [R] 修改配置文件中两个字段的名称使其符合yaml规范。
- [A] 添加“functions.on-name-prefix.order-desc”选项,用于决定在TabList中排序顺序。
2022-02-26 23:43:20 +08:00
carm 774e9b764e [2.4.2] 修复跨版本时配置文件不全而导致序列化异常的问题 2022-02-21 04:46:39 +08:00
carm a31da83531 Merge pull request #11 from Ghost-chu/master
Fixed #10, 自定义物品反序列化工具
2022-02-20 19:48:11 +08:00
Ghost_chu 61857d6305 清理debug 2022-02-20 19:46:26 +08:00
Ghost_chu dd96188b22 use FileConfiguration 2022-02-20 19:44:36 +08:00
Ghost_chu eb4ecaada9 Fixed #10, 自定义物品反序列化工具 2022-02-20 19:36:39 +08:00
carm acc9ea7e7c Update plugin.yml 2022-02-20 18:08:14 +08:00
carm 83b3c45d57 Update plugin.yml 2022-02-20 00:37:17 +08:00
carm 3ec39b9ca3 [2.4.1] 尝试修复 #9 问题。 2022-02-18 18:26:12 +08:00
carm 821b42cc03 每次构建同时提供“测试产物”与“最终产物”。 2022-02-17 21:42:14 +08:00
carm 81e0c0960f 每次构建同时提供“测试产物”与“最终产物”。 2022-02-17 21:40:52 +08:00
carm 99c577c361 [2.4.0] 添加自动检查更新机制,并允许开关。 2022-02-07 03:27:59 +08:00
carm 5df6e1fe34 [v2.3.3] 修改构建配置 2022-01-16 09:08:10 +08:00
carm 7c887822ba [v2.3.3] [R] 移除“v”前缀。 2022-01-16 08:18:16 +08:00
68 changed files with 2273 additions and 3501 deletions
+31
View File
@@ -0,0 +1,31 @@
# UserPrefix Actions 操作
## 使用方式
`actions` 配置节点下,可以配置多个操作,格式为 `[操作类型] {操作参数}`,例如:
- `[CHAT] HELLO %player_name%!`
- `[SOUND] ENTITY_PLAYER_LEVELUP`
## 操作类型
目前支持以下操作类型:
- `CHAT` 以玩家聊天的形式发送,若需要发送指令则添加“/”前缀
- `CONSOLE` 以后台的形式发送指令
- `MESSAGE` 向玩家发送一条消息
- `SOUND` 向玩家发送声音
- `CLOSE` 关闭当前打开的GUI
所有需要键入文本的类型均支持 [PlaceholderAPI](https://www.spigotmc.org/resources/6245/) 变量 。
## 限定点击类型
若您需要限定玩家点击的类型,如左键、右键等,则可以添加在操作类型后,以“`:`”分割,如:
- `[CLOSE:LEFT]` 代表左键点击关闭
- `[MESSAGE:MIDDLE] HELLO WORLD` 代表鼠标中间点击发送消息
详细点击类型见 [`org.bukkit.event.inventory.ClickType`](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/inventory/ClickType.html) 。
+1
View File
@@ -13,6 +13,7 @@
- 使用示例
- [前缀配置文件预设示例](../src/main/resources/prefixes/example-prefix.yml)
- [操作(Actions)配置](ACTIONS.md)
## [开发文档](JAVADOC-README.md)
-314
View File
@@ -1,314 +0,0 @@
[align=center][attachimg]1905166[/attachimg]
[/align]
[align=left][b][size=6]用户前缀系统插件[/size][/b][/align][align=left][hr][/align][align=left][img]https://www.codefactor.io/repository/github/carmjos/userprefix/badge?s=b76fec1f64726b5f19989aace6adb5f85fdab840[/img] [img]https://github.com/CarmJos/UserPrefix/actions/workflows/maven.yml/badge.svg?branch=master[/img] [img]https://visitor-badge.glitch.me/badge?page_id=userprefix.readme[/img]
[/align]
[align=left]轻便、高效、实时的用户前缀系统。[/align]
[align=left]本插件基于Spigot实现,[b]理论上支持全版本[/b]。[/align]
[align=left][size=5][b]示例[/b][/size][/align][align=left][hr][/align]
[attachimg]1905175[/attachimg]
[align=left][size=5][b]依赖[/b][/size][/align][align=left][hr][/align]
[align=left][list]
[*][size=3][b][必须] [/b][/size]插件本体基于 [url=https://hub.spigotmc.org/stash/projects/SPIGOT][b]Spigot-API[/b][/url] 与 [b][url=http://bukkit.org/]BukkitAPI[/url] [/b]实现。
[*][size=3][b][必须] [/b][/size]数据部分基于 [url=https://www.spigotmc.org/resources/luckperms.28140/][b]LuckPerms[/b][/url] 实现。
[*][size=3][b][推荐][/b] [/size]变量部分基于 [url=https://www.spigotmc.org/resources/6245/][b]PlaceholderAPI[/b][/url] 实现。
[/list]
[/align]详细依赖列表可见 [url=https://github.com/CarmJos/UserPrefix/network/dependencies]软件依赖[/url] 。
[size=5][b]特性[/b][/size]
[hr]
[list]
[*]理论上[b]全版本支持[/b]
[*]游戏内重载配置文件并实时更新到玩家!
[*]当玩家权限变更时会[b]实时监测前缀[/b],若权限不足则[b]自动更换[/b]前缀并提示!
[*]可配置的声音、消息!
[*]前缀图标可配置“选中”、“有权限”与“无权限”三种状态的物品
[*]物品的配置通过ItemStack原生配置,支持MC所有的设定!
[*]TabList自动按照前缀的权重排序 (如有冲突可关掉)
[*]玩家头顶前缀实时显示 (如有冲突可关掉)
[*][b]自动排序[/b],且[b]可翻页[/b]的GUI
[*]支持PlaceholderAPI变量!(凡支持的都可以使用,如BungeeTabListPlus)
[*]支持Hex颜色!(1.16以上版本) 格式 [u]&(#颜色代码)[/u]
[/list][size=5][b]
注意事项[/b][/size]
[hr]
[size=4][b]1. 版本支持问题[/b][/size]
本插件理论全版本支持,如果出现图标不加载、声音无法播放等问题请检查配置文件中物品与声音的type在当前版本是否存在。
以声音举例,村民表示可以的声音在低版本中为 “[color=#000000][backcolor=silver]VILLAGER_YES[/backcolor][/color]”,而在高版本中则变为了“[backcolor=silver][color=#000000]ENTITY_VILLAGER_YES[/color][/backcolor]”。
[size=4][b]2. 计分板异常问题[/b][/size]
头顶上前缀的显示与TabList的排序均使用到了团队计分板API。
如有冲突导致其他插件的计分板无法显示,请关掉配置文件中 [u]functions.OnNamePrefix[/u]。
[size=4][b]3. 物品图标配置问题[/b][/size]
物品相关均通过Bukkit提供的ItemStack序列化方法读取,相关配置方式请参考[url=https://www.spigotmc.org/wiki/itemstack-serialization/]ItemStack Serialization(物品序列化)[/url]。
[size=5][b]指令[/b][/size]
[hr]
本插件指令部分较为简单,大多通过GUI实现。
[code]/UserPrefix 或 /prefix 打开前缀更换GUI
/UserPrefixAdmin 查看管理员指令帮助
/UserPrefixAdmin reload 重载配置文件
/UserPrefixAdmin list 查看已配置的前缀内容[/code]
[size=5][b]变量 (PlaceholderAPI)[/b][/size]
[hr]
安装 [url=https://github.com/PlaceholderAPI/PlaceholderAPI]PlaceholderAPI[/url]后,可以输入 [u]/papi info UserPrefix[/u] 查看相关变量。
变量内容如下
[code]# %UserPrefix_prefix%
- 得到当前正在使用的前缀
# %UserPrefix_weight%
- 得到当前正在使用的前缀权重
# %UserPrefix_identifier%
- 得到当前正在使用的前缀标识
# %UserPrefix_name%
- 得到当前正在使用的前缀名
# %UserPrefix_has_<Identifier>%
- 判断玩家是否拥有某个前缀(true/false)[/code][size=5][b]
配置文件示例[/b][/size]
[hr]
[size=4][b]基础配置文件 [/b][/size][color=#24292f][font=Tahoma][size=4](config.yml)[/size][/font][/color]
[spoiler]
[code]
version: ${project.version} #配置文件版本,若与插件版本不同请记得检查配置文件内容
debug: false
custom-storage:
# 自定义存储位置
# 默认存储位置为 “插件文件夹”/prefixes
# 可以规定到远程文件夹中去寻找前缀相关的设定
# 支持绝对文件路径,如 "/etc/minecraft/configurations/prefixes/"
enable: false # 是否启用
path: "prefixes/" # 一定要指向一个文件夹!
functions:
OnNamePrefix: true # 是否给头顶上添加前缀,该方法用到了头顶的那个计分板,如有冲突请关掉哦~
autoUsePrefix: true # 自动前缀显示 当玩家没有自己选择一个前缀的时候,会自动使用所拥有的的前缀中权重最高的那一个
chat:
# 聊天功能
# - 我不推荐使用本插件的聊天功能,而是建议使用其他的聊天插件。
# - 本插件仅仅提供了**最基本**的格式变量支持,不包含其他任何功能。
# - 注意聊天格式需要遵守Bukkit原格式,即不得缺失 “%1$s” 和 “%2$s” 。
# - 本插件的聊天功能不影响其他插件对聊天事件的操作。
enable: false # 是否启用
format: "<%UserPrefix_prefix%%1$s> %2$s" #聊天的格式,注意 “%1$s” 和 “%2$s” 不可缺少,分别代表 玩家名 与 消息内容 。
GUI:
title: "&f&l我的前缀 &8| 列表"
items:
# 【必须】 GUI中可能存在的其他物品
next-page: # 下一页物品,如果没有下一页则不显示
==: org.bukkit.inventory.ItemStack
type: ARROW
meta:
==: ItemMeta
meta-type: UNSPECIFIC
display-name: "§f下一页"
lore:
- ""
- "§f右键可前往最后一页哦~"
previous-page: # 上一页物品,如果没有上一页则不显示
==: org.bukkit.inventory.ItemStack
type: ARROW
meta:
==: ItemMeta
meta-type: UNSPECIFIC
display-name: "§f上一页"
lore:
- ""
- "§f右键可前往第一页哦~"
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✔ 您正在使用该前缀"
[/code]
[/spoiler]
[b][size=4][backcolor=transparent]消息配置文件[/backcolor][/size] [/b][size=4](messages.yml)[/size]
[spoiler][code]selected:
- "&7您选择了 &f%(name) &7作为当前显示的前缀。"
expired:
- "&7您先前使用的前缀 &f%(oldName) &7已到期。"
- "&7现在已为您重新调整为 &f%(newName) &7。"
reload:
- "&a&l重载完成!&7共耗时 &f%(time)ms&7。"
help:
- "&3&l用户前缀系统 &f帮助"
- "&8#/upa&f list"
- "&8- &7查看当前前缀列表。"
- "&8#/upa&f reload"
- "&8- &7重载前缀配置。"
list-title:
- "&3&l用户前缀系统 &f前缀列表"
list-value:
- "&8#%(weight) &f%(identifier)"
- "&8- &7显示名 &r%(name) &7权限 &r%(permission)"
- "&8- &7内容示例&r %(content) %(sender_name)"[/code][/spoiler]
[size=4][backcolor=transparent][b]前缀配置文件[/b][/backcolor] (prefixes/*.yml) [自2.1.7可自定义目录][/size]
[align=left][font=-apple-system, BlinkMacSystemFont, &quot;][size=3][color=#000000]所有前缀均为单独的配置文件,存放于 [u]插件配置目录/prefixes[/u] 下,便于管理。[/color][/size][/font][/align][align=left][font=-apple-system, BlinkMacSystemFont, &quot;][size=3][color=#000000]文件名理论上可以随便取,推荐使用英文,部分符号可能会影响正常读取,请避免使用。[/color][/size][/font][/align][align=left][spoiler][/align][code]# 唯一标识 [必须]
# 将用于记录玩家所选的前缀,以及用于数据的缓存。
# 必须 必须 必须 保持唯一!
identifier: "pro"
# 名字 [必须]
# 切换的时候左下角会弹提示 用的就是这个名字
name: "&b&lPro&b"
# 内容 [必须]
# 显示在名字前面的内容
content: "§b§lPro §b"
# 权重 [必须]
# 用于GUI、TabList的排序和自动前缀显示
# 在GUI中,权重越高的会显示在越后面
# 在TabList中,权重越高的会显示在越上面
weight: 1
# 检测的权限 [非必须]
# 如果没有就是人人都能用,也代表不用配置“itemNoPermission”了(因为压根不可能显示没权限时候的物品)
permission: "yc.pro"
# 有权限时显示的物品 [必须]
# 当用户有权限且未选中时,会显示该物品
itemHasPermission: #
==: org.bukkit.inventory.ItemStack
type: DIAMOND
meta:
==: ItemMeta
meta-type: UNSPECIFIC
display-name: "§b§lPro §b会员前缀"
lore:
- "§7Pro会员专属称号"
- ""
- "§f尊贵的Pro会员专属称号。"
- "§f您将获得多种特权与更好的游戏体验。"
- ""
- "§a➥ 点击切换到该前缀"
# 正在使用时显示的物品 [非必需]
# 当用户正在使用时会显示这个物品,不配置即自动加载“itemHasPermission”
itemUsing:
==: 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✔ 您正在使用该前缀"
# 没有权限时显示的物品 [非必需]
# 如果没有权限就会显示这个item。如果不配置该物品,则玩家没有使用权限时不会显示在GUI里面。
itemNoPermission:
==: 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+会员以使用该前缀!"[/code][align=left][/spoiler][/align]
[size=5][b]下载地址[/b][/size]
[hr]
[size=3][b]最新版本 [/b][/size][size=3][b]2.1.7 [/b][/size][attach]1919669[/attach][quote][b][size=3]更新内容[/size][/b]
1. 提供自定义配置文件存储位置的功能,便于多子服共享配置。(custome-storage)
2. 添加控制台打开玩家菜单时的反馈(来自 @Yuri南城 )。
3. 支持由控制台为其他玩家打开GUI。
4. 若出现新配置文件条目,将自动写入默认值,便于配置。[/quote][spoiler][size=3][b]2.1.2 [/b][/size][attach]1912028[/attach][quote][b][size=3]更新内容[/size][/b]
1. 修复不开启插件自带的头顶前缀功能时出现的报错。[/quote]
[size=3][b]2.1.0 [/b][/size][attach]1909962[/attach][quote][b][size=3]更新内容[/size][/b]
1. 添加聊天相关设定,支持聊天前缀。(但不推荐使用哦~)[/quote]
[size=3][b] 2.0.0 [/b][/size][attach]1905700[/attach]
[quote][b][size=3]更新内容[/size][/b]
1. 修复低版本可能无法读取物品的bug。
2. 分离配置文件,消息配置文件改为messages.yml,前缀配置文件改 prefixes/*.yml,便于配置和管理。
3. 允许配置GUI中上一页和下一页的物品。
4. 补全帮助文档,在插件首次加载将提供一份英文版的配置,以便使用。[/quote]
[size=3][b] 1.2.5 [/b][/size][attach]1905430[/attach][quote][size=3][b]更新内容[/b]
1. 支持指令的多语言配置。[/size][/quote]
[b][size=3]
1.2.4[/size][size=4] [/size][/b][attach]1905431[/attach]
[quote][size=3][b]更新内容:[/b]
1. 修复closeAll方法中,未移除玩家缓存的bug。
2. 当玩家权限更新时,将关闭玩家的GUI,令其自行打开刷新所有前缀。
3. 允许配置GUI的标题。[/size][/quote]
[size=3][b]1.2.3 [/b][/size][attach]1905406[/attach]
[quote][size=3][b]更新内容:[/b]
[/size][size=3]1. 支持Hex颜色!(1.16以上版本) 格式 [u]&(#颜色代码)[/u] [/size]
[size=3][backcolor=initial]- 如 “[/backcolor][/size][backcolor=initial][size=3]LightSlateBlue [u]&(#8470FF)[/u] ”、 “DarkSlateBlue [u]&(#483D8B)[/u]”。[/size][/backcolor]
[backcolor=initial][size=3]2. 修复未安装PlaceholderAPI的情况下可能导致的部分报错。[/size][/backcolor]
[backcolor=initial][size=3]
[/size][/backcolor]
[/quote]
[/spoiler]
更多版本请移步 [url=https://github.com/CarmJos/UserPrefix/releases]Releases · CarmJos/UserPrefix (github.com)[/url] 。
[size=5][b]开源地址[/b][/size]
[hr]
[size=3]本插件于 [/size][url=https://github.com/CarmJos/UserPrefix]Github[/url][size=3] 开源。[/size]
[size=3]
[/size]
[color=#24292f][font=-apple-system, BlinkMacSystemFont, &quot;][size=16px]本项目源码采用 [/size][/font][/color][url=https://opensource.org/licenses/GPL-3.0]GNU General Public License v3.0[/url][color=#24292f][font=-apple-system, BlinkMacSystemFont, &quot;][size=16px] 开源协议。[/size][/font][/color]
[font=-apple-system, BlinkMacSystemFont][color=#24292f][size=16px]
[/size][/color][/font][size=3]喜欢本插件的话,也可以去Github上给我点个小星星哦~
[/size]
[size=5][b]插件支持[/b][/size]
[hr]
[size=4]本项目由 [url=https://www.mcbbs.net/group-2077-1.html]YourCraft(你的世界)[/url] 团队提供长期支持与维护。
欢迎加入 [url=https://jq.qq.com/?_wv=1027&k=W634w2NT]YourCraft技术交流群[/url] 深入探讨开发问题。[/size]
[attachimg]1905173[/attachimg]
[hr]
[align=center][b][size=4][color=#ff0000]本插件所用所有代码均为原创,不存在借用/抄袭等行为[/color][/size][/b][/align]
-284
View File
@@ -1,284 +0,0 @@
[URL='https://raw.githubusercontent.com/CarmJos/UserPrefix/master/img/banner.png'][IMG]https://raw.githubusercontent.com/CarmJos/UserPrefix/master/img/banner.png[/IMG][/URL]
[SIZE=6][B]UserPrefix Plugin[/B][/SIZE]
[URL='https://www.codefactor.io/repository/github/carmjos/userprefix'][IMG]https://camo.githubusercontent.com/6040f5c07ea04f9cb666459d6b6f08d19971fc29a26183cdc6595297b287234b/68747470733a2f2f7777772e636f6465666163746f722e696f2f7265706f7369746f72792f6769746875622f6361726d6a6f732f757365727072656669782f62616467653f733d62373666656331663634373236623566313939383961616365366164623566383566646162383430[/IMG][/URL] [URL='https://github.com/CarmJos/UserPrefix/actions/workflows/maven.yml'][IMG]https://github.com/CarmJos/UserPrefix/actions/workflows/maven.yml/badge.svg?branch=master[/IMG][/URL] [URL='https://opensource.org/licenses/GPL-3.0'][IMG]https://camo.githubusercontent.com/fd3e53788a319da1b7287d976c2f23fcb8e3d474249af0bf7f8e671f968faebd/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6963656e73652f4361726d4a6f732f55736572507265666978[/IMG][/URL] [URL='https://camo.githubusercontent.com/753f86cc8d2a514a026309afab251b090749751b35409f1e633c77cc0b3f5618/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4d696e6563726166742d4a617661253230312e382d2d4c61746573742d79656c6c6f77'][IMG]https://camo.githubusercontent.com/753f86cc8d2a514a026309afab251b090749751b35409f1e633c77cc0b3f5618/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4d696e6563726166742d4a617661253230312e382d2d4c61746573742d79656c6c6f77[/IMG][/URL] [URL='https://camo.githubusercontent.com/75543844e4f92947d837bd45ab75d5a268e63ff485cd7b354cf69f52e60be28d/68747470733a2f2f76697369746f722d62616467652e676c697463682e6d652f62616467653f706167655f69643d757365727072656669782e726561646d65'][IMG]https://camo.githubusercontent.com/75543844e4f92947d837bd45ab75d5a268e63ff485cd7b354cf69f52e60be28d/68747470733a2f2f76697369746f722d62616467652e676c697463682e6d652f62616467653f706167655f69643d757365727072656669782e726561646d65[/IMG][/URL]
Lightweight, efficient, and real-time user prefix system.
This plugin is implemented based on Spigot ,Theoretically support ALL MineCraft Versions.
The development of this plugin is based on Chinese, which purpose is to help Chinese developers learn Bukkit plugin development.
[QUOTE]
本插件已在 [URL='https://www.mcbbs.net/forum.php?mod=viewthread&tid=1261503']MCBBS[/URL] 上发布,欢迎中文用户来这里下载。
[/QUOTE]
[SIZE=5][B]Examples[/B][/SIZE]
[URL='https://raw.githubusercontent.com/CarmJos/UserPrefix/master/img/using-example.png'][IMG]https://raw.githubusercontent.com/CarmJos/UserPrefix/master/img/using-example.png[/IMG][/URL]
[SIZE=5][B]Dependencies[/B][/SIZE]
[LIST]
[*][B][Necessary][/B] Plugin developed based on [URL='https://hub.spigotmc.org/stash/projects/SPIGOT']Spigot-API[/URL] and [URL='http://bukkit.org/']BukkitAPI[/URL].
[*][B][Necessary][/B] Plugin data storage base on [URL='https://www.spigotmc.org/resources/luckperms.28140/']LuckPerms[/URL].
[*][Recommend] Placeholders based on [URL='https://www.spigotmc.org/resources/6245/']PlaceholderAPI[/URL] .
[/LIST]
For development dependencies, please see [URL='https://github.com/CarmJos/UserPrefix/network/dependencies']Dependencies[/URL] .
[SIZE=5][B]Features[/B][/SIZE]
[LIST]
[*]Theoretically support ALL MineCraft Versions.
[*]Reloading the configuration will automatically refresh the prefix of all players.
[*]Real-time judgment and feedback to the player when permissions are changed.
[*]Configurable sounds and messages.
[*]The prefix icon can be configured as "Selected", "Has Permission" and “No Permission”.
[LIST]
[*]Item configuration is natively configured through ItemStack, which supports all MC settings!
[/LIST]
[*]TabList is automatically sorted according to the weight of the prefix (if there is a conflict, it can be turned off)
[*]The prefix display on the player name (can be turned off if there is a conflict)
[*]GUI with automatic sorting and page turning!
[*]Support PlaceholderAPI variables!
[*]Support Hex color! (Version 1.16 and above) [U]&(#Color)[/U]
[LIST]
[*]Example: LightSlateBlue [U]&(#8470FF)[/U] 、 DarkSlateBlue [U]&(#483D8B)[/U]
[/LIST]
[/LIST]
[SIZE=5][B]Notice[/B][/SIZE]
[SIZE=4][B]1. Version support issues[/B][/SIZE]
This plugin theoretically supports all versions.
If the icon does not load, the sound cannot be played, etc., please check whether the type of the item and sound in the configuration file exists in the current version.
Take the SOUND as an example. The sound that the villager said "OK" is "[U]VILLAGER_YES[/U]" in the lower version, but it becomes "[U]ENTITY_VILLAGER_YES[/U]" in the higher version.
[SIZE=4][B]2. Scoreboard exception problem[/B][/SIZE]
The display of the prefix on the head and the sorting of the TabList both use the scoreboard API.
Please turn of the [U]functions.OnNamePrefix[/U] in the configuration if there is a conflict.
[SIZE=4][B]3. Item icon configuration problem[/B][/SIZE]
Items are read through the ItemStack serialization method provided by Bukkit. For related configuration methods, please refer to [URL='https://www.spigotmc.org/wiki/itemstack-serialization/']ItemStack Serialization[/URL].
[SIZE=5][B]Commands[/B]
[/SIZE]
[code]
/UserPrefix or /prefix #Open prefix GUI
/UserPrefixAdmin # View Admin Command Help
/UserPrefixAdmin reload # Reload Config
/UserPrefixAdmin list # List all configured prefixes.
[/code]
[SIZE=5][B]Placeholders (PlaceholderAPI)[/B][/SIZE]
After installed the [URL='https://github.com/PlaceholderAPI/PlaceholderAPI']PlaceholderAPI[/URL] , you can type /papi info UserPrefix to see all the placeholders.
[code]
# %UserPrefix_prefix%
- Get the content of the current prefix
# %UserPrefix_weight%
- Get the weight of the current prefix.
# %UserPrefix_identifier%
- Get the identifier of the current prefix.
# %UserPrefix_name%
- Get the name of the current prefix.
# %UserPrefix_has_<Identifier>%
- Determine whether the player has a certain prefix(true/false)
[/code]
[SIZE=5][B]Configuration Files[/B][/SIZE]
[B][SIZE=4][URL='https://github.com/CarmJos/UserPrefix/blob/master/src/main/resources/en_US/config.yml']Plugin Configuration[/URL] (config.yml)[/SIZE]
[/B]
Notice: The default configuration is based on Chinese. You can find the English Version [URL='https://github.com/CarmJos/UserPrefix/blob/master/src/main/resources/en_US/config.yml']here[/URL].
[code=YAML]
version: ${project.version}
debug: false #DEBUG OUT PUT
custom-storage:
# Custom storage location
# default location is "./prefixes"
# Support absolute file path , such as "/etc/minecraft/configurations/prefixes/"
enable: false
path: "prefixes/" # Must be a folder!
GUI:
title: "&f&lMy Prefixes List" # Title of the GUI
items:
next-page: # only show has next page
==: org.bukkit.inventory.ItemStack
type: ARROW
meta:
==: ItemMeta
meta-type: UNSPECIFIC
display-name: "§fNext Page"
lore:
- ""
- "§fRight-Click to the last page"
previous-page: # only show has previous page
==: org.bukkit.inventory.ItemStack
type: ARROW
meta:
==: ItemMeta
meta-type: UNSPECIFIC
display-name: "§fPrevious Page"
lore:
- ""
- "§fRight-Click to the first page"
functions:
# Whether to add a prefix to the top of the head,
# this method uses the scoreboard above the head,
# please turn it off if there is a conflict.
OnNamePrefix: true
# Automatic prefix select.
# When the player does not choose a prefix by himself,
# the prefix with the highest weight will be used automatically
autoUsePrefix: true
chat:
# Chat Function
# - I recommend using other chat plugins instead of using this plugin,
# - this plugin only provides very basic chat format placeholders.
# - Notice that: format must has “%1$s” and “%2$s” for PlayerName and Message (Bukkit Chat Event)
enable: false
format: "<%UserPrefix_prefix%%1$s> %2$s"
Sounds:
# Format is [SOUND_NAME:Volume:Pitch] or [SOUND_NAME:Volume] or [SOUND_NAME]
openGUI: "BLOCK_NOTE_BLOCK_PLING:1:1"
guiClick: "UI_BUTTON_CLICK"
prefixChange: "ENTITY_VILLAGER_YES"
prefixExpired: "ENTITY_VILLAGER_NO"
# The default prefix's weight is 0.
defaultPrefix:
name: "Default prefix"
content: "&b"
itemNotUsing:
==: org.bukkit.inventory.ItemStack
type: NAME_TAG
meta:
==: ItemMeta
meta-type: UNSPECIFIC
display-name: "§fThe default prefix §f(Click to select)"
lore:
- ""
- "§a➥ Click to use"
itemUsing:
==: org.bukkit.inventory.ItemStack
type: NAME_TAG
meta:
==: ItemMeta
meta-type: UNSPECIFIC
display-name: "§fThe default prefix"
lore:
- ""
- "§a✔ Selected"
[/code]
[B][SIZE=4][URL='https://github.com/CarmJos/UserPrefix/blob/master/src/main/resources/en_US/messages.yml']Messages Configuration[/URL] (messages.yml)[/SIZE][/B]
[code=YAML]
selected:
- "&7You have selected the &f%(name) &7as current prefix."
expired:
- "&7Your prefix &f%(oldName) &7has expired,"
- "&7Now the prefix is changed to &f%(newName) &7."
reload:
- "&a&lReload completed&7costs &f%(time)ms&7."
help:
- "&3&lUserPrefixAdmin &fHelp"
- "&8#/upa&f list"
- "&8- &7Show configured prefixes."
- "&8#/upa&f reload"
- "&8- &7Reload configuration."
list-title:
- "&3&lUserPrefixAdmin &fList"
list-value:
- "&8#%(weight) &f%(identifier)"
- "&8- &7Name &r%(name) &7Perm &r%(permission)"
- "&8- &7Example&r %(content) %(sender_name)"
[/code]
[B][URL='https://github.com/CarmJos/UserPrefix/blob/master/src/main/resources/en_US/example-prefix.yml']Prefixes [/URL][B][URL='https://github.com/CarmJos/UserPrefix/blob/master/src/main/resources/en_US/example-prefix.yml']Configuration[/URL] [/B](/prefixes/*.yml)
[/B]
All prefixes are separate configuration files, stored in the [U]<Data Folder>/prefixes/[/U] for easy management.
Some symbols in file name may affect reading, please avoid using them.
[code]# identifier [Necessary]
# This will be used for data-storage.
identifier: "pro"
# Name [Necessary]
# Use in messages.
name: "&b&lPro&b"
# Content [Necessary]
# Use in Placeholders
content: "§b§lPro §b"
# Weight [Necessary]
# used for sorting in the GUI and TabList
# In GUI : the larger is displayed at the back
# At TabList : the larger is displayed at the top
weight: 1
# Permission [Unnecessary]
# If there is no permission for detection, everyone can use it,
# which means there is no need to configure "itemNoPermission"
# (because it is impossible to display items without permission at all)
permission: "yc.vip"
# itemHasPermission [Necessary]
# This Item will be displayed when player has permission
itemHasPermission:
==: org.bukkit.inventory.ItemStack
type: DIAMOND
meta:
==: org.bukkit.inventory.ItemStack
type: DIAMOND
meta:
==: ItemMeta
meta-type: UNSPECIFIC
display-name: "§b§lVIP Prefix"
lore:
- ""
- "§a➥ Click to use"
# itemUsing [Unnecessary]
# This Item will be displayed when the prefix is selected.
# If there is no such configuration, it will automatically display "itemHasPermission".
itemUsing:
==: org.bukkit.inventory.ItemStack
type: DIAMOND
meta:
==: ItemMeta
meta-type: UNSPECIFIC
display-name: "§b§lVIP Prefix"
enchants:
PROTECTION_ENVIRONMENTAL: 1 #Add an enchantment so it looks like its selected
lore:
- ""
- "§a✔ Selected"
# itemNoPermission [Unnecessary]
# If player doesn't have the permission,this item will be displayed.
# If this item is not configured, it will not be displayed in the GUI when the player does not have permission to use it.
itemNoPermission:
==: org.bukkit.inventory.ItemStack
type: INK_SACK
damage: 8
meta:
==: ItemMeta
meta-type: UNSPECIFIC
display-name: "§b§lVIP §c(Buy it!)"
lore:
- ""
- "§e✯ Buy the VIP to use it!"[/code]
[SIZE=5][B]Support and Donation[/B][/SIZE]
This project is support by the [URL='https://www.ycraft.cn/']YourCraft(你的世界)[/URL] . [URL='https://raw.githubusercontent.com/CarmJos/UserPrefix/master/img/team-logo.png'][IMG]https://raw.githubusercontent.com/CarmJos/UserPrefix/master/img/team-logo.png[/IMG][/URL]
[SIZE=5][B]Open source agreement[/B][/SIZE]
The source code of this project uses [URL='https://opensource.org/licenses/GPL-3.0']GNU General Public License v3.0[/URL] License.
+11 -9
View File
@@ -1,30 +1,32 @@
---
name: 问题提交
about: 描述问题并提交,帮助我们对其进行检查与修复。
name: 问题提交
about: 描述问题并提交,帮助我们对其进行检查与修复。
title: ''
labels: bug
labels: bug
assignees: ''
---
### **问题简述**
用简短的话语描述一下大概问题。
<!--用简短的话语描述一下大概问题。-->
### **问题来源**
<!--
描述一下通过哪些操作才发现的问题,如:
1. 打开 '...'
2. 点击了 '....'
3. 出现了报错 '....'
-->
### **预期结果**(可选)
如果问题不发生,应该是什么情况
<!--如果问题不发生,应该是什么情况-->
### **问题截图/问题报错**
如果有报错或输出,请提供截图。
<!--如果有报错或输出,请提供截图。-->
### **操作环境**
请在后台输入 `version` 并复制相关输出。
<!--请在后台输入 `version` 并复制相关输出。-->
### **其他补充**
如有其他补充,可以在这里描述。
<!--如有其他补充,可以在这里描述。-->
+4 -4
View File
@@ -8,13 +8,13 @@ assignees: ''
---
### **功能简述**
简单的描述一下你想要的功能
<!--简单的描述一下你想要的功能-->
### **需求来源**
简单的描述一下为什么需要这个功能。
<!--简单的描述一下为什么需要这个功能。-->
### **功能参考**(可选)
如果有相关功能的参考,如文本、截图,请提供给我们。
<!--如果有相关功能的参考,如文本、截图,请提供给我们。-->
### **附加内容**
如果有什么小细节需要重点注意,请在这里告诉我们。
<!--如果有什么小细节需要重点注意,请在这里告诉我们。-->
+4 -4
View File
@@ -38,11 +38,11 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v2
uses: actions/checkout@v6
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
uses: github/codeql-action/init@v4
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
@@ -53,7 +53,7 @@ jobs:
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v1
uses: github/codeql-action/autobuild@v4
# ️ Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
@@ -67,4 +67,4 @@ jobs:
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1
uses: github/codeql-action/analyze@v4
+27 -4
View File
@@ -16,9 +16,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v6
- name: "Set up JDK"
uses: actions/setup-java@v2
uses: actions/setup-java@v5
with:
java-version: '11'
distribution: 'adopt'
@@ -47,7 +47,7 @@ jobs:
run: |
rm -rf docs
mkdir -vp docs
cp -vrf target/apidocs/* docs/
cp -vrf target/reports/apidocs/* docs/
cp -vrf .documentation/JAVADOC-README.md docs/README.md
- name: "Generate the Javadoc sitemap"
@@ -87,4 +87,27 @@ jobs:
- name: "Push javadocs"
run: |
cd docs
git push origin HEAD:gh-pages --force
git push origin HEAD:gh-pages --force
- name: "Modrinth Publish"
id: publish-modrinth
uses: Kir-Antipov/mc-publish@v3.3
with:
modrinth-id: tLcVHCPk
modrinth-featured: true
modrinth-token: ${{ secrets.MODRINTH_TOKEN }}
files: |
asset/*.jar
loaders: |
spigot
paper
purpur
folia
bukkit
game-versions: |
>=1.8 <=1.21.8
game-version-filter: releases
java: |
8
dependencies: |
placeholderapi(recommended)
+10 -5
View File
@@ -14,9 +14,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v6
- name: "Set up JDK"
uses: actions/setup-java@v2
uses: actions/setup-java@v5
with:
java-version: '11'
distribution: 'adopt'
@@ -31,9 +31,14 @@ jobs:
MAVEN_TOKEN: ${{secrets.GITHUB_TOKEN}}
- name: "Target Staging"
run: mkdir staging && cp target/*.jar staging
run: |
mkdir artifacts
cp -vrf target/ artifacts/target/
cp -vrf asset/*.jar artifacts
- name: "Upload artifact"
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v7
with:
name: Artifact
path: staging
path: artifacts
include-hidden-files: true
+9 -14
View File
@@ -1,5 +1,7 @@
![BANNER](.documentation/images/banner.png)
README LANGUAGES [ [中文](README.md) | [**English**](README-en.md) ]
# UserPrefix Plugin
[![CodeFactor](https://www.codefactor.io/repository/github/carmjos/userprefix/badge?s=b76fec1f64726b5f19989aace6adb5f85fdab840)](https://www.codefactor.io/repository/github/carmjos/userprefix)
@@ -18,8 +20,6 @@ development.
> This plugin has been published on [SpigotMC](https://www.spigotmc.org/resources/userprefix.96277/) .
> 本插件已在 [MCBBS](https://www.mcbbs.net/forum.php?mod=viewthread&tid=1261503) 上发布,欢迎中文用户来这里下载。
## Examples
![example](.documentation/images/using-example.png)
@@ -40,7 +40,6 @@ For development dependencies, please see [Dependencies](https://github.com/Carm
- Real-time judgment and feedback to the player when permissions are changed.
- Configurable sounds and messages.
- The prefix icon can be configured as "Selected", "Has Permission" and “No Permission”.
- Item configuration is natively configured through ItemStack, which supports all MC settings!
- TabList is automatically sorted according to the weight of the prefix (if there is a conflict, it can be turned off)
- The prefix display on the player name (can be turned off if there has any conflict)
- Simple Chat Format Placeholder support. (Not Recommended)
@@ -48,6 +47,8 @@ For development dependencies, please see [Dependencies](https://github.com/Carm
- Support PlaceholderAPI variables!
- Support [Hex Color](https://www.hexcolortool.com/)! (Version 1.16 and above) `&(#Color)`
- Example: LightSlateBlue `&(#8470FF)` 、 DarkSlateBlue `&(#483D8B)`
- Support Gradient Color! (Version 1.16 and above) `&<#Color1>Message&<#Color2>`
- Example: `&<#8470FF>Hello World!&<#483D8B>`
## Notice
@@ -65,12 +66,7 @@ becomes "`ENTITY_VILLAGER_YES`" in the higher version.
The display of the prefix on the head and the sorting of the TabList both use the scoreboard API.
Please turn of the `functions.OnNamePrefix` in the configuration if there is a conflict.
### 3. Item icon configuration problem
Items are read through the ItemStack serialization method provided by Bukkit. For related configuration methods, please
refer to [ItemStack Serialization](https://www.spigotmc.org/wiki/itemstack-serialization/).
Please turn of the `functions.on-name-prefix` in the configuration if there is a conflict.
## Commands
@@ -104,14 +100,13 @@ type `/papi info UserPrefix` to see all the placeholders.
## Configuration files
### Plugin Configuration ([`config.yml`](src/main/resources/en_US/config.yml) .
### Plugin Configuration ([`config.yml`]() .
Notice: The default configuration is based on Chinese. You can find
the [English Version here](src/main/resources/en_US/config.yml).
Will be generated on the first boot up.
### Messages Configuration ([`messages.yml`](src/main/resources/en_US/messages.yml))
### Messages Configuration ([`messages.yml`]())
Please see the [Source File](src/main/resources/en_US/messages.yml) .
Will be generated on the first boot up.
### Prefixes Configuration ([`prefixes/*.yml`](src/main/resources/en_US/example-prefix.yml))
+18 -29
View File
@@ -1,5 +1,7 @@
![BANNER](.documentation/images/banner.png)
README LANGUAGES [ [**中文**](README.md) | [English](README-en.md) ]
# 用户前缀系统插件
[![CodeFactor](https://www.codefactor.io/repository/github/carmjos/userprefix/badge?s=b76fec1f64726b5f19989aace6adb5f85fdab840)](https://www.codefactor.io/repository/github/carmjos/userprefix)
@@ -13,9 +15,7 @@
本插件基于Spigot实现,**理论上支持全版本**。
The **English version** of the introduction is [here](README-en.md).
> 本插件已在 [MCBBS](https://www.mcbbs.net/forum.php?mod=viewthread&tid=1261503) 与 [SpigotMC](https://www.spigotmc.org/resources/userprefix-hex-color-support-all-version.96277/) 上发布。
> 本插件已在 [SpigotMC](https://www.spigotmc.org/resources/userprefix-hex-color-support-all-version.96277/) 上发布。
## 示例
@@ -36,16 +36,18 @@ The **English version** of the introduction is [here](README-en.md).
- 当玩家权限变更时会实时监测前缀,若权限不足则自动更换前缀并提示
- 可配置的声音、消息!
- 前缀图标可配置“选中”、“有权限”与“无权限”三种状态的物品
- 物品的配置通过ItemStack原生配置,支持MC所有的设定!
- 具体的设定请参考其他文档哦~
- TabList自动按照前缀的权重排序 (如有冲突可关掉)
- 玩家头顶前缀显示 (如有冲突可关掉)
- 简单的聊天变量修改功能!(不推荐使用) `[自 v2.1.0 版本起]`
- 支持动态前缀内容轮换!`[自 v3.3.0 版本起]`
- 自动排序,且可翻页的GUI
- 支持PlaceholderAPI变量!(凡支持的都可以使用,如BungeeTabListPlus)
- 支持[Hex颜色](https://www.hexcolortool.com/)(1.16以上版本) `[自 v1.2.3 版本起]`
- 格式: `&(#颜色代码)`
- 示例: LightSlateBlue `&(#8470FF)` 、 DarkSlateBlue `&(#483D8B)`
- 支持渐变色!(1.16以上版本) `[自 v1.2.3 版本起]`
- 格式: `&<#颜色代码1>消息内容&<#颜色代码2>`
- 示例: `&<#8470FF>Hello World!&<#483D8B>`
## 注意事项
@@ -59,11 +61,7 @@ The **English version** of the introduction is [here](README-en.md).
头顶上前缀的显示与TabList的排序均使用到了计分板API。
如有冲突导致其他插件的计分板无法显示,请关掉配置文件中`functions.OnNamePrefix`
### 3. 物品图标配置问题
物品相关均通过Bukkit提供的ItemStack序列化方法读取,相关配置方式请参考[ItemStack Serialization(物品序列化)](https://www.spigotmc.org/wiki/itemstack-serialization/)。
如有冲突导致其他插件的计分板无法显示,请关掉配置文件中`functions.on-name-prefix`
## 指令
@@ -97,13 +95,13 @@ The **English version** of the introduction is [here](README-en.md).
## 配置文件示例
### 基础配置文件 ([`config.yml`](src/main/resources/config.yml))
### 基础配置文件 ([`config.yml`](src/main/java/cc/carm/plugin/userprefix/conf/PluginConfig.java))
详见 [源文件](src/main/resources/messages.yml)
将会在服务器首次启动时生成,如果您想要修改配置,请在服务器启动后打开配置文件
### 消息配置文件 ([`messages.yml`](src/main/resources/messages.yml))
### 消息配置文件 ([`messages.yml`](src/main/java/cc/carm/plugin/userprefix/conf/PluginMessages.java))
详见 [源文件](src/main/resources/messages.yml)
将会在服务器首次启动时生成,如果您想要修改配置,请在服务器启动后打开配置文件
### 前缀配置文件 ([`prefixes/*.yml`](src/main/resources/prefixes/example-prefix.yml))
@@ -122,11 +120,10 @@ The **English version** of the introduction is [here](README-en.md).
本项目由 [YourCraft(你的世界)](https://www.ycraft.cn) 团队提供长期支持与维护。
![TeamLogo](.documentation/images/team-logo.png)
若您觉得本插件做的不错,您可以捐赠支持我!
若您觉得本插件做的不错,您可以捐赠支持我!感谢您成为开源项目的支持者!
感谢您成为开源项目的支持者!
<img height=25% width=25% src="https://raw.githubusercontent.com/CarmJos/CarmJos/main/img/donate-code.jpg" alt=""/>
Many thanks to Jetbrains for kindly providing a license for me to work on this and other open-source projects.
[![](https://resources.jetbrains.com/storage/products/company/brand/logos/jb_beam.svg)](https://www.jetbrains.com/?from=https://github.com/CarmJos/UserPrefix)
## 开源协议
@@ -146,18 +143,10 @@ The **English version** of the introduction is [here](README-en.md).
> #### 可自由修改
> 如果你想添加或删除某个功能,没问题,如果你想在别的项目中使用部分代码,也没问题,唯一的要求是,使用了这段代码的项目也必须使用 GPL 协议。
>
> 需要注意的是,分发的时候,需要明确提供源代码和二进制文件,另外,用于某些程序的某些协议有一些问题和限制,你可以看一下 @PierreJoye 写的 Practical Guide to GPL Compliance 一文。使用 GPL 协议,你必须在源代码代码中包含相应信息,以及协议本身。
> 需要注意的是,分发的时候,需要明确提供源代码和二进制文件,另外,用于某些程序的某些协议有一些问题和限制,你可以看一下 @PierreJoye 写的 Practical Guide to GPL Compliance 一文。使用 GPL
> 协议,你必须在源代码代码中包含相应信息,以及协议本身。
>
> *以上文字来自 [五种开源协议GPL,LGPL,BSD,MIT,Apache](https://www.oschina.net/question/54100_9455) 。*
</details>
---
```text
_ _ _____ __ _
| | | | | __ \ / _|(_)
| | | | ___ ___ _ __ | |__) |_ __ ___ | |_ _ __ __
| | | |/ __| / _ \| '__|| ___/| '__|/ _ \| _|| |\ \/ /
| |__| |\__ \| __/| | | | | | | __/| | | | > <
\____/ |___/ \___||_| |_| |_| \___||_| |_|/_/\_\
```
## Project supported by JetBrains
+225 -96
View File
@@ -1,19 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.jdk.version>8</project.jdk.version>
<project.package>cc.carm.plugin.userprefix</project.package>
<maven.compiler.source>${project.jdk.version}</maven.compiler.source>
<maven.compiler.target>${project.jdk.version}</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.encoding>UTF-8</maven.compiler.encoding>
<deps.easyplugin.version>1.5.14</deps.easyplugin.version>
<deps.mineconfig.version>3.1.3</deps.mineconfig.version>
</properties>
<groupId>cc.carm.plugin</groupId>
<artifactId>userprefix</artifactId>
<version>v2.3.2</version>
<version>3.3.1</version>
<name>UserPrefix</name>
<description>轻便、高效、实时的用户前缀系统。</description>
@@ -21,12 +27,12 @@
<issueManagement>
<system>GitHub Issues</system>
<url>${project.url}/issues</url>
<url>https://github.com/CarmJos/UserPrefix/issues</url>
</issueManagement>
<ciManagement>
<system>GitHub Actions</system>
<url>${project.url}/actions/workflows/maven.yml</url>
<url>https://github.com/CarmJos/UserPrefix/actions/workflows/maven.yml</url>
</ciManagement>
<developers>
@@ -55,78 +61,165 @@
<repositories>
<repository>
<id>maven-central</id>
<url>https://repo1.maven.org/maven2/</url>
</repository>
<repository>
<id>sonatype</id>
<url>https://oss.sonatype.org/content/repositories/public/</url>
</repository>
<repository>
<id>spigot-repo</id>
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
</repository>
<repository>
<id>placeholder-api-repo</id>
<url>https://repo.extendedclip.com/content/repositories/placeholderapi/</url>
</repository>
<repository>
<id>oss-repo</id>
<url>https://oss.sonatype.org/content/groups/public/</url>
</repository>
<repository>
<id>luck-repo</id>
<url>https://repo.lucko.me/</url>
</repository>
<repository>
<id>maven-central</id>
<url>https://repo1.maven.org/maven2/</url>
<id>minecraft-libraries</id>
<name>Minecraft Libraries</name>
<url>https://libraries.minecraft.net/</url>
</repository>
<repository>
<id>github</id>
<name>GitHub Packages</name>
<url>https://maven.pkg.github.com/CarmJos/${project.name}</url>
<id>EasyPlugin</id>
<url>https://raw.githubusercontent.com/CarmJos/EasyPlugin/repo/</url>
</repository>
<repository>
<id>carm-repo</id>
<name>Carm's Repo</name>
<url>https://repo.carm.cc/repository/maven-public/</url>
</repository>
</repositories>
<distributionManagement>
<downloadUrl>${project.url}/releases</downloadUrl>
<downloadUrl>https://github.com/CarmJos/UserPrefix/releases</downloadUrl>
<repository>
<id>github</id>
<name>GitHub Packages</name>
<url>https://maven.pkg.github.com/CarmJos/${project.name}</url>
<url>https://maven.pkg.github.com/CarmJos/UserPrefix</url>
</repository>
</distributionManagement>
<dependencies>
<dependency>
<groupId>cc.carm.lib</groupId>
<artifactId>easyplugin-main</artifactId>
<version>${deps.easyplugin.version}</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>cc.carm.lib</groupId>
<artifactId>easyplugin-command</artifactId>
<version>${deps.easyplugin.version}</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>cc.carm.lib</groupId>
<artifactId>easyplugin-gui</artifactId>
<version>${deps.easyplugin.version}</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<!--suppress VulnerableLibrariesLocal -->
<dependency>
<groupId>cc.carm.lib</groupId>
<artifactId>easyplugin-githubchecker</artifactId>
<version>${deps.easyplugin.version}</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>cc.carm.lib</groupId>
<artifactId>easyplugin-placeholderapi</artifactId>
<version>${deps.easyplugin.version}</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>cc.carm.lib</groupId>
<artifactId>mineconfiguration-bukkit</artifactId>
<version>${deps.mineconfig.version}</version>
<scope>compile</scope>
<optional>true</optional>
<exclusions>
<exclusion>
<groupId>com.cryptomorin.xseries</groupId>
<artifactId>XSeries</artifactId>
</exclusion>
<exclusion>
<groupId>com.github.cryptomorin</groupId>
<artifactId>XSeries</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.github.cryptomorin</groupId>
<artifactId>XSeries</artifactId>
<version>13.8.0</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.mojang</groupId>
<artifactId>authlib</artifactId>
<version>6.0.54</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>1.17-R0.1-SNAPSHOT</version>
<version>1.19-R0.1-20220725.090125-47</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>me.clip</groupId>
<artifactId>placeholderapi</artifactId>
<version>2.10.9</version>
<version>2.12.2</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>net.luckperms</groupId>
<artifactId>api</artifactId>
<version>5.3</version>
<version>5.5</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.bstats</groupId>
<artifactId>bstats-bukkit</artifactId>
<version>2.2.1</version>
<version>3.2.1</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.jetbrains</groupId>
<artifactId>annotations</artifactId>
<version>26.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
@@ -137,14 +230,115 @@
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-clean-plugin</artifactId>
<version>3.5.0</version>
<configuration>
<filesets>
<fileset>
<directory>${project.basedir}/asset/</directory>
<useDefaultExcludes>true</useDefaultExcludes>
<includes>
<include>**/*</include>
</includes>
</fileset>
<fileset>
<directory>${project.basedir}/api-docs/</directory>
<useDefaultExcludes>true</useDefaultExcludes>
<includes>
<include>**/*</include>
</includes>
</fileset>
</filesets>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.15.0</version>
<configuration>
<source>${project.jdk.version}</source>
<target>${project.jdk.version}</target>
<encoding>UTF-8</encoding>
<compilerArgument>-parameters</compilerArgument>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.5.0</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.4.0</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>jar-no-fork</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.6.2</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
<configuration>
<finalName>${project.name}-${project.version}</finalName>
<outputDirectory>${project.basedir}/asset/</outputDirectory>
<createDependencyReducedPom>false</createDependencyReducedPom>
<minimizeJar>true</minimizeJar>
<relocations>
<relocation>
<pattern>org.bstats</pattern>
<shadedPattern>${project.package}.bstats</shadedPattern>
</relocation>
<relocation>
<pattern>cc.carm.lib</pattern>
<shadedPattern>${project.package}.lib</shadedPattern>
</relocation>
<relocation>
<pattern>org.json</pattern>
<shadedPattern>${project.package}.lib.json</shadedPattern>
</relocation>
<relocation>
<pattern>com.cryptomorin.xseries</pattern>
<shadedPattern>${project.package}.lib.xseries</shadedPattern>
</relocation>
</relocations>
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/MANIFEST.MF</exclude>
<exclude>META-INF/*.txt</exclude>
</excludes>
</filter>
</filters>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.2.0</version>
<version>3.12.0</version>
<configuration>
<classifier>javadoc</classifier>
<links>
<link>https://javadoc.io/doc/org.jetbrains/annotations/</link>
<link>https://hub.spigotmc.org/javadocs/bukkit/</link>
<link>https://carmjos.github.io/EasyPlugin/</link>
<link>https://carmjos.github.io/MineConfiguration/</link>
</links>
<detectJavaApiLink>true</detectJavaApiLink>
<encoding>UTF-8</encoding>
@@ -161,75 +355,10 @@
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
<compilerArgument>-parameters</compilerArgument>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.2.0</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.2.0</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>jar-no-fork</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.3</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<finalName>${project.name}-${project.version}</finalName>
<outputDirectory>${project.basedir}/asset/</outputDirectory>
<createDependencyReducedPom>false</createDependencyReducedPom>
</configuration>
</execution>
</executions>
<configuration>
<relocations>
<relocation>
<pattern>org.bstats</pattern>
<!-- Replace this with your package! -->
<shadedPattern>cc.carm.plugin.userprefix.bstats</shadedPattern>
</relocation>
</relocations>
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/MANIFEST.MF</exclude>
<exclude>META-INF/*.txt</exclude>
</excludes>
</filter>
</filters>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.1</version>
<version>3.5.6</version>
<configuration>
<useSystemClassLoader>false</useSystemClassLoader>
</configuration>
@@ -244,4 +373,4 @@
</build>
</project>
</project>
+13
View File
@@ -0,0 +1,13 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"config:base","mergeConfidence:all-badges"
],
"packageRules": [
{
"description": "Automerge non-major updates",
"matchUpdateTypes": ["minor", "patch"],
"automerge": true
}
]
}
+116 -117
View File
@@ -1,160 +1,159 @@
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.PluginConfig;
import cc.carm.lib.easyplugin.EasyPlugin;
import cc.carm.lib.easyplugin.gui.GUI;
import cc.carm.lib.easyplugin.gui.paged.AutoPagedGUI;
import cc.carm.lib.easyplugin.updatechecker.GHUpdateChecker;
import cc.carm.lib.easyplugin.utils.MessageUtils;
import cc.carm.plugin.userprefix.command.AdminCommand;
import cc.carm.plugin.userprefix.command.UserCommand;
import cc.carm.plugin.userprefix.conf.PluginConfig;
import cc.carm.plugin.userprefix.folia.FoliaScheduler;
import cc.carm.plugin.userprefix.hooker.UserPrefixExpansion;
import cc.carm.plugin.userprefix.listener.ChatListener;
import cc.carm.plugin.userprefix.listener.UserListener;
import cc.carm.plugin.userprefix.listener.processor.UserNodeUpdateProcessor;
import cc.carm.plugin.userprefix.listener.UserPermListener;
import cc.carm.plugin.userprefix.manager.ConfigManager;
import cc.carm.plugin.userprefix.manager.PrefixManager;
import cc.carm.plugin.userprefix.manager.ServiceManager;
import cc.carm.plugin.userprefix.manager.UserManager;
import cc.carm.plugin.userprefix.util.ColorParser;
import cc.carm.plugin.userprefix.util.MessageUtil;
import cc.carm.plugin.userprefix.ui.PrefixSelectGUI;
import net.luckperms.api.event.user.UserDataRecalculateEvent;
import org.bstats.bukkit.Metrics;
import org.bstats.charts.SimplePie;
import org.bstats.charts.SingleLineChart;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.PluginCommand;
import org.bukkit.command.TabCompleter;
import org.bukkit.event.Listener;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.java.JavaPlugin;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class Main extends JavaPlugin {
public class Main extends EasyPlugin {
private static Main instance;
private static Main instance;
@Override
public void onEnable() {
instance = this;
showPluginName();
log(getName() + " " + getDescription().getVersion() + " &7开始加载...");
long startTime = System.currentTimeMillis();
protected FoliaScheduler foliaScheduler;
protected boolean onFolia;
log("加载配置文件...");
ConfigManager.initConfig();
PrefixManager.init();
protected ConfigManager configManager;
protected PrefixManager prefixManager;
protected UserManager userManager;
log("注册指令...");
registerCommand("UserPrefix", new UserPrefixCommand());
registerCommand("UserPrefixAdmin", new UserPrefixAdminCommand());
@Override
protected boolean initialize() {
instance = this;
log("注册监听器...");
regListener(new UserListener());
regListener(new ChatListener());
ServiceManager.getService().getEventBus().subscribe(this, UserDataRecalculateEvent.class, UserNodeUpdateProcessor::process);
try {
Class.forName("io.papermc.paper.threadedregions.RegionizedServer");
this.onFolia = true;
} catch (ClassNotFoundException e) {
this.onFolia = false;
}
this.foliaScheduler = new FoliaScheduler(this, this.onFolia);
if (MessageUtil.hasPlaceholderAPI()) {
log("注册变量...");
new UserPrefixExpansion(getInstance()).register();
} else {
log("未安装 PlaceholderAPI 不进行变量注册...");
log("若您想使用变量进行前缀的显示,请安装PlaceholderAPI");
}
log("加载插件配置...");
this.configManager = new ConfigManager(getDataFolder());
this.prefixManager = new PrefixManager();
if (PluginConfig.METRICS.get()) {
log("启用统计数据...");
Metrics metrics = new Metrics(this, 13776);
metrics.addCustomChart(new SingleLineChart("active_prefixes", () -> PrefixManager.getPrefixes().size()));
metrics.addCustomChart(new SimplePie("custom_storage", () -> PluginConfig.CustomStorage.ENABLE.get() ? "ENABLE" : "DISABLE"));
metrics.addCustomChart(new SimplePie("lp_version", () -> ServiceManager.getService().getPluginMetadata().getVersion()));
metrics.addCustomChart(new SimplePie("papi_version", () -> {
Plugin plugin = Bukkit.getPluginManager().getPlugin("PlaceholderAPI");
if (plugin == null) return "Not installed";
else return plugin.getDescription().getVersion();
}));
}
log("加载前缀配置...");
int loaded = prefixManager.loadPrefixes();
log(" 成功加载了 " + loaded + " 个前缀配置。");
log("加载完成 ,共耗时 " + (System.currentTimeMillis() - startTime) + " ms 。");
log("加载用户管理器...");
this.userManager = new UserManager();
showAD();
log("注册指令...");
registerCommand("UserPrefix", new UserCommand());
registerCommand("UserPrefixAdmin", new AdminCommand(this));
if (Bukkit.getOnlinePlayers().size() > 0) {
Bukkit.getOnlinePlayers().forEach(UserManager::initPlayer); // 适配热重载
}
log("注册监听器...");
registerListener(new UserListener());
registerListener(new ChatListener());
ServiceManager.getService().getEventBus().subscribe(
this, UserDataRecalculateEvent.class,
UserPermListener::process
);
log("初始化GUI管理器...");
GUI.initialize(this);
AutoPagedGUI.defaultNextPage = (PluginConfig.GUI.BOTTOMS.NEXT_PAGE::get);
AutoPagedGUI.defaultPreviousPage = (PluginConfig.GUI.BOTTOMS.PREV_PAGE::get);
}
if (MessageUtils.hasPlaceholderAPI()) {
log("注册变量...");
new UserPrefixExpansion(this, getName()).register();
} else {
log("未安装 PlaceholderAPI ,跳过变量注册...");
log("若您想使用变量进行前缀的显示,请安装PlaceholderAPI");
}
@Override
public void onDisable() {
showPluginName();
log(getName() + " " + getDescription().getVersion() + " 开始卸载...");
long startTime = System.currentTimeMillis();
if (PluginConfig.METRICS.getNotNull()) {
log("启用统计数据...");
Metrics metrics = new Metrics(this, 13776);
metrics.addCustomChart(new SingleLineChart("active_prefixes", () -> prefixManager.getPrefixes().size()));
metrics.addCustomChart(new SimplePie("custom_storage", () -> PluginConfig.CUSTOM_STORAGE.ENABLE.getNotNull() ? "ENABLE" : "DISABLE"));
metrics.addCustomChart(new SimplePie("lp_version", () -> ServiceManager.getService().getPluginMetadata().getVersion()));
metrics.addCustomChart(new SimplePie("papi_version", () -> {
Plugin plugin = Bukkit.getPluginManager().getPlugin("PlaceholderAPI");
if (plugin == null) return "Not installed";
else return plugin.getDescription().getVersion();
}));
}
log("卸载监听器...");
Bukkit.getServicesManager().unregisterAll(this);
if (PluginConfig.CHECK_UPDATE.getNotNull()) {
log("开始检查更新...");
this.foliaScheduler.runAsync(GHUpdateChecker.runner(this));
} else {
log("已禁用检查更新,跳过。");
}
log("卸载完成 ,共耗时 " + (System.currentTimeMillis() - startTime) + " ms 。");
if (PluginConfig.FUNCTIONS.NAME_PREFIX.ENABLE.getNotNull() && this.isOnFolia()) {
log("插件正运行在 Folia 服务端上,头顶前缀功能不可用");
}
Bukkit.getOnlinePlayers().forEach(userManager::initPlayer); // 适配热重载
showAD();
log("&7感谢您使用 &3&lUserPrefix " + getDescription().getVersion() + "&7!");
log("&7本插件由 &b&lYourCraft &7提供长期支持与维护。");
return true;
}
}
@Override
protected void shutdown() {
/**
* 注册监听器
*
* @param listener 监听器
*/
public static void regListener(Listener listener) {
Bukkit.getPluginManager().registerEvents(listener, getInstance());
}
log("关闭已被打开的GUI...");
Bukkit.getOnlinePlayers().stream().filter(GUI::hasOpenedGUI).forEach(player -> {
player.closeInventory();
GUI.removeOpenedGUI(player);
PrefixSelectGUI.removeOpening(player);
});
public static void log(String message) {
Bukkit.getConsoleSender().sendMessage(ColorParser.parse("[" + getInstance().getName() + "] " + message));
}
log("&7感谢您使用 &3&lUserPrefix " + getDescription().getVersion() + "&7!");
log("&7本插件由 &b&lYourCraft &7提供长期支持与维护。");
}
public static void debug(String message) {
if (PluginConfig.DEBUG.get()) {
log("[DEBUG] " + message);
}
}
public boolean isOnFolia() {
return this.onFolia;
}
public static void error(String message) {
log("&c[ERROR] &r" + message);
}
public FoliaScheduler getFoliaScheduler() {
return this.foliaScheduler;
}
@Override
public boolean isDebugging() {
return PluginConfig.DEBUG.getNotNull();
}
public static JavaPlugin getInstance() {
return instance;
}
public static void info(String... message) {
getInstance().log(message);
}
public static void debugging(String... message) {
getInstance().debug(message);
}
public static void registerCommand(String commandName,
@NotNull CommandExecutor executor) {
registerCommand(commandName, executor, null);
}
public static void registerCommand(String commandName,
@NotNull CommandExecutor executor,
@Nullable TabCompleter tabCompleter) {
PluginCommand command = Bukkit.getPluginCommand(commandName);
if (command == null) return;
command.setExecutor(executor);
if (tabCompleter != null) command.setTabCompleter(tabCompleter);
}
private void showPluginName() {
log("&b _ _ &f _____ __ _ ");
log("&b| | | | &f| __ \\ / _|(_) ");
log("&b| | | | ___ ___ _ __ &f| |__) |_ __ ___ | |_ _ __ __");
log("&b| | | |/ __| / _ \\| '__|&f| ___/| '__|/ _ \\| _|| |\\ \\/ /");
log("&b| |__| |\\__ \\| __/| | &f| | | | | __/| | | | > < ");
log("&b \\____/ |___/ \\___||_| &f|_| |_| \\___||_| |_|/_/\\_\\");
log("&8 ");
log("&8> &f" + getDescription().getWebsite());
}
private void showAD() {
log("&7感谢您使用 &3&lUserPrefix " + getDescription().getVersion() + "&7!");
log("&7本插件由 &b&lYourCraft &7提供长期支持与维护。");
}
public static void severe(String... message) {
getInstance().error(message);
}
public static Main getInstance() {
return instance;
}
}
@@ -0,0 +1,26 @@
package cc.carm.plugin.userprefix;
import cc.carm.plugin.userprefix.conf.prefix.PrefixConfig;
import cc.carm.plugin.userprefix.manager.ConfigManager;
import cc.carm.plugin.userprefix.manager.PrefixManager;
import cc.carm.plugin.userprefix.manager.UserManager;
public class UserPrefixAPI {
public static PrefixManager getPrefixManager() {
return Main.getInstance().prefixManager;
}
public static ConfigManager getConfigManager() {
return Main.getInstance().configManager;
}
public static UserManager getUserManager() {
return Main.getInstance().userManager;
}
public static PrefixConfig getDefaultPrefix(){
return getPrefixManager().getDefaultPrefix();
}
}
@@ -0,0 +1,38 @@
package cc.carm.plugin.userprefix.command;
import cc.carm.lib.easyplugin.command.CommandHandler;
import cc.carm.plugin.userprefix.command.admin.ListCommand;
import cc.carm.plugin.userprefix.command.admin.ReloadCommand;
import cc.carm.plugin.userprefix.command.admin.SetCommand;
import cc.carm.plugin.userprefix.conf.PluginMessages;
import org.bukkit.command.CommandSender;
import org.bukkit.plugin.java.JavaPlugin;
import org.jetbrains.annotations.NotNull;
public class AdminCommand extends CommandHandler {
public AdminCommand(@NotNull JavaPlugin plugin) {
super(plugin);
registerSubCommand(new ListCommand(this, "list", "l"));
registerSubCommand(new SetCommand(this, "set"));
registerSubCommand(new ReloadCommand(this, "reload"));
}
@Override
public Void noArgs(CommandSender sender) {
return help(sender);
}
@Override
public Void noPermission(CommandSender sender) {
PluginMessages.COMMAND_USAGE.NO_PERM.sendTo(sender);
return null;
}
public static Void help(CommandSender sender) {
PluginMessages.COMMAND_USAGE.ADMIN.sendTo(sender);
return null;
}
}
@@ -1,5 +1,6 @@
package cc.carm.plugin.userprefix.command;
import cc.carm.plugin.userprefix.conf.PluginMessages;
import cc.carm.plugin.userprefix.ui.PrefixSelectGUI;
import org.bukkit.Bukkit;
import org.bukkit.command.Command;
@@ -8,7 +9,7 @@ import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
public class UserPrefixCommand implements CommandExecutor {
public class UserCommand implements CommandExecutor {
@Override
@@ -17,7 +18,7 @@ public class UserPrefixCommand implements CommandExecutor {
PrefixSelectGUI.open((Player) sender);
} else {
if (strings.length != 1) {
sender.sendMessage("输入 /prefix <ID> 为玩家打开前缀GUI。");
PluginMessages.COMMAND_USAGE.CONSOLE.sendTo(sender);
} else {
Player player = Bukkit.getPlayer(strings[0]);
if (player != null) {
@@ -1,73 +0,0 @@
package cc.carm.plugin.userprefix.command;
import cc.carm.plugin.userprefix.configuration.PluginConfig;
import cc.carm.plugin.userprefix.manager.ConfigManager;
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.ui.PrefixSelectGUI;
import cc.carm.plugin.userprefix.util.MessageUtil;
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")) {
MessageUtil.sendWithPlaceholders(sender, PluginConfig.Messages.LIST_TITLE.get());
for (ConfiguredPrefix value : PrefixManager.getPrefixes().values()) {
MessageUtil.sendWithPlaceholders(
sender, PluginConfig.Messages.LIST_VALUE.get(),
new String[]{
"%(weight)", "%(identifier)",
"%(name)", "%(permission)",
"%(content)", "%(sender_name)"
},
new Object[]{
value.getWeight(), value.getIdentifier(),
value.getName(), value.getPermission(),
value.getContent(), sender.getName()
}
);
}
return true;
} else if (aim.equalsIgnoreCase("reload")) {
long s1 = System.currentTimeMillis();
PrefixSelectGUI.closeAll(); // 关掉所有正在显示的前缀列表
ConfigManager.reload(); // 重载配置文件
PrefixManager.loadPrefixes(); //加载重载后了的前缀配置
for (Player onlinePlayer : Bukkit.getOnlinePlayers()) {
UserManager.checkPrefix(onlinePlayer, false);
/*
* 这里关掉loadOthers(为其他玩家更新)了。
* 因为每个玩家更新的时候会为其他人更新自己,
* 全部走完一遍后,所有玩家都会加载最新的前缀内容。
*/
UserManager.updatePrefixView(onlinePlayer, false);
}
MessageUtil.sendWithPlaceholders(
sender, PluginConfig.Messages.RELOAD.get(),
new String[]{"%(time)"}, new Object[]{(System.currentTimeMillis() - s1)}
);
return true;
}
return help(sender);
}
return help(sender);
}
public static boolean help(CommandSender sender) {
MessageUtil.send(sender, PluginConfig.Messages.HELP.get());
return true;
}
}
@@ -0,0 +1,34 @@
package cc.carm.plugin.userprefix.command.admin;
import cc.carm.lib.easyplugin.command.SubCommand;
import cc.carm.plugin.userprefix.UserPrefixAPI;
import cc.carm.plugin.userprefix.command.AdminCommand;
import cc.carm.plugin.userprefix.conf.PluginMessages;
import cc.carm.plugin.userprefix.conf.prefix.PrefixConfig;
import org.bukkit.command.CommandSender;
import org.bukkit.plugin.java.JavaPlugin;
import org.jetbrains.annotations.NotNull;
import java.util.Optional;
public class ListCommand extends SubCommand<AdminCommand> {
public ListCommand(@NotNull AdminCommand parent, String name, String... aliases) {
super(parent, name, aliases);
}
@Override
public Void execute(JavaPlugin plugin, CommandSender sender, String[] args) {
PluginMessages.LIST.HEADER.sendTo(sender);
for (PrefixConfig value : UserPrefixAPI.getPrefixManager().getPrefixes().values()) {
PluginMessages.LIST.VALUE.sendTo(sender,
value.getWeight(), value.getIdentifier(),
value.getName(), Optional.ofNullable(value.getPermission()).orElse("~"),
value.getContent(sender), sender.getName()
);
}
return null;
}
}
@@ -0,0 +1,43 @@
package cc.carm.plugin.userprefix.command.admin;
import cc.carm.lib.easyplugin.command.SubCommand;
import cc.carm.plugin.userprefix.UserPrefixAPI;
import cc.carm.plugin.userprefix.command.AdminCommand;
import cc.carm.plugin.userprefix.conf.PluginMessages;
import cc.carm.plugin.userprefix.ui.PrefixSelectGUI;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.plugin.java.JavaPlugin;
import org.jetbrains.annotations.NotNull;
public class ReloadCommand extends SubCommand<AdminCommand> {
public ReloadCommand(@NotNull AdminCommand parent, String name, String... aliases) {
super(parent, name, aliases);
}
@Override
public Void execute(JavaPlugin plugin, CommandSender sender, String[] args) {
long s1 = System.currentTimeMillis();
PrefixSelectGUI.closeAll(); // 关掉所有正在显示的前缀列表
try {
UserPrefixAPI.getConfigManager().reload(); // 重载配置文件
int num = UserPrefixAPI.getPrefixManager().loadPrefixes(); //加载重载后了的前缀配置
for (Player onlinePlayer : Bukkit.getOnlinePlayers()) {
UserPrefixAPI.getUserManager().checkPrefix(onlinePlayer, false);
/*
* 这里关掉loadOthers(为其他玩家更新)了。
* 因为每个玩家更新的时候会为其他人更新自己,
* 全部走完一遍后,所有玩家都会加载最新的前缀内容。
*/
UserPrefixAPI.getUserManager().updatePrefixView(onlinePlayer, false);
}
PluginMessages.RELOAD.SUCCESS.sendTo(sender, System.currentTimeMillis() - s1, num);
} catch (Exception e) {
PluginMessages.RELOAD.FAILED.sendTo(sender, e.getMessage());
e.printStackTrace();
}
return null;
}
}
@@ -0,0 +1,54 @@
package cc.carm.plugin.userprefix.command.admin;
import cc.carm.lib.easyplugin.command.SubCommand;
import cc.carm.plugin.userprefix.UserPrefixAPI;
import cc.carm.plugin.userprefix.command.AdminCommand;
import cc.carm.plugin.userprefix.conf.PluginMessages;
import cc.carm.plugin.userprefix.conf.prefix.PrefixConfig;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.plugin.java.JavaPlugin;
import org.jetbrains.annotations.NotNull;
public class SetCommand extends SubCommand<AdminCommand> {
public SetCommand(@NotNull AdminCommand parent, String name, String... aliases) {
super(parent, name, aliases);
}
@Override
public Void execute(JavaPlugin plugin, CommandSender sender, String[] args) {
if (args.length < 2) return getParent().noArgs(sender);
Player target = Bukkit.getPlayer(args[0]);
if (target == null) {
PluginMessages.NOT_ONLINE.sendTo(sender, args[0]);
return null;
}
PrefixConfig prefixConfig;
String prefixInput = args[1];
if (prefixInput.equalsIgnoreCase("default")) {
prefixConfig = UserPrefixAPI.getDefaultPrefix();
} else {
prefixConfig = UserPrefixAPI.getPrefixManager().getPrefix(prefixInput);
}
if (prefixConfig == null) {
PluginMessages.SET.PREFIX_NOT_FOUND.sendTo(sender, prefixInput);
return null;
}
if (!prefixConfig.checkPermission(target)) {
PluginMessages.SET.NO_PERM.sendTo(sender, target.getName(), prefixConfig.getName());
return null;
}
UserPrefixAPI.getUserManager().setPrefix(target, prefixConfig, true);
PluginMessages.SET.SUCCESS.sendTo(sender, target.getName(), prefixConfig.getName());
return null;
}
}
@@ -0,0 +1,213 @@
package cc.carm.plugin.userprefix.conf;
import cc.carm.lib.configuration.Configuration;
import cc.carm.lib.configuration.adapter.ValueType;
import cc.carm.lib.configuration.annotation.ConfigPath;
import cc.carm.lib.configuration.annotation.HeaderComments;
import cc.carm.lib.configuration.value.standard.ConfiguredList;
import cc.carm.lib.configuration.value.standard.ConfiguredValue;
import cc.carm.lib.mineconfiguration.bukkit.value.ConfiguredSound;
import cc.carm.lib.mineconfiguration.bukkit.value.item.ConfiguredItem;
import cc.carm.plugin.userprefix.conf.gui.GUIItems;
import cc.carm.plugin.userprefix.folia.MajorUtil;
import org.bukkit.Material;
import org.bukkit.inventory.ItemFlag;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
public class PluginConfig implements Configuration {
@HeaderComments({"开发者查错模式"})
public static final ConfiguredValue<Boolean> DEBUG = ConfiguredValue.of(Boolean.class, false);
@HeaderComments({
"统计数据设定",
" 该选项用于帮助开发者统计插件版本与使用情况,且绝不会影响性能与使用体验。",
" 当然,您也可以选择在这里关闭,或在plugins/bStats下的配置文件中关闭。"
})
public static final ConfiguredValue<Boolean> METRICS = ConfiguredValue.of(Boolean.class, true);
@HeaderComments({
"检查更新设定",
"该选项用于插件判断是否要检查更新,若您不希望插件检查更新并提示您,可以选择关闭。",
"检查更新为异步操作,绝不会影响性能与使用体验。"
})
public static final ConfiguredValue<Boolean> CHECK_UPDATE = ConfiguredValue.of(Boolean.class, true);
@HeaderComments({"自定义存储位置设定", "可以规定到远程文件夹中去加载前缀配置"})
public static final class CUSTOM_STORAGE implements Configuration {
@HeaderComments({"是否启用自定义存储位置"})
public static final ConfiguredValue<Boolean> ENABLE = ConfiguredValue.of(Boolean.class, false);
@HeaderComments({
"目标存储路径,必须指向一个文件夹。",
"默认存储位置为 “插件文件夹”/prefixes",
"支持绝对文件路径,如 \"/etc/minecraft/configurations/prefixes/\""
})
public static final ConfiguredValue<String> PATH = ConfiguredValue.of(String.class, "prefixes/");
}
@HeaderComments("功能设定")
public static class FUNCTIONS implements Configuration {
@ConfigPath("on-name-prefix")
@HeaderComments({"头顶与TabList前缀功能,该方法用到了玩家名计分板接口,如有冲突请关掉哦~"})
public static final class NAME_PREFIX implements Configuration {
@HeaderComments("是否开启本功能")
public static final ConfiguredValue<Boolean> ENABLE = ConfiguredValue.of(Boolean.class, true);
@HeaderComments("是否按降序排列,即权重越高的前缀显示在越上面;若为false则按升序排列。")
public static final ConfiguredValue<Boolean> ORDER_DESC = ConfiguredValue.of(Boolean.class, true);
}
@ConfigPath("auto-prefix-use")
@HeaderComments("自动使用前缀,即当玩家没有自己选择一个前缀的时候,会自动使用所拥有的的前缀中权重最高的那一个")
public static final ConfiguredValue<Boolean> AUTO_USE = ConfiguredValue.of(Boolean.class, true);
@HeaderComments({
"聊天功能设定",
"- 我不推荐使用本插件的聊天功能,而是建议使用其他的聊天插件。",
"- 本插件仅仅提供了**最基本**的格式变量支持,不包含其他任何功能。",
"- 注意聊天格式需要遵守Bukkit原格式,即不得缺失 “%1$s” 和 “%2$s” 。",
"- 本插件的聊天功能不影响其他插件对聊天事件的操作。"
})
public static final class CHAT implements Configuration {
@HeaderComments("是否开启本功能")
public static final ConfiguredValue<Boolean> ENABLE = ConfiguredValue.of(Boolean.class, false);
@HeaderComments({
"聊天的格式,注意 “%1$s” 和 “%2$s” 不可缺少。",
"- %1$s -> 玩家名", "- %2$s -> 聊天内容"
})
public static final ConfiguredValue<String> FORMAT = ConfiguredValue.of(String.class, "<%1$s> %2$s");
}
}
@HeaderComments({"前缀GUI界面设定"})
public static class GUI implements Configuration {
@HeaderComments("GUI的标题")
public static final ConfiguredValue<String> TITLE = ConfiguredValue.of(String.class, "&f&l我的前缀 &8| 列表");
@HeaderComments("GUI中的基本按钮物品")
public static final class BOTTOMS implements Configuration {
@HeaderComments("前往下一页的物品 (只有存在下一页时才会显示)")
public static final ConfiguredItem NEXT_PAGE = ConfiguredItem.create()
.defaultType(Material.ARROW)
.defaultName("下一页")
.defaultLore("&7&o右键可前往最后一页哦")
.build();
@ConfigPath("previous-page")
@HeaderComments({"前往上一页时的物品 (只有当前页不是第一页时才会显示)"})
public static final ConfiguredItem PREV_PAGE = ConfiguredItem.create()
.defaultType(Material.ARROW)
.defaultName("上一页")
.defaultLore("&7&o右键可前往第一页哦")
.build();
}
@HeaderComments("GUI中的其他按钮物品 (若与现有物品位置冲突,将被覆盖)")
public static final ConfiguredValue<GUIItems> ITEMS = ConfiguredValue
.builderOf(GUIItems.class).fromSection()
.defaults(GUIItems::defaults)
.serialize(GUIItems::serialize)
.parse(GUIItems::parse)
.build();
}
@HeaderComments({"相关的声音,留空则不播放声音", "格式为 【声音名:音量:音调】 或 【声音名:音量】 或 【声音名】"})
public static final class SOUNDS implements Configuration {
public static final ConfiguredSound GUI_OPEN = ConfiguredSound.of("BLOCK_NOTE_BLOCK_PLING", 0.5F, 0.8F);
public static final ConfiguredSound GUI_CLICK = ConfiguredSound.of("UI_BUTTON_CLICK");
public static final ConfiguredSound PREFIX_CHANGE = ConfiguredSound.of("ENTITY_VILLAGER_YES");
public static final ConfiguredSound PREFIX_EXPIRED = ConfiguredSound.of("ENTITY_VILLAGER_NO");
}
@HeaderComments({"默认前缀配置"})
public static final class DEFAULT_PREFIX implements Configuration {
@HeaderComments("默认前缀的显示名称,用于在消息提示中显示。")
public static final ConfiguredValue<String> NAME = ConfiguredValue.of(String.class, "默认前缀");
@HeaderComments({"默认前缀的描述信息。"})
public static final ConfiguredList<String> DESCRIPTION = ConfiguredList.builderOf(String.class).fromString()
.defaults("&7这是一个默认前缀", "&7您可以在前缀列表中选择其他前缀")
.build();
@HeaderComments({"默认前缀的权重,默认为0。"})
public static final ConfiguredValue<Integer> WEIGHT = ConfiguredValue.of(Integer.class, 0);
@HeaderComments({"默认前缀的内容,即用于显示的实际前缀"})
public static final ConfiguredValue<List<String>> CONTENT = ConfiguredValue
.builderOf(ValueType.ofList(String.class))
.from(Object.class)
.parse(obj -> {
if (obj instanceof List<?>) {
List<?> list = (List<?>) obj;
return list.stream()
.map(Object::toString)
.collect(Collectors.toList());
} else return Collections.singletonList(String.valueOf(obj));
})
.serialize(list -> {
if (list.isEmpty()) {
return "";
} else if (list.size() == 1) {
return list.get(0);
} else {
return list;
}
})
.defaults(Collections.singletonList("&r"))
.build();
@HeaderComments({"默认前缀的切换周期,单位为毫秒。(小于0则表示不进行切换,等于0则代表所及选取)"})
public static final ConfiguredValue<Long> PERIOD = ConfiguredValue.of(-1L);
@HeaderComments({"选择默认前缀时执行的操作"})
public static final ConfiguredList<String> ACTIONS = ConfiguredList.builderOf(String.class).fromString()
.defaults("[CONSOLE] " + "say %player_name% 选择了默认前缀")
.build();
@HeaderComments({"默认前缀的显示物品"})
public static final class ITEM implements Configuration {
@HeaderComments({"当未选择默认前缀时显示的物品"})
public static final ConfiguredItem NOT_USING = ConfiguredItem.create()
.defaultType(Material.NAME_TAG)
.defaultName("&f默认玩家前缀 &f(点击切换)")
.defaultLore("", "{&f&o }#description#{1,1}", "&a➥ 点击切换到该前缀")
.build();
@HeaderComments({"当选择了默认前缀时显示的物品"})
public static final ConfiguredItem USING = ConfiguredItem.create()
.defaultType(Material.NAME_TAG)
.defaultEnchant(MajorUtil.getEnchantProtection(), 1) // 附魔改过名
.defaultFlags(ItemFlag.HIDE_ENCHANTS)
.defaultName("&f默认玩家前缀")
.defaultLore("", "{&f&o }#description#{1,1}", "&a✔ 您正在使用该前缀")
.build();
}
}
}
@@ -0,0 +1,94 @@
package cc.carm.plugin.userprefix.conf;
import cc.carm.lib.configuration.Configuration;
import cc.carm.lib.configuration.annotation.HeaderComments;
import cc.carm.lib.mineconfiguration.bukkit.value.ConfiguredMessage;
public class PluginMessages implements Configuration {
public static final class COMMAND_USAGE implements Configuration {
public static final ConfiguredMessage<String> CONSOLE = ConfiguredMessage.asString().defaults(
"&f请输入 &b/prefix <玩家ID> &f为指定玩家打开前缀GUI。"
).build();
public static final ConfiguredMessage<String> ADMIN = ConfiguredMessage.asString().defaults(
"&3&l用户前缀系统 &f帮助",
"&8# &f/upa list",
"&8- &7查看当前前缀列表。",
"&8# &f/upa set &b<玩家ID> &b<前缀ID>",
"&8- &7为玩家设定指定前缀。",
"&8- &7&o注意:玩家必须拥有指定前缀的权限。",
"&8# &f/upa reload",
"&8- &7重载前缀配置。"
).build();
public static final ConfiguredMessage<String> NO_PERM = ConfiguredMessage.asString()
.defaults("&c&l抱歉!&f但您没有权限使用该指令。")
.build();
}
public static final ConfiguredMessage<String> NOT_ONLINE = ConfiguredMessage.asString()
.defaults("&7玩家 &b%(player) &7并不在线。")
.params("player").build();
public static final ConfiguredMessage<String> SELECTED = ConfiguredMessage.asString()
.defaults("&7您选择了 &f%(name) &7作为当前显示的前缀。")
.params("name").build();
public static final ConfiguredMessage<String> EXPIRED = ConfiguredMessage.asString()
.defaults(
"&7您先前使用的前缀 &f%(oldName) &7已到期。",
"&7现在已为您重新调整为 &f%(newName) &7。"
).params("oldName", "newName").build();
public static final ConfiguredMessage<String> REMOVED = ConfiguredMessage.asString()
.defaults("&7您先前使用的前缀已被移除,现在已为您重新调整为 &f%(newName) &7。")
.params("newName").build();
public static final class RELOAD implements Configuration {
public static final ConfiguredMessage<String> SUCCESS = ConfiguredMessage.asString()
.defaults("&a&l重载完成!&7耗时 &f%(time)ms&7,共加载了 &b%(count) &7个前缀。")
.params("time", "count").build();
public static final ConfiguredMessage<String> FAILED = ConfiguredMessage.asString()
.defaults("&c&l重载出错!&7错误提示为 &8“&r%(error)&8”。")
.params("error").build();
}
public static final class SET implements Configuration {
public static final ConfiguredMessage<String> SUCCESS = ConfiguredMessage.asString()
.defaults("&a&l设置成功!&7成功设定玩家 &b%(player) &f的前缀为 &r%(prefix) &f。")
.params("player", "prefix").build();
public static final ConfiguredMessage<String> PREFIX_NOT_FOUND = ConfiguredMessage.asString()
.defaults("&c&l无法设置!&7不存在ID为 &b%(prefix) &7的前缀。")
.params("prefix").build();
public static final ConfiguredMessage<String> NO_PERM = ConfiguredMessage.asString()
.defaults("&c&l无法设置!&7玩家 &b%(player) &7并没有前缀 &r%(prefix) &7的前缀。")
.params("player", "prefix")
.build();
}
@HeaderComments("管理员使用的 “/upa list” 指令的格式")
public static final class LIST implements Configuration {
public static final ConfiguredMessage<String> HEADER = ConfiguredMessage.asString()
.defaults("&3&l用户前缀系统 &f前缀列表").build();
public static final ConfiguredMessage<String> VALUE = ConfiguredMessage.asString().defaults(
"&8#%(weight) &f%(identifier)",
"&8- &7显示名 &r%(name) &7权限 &r%(permission)",
"&8- &7内容示例&r %(content) %(sender_name)"
).params("weight", "identifier", "name", "permission", "content", "sender_name").build();
}
}
@@ -0,0 +1,59 @@
package cc.carm.plugin.userprefix.conf.gui;
import cc.carm.lib.configuration.source.section.ConfigureSection;
import cc.carm.lib.easyplugin.gui.configuration.GUIActionConfiguration;
import cc.carm.lib.easyplugin.gui.configuration.GUIActionType;
import cc.carm.lib.easyplugin.gui.configuration.GUIConfiguration;
import cc.carm.lib.easyplugin.gui.configuration.GUIItemConfiguration;
import cc.carm.lib.mineconfiguration.bukkit.source.BukkitSection;
import cc.carm.plugin.userprefix.Main;
import org.bukkit.Material;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.stream.Collectors;
public class GUIItems {
protected final Map<String, GUIItemConfiguration> items;
public GUIItems(Map<String, GUIItemConfiguration> items) {
this.items = items;
}
public Map<String, GUIItemConfiguration> getItems() {
return items;
}
public Map<String, Object> serialize() {
return items.entrySet().stream().collect(Collectors.toMap(
Map.Entry::getKey,
entry -> entry.getValue().serialize(), (a, b) -> b,
LinkedHashMap::new
));
}
public static GUIItems parse(ConfigureSection section) {
if (!(section instanceof BukkitSection)) {
Main.severe("Unknown configure source, it should never happened!");
Main.severe("Required: " + BukkitSection.class.getSimpleName());
Main.severe("Found: " + section.getClass().getSimpleName());
return new GUIItems(new LinkedHashMap<>());
}
BukkitSection craft = (BukkitSection) section;
return new GUIItems(GUIConfiguration.readItems(craft.data()));
}
public static GUIItems defaults() {
LinkedHashMap<String, GUIItemConfiguration> map = new LinkedHashMap<>();
map.put("back", new GUIItemConfiguration(
Material.BARRIER, 1, 0, "&c&l返回",
Collections.singletonList("&f点击即可返回上一菜单"),
Collections.singletonList(GUIActionConfiguration.of(GUIActionType.CHAT, "/menu")),
Collections.singletonList(49)
));
return new GUIItems(map);
}
}
@@ -0,0 +1,202 @@
package cc.carm.plugin.userprefix.conf.prefix;
import cc.carm.lib.easyplugin.gui.configuration.GUIActionConfiguration;
import cc.carm.lib.easyplugin.gui.configuration.GUIActionType;
import cc.carm.lib.easyplugin.utils.ColorParser;
import cc.carm.lib.easyplugin.utils.MessageUtils;
import cc.carm.lib.mineconfiguration.bukkit.value.item.PreparedItem;
import cc.carm.plugin.userprefix.Main;
import cc.carm.plugin.userprefix.manager.ServiceManager;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
public class PrefixConfig {
static final Random RANDOM = new Random();
protected final @NotNull String identifier;
protected final @NotNull String name;
protected final @NotNull List<String> content;
protected final @NotNull List<String> description;
protected final long period; // 内容变更周期,单位为毫秒
protected final int weight;
protected final @Nullable String permission;
protected final @NotNull List<GUIActionConfiguration> actions;
protected final @NotNull ItemStack itemHasPermission;
protected final @Nullable ItemStack itemNoPermission;
protected final @Nullable ItemStack itemWhenUsing;
public PrefixConfig(@NotNull String identifier, @NotNull String name,
@NotNull String content, int weight, @Nullable String permission,
@NotNull List<GUIActionConfiguration> actions,
@NotNull ItemStack itemHasPermission,
@Nullable ItemStack itemWhenUsing,
@Nullable ItemStack itemNoPermission) {
this(
identifier, name, new ArrayList<>(),
content, weight, permission, actions,
itemHasPermission, itemWhenUsing, itemNoPermission
);
}
public PrefixConfig(@NotNull String identifier,
@NotNull String name, @NotNull List<String> description,
@NotNull String content, int weight, @Nullable String permission,
@NotNull List<GUIActionConfiguration> actions,
@NotNull ItemStack itemHasPermission,
@Nullable ItemStack itemWhenUsing,
@Nullable ItemStack itemNoPermission) {
this(
identifier, name, description,
Collections.singletonList(content), 0, weight, permission, actions,
itemHasPermission, itemWhenUsing, itemNoPermission
);
}
public PrefixConfig(@NotNull String identifier,
@NotNull String name, @NotNull List<String> description,
@NotNull List<String> content, long period,
int weight, @Nullable String permission,
@NotNull List<GUIActionConfiguration> actions,
@NotNull ItemStack itemHasPermission,
@Nullable ItemStack itemWhenUsing,
@Nullable ItemStack itemNoPermission) {
this.identifier = identifier;
this.name = name;
this.description = description;
this.content = content;
this.period = period;
this.weight = weight;
this.permission = permission;
this.actions = actions;
this.itemHasPermission = itemHasPermission;
this.itemNoPermission = itemNoPermission;
this.itemWhenUsing = itemWhenUsing;
}
@NotNull
public String getIdentifier() {
return identifier;
}
@NotNull
public String getName() {
return ColorParser.parse(name);
}
@NotNull
public List<String> getDescription() {
return ColorParser.parse(description);
}
@NotNull
public String getContent(CommandSender viewer) {
if (content.isEmpty()) return "?";
if (period < 0 || content.size() == 1) {
return ColorParser.parse(MessageUtils.setPlaceholders(viewer, content.get(0)));
}
if (period == 0) {
// PERIOD 为0时,随机返回一个内容
int index = RANDOM.nextInt(content.size());
return ColorParser.parse(MessageUtils.setPlaceholders(viewer, content.get(index)));
} else {
// 可变化的内容,则基于偏移量与时间戳计算目标index
long curr = System.currentTimeMillis();
long offset = curr % period; // 计算偏移量
int index = (int) (offset / (period / content.size())); // 计算索引
return ColorParser.parse(MessageUtils.setPlaceholders(viewer, content.get(index)));
}
}
public int getWeight() {
return weight;
}
@Nullable
public String getPermission() {
return permission;
}
@NotNull
public ItemStack getItemHasPermission(@Nullable Player player) {
return getItem(player, this.itemHasPermission);
}
@Nullable
public ItemStack getItemNoPermission(@Nullable Player player) {
return getItem(player, this.itemNoPermission);
}
@Nullable
public ItemStack getItemWhenUsing(@Nullable Player player) {
return getItem(player, this.itemWhenUsing);
}
@Contract("_,!null->!null")
protected @Nullable ItemStack getItem(@Nullable Player player, @Nullable ItemStack item) {
PreparedItem prepared = PreparedItem.of(item);
prepared.parser((p, s) -> ColorParser.parse(MessageUtils.setPlaceholders(p, s)));
if (!getDescription().isEmpty()) {
prepared.insert("description", getDescription());
prepared.placeholder("description", String.join("\n", getDescription()));
}
prepared.placeholder("name", getName());
prepared.placeholder("identifier", getIdentifier());
prepared.placeholder("content", getContent(player));
prepared.placeholder("permission", getPermission());
prepared.placeholder("weight", String.valueOf(getWeight()));
prepared.placeholder("hasPermission", String.valueOf(checkPermission(player)));
prepared.placeholder("public", String.valueOf(isPublic()));
prepared.placeholder("visible", String.valueOf(isVisible(player)));
return prepared.get(player);
}
public boolean isPublic() {
return getPermission() == null;
}
public void executeActions(@NotNull Player player) {
this.actions.forEach(action -> {
if (action.getActionType() == GUIActionType.CONSOLE) { // 控制台执行命令必须在全局调度器中执行
Main.getInstance().getFoliaScheduler().runGlobal(false, () -> action.executeAction(player));
} else {
action.executeAction(player);
}
});
}
public boolean isVisible(Player player) {
return this.itemNoPermission != null || checkPermission(player);
}
/**
* 判断某玩家是否有权限使用该前缀
*
* @param player 玩家
* @return 若前缀标识不存在,则返回false;若前缀为默认前缀,或该前缀无权限,或玩家有该前缀的权限,则返回true。
*/
public boolean checkPermission(Player player) {
return permission == null || ServiceManager.hasPermission(player, permission);
}
}
@@ -1,85 +0,0 @@
package cc.carm.plugin.userprefix.configuration;
import cc.carm.plugin.userprefix.configuration.message.ConfigMessageList;
import cc.carm.plugin.userprefix.configuration.values.ConfigSound;
import cc.carm.plugin.userprefix.configuration.values.ConfigValue;
import cc.carm.plugin.userprefix.util.ItemStackFactory;
import org.bukkit.Material;
import org.bukkit.inventory.ItemStack;
public class PluginConfig {
public static ConfigValue<Boolean> DEBUG = new ConfigValue<>("debug", Boolean.class, false);
public static ConfigValue<Boolean> METRICS = new ConfigValue<>("metrics", 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, "prefixes/");
}
public static class Functions {
public static ConfigValue<Boolean> NAME_PREFIX = new ConfigValue<>("functions.OnNamePrefix", Boolean.class, true);
public static ConfigValue<Boolean> AUTO_USE = new ConfigValue<>("functions.autoUsePrefix", Boolean.class, true);
public static class Chat {
public static ConfigValue<Boolean> ENABLE = new ConfigValue<>("functions.chat.enable", Boolean.class, false);
public static ConfigValue<String> FORMAT = new ConfigValue<>("functions.chat.format", String.class, "<%1$s> %2$s");
}
}
public static class GUI {
public static ConfigValue<String> TITLE = new ConfigValue<>("GUI.title", String.class, "&f&l我的前缀 &8| 列表");
public static class Items {
public static ConfigValue<ItemStack> NEXT_PAGE = new ConfigValue<>("GUI.items.next-page", ItemStack.class,
new ItemStackFactory(Material.ARROW)
.setDisplayName("下一页")
.addLore("&7&o右键可前往最后一页哦")
.toItemStack()
);
public static ConfigValue<ItemStack> PREVIOUS_PAGE = new ConfigValue<>("GUI.items.previous-page", ItemStack.class,
new ItemStackFactory(Material.ARROW)
.setDisplayName("上一页")
.addLore("&7&o右键可前往第一页哦")
.toItemStack()
);
}
}
public static class Messages {
public static ConfigMessageList SELECTED = new ConfigMessageList("selected");
public static ConfigMessageList EXPIRED = new ConfigMessageList("expired");
public static ConfigMessageList REMOVED = new ConfigMessageList("removed");
public static ConfigMessageList RELOAD = new ConfigMessageList("reload");
public static ConfigMessageList HELP = new ConfigMessageList("help");
public static ConfigMessageList LIST_TITLE = new ConfigMessageList("list-title");
public static ConfigMessageList LIST_VALUE = new ConfigMessageList("list-value");
}
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");
}
}
@@ -1,64 +0,0 @@
package cc.carm.plugin.userprefix.configuration.file;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.plugin.java.JavaPlugin;
import java.io.File;
import java.io.IOException;
public class FileConfig {
private final JavaPlugin plugin;
private final String fileName;
private File file;
private FileConfiguration config;
public FileConfig(final JavaPlugin plugin) {
this(plugin, "config.yml");
}
public FileConfig(final JavaPlugin plugin, final String name) {
this.plugin = plugin;
this.fileName = name;
initFile();
}
private void initFile() {
this.file = new File(plugin.getDataFolder(), fileName);
if (!this.file.exists()) {
if (!this.file.getParentFile().exists()) {
this.file.getParentFile().mkdirs();
}
plugin.saveResource(fileName, true);
}
this.config = YamlConfiguration.loadConfiguration(this.file);
}
public File getFile() {
return file;
}
public FileConfiguration getConfig() {
return config;
}
public void save() {
try {
getConfig().save(getFile());
} catch (IOException e) {
e.printStackTrace();
}
}
public void reload() {
if (getFile().exists()) {
this.config = YamlConfiguration.loadConfiguration(getFile());
} else {
initFile();
}
}
}
@@ -1,32 +0,0 @@
package cc.carm.plugin.userprefix.configuration.message;
import cc.carm.plugin.userprefix.configuration.values.ConfigValue;
import cc.carm.plugin.userprefix.manager.ConfigManager;
import cc.carm.plugin.userprefix.util.MessageUtil;
import org.bukkit.command.CommandSender;
import java.util.Arrays;
public class ConfigMessage extends ConfigValue<String> {
public ConfigMessage(String configSection) {
this(configSection, null);
}
public ConfigMessage(String configSection, String defaultValue) {
super(ConfigManager.getMessageConfig(), configSection, String.class, defaultValue);
}
public void send(CommandSender sender) {
MessageUtil.send(sender, get());
}
public void sendWithPlaceholders(CommandSender sender) {
MessageUtil.sendWithPlaceholders(sender, get());
}
public void sendWithPlaceholders(CommandSender sender, String[] params, Object[] values) {
MessageUtil.sendWithPlaceholders(sender, Arrays.asList(get()), params, values);
}
}
@@ -1,29 +0,0 @@
package cc.carm.plugin.userprefix.configuration.message;
import cc.carm.plugin.userprefix.configuration.values.ConfigValueList;
import cc.carm.plugin.userprefix.manager.ConfigManager;
import cc.carm.plugin.userprefix.util.MessageUtil;
import org.bukkit.command.CommandSender;
public class ConfigMessageList extends ConfigValueList<String> {
public ConfigMessageList(String configSection) {
super(ConfigManager.getMessageConfig(), configSection, String.class);
}
public ConfigMessageList(String configSection, String[] defaultValue) {
super(ConfigManager.getMessageConfig(), configSection, String.class, defaultValue);
}
public void send(CommandSender sender) {
MessageUtil.send(sender, get());
}
public void sendWithPlaceholders(CommandSender sender) {
MessageUtil.sendWithPlaceholders(sender, get());
}
public void sendWithPlaceholders(CommandSender sender, String[] params, Object[] values) {
MessageUtil.sendWithPlaceholders(sender, get(), params, values);
}
}
@@ -1,73 +0,0 @@
package cc.carm.plugin.userprefix.configuration.values;
import cc.carm.plugin.userprefix.Main;
import cc.carm.plugin.userprefix.configuration.file.FileConfig;
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 {
FileConfig source;
String configSection;
Sound defaultValue;
public ConfigSound(String configSection) {
this(configSection, null);
}
public ConfigSound(String configSection, Sound defaultValue) {
this(ConfigManager.getPluginConfig(), configSection, defaultValue);
}
public ConfigSound(FileConfig source, String configSection, Sound defaultValue) {
this.source = source;
this.configSection = configSection;
this.defaultValue = defaultValue;
}
public FileConfiguration getConfiguration() {
return this.source.getConfig();
}
public void set(Sound value, float volume) {
getConfiguration().set(this.configSection, value.name() + ":" + volume);
this.save();
}
public void set(Sound value, float volume, float pitch) {
getConfiguration().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 = getConfiguration().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.error("声音 " + this.configSection + " 配置错误,不存在 " + soundString + " ,请检查。");
Main.error("There's no sound matches in " + this.configSection + " , please check the configuration");
}
}
if (finalSound != null) {
player.playSound(player.getLocation(), finalSound, volume, pitch);
}
}
public void save() {
this.source.save();
}
}
@@ -1,58 +0,0 @@
package cc.carm.plugin.userprefix.configuration.values;
import cc.carm.plugin.userprefix.configuration.file.FileConfig;
import cc.carm.plugin.userprefix.manager.ConfigManager;
import org.bukkit.configuration.file.FileConfiguration;
public class ConfigValue<V> {
FileConfig source;
String configSection;
Class<V> clazz;
V defaultValue;
public ConfigValue(String configSection, Class<V> clazz) {
this(configSection, clazz, null);
}
public ConfigValue(String configSection, Class<V> clazz, V defaultValue) {
this(ConfigManager.getPluginConfig(), configSection, clazz, defaultValue);
}
public ConfigValue(FileConfig source, String configSection, Class<V> clazz, V defaultValue) {
this.source = source;
this.configSection = configSection;
this.clazz = clazz;
this.defaultValue = defaultValue;
}
public FileConfiguration getConfiguration() {
return this.source.getConfig();
}
public V get() {
if (getConfiguration().contains(this.configSection)) {
Object val = getConfiguration().get(this.configSection, this.defaultValue);
return this.clazz.isInstance(val) ? this.clazz.cast(val) : this.defaultValue;
} else {
// 如果没有默认值,就把配置写进去,便于配置
return setDefault();
}
}
public void set(V value) {
getConfiguration().set(this.configSection, value);
this.save();
}
public void save() {
this.source.save();
}
public V setDefault() {
set(this.defaultValue);
return this.defaultValue;
}
}
@@ -1,71 +0,0 @@
package cc.carm.plugin.userprefix.configuration.values;
import cc.carm.plugin.userprefix.configuration.file.FileConfig;
import cc.carm.plugin.userprefix.manager.ConfigManager;
import org.bukkit.configuration.file.FileConfiguration;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class ConfigValueList<V> {
FileConfig source;
String configSection;
Class<V> clazz;
V[] defaultValue;
public ConfigValueList(String configSection, Class<V> clazz) {
this(ConfigManager.getPluginConfig(), configSection, clazz);
}
public ConfigValueList(String configSection, Class<V> clazz, V[] defaultValue) {
this(ConfigManager.getPluginConfig(), configSection, clazz, defaultValue);
}
public ConfigValueList(FileConfig configuration, String configSection, Class<V> clazz) {
this(configuration, configSection, clazz, null);
}
public ConfigValueList(FileConfig configuration, String configSection, Class<V> clazz, V[] defaultValue) {
this.source = configuration;
this.configSection = configSection;
this.clazz = clazz;
this.defaultValue = defaultValue;
}
public FileConfiguration getConfiguration() {
return this.source.getConfig();
}
public ArrayList<V> get() {
List<?> list = getConfiguration().getList(this.configSection);
if (list == null) {
if (defaultValue != null) {
return new ArrayList<>(Arrays.asList(defaultValue));
} else {
return new ArrayList(0);
}
} else {
ArrayList<V> result = new ArrayList();
for (Object object : list) {
if (this.clazz.isInstance(object)) {
result.add(this.clazz.cast(object));
}
}
return result;
}
}
public void set(ArrayList<V> value) {
getConfiguration().set(this.configSection, value);
this.save();
}
public void save() {
this.source.save();
}
}
@@ -1,55 +1,72 @@
package cc.carm.plugin.userprefix.event;
import cc.carm.plugin.userprefix.model.ConfiguredPrefix;
import cc.carm.plugin.userprefix.Main;
import cc.carm.plugin.userprefix.conf.prefix.PrefixConfig;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.Cancellable;
import org.bukkit.event.HandlerList;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Optional;
import java.util.function.Consumer;
public class UserPrefixChangeEvent extends UserPrefixEvent implements Cancellable {
public static HandlerList handler = new HandlerList();
public static HandlerList handler = new HandlerList();
private boolean cancelled;
private boolean cancelled;
private final @Nullable ConfiguredPrefix before;
private @NotNull ConfiguredPrefix after;
private final @Nullable PrefixConfig before;
private @NotNull PrefixConfig after;
public UserPrefixChangeEvent(@NotNull Player who,
@Nullable ConfiguredPrefix before,
@NotNull ConfiguredPrefix after) {
super(who);
this.before = before;
this.after = after;
}
public UserPrefixChangeEvent(@NotNull Player who,
@Nullable PrefixConfig before,
@NotNull PrefixConfig after) {
super(who);
this.before = before;
this.after = after;
}
public @Nullable ConfiguredPrefix getBefore() {
return before;
}
public @Nullable PrefixConfig getBefore() {
return before;
}
public @NotNull ConfiguredPrefix getAfter() {
return after;
}
public @NotNull PrefixConfig getAfter() {
return after;
}
public void setAfter(@NotNull ConfiguredPrefix after) {
this.after = after;
}
public void setAfter(@NotNull PrefixConfig after) {
this.after = after;
}
@Override
public boolean isCancelled() {
if (before == null) return false; //Could not be cancelled when prefix is null.
else return this.cancelled;
}
@Override
public boolean isCancelled() {
if (before == null) return false; //Could not be cancelled when prefix is null.
else return this.cancelled;
}
@Override
public void setCancelled(boolean cancel) {
this.cancelled = cancel;
}
@Override
public void setCancelled(boolean cancel) {
this.cancelled = cancel;
}
@NotNull
@Override
public HandlerList getHandlers() {
return handler;
}
public static void call(@NotNull Player who,
@Nullable PrefixConfig before,
@NotNull PrefixConfig after,
@Nullable Consumer<@Nullable PrefixConfig> finish) {
UserPrefixChangeEvent event = new UserPrefixChangeEvent(who, before, after);
Main.getInstance().getFoliaScheduler().runGlobal(true, () -> {
Bukkit.getPluginManager().callEvent(event);
Optional.ofNullable(finish).ifPresent(f -> f.accept(event.getAfter()));
});
}
@NotNull
@Override
public HandlerList getHandlers() {
return handler;
}
}
@@ -1,30 +1,37 @@
package cc.carm.plugin.userprefix.event;
import cc.carm.plugin.userprefix.model.ConfiguredPrefix;
import cc.carm.plugin.userprefix.Main;
import cc.carm.plugin.userprefix.conf.prefix.PrefixConfig;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.HandlerList;
import org.jetbrains.annotations.NotNull;
public class UserPrefixExpireEvent extends UserPrefixEvent {
public static HandlerList handler = new HandlerList();
public static HandlerList handler = new HandlerList();
public final @NotNull ConfiguredPrefix expiredPrefix;
public final @NotNull PrefixConfig expiredPrefix;
public UserPrefixExpireEvent(@NotNull Player who,
@NotNull ConfiguredPrefix expiredPrefix) {
super(who);
this.expiredPrefix = expiredPrefix;
}
public UserPrefixExpireEvent(@NotNull Player who,
@NotNull PrefixConfig expiredPrefix) {
super(who);
this.expiredPrefix = expiredPrefix;
}
public @NotNull ConfiguredPrefix getExpiredPrefix() {
return expiredPrefix;
}
public @NotNull PrefixConfig getExpiredPrefix() {
return expiredPrefix;
}
@NotNull
@Override
public HandlerList getHandlers() {
return handler;
}
public static void call(@NotNull Player player, @NotNull PrefixConfig currentPrefix) {
Main.getInstance().getFoliaScheduler().runGlobal(true, () -> Bukkit.getPluginManager().callEvent(new UserPrefixExpireEvent(player, currentPrefix)));
}
@NotNull
@Override
public HandlerList getHandlers() {
return handler;
}
}
@@ -0,0 +1,121 @@
package cc.carm.plugin.userprefix.folia;
import cc.carm.plugin.userprefix.Main;
import org.bukkit.Bukkit;
import org.bukkit.entity.Entity;
import org.bukkit.plugin.Plugin;
import java.lang.reflect.InvocationTargetException;
import java.util.function.Consumer;
public class FoliaScheduler {
private final Main plugin;
private final boolean folia;
public FoliaScheduler(Main plugin, boolean folia) {
this.plugin = plugin;
this.folia = folia;
}
public void runAsync(Runnable task) {
if (this.folia) {
this.runAsyncFolia(task);
} else {
this.runAsyncBukkit(task);
}
}
private void runAsyncBukkit(Runnable task) {
Bukkit.getScheduler().runTaskAsynchronously(this.plugin, task);
}
private void runAsyncFolia(Runnable task) {
// AsyncScheduler asyncScheduler = Bukkit.getAsyncScheduler();
// asyncScheduler.runNow(this.plugin, t -> task.run());
try {
Object asyncScheduler = Bukkit.class.getMethod("getAsyncScheduler")
.invoke(null);
asyncScheduler.getClass().getMethod("runNow", Plugin.class, Consumer.class)
.invoke(asyncScheduler, this.plugin, (Consumer<?>) t -> task.run());
} catch (IllegalAccessException | NoSuchMethodException e) {
Main.severe("unexpected exception during reflection (#runAsyncFolia), it should never happen!");
e.printStackTrace();
} catch (InvocationTargetException e) {
throw (RuntimeException) e.getTargetException();
}
}
/**
* Folia 上在实体所在的调度器上执行任务,或在 Bukkit 上同步执行任务
*
* @param entity 实体
* @param forceSync 若为 trueBukkit 下强制同步运行;若为 false,Bukkit 下直接执行
* @param task 任务
* @see FoliaScheduler#runBukkit(boolean, Runnable)
* @see FoliaScheduler#runOnEntityFolia(Entity, Runnable)
*/
public void runOnEntity(Entity entity, boolean forceSync, Runnable task) {
if (this.folia) {
this.runOnEntityFolia(entity, task);
} else {
this.runBukkit(forceSync, task);
}
}
private void runOnEntityFolia(Entity entity, Runnable task) {
// EntityScheduler entityScheduler = entity.getScheduler();
// entityScheduler.run(this.plugin, t -> task.run(), null);
try {
Object entityScheduler = Entity.class.getMethod("getScheduler")
.invoke(entity);
entityScheduler.getClass().getMethod("run", Plugin.class, Consumer.class, Runnable.class)
.invoke(entityScheduler, this.plugin, (Consumer<?>) t -> task.run(), null);
} catch (IllegalAccessException | NoSuchMethodException e) {
Main.severe("unexpected exception during reflection (#runOnEntityFolia), it should never happen!");
e.printStackTrace();
} catch (InvocationTargetException e) {
throw (RuntimeException) e.getTargetException();
}
}
/**
* Folia 上在全局调度器上执行任务,或在 Bukkit 上同步执行任务
*
* @param forceSync 若为 trueBukkit 下强制同步运行;若为 false,Bukkit 下直接执行
* @param task 任务
* @see FoliaScheduler#runBukkit(boolean, Runnable)
* @see FoliaScheduler#runGlobalFolia(Runnable)
*/
public void runGlobal(boolean forceSync, Runnable task) {
if (this.folia) {
this.runGlobalFolia(task);
} else {
this.runBukkit(forceSync, task);
}
}
private void runGlobalFolia(Runnable task) {
// GlobalRegionScheduler globalRegionScheduler = Bukkit.getGlobalRegionScheduler();
// globalRegionScheduler.execute(this.plugin, task);
try {
Object globalRegionScheduler = Bukkit.class.getMethod("getGlobalRegionScheduler")
.invoke(null);
globalRegionScheduler.getClass().getMethod("execute", Plugin.class, Runnable.class)
.invoke(globalRegionScheduler, this.plugin, task);
} catch (IllegalAccessException | NoSuchMethodException e) {
Main.severe("unexpected exception during reflection (#runGlobalFolia), it should never happen!");
e.printStackTrace();
} catch (InvocationTargetException e) {
throw (RuntimeException) e.getTargetException();
}
}
private void runBukkit(boolean forceSync, Runnable task) {
if (forceSync) {
Bukkit.getScheduler().runTask(this.plugin, task);
} else {
task.run();
}
}
}
@@ -0,0 +1,23 @@
package cc.carm.plugin.userprefix.folia;
import cc.carm.plugin.userprefix.Main;
import org.bukkit.enchantments.Enchantment;
public interface MajorUtil {
static Enchantment getEnchantProtection() {
Class<Enchantment> enchantmentClass = Enchantment.class;
try {
return (Enchantment) enchantmentClass.getField("PROTECTION_ENVIRONMENTAL").get(null);
} catch (NoSuchFieldException e1) {
try {
return (Enchantment) enchantmentClass.getField("PROTECTION").get(null);
} catch (NoSuchFieldException | IllegalAccessException e2) {
Main.severe("unexpected exception during reflection (#getEnchantProtection), it should never happen!");
throw new RuntimeException(e2);
}
} catch (IllegalAccessException e) {
Main.severe("unexpected exception during reflection (#getEnchantProtection), it should never happen!");
throw new RuntimeException(e);
}
}
}
@@ -1,21 +1,29 @@
package cc.carm.plugin.userprefix.nametag;
package cc.carm.plugin.userprefix.hooker;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.scoreboard.Scoreboard;
import org.bukkit.scoreboard.Team;
import org.jetbrains.annotations.Range;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
/**
* 用户头顶标签
*
* @author LSeng, Tony, Carm
*/
public class UserNameTag {
public static final int MAX_ORDER = 999;
private final Player viewer;
private Team team;
private final Scoreboard sb;
private int order = 99999;
private int order = MAX_ORDER;
private final Map<UUID, Integer> targetOrders = new HashMap<>();
private final Map<UUID, String> previousTeamNames = new HashMap<>();
@@ -35,7 +43,7 @@ public class UserNameTag {
/**
* 设置自己的前缀
*
* @param prefix
* @param prefix 前缀内容
*/
public void setPrefix(String prefix) {
team.setPrefix(prefix);
@@ -45,8 +53,8 @@ public class UserNameTag {
/**
* 设置某个玩家的前缀
*
* @param target
* @param prefix
* @param target 目标
* @param prefix 前缀内容
*/
public void setPrefix(Player target, String prefix) {
if (target == viewer) {
@@ -63,11 +71,9 @@ public class UserNameTag {
/**
* 设置名字在TabList中的顺序
*
* @param order
* @param order 顺序 (0~999)
*/
public void setOrder(int order) {
if (order < 0 || order > 99999)
throw new IllegalArgumentException("order must be in 0~99999");
public void setOrder(@Range(from = 0, to = 999) int order) {
this.order = order;
targetOrders.put(viewer.getUniqueId(), order);
update(viewer);
@@ -78,16 +84,13 @@ public class UserNameTag {
*
* @param order 顺序
*/
public void setOrder(Player target, int order) {
if (order < 0 || order > 99999)
throw new IllegalArgumentException("order must be in 0~99999");
public void setOrder(Player target, @Range(from = 0, to = 999) int order) {
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);
if (viewer != target) update(target);
}
public void update(Player target) {
@@ -110,7 +113,7 @@ public class UserNameTag {
team = newTeam;
}
} else {
int order = targetOrders.getOrDefault(target.getUniqueId(), 99999);
int order = targetOrders.getOrDefault(target.getUniqueId(), 999);
String previousTeamName = previousTeamNames.get(target.getUniqueId());
if (previousTeamName == null) {
return;
@@ -138,7 +141,7 @@ public class UserNameTag {
}
private Team checkTeam(Player target) {
int order = targetOrders.getOrDefault(target.getUniqueId(), 99999);
int order = targetOrders.getOrDefault(target.getUniqueId(), 999);
String name = order + target.getUniqueId().toString().substring(0, 10);
Team targetTeam = this.sb.getTeam(name);
if (targetTeam == null) {
@@ -1,91 +1,82 @@
package cc.carm.plugin.userprefix.hooker;
import cc.carm.plugin.userprefix.manager.PrefixManager;
import cc.carm.plugin.userprefix.manager.UserManager;
import cc.carm.plugin.userprefix.model.ConfiguredPrefix;
import me.clip.placeholderapi.expansion.PlaceholderExpansion;
import cc.carm.lib.easyplugin.papi.EasyPlaceholder;
import cc.carm.lib.easyplugin.papi.expansion.SubExpansion;
import cc.carm.lib.easyplugin.papi.handler.PlaceholderHandler;
import cc.carm.plugin.userprefix.UserPrefixAPI;
import cc.carm.plugin.userprefix.conf.prefix.PrefixConfig;
import org.bukkit.OfflinePlayer;
import org.bukkit.entity.Player;
import org.bukkit.plugin.java.JavaPlugin;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.Collections;
import java.util.function.BiFunction;
import java.util.function.Function;
public class UserPrefixExpansion extends PlaceholderExpansion {
public class UserPrefixExpansion extends EasyPlaceholder {
JavaPlugin plugin;
public UserPrefixExpansion(@NotNull JavaPlugin plugin, @NotNull String rootIdentifier) {
super(plugin, rootIdentifier);
handle("version", (player, args) -> getVersion());
handle("identifier", handlePrefix(PrefixConfig::getIdentifier), "id");
handle("prefix", handlePrefix(PrefixConfig::getContent));
handle("name", handlePrefix(PrefixConfig::getName));
handle("description", handlePrefix(p -> String.join("\n", p.getDescription())));
handle("weight", handlePrefix(PrefixConfig::getWeight));
handle("amount", handlePlayer(
(player) -> UserPrefixAPI.getUserManager().getUsablePrefixes(player).size() + 1)
);
handle("has", handlePlayer((player, args) -> {
if (args.length < 1) return "参数不足";
PrefixConfig prefix = UserPrefixAPI.getPrefixManager().getPrefix(args[0]);
if (prefix == null) return "该前缀不存在";
return prefix.checkPermission(player);
}), Collections.singletonList("<前缀ID>"));
public UserPrefixExpansion(JavaPlugin plugin) {
this.plugin = plugin;
}
@Override
public @NotNull List<String> getPlaceholders() {
List<String> placeholders = new ArrayList<>();
placeholders.add("%UserPrefix_prefix%");
placeholders.add("%UserPrefix_weight%");
placeholders.add("%UserPrefix_identifier%");
placeholders.add("%UserPrefix_name%");
placeholders.add("%UserPrefix_has_<Identifier>%");
return placeholders;
public PlaceholderHandler handlePrefix(Function<PrefixConfig, Object> handler) {
return handlePlayer((player, args) -> handler.apply(UserPrefixAPI.getUserManager().getPrefix(player)));
}
public PlaceholderHandler handlePrefix(BiFunction<PrefixConfig, Player, Object> handler) {
return handlePlayer((player, args) -> handler.apply(UserPrefixAPI.getUserManager().getPrefix(player), player));
}
/**
* This is required or else PlaceholderAPI will unregister the Expansion on reload
*/
@Override
public boolean canRegister() {
public boolean persist() {
return true;
}
@Override
public @NotNull String getAuthor() {
return plugin.getDescription().getAuthors().toString();
public PlaceholderHandler handlePlayer(BiFunction<Player, String[], Object> handler) {
return (player, args) -> {
if (player == null || !player.isOnline()) return "Loading...";
return handler.apply((Player) player, args);
};
}
public PlaceholderHandler handlePlayer(Function<Player, Object> handler) {
return handlePlayer((player, args) -> handler.apply(player));
}
@Override
public @NotNull String getIdentifier() {
return "UserPrefix";
public String onErrorParams(@Nullable OfflinePlayer player) {
return "参数不足";
}
@Override
public @NotNull 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", "");
}
default: {
return "参数错误";
}
}
public String onException(@Nullable OfflinePlayer player, @NotNull SubExpansion<?> expansion,
@NotNull Exception exception) {
exception.printStackTrace();
return "参数错误";
}
}
@@ -1,32 +1,30 @@
package cc.carm.plugin.userprefix.listener;
import cc.carm.lib.easyplugin.utils.MessageUtils;
import cc.carm.plugin.userprefix.Main;
import cc.carm.plugin.userprefix.configuration.PluginConfig;
import cc.carm.plugin.userprefix.util.MessageUtil;
import me.clip.placeholderapi.PlaceholderAPI;
import cc.carm.plugin.userprefix.conf.PluginConfig;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.AsyncPlayerChatEvent;
public class ChatListener implements Listener {
@EventHandler
public void onChat(AsyncPlayerChatEvent event) {
if (!PluginConfig.Functions.Chat.ENABLE.get()) return;
String format = PluginConfig.Functions.Chat.FORMAT.get();
if (format == null || format.length() < 1) return;
@EventHandler
public void onChat(AsyncPlayerChatEvent event) {
if (!PluginConfig.FUNCTIONS.CHAT.ENABLE.getNotNull()) return;
if (!MessageUtil.hasPlaceholderAPI()) return;
String format = PluginConfig.FUNCTIONS.CHAT.FORMAT.get();
if (format == null || format.length() < 1) return;
try {
event.setFormat(PlaceholderAPI.setPlaceholders(event.getPlayer(), format));
} catch (Exception exception) {
Main.error("请检查配置文件中聊天相关是否配置正确。");
Main.error("Please check the chat configuration.");
exception.printStackTrace();
}
try {
event.setFormat(MessageUtils.setPlaceholders(event.getPlayer(), format));
} catch (Exception exception) {
Main.severe("请检查配置文件中聊天相关是否配置正确。");
Main.severe("Please check the chat configuration.");
exception.printStackTrace();
}
}
}
}
@@ -1,6 +1,6 @@
package cc.carm.plugin.userprefix.listener;
import cc.carm.plugin.userprefix.manager.UserManager;
import cc.carm.plugin.userprefix.UserPrefixAPI;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
@@ -8,17 +8,14 @@ import org.bukkit.event.player.PlayerQuitEvent;
public class UserListener implements Listener {
@EventHandler
public void onJoin(PlayerJoinEvent event) {
UserManager.initPlayer(event.getPlayer());
UserPrefixAPI.getUserManager().initPlayer(event.getPlayer());
}
@EventHandler
public void onLeave(PlayerQuitEvent event) {
UserManager.unloadPlayer(event.getPlayer());
UserPrefixAPI.getUserManager().unloadPlayer(event.getPlayer());
}
}
@@ -0,0 +1,25 @@
package cc.carm.plugin.userprefix.listener;
import cc.carm.plugin.userprefix.Main;
import cc.carm.plugin.userprefix.UserPrefixAPI;
import cc.carm.plugin.userprefix.ui.PrefixSelectGUI;
import net.luckperms.api.event.user.UserDataRecalculateEvent;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
public class UserPermListener {
public static void process(UserDataRecalculateEvent event) {
Player player = Bukkit.getPlayer(event.getUser().getUniqueId());
if (player == null) return;
UserPrefixAPI.getUserManager().checkPrefix(player, true);
if (PrefixSelectGUI.openingUsers.contains(player)) {
Main.getInstance().getFoliaScheduler().runOnEntity(player, true, () -> {
// 玩家权限更新,同步关闭其GUI,以令其重新打开刷新自己的前缀。
player.closeInventory();
PrefixSelectGUI.removeOpening(player);
});
}
}
}
@@ -1,22 +0,0 @@
package cc.carm.plugin.userprefix.listener.processor;
import cc.carm.plugin.userprefix.manager.UserManager;
import cc.carm.plugin.userprefix.ui.PrefixSelectGUI;
import net.luckperms.api.event.user.UserDataRecalculateEvent;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
public class UserNodeUpdateProcessor {
public static void process(UserDataRecalculateEvent event) {
Player player = Bukkit.getPlayer(event.getUser().getUniqueId());
if (player == null) return;
UserManager.checkPrefix(player, true);
if (PrefixSelectGUI.openingUsers.contains(player)) {
// 玩家权限更新,关闭其GUI,以令其重新打开刷新自己的前缀。
player.closeInventory();
PrefixSelectGUI.removeOpening(player);
}
}
}
@@ -1,48 +1,51 @@
package cc.carm.plugin.userprefix.manager;
import cc.carm.plugin.userprefix.Main;
import cc.carm.plugin.userprefix.configuration.file.FileConfig;
import cc.carm.lib.configuration.source.ConfigurationHolder;
import cc.carm.lib.easyplugin.utils.JarResourceUtils;
import cc.carm.lib.mineconfiguration.bukkit.MineConfiguration;
import cc.carm.plugin.userprefix.conf.PluginConfig;
import cc.carm.plugin.userprefix.conf.PluginMessages;
import java.io.File;
import java.io.IOException;
public class ConfigManager {
private static FileConfig config;
private static FileConfig messageConfig;
private final ConfigurationHolder<?> configProvider;
private final ConfigurationHolder<?> messageProvider;
public ConfigManager(File dataFolder) {
firstInitialize(dataFolder);
this.configProvider = MineConfiguration.from(new File(dataFolder, "config.yml"), null);
this.messageProvider = MineConfiguration.from(new File(dataFolder, "messages.yml"), null);
this.configProvider.initialize(PluginConfig.class);
this.messageProvider.initialize(PluginMessages.class);
}
public static void initConfig() {
File configFile = new File(Main.getInstance().getDataFolder(), "config.yml");
protected void firstInitialize(File dataFolder) {
File configFile = new File(dataFolder, "config.yml");
if (!configFile.exists()) {
//没找到配置文件,可能是第一次加载此插件
//把一些英文版的东西复制出来,方便英文用户使用。
Main.getInstance().saveResource("prefixes/example-prefix.yml", false);
Main.getInstance().saveResource("en_US/config.yml", false);
Main.getInstance().saveResource("en_US/messages.yml", false);
Main.getInstance().saveResource("en_US/example-prefix.yml", false);
try {
JarResourceUtils.copyFolderFromJar("i18n", dataFolder, JarResourceUtils.CopyOption.COPY_IF_NOT_EXIST);
JarResourceUtils.copyFolderFromJar("prefixes", dataFolder, JarResourceUtils.CopyOption.COPY_IF_NOT_EXIST);
} catch (IOException e) {
e.printStackTrace();
}
}
ConfigManager.config = new FileConfig(Main.getInstance(), "config.yml");
ConfigManager.messageConfig = new FileConfig(Main.getInstance(), "messages.yml");
}
public static FileConfig getPluginConfig() {
return config;
public ConfigurationHolder<?> getConfigProvider() {
return configProvider;
}
public static FileConfig getMessageConfig() {
return messageConfig;
public ConfigurationHolder<?> getMessageProvider() {
return messageProvider;
}
public static void reload() {
getPluginConfig().reload();
getMessageConfig().reload();
public void reload() throws Exception {
getConfigProvider().reload();
getMessageProvider().reload();
}
public static void saveConfig() {
getPluginConfig().save();
getMessageConfig().save();
}
}
@@ -1,171 +1,180 @@
package cc.carm.plugin.userprefix.manager;
import cc.carm.lib.easyplugin.gui.configuration.GUIActionConfiguration;
import cc.carm.lib.easyplugin.utils.ItemStackFactory;
import cc.carm.plugin.userprefix.Main;
import cc.carm.plugin.userprefix.configuration.PluginConfig;
import cc.carm.plugin.userprefix.model.ConfiguredPrefix;
import cc.carm.plugin.userprefix.util.ItemStackFactory;
import cc.carm.plugin.userprefix.conf.PluginConfig;
import cc.carm.plugin.userprefix.conf.prefix.PrefixConfig;
import com.cryptomorin.xseries.XItemStack;
import org.bukkit.Material;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.inventory.ItemFlag;
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.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.*;
import java.util.stream.Collectors;
public class PrefixManager {
public static ConfiguredPrefix defaultPrefix;
public static HashMap<String, ConfiguredPrefix> prefixes = new HashMap<>();
protected static final String FOLDER_NAME = "prefixes";
private static final String FOLDER_NAME = "prefixes";
protected @NotNull Map<String, PrefixConfig> prefixes = new HashMap<>();
protected PrefixConfig defaultPrefix;
public static void init() {
loadPrefixes();
Main.log("共加载了 " + prefixes.size() + " 个前缀。");
}
public int loadPrefixes() {
loadDefaultPrefix();
loadConfiguredPrefixes();
return prefixes.size();
}
public static void loadPrefixes() {
loadDefaultPrefix();
loadConfiguredPrefixes();
}
@SuppressWarnings("ResultOfMethodCallIgnored")
public void loadConfiguredPrefixes() {
@SuppressWarnings("ResultOfMethodCallIgnored")
public static void loadConfiguredPrefixes() {
File prefixDataFolder = getStorageFolder();
if (!prefixDataFolder.isDirectory() || !prefixDataFolder.exists()) {
prefixDataFolder.mkdir();
}
File prefixDataFolder = getStorageFolder();
if (!prefixDataFolder.isDirectory() || !prefixDataFolder.exists()) {
prefixDataFolder.mkdir();
}
String[] filesList = prefixDataFolder.list();
if (filesList == null || filesList.length < 1) {
Main.severe("配置文件夹中暂无任何前缀配置问,请检查。");
Main.severe("There's no configured prefix.");
Main.severe("Path: " + prefixDataFolder.getAbsolutePath());
return;
}
String[] filesList = prefixDataFolder.list();
if (filesList == null || filesList.length < 1) {
Main.error("配置文件夹中暂无任何前缀配置问,请检查。");
Main.error("There's no configured prefix.");
Main.error("Path: " + prefixDataFolder.getAbsolutePath());
return;
}
List<File> files = Arrays.stream(filesList)
.map(s -> new File(prefixDataFolder, s))
.filter(File::isFile)
.collect(Collectors.toList());
List<File> files = Arrays.stream(filesList)
.map(s -> new File(prefixDataFolder, s))
.filter(File::isFile)
.collect(Collectors.toList());
HashMap<String, PrefixConfig> loaded = new HashMap<>();
HashMap<String, ConfiguredPrefix> dataPrefixes = new HashMap<>();
if (files.size() > 0) {
for (File file : files) {
try {
PrefixConfig prefix = addPrefix(file);
Main.debugging("完成前缀加载 " + prefix.getIdentifier() + " : " + prefix.getName());
loaded.put(prefix.getIdentifier(), prefix);
} catch (Exception ex) {
Main.severe("在加载前缀 " + file.getAbsolutePath() + " 时出错,请检查配置!");
Main.severe("Error occurred when loading prefix #" + file.getAbsolutePath() + " !");
ex.printStackTrace();
}
}
}
if (files.size() > 0) {
for (File file : files) {
try {
ConfiguredPrefix prefix = new ConfiguredPrefix(file);
Main.log("完成前缀加载 " + prefix.getIdentifier() + " : " + prefix.getName());
Main.log("Successfully loaded " + prefix.getIdentifier() + " : " + prefix.getName());
dataPrefixes.put(prefix.getIdentifier(), prefix);
} catch (Exception ex) {
Main.error("在加载前缀 " + file.getAbsolutePath() + " 时出错,请检查配置!");
Main.error("Error occurred when loading prefix #" + file.getAbsolutePath() + " !");
ex.printStackTrace();
}
}
}
this.prefixes = loaded;
}
PrefixManager.prefixes.clear();
PrefixManager.prefixes = dataPrefixes;
}
public void loadDefaultPrefix() {
this.defaultPrefix = new PrefixConfig(
"default",
PluginConfig.DEFAULT_PREFIX.NAME.getNotNull(),
PluginConfig.DEFAULT_PREFIX.DESCRIPTION.getNotNull(),
PluginConfig.DEFAULT_PREFIX.CONTENT.resolve(),
PluginConfig.DEFAULT_PREFIX.PERIOD.resolve(),
PluginConfig.DEFAULT_PREFIX.WEIGHT.getNotNull(),
null,
readActions(PluginConfig.DEFAULT_PREFIX.ACTIONS.get()),
PluginConfig.DEFAULT_PREFIX.ITEM.NOT_USING.getNotNull(),
PluginConfig.DEFAULT_PREFIX.ITEM.USING.get(), null
);
Main.debugging(" 完成默认前缀加载 " + defaultPrefix.getName());
}
public static void loadDefaultPrefix() {
PrefixManager.defaultPrefix = null;
ConfigurationSection defaultPrefixSection = ConfigManager.getPluginConfig()
.getConfig().getConfigurationSection("defaultPrefix");
if (defaultPrefixSection != null) {
try {
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()
);
PrefixManager.defaultPrefix = new ConfiguredPrefix("default", name, content, 0, null, itemNotUsing, null, itemUsing);
} catch (Exception ex) {
Main.error("在加载默认前缀时出错,请检查配置!");
Main.error("Error occurred when loading default prefix, please check the configuration.");
ex.printStackTrace();
}
} else {
PrefixManager.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()
);
}
public List<PrefixConfig> getVisiblePrefix(Player player) {
return getPrefixes().values().stream()
.filter(c -> c.isVisible(player))
.sorted(Comparator.comparingInt(PrefixConfig::getWeight))
.collect(Collectors.toList());
}
Main.log("完成默认前缀加载 " + defaultPrefix.getName());
Main.log("Successfully loaded default prefix " + defaultPrefix.getName());
}
@NotNull
public PrefixConfig getDefaultPrefix() {
return defaultPrefix;
}
public static List<ConfiguredPrefix> getVisiblePrefix() {
return PrefixManager.getPrefixes().values().stream()
.filter(ConfiguredPrefix::isVisibleNoPermission)
.sorted(Comparator.comparingInt(ConfiguredPrefix::getWeight))
.collect(Collectors.toList());
}
@NotNull
public Map<String, PrefixConfig> getPrefixes() {
return prefixes;
}
@NotNull
public static ConfiguredPrefix getDefaultPrefix() {
return defaultPrefix;
}
@Nullable
public PrefixConfig getPrefix(String identifier) {
if (identifier == null) {
return null;
} else if (identifier.equalsIgnoreCase("default")) {
return getDefaultPrefix();
} else {
return getPrefixes().get(identifier);
}
}
@NotNull
public static HashMap<String, ConfiguredPrefix> getPrefixes() {
return prefixes;
}
@Nullable
public static ConfiguredPrefix getPrefix(String identifier) {
if (identifier == null) {
return null;
} else if (identifier.equalsIgnoreCase("default")) {
return getDefaultPrefix();
} else {
return getPrefixes().get(identifier);
}
}
protected File getStorageFolder() {
if (PluginConfig.CUSTOM_STORAGE.ENABLE.getNotNull()) {
return new File(PluginConfig.CUSTOM_STORAGE.PATH.getNotNull());
} else {
return new File(Main.getInstance().getDataFolder() + File.separator + FOLDER_NAME);
}
}
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);
}
}
public static @NotNull PrefixConfig addPrefix(@NotNull File file) throws Exception {
FileConfiguration conf = YamlConfiguration.loadConfiguration(file);
String identifier = conf.getString("identifier");
if (identifier == null)
throw new Exception("配置文件 " + file.getAbsolutePath() + " 中没有配置前缀的唯一标识。");
String name = conf.getString("name");
if (name == null) throw new Exception("配置文件 " + file.getAbsolutePath() + " 中没有配置前缀的显示名称。");
List<String> content = new ArrayList<>();
if (conf.isList("content")) {
content = conf.getStringList("content");
} else {
String single = conf.getString("content");
if (single != null) content.add(single);
}
return new PrefixConfig(
identifier, name,
conf.getStringList("description"),
content,
conf.getLong("period", -1L),
conf.getInt("weight", 1),
conf.getString("permission"),
readActions(conf.getStringList("actions")),
readItem(
conf.getConfigurationSection("item.has-perm"),
new ItemStackFactory(Material.STONE)
.setDisplayName(name)
.addLore("§a➥ 点击切换到该前缀")
.toItemStack()
),
readItem(conf.getConfigurationSection("item.using"), null),
readItem(conf.getConfigurationSection("item.no-perm"), null)
);
}
@Contract("_,!null->!null")
protected static ItemStack readItem(@Nullable ConfigurationSection section,
@Nullable ItemStack defaultValue) throws Exception {
if (section == null) return defaultValue;
else return XItemStack.deserialize(section);
}
protected static List<GUIActionConfiguration> readActions(@NotNull List<String> strings) {
return strings.stream().map(GUIActionConfiguration::deserialize).filter(Objects::nonNull).collect(Collectors.toList());
}
}
@@ -42,4 +42,8 @@ public class ServiceManager {
return user.getCachedData().getPermissionData().checkPermission(permission).asBoolean();
}
public static boolean hasPermission(Player player, String permission) {
return hasPermission(getUser(player), permission);
}
}
@@ -1,13 +1,15 @@
package cc.carm.plugin.userprefix.manager;
import cc.carm.lib.easyplugin.gui.GUI;
import cc.carm.plugin.userprefix.Main;
import cc.carm.plugin.userprefix.configuration.PluginConfig;
import cc.carm.plugin.userprefix.UserPrefixAPI;
import cc.carm.plugin.userprefix.conf.PluginConfig;
import cc.carm.plugin.userprefix.conf.PluginMessages;
import cc.carm.plugin.userprefix.conf.prefix.PrefixConfig;
import cc.carm.plugin.userprefix.event.UserPrefixChangeEvent;
import cc.carm.plugin.userprefix.event.UserPrefixExpireEvent;
import cc.carm.plugin.userprefix.model.ConfiguredPrefix;
import cc.carm.plugin.userprefix.nametag.UserNameTag;
import cc.carm.plugin.userprefix.hooker.UserNameTag;
import cc.carm.plugin.userprefix.ui.PrefixSelectGUI;
import cc.carm.plugin.userprefix.util.gui.GUI;
import net.luckperms.api.model.user.User;
import net.luckperms.api.node.NodeType;
import net.luckperms.api.node.types.MetaNode;
@@ -21,270 +23,257 @@ import java.util.stream.Collectors;
public class UserManager {
public static HashMap<UUID, UserNameTag> nameTags = new HashMap<>();
// LuckPerms竟然会把所有的metaKey全部转换为小写... 那我这里就直接写成小写吧~
public static final @NotNull String META_KEY = "userprefix";
public static HashSet<UUID> checkingPlayers = new HashSet<>();
protected final HashMap<UUID, UserNameTag> nameTags = new HashMap<>();
@Nullable
public static UserNameTag getNameTag(Player player) {
if (PluginConfig.Functions.NAME_PREFIX.get()) {
if (nameTags.containsKey(player.getUniqueId())) {
return nameTags.get(player.getUniqueId());
} else {
return createNameTag(player);
}
} else {
return null;
}
}
protected final HashSet<UUID> checkingPlayers = new HashSet<>();
@NotNull
public static UserNameTag createNameTag(Player player) {
if (nameTags.containsKey(player.getUniqueId())) return nameTags.get(player.getUniqueId());
UserNameTag nameTag = new UserNameTag(player);
nameTags.put(player.getUniqueId(), nameTag);
return nameTag;
}
@Nullable
public UserNameTag getNameTag(Player player) {
if (this.isNamePrefixEnabled()) {
if (nameTags.containsKey(player.getUniqueId())) {
return nameTags.get(player.getUniqueId());
} else {
return createNameTag(player);
}
} else {
return null;
}
}
public static void initPlayer(Player player) {
UserManager.checkPrefix(player, false);
if (PluginConfig.Functions.NAME_PREFIX.get()) {
UserManager.createNameTag(player);
UserManager.updatePrefixView(player, true);
}
}
@NotNull
public UserNameTag createNameTag(Player player) {
if (nameTags.containsKey(player.getUniqueId())) return nameTags.get(player.getUniqueId());
UserNameTag nameTag = new UserNameTag(player);
nameTags.put(player.getUniqueId(), nameTag);
return nameTag;
}
public static void unloadPlayer(Player player) {
PrefixSelectGUI.removeOpening(player);
UserManager.unloadNameTag(player.getUniqueId());
UserManager.checkingPlayers.remove(player.getUniqueId());
GUI.removeOpenedGUI(player); // 清空打开过的GUI缓存 (用于记录物品点击的
}
public void initPlayer(Player player) {
checkPrefix(player, false);
if (this.isNamePrefixEnabled()) {
createNameTag(player);
updatePrefixView(player, true);
}
}
/**
* 更新前缀显示效果
*
* @param player 玩家
* @param loadOthers 是否为玩家更新其他人的前缀(一般用于加入游戏)
*/
public static void updatePrefixView(Player player, boolean loadOthers) {
if (!PluginConfig.Functions.NAME_PREFIX.get()) return; //未启用的情况下,不需要进行任何操作。
UserNameTag tag = getNameTag(player);
if (tag == null) return; //未启用的情况下,不需要进行任何操作。
public void unloadPlayer(Player player) {
PrefixSelectGUI.removeOpening(player);
unloadNameTag(player.getUniqueId());
checkingPlayers.remove(player.getUniqueId());
GUI.removeOpenedGUI(player); // 清空打开过的GUI缓存 (用于记录物品点击的
}
ConfiguredPrefix playerPrefix = UserManager.getPrefix(player);
/**
* 更新前缀显示效果
*
* @param player 玩家
* @param loadOthers 是否为玩家更新其他人的前缀(一般用于加入游戏)
*/
public void updatePrefixView(Player player, boolean loadOthers) {
if (!this.isNamePrefixEnabled()) return; //未启用的情况下,不需要进行任何操作。
UserNameTag tag = getNameTag(player);
if (tag == null) return; //未启用的情况下,不需要进行任何操作。
tag.setPrefix(playerPrefix.getContent());
tag.setOrder(playerPrefix.getWeight());
PrefixConfig playerPrefix = getPrefix(player);
boolean descOrder = PluginConfig.FUNCTIONS.NAME_PREFIX.ORDER_DESC.getNotNull();
Main.debug("为玩家 " + player.getName() + " 设置了 " + player.getName() + "的前缀为 #" + playerPrefix.getWeight() + " " + playerPrefix.getName());
tag.setPrefix(playerPrefix.getContent(player));
tag.setOrder(descOrder ? 999 - playerPrefix.getWeight() : playerPrefix.getWeight());
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());
Main.debugging("为玩家 " + player.getName() + " 设置了 " + player.getName() + "的前缀为 #" + playerPrefix.getWeight() + " " + playerPrefix.getName());
}
for (Player onlinePlayer : Bukkit.getOnlinePlayers()) {
if (onlinePlayer.equals(player)) continue;
UserNameTag onlinePlayerTag = getNameTag(onlinePlayer);
if (loadOthers) {
ConfiguredPrefix onlinePlayerPrefix = UserManager.getPrefix(onlinePlayer);
tag.setPrefix(onlinePlayer, onlinePlayerPrefix.getContent());
tag.setOrder(onlinePlayer, onlinePlayerPrefix.getWeight());
Main.debug("为玩家 " + player.getName() + " 设置了 " + onlinePlayer.getName() + "的前缀为 #" + onlinePlayerPrefix.getWeight() + " " + onlinePlayerPrefix.getName());
}
}
}
if (onlinePlayerTag != null) {
onlinePlayerTag.setPrefix(player, playerPrefix.getContent(onlinePlayer));
onlinePlayerTag.setOrder(player, descOrder ? 999 - playerPrefix.getWeight() : playerPrefix.getWeight());
Main.debugging("为玩家 " + onlinePlayer.getName() + " 设置了 " + player.getName() + "的前缀为 #" + playerPrefix.getWeight() + " " + playerPrefix.getName());
/**
* 检查玩家的前缀的使用权
*
* @param player 玩家
* @param updateView 是否更新头顶与TabList中的前缀
*/
public static void checkPrefix(Player player, boolean updateView) {
if (checkingPlayers.contains(player.getUniqueId())) {
/*
* 这里为了避免极短时间内的重复触发导致多次判断且结果相同误导玩家,
* 故没有采用同步锁,而是采用添加到一个临时Set中,对Set中玩家跳过判断。
*/
return;
}
checkingPlayers.add(player.getUniqueId());
String currentPrefixData = UserManager.getPrefixData(player);
}
if (!UserManager.isPrefixUsable(player, currentPrefixData)) {
ConfiguredPrefix currentPrefix = PrefixManager.getPrefix(currentPrefixData);
ConfiguredPrefix newPrefix = UserManager.getHighestPrefix(player);
if (loadOthers) {
PrefixConfig onlinePlayerPrefix = getPrefix(onlinePlayer);
tag.setPrefix(onlinePlayer, onlinePlayerPrefix.getContent(player));
tag.setOrder(onlinePlayer, descOrder ? 999 - onlinePlayerPrefix.getWeight() : onlinePlayerPrefix.getWeight());
Main.debugging("为玩家 " + player.getName() + " 设置了 " + onlinePlayer.getName() + "的前缀为 #" + onlinePlayerPrefix.getWeight() + " " + onlinePlayerPrefix.getName());
}
}
}
if (currentPrefix != null) {
//当前前缀不为空,则代表属于前缀过期的情况
Bukkit.getPluginManager().callEvent(new UserPrefixExpireEvent(player, currentPrefix));
/**
* 检查玩家的前缀的使用权
*
* @param player 玩家
* @param updateView 是否更新头顶与TabList中的前缀
*/
public void checkPrefix(Player player, boolean updateView) {
if (checkingPlayers.contains(player.getUniqueId())) {
/*
* 这里为了避免极短时间内的重复触发导致多次判断且结果相同误导玩家,
* 故没有采用同步锁,而是采用添加到一个临时Set中,对Set中玩家跳过判断。
*/
return;
}
checkingPlayers.add(player.getUniqueId());
String currentPrefixData = getPrefixData(player);
// 发送消息
PluginConfig.Messages.EXPIRED.sendWithPlaceholders(player,
new String[]{"%(newName)", "%(oldName)"},
new Object[]{newPrefix.getName(), currentPrefix.getName()}
);
if (!isPrefixUsable(player, currentPrefixData)) {
PrefixConfig currentPrefix = UserPrefixAPI.getPrefixManager().getPrefix(currentPrefixData);
PrefixConfig newPrefix = getHighestPrefix(player);
// 播放声音
PluginConfig.Sounds.PREFIX_EXPIRED.play(player);
} else {
// 当前前缀为空,则代表是旧的前缀不存在了,
PluginConfig.Messages.REMOVED.sendWithPlaceholders(player,
new String[]{"%(newName)", "%(oldName)"},
new Object[]{newPrefix.getName(), currentPrefixData}
);
}
if (currentPrefix != null) {
//当前前缀不为空,则代表属于前缀过期的情况
UserPrefixExpireEvent.call(player, currentPrefix);
UserPrefixChangeEvent event = new UserPrefixChangeEvent(player, currentPrefix, newPrefix);
Bukkit.getPluginManager().callEvent(event);
// 发送消息
PluginMessages.EXPIRED.sendTo(player, currentPrefix.getName(), newPrefix.getName());
if (!event.isCancelled()) {
// 更新前缀
UserManager.setPrefix(player, event.getAfter(), updateView);
}
// 播放声音
PluginConfig.SOUNDS.PREFIX_EXPIRED.playTo(player);
} else {
// 当前前缀为空,则代表是旧的前缀不存在了,
PluginMessages.REMOVED.sendTo(player, newPrefix.getName());
}
}
UserPrefixChangeEvent.call(player, currentPrefix, newPrefix, (after) -> {
if (after != null) setPrefix(player, after, updateView);
checkingPlayers.remove(player.getUniqueId());
});
checkingPlayers.remove(player.getUniqueId());
}
} else {
checkingPlayers.remove(player.getUniqueId());
}
}
public static void unloadNameTag(UUID uuid) {
nameTags.remove(uuid);
}
public void unloadNameTag(UUID uuid) {
nameTags.remove(uuid);
}
/**
* 得到玩家的前缀。
* 该方法会自动判断玩家当前的前缀是否可用,并返回最终可用的前缀。
*
* @param player 玩家
* @return 前缀配置
*/
@NotNull
public static ConfiguredPrefix getPrefix(Player player) {
String identifier = getPrefixData(player);
if (identifier == null || !isPrefixUsable(player, identifier)) {
return getHighestPrefix(player);
} else {
ConfiguredPrefix prefix = PrefixManager.getPrefix(identifier);
return prefix == null ? PrefixManager.getDefaultPrefix() : prefix;
}
}
/**
* 得到玩家的前缀。
* 该方法会自动判断玩家当前的前缀是否可用,并返回最终可用的前缀。
*
* @param player 玩家
* @return 前缀配置
*/
@NotNull
public PrefixConfig getPrefix(Player player) {
String identifier = getPrefixData(player);
if (identifier == null || !isPrefixUsable(player, identifier)) {
return getHighestPrefix(player);
} else {
PrefixConfig prefix = UserPrefixAPI.getPrefixManager().getPrefix(identifier);
return prefix == null ? UserPrefixAPI.getDefaultPrefix() : prefix;
}
}
/**
* 设定玩家前缀
*
* @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 玩家
* @param prefix 前缀配置
* @param updateView 是否更新头顶上、TabList的前缀
*/
public void setPrefix(Player player, PrefixConfig prefix, boolean updateView) {
setPrefixData(player, prefix.getIdentifier());
prefix.executeActions(player);
if (updateView) updatePrefixView(player, false);
}
/**
* 得到玩家所有可用的前缀
*
* @param player 玩家
* @return 可用前缀列表
*/
@NotNull
public static List<ConfiguredPrefix> getUsablePrefixes(Player player) {
return PrefixManager.getPrefixes().values().stream()
.filter(configuredPrefix -> isPrefixUsable(player, configuredPrefix)) //过滤出玩家可用的前缀
.sorted(Comparator.comparingInt(ConfiguredPrefix::getWeight)) // 以前缀排序
.collect(Collectors.toList()); // 返回集合
}
/**
* 得到玩家所有可用的前缀
*
* @param player 玩家
* @return 可用前缀列表
*/
@NotNull
public List<PrefixConfig> getUsablePrefixes(Player player) {
return UserPrefixAPI.getPrefixManager().getPrefixes().values().stream()
.filter(prefix -> prefix.checkPermission(player)) //过滤出玩家可用的前缀
.sorted(Comparator.comparingInt(PrefixConfig::getWeight)) // 以前缀排序
.collect(Collectors.toList()); // 返回集合
}
/**
* 得到玩家可使用的最高权重的权限
* 注意:若配置文件中关闭了 “autoUsePrefix” ,则会返回默认前缀。
*
* @param player 玩家
* @return 权限内容
*/
@NotNull
public static ConfiguredPrefix getHighestPrefix(Player player) {
if (PluginConfig.Functions.AUTO_USE.get()) {
// 关闭了自动选择,就直接给默认的前缀,让玩家自己去设置吧~
return PrefixManager.getDefaultPrefix();
}
return getUsablePrefixes(player).stream()
.max(Comparator.comparingInt(ConfiguredPrefix::getWeight)) // 取权重最大
.orElseGet(PrefixManager::getDefaultPrefix); // 啥都没有? 返回默认前缀。
}
/**
* 得到玩家可使用的最高权重的权限
* 注意:若配置文件中关闭了 “autoUsePrefix” ,则会返回默认前缀。
*
* @param player 玩家
* @return 权限内容
*/
@NotNull
public PrefixConfig getHighestPrefix(Player player) {
if (!PluginConfig.FUNCTIONS.AUTO_USE.getNotNull()) {
// 关闭了自动选择,就直接给默认的前缀,让玩家自己去设置吧~
return UserPrefixAPI.getDefaultPrefix();
}
return getUsablePrefixes(player).stream()
.max(Comparator.comparingInt(PrefixConfig::getWeight)) // 取权重最大
.orElseGet(UserPrefixAPI::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.isPublic()
|| ServiceManager.hasPermission(ServiceManager.getUser(player), configuredPrefix.getPermission());
}
/**
* 得到用户当前正在使用的前缀Identifier。
* 该方法通过LuckPerms的MetaData实现,因此可以通过指令去操作。
*
* @param player 玩家
* @return 正在使用的前缀Identifier(若不存在则返回null, 代表未设置前缀)
*/
@Nullable
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不会去覆盖一个Meta,需要手动清除。
if (prefixIdentifier != null) {
user.data().add(MetaNode.builder("userprefix", prefixIdentifier).build());
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")));
}
/**
* 判断一个前缀对某玩家是否可用
*
* @param player 玩家
* @param prefixIdentifier 前缀标识
* @return 若前缀标识不存在,则返回false;若前缀为默认前缀,或该前缀无权限,或玩家有该前缀的权限,则返回true。
*/
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
public boolean isPrefixUsable(Player player, String prefixIdentifier) {
if (prefixIdentifier == null || prefixIdentifier.equalsIgnoreCase("default")) return true;
PrefixConfig prefix = UserPrefixAPI.getPrefixManager().getPrefix(prefixIdentifier);
return prefix != null && prefix.checkPermission(player);
}
/**
* 得到用户当前正在使用的前缀Identifier。
* 该方法通过LuckPerms的MetaData实现,因此可以通过指令去操作。
*
* @param player 玩家
* @return 正在使用的前缀Identifier(若不存在则返回null, 代表未设置前缀)
*/
@Nullable
public String getPrefixData(Player player) {
return ServiceManager.getAPI().getMetaData(player)
.getMetaValue(META_KEY, String::valueOf)
.orElse(null);
}
/**
* 设定用户所使用的的prefix。
* 该方法通过LuckPerms的MetaData实现,因此可以通过指令去操作。
*
* @param player 玩家
* @param prefixIdentifier 前缀的标识
*/
public void setPrefixData(Player player, String prefixIdentifier) {
User user = ServiceManager.getUser(player);
clearPrefixData(player); // 清除掉旧的数据,LuckPerms不会去覆盖一个Meta,需要手动清除。
if (prefixIdentifier != null) {
user.data().add(MetaNode.builder(META_KEY, prefixIdentifier).build());
ServiceManager.getService().getUserManager().saveUser(user); // 保存数据
}
}
/**
* 清除玩家所选择的前缀数据
*
* @param player 玩家
*/
public void clearPrefixData(Player player) {
User user = ServiceManager.getUser(player);
user.data().clear(NodeType.META.predicate(mn -> mn.getMetaKey().equals(META_KEY)));
}
private boolean isNamePrefixEnabled() {
return PluginConfig.FUNCTIONS.NAME_PREFIX.ENABLE.getNotNull() && !Main.getInstance().isOnFolia();
}
}
@@ -1,159 +0,0 @@
package cc.carm.plugin.userprefix.model;
import cc.carm.plugin.userprefix.util.ColorParser;
import cc.carm.plugin.userprefix.util.ItemStackFactory;
import cc.carm.plugin.userprefix.util.MessageUtil;
import org.bukkit.Material;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.util.List;
public class ConfiguredPrefix {
@Nullable
private File dataFile;
@Nullable
private FileConfiguration configuration;
String identifier;
String name;
String content;
int weight;
String permission;
ItemStack itemHasPermission;
ItemStack itemNoPermission;
ItemStack itemWhenUsing;
public ConfiguredPrefix(@NotNull File dataFile) {
this.dataFile = dataFile;
this.configuration = YamlConfiguration.loadConfiguration(dataFile);
if (getConfiguration() != null) {
this.identifier = getConfiguration().getString("identifier", "ERROR");
this.name = getConfiguration().getString("name", "ERROR");
this.content = getConfiguration().getString("content", "&r");
this.permission = getConfiguration().getString("permission");
this.weight = getConfiguration().getInt("weight", 1);
this.itemHasPermission = (ItemStack) getConfiguration().get("itemHasPermission",
new ItemStackFactory(Material.STONE).setDisplayName(name).addLore(" ").addLore("§a➥ 点击切换到该前缀").toItemStack()
);
this.itemNoPermission = (ItemStack) getConfiguration().get("itemNoPermission", itemHasPermission);
this.itemWhenUsing = (ItemStack) getConfiguration().get("itemUsing", itemHasPermission);
}
}
public ConfiguredPrefix(@NotNull String identifier,
@NotNull String name,
@NotNull String content,
int weight, @Nullable String permission,
@NotNull ItemStack itemHasPermission,
@Nullable ItemStack itemNoPermission,
@Nullable 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;
}
@Nullable
public FileConfiguration getConfiguration() {
return configuration;
}
@NotNull
public String getIdentifier() {
return identifier;
}
@NotNull
public String getName() {
return name;
}
@NotNull
public String getContent() {
return ColorParser.parse(content);
}
public int getWeight() {
return weight;
}
@Nullable
public String getPermission() {
return permission;
}
@NotNull
public ItemStack getItemHasPermission(@Nullable Player player) {
return parseItemStackText(this.itemHasPermission, player);
}
@NotNull
public ItemStack getItemHasPermission() {
return getItemHasPermission(null);
}
@Nullable
public ItemStack getItemNoPermission(@Nullable Player player) {
return parseItemStackText(itemNoPermission, player);
}
@Nullable
public ItemStack getItemNoPermission() {
return getItemNoPermission(null);
}
@Nullable
public ItemStack getItemWhenUsing(@Nullable Player player) {
return parseItemStackText(itemWhenUsing, player);
}
@Nullable
public ItemStack getItemWhenUsing() {
return getItemWhenUsing(null);
}
public boolean isPublic() {
return getPermission() == null;
}
public boolean isVisibleNoPermission() {
return this.itemNoPermission != null;
}
@NotNull
private static ItemStack parseItemStackText(@NotNull ItemStack source, @Nullable Player player) {
if (player == null) return source;
ItemMeta meta = source.getItemMeta();
String displayName = null;
List<String> lore = null;
if (meta != null) {
if (meta.hasDisplayName()) displayName = meta.getDisplayName();
if (meta.hasLore()) lore = meta.getLore();
}
ItemStackFactory factory = new ItemStackFactory(source);
if (displayName != null) factory.setDisplayName(MessageUtil.setPlaceholders(player, displayName));
if (lore != null) factory.setLore(MessageUtil.setPlaceholders(player, lore));
return factory.toItemStack();
}
}
@@ -1,97 +1,117 @@
package cc.carm.plugin.userprefix.ui;
import cc.carm.plugin.userprefix.configuration.PluginConfig;
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.MessageUtil;
import cc.carm.plugin.userprefix.util.gui.AutoPagedGUI;
import cc.carm.plugin.userprefix.util.gui.GUIItem;
import cc.carm.plugin.userprefix.util.gui.GUIType;
import cc.carm.lib.easyplugin.gui.GUIItem;
import cc.carm.lib.easyplugin.gui.GUIType;
import cc.carm.lib.easyplugin.gui.paged.AutoPagedGUI;
import cc.carm.plugin.userprefix.UserPrefixAPI;
import cc.carm.plugin.userprefix.conf.PluginConfig;
import cc.carm.plugin.userprefix.conf.PluginMessages;
import cc.carm.plugin.userprefix.conf.prefix.PrefixConfig;
import cc.carm.plugin.userprefix.event.UserPrefixChangeEvent;
import org.bukkit.entity.Player;
import org.bukkit.event.inventory.ClickType;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
public class PrefixSelectGUI extends AutoPagedGUI {
public static HashSet<Player> openingUsers = new HashSet<>();
public static HashSet<Player> openingUsers = new HashSet<>();
Player player;
protected final Player player;
public PrefixSelectGUI(Player player) {
super(GUIType.SIXBYNINE, PluginConfig.GUI.TITLE.get(), 10, 43);
this.player = player;
public PrefixSelectGUI(Player player) {
super(GUIType.SIX_BY_NINE, PluginConfig.GUI.TITLE.get(), 10, 43);
this.player = player;
setPreviousPageSlot(18);
setNextPageSlot(26);
setPreviousPageSlot(18);
setNextPageSlot(26);
loadItems();
}
loadExtraIcons();
loadItems();
}
public Player getPlayer() {
return player;
}
public Player getPlayer() {
return player;
}
public void loadItems() {
List<ConfiguredPrefix> prefixList = new ArrayList<>();
prefixList.add(PrefixManager.getDefaultPrefix());
prefixList.addAll(PrefixManager.getVisiblePrefix()); //只需要读取看得见的
public void loadExtraIcons() {
PluginConfig.GUI.ITEMS.getNotNull().getItems().values().forEach(v -> v.setupItems(player, this));
}
ConfiguredPrefix usingPrefix = UserManager.getPrefix(getPlayer());
public void loadItems() {
List<PrefixConfig> prefixList = new ArrayList<>();
prefixList.add(UserPrefixAPI.getPrefixManager().getDefaultPrefix());
prefixList.addAll(UserPrefixAPI.getPrefixManager().getVisiblePrefix(player)); //只需要读取看得见的
for (ConfiguredPrefix prefix : prefixList) {
if (prefix.getIdentifier().equals(usingPrefix.getIdentifier())) {
addItem(new GUIItem(prefix.getItemWhenUsing(player) != null ? prefix.getItemWhenUsing(player) : prefix.getItemHasPermission(player)));
} else if (UserManager.isPrefixUsable(player, prefix)) {
addItem(new GUIItem(prefix.getItemHasPermission(player)) {
@Override
public void onClick(ClickType type) {
//再次检查,防止打开GUI后、选择前的时间段内权限消失
if (UserManager.isPrefixUsable(player, prefix)) {
player.closeInventory();
UserManager.setPrefix(player, prefix, true);
PrefixConfig usingPrefix = UserPrefixAPI.getUserManager().getPrefix(getPlayer());
PluginConfig.Sounds.PREFIX_CHANGE.play(player);
MessageUtil.sendWithPlaceholders(player, PluginConfig.Messages.SELECTED.get(),
new String[]{"%(name)"},
new Object[]{prefix.getName()});
for (PrefixConfig prefix : prefixList) {
if (prefix.getIdentifier().equals(usingPrefix.getIdentifier())) {
addItem(new GUIItem(Optional
.ofNullable(prefix.getItemWhenUsing(player))
.orElse(prefix.getItemHasPermission(player))
));
} else if (prefix.checkPermission(player)) {
addItem(new GUIItem(prefix.getItemHasPermission(player)) {
}
}
});
} else {
addItem(new GUIItem(prefix.getItemNoPermission(player)));
}
}
@Override
public void onClick(Player clicker, ClickType type) {
player.closeInventory();
//再次检查,防止打开GUI后、选择前的时间段内权限消失
if (prefix.checkPermission(player)) {
// 发送消息与提示
PluginConfig.SOUNDS.PREFIX_CHANGE.playTo(player);
PluginMessages.SELECTED.sendTo(player, prefix.getName());
UserPrefixChangeEvent.call(player, usingPrefix, prefix, config -> {
if (config == null) return;
UserPrefixAPI.getUserManager().setPrefix(player, config, true);
});
} else {
PluginConfig.SOUNDS.GUI_CLICK.playTo(player);
}
}
});
} else {
addItem(new GUIItem(prefix.getItemNoPermission(player)) {
@Override
public void onClick(Player clicker, ClickType type) {
PluginConfig.SOUNDS.GUI_CLICK.playTo(player);
}
});
}
}
}
}
@Override
public void onClose() {
removeOpening(player);
}
@Override
public void onClose() {
removeOpening(player);
}
public static void removeOpening(Player player) {
openingUsers.remove(player);
}
public static void removeOpening(Player player) {
openingUsers.remove(player);
}
public static void closeAll() {
for (Player player : new HashSet<>(openingUsers)) {
player.closeInventory();
}
openingUsers.clear();
}
public static void closeAll() {
for (Player player : new HashSet<>(openingUsers)) {
player.closeInventory();
}
openingUsers.clear();
}
public static void open(Player player) {
player.closeInventory(); // 防止冲突
PluginConfig.Sounds.GUI_OPEN.play(player);
new PrefixSelectGUI(player).openGUI(player);
openingUsers.add(player);
}
public static void open(Player player) {
// player.closeInventory(); // 防止冲突
PluginConfig.SOUNDS.GUI_OPEN.playTo(player);
new PrefixSelectGUI(player).openGUI(player);
openingUsers.add(player);
}
}
@@ -1,33 +0,0 @@
package cc.carm.plugin.userprefix.util;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class ColorParser {
public static String parse(String text) {
text = parseHexColor(text);
return parseColor(text);
}
public static String parseColor(final String text) {
return text.replaceAll("&", "§").replace("§§", "&");
}
public static String parseHexColor(String text) {
Pattern pattern = Pattern.compile("&\\((&?#[0-9a-fA-F]{6})\\)");
Matcher matcher = pattern.matcher(text);
while (matcher.find()) {
String hexColor = text.substring(matcher.start() + 2, matcher.end() - 1);
hexColor = hexColor.replace("&", "");
StringBuilder bukkitColorCode = new StringBuilder('§' + "x");
for (int i = 1; i < hexColor.length(); i++) {
bukkitColorCode.append('§').append(hexColor.charAt(i));
}
text = text.replaceAll("&\\(" + hexColor + "\\)", bukkitColorCode.toString().toLowerCase());
matcher.reset(text);
}
return text;
}
}
@@ -1,139 +0,0 @@
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.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
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(@NotNull String name) {
ItemMeta im = this.item.getItemMeta();
if (im != null) {
im.setDisplayName(ColorParser.parse(name));
this.item.setItemMeta(im);
}
return this;
}
public ItemStackFactory setLore(@NotNull List<String> loreList) {
ItemMeta im = this.item.getItemMeta();
if (im != null) {
im.setLore(
loreList.stream()
.map(ColorParser::parse)
.collect(Collectors.toList())
);
this.item.setItemMeta(im);
}
return this;
}
public ItemStackFactory addLore(@NotNull String s) {
ItemMeta im = this.item.getItemMeta();
if (im != null) {
List<String> lore = im.getLore() != null ? im.getLore() : new ArrayList<>();
lore.add(ColorParser.parse(s));
im.setLore(lore);
this.item.setItemMeta(im);
}
return this;
}
public ItemStackFactory addEnchant(@NotNull Enchantment enchant, int level, boolean ignoreLevelRestriction) {
ItemMeta im = this.item.getItemMeta();
if (im != null) {
im.addEnchant(enchant, level, ignoreLevelRestriction);
this.item.setItemMeta(im);
}
return this;
}
public ItemStackFactory removeEnchant(@NotNull Enchantment enchant) {
ItemMeta im = this.item.getItemMeta();
if (im != null) {
im.removeEnchant(enchant);
this.item.setItemMeta(im);
}
return this;
}
public ItemStackFactory addFlag(@NotNull ItemFlag flag) {
ItemMeta im = this.item.getItemMeta();
if (im != null) {
im.addItemFlags(flag);
this.item.setItemMeta(im);
}
return this;
}
public ItemStackFactory removeFlag(@NotNull ItemFlag flag) {
ItemMeta im = this.item.getItemMeta();
if (im != null) {
im.removeItemFlags(flag);
this.item.setItemMeta(im);
}
return this;
}
public ItemStackFactory setUnbreakable(boolean unbreakable) {
ItemMeta im = this.item.getItemMeta();
if (im != null) {
im.setUnbreakable(unbreakable);
this.item.setItemMeta(im);
}
return this;
}
}
@@ -1,102 +0,0 @@
package cc.carm.plugin.userprefix.util;
import me.clip.placeholderapi.PlaceholderAPI;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.stream.Collectors;
public class MessageUtil {
public static boolean hasPlaceholderAPI() {
return Bukkit.getPluginManager().getPlugin("PlaceholderAPI") != null;
}
public static void send(@Nullable CommandSender sender, List<String> messages) {
if (messages == null || messages.isEmpty() || sender == null) return;
for (String s : messages) {
sender.sendMessage(ColorParser.parse(s));
}
}
public static void send(@Nullable CommandSender sender, String... messages) {
send(sender, Arrays.asList(messages));
}
public static void sendWithPlaceholders(CommandSender sender, String... messages) {
sendWithPlaceholders(sender, Arrays.asList(messages));
}
public static void sendWithPlaceholders(@Nullable CommandSender sender, List<String> messages) {
if (messages == null || messages.isEmpty() || sender == null) return;
send(sender, setPlaceholders(sender, messages));
}
public static void sendWithPlaceholders(@Nullable CommandSender sender, List<String> messages, String param, Object value) {
sendWithPlaceholders(sender, messages, new String[]{param}, new Object[]{value});
}
public static void sendWithPlaceholders(@Nullable CommandSender sender, List<String> messages, String[] params, Object[] values) {
sendWithPlaceholders(sender, setCustomParams(messages, params, values));
}
public static String setPlaceholders(@Nullable CommandSender sender, String message) {
if (message == null) return null;
message = ColorParser.parse(message);
if (sender == null) return message;
if (hasPlaceholderAPI() && sender instanceof Player) {
return PlaceholderAPI.setPlaceholders((Player) sender, message);
} else {
return message;
}
}
public static List<String> setPlaceholders(@Nullable CommandSender sender, List<String> messages) {
if (messages == null || messages.isEmpty()) return new ArrayList<>();
messages = messages.stream().map(ColorParser::parse).collect(Collectors.toList());
if (sender == null) return messages;
if (hasPlaceholderAPI() && sender instanceof Player) {
return PlaceholderAPI.setPlaceholders((Player) sender, messages);
} else {
return messages;
}
}
public static List<String> setPlaceholders(@Nullable CommandSender sender, List<String> messages, String[] params, Object[] values) {
return setPlaceholders(sender, setCustomParams(messages, params, values));
}
public static List<String> setCustomParams(List<String> messages, String param, Object value) {
return setCustomParams(messages, new String[]{param}, new Object[]{value});
}
public static List<String> setCustomParams(List<String> messages, String[] params, Object[] values) {
if (params.length != values.length) return messages;
HashMap<String, Object> paramsMap = new HashMap<>();
for (int i = 0; i < params.length; i++) {
paramsMap.put(params[i], values[i]);
}
return setCustomParams(messages, paramsMap);
}
public static List<String> setCustomParams(List<String> messages, HashMap<String, Object> params) {
List<String> list = new ArrayList<>();
for (String message : messages) {
String afterMessage = message;
for (Map.Entry<String, Object> entry : params.entrySet()) {
afterMessage = afterMessage.replace(entry.getKey(), entry.getValue().toString());
}
list.add(afterMessage);
}
return list;
}
}
@@ -1,93 +0,0 @@
package cc.carm.plugin.userprefix.util.gui;
import cc.carm.plugin.userprefix.configuration.PluginConfig;
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 ? PluginConfig.GUI.Items.PREVIOUS_PAGE.get() : previousPageUI) {
@Override
public void onClick(ClickType type) {
if (type == ClickType.RIGHT) {
goFirstPage();
} else {
goPreviousPage();
}
PluginConfig.Sounds.GUI_CLICK.play(user);
openGUI(user);
}
});
} else {
setItem(previousPageSlot, null);
}
}
if (nextPageSlot >= 0) {
if (hasNextPage()) {
setItem(nextPageSlot, new GUIItem(nextPageUI == null ? PluginConfig.GUI.Items.NEXT_PAGE.get() : nextPageUI) {
@Override
public void onClick(ClickType type) {
if (type == ClickType.RIGHT) {
goLastPage();
} else {
goNextPage();
}
PluginConfig.Sounds.GUI_CLICK.play(user);
openGUI(user);
}
});
} else {
setItem(nextPageSlot, null);
}
}
super.openGUI(user);
}
}
@@ -1,170 +0,0 @@
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<GUIItem> 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);
}
}
@@ -1,291 +0,0 @@
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.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<Player, GUI> openedGUIs = new HashMap<>();
GUIType type;
String name;
public GUIItem[] items;
public Inventory inv;
boolean setCancelledIfClickOnTarget = true;
boolean setCancelledIfClickOnSelf = true;
boolean setCancelledIfClickOnOuter = true;
Map<String, Object> flags;
public Listener listener;
public GUI(GUIType type, String name) {
this.type = type;
this.name = ColorParser.parse(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;
case SIXBYNINE:
this.items = new GUIItem[54];
break;
case CANCEL:
default:
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<HumanEntity> 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");
}
inv = Bukkit.createInventory(null, this.items.length, this.name);
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) {
if (!(event.getWhoClicked() instanceof Player)) return;
Player p = (Player) event.getWhoClicked();
rawClickListener(event);
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) {
System.err.print("err cause by GUI(" + GUI.this + "), name=" + name);
e.printStackTrace();
return;
}
} else if (setCancelledIfClickOnOuter) {
event.setCancelled(true);
}
if (hasOpenedGUI(p)
&& 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()].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()) && 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;
removeOpenedGUI(p);
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);
}
}
@@ -1,77 +0,0 @@
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<GUIClickAction> actions = new HashSet<>();
public Set<GUIClickAction> 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 点击的类型
*/
public void onClick(ClickType type) {
}
public void addClickAction(GUIClickAction action) {
actions.add(action);
}
public void addActionIgnoreActive(GUIClickAction action) {
actionsIgnoreActive.add(action);
}
public void rawClickAction(InventoryClickEvent event) {
}
public void realRawClickAction(InventoryClickEvent event) {
}
/**
* 玩家点击GUI后执行的代码
*
* @param player 点击GUI的玩家
*/
public void customAction(Player player) {
}
public abstract static class GUIClickAction {
public abstract void run(ClickType type, Player player);
}
}
@@ -1,16 +0,0 @@
package cc.carm.plugin.userprefix.util.gui;
/**
* @author LSeng
*/
public enum GUIType {
ONEBYNINE,
TWOBYNINE,
THREEBYNINE,
FOURBYNINE,
FIVEBYNINE,
SIXBYNINE,
CANCEL;
}
@@ -1,75 +0,0 @@
package cc.carm.plugin.userprefix.util.gui;
import java.util.ArrayList;
import java.util.List;
public abstract class PagedGUI extends GUI {
List<GUIItem> 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<GUIItem> 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();
}
+6
View File
@@ -0,0 +1,6 @@
&f __ __ &3___ ____
&f / / / /__ ___ ____&3/ _ \_______ / _(_)_ __
&f/ /_/ (_-</ -_) __&3/ ___/ __/ -_) _/ /\ \ /
&f\____/___/\__/_/ &3/_/ /_/ \__/_//_//_\_\
&8| &fUser&3Prefix &8(&fv${project.version}&8) &7by &bYourCraft
&8| &7更多详情信息请参阅 &f${project.url} &7。
-85
View File
@@ -1,85 +0,0 @@
version: ${project.version} #配置文件版本,若与插件版本不同请记得检查配置文件内容
debug: false
# 统计数据设定
# 改选项用于帮助开发者统计插件版本与使用情况,且绝不会影响性能与使用体验。
# 当然,您也可以选择在这里关闭,或在plugins/bStats下的配置文件中关闭。
metrics: true
custom-storage:
# 自定义存储位置
# 默认存储位置为 “插件文件夹”/prefixes
# 可以规定到远程文件夹中去寻找前缀相关的设定
# 支持绝对文件路径,如 "/etc/minecraft/configurations/prefixes/"
enable: false # 是否启用
path: "prefixes/" # 一定要指向一个文件夹!
functions:
OnNamePrefix: true # 是否给头顶上添加前缀,该方法用到了头顶的那个计分板,如有冲突请关掉哦~
autoUsePrefix: true # 自动前缀显示 当玩家没有自己选择一个前缀的时候,会自动使用所拥有的的前缀中权重最高的那一个
chat:
# 聊天功能
# - 我不推荐使用本插件的聊天功能,而是建议使用其他的聊天插件。
# - 本插件仅仅提供了**最基本**的格式变量支持,不包含其他任何功能。
# - 注意聊天格式需要遵守Bukkit原格式,即不得缺失 “%1$s” 和 “%2$s” 。
# - 本插件的聊天功能不影响其他插件对聊天事件的操作。
enable: false # 是否启用
format: "<%UserPrefix_prefix%%1$s> %2$s" #聊天的格式,注意 “%1$s” 和 “%2$s” 不可缺少,分别代表 玩家名 与 消息内容 。
GUI:
title: "&f&l我的前缀 &8| 列表"
items:
# 【必须】 GUI中可能存在的其他物品
next-page: # 下一页物品,如果没有下一页则不显示
==: org.bukkit.inventory.ItemStack
type: ARROW
meta:
==: ItemMeta
meta-type: UNSPECIFIC
display-name: "&f下一页"
lore:
- ""
- "&f右键可前往最后一页哦~"
previous-page: # 上一页物品,如果没有上一页则不显示
==: org.bukkit.inventory.ItemStack
type: ARROW
meta:
==: ItemMeta
meta-type: UNSPECIFIC
display-name: "&f上一页"
lore:
- ""
- "&f右键可前往第一页哦~"
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✔ 您正在使用该前缀"
-87
View File
@@ -1,87 +0,0 @@
version: ${project.version} # DO NOT EDIT IT
debug: false #DEBUG OUT PUT
metrics: true #Metrics stats (to help developer know the stats)
custom-storage:
# Custom storage location
# default location is "./prefixes"
# Support absolute file path , such as "/etc/minecraft/configurations/prefixes/"
enable: false
path: "prefixes/" # Must be a folder!
GUI:
title: "&f&lMy Prefixes List" # Title of the GUI
items:
next-page: # only show has next page
==: org.bukkit.inventory.ItemStack
type: ARROW
meta:
==: ItemMeta
meta-type: UNSPECIFIC
display-name: "§fNext Page"
lore:
- ""
- "§fRight-Click to the last page"
previous-page: # only show has previous page
==: org.bukkit.inventory.ItemStack
type: ARROW
meta:
==: ItemMeta
meta-type: UNSPECIFIC
display-name: "§fPrevious Page"
lore:
- ""
- "§fRight-Click to the first page"
functions:
# Whether to add a prefix to the top of the head,
# this method uses the scoreboard above the head,
# please turn it off if there is a conflict.
OnNamePrefix: true
# Automatic prefix select.
# When the player does not choose a prefix by himself,
# the prefix with the highest weight will be used automatically
autoUsePrefix: true
chat:
# Chat Function
# - I recommend using other chat plugins instead of using this plugin,
# - this plugin only provides very basic chat format placeholders.
# - Notice that: format must has “%1$s” and “%2$s” for PlayerName and Message (Bukkit Chat Event)
enable: false
format: "<%UserPrefix_prefix%%1$s> %2$s"
Sounds:
# Format is [SOUND_NAME:Volume:Pitch] or [SOUND_NAME:Volume] or [SOUND_NAME]
openGUI: "BLOCK_NOTE_BLOCK_PLING:1:1"
guiClick: "UI_BUTTON_CLICK"
prefixChange: "ENTITY_VILLAGER_YES"
prefixExpired: "ENTITY_VILLAGER_NO"
# The default prefix's weight is 0.
defaultPrefix:
name: "Default prefix"
content: "&b"
itemNotUsing:
==: org.bukkit.inventory.ItemStack
type: NAME_TAG
meta:
==: ItemMeta
meta-type: UNSPECIFIC
display-name: "§fThe default prefix §f(Click to select)"
lore:
- ""
- "§a➥ Click to use"
itemUsing:
==: org.bukkit.inventory.ItemStack
type: NAME_TAG
meta:
==: ItemMeta
meta-type: UNSPECIFIC
display-name: "§fThe default prefix"
lore:
- ""
- "§a✔ Selected"
@@ -1,68 +0,0 @@
# identifier [Necessary]
# This will be used for data-storage.
identifier: "pro"
# Name [Necessary]
# Use in messages.
name: "&b&lPro&b"
# Content [Necessary]
# Use in Placeholders
content: "&b&lPro &b"
# Weight [Necessary]
# used for sorting in the GUI and TabList
# In GUI : the larger is displayed at the back
# At TabList : the larger is displayed at the top
weight: 1
# Permission [Unnecessary]
# If there is no permission for detection, everyone can use it,
# which means there is no need to configure "itemNoPermission"
# (because it is impossible to display items without permission at all)
permission: "yc.vip"
# itemHasPermission [Necessary]
# This Item will be displayed when player has permission
itemHasPermission:
==: org.bukkit.inventory.ItemStack
type: DIAMOND
meta:
==: ItemMeta
meta-type: UNSPECIFIC
display-name: "&b&lVIP Prefix"
lore:
- ""
- "&a➥ Click to use"
# itemUsing [Unnecessary]
# This Item will be displayed when the prefix is selected.
# If there is no such configuration, it will automatically display "itemHasPermission".
itemUsing:
==: org.bukkit.inventory.ItemStack
type: DIAMOND
meta:
==: ItemMeta
meta-type: UNSPECIFIC
display-name: "&b&lVIP Prefix"
enchants:
PROTECTION_ENVIRONMENTAL: 1 #Add an enchantment so it looks like its selected
lore:
- ""
- "&a✔ Selected"
# itemNoPermission [Unnecessary]
# If player doesn't have the permission,this item will be displayed.
# If this item is not configured, it will not be displayed in the GUI when the player does not have permission to use it.
itemNoPermission:
==: org.bukkit.inventory.ItemStack
type: INK_SACK
damage: 8
meta:
==: ItemMeta
meta-type: UNSPECIFIC
display-name: "&b&lVIP &c(Buy it!)"
lore:
- ""
- "&e✯ Buy the VIP to use it!"
-21
View File
@@ -1,21 +0,0 @@
selected:
- "&7You have selected the &f%(name) &7as current prefix."
expired:
- "&7Your prefix &f%(oldName) &7has expired,"
- "&7Now the prefix is changed to &f%(newName) &7."
removed:
- "&7Your using prefix has been removed, now the prefix is changed to &f%(newName) &7."
reload:
- "&a&lReload completed&7costs &f%(time)ms&7."
help:
- "&3&lUserPrefixAdmin &fHelp"
- "&8#/upa&f list"
- "&8- &7Show configured prefixes."
- "&8#/upa&f reload"
- "&8- &7Reload configuration."
list-title:
- "&3&lUserPrefixAdmin &fList"
list-value:
- "&8#%(weight) &f%(identifier)"
- "&8- &7Name &r%(name) &7Perm &r%(permission)"
- "&8- &7Example&r %(content) %(sender_name)"
@@ -0,0 +1,71 @@
# identifier [Necessary]
# This will be used for data-storage.
identifier: "pro"
# Name [Necessary]
# Use in messages.
name: "&b&lPro&b"
# Content [Necessary]
# Use in Placeholders
# Supports multiple lines for content rotation.
content: "&b&lPro &b"
# Period [Unnecessary]
# Set the rotation period of the prefix content, in milliseconds.
# If set to -1, it will not rotate.
# If set to 0, it will randomly select a line each time the prefix is fetched
# If set to a value greater than 0, it will rotate the content every specified milliseconds.
period: -1
# Weight [Necessary]
# used for sorting in the GUI and TabList
# In GUI : the larger is displayed at the back
# At TabList : the larger is displayed at the top
weight: 1
# Permission [Unnecessary]
# If there is no permission for detection, everyone can use it,
# which means there is no need to configure "itemNoPermission"
# (because it is impossible to display items without permission at all)
permission: "yc.vip"
# Actions when select [Unnecessary]
# Please check https://github.com/CarmJos/UserPrefix/tree/master/.documentation/ACTIONS.md
actions:
- "[CONSOLE] say %player_name% selected PRO "
# Display item configuration
# More details please see https://github.com/CryptoMorin/XSeries/wiki/XItemStack
item:
# itemHasPermission [Necessary]
# This Item will be displayed when player has permission
has-perm:
material: DIAMOND
name: "&b&lVIP Prefix"
lore:
- ""
- "&a➥ Click to use"
# itemUsing [Unnecessary]
# This Item will be displayed when the prefix is selected.
# If there is no such configuration, it will automatically display "itemHasPermission".
using:
material: DIAMOND
name: "&b&lVIP Prefix"
enchants:
PROTECTION_ENVIRONMENTAL: 1 #Add an enchantment so it looks like its selected
lore:
- ""
- "&a✔ Selected"
# itemNoPermission [Unnecessary]
# If player doesn't have the permission,this item will be displayed.
# If this item is not configured, it will not be displayed in the GUI when the player does not have permission to use it.
no-perm:
material: INK_SACK
data: 8
name: "&b&lVIP &c(Buy it!)"
lore:
- ""
- "&e✯ Buy the VIP to use it!"
@@ -0,0 +1,68 @@
# Nombre [Necesario]
# Uso en mensajes.
name: "&b&lPro&b"
# Contenido [Necesario]
# Uso en Placeholders
# Soporta múltiples líneas para la rotación de contenido.
content: "&b&lPro &b"
# Periodo [Innecesario]
# Establece el periodo de rotación del contenido del prefijo, en milisegundos.
# Si se establece en -1, no rotará.
# Si se establece en 0, seleccionará aleatoriamente una línea cada vez que se
# obtenga el prefijo.
# Si se establece en un valor mayor que 0, rotará el contenido cada los mil
# segundos especificados.
period: -1
# Weight [Necesario]
# utilizado para ordenar en la GUI y TabList
# En GUI : el más grande se muestra en la parte posterior
# En TabList : el más grande se muestra en la parte superior
weight: 1
# Permisos [Innecesario]
# Si no hay permiso para la detección, todo el mundo puede utilizarlo,
# lo que significa que no es necesario configurar "itemNoPermission"
# (porque es imposible mostrar elementos sin permiso en absoluto)
permission: "yc.vip"
# Acciones al seleccionar [Innecesario]
# Por favor, compruebe https://github.com/CarmJos/UserPrefix/tree/master/.documentation/ACTIONS.md
actions:
- "[CONSOLE] say %player_name% Has seleccionado La etiqueta PRO"
item:
# itemHasPermission [Necesario]
# Este elemento se mostrará cuando el jugador tenga permiso
has-perm:
material: DIAMOND
name: "&b&lVIP Prefix"
lore:
- ""
- "&a➥ Pulsa para utilizar"
# itemUsing [Innecesario]
# Este elemento se mostrará cuando se seleccione el prefijo.
# Si no existe tal configuración, se mostrará automáticamente "itemHasPermission".
using:
material: DIAMOND
name: "&b&lPrefix VIP"
enchants:
PROTECTION_ENVIRONMENTAL: 1 #Añade un encantamiento para que parezca que está seleccionado
lore:
- ""
- "&a✔ Seleccionado"
# itemNoPermission [Innecesario]
# Si el jugador no tiene el permiso,este item sera mostrado.
# Si este elemento no está configurado, no se mostrará en la GUI cuando el jugador no tenga permiso para usarlo.
no-perm:
material: INK_SACK
data: 8
name: "&b&lVIP &c(¡Cómpralo!)"
lore:
- ""
- "&e✯ Necesitas Comprar el VIP para utilizarlo!"
-21
View File
@@ -1,21 +0,0 @@
selected:
- "&7您选择了 &f%(name) &7作为当前显示的前缀。"
expired:
- "&7您先前使用的前缀 &f%(oldName) &7已到期。"
- "&7现在已为您重新调整为 &f%(newName) &7。"
removed:
- "&7您先前使用的前缀已被移除,现在已为您重新调整为 &f%(newName) &7。"
reload:
- "&a&l重载完成!&7共耗时 &f%(time)ms&7。"
help:
- "&3&l用户前缀系统 &f帮助"
- "&8#/upa&f list"
- "&8- &7查看当前前缀列表。"
- "&8#/upa&f reload"
- "&8- &7重载前缀配置。"
list-title:
- "&3&l用户前缀系统 &f前缀列表"
list-value:
- "&8#%(weight) &f%(identifier)"
- "&8- &7显示名 &r%(name) &7权限 &r%(permission)"
- "&8- &7内容示例&r %(content) %(sender_name)"
+10 -3
View File
@@ -1,4 +1,4 @@
main: cc.carm.plugin.userprefix.Main
main: ${project.package}.Main
name: ${project.name}
version: ${project.version}
authors:
@@ -7,11 +7,19 @@ authors:
- SakuraGame
website: ${project.url}
description: ${project.description}
api-version: 1.13
folia-supported: true
depend:
- LuckPerms
softdepend:
- PlaceholderAPI
permissions:
"UserPrefix.admin":
description: "用户前缀系统的管理命令权限。"
default: op
commands:
UserPrefix:
aliases:
@@ -20,6 +28,5 @@ commands:
UserPrefixAdmin:
aliases:
- upa
- prefixAdmin
permission: "UserPrefix.admin"
description: "用户前缀系统管理指令,可以查看前缀列表与重载配置文件。"
description: "用户前缀系统管理指令,可以查看前缀列表与重载配置文件。"
+49 -45
View File
@@ -5,16 +5,24 @@ identifier: "pro"
# 名字 [必须]
# 切换的时候左下角会弹提示 用的就是这个名字
name: "&b&lPro&b"
name: "&b&lPro &b会员"
# 内容 [必须]
# 显示在名字前面的内容
# 支持多行内容以实现前缀内容的轮换
content: "&b&lPro &b"
# 轮换周期 [非必须]
# 设置前缀内容的轮换周期,单位为毫秒。
# 若数值为-1,则表示不轮换。
# 若数值为0,则每次获取前缀时都会随机选择一行内容。
# 若数值大于0,则表示每隔指定毫秒数轮换一次内容
period: -1
# 权重 [必须]
# 用于GUI、TabList的排序和自动前缀显示
# 在GUI中,权重越高的会显示在越后面
# 在TabList中,权重越高的会显示在越上面
# 在TabList中,显示顺序可以在 config.yml 中自定义
weight: 1
@@ -22,57 +30,53 @@ weight: 1
# 如果没有就是人人都能用,也代表不用配置“itemNoPermission”了(因为压根不可能显示没权限时候的物品)
permission: "yc.pro"
# 有权限时显示的物品 [必须]
# 当用户有权限且未选中时,会显示该物品
itemHasPermission: #
==: org.bukkit.inventory.ItemStack
type: DIAMOND
meta:
==: ItemMeta
meta-type: UNSPECIFIC
display-name: "&b&lPro &b会员前缀"
# 选择这个前缀时执行的操作 [必须]
# 用于在玩家选择/取消选择前缀时执行相应动作,以便于实现一些特殊的功能。
# 具体操作类型详见介绍,前缀操作不支持限定点击类型。
# https://github.com/CarmJos/UserPrefix/tree/master/.documentation/ACTIONS.md
actions:
- "[CONSOLE] say %player_name% 选择了 Pro会员前缀 "
description: # 描述 [非必须]
- "&f尊贵的Pro会员专属称号。"
- "&f您将获得多种特权与更好的游戏体验。"
- "&f您可以输入 &b/vip &f指令查看详细特权!"
# 该前缀的GUI物品配置
# 物品配置方式详见 https://github.com/CryptoMorin/XSeries/wiki/XItemStack
item:
# 有权限时显示的物品 [必须]
# 当用户有权限且未选中时,会显示该物品
has-perm:
material: DIAMOND
name: "%(name)"
lore:
- "&7Pro会员专属称号"
- ""
- "&f尊贵的Pro会员专属称号。"
- "&f您将获得多种特权与更好的游戏体验。"
- ""
- "#desciption#{1,1}"
- "&a➥ 点击切换到该前缀"
# 正在使用时显示的物品 [非必需]
# 当用户正在使用时会显示这个物品,不配置即自动加载“itemHasPermission”
itemUsing:
==: org.bukkit.inventory.ItemStack
type: DIAMOND
meta:
==: ItemMeta
meta-type: UNSPECIFIC
display-name: "&b&lPro &b会员前缀"
# 正在使用时显示的物品 [非必需]
# 当用户正在使用时会显示这个物品,不配置即自动加载“itemHasPermission”
using:
material: DIAMOND
name: "%(name)"
flags:
- HIDE_ENCHANTS # 隐藏附魔显示
enchants:
PROTECTION_ENVIRONMENTAL: 1 #加一个附魔这样看上去就像是选中了的
lore:
- "&7Pro会员专属称号"
- ""
- "&f尊贵的Pro会员专属称号。"
- "&f您将获得多种特权与更好的游戏体验。"
- ""
- "#desciption#{1,1}"
- "&a✔ 您正在使用该前缀"
# 没有权限时显示的物品 [非必需]
# 如果没有权限就会显示这个item。如果不配置该物品,则玩家没有使用权限时不会显示在GUI里面。
itemNoPermission:
==: org.bukkit.inventory.ItemStack
type: INK_SACK
damage: 8
meta:
==: ItemMeta
meta-type: UNSPECIFIC
display-name: "&b&lPro+ &b会员前缀 &c(未拥有)"
# 没有权限时显示的物品 [非必需]
# 如果没有权限就会显示这个item。如果不配置该物品,则玩家没有使用权限时不会显示在GUI里面。
no-perm:
material: INK_SACK
data: 8
name: "%(name) &c(未拥有)"
lore:
- "&7Pro+会员专属称号"
- ""
- "&f尊贵的Pro会员专属称号。"
- "&f您将获得多种特权与更好的游戏体验。"
- "&f您可以输入 &b/vip &f指令查看详细特权!"
- ""
- "&e✯ 加入Pro+会员以使用该前缀!"
- "&7Pro 会员专属称号"
- "#desciption#{1,1}"
- "&e✯ 加入Pro会员以使用该前缀!"
+1 -1
View File
@@ -1,4 +1,4 @@
import cc.carm.plugin.userprefix.util.ColorParser;
import cc.carm.lib.easyplugin.utils.ColorParser;
import org.junit.Test;
public class ColorParseTest {