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

Compare commits

...

159 Commits

Author SHA1 Message Date
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
carm 6b601950a3 [v2.3.2] [R] 整合构建、部署(Javadoc)与版本发布。 2022-01-16 08:02:06 +08:00
carm d752461c74 [v2.3.1] [F] 修复前缀数量超过一页时翻页可能出现错误的问题 2022-01-13 01:00:46 +08:00
carm 548f1d366f 修改参考配置的位置 2022-01-07 09:23:40 +08:00
carm 686b28cf93 修改参考配置的位置 2022-01-07 09:13:05 +08:00
carm 92e14e29fe 修复格式错误 2022-01-07 09:01:36 +08:00
carm 78f9639092 [v2.3.0] [U] 修改图片位置 2022-01-07 08:45:46 +08:00
carm 096c23351f Merge remote-tracking branch 'origin/master' 2022-01-07 08:37:36 +08:00
carm b4f8404648 [v2.3.0] 版本更新
- [A] 添加数个事件,方便其他插件调用使用。
- [A] 当原前缀已不存在的情况下的向玩家发送消息。
2022-01-07 08:37:25 +08:00
carm 623356ce21 Update README.md 2022-01-03 15:32:57 +08:00
carm 9164541e4f Merge remote-tracking branch 'origin/master' 2022-01-02 10:14:23 +08:00
carm 2453305420 添加更多统计数据 2022-01-02 10:14:11 +08:00
carm 1856de5e5d Update README.md 2022-01-02 09:33:03 +08:00
carm c38adbeca6 添加统计数据显示 2021-12-31 02:34:31 +08:00
carm 0e90890c67 [v2.2.1] [A] 添加统计数据获取与开关设定。 2021-12-31 02:00:53 +08:00
carm 7e4ca870b5 [v2.2.1] [U] 添加统计数据获取与开关设定。 2021-12-31 01:57:53 +08:00
carm 42b71714aa [v2.2.0] [U] 增添对原生ItemStack配置中 displayName 和 lore的RPG颜色代码支持与PlaceholderAPI变量支持。 2021-12-31 00:08:07 +08:00
carm 0d6247b0f2 [v2.2.0] [U] 增添对原生ItemStack配置中 displayName 和 lore的RPG颜色代码支持与PlaceholderAPI变量支持。 2021-12-30 22:56:33 +08:00
carm 1b9f4f3e55 添加折叠 2021-12-20 18:57:53 +08:00
carm b76081bb6b 添加折叠 2021-12-20 18:55:45 +08:00
carm b8d6862b17 添加折叠 2021-12-20 18:52:42 +08:00
carm 46fb39d7bb 修改路径 2021-11-23 04:37:51 +08:00
carm 523a4f872b 移除样式 2021-11-23 04:31:16 +08:00
carm 5151f1f917 不再整理 2021-11-23 04:27:22 +08:00
carm 296339ca6f 不再整理 2021-11-23 04:22:09 +08:00
carm 63b69defcc 改为中文界面 2021-11-23 04:20:37 +08:00
carm 857ce5c27b 改为中文界面 2021-11-23 04:19:38 +08:00
carm a5189ff314 改为中文界面 2021-11-23 04:16:59 +08:00
carm bacb0b1bed 添加字符集限定 2021-11-23 04:11:31 +08:00
carm 1826f08575 移除自定义css 2021-11-23 04:05:49 +08:00
carm 4df2a49c5b 移除自定义css 2021-11-23 04:03:47 +08:00
carm cbb9928232 自动部署 2021-11-23 03:58:49 +08:00
carm 967984e0c3 自动部署 2021-11-23 03:46:35 +08:00
carm ad780a00bd 自动部署 2021-11-23 03:45:31 +08:00
carm 43c2c14cb3 自动部署 2021-11-23 03:38:26 +08:00
carm 8521d87c46 优化javadoc 2021-11-23 03:36:30 +08:00
carm 378e518e72 Javadoc自动部署 2021-11-23 03:34:03 +08:00
carm 84cbe0c61a 优化javadoc 2021-11-23 03:24:35 +08:00
carm 90f7f7f2e5 优化javadoc 2021-11-23 03:23:39 +08:00
carm 48147aec16 优化javadoc 2021-11-23 03:21:12 +08:00
carm e7c8ce284c 优化javadoc 2021-11-23 03:19:44 +08:00
carm e8b603debc 优化javadoc 2021-11-23 03:17:49 +08:00
carm 72730fad1a 测试Javadoc部署 2021-11-23 03:06:39 +08:00
carm a87e97f744 测试Javadoc部署 2021-11-23 03:02:51 +08:00
carm b2f7338ba6 测试Javadoc部署 2021-11-23 02:58:03 +08:00
carm f91a2aa198 测试Javadoc部署 2021-11-23 02:56:11 +08:00
carm 124ee0f9c3 测试Javadoc部署 2021-11-23 02:46:51 +08:00
carm 9f738ff1ae 修改版本号 2021-11-21 20:01:49 +08:00
carm f23fa03f7a 添加ASCII文字与插件介绍 2021-11-21 19:53:12 +08:00
carm cf749bf5c0 添加下载地址 2021-11-02 11:47:13 +08:00
Carm 01e321686f Merge pull request #6 from CarmJos/dependabot/maven/junit-junit-4.13.1
Bump junit from 4.13 to 4.13.1
2021-10-30 17:59:07 +08:00
dependabot[bot] deba26c713 Bump junit from 4.13 to 4.13.1
Bumps [junit](https://github.com/junit-team/junit4) from 4.13 to 4.13.1.
- [Release notes](https://github.com/junit-team/junit4/releases)
- [Changelog](https://github.com/junit-team/junit4/blob/main/doc/ReleaseNotes4.13.1.md)
- [Commits](https://github.com/junit-team/junit4/compare/r4.13...r4.13.1)

---
updated-dependencies:
- dependency-name: junit:junit
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-10-30 09:56:26 +00:00
Carm 5b4d49f6ed Create codeql-analysis.yml 2021-10-30 17:54:55 +08:00
carm 2e5250dd36 更新帖子 2021-10-25 08:10:54 +08:00
carm 88730c98f2 更新帖子 2021-10-25 07:58:43 +08:00
carm e003a543f5 支持由控制台为其他玩家打开GUI 2021-10-24 21:22:08 +08:00
Carm 07edaeb401 Merge pull request #5 from Yurinann/master
添加控制台尝试打开称号菜单时的反馈
2021-10-24 21:17:10 +08:00
carm 2357699324 自动提交默认值 2021-10-24 21:14:29 +08:00
“Yurinan” c2fa1a1e95 添加控制台尝试打开称号菜单时的反馈 2021-10-24 14:29:57 +08:00
carm 3b03ec4616 修改readme 2021-10-24 07:22:23 +08:00
carm 1a2644210b 2.1.7 提供自定义配置文件存储位置的功能 2021-10-24 07:12:47 +08:00
carm 83b9ced6f5 2.1.6 补充介绍,优化部署密钥配置。 2021-10-21 21:46:01 +08:00
carm dd96c6d6da 修改源地址 2021-10-19 10:49:35 +08:00
carm ba7d4b90c3 修改源地址 2021-10-19 10:47:55 +08:00
carm ab984acc73 修改源地址 2021-10-19 10:41:18 +08:00
carm 32df1de83d 2.1.5
1. 修复示例config中yaml格式错误的问题
2. 更改构建方式,分开构建与部署的工作流。
2021-10-19 10:40:02 +08:00
carm dfe447393e 2.1.5
1. 修复示例config中yaml格式错误的问题
2. 更改构建方式,分开构建与部署的工作流。
2021-10-19 10:37:41 +08:00
Carm 26ce6d7e66 Merge pull request #3 from OskyEdz/patch-1
YAML formatting
2021-10-19 10:16:04 +08:00
OskyEdz 2e4d1122d9 YAML formatting
Fixed mapping values are not allowed in this context error.
2021-10-19 02:15:38 +02:00
carm b5a2199e1e Update .gitignore 2021-10-19 00:37:07 +08:00
carm 3678a201c7 2.1.4 修改tag机制与测试部署 2021-10-07 23:04:00 +08:00
carm f08a5e84d3 2.1.3 自动构建与部署 (无实质更新) 2021-10-07 22:52:20 +08:00
carm 7743bbb3ff 2.1.3 自动构建与部署 (无实质更新) 2021-10-07 22:50:23 +08:00
carm 1942c50078 2.1.3 自动构建与部署 (无实质更新) 2021-10-07 22:40:53 +08:00
carm 2b701510bc 添加更多maven介绍 2021-10-07 22:37:31 +08:00
carm 0d17e7f1b6 添加packages自动部署 2021-10-07 21:51:16 +08:00
carm 3a41560972 添加packages自动部署 2021-10-07 21:49:39 +08:00
Carm 313aa77d72 Update README.md 2021-10-05 19:40:48 +08:00
Carm dab774a389 Update README.md 2021-10-05 19:29:51 +08:00
Carm 5469c3101f Delete PAY.jpg 2021-10-05 19:29:38 +08:00
Carm dbf826cec8 补全协议介绍 2021-10-05 18:17:08 +08:00
Carm 87c87127b9 Update maven.yml 2021-10-05 18:14:41 +08:00
Carm 50c35488ef Update README.md 2021-10-03 23:54:12 +08:00
Carm 29539ddb6f Update README-en.md 2021-10-03 23:53:40 +08:00
Carm cab2b60abf add code link 2021-10-03 17:57:22 +08:00
Carm 693ff4a4af 添加相关代码参考的地址 2021-10-03 17:28:48 +08:00
Carm 7c90b8ff99 Rename ----.md to feature_issues.md 2021-10-02 17:00:06 +08:00
Carm e5ba217873 Update issue templates 2021-10-02 16:59:42 +08:00
Carm 33e0e64b7d Rename ----.md to bugs_report.md 2021-10-02 16:54:01 +08:00
Carm 6eb60d83d8 添加问题issues模板 2021-10-02 16:53:24 +08:00
carm 875655bc60 2.1.2版本更新 修复 “OnNamePrefix” 为true时存在的报错现象 2021-10-02 15:41:39 +08:00
carm 9ca289e4ba 2.1.1版本更新 修复 “OnNamePrefix” 为false时存在的报错现象 2021-10-02 15:40:35 +08:00
carm 41a9582d03 2.1.1版本更新 修复 “OnNamePrefix” 为false时存在的报错现象 2021-10-02 15:24:54 +08:00
carm e14901ac86 2.1.0版本更新 支持聊天前缀 2021-09-27 01:32:17 +08:00
carm 9a620d7324 更新帖子内容 2021-09-18 13:40:02 +08:00
carm 9cff1c03dc 2.0.0
1. 修复低版本可能无法读取物品的bug。
2. 分离配置文件,消息配置文件改为`messages.yml`,前缀配置文件改为`prefixes/*.yml`,便于配置和管理。
3. 允许配置GUI中上一页和下一页的物品。
4. 补全帮助文档,在插件首次加载将提供一份英文版的配置,以便使用。
2021-09-18 13:19:06 +08:00
Carm 3a457082b1 Update README-en.md 2021-09-17 22:00:22 +08:00
Carm 12218a2141 Update README.md 2021-09-17 22:00:05 +08:00
carm 389e4d3904 添加自动上传构建设定 2021-09-17 19:47:51 +08:00
Carm 6ac3cea7c0 Update spigot.source 2021-09-17 19:24:28 +08:00
carm 3f14294e67 添加发布地址 2021-09-17 19:19:42 +08:00
carm 93521f6621 添加Spigot帖子源码 2021-09-17 19:17:37 +08:00
carm 43a4fb08a6 多语言支持 2021-09-17 19:10:23 +08:00
carm 3dd6398afa 添加英文版介绍 2021-09-17 18:53:20 +08:00
carm 799bb4e52b 更新帖子内容。 2021-09-17 18:09:00 +08:00
carm 7ea3934db9 1. 修复closeAll方法中,未移除玩家缓存的bug。
2. 当玩家权限更新时,将关闭玩家的GUI,令其自行打开刷新所有前缀。
3. 允许配置GUI的标题。
2021-09-17 18:07:26 +08:00
carm 0d20860166 更新帖子内容 2021-09-17 17:45:26 +08:00
carm b0c6c793a8 更新帖子内容 2021-09-17 17:40:55 +08:00
carm b6319eec78 支持支持Hex颜色!(1.16以上版本) 格式 &(#颜色代码) 。 2021-09-17 17:39:48 +08:00
carm 8bf19a69f8 修复可能导致报错的PlaceholderAPI问题 2021-09-17 08:45:36 +08:00
Carm 5d4b131a13 Create mcbbs.source 2021-09-16 22:51:08 +08:00
Carm 5c2b062839 Update README.md 2021-09-16 22:10:05 +08:00
Carm dbe4b187d3 修改开源协议地址 2021-09-16 18:45:46 +08:00
carm 419e440700 修改示例图片地址 2021-09-16 18:40:36 +08:00
carm 7052ea6ec4 修改示例图片名称 2021-09-16 18:39:48 +08:00
carm 264b69b115 添加发布地址与图片 2021-09-16 18:38:47 +08:00
carm c16d12d718 修改版本号 2021-09-16 17:26:04 +08:00
carm 796aac50c2 移除一个本不需要的配置 2021-09-16 17:25:00 +08:00
Carm a8a1b844cc Update README.md 2021-09-16 16:43:45 +08:00
Carm a1cdf896da Update README.md 2021-09-16 16:43:12 +08:00
Carm 1d2960001c 添加团队logo 2021-09-16 16:42:04 +08:00
Carm 8ec9f88553 Add files via upload 2021-09-16 16:40:32 +08:00
Carm 0c0f2fad16 Update README.md 2021-09-16 16:39:59 +08:00
Carm f3920c7e23 Add files via upload 2021-09-16 16:39:16 +08:00
Carm 0344975371 Create README.md 2021-09-16 16:38:52 +08:00
Carm 259955153a Update README.md 2021-09-15 22:03:55 +08:00
Carm c72c7b8639 Update README.md 2021-09-15 19:26:10 +08:00
Carm c1fd97b765 Update README.md 2021-09-15 19:19:08 +08:00
Carm f0c2d98f7b Create LICENSE 2021-09-15 19:18:42 +08:00
Carm 77eeb7a8a2 Update README.md 2021-09-15 17:56:22 +08:00
Carm ae4a6717b6 Merge pull request #1 from CarmJos/add-license-1
Create LICENSE
2021-09-15 17:55:43 +08:00
Carm f7b5bff6e3 Create LICENSE 2021-09-15 17:55:27 +08:00
Carm 44fcc6b77d Update README.md 2021-09-15 17:50:04 +08:00
Carm 03f7a2c498 Update README.md 2021-09-15 17:49:34 +08:00
Carm 7a755767e9 Update README.md 2021-09-15 17:45:38 +08:00
Carm 97dd06a5b5 Update README.md 2021-09-15 16:46:39 +08:00
Carm 1ffef4179e Update README.md 2021-09-15 16:46:06 +08:00
Carm 7f3f3b65a2 Update README.md 2021-09-15 16:42:53 +08:00
Carm 393e205bb1 Update README.md 2021-09-15 16:41:44 +08:00
carm 85f5e7d95a 当出现错误时,将发送错误的前缀是哪一个。 2021-09-15 16:36:25 +08:00
carm ee2cc259ef 优化代码 2021-09-08 19:10:22 +08:00
carm bd303f24b7 优化代码 2021-09-08 19:08:20 +08:00
carm a95d4d8cbb 移除失效代码 2021-09-08 18:39:40 +08:00
carm 7af506362b 1.2.1-SNAPSHOT 修复一些小问题
1. 添加了更多的注释
2. 修复了玩家GUI缓存未清空的问题。
3. 添加了@Nullable与@NotNull的标识,便于后续代码更新维护。
2021-09-08 18:36:16 +08:00
Carm 6c175816ce Update README.md 2021-09-08 17:57:17 +08:00
Carm 2e57e03927 添加CI显示 2021-09-05 10:47:08 +08:00
Carm 03afb12161 添加代码质量显示 2021-09-05 10:45:50 +08:00
carm bcc8d077a9 采用外部库 2021-08-25 14:27:34 +08:00
carm eaa386a6ab 采用外部库 2021-08-25 14:25:43 +08:00
carm 22bf9d7d94 Merge remote-tracking branch 'origin/master' 2021-08-25 14:22:30 +08:00
carm 3de812c42a 采用外部库 2021-08-25 14:21:04 +08:00
Carm 6abc13af23 自动化CI 2021-08-25 14:17:52 +08:00
carm bb126c676e 优化代码结构,移除无用代码,减少代码复用。 2021-08-25 13:37:31 +08:00
carm f6a5538da3 修复高版本重载失效的问题 2021-08-25 13:37:09 +08:00
59 changed files with 3916 additions and 1174 deletions
+9
View File
@@ -0,0 +1,9 @@
# UserPrefix Javadoc
基于 [Github Pages](https://pages.github.com/) 搭建,请访问 [JavaDoc](https://carmjos.github.io/UserPrefix) 。
## 如何实现?
若您也想通过 [Github Actions](https://docs.github.com/en/actions/learn-github-actions)
自动部署项目的Javadoc到 [Github Pages](https://pages.github.com/)
可以参考我的文章 [《自动部署Javadoc到Github Pages》](https://pages.carm.cc/doc/javadoc-in-github.html) 。
+73
View File
@@ -0,0 +1,73 @@
```text
_ _ _____ __ _
| | | | | __ \ / _|(_)
| | | | ___ ___ _ __ | |__) |_ __ ___ | |_ _ __ __
| | | |/ __| / _ \| '__|| ___/| '__|/ _ \| _|| |\ \/ /
| |__| |\__ \| __/| | | | | | | __/| | | | > <
\____/ |___/ \___||_| |_| |_| \___||_| |_|/_/\_\
```
# UserPrefix 帮助介绍文档
## 插件介绍目录
- 使用示例
- [前缀配置文件预设示例](../src/main/resources/prefixes/example-prefix.yml)
## [开发文档](JAVADOC-README.md)
基于 [Github Pages](https://pages.github.com/) 搭建,请访问 [JavaDoc](https://carmjos.github.io/UltraDepository) 。
## 依赖方式
### Maven 依赖
```xml
<project>
<repositories>
<repository>
<!--采用github依赖库,安全稳定,但需要配置 (推荐)-->
<id>UserPrefix</id>
<name>GitHub Packages</name>
<url>https://maven.pkg.github.com/CarmJos/UserPrefix</url>
</repository>
<repository>
<!--采用我的私人依赖库,简单方便,但可能因为变故而无法使用-->
<id>carm-repo</id>
<name>Carm's Repo</name>
<url>https://repo.carm.cc/repository/maven-public/</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>cc.carm.plugin</groupId>
<artifactId>userprefix</artifactId>
<version>[LATEST RELEASE]</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
```
### Gradle 依赖
```groovy
repositories {
// 采用github依赖库,安全稳定,但需要配置 (推荐)
maven { url 'https://maven.pkg.github.com/CarmJos/UserPrefix' }
// 采用我的私人依赖库,简单方便,但可能因为变故而无法使用
maven { url 'https://repo.carm.cc/repository/maven-public/' }
}
dependencies {
compileOnly "cc.carm.plugin:userprefix:[LATEST RELEASE]"
}
```
+5
View File
@@ -0,0 +1,5 @@
# 图片文件夹
用于存放相关介绍图片。
图片保留版权,未经授权禁止使用。
Binary file not shown.

After

Width:  |  Height:  |  Size: 723 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 212 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 920 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

+314
View File
@@ -0,0 +1,314 @@
[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
@@ -0,0 +1,284 @@
[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.
+30
View File
@@ -0,0 +1,30 @@
---
name: 问题提交
about: 描述问题并提交,帮助我们对其进行检查与修复。
title: ''
labels: bug
assignees: ''
---
### **问题简述**
用简短的话语描述一下大概问题。
### **问题来源**
描述一下通过哪些操作才发现的问题,如:
1. 打开 '...'
2. 点击了 '....'
3. 出现了报错 '....'
### **预期结果**(可选)
如果问题不发生,应该是什么情况
### **问题截图/问题报错**
如果有报错或输出,请提供截图。
### **操作环境**
请在后台输入 `version` 并复制相关输出。
### **其他补充**
如有其他补充,可以在这里描述。
+20
View File
@@ -0,0 +1,20 @@
---
name: 功能需求
about: 希望我们提供更多的功能。
title: ''
labels: enhancement
assignees: ''
---
### **功能简述**
简单的描述一下你想要的功能
### **需求来源**
简单的描述一下为什么需要这个功能。
### **功能参考**(可选)
如果有相关功能的参考,如文本、截图,请提供给我们。
### **附加内容**
如果有什么小细节需要重点注意,请在这里告诉我们。
+70
View File
@@ -0,0 +1,70 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL Analysis"
on:
push:
branches: [ master ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ master ]
schedule:
- cron: '44 6 * * 4'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ 'java' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
# Learn more about CodeQL language support at https://git.io/codeql-language-support
steps:
- name: Checkout repository
uses: actions/checkout@v2
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main
# 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
# ️ Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language
#- run: |
# make bootstrap
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1
+90
View File
@@ -0,0 +1,90 @@
# This workflow will build a Java project with Maven, and cache/restore any dependencies to improve the workflow execution time
# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven
name: Deploy & Upload
on:
# 支持手动触发构建
workflow_dispatch:
release:
# 创建release的时候触发
types: [ published ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: "Set up JDK"
uses: actions/setup-java@v2
with:
java-version: '11'
distribution: 'adopt'
cache: maven
server-id: github
server-username: MAVEN_USERNAME
server-password: MAVEN_TOKEN
- name: "Maven Deploy"
run: mvn -B deploy --file pom.xml -DskipTests
env:
MAVEN_USERNAME: ${{ github.repository_owner }}
MAVEN_TOKEN: ${{secrets.GITHUB_TOKEN}}
- name: "Release Asset Upload"
id: upload-release-asset
uses: shogo82148/actions-upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ github.event.release.upload_url }}
asset_path: asset/*.jar
asset_content_type: application/java-archive
- name: "Javadoc Deploy Staging"
run: |
rm -rf docs
mkdir -vp docs
cp -vrf target/apidocs/* docs/
cp -vrf .documentation/JAVADOC-README.md docs/README.md
- name: "Generate the Javadoc sitemap"
id: sitemap
uses: cicirello/generate-sitemap@v1
with:
base-url-path: https://${{ github.repository_owner }}.github.io/${{ github.event.repository.name }}
path-to-root: docs
- name: "Output Javadoc stats"
run: |
echo "sitemap-path = ${{ steps.sitemap.outputs.sitemap-path }}"
echo "url-count = ${{ steps.sitemap.outputs.url-count }}"
echo "excluded-count = ${{ steps.sitemap.outputs.excluded-count }}"
- name: "Configure Git"
env:
DEPLOY_PRI: ${{secrets.DEPLOY_PRI}}
run: |
sudo timedatectl set-timezone "Asia/Shanghai"
mkdir -p ~/.ssh/
echo "$DEPLOY_PRI" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
ssh-keyscan github.com >> ~/.ssh/known_hosts
git config --global user.name 'CarmJos'
git config --global user.email 'carm@carm.cc'
- name: "Commit Javadocs"
run: |
cd docs
git init
git remote add origin git@github.com:${{ github.repository }}.git
git checkout -b gh-pages
git add -A
git commit -m "API Document generated."
- name: "Push javadocs"
run: |
cd docs
git push origin HEAD:gh-pages --force
+43
View File
@@ -0,0 +1,43 @@
# This workflow will build a Java project with Maven, and cache/restore any dependencies to improve the workflow execution time
# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven
name: Build & Tests
on:
# 支持手动触发构建
workflow_dispatch:
push:
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: "Set up JDK"
uses: actions/setup-java@v2
with:
java-version: '11'
distribution: 'adopt'
cache: maven
server-id: github
server-username: MAVEN_USERNAME
server-password: MAVEN_TOKEN
- name: "Package"
run: mvn -B package --file pom.xml
env:
MAVEN_USERNAME: ${{ github.repository_owner }}
MAVEN_TOKEN: ${{secrets.GITHUB_TOKEN}}
- name: "Target Staging"
run: |
mkdir artifacts
cp -vrf target/ artifacts/target/
cp -vrf asset/*.jar artifacts
- name: "Upload artifact"
uses: actions/upload-artifact@v2
with:
name: Artifact
path: artifacts
+3 -1
View File
@@ -1,3 +1,5 @@
/.idea/
/target/
./*.iml
./*.iml
*.iml
asset/
+674
View File
@@ -0,0 +1,674 @@
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<https://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<https://www.gnu.org/licenses/why-not-lgpl.html>.
+133
View File
@@ -0,0 +1,133 @@
![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)
![CodeSize](https://img.shields.io/github/languages/code-size/CarmJos/UserPrefix)
[![License](https://img.shields.io/github/license/CarmJos/UserPrefix)](https://opensource.org/licenses/GPL-3.0)
[![Java CI with Maven](https://github.com/CarmJos/UserPrefix/actions/workflows/maven.yml/badge.svg?branch=master)](https://github.com/CarmJos/UserPrefix/actions/workflows/maven.yml)
![Support](https://img.shields.io/badge/Minecraft-Java%201.8--Latest-yellow)
![](https://visitor-badge.glitch.me/badge?page_id=userprefix.readme)
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.
> 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)
## Dependencies
- **[Necessary]** Plugin developed based on [Spigot-API](https://hub.spigotmc.org/stash/projects/SPIGOT)
and [BukkitAPI](http://bukkit.org/).
- **[Necessary]** Plugin data storage base on [LuckPerms](https://www.spigotmc.org/resources/luckperms.28140/).
- **[Recommend]** Placeholders based on [PlaceholderAPI](https://www.spigotmc.org/resources/6245/) .
For development dependencies, please see [Dependencies](https://github.com/CarmJos/UserPrefix/network/dependencies) .
## Features
- **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”.
- 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)
- GUI with automatic sorting and page turning!
- Support PlaceholderAPI variables!
- Support [Hex Color](https://www.hexcolortool.com/)! (Version 1.16 and above) `&(#Color)`
- Example: LightSlateBlue `&(#8470FF)` 、 DarkSlateBlue `&(#483D8B)`
## Notice
### 1. Version support issues
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 "`VILLAGER_YES`" in the lower version, but it
becomes "`ENTITY_VILLAGER_YES`" in the higher version.
### 2. Scoreboard exception problem
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/).
## Commands
This plugin's Commands are based on Chinese!
**May support multi-language in the future.**
```text
/UserPrefix or /prefix #Open prefix GUI
/UserPrefixAdmin # View Admin Command Help
/UserPrefixAdmin reload # Reload Config
/UserPrefixAdmin list # List all configured prefixes.
```
## Placeholders (PlaceholderAPI)
After installed the [PlaceholderAPI](https://github.com/PlaceholderAPI/PlaceholderAPI) , you can
type `/papi info UserPrefix` to see all the placeholders.
```text
# %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)
```
## Configuration files
### Plugin Configuration ([`config.yml`](src/main/resources/en_US/config.yml) .
Notice: The default configuration is based on Chinese. You can find
the [English Version here](src/main/resources/en_US/config.yml).
### Messages Configuration ([`messages.yml`](src/main/resources/en_US/messages.yml))
Please see the [Source File](src/main/resources/en_US/messages.yml) .
### Prefixes Configuration ([`prefixes/*.yml`](src/main/resources/en_US/example-prefix.yml))
All prefixes are separate configuration files, stored in the `<Data Folder>/prefixes/` for easy management.
Some symbols in file name may affect reading, please avoid using them.
## Support and Donation
This project is support by the [YourCraft(你的世界)](https://www.ycraft.cn) .
![TeamLogo](.documentation/images/team-logo.png)
## Open source agreement
The source code of this project uses [GNU General Public License v3.0](https://opensource.org/licenses/GPL-3.0)
License.
+93 -105
View File
@@ -1,22 +1,51 @@
![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)
![CodeSize](https://img.shields.io/github/languages/code-size/CarmJos/UserPrefix)
[![Download](https://img.shields.io/github/downloads/CarmJos/UserPrefix/total)](https://github.com/CarmJos/UserPrefix/releases)
[![Java CI with Maven](https://github.com/CarmJos/UserPrefix/actions/workflows/maven.yml/badge.svg?branch=master)](https://github.com/CarmJos/UserPrefix/actions/workflows/maven.yml)
![Support](https://img.shields.io/badge/Minecraft-Java%201.8--Latest-green)
![](https://visitor-badge.glitch.me/badge?page_id=userprefix.readme)
轻便、高效、实时的用户前缀系统。
数据部分基于 [LuckPerms](https://www.spigotmc.org/resources/luckperms.28140/) 实现
本插件基于Spigot实现,**理论上支持全版本**
> 本插件已在 [MCBBS](https://www.mcbbs.net/forum.php?mod=viewthread&tid=1261503) 与 [SpigotMC](https://www.spigotmc.org/resources/userprefix-hex-color-support-all-version.96277/) 上发布。
## 示例
![example](.documentation/images/using-example.png)
## 依赖
- **[必须]** 插件本体基于 [Spigot-API](https://hub.spigotmc.org/stash/projects/SPIGOT)、[BukkitAPI](http://bukkit.org/) 实现。
- **[必须]** 数据部分基于 [LuckPerms](https://www.spigotmc.org/resources/luckperms.28140/) 实现。
- **[推荐]** 变量部分基于 [PlaceholderAPI](https://www.spigotmc.org/resources/6245/) 实现。
详细依赖列表可见 [Dependencies](https://github.com/CarmJos/UserPrefix/network/dependencies) 。
## 特性
- 理论上全版本支持!
- 游戏内重载配置文件并实时更新到玩家!
- 当玩家权限变更时会实时监测前缀,若权限不足则自动更换前缀并提示
- 当玩家权限变更时会实时监测前缀,若权限不足则自动更换前缀并提示
- 可配置的声音、消息!
- 前缀图标可配置“选中”、“有权限”与“无权限”三种状态的物品
- 物品的配置通过ItemStack原生配置,支持MC所有的设定!
- 具体的设定请参考其他文档哦~
- TabList自动按照前缀的权重排序 (如有冲突可关掉)
- 玩家头顶前缀显示 (如有冲突可关掉)
- 自动排序,且可翻页的GUI
- 支持PlaceholderAPI变量
- 简单的聊天变量修改功能!(不推荐使用) `[自 v2.1.0 版本起]`
- 自动排序,且可翻页的GUI
- 支持PlaceholderAPI变量!(凡支持的都可以使用,如BungeeTabListPlus)
- 支持[Hex颜色](https://www.hexcolortool.com/)(1.16以上版本) `[自 v1.2.3 版本起]`
- 格式: `&(#颜色代码)`
- 示例: LightSlateBlue `&(#8470FF)` 、 DarkSlateBlue `&(#483D8B)`
## 注意事项
@@ -33,7 +62,8 @@
如有冲突导致其他插件的计分板无法显示,请关掉配置文件中`functions.OnNamePrefix`
### 3. 物品图标配置问题
物品相关均通过Bukkit提供的ItemStack序列化方法读取,相关配置方式请参考其他文档。
物品相关均通过Bukkit提供的ItemStack序列化方法读取,相关配置方式请参考[ItemStack Serialization(物品序列化)](https://www.spigotmc.org/wiki/itemstack-serialization/)。
## 指令
@@ -67,109 +97,67 @@
## 配置文件示例
```yaml
version: 1.0.0-SNAPSHOT # 配置文件版本,一般不会动。
### 基础配置文件 ([`config.yml`](src/main/resources/config.yml))
debug: false #debug输出,开发者用的
详见 [源文件](src/main/resources/messages.yml) 。
functions:
OnNamePrefix: true # 是否给头顶上添加前缀,该方法用到了头顶的那个计分板,如有冲突请关掉哦~
autoUsePrefix: true # 自动前缀显示 当玩家没有自己选择一个前缀的时候,会自动使用所拥有的的前缀中权重最高的那一个
### 消息配置文件 ([`messages.yml`](src/main/resources/messages.yml))
messages:
selected:
- "&7您选择了 &f%(name) &7作为当前显示的前缀。"
expired:
- "&7您先前使用的前缀 &f%(oldName) &7已到期。"
- "&7现在已为您重新调整为 &f%(newName) &7。"
help:
- "&7输入 &b/prefix &7打开前缀选择菜单。"
详见 [源文件](src/main/resources/messages.yml) 。
Sounds: #相关的声音,注释掉则不播放声音 格式为 【声音名:音量:音调】 或 【声音名:音量】 或 【声音名】
openGUI: "BLOCK_NOTE_BLOCK_PLING:1:1"
guiClick: "UI_BUTTON_CLICK"
prefixChange: "ENTITY_VILLAGER_YES"
prefixExpired: "ENTITY_VILLAGER_NO"
### 前缀配置文件 ([`prefixes/*.yml`](src/main/resources/prefixes/example-prefix.yml))
# 默认前缀的配置
# 默认前缀的权重为0哦
defaultPrefix:
name: "默认前缀"
content: "&b"
itemNotUsing:
==: org.bukkit.inventory.ItemStack
type: NAME_TAG
meta:
==: ItemMeta
meta-type: UNSPECIFIC
display-name: "§f默认玩家前缀 §f(点击切换)"
lore:
- ""
- "§a➥ 点击切换到该前缀"
itemUsing:
==: org.bukkit.inventory.ItemStack
type: NAME_TAG
meta:
==: ItemMeta
meta-type: UNSPECIFIC
display-name: "§f默认玩家前缀"
lore:
- ""
- "§a✔ 您正在使用该前缀"
所有前缀均为单独的配置文件,存放于 `插件配置目录/prefixes` 下,便于管理。
prefixes:
VIP:
name: "&b&lPro&b" # [必须] 名字(切换的时候左下角会弹提示 用的就是这个名字)
content: "§b§lPro §b" # [必须] 显示在名字前面的内容
weight: 1 # [必须] 权重,用于GUI里面的排序(越大显示在越后面)和自动前缀显示
permission: "yc.pro" # [非必须] 检测的权限,如果没有就是人人都能用,也代表不用配置“itemNoPermission”了(因为压根不可能显示没权限时候的物品)
itemHasPermission:
# [必须] 当有权限的时候会显示这个Item
==: org.bukkit.inventory.ItemStack
type: DIAMOND
meta:
==: ItemMeta
meta-type: UNSPECIFIC
display-name: "§b§lPro §b会员前缀"
lore:
- "§7Pro会员专属称号"
- ""
- "§f尊贵的Pro会员专属称号。"
- "§f您将获得多种特权与更好的游戏体验。"
- ""
- "§a➥ 点击切换到该前缀"
itemUsing:
# [非必需] 当有权限的时候会显示这个Item,如果没有这个配置就自动显示“itemHasPermission”的。
==: org.bukkit.inventory.ItemStack
type: DIAMOND
meta:
==: ItemMeta
meta-type: UNSPECIFIC
display-name: "§b§lPro §b会员前缀"
enchants:
PROTECTION_ENVIRONMENTAL: 1 #加一个附魔这样看上去就像是选中了的
lore:
- "§7Pro会员专属称号"
- ""
- "§f尊贵的Pro会员专属称号。"
- "§f您将获得多种特权与更好的游戏体验。"
- ""
- "§a✔ 您正在使用该前缀"
itemNoPermission:
# [非必需] 如果没有权限就会显示这个item。如果不配置该物品,则玩家没有使用权限时不会显示在GUI里面。
==: org.bukkit.inventory.ItemStack
type: INK_SACK
damage: 8
meta:
==: ItemMeta
meta-type: UNSPECIFIC
display-name: "§b§lPro+ §b会员前缀 §c(未拥有)"
lore:
- "§7Pro+会员专属称号"
- ""
- "§f尊贵的Pro会员专属称号。"
- "§f您将获得多种特权与更好的游戏体验。"
- "§f您可以输入 §b/vip §f指令查看详细特权!"
- ""
- "§e✯ 加入Pro+会员以使用该前缀!"
```
文件名理论上可以随便取,推荐使用英文,部分符号可能会影响正常读取,请避免使用。
您可以 [点击这里](src/main/resources/prefixes/example-prefix.yml) 查看示例前缀配置文件。
## 使用统计
[![bStats](https://bstats.org/signatures/bukkit/UserPrefix.svg)](https://bstats.org/plugin/bukkit/UserPrefix/13776)
## 支持与捐赠
本项目由 [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=""/>
## 开源协议
本项目源码采用 [GNU General Public License v3.0](https://opensource.org/licenses/GPL-3.0) 开源协议。
<details>
<summary>关于 GPL 协议</summary>
> GNU General Public Licence (GPL) 有可能是开源界最常用的许可模式。GPL 保证了所有开发者的权利,同时为使用者提供了足够的复制,分发,修改的权利:
>
> #### 可自由复制
> 你可以将软件复制到你的电脑,你客户的电脑,或者任何地方。复制份数没有任何限制。
> #### 可自由分发
> 在你的网站提供下载,拷贝到U盘送人,或者将源代码打印出来从窗户扔出去(环保起见,请别这样做)。
> #### 可以用来盈利
> 你可以在分发软件的时候收费,但你必须在收费前向你的客户提供该软件的 GNU GPL 许可协议,以便让他们知道,他们可以从别的渠道免费得到这份软件,以及你收费的理由。
> #### 可自由修改
> 如果你想添加或删除某个功能,没问题,如果你想在别的项目中使用部分代码,也没问题,唯一的要求是,使用了这段代码的项目也必须使用 GPL 协议。
>
> 需要注意的是,分发的时候,需要明确提供源代码和二进制文件,另外,用于某些程序的某些协议有一些问题和限制,你可以看一下 @PierreJoye 写的 Practical Guide to GPL Compliance 一文。使用 GPL 协议,你必须在源代码代码中包含相应信息,以及协议本身。
>
> *以上文字来自 [五种开源协议GPL,LGPL,BSD,MIT,Apache](https://www.oschina.net/question/54100_9455) 。*
</details>
---
```text
_ _ _____ __ _
| | | | | __ \ / _|(_)
| | | | ___ ___ _ __ | |__) |_ __ ___ | |_ _ __ __
| | | |/ __| / _ \| '__|| ___/| '__|/ _ \| _|| |\ \/ /
| |__| |\__ \| __/| | | | | | | __/| | | | > <
\____/ |___/ \___||_| |_| |_| \___||_| |_|/_/\_\
```
+179 -93
View File
@@ -4,36 +4,104 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>cc.carm.plugin</groupId>
<artifactId>UserPrefix</artifactId>
<version>1.0.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<maven.deploy.skip>true</maven.deploy.skip>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.encoding>UTF-8</maven.compiler.encoding>
</properties>
<groupId>cc.carm.plugin</groupId>
<artifactId>userprefix</artifactId>
<version>2.4.1</version>
<name>UserPrefix</name>
<description>轻便、高效、实时的用户前缀系统。</description>
<url>https://github.com/CarmJos/UserPrefix</url>
<issueManagement>
<system>GitHub Issues</system>
<url>${project.url}/issues</url>
</issueManagement>
<ciManagement>
<system>GitHub Actions</system>
<url>${project.url}/actions/workflows/maven.yml</url>
</ciManagement>
<developers>
<developer>
<id>CarmJos</id>
<name>Carm Jos</name>
<email>carm@carm.cc</email>
<url>https://work.carm.cc</url>
<roles>
<role>Main Developer</role>
</roles>
</developer>
</developers>
<organization>
<name>YourCraft你的世界</name>
<url>https://www.ycraft.cn/</url>
</organization>
<licenses>
<license>
<name>GNU General Public License v3.0</name>
<url>https://opensource.org/licenses/GPL-3.0</url>
</license>
</licenses>
<repositories>
<repository>
<id>ycraft</id>
<url>https://maven.ycraft.cn/repository/maven-public/</url>
<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>
</repository>
<repository>
<id>github</id>
<name>GitHub Packages</name>
<url>https://maven.pkg.github.com/CarmJos/${project.name}</url>
</repository>
</repositories>
<distributionManagement>
<downloadUrl>${project.url}/releases</downloadUrl>
<repository>
<id>github</id>
<name>GitHub Packages</name>
<url>https://maven.pkg.github.com/CarmJos/${project.name}</url>
</repository>
</distributionManagement>
<dependencies>
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot</artifactId>
<artifactId>spigot-api</artifactId>
<version>1.17-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
@@ -53,29 +121,123 @@
</dependency>
<dependency>
<groupId>com.comphenix.protocol</groupId>
<artifactId>ProtocolLib</artifactId>
<version>4.5.0</version>
<scope>provided</scope>
<groupId>org.bstats</groupId>
<artifactId>bstats-bukkit</artifactId>
<version>3.0.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.comphenix.packetwrapper</groupId>
<artifactId>PacketWrapper</artifactId>
<version>1.13-R0.1-SNAPSHOT</version>
<scope>provided</scope>
<groupId>cc.carm.lib</groupId>
<artifactId>githubreleases4j</artifactId>
<version>1.3.1</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.2.0</version>
<configuration>
<classifier>javadoc</classifier>
<links>
<link>https://javadoc.io/doc/org.jetbrains/annotations/</link>
</links>
<detectJavaApiLink>true</detectJavaApiLink>
<encoding>UTF-8</encoding>
<charset>UTF-8</charset>
<docencoding>UTF-8</docencoding>
<locale>zh_CN</locale>
</configuration>
<executions>
<execution>
<id>attach-javadocs</id>
<goals>
<goal>jar</goal>
</goals>
</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>
</execution>
</executions>
<configuration>
<finalName>${project.name}-${project.version}</finalName>
<outputDirectory>${project.basedir}/asset/</outputDirectory>
<createDependencyReducedPom>false</createDependencyReducedPom>
<relocations>
<relocation>
<pattern>org.bstats</pattern>
<shadedPattern>cc.carm.plugin.userprefix.bstats</shadedPattern>
</relocation>
<relocation>
<pattern>cc.carm.lib.githubreleases4j</pattern>
<shadedPattern>cc.carm.plugin.userprefix.lib.github</shadedPattern>
</relocation>
<relocation>
<pattern>org.json</pattern>
<shadedPattern>cc.carm.plugin.userprefix.lib.json</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>
@@ -85,82 +247,6 @@
</configuration>
</plugin>
</plugins>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.2.0</version>
<configuration>
<classifier>javadoc</classifier>
<links>
<link>https://javadoc.io/doc/org.jetbrains/annotations/</link>
</links>
<detectJavaApiLink>true</detectJavaApiLink>
<encoding>UTF-8</encoding>
</configuration>
<executions>
<execution>
<id>attach-javadocs</id>
<goals>
<goal>jar</goal>
</goals>
</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-shade-plugin</artifactId>
<version>3.2.3</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<createDependencyReducedPom>false</createDependencyReducedPom>
</configuration>
</execution>
</executions>
<configuration>
<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>
<configuration>
<useSystemClassLoader>false</useSystemClassLoader>
</configuration>
</plugin>
</plugins>
</pluginManagement>
<resources>
<resource>
<directory>src/main/resources</directory>
@@ -2,18 +2,31 @@ package cc.carm.plugin.userprefix;
import cc.carm.plugin.userprefix.command.UserPrefixAdminCommand;
import cc.carm.plugin.userprefix.command.UserPrefixCommand;
import cc.carm.plugin.userprefix.configuration.PrefixConfig;
import cc.carm.plugin.userprefix.configuration.PluginConfig;
import cc.carm.plugin.userprefix.hooker.UpdateChecker;
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.manager.ServiceManager;
import net.luckperms.api.event.user.UserDataRecalculateEvent;
import cc.carm.plugin.userprefix.manager.ConfigManager;
import cc.carm.plugin.userprefix.manager.PrefixManager;
import cc.carm.plugin.userprefix.manager.ServiceManager;
import cc.carm.plugin.userprefix.manager.UserManager;
import cc.carm.plugin.userprefix.util.ColorParser;
import cc.carm.plugin.userprefix.util.MessageUtil;
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 {
@@ -22,7 +35,7 @@ public class Main extends JavaPlugin {
@Override
public void onEnable() {
instance = this;
showPluginName();
log(getName() + " " + getDescription().getVersion() + " &7开始加载...");
long startTime = System.currentTimeMillis();
@@ -31,26 +44,56 @@ public class Main extends JavaPlugin {
PrefixManager.init();
log("注册指令...");
Bukkit.getPluginCommand("UserPrefix").setExecutor(new UserPrefixCommand());
Bukkit.getPluginCommand("UserPrefixAdmin").setExecutor(new UserPrefixAdminCommand());
registerCommand("UserPrefix", new UserPrefixCommand());
registerCommand("UserPrefixAdmin", new UserPrefixAdminCommand());
log("注册监听器...");
regListener(new UserListener());
regListener(new ChatListener());
ServiceManager.getService().getEventBus().subscribe(this, UserDataRecalculateEvent.class, UserNodeUpdateProcessor::process);
if (Bukkit.getPluginManager().getPlugin("PlaceholderAPI") != null) {
if (MessageUtil.hasPlaceholderAPI()) {
log("注册变量...");
new UserPrefixExpansion(getInstance()).register();
} else {
log("未安装 PlaceholderAPI 放弃注册变量...");
log("未安装 PlaceholderAPI 不进行变量注册...");
log("若您想使用变量进行前缀的显示,请安装PlaceholderAPI");
}
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();
}));
}
if (PluginConfig.CHECK_UPDATE.get()) {
log("开始检查更新...");
UpdateChecker.checkUpdate(getDescription().getVersion());
} else {
log("已禁用检查更新,跳过。");
}
log("加载完成 ,共耗时 " + (System.currentTimeMillis() - startTime) + " ms 。");
showAD();
if (Bukkit.getOnlinePlayers().size() > 0) {
Bukkit.getOnlinePlayers().forEach(UserManager::initPlayer); // 适配热重载
}
log("加载完成 ,共耗时 " + (System.currentTimeMillis() - startTime) + " ms 。");
}
@Override
public void onDisable() {
showPluginName();
log(getName() + " " + getDescription().getVersion() + " 开始卸载...");
long startTime = System.currentTimeMillis();
@@ -58,6 +101,9 @@ public class Main extends JavaPlugin {
Bukkit.getServicesManager().unregisterAll(this);
log("卸载完成 ,共耗时 " + (System.currentTimeMillis() - startTime) + " ms 。");
showAD();
}
/**
@@ -70,17 +116,53 @@ public class Main extends JavaPlugin {
}
public static void log(String message) {
Bukkit.getConsoleSender().sendMessage(ColorParser.parseColor("[" + getInstance().getName() + "] " + message));
Bukkit.getConsoleSender().sendMessage(ColorParser.parse("[" + getInstance().getName() + "] " + message));
}
public static void debug(String message) {
if (PrefixConfig.DEBUG.get()) {
log("[DEBUG] " + ColorParser.parseColor(message));
if (PluginConfig.DEBUG.get()) {
log("[DEBUG] " + message);
}
}
public static void error(String message) {
log("&c[ERROR] &r" + message);
}
public static JavaPlugin getInstance() {
return instance;
}
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提供长期支持与维护。");
}
}
@@ -1,10 +1,12 @@
package cc.carm.plugin.userprefix.command;
import cc.carm.plugin.userprefix.ui.PrefixSelectGUI;
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.util.ColorParser;
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;
@@ -20,17 +22,28 @@ public class UserPrefixAdminCommand implements CommandExecutor {
if (args.length == 1) {
String aim = args[0];
if (aim.equalsIgnoreCase("list")) {
sender.sendMessage(ColorParser.parseColor("&3&l用户前缀系统 &f前缀列表"));
MessageUtil.sendWithPlaceholders(sender, PluginConfig.Messages.LIST_TITLE.get());
for (ConfiguredPrefix value : PrefixManager.getPrefixes().values()) {
sender.sendMessage(ColorParser.parseColor("&8#" + value.getWeight() + " &f" + value.getIdentifier()));
sender.sendMessage(ColorParser.parseColor("&8- &7显示名 &r" + value.getName() + " &7权限&r " + value.getPermission()));
sender.sendMessage(ColorParser.parseColor("&8- &7内容示例&r " + value.getContent() + sender.getName()));
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(); // 关掉所有正在显示的前缀列表
PrefixManager.loadConfiguredPrefixes(); //重载配置文件
ConfigManager.reload(); // 重载配置文件
PrefixManager.loadPrefixes(); //加载重载后了的前缀配置
for (Player onlinePlayer : Bukkit.getOnlinePlayers()) {
UserManager.checkPrefix(onlinePlayer, false);
/*
@@ -40,7 +53,10 @@ public class UserPrefixAdminCommand implements CommandExecutor {
*/
UserManager.updatePrefixView(onlinePlayer, false);
}
sender.sendMessage(ColorParser.parseColor("&a&l重载完成!&7共耗时 &f" + (System.currentTimeMillis() - s1) + " ms&7。"));
MessageUtil.sendWithPlaceholders(
sender, PluginConfig.Messages.RELOAD.get(),
new String[]{"%(time)"}, new Object[]{(System.currentTimeMillis() - s1)}
);
return true;
}
return help(sender);
@@ -49,11 +65,7 @@ public class UserPrefixAdminCommand implements CommandExecutor {
}
public static boolean help(CommandSender sender) {
sender.sendMessage(ColorParser.parseColor("&3&l用户前缀系统 &f帮助"));
sender.sendMessage(ColorParser.parseColor("&8#&f list"));
sender.sendMessage(ColorParser.parseColor("&8- &7查看当前前缀列表。"));
sender.sendMessage(ColorParser.parseColor("&8#&f reload"));
sender.sendMessage(ColorParser.parseColor("&8- &7重载前缀配置。"));
MessageUtil.send(sender, PluginConfig.Messages.HELP.get());
return true;
}
@@ -1,6 +1,7 @@
package cc.carm.plugin.userprefix.command;
import cc.carm.plugin.userprefix.ui.PrefixSelectGUI;
import org.bukkit.Bukkit;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
@@ -14,6 +15,15 @@ public class UserPrefixCommand implements CommandExecutor {
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String s, @NotNull String[] strings) {
if (sender instanceof Player) {
PrefixSelectGUI.open((Player) sender);
} else {
if (strings.length != 1) {
sender.sendMessage("输入 /prefix <ID> 为玩家打开前缀GUI。");
} else {
Player player = Bukkit.getPlayer(strings[0]);
if (player != null) {
PrefixSelectGUI.open(player);
}
}
}
return true;
}
@@ -0,0 +1,87 @@
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 final ConfigValue<Boolean> CHECK_UPDATE = new ConfigValue<>("check-update", 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,37 +0,0 @@
package cc.carm.plugin.userprefix.configuration;
import cc.carm.plugin.userprefix.configuration.values.ConfigSound;
import cc.carm.plugin.userprefix.configuration.values.ConfigValue;
import cc.carm.plugin.userprefix.configuration.values.ConfigValueList;
public class PrefixConfig {
public static ConfigValue<Boolean> DEBUG = new ConfigValue<>("debug", Boolean.class, false);
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 Messages {
public static ConfigValueList<String> SELECTED = new ConfigValueList<>("messages.selected", String.class);
public static ConfigValueList<String> EXPIRED = new ConfigValueList<>("messages.expired", String.class);
public static ConfigValueList<String> HELP = new ConfigValueList<>("messages.help", String.class);
}
public static class Sounds {
public static ConfigSound GUI_OPEN = new ConfigSound("Sounds.openGUI");
public static ConfigSound GUI_CLICK = new ConfigSound("Sounds.guiClick");
public static ConfigSound PREFIX_CHANGE = new ConfigSound("Sounds.prefixChange");
public static ConfigSound PREFIX_EXPIRED = new ConfigSound("Sounds.prefixExpired");
}
}
@@ -0,0 +1,64 @@
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();
}
}
}
@@ -0,0 +1,32 @@
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);
}
}
@@ -0,0 +1,29 @@
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,60 +1,73 @@
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 {
FileConfiguration source;
String configSection;
Sound defaultValue;
public ConfigSound(String configSection) {
this(configSection, null);
}
FileConfig source;
String configSection;
public ConfigSound(String configSection, Sound defaultValue) {
this.source = ConfigManager.getConfig();
this.configSection = configSection;
this.defaultValue = defaultValue;
}
Sound defaultValue;
public void set(Sound value, float volume) {
this.source.set(this.configSection, value.name() + ":" + volume);
this.save();
}
public ConfigSound(String configSection) {
this(configSection, null);
}
public void set(Sound value, float volume, float pitch) {
this.source.set(this.configSection, value.name() + ":" + volume + ":" + pitch);
this.save();
}
public ConfigSound(String configSection, Sound defaultValue) {
this(ConfigManager.getPluginConfig(), configSection, defaultValue);
}
public void play(Player player) {
Sound finalSound = defaultValue;
float pitch = 1;
float volume = 1;
String soundString = this.source.getString(this.configSection);
if (soundString != null) {
String[] args = soundString.contains(":") ? soundString.split(":") : new String[]{soundString};
try {
if (args.length >= 1) finalSound = Sound.valueOf(args[0]);
if (args.length >= 2) volume = Float.parseFloat(args[1]);
if (args.length >= 3) volume = Float.parseFloat(args[2]);
} catch (Exception exception) {
Main.log("声音 " + this.configSection + " 配置错误,不存在 " + soundString + " ,请检查。");
}
}
if (finalSound != null) {
player.playSound(player.getLocation(), finalSound, volume, pitch);
}
public 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 save() {
ConfigManager.saveConfig();
}
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,10 +1,13 @@
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> {
FileConfiguration source;
FileConfig source;
String configSection;
Class<V> clazz;
V defaultValue;
@@ -14,24 +17,42 @@ public class ConfigValue<V> {
}
public ConfigValue(String configSection, Class<V> clazz, V defaultValue) {
this.source = ConfigManager.getConfig();
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() {
Object val = this.source.get(this.configSection, this.defaultValue);
return this.clazz.isInstance(val) ? this.clazz.cast(val) : this.defaultValue;
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) {
this.source.set(this.configSection, value);
getConfiguration().set(this.configSection, value);
this.save();
}
public void save() {
ConfigManager.saveConfig();
this.source.save();
}
public V setDefault() {
set(this.defaultValue);
return this.defaultValue;
}
}
@@ -1,26 +1,52 @@
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> {
FileConfiguration source;
FileConfig source;
String configSection;
Class<V> clazz;
V[] defaultValue;
public ConfigValueList(String configSection, Class<V> clazz) {
this.source = ConfigManager.getConfig();
this.configSection = configSection;
this.clazz = 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 = this.source.getList(this.configSection);
List<?> list = getConfiguration().getList(this.configSection);
if (list == null) {
return new ArrayList(0);
if (defaultValue != null) {
return new ArrayList<>(Arrays.asList(defaultValue));
} else {
return new ArrayList(0);
}
} else {
ArrayList<V> result = new ArrayList();
@@ -29,17 +55,17 @@ public class ConfigValueList<V> {
result.add(this.clazz.cast(object));
}
}
return result;
}
}
public void set(ArrayList<V> value) {
this.source.set(this.configSection, value);
getConfiguration().set(this.configSection, value);
this.save();
}
public void save() {
ConfigManager.saveConfig();
this.source.save();
}
}
@@ -0,0 +1,75 @@
package cc.carm.plugin.userprefix.event;
import cc.carm.plugin.userprefix.Main;
import cc.carm.plugin.userprefix.model.ConfiguredPrefix;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.Cancellable;
import org.bukkit.event.HandlerList;
import org.bukkit.scheduler.BukkitRunnable;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.function.Consumer;
public class UserPrefixChangeEvent extends UserPrefixEvent implements Cancellable {
public static HandlerList handler = new HandlerList();
private boolean cancelled;
private final @Nullable ConfiguredPrefix before;
private @NotNull ConfiguredPrefix after;
public UserPrefixChangeEvent(@NotNull Player who,
@Nullable ConfiguredPrefix before,
@NotNull ConfiguredPrefix after) {
super(who);
this.before = before;
this.after = after;
}
public @Nullable ConfiguredPrefix getBefore() {
return before;
}
public @NotNull ConfiguredPrefix getAfter() {
return after;
}
public void setAfter(@NotNull ConfiguredPrefix 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 void setCancelled(boolean cancel) {
this.cancelled = cancel;
}
@NotNull
@Override
public HandlerList getHandlers() {
return handler;
}
public static void call(@NotNull Player who,
@Nullable ConfiguredPrefix before,
@NotNull ConfiguredPrefix after,
@Nullable Consumer<@Nullable ConfiguredPrefix> finish) {
new BukkitRunnable() {
@Override
public void run() {
UserPrefixChangeEvent event = new UserPrefixChangeEvent(who, before, after);
Bukkit.getPluginManager().callEvent(event);
if (finish != null) finish.accept(event.isCancelled() ? null : event.getAfter());
}
}.runTask(Main.getInstance());
}
}
@@ -0,0 +1,13 @@
package cc.carm.plugin.userprefix.event;
import org.bukkit.entity.Player;
import org.bukkit.event.player.PlayerEvent;
import org.jetbrains.annotations.NotNull;
public abstract class UserPrefixEvent extends PlayerEvent {
public UserPrefixEvent(@NotNull Player who) {
super(who);
}
}
@@ -0,0 +1,43 @@
package cc.carm.plugin.userprefix.event;
import cc.carm.plugin.userprefix.Main;
import cc.carm.plugin.userprefix.model.ConfiguredPrefix;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.HandlerList;
import org.bukkit.scheduler.BukkitRunnable;
import org.jetbrains.annotations.NotNull;
public class UserPrefixExpireEvent extends UserPrefixEvent {
public static HandlerList handler = new HandlerList();
public final @NotNull ConfiguredPrefix expiredPrefix;
public UserPrefixExpireEvent(@NotNull Player who,
@NotNull ConfiguredPrefix expiredPrefix) {
super(who);
this.expiredPrefix = expiredPrefix;
}
public @NotNull ConfiguredPrefix getExpiredPrefix() {
return expiredPrefix;
}
@NotNull
@Override
public HandlerList getHandlers() {
return handler;
}
public static void call(@NotNull Player player, @NotNull ConfiguredPrefix currentPrefix) {
new BukkitRunnable() {
@Override
public void run() {
Bukkit.getPluginManager().callEvent(new UserPrefixExpireEvent(player, currentPrefix));
}
}.runTask(Main.getInstance());
}
}
@@ -0,0 +1,35 @@
package cc.carm.plugin.userprefix.hooker;
import cc.carm.lib.githubreleases4j.GithubReleases4J;
import cc.carm.plugin.userprefix.Main;
import org.bukkit.scheduler.BukkitRunnable;
public class UpdateChecker {
public static void checkUpdate(String currentVersion) {
new BukkitRunnable() {
@Override
public void run() {
String downloadURL = GithubReleases4J.getReleasesURL("CarmJos", "UserPrefix");
Integer behindVersions = GithubReleases4J.getVersionBehind(
"CarmJos", "UserPrefix", currentVersion
);
if (behindVersions == null) {
Main.error("检查更新失败,请您定期查看插件是否更新,避免安全问题。");
Main.error("下载地址 " + downloadURL);
} else if (behindVersions == 0) {
Main.log("检查完成,当前已是最新版本。");
} else if (behindVersions > 0) {
Main.log("发现新版本! 目前已落后 " + behindVersions + " 个版本。");
Main.log("最新版下载地址 " + downloadURL);
} else {
Main.error("检查更新失败! 当前版本未知,请您使用原生版本以避免安全问题。");
Main.error("最新版下载地址 " + downloadURL);
}
}
}.runTaskAsynchronously(Main.getInstance());
}
}
@@ -1,9 +1,9 @@
package cc.carm.plugin.userprefix.hooker;
import me.clip.placeholderapi.expansion.PlaceholderExpansion;
import cc.carm.plugin.userprefix.manager.PrefixManager;
import cc.carm.plugin.userprefix.manager.UserManager;
import cc.carm.plugin.userprefix.model.ConfiguredPrefix;
import me.clip.placeholderapi.expansion.PlaceholderExpansion;
import org.bukkit.entity.Player;
import org.bukkit.plugin.java.JavaPlugin;
import org.jetbrains.annotations.NotNull;
@@ -36,17 +36,17 @@ public class UserPrefixExpansion extends PlaceholderExpansion {
}
@Override
public String getAuthor() {
public @NotNull String getAuthor() {
return plugin.getDescription().getAuthors().toString();
}
@Override
public String getIdentifier() {
public @NotNull String getIdentifier() {
return "UserPrefix";
}
@Override
public String getVersion() {
public @NotNull String getVersion() {
return plugin.getDescription().getVersion();
}
@@ -82,8 +82,10 @@ public class UserPrefixExpansion extends PlaceholderExpansion {
case "version": {
return getVersion().replace("-SNAPSHOT", "");
}
default: {
return "参数错误";
}
}
return null;
}
}
@@ -0,0 +1,32 @@
package cc.carm.plugin.userprefix.listener;
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 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;
if (!MessageUtil.hasPlaceholderAPI()) return;
try {
event.setFormat(PlaceholderAPI.setPlaceholders(event.getPlayer(), format));
} catch (Exception exception) {
Main.error("请检查配置文件中聊天相关是否配置正确。");
Main.error("Please check the chat configuration.");
exception.printStackTrace();
}
}
}
@@ -1,9 +1,6 @@
package cc.carm.plugin.userprefix.listener;
import cc.carm.plugin.userprefix.configuration.PrefixConfig;
import cc.carm.plugin.userprefix.manager.UserManager;
import cc.carm.plugin.userprefix.ui.PrefixSelectGUI;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
@@ -14,25 +11,13 @@ public class UserListener implements Listener {
@EventHandler
public void onJoin(PlayerJoinEvent event) {
Player player = event.getPlayer();
UserManager.checkPrefix(player, false);
if (PrefixConfig.Functions.NAME_PREFIX.get()) {
UserManager.createNameTag(event.getPlayer());
UserManager.updatePrefixView(event.getPlayer(), true);
}
UserManager.initPlayer(event.getPlayer());
}
@EventHandler
public void onLeave(PlayerQuitEvent event) {
PrefixSelectGUI.removeOpening(event.getPlayer());
UserManager.unloadNameTag(event.getPlayer().getUniqueId());
UserManager.checkingPlayers.remove(event.getPlayer().getUniqueId());
UserManager.unloadPlayer(event.getPlayer());
}
@@ -1,29 +1,22 @@
package cc.carm.plugin.userprefix.listener.processor;
import net.luckperms.api.event.user.UserDataRecalculateEvent;
import net.luckperms.api.model.user.User;
import cc.carm.plugin.userprefix.manager.UserManager;
import 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(NodeRemoveEvent event) {
// if (event.getTarget() instanceof User) {
// if (!(event.getNode() instanceof PermissionNode)) return;
// User user = (User) event.getTarget();
// Player player = Bukkit.getPlayer(user.getUniqueId());
// if (player == null) return;
// UserManager.checkPrefix(player, true);
// }
// }
public static void process(UserDataRecalculateEvent event) {
User user = event.getUser();
Player player = Bukkit.getPlayer(user.getUniqueId());
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,30 +1,47 @@
package cc.carm.plugin.userprefix.manager;
import cc.carm.plugin.userprefix.Main;
import org.bukkit.configuration.file.FileConfiguration;
import cc.carm.plugin.userprefix.configuration.file.FileConfig;
import java.io.File;
public class ConfigManager {
private static FileConfiguration config;
private static FileConfig config;
private static FileConfig messageConfig;
public static void initConfig() {
Main.getInstance().saveDefaultConfig();
Main.getInstance().reloadConfig();
File configFile = new File(Main.getInstance().getDataFolder(), "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);
}
config = Main.getInstance().getConfig();
ConfigManager.config = new FileConfig(Main.getInstance(), "config.yml");
ConfigManager.messageConfig = new FileConfig(Main.getInstance(), "messages.yml");
}
public static FileConfiguration getConfig() {
public static FileConfig getPluginConfig() {
return config;
}
public static void reloadConfig() {
Main.getInstance().reloadConfig();
config = Main.getInstance().getConfig();
public static FileConfig getMessageConfig() {
return messageConfig;
}
public static void reload() {
getPluginConfig().reload();
getMessageConfig().reload();
}
public static void saveConfig() {
Main.getInstance().saveConfig();
getPluginConfig().save();
getMessageConfig().save();
}
@@ -1,6 +1,7 @@
package cc.carm.plugin.userprefix.manager;
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 org.bukkit.Material;
@@ -8,7 +9,11 @@ import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.inventory.ItemFlag;
import org.bukkit.inventory.ItemStack;
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;
@@ -16,114 +21,151 @@ import java.util.stream.Collectors;
public class PrefixManager {
public static ConfiguredPrefix defaultPrefix;
public static HashMap<String, ConfiguredPrefix> prefixes = new HashMap<>();
public static ConfiguredPrefix defaultPrefix;
public static HashMap<String, ConfiguredPrefix> prefixes = new HashMap<>();
private static final String FOLDER_NAME = "prefixes";
public static void init() {
loadConfiguredPrefixes();
Main.log("共加载了 " + prefixes.size() + " 个前缀。");
}
public static void init() {
loadPrefixes();
Main.log("共加载了 " + prefixes.size() + " 个前缀。");
}
public static void loadConfiguredPrefixes() {
loadDefaultPrefix();
public static void loadPrefixes() {
loadDefaultPrefix();
loadConfiguredPrefixes();
}
ConfigurationSection prefixesSection = ConfigManager.getConfig().getConfigurationSection("prefixes");
if (prefixesSection == null || prefixesSection.getKeys(false).isEmpty()) {
Main.log("配置文件中暂无任何前缀配置,请检查。");
return;
}
HashMap<String, ConfiguredPrefix> dataPrefixes = new HashMap<>();
for (String prefixIdentifier : prefixesSection.getKeys(false)) {
ConfigurationSection configuredPrefixSection = prefixesSection.getConfigurationSection(prefixIdentifier);
if (configuredPrefixSection == null) continue;
@SuppressWarnings("ResultOfMethodCallIgnored")
public static void loadConfiguredPrefixes() {
String name = configuredPrefixSection.getString("name", "前缀名配置错误");
String content = configuredPrefixSection.getString("content", "&r");
String permission = configuredPrefixSection.getString("permission");
int weight = configuredPrefixSection.getInt("weight", 1);
File prefixDataFolder = getStorageFolder();
if (!prefixDataFolder.isDirectory() || !prefixDataFolder.exists()) {
prefixDataFolder.mkdir();
}
ItemStack itemHasPermission = configuredPrefixSection.getItemStack("itemHasPermission",
new ItemStackFactory(Material.STONE).setDisplayName(name).addLore(" ").addLore("§a➥ 点击切换到该前缀").toItemStack()
);
ItemStack itemNoPermission = configuredPrefixSection.getItemStack("itemNoPermission", itemHasPermission);
ItemStack itemUsing = configuredPrefixSection.getItemStack("itemUsing", itemHasPermission);
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());
Main.log("完成前缀加载 " + prefixIdentifier + " : " + name);
HashMap<String, ConfiguredPrefix> dataPrefixes = new HashMap<>();
dataPrefixes.put(prefixIdentifier, new ConfiguredPrefix(prefixIdentifier, name, content, weight, permission, itemHasPermission, itemNoPermission, itemUsing));
}
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();
}
}
}
prefixes = dataPrefixes;
}
PrefixManager.prefixes.clear();
PrefixManager.prefixes = dataPrefixes;
}
public static void loadDefaultPrefix() {
ConfigurationSection defaultPrefixSection = ConfigManager.getConfig().getConfigurationSection("defaultPrefix");
if (defaultPrefixSection != null) {
String name = defaultPrefixSection.getString("name", "默认前缀");
String content = defaultPrefixSection.getString("content", "&r");
ItemStack itemNotUsing = defaultPrefixSection.getItemStack(
"itemNotUsing",
new ItemStackFactory(Material.NAME_TAG)
.setDisplayName("&f默认前缀")
.addLore(" ")
.addLore("§a➥ 点击切换到该前缀")
.toItemStack()
);
ItemStack itemUsing = defaultPrefixSection.getItemStack("itemUsing",
new ItemStackFactory(Material.NAME_TAG)
.setDisplayName("&f默认前缀")
.addLore(" ")
.addLore("§a✔ 您正在使用该前缀")
.addEnchant(Enchantment.DURABILITY, 1, false)
.addFlag(ItemFlag.HIDE_ENCHANTS)
.toItemStack()
);
defaultPrefix = new ConfiguredPrefix("default", name, content, 0, null, itemNotUsing, null, itemUsing);
} else {
defaultPrefix = new ConfiguredPrefix("default", "默认前缀", "&r", 0, null,
new ItemStackFactory(Material.NAME_TAG)
.setDisplayName("&f默认前缀")
.addLore(" ")
.addLore("§a➥ 点击切换到该前缀")
.toItemStack(),
null,
new ItemStackFactory(Material.NAME_TAG)
.setDisplayName("&f默认前缀")
.addLore(" ")
.addLore("§a✔ 您正在使用该前缀")
.addEnchant(Enchantment.DURABILITY, 1, false)
.addFlag(ItemFlag.HIDE_ENCHANTS)
.toItemStack()
);
}
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()
);
}
Main.log("完成默认前缀加载 " + defaultPrefix.getName());
}
Main.log("完成默认前缀加载 " + defaultPrefix.getName());
Main.log("Successfully loaded default prefix " + defaultPrefix.getName());
}
public static List<ConfiguredPrefix> getVisiblePrefix() {
return PrefixManager.getPrefixes().values().stream()
.filter(ConfiguredPrefix::isVisibleNoPermission)
.sorted(Comparator.comparingInt(ConfiguredPrefix::getWeight))
.collect(Collectors.toList());
}
public static List<ConfiguredPrefix> getVisiblePrefix() {
return PrefixManager.getPrefixes().values().stream()
.filter(ConfiguredPrefix::isVisibleNoPermission)
.sorted(Comparator.comparingInt(ConfiguredPrefix::getWeight))
.collect(Collectors.toList());
}
public static ConfiguredPrefix getDefaultPrefix() {
return defaultPrefix;
}
@NotNull
public static ConfiguredPrefix getDefaultPrefix() {
return defaultPrefix;
}
public static HashMap<String, ConfiguredPrefix> getPrefixes() {
return prefixes;
}
@NotNull
public static HashMap<String, ConfiguredPrefix> getPrefixes() {
return prefixes;
}
public static ConfiguredPrefix getPrefix(String identifier) {
if (identifier == null || identifier.equalsIgnoreCase("default")) {
return getDefaultPrefix();
} else {
return getPrefixes().get(identifier);
}
}
@Nullable
public static ConfiguredPrefix getPrefix(String identifier) {
if (identifier == null) {
return null;
} else if (identifier.equalsIgnoreCase("default")) {
return getDefaultPrefix();
} else {
return getPrefixes().get(identifier);
}
}
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);
}
}
}
@@ -1,222 +1,289 @@
package cc.carm.plugin.userprefix.manager;
import cc.carm.plugin.userprefix.Main;
import cc.carm.plugin.userprefix.configuration.PrefixConfig;
import cc.carm.plugin.userprefix.configuration.PluginConfig;
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.util.MessageUtil;
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;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.stream.Collectors;
public class UserManager {
public static HashMap<UUID, UserNameTag> nameTags = new HashMap<>();
public static HashMap<UUID, UserNameTag> nameTags = new HashMap<>();
public static HashSet<UUID> checkingPlayers = new HashSet<>();
public static HashSet<UUID> checkingPlayers = new HashSet<>();
public static UserNameTag getNameTag(Player player) {
return nameTags.get(player.getUniqueId());
}
@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;
}
}
public static UserNameTag createNameTag(Player player) {
UserNameTag nameTag = new UserNameTag(player);
nameTags.put(player.getUniqueId(), nameTag);
return nameTag;
}
@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;
}
/**
* 更新前缀显示效果
*
* @param player 玩家
* @param loadOthers 是否为玩家更新其他人的前缀(一般用于加入游戏)
*/
public static void updatePrefixView(Player player, boolean loadOthers) {
ConfiguredPrefix playerPrefix = UserManager.getPrefix(player);
public static void initPlayer(Player player) {
UserManager.checkPrefix(player, false);
if (PluginConfig.Functions.NAME_PREFIX.get()) {
UserManager.createNameTag(player);
UserManager.updatePrefixView(player, true);
}
}
UserNameTag tag = getNameTag(player);
public static void unloadPlayer(Player player) {
PrefixSelectGUI.removeOpening(player);
UserManager.unloadNameTag(player.getUniqueId());
UserManager.checkingPlayers.remove(player.getUniqueId());
GUI.removeOpenedGUI(player); // 清空打开过的GUI缓存 (用于记录物品点击的
}
tag.setPrefix(playerPrefix.getContent());
tag.setOrder(playerPrefix.getWeight());
/**
* 更新前缀显示效果
*
* @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; //未启用的情况下,不需要进行任何操作。
Main.debug("为玩家 " + player.getName() + " 设置了 " + player.getName() + "的前缀为 #" + playerPrefix.getWeight() + " " + playerPrefix.getName());
ConfiguredPrefix playerPrefix = UserManager.getPrefix(player);
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());
tag.setPrefix(playerPrefix.getContent());
tag.setOrder(playerPrefix.getWeight());
}
Main.debug("为玩家 " + player.getName() + " 设置了 " + player.getName() + "的前缀为 #" + playerPrefix.getWeight() + " " + playerPrefix.getName());
if (loadOthers) {
ConfiguredPrefix onlinePlayerPrefix = UserManager.getPrefix(onlinePlayer);
if (onlinePlayerPrefix != null) {
tag.setPrefix(onlinePlayer, onlinePlayerPrefix.getContent());
tag.setOrder(onlinePlayer, onlinePlayerPrefix.getWeight());
Main.debug("为玩家 " + player.getName() + " 设置了 " + player.getName() + "的前缀为 #" + onlinePlayerPrefix.getWeight() + " " + onlinePlayerPrefix.getName());
}
}
}
}
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());
/**
* 检查玩家的前缀的使用权
*
* @param player 玩家
* @param updateView 是否更新头顶与TabList中的前缀
*/
public static void checkPrefix(Player player, boolean updateView) {
if (checkingPlayers.contains(player.getUniqueId())) return;
checkingPlayers.add(player.getUniqueId());
String currentPrefixIdentifier = UserManager.getPrefixData(player);
ConfiguredPrefix currentPrefix = PrefixManager.getPrefix(currentPrefixIdentifier);
if (!UserManager.isPrefixUsable(player, currentPrefixIdentifier)) {
ConfiguredPrefix newPrefix = UserManager.getHighestPrefix(player);
// 更新前缀
UserManager.setPrefix(player, newPrefix, updateView);
// 发送消息
MessageUtil.sendWithPlaceholders(player, PrefixConfig.Messages.EXPIRED.get(),
new String[]{"%(newName)", "%(oldName)"},
new Object[]{newPrefix.getName(), currentPrefix != null ? currentPrefix.getName() : currentPrefixIdentifier}
);
// 播放声音
PrefixConfig.Sounds.PREFIX_EXPIRED.play(player);
}
checkingPlayers.remove(player.getUniqueId());
}
}
public static void unloadNameTag(UUID uuid) {
nameTags.remove(uuid);
}
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());
}
}
}
/**
* 得到玩家的前缀
* 该方法会自动判断玩家当前的前缀是否可用,并返回最终可用的前缀。
*
* @param player 玩家
* @return 前缀配置
*/
public static ConfiguredPrefix getPrefix(Player player) {
String identifier = getPrefixData(player);
if (identifier == null || !isPrefixUsable(player, identifier)) {
return getHighestPrefix(player);
} else {
return PrefixManager.getPrefix(identifier);
}
}
/**
* 检查玩家的前缀的使用权
*
* @param player 玩家
* @param 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);
/**
* 设定玩家前缀
*
* @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);
}
if (!UserManager.isPrefixUsable(player, currentPrefixData)) {
ConfiguredPrefix currentPrefix = PrefixManager.getPrefix(currentPrefixData);
ConfiguredPrefix newPrefix = UserManager.getHighestPrefix(player);
/**
* 得到玩家所有可用的前缀
*
* @param player 玩家
* @return 可用前缀列表
*/
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());
}
if (currentPrefix != null) {
//当前前缀不为空,则代表属于前缀过期的情况
UserPrefixExpireEvent.call(player, currentPrefix);
// 发送消息
PluginConfig.Messages.EXPIRED.sendWithPlaceholders(player,
new String[]{"%(newName)", "%(oldName)"},
new Object[]{newPrefix.getName(), currentPrefix.getName()}
);
// 播放声音
PluginConfig.Sounds.PREFIX_EXPIRED.play(player);
} else {
// 当前前缀为空,则代表是旧的前缀不存在了,
PluginConfig.Messages.REMOVED.sendWithPlaceholders(player,
new String[]{"%(newName)", "%(oldName)"},
new Object[]{newPrefix.getName(), currentPrefixData}
);
}
UserPrefixChangeEvent.call(player, currentPrefix, newPrefix, (after) -> {
if (after != null) {
UserManager.setPrefix(player, after, updateView);
}
checkingPlayers.remove(player.getUniqueId());
});
} else {
checkingPlayers.remove(player.getUniqueId());
}
}
public static 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 玩家
* @param prefix 前缀配置
* @param updateView 是否更新头顶上、TabList的前缀
*/
public static void setPrefix(Player player, ConfiguredPrefix prefix, boolean updateView) {
setPrefixData(player, prefix.getIdentifier());
if (updateView) updatePrefixView(player, false);
}
/**
* 得到玩家所有可用的前缀
*
* @param player 玩家
* @return 可用前缀列表
*/
@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()); // 返回集合
}
/**
* 得到玩家可使用的最高权重的权限
* 注意:若配置文件中关闭了 “autoUsePrefix” ,则会返回默认前缀。
*
* @param player 玩家
* @return 权限内容
*/
public static ConfiguredPrefix getHighestPrefix(Player player) {
if (PrefixConfig.Functions.AUTO_USE.get()) {
// 关闭了自动选择,就直接给默认的前缀,让玩家自己去设置吧
return PrefixManager.getDefaultPrefix();
}
List<ConfiguredPrefix> prefixes = getUsablePrefixes(player);
return prefixes.stream().max(Comparator.comparingInt(ConfiguredPrefix::getWeight)).orElseGet(PrefixManager::getDefaultPrefix);
}
/**
* 得到玩家可使用的最高权重的权限
* 注意:若配置文件中关闭了 “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); // 啥都没有? 返回默认前缀。
}
/**
* 判断一个前缀对某玩家是否可用
*
* @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 prefixIdentifier 前缀标识
* @return 若前缀标识不存在,则返回false;若前缀为默认前缀,或该前缀无权限,或玩家有该前缀的权限,则返回true。
*/
public static boolean isPrefixUsable(Player player, String prefixIdentifier) {
if (prefixIdentifier == null || prefixIdentifier.equalsIgnoreCase("default")) return true;
ConfiguredPrefix prefix = PrefixManager.getPrefix(prefixIdentifier);
if (prefix == null) return false;
return isPrefixUsable(player, prefix);
}
/**
* 判断一个前缀对某玩家是否可用
*
* @param player 玩家
* @param configuredPrefix 前缀配置
* @return 若前缀标识不存在,则返回false;若前缀为默认前缀,或该前缀无权限,或玩家有该前缀的权限,则返回true。
*/
public static boolean isPrefixUsable(Player player, ConfiguredPrefix configuredPrefix) {
return configuredPrefix.getPermission() == null || ServiceManager.hasPermission(ServiceManager.getUser(player), configuredPrefix.getPermission());
}
/**
* 判断一个前缀对某玩家是否可用
*
* @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)
*/
public static String getPrefixData(Player player) {
return ServiceManager.getAPI().getMetaData(player).getMetaValue("userprefix", String::valueOf).orElse(null);
}
/**
* 得到用户当前正在使用的前缀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竟然会把所有的metaKey全部转换为小写... 那我这里就直接写成小写吧~
MetaNode node = MetaNode.builder("userprefix", prefixIdentifier).build();
user.data().add(node);
ServiceManager.getService().getUserManager().saveUser(user);
}
/**
* 设定用户所使用的的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 玩家
*/
public static void clearPrefixData(Player player) {
User user = ServiceManager.getUser(player);
// LuckPerms竟然会把所有的metaKey全部转换为小写... 那我这里就直接写成小写吧~
user.data().clear(NodeType.META.predicate(mn -> mn.getMetaKey().equals("userprefix")));
}
}
@@ -1,97 +1,159 @@
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 {
String identifier;
@Nullable
private File dataFile;
@Nullable
private FileConfiguration configuration;
String name;
String content;
String identifier;
int weight;
String name;
String content;
String permission;
int weight;
ItemStack itemHasPermission;
ItemStack itemNoPermission;
ItemStack itemWhenUsing;
String permission;
public ConfiguredPrefix(String identifier, String name, String content, int weight, String permission, ItemStack itemHasPermission, ItemStack itemNoPermission, ItemStack itemWhenUsing) {
this.identifier = identifier;
this.name = name;
this.content = content;
this.weight = weight;
this.permission = permission;
this.itemHasPermission = itemHasPermission;
this.itemNoPermission = itemNoPermission;
this.itemWhenUsing = itemWhenUsing;
}
public String getIdentifier() {
return identifier;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getContent() {
return ColorParser.parseColor(content);
}
public void setContent(String content) {
this.content = content;
}
public int getWeight() {
return weight;
}
public void setWeight(int weight) {
this.weight = weight;
}
public String getPermission() {
return permission;
}
public void setPermission(String permission) {
this.permission = permission;
}
public ItemStack getItemHasPermission() {
return itemHasPermission;
}
public void setItemHasPermission(ItemStack itemHasPermission) {
this.itemHasPermission = itemHasPermission;
}
public ItemStack getItemNoPermission() {
return itemNoPermission;
}
public void setItemNoPermission(ItemStack itemNoPermission) {
this.itemNoPermission = itemNoPermission;
}
public ItemStack getItemWhenUsing() {
return itemWhenUsing;
}
public void setItemWhenUsing(ItemStack itemWhenUsing) {
this.itemWhenUsing = itemWhenUsing;
}
public boolean isVisibleNoPermission() {
return this.itemNoPermission != null;
}
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,13 +1,13 @@
package cc.carm.plugin.userprefix.ui;
import cc.carm.plugin.userprefix.configuration.PrefixConfig;
import cc.carm.plugin.userprefix.util.MessageUtil;
import cc.carm.plugin.userprefix.util.gui.GUIType;
import cc.carm.plugin.userprefix.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 org.bukkit.entity.Player;
import org.bukkit.event.inventory.ClickType;
@@ -17,78 +17,81 @@ import java.util.List;
public class PrefixSelectGUI extends AutoPagedGUI {
public static HashSet<Player> openingUsers = new HashSet<>();
public static HashSet<Player> openingUsers = new HashSet<>();
Player player;
Player player;
public PrefixSelectGUI(Player player) {
super(GUIType.SIXBYNINE, "&f&l我的前缀 &8| 列表", 10, 43);
this.player = player;
public PrefixSelectGUI(Player player) {
super(GUIType.SIXBYNINE, PluginConfig.GUI.TITLE.get(), 10, 43);
this.player = player;
setPreviousPageSlot(18);
setNextPageSlot(26);
setPreviousPageSlot(18);
setNextPageSlot(26);
loadItems();
}
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 loadItems() {
List<ConfiguredPrefix> prefixList = new ArrayList<>();
prefixList.add(PrefixManager.getDefaultPrefix());
prefixList.addAll(PrefixManager.getVisiblePrefix()); //只需要读取看得见的
ConfiguredPrefix usingPrefix = UserManager.getPrefix(getPlayer());
ConfiguredPrefix usingPrefix = UserManager.getPrefix(getPlayer());
for (ConfiguredPrefix prefix : prefixList) {
if (prefix.getIdentifier().equals(usingPrefix.getIdentifier())) {
addItem(new GUIItem(prefix.getItemWhenUsing() != null ? prefix.getItemWhenUsing() : prefix.getItemHasPermission()));
} else if (UserManager.isPrefixUsable(player, prefix)) {
addItem(new GUIItem(prefix.getItemHasPermission()) {
@Override
public void onClick(ClickType type) {
if (UserManager.isPrefixUsable(player, prefix)) { //再次检查,防止打开GUI后、选择前的时间段内权限消失
player.closeInventory();
UserManager.setPrefix(player, prefix, true);
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.Sounds.PREFIX_CHANGE.play(player);
MessageUtil.sendWithPlaceholders(player, PrefixConfig.Messages.SELECTED.get(),
new String[]{"%(name)"},
new Object[]{prefix.getName()});
PluginConfig.Sounds.PREFIX_CHANGE.play(player);
MessageUtil.sendWithPlaceholders(player, PluginConfig.Messages.SELECTED.get(),
new String[]{"%(name)"},
new Object[]{prefix.getName()});
}
}
});
} else {
addItem(new GUIItem(prefix.getItemNoPermission()));
}
}
}
}
});
} else {
addItem(new GUIItem(prefix.getItemNoPermission(player)));
}
}
}
}
@Override
public void onClose() {
openingUsers.remove(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();
}
}
public static void closeAll() {
for (Player player : new HashSet<>(openingUsers)) {
player.closeInventory();
}
openingUsers.clear();
}
public static void open(Player player) {
PrefixConfig.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.play(player);
new PrefixSelectGUI(player).openGUI(player);
openingUsers.add(player);
}
}
@@ -1,10 +1,33 @@
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;
}
}
@@ -6,11 +6,11 @@ import org.bukkit.enchantments.Enchantment;
import org.bukkit.inventory.ItemFlag;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.inventory.meta.SkullMeta;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.stream.Collectors;
public class ItemStackFactory {
ItemStack item;
@@ -57,85 +57,83 @@ public class ItemStackFactory {
return this;
}
public ItemStackFactory setDisplayName(String name) {
public ItemStackFactory setDisplayName(@NotNull String name) {
ItemMeta im = this.item.getItemMeta();
im.setDisplayName(name.replace("&", "§").replace("§§", "&&"));
this.item.setItemMeta(im);
return this;
}
public ItemStackFactory setLore(List<String> lores) {
ItemMeta im = this.item.getItemMeta();
List<String> lores_ = new ArrayList();
Iterator var4 = lores.iterator();
while (var4.hasNext()) {
String lore = (String) var4.next();
lores_.add(lore.replace("&", "§").replace("§§", "&&"));
if (im != null) {
im.setDisplayName(ColorParser.parse(name));
this.item.setItemMeta(im);
}
im.setLore(lores_);
this.item.setItemMeta(im);
return this;
}
public ItemStackFactory addLore(String name) {
public ItemStackFactory setLore(@NotNull List<String> loreList) {
ItemMeta im = this.item.getItemMeta();
Object lores;
if (im.hasLore()) {
lores = im.getLore();
} else {
lores = new ArrayList();
if (im != null) {
im.setLore(
loreList.stream()
.map(ColorParser::parse)
.collect(Collectors.toList())
);
this.item.setItemMeta(im);
}
((List) lores).add(name.replace("&", "§").replace("§§", "&&"));
im.setLore((List) lores);
this.item.setItemMeta(im);
return this;
}
public ItemStackFactory addEnchant(Enchantment ench, int level, boolean ignoreLevelRestriction) {
public ItemStackFactory addLore(@NotNull String s) {
ItemMeta im = this.item.getItemMeta();
im.addEnchant(ench, level, ignoreLevelRestriction);
this.item.setItemMeta(im);
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 removeEnchant(Enchantment ench) {
public ItemStackFactory addEnchant(@NotNull Enchantment enchant, int level, boolean ignoreLevelRestriction) {
ItemMeta im = this.item.getItemMeta();
im.removeEnchant(ench);
this.item.setItemMeta(im);
return this;
}
public ItemStackFactory addFlag(ItemFlag flag) {
ItemMeta im = this.item.getItemMeta();
im.addItemFlags(new ItemFlag[]{flag});
this.item.setItemMeta(im);
return this;
}
public ItemStackFactory removeFlag(ItemFlag flag) {
ItemMeta im = this.item.getItemMeta();
im.removeItemFlags(new ItemFlag[]{flag});
this.item.setItemMeta(im);
return this;
}
public ItemStackFactory setUnbreakable(boolean unbreakable) {
ItemMeta im = this.item.getItemMeta();
im.setUnbreakable(unbreakable);
this.item.setItemMeta(im);
return this;
}
public ItemStackFactory setSkullOwner(String name) {
if (this.item.getType() == Material.PLAYER_HEAD || this.item.getType() == Material.PLAYER_WALL_HEAD) {
SkullMeta im = (SkullMeta) this.item.getItemMeta();
im.setOwner(name);
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,63 +1,102 @@
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 void send(Player player, List<String> messages) {
for (String s : messages) {
player.sendMessage(ColorParser.parseColor(s));
}
}
public static boolean hasPlaceholderAPI() {
return Bukkit.getPluginManager().getPlugin("PlaceholderAPI") != null;
}
public static void send(Player player, String... messages) {
send(player, Arrays.asList(messages));
}
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 sendWithPlaceholders(Player player, String... messages) {
sendWithPlaceholders(player, Arrays.asList(messages));
}
public static void send(@Nullable CommandSender sender, String... messages) {
send(sender, Arrays.asList(messages));
}
public static void sendWithPlaceholders(Player player, List<String> messages) {
send(player, PlaceholderAPI.setPlaceholders(player, messages));
}
public static void sendWithPlaceholders(CommandSender sender, String... messages) {
sendWithPlaceholders(sender, Arrays.asList(messages));
}
public static void sendWithPlaceholders(Player player, List<String> messages, String param, Object value) {
sendWithPlaceholders(player, messages, new String[]{param}, new Object[]{value});
}
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(Player player, List<String> messages, String[] params, Object[] values) {
sendWithPlaceholders(player, setCustomParams(messages, params, values));
}
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 List<String> setCustomParams(List<String> messages, String param, Object value) {
return setCustomParams(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 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 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;
}
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,105 +1,93 @@
package cc.carm.plugin.userprefix.util.gui;
import cc.carm.plugin.userprefix.configuration.PrefixConfig;
import cc.carm.plugin.userprefix.util.ItemStackFactory;
import org.bukkit.Material;
import 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;
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[] range) {
super(type, name, range);
}
public AutoPagedGUI(GUIType type, String name, int a, int b) {
super(type, name, a, b);
}
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 setPreviousPageUI(ItemStack lastPageUI) {
this.previousPageUI = lastPageUI;
}
public void setNextPageUI(ItemStack nextPageUI) {
this.nextPageUI = nextPageUI;
}
public void setNextPageUI(ItemStack nextPageUI) {
this.nextPageUI = nextPageUI;
}
public void setNoPreviousPageUI(ItemStack noPreviousPageUI) {
this.noPreviousPageUI = noPreviousPageUI;
}
public void setNoPreviousPageUI(ItemStack noPreviousPageUI) {
this.noPreviousPageUI = noPreviousPageUI;
}
public void setNoNextPageUI(ItemStack noNextPageUI) {
this.noNextPageUI = noNextPageUI;
}
public void setNoNextPageUI(ItemStack noNextPageUI) {
this.noNextPageUI = noNextPageUI;
}
public void setPreviousPageSlot(int slot) {
this.previousPageSlot = slot;
}
public void setPreviousPageSlot(int slot) {
this.previousPageSlot = slot;
}
public void setNextPageSlot(int slot) {
this.nextPageSlot = slot;
}
public void setNextPageSlot(int slot) {
this.nextPageSlot = slot;
}
@Override
public void openGUI(Player user) {
if (previousPageSlot >= 0)
if (hasPreviousPage()) {
setItem(previousPageSlot, new GUIItem(previousPageUI == null ? new ItemStackFactory(Material.ARROW)
.setDisplayName("&f上一页")
.addLore("&7&o右键可前往第一页哦")
.toItemStack() : previousPageUI) {
@Override
public void ClickAction(ClickType type, Player u) {
if (type == ClickType.RIGHT) {
goFirstPage();
} else {
goPreviousPage();
}
PrefixConfig.Sounds.GUI_CLICK.play(u);
openGUI(u);
// u.playSound(u.getLocation(), Sound.ENTITY_CHICKEN_EGG, 0.5f, 1);
}
});
} else {
// setItem(previousPageSlot, new GUIItem(noPreviousPageUI == null ? new ItemStackFactory(Material.GRAY_STAINED_GLASS_PANE)
// .setDisplayName("已经是第一页啦")
// .toItemStack() : noPreviousPageUI));
}
@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 (previousPageSlot >= 0)
if (hasNextPage()) {
setItem(nextPageSlot, new GUIItem(nextPageUI == null ? new ItemStackFactory(Material.ARROW)
.setDisplayName("下一页")
.addLore("&7&o右键可前往最后一页哦")
.toItemStack() : nextPageUI) {
@Override
public void ClickAction(ClickType type, Player u) {
if (type == ClickType.RIGHT) {
goLastPage();
} else {
goNextPage();
}
PrefixConfig.Sounds.GUI_CLICK.play(u);
openGUI(u);
// u.playSound(u.getLocation(), Sound.ENTITY_CHICKEN_EGG, 0.5f, 1);
}
});
} else {
// setItem(nextPageSlot, new GUIItem(noNextPageUI == null ? new ItemStackFactory(Material.GRAY_STAINED_GLASS_PANE)
// .setDisplayName("已经是最后一页啦")
// .toItemStack() : noNextPageUI));
}
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);
}
super.openGUI(user);
}
}
@@ -12,7 +12,6 @@ import org.bukkit.event.Listener;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.event.inventory.InventoryCloseEvent;
import org.bukkit.event.inventory.InventoryDragEvent;
import org.bukkit.event.inventory.InventoryType;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
@@ -39,7 +38,7 @@ public class GUI {
public GUI(GUIType type, String name) {
this.type = type;
this.name = ColorParser.parseColor(name);
this.name = ColorParser.parse(name);
switch (type) {
case ONEBYNINE:
this.items = new GUIItem[9];
@@ -56,52 +55,11 @@ public class GUI {
case FIVEBYNINE:
this.items = new GUIItem[45];
break;
default:
case SIXBYNINE:
this.items = new GUIItem[54];
break;
case HOPPER:
this.items = new GUIItem[InventoryType.HOPPER.getDefaultSize()];
break;
case BEACON:
this.items = new GUIItem[InventoryType.BEACON.getDefaultSize()];
break;
case DISPENSER:
this.items = new GUIItem[InventoryType.DISPENSER.getDefaultSize()];
break;
case DROPPER:
this.items = new GUIItem[InventoryType.DROPPER.getDefaultSize()];
break;
case FURNACE:
this.items = new GUIItem[InventoryType.FURNACE.getDefaultSize()];
break;
case WORKBENCH:
this.items = new GUIItem[InventoryType.WORKBENCH.getDefaultSize()];
break;
case CRAFTING:
this.items = new GUIItem[InventoryType.CRAFTING.getDefaultSize()];
break;
case ENCHANTING:
this.items = new GUIItem[InventoryType.ENCHANTING.getDefaultSize()];
break;
case BREWING:
this.items = new GUIItem[InventoryType.BREWING.getDefaultSize()];
break;
case PLAYER:
this.items = new GUIItem[InventoryType.PLAYER.getDefaultSize()];
break;
case MERCHANT:
this.items = new GUIItem[InventoryType.MERCHANT.getDefaultSize()];
break;
case ENDER_CHEST:
this.items = new GUIItem[InventoryType.ENDER_CHEST.getDefaultSize()];
break;
case CREATIVE:
this.items = new GUIItem[InventoryType.CREATIVE.getDefaultSize()];
break;
case CANCEL:
default:
this.items = null;
}
}
@@ -209,57 +167,8 @@ public class GUI {
if (this.type == GUIType.CANCEL) {
throw new NullPointerException("被取消或不存在的GUI");
}
switch (type) {
default:
case ONEBYNINE:
case TWOBYNINE:
case THREEBYNINE:
case FOURBYNINE:
case FIVEBYNINE:
case SIXBYNINE:
inv = Bukkit.createInventory(null, this.items.length, this.name);
break;
case HOPPER:
inv = Bukkit.createInventory(null, InventoryType.HOPPER, this.name);
break;
case BEACON:
inv = Bukkit.createInventory(null, InventoryType.BEACON, this.name);
break;
case DISPENSER:
inv = Bukkit.createInventory(null, InventoryType.DISPENSER, this.name);
break;
case DROPPER:
inv = Bukkit.createInventory(null, InventoryType.DROPPER, this.name);
break;
case FURNACE:
inv = Bukkit.createInventory(null, InventoryType.FURNACE, this.name);
break;
case WORKBENCH:
inv = Bukkit.createInventory(null, InventoryType.WORKBENCH, this.name);
break;
case CRAFTING:
inv = Bukkit.createInventory(null, InventoryType.CRAFTING, this.name);
break;
case ENCHANTING:
inv = Bukkit.createInventory(null, InventoryType.ENCHANTING, this.name);
break;
case BREWING:
inv = Bukkit.createInventory(null, InventoryType.BREWING, this.name);
break;
case PLAYER:
inv = Bukkit.createInventory(null, InventoryType.PLAYER, this.name);
break;
case CREATIVE:
inv = Bukkit.createInventory(null, InventoryType.CREATIVE, this.name);
break;
case MERCHANT:
inv = Bukkit.createInventory(null, InventoryType.MERCHANT, this.name);
break;
case ENDER_CHEST:
inv = Bukkit.createInventory(null, InventoryType.ENDER_CHEST, this.name);
break;
}
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));
@@ -275,31 +184,34 @@ public class GUI {
Bukkit.getPluginManager().registerEvents(listener = new Listener() {
@EventHandler
public void onInventoryClickEvent(InventoryClickEvent event) {
rawClickListener(event);
if (!(event.getWhoClicked() instanceof Player)) {
return;
}
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)
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();
System.err.print("err cause by GUI(" + GUI.this.toString() + "), name=" + name);
return;
}
} else {
if (setCancelledIfClickOnOuter) event.setCancelled(true);
} else if (setCancelledIfClickOnOuter) {
event.setCancelled(true);
}
if (hasOpenedGUI(p) && /*player.openedGUI.inv.equals(event.getClickedInventory())*/ getOpenedGUI(p) == GUI.this && event.getClickedInventory() != null) {
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()].ClickAction(event.getClick(), player);
GUI.this.items[event.getSlot()].rawClickAction(event);
if (!GUI.this.items[event.getSlot()].actions.isEmpty()) {
for (GUIItem.GUIClickAction action : GUI.this.items[event.getSlot()].actions) {
@@ -313,8 +225,8 @@ public class GUI {
}
}
}
} else if (event.getClickedInventory().equals(p.getInventory())) {
if (setCancelledIfClickOnSelf) event.setCancelled(true);
} else if (event.getClickedInventory().equals(p.getInventory()) && setCancelledIfClickOnSelf) {
event.setCancelled(true);
}
}
}
@@ -336,6 +248,7 @@ public class GUI {
if (event.getInventory().equals(inv)) {
HandlerList.unregisterAll(this);
listener = null;
removeOpenedGUI(p);
onClose();
}
}
@@ -36,17 +36,6 @@ public class GUIItem {
actionActive = b;
}
/**
* 玩家点击GUI后执行的代码
*
* @param type 点击的类型
* @param player 点击GUI的玩家
*/
@Deprecated
public void ClickAction(ClickType type, Player player) {
}
/**
* 玩家点击GUI后执行的代码
*
@@ -64,10 +53,6 @@ public class GUIItem {
actionsIgnoreActive.add(action);
}
public void customAction() {
}
public void rawClickAction(InventoryClickEvent event) {
}
@@ -76,10 +61,6 @@ public class GUIItem {
}
public void customAction(Object obj) {
}
/**
* 玩家点击GUI后执行的代码
*
@@ -11,20 +11,6 @@ public enum GUIType {
FOURBYNINE,
FIVEBYNINE,
SIXBYNINE,
DISPENSER,
DROPPER,
FURNACE,
WORKBENCH,
CRAFTING,
ENCHANTING,
BREWING,
PLAYER,
CREATIVE,
MERCHANT,
ENDER_CHEST,
BEACON,
HOPPER,
UNKNOWN,
CANCEL;
}
+55 -67
View File
@@ -1,19 +1,61 @@
version: ${project.version}
version: ${project.version} #配置文件版本,若与插件版本不同请记得检查配置文件内容
debug: false
# 统计数据设定
# 改选项用于帮助开发者统计插件版本与使用情况,且绝不会影响性能与使用体验。
# 当然,您也可以选择在这里关闭,或在plugins/bStats下的配置文件中关闭。
metrics: true
# 检查更新设定
# 该选项用于插件判断是否要检查更新,若您不希望插件检查更新并提示您,可以选择关闭。
# 检查更新为异步操作,绝不会影响性能与使用体验。
check-update: 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” 不可缺少,分别代表 玩家名 与 消息内容 。
messages:
selected:
- "&7您选择了 &f%(name) &7作为当前显示的前缀。"
expired:
- "&7您先前使用的前缀 &f%(oldName) &7已到期。"
- "&7现在已为您重新调整为 &f%(newName) &7。"
help:
- "&7输入 &b/prefix &7打开前缀选择菜单。"
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"
@@ -32,71 +74,17 @@ defaultPrefix:
meta:
==: ItemMeta
meta-type: UNSPECIFIC
display-name: "§f默认玩家前缀 §f(点击切换)"
display-name: "&f默认玩家前缀 &f(点击切换)"
lore:
- ""
- "§a➥ 点击切换到该前缀"
- "&a➥ 点击切换到该前缀"
itemUsing:
==: org.bukkit.inventory.ItemStack
type: NAME_TAG
meta:
==: ItemMeta
meta-type: UNSPECIFIC
display-name: "§f默认玩家前缀"
display-name: "&f默认玩家前缀"
lore:
- ""
- "§a✔ 您正在使用该前缀"
prefixes:
VIP:
name: "&b&lPro&b" # [必须] 名字(切换的时候左下角会弹提示 用的就是这个名字)
content: "§b§lPro §b" # [必须] 显示在名字前面的内容
weight: 1 # [必须] 权重,用于GUI里面的排序(越大显示在越后面)和自动前缀显示
permission: "yc.pro" # [非必须] 检测的权限,如果没有就是人人都能用,也代表不用配置“itemNoPermission”了(因为压根不可能显示没权限时候的物品)
itemHasPermission: # [必须] 当有权限的时候会显示这个Item
==: org.bukkit.inventory.ItemStack
type: DIAMOND
meta:
==: ItemMeta
meta-type: UNSPECIFIC
display-name: "§b§lPro §b会员前缀"
lore:
- "§7Pro会员专属称号"
- ""
- "§f尊贵的Pro会员专属称号。"
- "§f您将获得多种特权与更好的游戏体验。"
- ""
- "§a➥ 点击切换到该前缀"
itemUsing: # [非必需] 当有权限的时候会显示这个Item,如果没有这个配置就自动显示“itemHasPermission”的。
==: org.bukkit.inventory.ItemStack
type: DIAMOND
meta:
==: ItemMeta
meta-type: UNSPECIFIC
display-name: "§b§lPro §b会员前缀"
enchants:
PROTECTION_ENVIRONMENTAL: 1 #加一个附魔这样看上去就像是选中了的
lore:
- "§7Pro会员专属称号"
- ""
- "§f尊贵的Pro会员专属称号。"
- "§f您将获得多种特权与更好的游戏体验。"
- ""
- "§a✔ 您正在使用该前缀"
itemNoPermission: # [非必需] 如果没有权限就会显示这个item。如果不配置该物品,则玩家没有使用权限时不会显示在GUI里面。
==: org.bukkit.inventory.ItemStack
type: INK_SACK
damage: 8
meta:
==: ItemMeta
meta-type: UNSPECIFIC
display-name: "§b§lPro+ §b会员前缀 §c(未拥有)"
lore:
- "§7Pro+会员专属称号"
- ""
- "§f尊贵的Pro会员专属称号。"
- "§f您将获得多种特权与更好的游戏体验。"
- "§f您可以输入 §b/vip §f指令查看详细特权!"
- ""
- "§e✯ 加入Pro+会员以使用该前缀!"
- "&a✔ 您正在使用该前缀"
+90
View File
@@ -0,0 +1,90 @@
version: ${project.version} # DO NOT EDIT IT
debug: false #DEBUG OUT PUT
metrics: true #Metrics stats (to help developer know the stats)
# Auto check the updates.
check-update: true
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"
@@ -0,0 +1,68 @@
# 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
@@ -0,0 +1,21 @@
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)"
+21
View File
@@ -0,0 +1,21 @@
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)"
+6 -1
View File
@@ -1,8 +1,13 @@
main: cc.carm.plugin.userprefix.Main
name: UserPrefix
name: ${project.name}
version: ${project.version}
authors:
- Carm
- ${project.organization.name}
- SakuraGame
website: ${project.url}
description: ${project.description}
depend:
- LuckPerms
softdepend:
@@ -0,0 +1,78 @@
# 唯一标识 [必须]
# 将用于记录玩家所选的前缀,以及用于数据的缓存。
# 必须 必须 必须 保持唯一!
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+会员以使用该前缀!"
+14
View File
@@ -0,0 +1,14 @@
import cc.carm.plugin.userprefix.util.ColorParser;
import org.junit.Test;
public class ColorParseTest {
@Test
public void onTest() {
String testString = "&f爱的人永远不爱我,为何付出得到的只有&(#aaaaaa)背叛。";
System.out.println(ColorParser.parseHexColor(testString));
}
}