├── .documentation
├── README.md
├── javadoc
│ └── JAVADOC-README.md
└── repository
│ └── REPO-README.md
├── .github
├── FUNDING.yml
├── ISSUE_TEMPLATE
│ ├── bugs_report.md
│ └── feature_issues.md
└── workflows
│ ├── deploy.yml
│ └── maven.yml
├── .gitignore
├── LICENSE
├── README.md
├── base
├── color
│ ├── pom.xml
│ └── src
│ │ ├── main
│ │ └── java
│ │ │ └── cc
│ │ │ └── carm
│ │ │ └── lib
│ │ │ └── easyplugin
│ │ │ └── utils
│ │ │ └── ColorParser.java
│ │ └── test
│ │ └── java
│ │ └── ColorParseTest.java
├── command-alias
│ ├── pom.xml
│ └── src
│ │ └── main
│ │ └── java
│ │ └── cc
│ │ └── carm
│ │ └── lib
│ │ └── easyplugin
│ │ └── command
│ │ └── alias
│ │ ├── AliasCommand.java
│ │ └── AliasCommandManager.java
├── command
│ ├── pom.xml
│ └── src
│ │ └── main
│ │ └── java
│ │ └── cc
│ │ └── carm
│ │ └── lib
│ │ └── easyplugin
│ │ └── command
│ │ ├── CommandHandler.java
│ │ ├── NamedExecutor.java
│ │ ├── SimpleCompleter.java
│ │ └── SubCommand.java
├── gui
│ ├── pom.xml
│ └── src
│ │ ├── main
│ │ └── java
│ │ │ └── cc
│ │ │ └── carm
│ │ │ └── lib
│ │ │ └── easyplugin
│ │ │ └── gui
│ │ │ ├── GUI.java
│ │ │ ├── GUIItem.java
│ │ │ ├── GUIListener.java
│ │ │ ├── GUIType.java
│ │ │ ├── configuration
│ │ │ ├── GUIActionConfiguration.java
│ │ │ ├── GUIActionType.java
│ │ │ ├── GUIConfiguration.java
│ │ │ └── GUIItemConfiguration.java
│ │ │ └── paged
│ │ │ ├── AutoPagedGUI.java
│ │ │ ├── CommonPagedGUI.java
│ │ │ └── PagedGUI.java
│ │ └── test
│ │ └── java
│ │ ├── ActionReadTest.java
│ │ └── PageTest.java
├── main
│ ├── pom.xml
│ └── src
│ │ └── main
│ │ └── java
│ │ └── cc
│ │ └── carm
│ │ └── lib
│ │ └── easyplugin
│ │ ├── EasyPlugin.java
│ │ ├── i18n
│ │ └── EasyPluginMessageProvider.java
│ │ └── utils
│ │ ├── ItemStackFactory.java
│ │ ├── MessageUtils.java
│ │ └── SchedulerUtils.java
├── messages
│ └── pom.xml
├── storage
│ ├── pom.xml
│ └── src
│ │ └── main
│ │ └── java
│ │ └── cc
│ │ └── carm
│ │ └── lib
│ │ └── easyplugin
│ │ └── storage
│ │ ├── DataStorage.java
│ │ ├── StorageType.java
│ │ ├── file
│ │ ├── FileBasedStorage.java
│ │ ├── FolderBasedStorage.java
│ │ └── YAMLBasedStorage.java
│ │ └── plugin
│ │ └── PluginBasedStorage.java
├── user
│ ├── pom.xml
│ └── src
│ │ └── main
│ │ └── java
│ │ └── cc
│ │ └── carm
│ │ └── lib
│ │ └── easyplugin
│ │ └── user
│ │ ├── AbstractUserData.java
│ │ ├── UserData.java
│ │ ├── UserDataManager.java
│ │ └── UserDataRegistry.java
└── utils
│ ├── pom.xml
│ └── src
│ └── main
│ └── java
│ └── cc
│ └── carm
│ └── lib
│ └── easyplugin
│ └── utils
│ ├── EasyCooldown.java
│ └── JarResourceUtils.java
├── collection
├── all
│ └── pom.xml
├── bom
│ └── pom.xml
└── common
│ └── pom.xml
├── extension
├── gh-checker
│ ├── pom.xml
│ └── src
│ │ ├── main
│ │ └── java
│ │ │ └── cc
│ │ │ └── carm
│ │ │ └── lib
│ │ │ └── easyplugin
│ │ │ └── updatechecker
│ │ │ └── GHUpdateChecker.java
│ │ └── test
│ │ └── java
│ │ └── cc
│ │ └── carm
│ │ └── lib
│ │ └── easyplugin
│ │ └── updatechecker
│ │ └── RegexTest.java
├── papi
│ ├── pom.xml
│ └── src
│ │ ├── main
│ │ └── java
│ │ │ └── cc
│ │ │ └── carm
│ │ │ └── lib
│ │ │ └── easyplugin
│ │ │ └── papi
│ │ │ ├── EasyPlaceholder.java
│ │ │ ├── expansion
│ │ │ ├── EasyExpansion.java
│ │ │ ├── SectionExpansion.java
│ │ │ └── SubExpansion.java
│ │ │ └── handler
│ │ │ └── PlaceholderHandler.java
│ │ └── test
│ │ └── java
│ │ └── cc
│ │ └── carm
│ │ └── tests
│ │ └── easyplugin
│ │ └── papi
│ │ ├── DemoPlaceholder.java
│ │ └── PlaceholderTest.java
└── vault
│ ├── pom.xml
│ └── src
│ └── main
│ └── java
│ └── cc
│ └── carm
│ └── lib
│ └── easyplugin
│ └── vault
│ └── EasyVault.java
├── pom.xml
└── renovate.json
/.documentation/README.md:
--------------------------------------------------------------------------------
1 | # 欢迎使用 EasyPlugin !
2 |
3 | 这个项目刚刚创建,详细的Javadoc与开发指南还在补充,请给我一点时间~
--------------------------------------------------------------------------------
/.documentation/javadoc/JAVADOC-README.md:
--------------------------------------------------------------------------------
1 | # EasyPlugin Javadoc
2 |
3 | 基于 [Github Pages](https://pages.github.com/) 搭建,请访问 [JavaDoc](https://carmjos.github.io/EasyPlugin) 。
4 |
5 | ## 如何实现?
6 |
7 | 若您也想通过 [Github Actions](https://docs.github.com/en/actions/learn-github-actions)
8 | 自动部署项目的Javadoc到 [Github Pages](https://pages.github.com/) ,
9 | 可以参考我的文章 [《自动部署Javadoc到Github Pages》](https://pages.carm.cc/doc/javadoc-in-github.html) 。
--------------------------------------------------------------------------------
/.documentation/repository/REPO-README.md:
--------------------------------------------------------------------------------
1 | # EasyPlugin Repository
2 |
3 | 采用github的repo分支进行依赖,随项目发布而自动更新。
4 |
5 | 其他依赖方式见主页介绍。
6 |
7 | ## 依赖方式
8 |
9 | ### Maven
10 |
11 | ```xml
12 |
13 |
14 | EasyPlugin
15 | GitHub Branch Repository
16 | https://github.com/CarmJos/EasyPlugin/blob/repo/
17 |
18 |
19 | ```
20 |
21 | ### Gradle
22 |
23 | ```groovy
24 | repositories {
25 | maven { url 'https://github.com/CarmJos/EasyPlugin/blob/repo/' }
26 | }
27 | ```
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | #github: [ CarmJos ]
2 | custom: [ 'https://donate.carm.cc' ]
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bugs_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: 问题提交
3 | about: 描述问题并提交,帮助我们对其进行检查与修复。
4 | title: ''
5 | labels: bug
6 | assignees: ''
7 |
8 | ---
9 |
10 | ### **问题简述**
11 |
12 | 用简短的话语描述一下大概问题。
13 |
14 | ### **问题来源**
15 |
16 | 描述一下通过哪些操作才发现的问题,如:
17 |
18 | 1. 使用了 ...
19 | 2. 输入了 ...
20 | 3. 出现了报错 ...
21 |
22 | ### **预期结果**(可选)
23 |
24 | 如果问题不发生,应该是什么情况
25 |
26 | ### **问题截图/问题报错**
27 |
28 | 如果有报错或输出,请提供截图。
29 |
30 | ### **操作环境**
31 |
32 | - 系统环境: `Windows 10` / `Ubuntu` / `...`
33 | - Java版本: `JDK11` / `OPENJDK8` / `JRE8` / `...`
34 | - 服务端版本: 请在后台输入 `version` 并复制相关输出。
35 |
36 | ### **其他补充**
37 |
38 | 如有其他补充,可以在这里描述。
39 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_issues.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: 功能需求
3 | about: 希望我们提供更多的功能。
4 | title: ''
5 | labels: enhancement
6 | assignees: ''
7 |
8 | ---
9 |
10 | ### **功能简述**
11 |
12 | 简单的描述一下你想要的功能
13 |
14 | ### **需求来源**
15 |
16 | 简单的描述一下为什么需要这个功能。
17 |
18 | ### **功能参考**(可选)
19 |
20 | 如果有相关功能的参考,如文本、截图,请提供给我们。
21 |
22 | ### **附加内容**
23 |
24 | 如果有什么小细节需要重点注意,请在这里告诉我们。
25 |
--------------------------------------------------------------------------------
/.github/workflows/deploy.yml:
--------------------------------------------------------------------------------
1 | # This workflow will build a Java project with Maven, and cache/restore any dependencies to improve the workflow execution time
2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven
3 |
4 | name: Deploy & Publish
5 |
6 | on:
7 | # 支持手动触发构建
8 | workflow_dispatch:
9 | release:
10 | # 创建release的时候触发
11 | types: [ published ]
12 |
13 | jobs:
14 | packages-deploy:
15 | runs-on: ubuntu-latest
16 | steps:
17 | - uses: actions/checkout@v4
18 | - name: "Set up JDK"
19 | uses: actions/setup-java@v4
20 | with:
21 | java-version: '8'
22 | distribution: 'adopt'
23 | cache: maven
24 | server-id: github
25 | server-username: MAVEN_USERNAME
26 | server-password: MAVEN_TOKEN
27 |
28 | - name: "Maven Deploy"
29 | run: mvn -B -Pgithub deploy --file pom.xml -DskipTests -Dgpg.skip
30 | env:
31 | MAVEN_USERNAME: ${{ github.repository_owner }}
32 | MAVEN_TOKEN: ${{secrets.GITHUB_TOKEN}}
33 |
34 |
35 | github-deploy:
36 | runs-on: ubuntu-latest
37 | steps:
38 | - uses: actions/checkout@v4
39 | - name: "Set up JDK"
40 | uses: actions/setup-java@v4
41 | with:
42 | java-version: '8'
43 | distribution: 'adopt'
44 | cache: maven
45 | - name: "Maven Deploy"
46 | run: mvn -B -Plocal deploy --file pom.xml -DskipTests -Dgpg.skip
47 |
48 | - name: "Copy artifacts"
49 | run: |
50 | rm -rf deploy
51 | mkdir -vp deploy
52 | cp -vrf $HOME/local-deploy/* deploy/
53 | cp -vrf .documentation/repository/REPO-README.md deploy/README.md
54 |
55 | - name: "Generate Javadoc"
56 | run: mvn -B javadoc:aggregate --file pom.xml -DskipTests
57 |
58 | - name: "Copy Javadoc"
59 | run: |
60 | rm -rf docs
61 | mkdir -vp docs
62 | cp -vrf target/reports/apidocs/* docs/
63 | cp -vrf .documentation/javadoc/JAVADOC-README.md docs/README.md
64 |
65 | - name: "Generate Javadoc sitemap"
66 | id: sitemap
67 | uses: cicirello/generate-sitemap@v1
68 | with:
69 | base-url-path: https://${{ github.repository_owner }}.github.io/${{ github.event.repository.name }}
70 | path-to-root: docs
71 |
72 | - name: "Output Javadoc stats"
73 | run: |
74 | echo "sitemap-path = ${{ steps.sitemap.outputs.sitemap-path }}"
75 | echo "url-count = ${{ steps.sitemap.outputs.url-count }}"
76 | echo "excluded-count = ${{ steps.sitemap.outputs.excluded-count }}"
77 | ls -l docs
78 |
79 | - name: "Configure Git"
80 | env:
81 | DEPLOY_PRI: ${{secrets.DEPLOY_PRI}}
82 | run: |
83 | sudo timedatectl set-timezone "Asia/Shanghai"
84 | mkdir -p ~/.ssh/
85 | echo "$DEPLOY_PRI" > ~/.ssh/id_rsa
86 | chmod 600 ~/.ssh/id_rsa
87 | ssh-keyscan github.com >> ~/.ssh/known_hosts
88 | git config --global user.name '${{ github.repository_owner }}'
89 | git config --global user.email '${{ github.repository_owner }}@users.noreply.github.com'
90 |
91 | - name: "Commit&Push repository files"
92 | run: |
93 | cd deploy
94 | git init
95 | git remote add origin git@github.com:${{ github.repository_owner }}/${{ github.event.repository.name }}.git
96 | git checkout -b repo
97 | git add -A
98 | git commit -m "Maven project deployment."
99 | git push origin HEAD:repo --force
100 |
101 | - name: "Commit&Push API documentation"
102 | run: |
103 | cd docs
104 | git init
105 | git remote add origin git@github.com:${{ github.repository_owner }}/${{ github.event.repository.name }}.git
106 | git checkout -b gh-pages
107 | git add -A
108 | git commit -m "API Document generated."
109 | git push origin HEAD:gh-pages --force
110 |
111 | central-deploy:
112 | name: "Deploy Project (Central)"
113 | runs-on: ubuntu-latest
114 | steps:
115 | - uses: actions/checkout@v4
116 | - name: "Set up JDK"
117 | uses: actions/setup-java@v4
118 | with:
119 | java-version: '8'
120 | distribution: 'adopt'
121 | cache: maven
122 | server-id: central
123 | server-username: MAVEN_USERNAME
124 | server-password: MAVEN_PASSWORD
125 | gpg-private-key: ${{ secrets.MAVEN_GPG_PRIVATE_KEY }} # Value of the GPG private key to import
126 | gpg-passphrase: MAVEN_GPG_PASSPHRASE # env variable for GPG private key passphrase
127 |
128 | - name: "Central Deploy"
129 | run: mvn -B -Pcentral deploy --file pom.xml -DskipTests
130 | env:
131 | MAVEN_USERNAME: ${{ secrets.OSSRH_USER }}
132 | MAVEN_PASSWORD: ${{ secrets.OSSRH_PASS }}
133 | MAVEN_GPG_PASSPHRASE: ${{ secrets.MAVEN_GPG_PASSPHRASE }}
134 |
--------------------------------------------------------------------------------
/.github/workflows/maven.yml:
--------------------------------------------------------------------------------
1 | # This workflow will build a Java project with Maven, and cache/restore any dependencies to improve the workflow execution time
2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven
3 |
4 | name: Build & Tests
5 |
6 | on:
7 | # 支持手动触发构建
8 | workflow_dispatch:
9 | push:
10 |
11 | jobs:
12 | build:
13 |
14 | runs-on: ubuntu-latest
15 |
16 | steps:
17 | - uses: actions/checkout@v4
18 | - name: "Set up JDK"
19 | uses: actions/setup-java@v4
20 | with:
21 | cache: maven
22 | java-version: '8'
23 | distribution: 'adopt'
24 | server-id: github
25 | server-username: MAVEN_USERNAME
26 | server-password: MAVEN_TOKEN
27 | - name: "Package"
28 | run: mvn -B package --file pom.xml -Dmaven.javadoc.skip=true -Dgpg.skip
29 | env:
30 | MAVEN_USERNAME: ${{ github.repository_owner }}
31 | MAVEN_TOKEN: ${{secrets.GITHUB_TOKEN}}
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /.idea/
2 | /target/
3 | /*/target/
4 | **.iml
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Carm
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ```text
2 | ______ ____ __ _
3 | / ____/___ ________ __ / __ \/ /_ ______ _(_)___
4 | / __/ / __ `/ ___/ / / / / /_/ / / / / / __ `/ / __ \
5 | / /___/ /_/ (__ ) /_/ / / ____/ / /_/ / /_/ / / / / /
6 | /_____/\__,_/____/\__, / /_/ /_/\__,_/\__, /_/_/ /_/
7 | /____/ /____/
8 | ```
9 |
10 | # EasyPlugin
11 |
12 | [](https://github.com/CarmJos/EasyPlugin/releases)
13 | [](https://opensource.org/licenses/MIT)
14 | [](https://github.com/CarmJos/EasyPlugin/actions/workflows/maven.yml)
15 | [](https://www.codefactor.io/repository/github/carmjos/EasyPlugin)
16 | 
17 | 
18 |
19 | 轻松(做)插件,旨在于方便快捷的使用Bukkit实现MC中的一些功能。
20 |
21 | ## 优势
22 |
23 | - 轻便独立的功能模块,按需使用,避免大量打包!
24 | - 详细的Javadoc与使用文档,轻松上手,方便使用!
25 | - 持续的更新与优化,需求不止,更新不止!
26 | - 如需新功能支持,请通过 [Issues](https://github.com/CarmJos/EasyPlugin/issues) 提交功能需求。
27 |
28 | ## 内容
29 |
30 | 项目初创不久,加 * 的仍在开发更新中...欢迎各路大佬帮助提供本项目的开发文档~
31 |
32 | ### 集合部分 (`/collection`)
33 |
34 | - All [`easyplugin-all`](collection/all)
35 | - Common [`easyplugin-common`](collection/common)
36 |
37 | ### 主要部分 (`/base`)
38 |
39 | - Color [`easyplugin-color`](base/color)
40 | - 颜色工具类模块,提供功能全面的MC颜色解析转换工具。
41 | - 支持 `&+颜色代码`(原版颜色)、`§(#XXXXXX)`(RGB颜色) 与 `&<#XXXXXX>`(前后标注RGB颜色渐变)。
42 | - Utils [`easyplugin-utils`](base/utils)
43 | - 通用工具类模块,该模块中的内容支持在Bungee、Bukkit使用。
44 | - 本模块提供
45 | - `ColorParser` 支持RGB颜色与RGB渐变色的颜色解析器。
46 | - `EasyCooldown` 快速创造一个冷却时间的管理器。
47 | - `JarResourceUtils` 快速读取Jar包内容的工具类。
48 | - Main [`easyplugin-main`](base/main)
49 | - 主要接口模块,提供了方便的插件入口类与相关工具类。
50 | - Command [`easyplugin-command`](base/command)
51 | - 指令接口模块,便于快速进行子指令的实现,并提供单独的TabComplete方法。
52 | - 随本项目提供了 `SimpleCompleter` 类,用于快速创建补全的内容列表。
53 | - GUI [`easyplugin-gui`](base/main)
54 | - 简单便捷的箱子GUI接口,可以快速实现GUI中不同图标的点击功能。
55 | - 随本项目提供了 `AutoPagedGUI` 等翻页GUI抽象类。
56 | - Storage [`easyplugin-storage`](base/storage)
57 | - 抽象存储管理器,便于实现不同的存储类型。
58 | - 随本项目提供了 `FileBasedStorage`、`FolderBasedStorage` 等常用存储抽象方法。
59 | - Messages [`easyplugin-message`](base/messages)
60 | - 随本项目提供了基于 MineConfiguration 实现的 `EasyMessages` 类。
61 | - 支持多种消息配置,包括文本消息、ActionBar消息、Title消息、声音、粒子效果播放等。
62 | - 支持消息间的延迟发送。
63 |
64 | ### 独立项目部分
65 |
66 | > 以下项目均已独立出单独项目,如需使用,**强烈建议自行引用对应的项目**,以支持完整的Javadoc并获取源码内容!
67 |
68 | - _Listener_ -> [**EasyListener**](https://github.com/CarmJos/EasyListener)
69 | - _Configuration_ -> [**MineConfiguration**](https://github.com/CarmJos/MineConfiguration)
70 | - _Database_ -> [**EasySQL**](https://github.com/CarmJos/EasySQL)
71 |
72 | ### 附属部分 (`/extension`)
73 |
74 | - [PlaceholderAPI](https://www.spigotmc.org/resources/6245/) [`easyplugin-placeholderapi`](extension/papi)
75 | - PlaceholderAPI 扩展模块,提供了方便的 PlaceholderAPI 变量注册方法。
76 | - [Vault](https://github.com/MilkBowl/VaultAPI)* [`easyplugin-vault`](extension/vault)
77 |
78 | ## 开发
79 |
80 | 详细开发介绍请 [点击这里](.documentation/README.md) , JavaDoc(最新Release)
81 | 请 [点击这里](https://carmjos.github.io/EasyPlugin) 。
82 |
83 | ### 示例代码
84 |
85 | 您可以 [点击这里](https://github.com/CarmJos/UltraDepository)
86 | 查看实例项目演示,更多演示详见 [开发介绍](.documentation/README.md) 。
87 |
88 | ### 依赖方式
89 |
90 |
91 | 展开查看 Maven 依赖方式
92 |
93 | ```xml
94 |
95 |
96 |
97 |
98 |
99 |
100 | EasyPlugin
101 | GitHub Packages
102 | https://raw.githubusercontent.com/CarmJos/EasyPlugin/repo/
103 |
104 |
105 |
106 |
107 | carm-repo
108 | Carm's Repo
109 | https://repo.carm.cc/repository/maven-public/
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 | cc.carm.lib
118 | easyplugin-all
119 | [LATEST RELEASE]
120 | compile
121 |
122 |
123 |
124 |
125 | cc.carm.lib
126 | easyplugin-common
127 | [LATEST RELEASE]
128 | compile
129 |
130 |
131 |
132 |
133 | cc.carm.lib
134 | easyplugin-main
135 | [LATEST RELEASE]
136 | compile
137 |
138 |
139 |
140 |
141 |
142 |
143 | ```
144 |
145 |
146 |
147 |
148 | 展开查看 Gradle 依赖方式
149 |
150 | ```groovy
151 | repositories {
152 | // 采用github依赖库,安全稳定,但需要配置 (推荐)
153 | maven { url 'https://raw.githubusercontent.com/CarmJos/EasyPlugin/repo/' }
154 |
155 | // 采用我的私人依赖库,简单方便,但可能因为变故而无法使用
156 | maven { url 'https://repo.carm.cc/repository/maven-public/' }
157 | }
158 |
159 | dependencies {
160 |
161 | //大全集版本,包含项目内所有模块
162 | api "cc.carm.lib:easyplugin-all:[LATEST RELEASE]"
163 |
164 | //常用接口集,包含除附属插件模块外的所有模块
165 | api "cc.carm.lib:easyplugin-common:[LATEST RELEASE]"
166 |
167 | //插件主要接口模块,包含方便的插件入口类与相关工具类
168 | api "cc.carm.lib:easyplugin-main:[LATEST RELEASE]"
169 |
170 | // 其他模块自行选择,详见 “内容”
171 |
172 | }
173 | ```
174 |
175 |
176 |
177 | ## 支持与捐赠
178 |
179 | 若您觉得本插件做的不错,您可以通过捐赠支持我!
180 |
181 | 感谢您对开源项目的支持!
182 |
183 |
184 |
185 | ## 开源协议
186 |
187 | 本项目源码采用 [The MIT License](https://opensource.org/licenses/MIT) 开源协议。
188 |
189 | 关于 MIT 协议
190 |
191 | > MIT 协议可能是几大开源协议中最宽松的一个,核心条款是:
192 | >
193 | > 该软件及其相关文档对所有人免费,可以任意处置,包括使用,复制,修改,合并,发表,分发,再授权,或者销售。唯一的限制是,软件中必须包含上述版
194 | > 权和许可提示。
195 | >
196 | > 这意味着:
197 | > - 你可以自由使用,复制,修改,可以用于自己的项目。
198 | > - 可以免费分发或用来盈利。
199 | > - 唯一的限制是必须包含许可声明。
200 | >
201 | > MIT 协议是所有开源许可中最宽松的一个,除了必须包含许可声明外,再无任何限制。
202 | >
203 | > *以上文字来自 [五种开源协议GPL,LGPL,BSD,MIT,Apache](https://www.oschina.net/question/54100_9455) 。*
204 |
--------------------------------------------------------------------------------
/base/color/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | cc.carm.lib
8 | easyplugin-parent
9 | 1.5.13
10 | ../../pom.xml
11 |
12 |
13 | ${project.jdk.version}
14 | ${project.jdk.version}
15 | UTF-8
16 | UTF-8
17 |
18 | easyplugin-color
19 |
20 | EasyPlugin-Color
21 | 轻松插件颜色模块,支持简单便捷的颜色解析器,包括基本颜色、RGB颜色与RGB渐变颜色。
22 | https://github.com/CarmJos/EasyPlugin
23 |
24 |
25 |
26 | CarmJos
27 | Carm Jos
28 | carm@carm.cc
29 | https://www.carm.cc
30 |
31 |
32 |
33 |
34 |
35 | The MIT License
36 | https://opensource.org/licenses/MIT
37 |
38 |
39 |
40 |
41 | GitHub Issues
42 | https://github.com/CarmJos/EasyPlugin/issues
43 |
44 |
45 |
46 | GitHub Actions
47 | https://github.com/CarmJos/EasyPlugin/actions/workflows/maven.yml
48 |
49 |
50 |
51 |
52 |
53 | org.apache.maven.plugins
54 | maven-javadoc-plugin
55 |
56 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/base/color/src/main/java/cc/carm/lib/easyplugin/utils/ColorParser.java:
--------------------------------------------------------------------------------
1 | package cc.carm.lib.easyplugin.utils;
2 |
3 | import org.jetbrains.annotations.NotNull;
4 | import org.jetbrains.annotations.Nullable;
5 |
6 | import java.awt.*;
7 | import java.util.List;
8 | import java.util.*;
9 | import java.util.regex.Matcher;
10 | import java.util.regex.Pattern;
11 | import java.util.stream.Collectors;
12 | import java.util.stream.IntStream;
13 |
14 | /**
15 | * 颜色解析器。
16 | *
普通颜色 格式 {@code &+颜色代码 },如 {@literal &c} 、{@literal &a}
17 | *
RGB颜色(版本需要≥1.14) 格式 {@code &(#XXXXXX) },如 {@literal &(#aaaaaa)}
18 | *
渐变RBG颜色(版本需要≥1.14) 格式 {@code &<#XXXXXX>FOOBAR&<#XXXXXX> }
19 | *
注意:当使用渐变RGB颜色时,普通颜色代码与RGB颜色代码将失效。
20 | */
21 | public class ColorParser {
22 |
23 | public static final Pattern HEX_PATTERN = Pattern.compile("&\\(&?#([\\da-fA-F]{6})\\)");
24 | public static final Pattern GRADIENT_PATTERN = Pattern.compile("&<&?#([\\da-fA-F]{6})>");
25 | public static final Pattern COLOR_PATTERN = Pattern.compile("([&§][0-9a-fA-FrRxX])+"); // 会影响颜色的代码
26 | public static final Pattern FORMAT_PATTERN = Pattern.compile("([&§][0-9a-fA-Fk-oK-OrRxX])+"); // MC可用的格式化代码
27 |
28 | /**
29 | * 清除一条消息中的全部颜色代码 (包括RGB颜色代码与渐变颜色代码)
30 | *
31 | * @param text 源消息内容
32 | * @return 清理颜色后的消息
33 | */
34 | public static @NotNull String clear(@NotNull String text) {
35 | text = HEX_PATTERN.matcher(text).replaceAll("");
36 | text = GRADIENT_PATTERN.matcher(text).replaceAll("");
37 | text = FORMAT_PATTERN.matcher(text).replaceAll("");
38 | return text;
39 | }
40 |
41 | /**
42 | * 对一条消息进行颜色解析,包括普通颜色代码、RGB颜色代码与RBG渐变代码。
43 | *
44 | * @param text 源消息内容
45 | * @return 解析后的消息
46 | */
47 | public static @NotNull String parse(@NotNull String text) {
48 | return parseBaseColor(parseGradientColor(parseHexColor(text)));
49 | }
50 |
51 | /**
52 | * 对多条消息进行颜色解析,包括普通颜色代码、RGB颜色代码与RBG渐变代码。
53 | *
54 | * @param texts 源消息内容
55 | * @return 解析后的消息
56 | */
57 | public static @NotNull String[] parse(@NotNull String... texts) {
58 | return parse(Arrays.asList(texts)).toArray(new String[0]);
59 | }
60 |
61 | /**
62 | * 对多条消息进行颜色解析,包括普通颜色代码、RGB颜色代码与RBG渐变代码。
63 | *
64 | * @param texts 源消息内容
65 | * @return 解析后的消息
66 | */
67 | public static @NotNull List parse(@NotNull Collection texts) {
68 | return texts.stream().map(ColorParser::parse).collect(Collectors.toList());
69 | }
70 |
71 | /**
72 | * 解析消息中的基本颜色代码格式 {@code &+颜色代码 },如 {@literal &c} 、{@literal &a}
73 | *
74 | * @param text 消息内容
75 | * @return RGB处理后的消息
76 | * @see net.md_5.bungee.api.ChatColor
77 | */
78 | public static String parseBaseColor(final String text) {
79 | return text.replaceAll("&", "§").replace("§§", "&");
80 | }
81 |
82 | /**
83 | * 解析消息中的RGB颜色代码(版本需要≥1.14) 格式 {@code &(#XXXXXX) },如 {@literal &(#aaaaaa)}
84 | *
85 | * @param text 消息内容
86 | * @return RGB处理后的消息
87 | */
88 | public static String parseHexColor(String text) {
89 | Matcher matcher = HEX_PATTERN.matcher(text);
90 | while (matcher.find()) {
91 | text = matcher.replaceFirst(buildHexColor(matcher.group(1)).toLowerCase());
92 | matcher.reset(text);
93 | }
94 | return text;
95 | }
96 |
97 | /**
98 | * 对一条消息进行RGB渐变处理(版本需要≥1.14),格式 {@code &<#XXXXXX>FOOBAR&<#XXXXXX> }。
99 | *
100 | * @param text 消息内容
101 | * @return RGB渐变处理后的消息
102 | */
103 | public static @NotNull String parseGradientColor(@NotNull String text) {
104 | List colors = new ArrayList<>();
105 |
106 | Matcher matcher = ColorParser.GRADIENT_PATTERN.matcher(text);
107 | while (matcher.find()) colors.add(matcher.group(1));
108 |
109 | if (colors.isEmpty()) return text; // 无渐变颜色,直接跳出
110 |
111 | String[] parts = ColorParser.GRADIENT_PATTERN.split(text);
112 | StringBuilder builder = new StringBuilder();
113 | for (int i = 0; i < parts.length; i++) {
114 | String startHex = i - 1 >= 0 && colors.size() > i - 1 ? colors.get(i - 1) : null; // 本条消息的起始颜色
115 | String endHex = colors.size() > i ? colors.get(i) : null; // 本条消息的结束颜色
116 | builder.append(gradientText(parts[i], startHex, endHex));
117 | }
118 |
119 | return builder.toString();
120 | }
121 |
122 | public static @NotNull String gradientText(@NotNull String text,
123 | @Nullable Color startColor, @Nullable Color endColor) {
124 | Objects.requireNonNull(text, "Text to be gradient should not be null!");
125 | if (startColor == null || endColor == null || text.isEmpty()) {
126 | // 起始颜色有任一为空,则不进行渐变上色。
127 | // 若有起始颜色,则代表其跟在某个渐变之后,应当添加"&r"阻断前面的渐变。
128 | return (startColor != null ? "&r" : "") + text;
129 | }
130 |
131 | // 用于记录消息中特殊格式的位置
132 | // 在渐变中,允许使用格式字符与颜色字符来改变其中某个字的颜色/格式,以支持更多形式内容。
133 | LinkedHashMap extraFormats = new LinkedHashMap<>();
134 | Matcher matcher = ColorParser.FORMAT_PATTERN.matcher(text);
135 | while (matcher.find()) {
136 | extraFormats.put(matcher.start(), matcher.group());
137 | text = matcher.replaceFirst("");
138 | matcher.reset(text);
139 | }
140 |
141 | if (text.length() == 1) {
142 | // 当只有一个实际字符时,无需进行渐变计算,直接返回 中间颜色+起始格式(如果有)+消息 即可。
143 | return colorText(text, extraFormats.get(0), buildHexColor(mediumHex(startColor, endColor)));
144 | }
145 |
146 | String[] characters = text.split("");
147 | int step = characters.length; // 变换次数
148 |
149 | // 决定每种颜色变换的方向
150 | int rDirection = startColor.getRed() < endColor.getRed() ? 1 : -1;
151 | int gDirection = startColor.getGreen() < endColor.getGreen() ? 1 : -1;
152 | int bDirection = startColor.getBlue() < endColor.getBlue() ? 1 : -1;
153 |
154 | // 决定每种颜色每次变换的度
155 | int rStep = Math.abs(startColor.getRed() - endColor.getRed()) / (step - 1);
156 | int gStep = Math.abs(startColor.getGreen() - endColor.getGreen()) / (step - 1);
157 | int bStep = Math.abs(startColor.getBlue() - endColor.getBlue()) / (step - 1);
158 |
159 | String[] hexes = IntStream.range(0, step).mapToObj(i -> colorToHex(
160 | startColor.getRed() + rStep * i * rDirection,
161 | startColor.getGreen() + gStep * i * gDirection,
162 | startColor.getBlue() + bStep * i * bDirection
163 | )).toArray(String[]::new);
164 |
165 | StringBuilder sb = new StringBuilder();
166 | String extra = null;
167 | for (int i = 0; i < characters.length; i++) {
168 | extra = buildExtraFormat(extra, extraFormats.get(i));
169 | String s = colorText(characters[i], extra, buildHexColor(hexes[i]));
170 | sb.append(s);
171 | }
172 | return sb.toString();
173 | }
174 |
175 | protected static String gradientText(@NotNull String text, @Nullable String startHex, @Nullable String endHex) {
176 | return gradientText(text,
177 | startHex == null ? null : Color.decode("0x" + startHex),
178 | endHex == null ? null : Color.decode("0x" + endHex)
179 | );
180 | }
181 |
182 | private static String mediumHex(@NotNull Color start, @NotNull Color end) {
183 | return colorToHex(
184 | Math.abs(start.getRed() - end.getRed()) / 2,
185 | Math.abs(start.getGreen() - end.getGreen()) / 2,
186 | Math.abs(start.getBlue() - end.getBlue()) / 2
187 | );
188 | }
189 |
190 | private static String colorText(String message, @Nullable String format, @Nullable String color) {
191 | if (format != null && COLOR_PATTERN.matcher(format).find()) {
192 | // format中存在影响颜色的内容,则当前消息的颜色会被覆盖。
193 | // 为了减少最终消息的长度,故直接返回键入的FORMAT和对应消息的内容。
194 | return format + message;
195 | }
196 | return (color == null ? "" : color) + (format == null ? "" : parseBaseColor(format)) + message;
197 | }
198 |
199 | protected static String colorToHex(Color color) {
200 | return colorToHex(color.getRed(), color.getGreen(), color.getBlue());
201 | }
202 |
203 | protected static String colorToHex(int r, int g, int b) {
204 | // 将R、G、B转换为16进制(若非2位则补0)输出
205 | return String.format("%02X%02X%02X", r, g, b);
206 | }
207 |
208 | protected static String buildHexColor(String hexCode) {
209 | return Arrays.stream(hexCode.split("")).map(s -> '§' + s)
210 | .collect(Collectors.joining("", '§' + "x", ""));
211 | }
212 |
213 | protected static String buildExtraFormat(String current, String extra) {
214 | if (extra != null) current = (current == null ? "" : current) + extra;
215 | return isResetCode(current) ? null : current;
216 | }
217 |
218 | protected static boolean isResetCode(String input) {
219 | return input != null && (input.toLowerCase().endsWith("&r") || input.toLowerCase().endsWith("§r"));
220 | }
221 |
222 | }
--------------------------------------------------------------------------------
/base/color/src/test/java/ColorParseTest.java:
--------------------------------------------------------------------------------
1 | import cc.carm.lib.easyplugin.utils.ColorParser;
2 | import org.junit.Test;
3 |
4 | import java.util.LinkedHashMap;
5 | import java.util.regex.Matcher;
6 |
7 | import static cc.carm.lib.easyplugin.utils.ColorParser.*;
8 |
9 | public class ColorParseTest {
10 |
11 |
12 | @Test
13 | public void test() {
14 | System.out.println(" ");
15 | System.out.println(parseGradientColor("&<#AAAAAA>我真的&<#BBBBBB>爱死&<#111111>你&<#FFFFFF>!"));
16 |
17 | // 测试穿插
18 | System.out.println(parse("&<#AAAAAA>&l&m我真的&o尊的&r真的&<#BBBBBB>&o爱死&<#111111>你&<#FFFFFF>了&r!"));
19 | System.out.println(parse("&<#AAAAAA>&l我&r真&(#666666)的&<#BBBBBB>&o爱死&<#111111>你&<#FFFFFF>了&r!"));
20 | System.out.println(parse("&r正常的颜色理应&c&l不受影响&r。"));
21 |
22 | System.out.println(clear("&f&l测试&<#AAAAAA>清理颜色代码&<#111111> &&这样应该&(#666666)不被影响 &f。"));
23 | }
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/base/command-alias/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | cc.carm.lib
8 | easyplugin-parent
9 | 1.5.13
10 | ../../pom.xml
11 |
12 |
13 | ${project.jdk.version}
14 | ${project.jdk.version}
15 | UTF-8
16 | UTF-8
17 |
18 | easyplugin-command-alias
19 | jar
20 |
21 | EasyPlugin-Command-Alias
22 | 轻松插件指令别名映射模块,支持将插件内复杂的子指令简化为一个单独的指令,方便玩家使用。
23 | https://github.com/CarmJos/EasyPlugin
24 |
25 |
26 |
27 | CarmJos
28 | Carm Jos
29 | carm@carm.cc
30 | https://www.carm.cc
31 |
32 |
33 |
34 |
35 |
36 | The MIT License
37 | https://opensource.org/licenses/MIT
38 |
39 |
40 |
41 |
42 | GitHub Issues
43 | https://github.com/CarmJos/EasyPlugin/issues
44 |
45 |
46 |
47 | GitHub Actions
48 | https://github.com/CarmJos/EasyPlugin/actions/workflows/maven.yml
49 |
50 |
51 |
52 |
53 |
54 |
55 | org.apache.maven.plugins
56 | maven-javadoc-plugin
57 |
58 |
59 |
60 |
61 |
--------------------------------------------------------------------------------
/base/command-alias/src/main/java/cc/carm/lib/easyplugin/command/alias/AliasCommand.java:
--------------------------------------------------------------------------------
1 | package cc.carm.lib.easyplugin.command.alias;
2 |
3 | import org.bukkit.Location;
4 | import org.bukkit.command.Command;
5 | import org.bukkit.command.CommandSender;
6 | import org.bukkit.command.SimpleCommandMap;
7 | import org.jetbrains.annotations.NotNull;
8 | import org.jetbrains.annotations.Nullable;
9 |
10 | import java.util.Collections;
11 | import java.util.List;
12 | import java.util.Optional;
13 |
14 | public class AliasCommand extends Command {
15 |
16 | protected final AliasCommandManager aliasCommandManager;
17 | protected final String targetCommand;
18 |
19 | public AliasCommand(String name, AliasCommandManager aliasCommandManager, String targetCommand) {
20 | super(name);
21 | this.aliasCommandManager = aliasCommandManager;
22 | this.targetCommand = targetCommand;
23 | }
24 |
25 | protected SimpleCommandMap getCommandMap() {
26 | return this.aliasCommandManager.getCommandMap();
27 | }
28 |
29 | protected String buildCommand(String[] args) {
30 | return this.targetCommand + " " + String.join(" ", args);
31 | }
32 |
33 | @Override
34 | public boolean execute(@NotNull CommandSender sender, @NotNull String commandLabel, @NotNull String[] args) {
35 | return getCommandMap().dispatch(sender, buildCommand(args));
36 | }
37 |
38 | @NotNull
39 | @Override
40 | public List tabComplete(@NotNull CommandSender sender, @NotNull String alias,
41 | @NotNull String[] args, @Nullable Location location) throws IllegalArgumentException {
42 | return Optional.ofNullable(getCommandMap().tabComplete(sender, buildCommand(args))).orElse(Collections.emptyList());
43 | }
44 |
45 | @NotNull
46 | @Override
47 | public List tabComplete(@NotNull CommandSender sender, @NotNull String alias, @NotNull String[] args) throws IllegalArgumentException {
48 | return tabComplete(sender, alias, args, null);
49 | }
50 |
51 | }
--------------------------------------------------------------------------------
/base/command-alias/src/main/java/cc/carm/lib/easyplugin/command/alias/AliasCommandManager.java:
--------------------------------------------------------------------------------
1 | package cc.carm.lib.easyplugin.command.alias;
2 |
3 | import org.bukkit.Bukkit;
4 | import org.bukkit.command.Command;
5 | import org.bukkit.command.SimpleCommandMap;
6 | import org.bukkit.plugin.SimplePluginManager;
7 | import org.bukkit.plugin.java.JavaPlugin;
8 | import org.jetbrains.annotations.NotNull;
9 |
10 | import java.lang.reflect.Field;
11 | import java.util.HashMap;
12 | import java.util.Map;
13 |
14 | /**
15 | * 指令简化别名(简化映射)管理器
16 | *
支持将插件内复杂的子指令简化为一个单独的指令,方便玩家使用。
17 | */
18 | public class AliasCommandManager {
19 |
20 | protected final @NotNull JavaPlugin plugin;
21 | protected final @NotNull String prefix;
22 |
23 | protected final @NotNull SimpleCommandMap commandMap;
24 | protected final @NotNull Field knownCommandsFiled;
25 |
26 | protected final @NotNull Map registeredCommands = new HashMap<>();
27 |
28 | public AliasCommandManager(@NotNull JavaPlugin plugin) throws Exception {
29 | this(plugin, plugin.getName());
30 | }
31 |
32 | public AliasCommandManager(@NotNull JavaPlugin plugin, @NotNull String prefix) throws Exception {
33 | this.plugin = plugin;
34 | this.prefix = prefix.trim();
35 |
36 | SimplePluginManager manager = (SimplePluginManager) Bukkit.getPluginManager();
37 | Field commandMapField = SimplePluginManager.class.getDeclaredField("commandMap");
38 | commandMapField.setAccessible(true);
39 | this.commandMap = (SimpleCommandMap) commandMapField.get(manager);
40 |
41 | this.knownCommandsFiled = SimpleCommandMap.class.getDeclaredField("knownCommands");
42 | this.knownCommandsFiled.setAccessible(true);
43 | }
44 |
45 | @SuppressWarnings("unchecked")
46 | protected Map getKnownCommands() {
47 | try {
48 | return (Map) knownCommandsFiled.get(commandMap);
49 | } catch (IllegalAccessException e) {
50 | e.printStackTrace();
51 | }
52 | return new HashMap<>();
53 | }
54 |
55 | protected @NotNull SimpleCommandMap getCommandMap() {
56 | return commandMap;
57 | }
58 |
59 | public String getCommandPrefix() {
60 | return this.prefix;
61 | }
62 |
63 | public void register(@NotNull String alias, @NotNull String subCommand) {
64 | AliasCommand current = this.registeredCommands.get(alias);
65 | if (current != null) current.unregister(getCommandMap());
66 |
67 | AliasCommand cmd = new AliasCommand(alias, this, getCommandPrefix() + " " + subCommand);
68 | this.registeredCommands.put(alias, cmd);
69 | getCommandMap().register(plugin.getName(), cmd);
70 | }
71 |
72 | public void unregister(@NotNull String alias) {
73 | AliasCommand current = this.registeredCommands.remove(alias);
74 | if (current != null) {
75 | getKnownCommands().remove(alias);
76 | current.unregister(getCommandMap());
77 | }
78 | }
79 |
80 | public void unregisterAll() {
81 | registeredCommands.forEach((k, v) -> {
82 | getKnownCommands().remove(k);
83 | v.unregister(getCommandMap());
84 | });
85 | registeredCommands.clear();
86 | }
87 |
88 | }
89 |
--------------------------------------------------------------------------------
/base/command/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | easyplugin-parent
7 | cc.carm.lib
8 | 1.5.13
9 | ../../pom.xml
10 |
11 | 4.0.0
12 |
13 | ${project.jdk.version}
14 | ${project.jdk.version}
15 | UTF-8
16 | UTF-8
17 |
18 | easyplugin-command
19 | jar
20 |
21 | EasyPlugin-Command
22 | 轻松插件指令接口模块,方便快捷的编写子指令。
23 | https://github.com/CarmJos/EasyPlugin
24 |
25 |
26 |
27 | CarmJos
28 | Carm Jos
29 | carm@carm.cc
30 | https://www.carm.cc
31 |
32 |
33 |
34 |
35 |
36 | The MIT License
37 | https://opensource.org/licenses/MIT
38 |
39 |
40 |
41 |
42 | GitHub Issues
43 | https://github.com/CarmJos/EasyPlugin/issues
44 |
45 |
46 |
47 | GitHub Actions
48 | https://github.com/CarmJos/EasyPlugin/actions/workflows/maven.yml
49 |
50 |
51 |
52 |
53 |
54 |
55 | org.apache.maven.plugins
56 | maven-javadoc-plugin
57 |
58 |
59 |
60 |
61 |
--------------------------------------------------------------------------------
/base/command/src/main/java/cc/carm/lib/easyplugin/command/CommandHandler.java:
--------------------------------------------------------------------------------
1 | package cc.carm.lib.easyplugin.command;
2 |
3 | import org.bukkit.command.Command;
4 | import org.bukkit.command.CommandSender;
5 | import org.bukkit.command.TabExecutor;
6 | import org.bukkit.plugin.java.JavaPlugin;
7 | import org.bukkit.util.StringUtil;
8 | import org.jetbrains.annotations.NotNull;
9 | import org.jetbrains.annotations.Nullable;
10 |
11 | import java.util.*;
12 | import java.util.stream.Collectors;
13 |
14 | @SuppressWarnings("UnusedReturnValue")
15 | public abstract class CommandHandler implements TabExecutor, NamedExecutor {
16 |
17 | protected final @NotNull JavaPlugin plugin;
18 | protected final @NotNull String cmd;
19 | protected final @NotNull List aliases;
20 |
21 | protected final @NotNull Map> registeredCommands = new HashMap<>();
22 | protected final @NotNull Map registeredHandlers = new HashMap<>();
23 |
24 | protected final @NotNull Map aliasesMap = new HashMap<>();
25 |
26 | public CommandHandler(@NotNull JavaPlugin plugin) {
27 | this(plugin, plugin.getName());
28 | }
29 |
30 | public CommandHandler(@NotNull JavaPlugin plugin, @NotNull String cmd) {
31 | this(plugin, cmd, new String[0]);
32 | }
33 |
34 | public CommandHandler(@NotNull JavaPlugin plugin, @NotNull String cmd, @NotNull String... aliases) {
35 | this.plugin = plugin;
36 | this.cmd = cmd;
37 | this.aliases = Arrays.asList(aliases);
38 | }
39 |
40 | public abstract Void noArgs(CommandSender sender);
41 |
42 | public Void unknownCommand(CommandSender sender, String[] args) {
43 | return noArgs(sender);
44 | }
45 |
46 | public abstract Void noPermission(CommandSender sender);
47 |
48 | public Void onException(CommandSender sender, SubCommand> cmd, Exception ex) {
49 | sender.sendMessage("Error occurred when executing " + cmd.getIdentifier() + ": " + ex.getLocalizedMessage());
50 | ex.printStackTrace();
51 | return null;
52 | }
53 |
54 | @Override
55 | public @NotNull List getAliases() {
56 | return aliases;
57 | }
58 |
59 | @Override
60 | public @NotNull String getIdentifier() {
61 | return this.cmd;
62 | }
63 |
64 | public void registerSubCommand(SubCommand> command) {
65 | String name = command.getIdentifier().toLowerCase();
66 | this.registeredCommands.put(name, command);
67 | command.getAliases().forEach(alias -> this.aliasesMap.put(alias.toLowerCase(), name));
68 | }
69 |
70 | public void registerHandler(CommandHandler handler) {
71 | String name = handler.getIdentifier().toLowerCase();
72 | this.registeredHandlers.put(name, handler);
73 | handler.getAliases().forEach(alias -> this.aliasesMap.put(alias.toLowerCase(), name));
74 | }
75 |
76 | @Override
77 | public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) {
78 | if (!this.hasPermission(sender)) {
79 | noPermission(sender);
80 | return true;
81 | }
82 |
83 | if (args.length == 0) {
84 | this.noArgs(sender);
85 | return true;
86 | }
87 |
88 | String input = args[0].toLowerCase();
89 |
90 | CommandHandler handler = getHandler(input);
91 | if (handler != null) {
92 | if (!handler.hasPermission(sender)) {
93 | this.noPermission(sender);
94 | } else {
95 | handler.onCommand(sender, command, label, this.shortenArgs(args));
96 | }
97 | return true;
98 | }
99 |
100 | SubCommand> sub = getSubCommand(input);
101 | if (sub == null) {
102 | this.unknownCommand(sender, args);
103 | } else if (!sub.hasPermission(sender)) {
104 | this.noPermission(sender);
105 | } else {
106 | try {
107 | sub.execute(this.plugin, sender, this.shortenArgs(args));
108 | } catch (Exception ex) {
109 | this.onException(sender, sub, ex);
110 | }
111 | }
112 |
113 | return true;
114 | }
115 |
116 | @Override
117 | public List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, String[] args) {
118 | if (args.length == 0) return Collections.emptyList();
119 |
120 | String input = args[0].toLowerCase();
121 | if (args.length == 1) {
122 | return getExecutors().stream()
123 | .filter(e -> e.hasPermission(sender))
124 | .map(NamedExecutor::getIdentifier)
125 | .filter(s -> StringUtil.startsWithIgnoreCase(s, input))
126 | .collect(Collectors.toList());
127 | } else {
128 |
129 | CommandHandler handler = getHandler(input);
130 | if (handler != null && handler.hasPermission(sender)) {
131 | return handler.onTabComplete(sender, command, alias, this.shortenArgs(args));
132 | }
133 |
134 | SubCommand> sub = getSubCommand(input);
135 | if (sub != null && sub.hasPermission(sender)) {
136 | return sub.tabComplete(this.plugin, sender, this.shortenArgs(args));
137 | }
138 |
139 | return Collections.emptyList();
140 | }
141 | }
142 |
143 | public List getExecutors() {
144 | Set executors = new HashSet<>();
145 | executors.addAll(this.registeredHandlers.values());
146 | executors.addAll(this.registeredCommands.values());
147 | List sortedExecutors = new ArrayList<>(executors);
148 | sortedExecutors.sort(Comparator.comparing(NamedExecutor::getIdentifier));
149 | return sortedExecutors;
150 | }
151 |
152 | protected @Nullable CommandHandler getHandler(@NotNull String name) {
153 | CommandHandler fromName = this.registeredHandlers.get(name);
154 | if (fromName != null) return fromName;
155 |
156 | String nameFromAlias = this.aliasesMap.get(name);
157 | if (nameFromAlias == null) return null;
158 | else return this.registeredHandlers.get(nameFromAlias);
159 | }
160 |
161 | protected @Nullable SubCommand> getSubCommand(@NotNull String name) {
162 | SubCommand> fromName = this.registeredCommands.get(name);
163 | if (fromName != null) return fromName;
164 |
165 | String nameFromAlias = this.aliasesMap.get(name);
166 | if (nameFromAlias == null) return null;
167 | else return this.registeredCommands.get(nameFromAlias);
168 | }
169 |
170 | protected String[] shortenArgs(String[] args) {
171 | if (args.length == 0) {
172 | return args;
173 | } else {
174 | List argList = new ArrayList<>(Arrays.asList(args).subList(1, args.length));
175 | return argList.toArray(new String[0]);
176 | }
177 | }
178 |
179 | }
180 |
--------------------------------------------------------------------------------
/base/command/src/main/java/cc/carm/lib/easyplugin/command/NamedExecutor.java:
--------------------------------------------------------------------------------
1 | package cc.carm.lib.easyplugin.command;
2 |
3 | import org.bukkit.command.CommandSender;
4 | import org.jetbrains.annotations.NotNull;
5 | import org.jetbrains.annotations.Nullable;
6 |
7 | import java.util.Arrays;
8 | import java.util.List;
9 | import java.util.Optional;
10 | import java.util.function.Function;
11 |
12 | public interface NamedExecutor {
13 |
14 | @NotNull String getIdentifier();
15 |
16 | @NotNull List getAliases();
17 |
18 | default boolean hasPermission(@NotNull CommandSender sender) {
19 | return true;
20 | }
21 |
22 | default Void sendMessage(@NotNull CommandSender sender, @NotNull String... messages) {
23 | return sendMessage(sender, Function.identity(), messages);
24 | }
25 |
26 | default Void sendMessage(@NotNull CommandSender sender,
27 | @Nullable Function parser,
28 | @NotNull String... messages) {
29 | if (messages == null || messages.length == 0) return null;
30 | Function finalParser = Optional.ofNullable(parser).orElse(Function.identity());
31 | Arrays.stream(messages).map(finalParser).forEach(sender::sendMessage);
32 | return null;
33 | }
34 |
35 | }
36 |
--------------------------------------------------------------------------------
/base/command/src/main/java/cc/carm/lib/easyplugin/command/SimpleCompleter.java:
--------------------------------------------------------------------------------
1 | package cc.carm.lib.easyplugin.command;
2 |
3 | import com.google.common.collect.ImmutableList;
4 | import org.bukkit.Bukkit;
5 | import org.bukkit.Material;
6 | import org.bukkit.OfflinePlayer;
7 | import org.bukkit.World;
8 | import org.bukkit.enchantments.Enchantment;
9 | import org.bukkit.entity.HumanEntity;
10 | import org.bukkit.potion.PotionEffectType;
11 | import org.bukkit.util.StringUtil;
12 | import org.jetbrains.annotations.NotNull;
13 |
14 | import java.util.Arrays;
15 | import java.util.Collection;
16 | import java.util.List;
17 | import java.util.Objects;
18 | import java.util.stream.Collectors;
19 | import java.util.stream.Stream;
20 |
21 | public class SimpleCompleter {
22 |
23 | public static @NotNull List none() {
24 | return ImmutableList.of();
25 | }
26 |
27 | public static @NotNull List objects(@NotNull String input, Collection> objects) {
28 | return objects(input, objects.size(), objects);
29 | }
30 |
31 | public static @NotNull List objects(@NotNull String input, int limit, Collection> objects) {
32 | return objects(input, limit, objects.stream());
33 | }
34 |
35 | public static @NotNull List objects(@NotNull String input, Stream> stream) {
36 | return objects(input, 20, stream);
37 | }
38 |
39 | public static @NotNull List objects(@NotNull String input, int limit, Stream> stream) {
40 | return stream.filter(Objects::nonNull).map(Object::toString)
41 | .filter(s -> StringUtil.startsWithIgnoreCase(s, input))
42 | .limit(Math.max(0, limit)).collect(Collectors.toList());
43 | }
44 |
45 | public static @NotNull List text(@NotNull String input, String... texts) {
46 | return text(input, texts.length, texts);
47 | }
48 |
49 | public static @NotNull List text(@NotNull String input, int limit, String... texts) {
50 | return text(input, limit, Arrays.asList(texts));
51 | }
52 |
53 | public static @NotNull List text(@NotNull String input, Collection texts) {
54 | return text(input, texts.size(), texts);
55 | }
56 |
57 | public static @NotNull List text(@NotNull String input, int limit, Collection texts) {
58 | return objects(input, limit, texts);
59 | }
60 |
61 | public static @NotNull List onlinePlayers(@NotNull String input) {
62 | return onlinePlayers(input, 10);
63 | }
64 |
65 | public static @NotNull List onlinePlayers(@NotNull String input, int limit) {
66 | return objects(input, limit, Bukkit.getOnlinePlayers().stream().map(HumanEntity::getName));
67 | }
68 |
69 | public static @NotNull List allPlayers(@NotNull String input) {
70 | return allPlayers(input, 10);
71 | }
72 |
73 | public static @NotNull List allPlayers(@NotNull String input, int limit) {
74 | return objects(input, limit, Arrays.stream(Bukkit.getOfflinePlayers()).map(OfflinePlayer::getName));
75 | }
76 |
77 | public static @NotNull List worlds(@NotNull String input) {
78 | return worlds(input, 10);
79 | }
80 |
81 | public static @NotNull List worlds(@NotNull String input, int limit) {
82 | return objects(input, limit, Bukkit.getWorlds().stream().map(World::getName));
83 | }
84 |
85 | public static @NotNull List materials(@NotNull String input) {
86 | return materials(input, 10);
87 | }
88 |
89 | public static @NotNull List materials(@NotNull String input, int limit) {
90 | return objects(input, limit, Arrays.stream(Material.values()).map(Enum::name));
91 | }
92 |
93 | public static @NotNull List effects(@NotNull String input) {
94 | return effects(input, 10);
95 | }
96 |
97 | public static @NotNull List effects(@NotNull String input, int limit) {
98 | return objects(input, limit, Arrays.stream(PotionEffectType.values()).map(PotionEffectType::getName));
99 | }
100 |
101 | public static @NotNull List enchantments(@NotNull String input) {
102 | return effects(input, 10);
103 | }
104 |
105 | @SuppressWarnings("deprecation")
106 | public static @NotNull List enchantments(@NotNull String input, int limit) {
107 | return objects(input, limit, Arrays.stream(Enchantment.values()).map(Enchantment::getName));
108 | }
109 |
110 | }
111 |
--------------------------------------------------------------------------------
/base/command/src/main/java/cc/carm/lib/easyplugin/command/SubCommand.java:
--------------------------------------------------------------------------------
1 | package cc.carm.lib.easyplugin.command;
2 |
3 | import org.bukkit.command.CommandSender;
4 | import org.bukkit.plugin.java.JavaPlugin;
5 | import org.jetbrains.annotations.NotNull;
6 | import org.jetbrains.annotations.Unmodifiable;
7 |
8 | import java.util.Arrays;
9 | import java.util.Collections;
10 | import java.util.List;
11 |
12 | @SuppressWarnings("UnusedReturnValue")
13 | public abstract class SubCommand implements NamedExecutor {
14 |
15 | private final @NotNull C parent;
16 |
17 | private final String identifier;
18 | private final List aliases;
19 |
20 | public SubCommand(@NotNull C parent, String identifier, String... aliases) {
21 | this.parent = parent;
22 | this.identifier = identifier;
23 | this.aliases = Arrays.asList(aliases);
24 | }
25 |
26 | public @NotNull C getParent() {
27 | return parent;
28 | }
29 |
30 | @Override
31 | public @NotNull String getIdentifier() {
32 | return this.identifier;
33 | }
34 |
35 | @Override
36 | @Unmodifiable
37 | public @NotNull List getAliases() {
38 | return this.aliases;
39 | }
40 |
41 | public abstract Void execute(JavaPlugin plugin, CommandSender sender, String[] args) throws Exception;
42 |
43 | public List tabComplete(JavaPlugin plugin, CommandSender sender, String[] args) {
44 | return Collections.emptyList();
45 | }
46 |
47 | }
48 |
--------------------------------------------------------------------------------
/base/gui/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | easyplugin-parent
7 | cc.carm.lib
8 | 1.5.13
9 | ../../pom.xml
10 |
11 | 4.0.0
12 |
13 |
14 | ${project.jdk.version}
15 | ${project.jdk.version}
16 | UTF-8
17 | UTF-8
18 |
19 |
20 | easyplugin-gui
21 | jar
22 |
23 | EasyPlugin-GUI
24 | 轻松插件GUI接口模块,方便快捷的创建箱子GUI界面。
25 | https://github.com/CarmJos/EasyPlugin
26 |
27 |
28 |
29 | CarmJos
30 | Carm Jos
31 | carm@carm.cc
32 | https://www.carm.cc
33 |
34 |
35 |
36 |
37 |
38 | The MIT License
39 | https://opensource.org/licenses/MIT
40 |
41 |
42 |
43 |
44 | GitHub Issues
45 | https://github.com/CarmJos/EasyPlugin/issues
46 |
47 |
48 |
49 | GitHub Actions
50 | https://github.com/CarmJos/EasyPlugin/actions/workflows/maven.yml
51 |
52 |
53 |
54 |
55 | ${project.parent.groupId}
56 | easyplugin-main
57 | ${project.parent.version}
58 | provided
59 |
60 |
61 |
62 |
63 |
64 |
65 | org.apache.maven.plugins
66 | maven-javadoc-plugin
67 |
68 |
69 |
70 |
71 |
--------------------------------------------------------------------------------
/base/gui/src/main/java/cc/carm/lib/easyplugin/gui/GUI.java:
--------------------------------------------------------------------------------
1 | package cc.carm.lib.easyplugin.gui;
2 |
3 | import cc.carm.lib.easyplugin.utils.ColorParser;
4 | import org.bukkit.Bukkit;
5 | import org.bukkit.Material;
6 | import org.bukkit.entity.HumanEntity;
7 | import org.bukkit.entity.Player;
8 | import org.bukkit.event.inventory.InventoryClickEvent;
9 | import org.bukkit.event.inventory.InventoryDragEvent;
10 | import org.bukkit.inventory.Inventory;
11 | import org.bukkit.inventory.ItemStack;
12 | import org.bukkit.plugin.java.JavaPlugin;
13 | import org.jetbrains.annotations.NotNull;
14 | import org.jetbrains.annotations.Nullable;
15 |
16 | import java.util.*;
17 | import java.util.stream.IntStream;
18 |
19 | public class GUI {
20 |
21 | private static JavaPlugin plugin;
22 | private static final Map openedGUIs = new HashMap<>();
23 |
24 | public static void initialize(JavaPlugin plugin) {
25 | GUI.plugin = plugin;
26 | }
27 |
28 | public static JavaPlugin getPlugin() {
29 | return plugin;
30 | }
31 |
32 | public static Map getOpenedGUIs() {
33 | return openedGUIs;
34 | }
35 |
36 | protected final @NotNull GUIType type;
37 | protected @NotNull String title;
38 | protected Inventory inv;
39 |
40 | protected final SortedMap items = new TreeMap<>();
41 | protected ItemStack emptyItem = null;
42 |
43 | protected boolean cancelOnTarget = true; // 当玩家点击GUI时是否取消对应事件
44 | protected boolean cancelOnSelf = true; // 当玩家点击自己背包时是否取消对应事件
45 | protected boolean cancelOnOuter = true; // 当玩家点击界面外时是否取消对应事件
46 |
47 | protected final Map flags = new LinkedHashMap<>();
48 |
49 | protected GUIListener listener;
50 |
51 | public GUI(@NotNull GUIType type, @NotNull String title) {
52 | this.type = type;
53 | this.title = ColorParser.parse(title);
54 | }
55 |
56 | public SortedMap<@NotNull Integer, @NotNull GUIItem> getItems() {
57 | return items;
58 | }
59 |
60 | public final void setItem(int index, @Nullable GUIItem item) {
61 | if (item == null) {
62 | this.items.remove(index);
63 | } else {
64 | this.items.put(index, item);
65 | }
66 | }
67 |
68 | public void setItemStack(int index, @Nullable ItemStack item) {
69 | setItem(index, item == null ? null : new GUIItem(item));
70 | }
71 |
72 | public void setItem(GUIItem item, int... index) {
73 | for (int i : index) {
74 | setItem(i, item);
75 | }
76 | }
77 |
78 | public void setItemStack(ItemStack item, int... index) {
79 | for (int i : index) {
80 | setItemStack(i, item);
81 | }
82 | }
83 |
84 | /**
85 | * 设置GUI上方(箱子部分)
86 | * @param row 行数,1为第1行
87 | * @param column 列数,1为第1列
88 | * @param item GUIItem
89 | */
90 | public void setItem(int row, int column, @NotNull GUIItem item){
91 | if(row <= 0 || column <= 0) throw new IllegalArgumentException("行数和列数都不能小于等于零");
92 | if(row > type.getLines()) throw new IllegalArgumentException("行数("+row+")大于GUI大小限制("+type.getLines()+")");
93 | if(column > 9) throw new IllegalArgumentException("列数("+column+")不能大于9");
94 | setItem(9*(row-1)+column-1, item);
95 | }
96 |
97 | public GUIItem getItem(int index) {
98 | return this.items.get(index);
99 | }
100 |
101 | public void setEmptyItem(ItemStack item) {
102 | this.emptyItem = item;
103 | }
104 |
105 | protected void fillEmptySlots(@NotNull Inventory inventory) {
106 | if (emptyItem == null) return;
107 | IntStream.range(0, inventory.getSize())
108 | .filter(i -> inventory.getItem(i) == null)
109 | .forEach(index -> inventory.setItem(index, emptyItem));
110 | }
111 |
112 | protected void applyToInventory(Inventory inventory) {
113 | IntStream.range(0, inventory.getSize()).forEach(index -> inventory.setItem(index, new ItemStack(Material.AIR)));
114 | getItems().forEach((index, item) -> inventory.setItem(index, item.getDisplay()));
115 | fillEmptySlots(inventory);
116 | }
117 |
118 | public void updateTitle(@NotNull String title) {
119 | this.title = ColorParser.parse(title);
120 | if (this.inv != null) {
121 | this.inv = Bukkit.createInventory(null, this.type.getSize(), this.title);
122 | applyToInventory(this.inv);
123 | }
124 | }
125 |
126 | /**
127 | * 更新玩家箱子的视图
128 | */
129 | public void updateView() {
130 | this.onUpdate();
131 | if (this.inv != null) {
132 | List viewers = this.inv.getViewers();
133 | applyToInventory(this.inv);
134 | viewers.forEach(p -> ((Player) p).updateInventory());
135 | }
136 | }
137 |
138 | /**
139 | * 设置是否取消点击GUI内物品的事件
140 | * 如果不取消,玩家可以从GUI中拿取物品。
141 | *
142 | * @param b 是否取消
143 | */
144 | public void setCancelOnTarget(boolean b) {
145 | this.cancelOnTarget = b;
146 | }
147 |
148 | /**
149 | * 设置是否取消点击自己背包内物品的事件
150 | * 如果不取消,玩家可以从自己的背包中拿取物品。
151 | *
152 | * @param b 是否取消
153 | */
154 | public void setCancelOnSelf(boolean b) {
155 | this.cancelOnSelf = b;
156 | }
157 |
158 | /**
159 | * 设置是否取消点击GUI外的事件
160 | * 如果不取消,玩家可以把物品从GUI或背包中丢出去
161 | *
162 | * @param b 是否取消
163 | */
164 | public void setCancelOnOuter(boolean b) {
165 | this.cancelOnOuter = b;
166 | }
167 |
168 | public Object getFlag(String flag) {
169 | return this.flags.get(flag);
170 | }
171 |
172 | public void setFlag(String flag, Object obj) {
173 | this.flags.put(flag, obj);
174 | }
175 |
176 | public void removeFlag(String flag) {
177 | this.flags.remove(flag);
178 | }
179 |
180 | public void rawClickListener(InventoryClickEvent event) {
181 | }
182 |
183 | public void openGUI(Player player) {
184 | if (this.type == GUIType.CANCEL) {
185 | throw new IllegalStateException("被取消或不存在的GUI");
186 | }
187 |
188 | Inventory ui = Bukkit.createInventory(null, this.type.getSize(), this.title);
189 | applyToInventory(ui);
190 |
191 | GUI previous = getOpenedGUI(player);
192 | if (previous != null && previous.listener != null) {
193 | previous.listener.close(player);
194 | }
195 |
196 | setOpenedGUI(player, this);
197 |
198 | this.inv = ui;
199 | player.openInventory(ui);
200 |
201 | if (listener == null) {
202 | this.listener = new GUIListener(this);
203 | Bukkit.getPluginManager().registerEvents(this.listener, getPlugin());
204 | }
205 | }
206 |
207 | /**
208 | * 拖动GUI内物品是执行的代码
209 | *
210 | * @param event InventoryDragEvent
211 | */
212 | public void onDrag(InventoryDragEvent event) {
213 | }
214 |
215 | /**
216 | * 关闭GUI时执行的代码
217 | */
218 | public void onClose() {
219 | }
220 |
221 | /**
222 | * 当GUI更新时执行的代码
223 | */
224 | public void onUpdate() {
225 | }
226 |
227 | public GUIType getGUIType() {
228 | return type;
229 | }
230 |
231 | public String getGUIName() {
232 | return title;
233 | }
234 |
235 | public static void setOpenedGUI(Player player, GUI gui) {
236 | getOpenedGUIs().put(player.getUniqueId(), gui);
237 | }
238 |
239 | public static boolean hasOpenedGUI(Player player) {
240 | return getOpenedGUIs().containsKey(player.getUniqueId());
241 | }
242 |
243 | public static GUI getOpenedGUI(Player player) {
244 | return getOpenedGUIs().get(player.getUniqueId());
245 | }
246 |
247 | public static void removeOpenedGUI(Player player) {
248 | getOpenedGUIs().remove(player.getUniqueId());
249 | }
250 |
251 | public static void closeAll() {
252 | Set viewers = new HashSet<>();
253 | getOpenedGUIs().values().stream().flatMap(gui -> gui.inv.getViewers().stream()).forEach(viewers::add);
254 | viewers.forEach(HumanEntity::closeInventory);
255 | getOpenedGUIs().clear();
256 | }
257 |
258 | }
259 |
--------------------------------------------------------------------------------
/base/gui/src/main/java/cc/carm/lib/easyplugin/gui/GUIItem.java:
--------------------------------------------------------------------------------
1 | package cc.carm.lib.easyplugin.gui;
2 |
3 | import org.bukkit.entity.Player;
4 | import org.bukkit.event.inventory.ClickType;
5 | import org.bukkit.event.inventory.InventoryClickEvent;
6 | import org.bukkit.inventory.ItemStack;
7 |
8 | import java.util.HashSet;
9 | import java.util.Set;
10 |
11 | public class GUIItem {
12 |
13 | protected ItemStack display;
14 | protected boolean actionActive = true;
15 |
16 | protected final Set actions = new HashSet<>();
17 | protected final Set actionsIgnoreActive = new HashSet<>();
18 |
19 | public GUIItem(ItemStack display) {
20 | this.display = display;
21 | }
22 |
23 | public final ItemStack getDisplay() {
24 | return this.display;
25 | }
26 |
27 | public final void setDisplay(ItemStack display) {
28 | this.display = display;
29 | }
30 |
31 | public final boolean isActionActive() {
32 | return this.actionActive;
33 | }
34 |
35 | public final void setActionActive(boolean b) {
36 | actionActive = b;
37 | }
38 |
39 | /**
40 | * 玩家点击该物品后执行的代码
41 | * 可以使用 {@link #onClick(Player, ClickType)} 操作点击者
42 | *
43 | * @param type 点击的类型
44 | */
45 | @Deprecated
46 | @SuppressWarnings("DeprecatedIsStillUsed")
47 | public void onClick(ClickType type) {
48 | }
49 |
50 | /**
51 | * 玩家点击GUI后执行的代码
52 | *
53 | * @param clicker 点击的玩家
54 | * @param type 点击的类型
55 | */
56 | public void onClick(Player clicker, ClickType type) {
57 | this.onClick(type); // Deprecated method support
58 | }
59 |
60 | public void addClickAction(GUIClickAction action) {
61 | actions.add(action);
62 | }
63 |
64 | public void addActionIgnoreActive(GUIClickAction action) {
65 | actionsIgnoreActive.add(action);
66 | }
67 |
68 | public void rawClickAction(InventoryClickEvent event) {
69 |
70 | }
71 |
72 | /**
73 | * 自定义点击事件代码 (须自行触发)
74 | *
75 | * @param player 点击GUI的玩家
76 | */
77 | public void customAction(Player player) {
78 |
79 | }
80 |
81 | public Set getActions() {
82 | return actions;
83 | }
84 |
85 | public Set getActionsIgnoreActive() {
86 | return actionsIgnoreActive;
87 | }
88 |
89 | public abstract static class GUIClickAction {
90 | public abstract void run(ClickType type, Player player);
91 | }
92 |
93 | }
94 |
--------------------------------------------------------------------------------
/base/gui/src/main/java/cc/carm/lib/easyplugin/gui/GUIListener.java:
--------------------------------------------------------------------------------
1 | package cc.carm.lib.easyplugin.gui;
2 |
3 | import org.bukkit.entity.Player;
4 | import org.bukkit.event.EventHandler;
5 | import org.bukkit.event.HandlerList;
6 | import org.bukkit.event.Listener;
7 | import org.bukkit.event.inventory.InventoryClickEvent;
8 | import org.bukkit.event.inventory.InventoryCloseEvent;
9 | import org.bukkit.event.inventory.InventoryCreativeEvent;
10 | import org.bukkit.event.inventory.InventoryDragEvent;
11 | import org.bukkit.event.player.PlayerQuitEvent;
12 |
13 | public class GUIListener implements Listener {
14 |
15 | protected final GUI currentGUI;
16 |
17 | public GUIListener(GUI gui) {
18 | this.currentGUI = gui;
19 | }
20 |
21 | public GUI getCurrentGUI() {
22 | return currentGUI;
23 | }
24 |
25 | @EventHandler
26 | public void onInventoryClickEvent(InventoryClickEvent event) {
27 | if (!(event.getWhoClicked() instanceof Player)) return;
28 | Player player = (Player) event.getWhoClicked();
29 | if (!GUI.hasOpenedGUI(player)) return;
30 | if (GUI.getOpenedGUI(player) != getCurrentGUI()) return;
31 |
32 | getCurrentGUI().rawClickListener(event);
33 |
34 | if (event.getSlot() == -999 && getCurrentGUI().cancelOnOuter) {
35 | event.setCancelled(true);
36 | return;
37 | }
38 |
39 | if (event.getClickedInventory() == null) return;
40 |
41 | if (event.getClickedInventory().equals(getCurrentGUI().inv)) {
42 |
43 | if (getCurrentGUI().cancelOnTarget) event.setCancelled(true);
44 |
45 | if (event.getSlot() != -999) {
46 | GUIItem clickedItem = getCurrentGUI().getItem(event.getSlot());
47 | if (clickedItem != null) {
48 | if (clickedItem.isActionActive()) {
49 | clickedItem.onClick(player, event.getClick());
50 | clickedItem.rawClickAction(event);
51 | clickedItem.actions.forEach(action -> action.run(event.getClick(), player));
52 | }
53 | clickedItem.actionsIgnoreActive.forEach(action -> action.run(event.getClick(), player));
54 | }
55 | }
56 |
57 | } else if (event.getClickedInventory().equals(player.getInventory()) && getCurrentGUI().cancelOnSelf) {
58 | event.setCancelled(true);
59 | }
60 |
61 | }
62 |
63 | @EventHandler
64 | public void onDrag(InventoryDragEvent e) {
65 | if (!(e.getWhoClicked() instanceof Player)) return;
66 | if (e.getInventory().equals(getCurrentGUI().inv)
67 | || e.getInventory().equals(e.getWhoClicked().getInventory())) {
68 | getCurrentGUI().onDrag(e);
69 | }
70 | }
71 |
72 | @EventHandler
73 | public void onInventoryCloseEvent(InventoryCloseEvent event) {
74 | if (!(event.getPlayer() instanceof Player)) return;
75 | if (!event.getInventory().equals(getCurrentGUI().inv)) return;
76 |
77 | close((Player) event.getPlayer());
78 |
79 | }
80 |
81 | protected void close(Player p) {
82 | HandlerList.unregisterAll(this);
83 | getCurrentGUI().listener = null;
84 | GUI.removeOpenedGUI(p);
85 | getCurrentGUI().onClose();
86 | }
87 |
88 | @EventHandler
89 | public void onPlayerLeave(PlayerQuitEvent event) {
90 | GUI.removeOpenedGUI(event.getPlayer());
91 | }
92 |
93 | }
94 |
--------------------------------------------------------------------------------
/base/gui/src/main/java/cc/carm/lib/easyplugin/gui/GUIType.java:
--------------------------------------------------------------------------------
1 | package cc.carm.lib.easyplugin.gui;
2 |
3 | import org.jetbrains.annotations.NotNull;
4 |
5 | import java.util.Arrays;
6 |
7 | public enum GUIType {
8 |
9 | ONE_BY_NINE(1, 9),
10 | TWO_BY_NINE(2, 18),
11 | THREE_BY_NINE(3, 27),
12 | FOUR_BY_NINE(4, 36),
13 | FIVE_BY_NINE(5, 45),
14 | SIX_BY_NINE(6, 54),
15 |
16 | CANCEL(0, 0);
17 |
18 | private final int lines;
19 | private final int size;
20 |
21 | GUIType(int lines, int size) {
22 | this.lines = lines;
23 | this.size = size;
24 | }
25 |
26 | public int getLines() {
27 | return lines;
28 | }
29 |
30 | public int getSize() {
31 | return size;
32 | }
33 |
34 | @NotNull
35 | public static GUIType getBySize(int size) {
36 | return Arrays.stream(values()).filter(type -> type.getSize() == size).findFirst().orElse(CANCEL);
37 | }
38 |
39 | @NotNull
40 | public static GUIType getByLines(int lines) {
41 | return Arrays.stream(values()).filter(type -> type.getLines() == lines).findFirst().orElse(CANCEL);
42 | }
43 |
44 |
45 | @NotNull
46 | public static GUIType getByName(String name) {
47 | return Arrays.stream(values()).filter(type -> type.name().equalsIgnoreCase(name)).findFirst().orElse(CANCEL);
48 | }
49 |
50 | }
51 |
--------------------------------------------------------------------------------
/base/gui/src/main/java/cc/carm/lib/easyplugin/gui/configuration/GUIActionConfiguration.java:
--------------------------------------------------------------------------------
1 | package cc.carm.lib.easyplugin.gui.configuration;
2 |
3 | import cc.carm.lib.easyplugin.gui.GUIItem;
4 | import org.bukkit.entity.Player;
5 | import org.bukkit.event.inventory.ClickType;
6 | import org.jetbrains.annotations.Contract;
7 | import org.jetbrains.annotations.NotNull;
8 | import org.jetbrains.annotations.Nullable;
9 |
10 | public class GUIActionConfiguration {
11 |
12 | public static @NotNull GUIActionConfiguration of(@NotNull GUIActionType actionType,
13 | @Nullable ClickType clickType,
14 | @Nullable String actionContent) {
15 | return new GUIActionConfiguration(actionType, clickType, actionContent);
16 | }
17 |
18 | public static @NotNull GUIActionConfiguration of(@NotNull GUIActionType actionType,
19 | @Nullable String actionContent) {
20 | return of(actionType, null, actionContent);
21 | }
22 |
23 | public static @NotNull GUIActionConfiguration of(@NotNull GUIActionType actionType,
24 | @Nullable ClickType clickType) {
25 | return of(actionType, clickType, null);
26 | }
27 |
28 | public static @NotNull GUIActionConfiguration of(@NotNull GUIActionType actionType) {
29 | return of(actionType, null, null);
30 | }
31 |
32 | protected final @NotNull GUIActionType actionType;
33 |
34 | protected final @Nullable ClickType clickType;
35 | protected final @Nullable String actionContent;
36 |
37 | public GUIActionConfiguration(@NotNull GUIActionType actionType,
38 | @Nullable ClickType clickType,
39 | @Nullable String actionContent) {
40 | this.clickType = clickType;
41 | this.actionType = actionType;
42 | this.actionContent = actionContent;
43 | }
44 |
45 | public @Nullable ClickType getClickType() {
46 | return clickType;
47 | }
48 |
49 | public @NotNull GUIActionType getActionType() {
50 | return actionType;
51 | }
52 |
53 | public @Nullable String getActionContent() {
54 | return actionContent;
55 | }
56 |
57 | public void checkAction(Player player, ClickType type) {
58 | if (getClickType() == null || getClickType() == type) executeAction(player);
59 | }
60 |
61 | public void executeAction(Player targetPlayer) {
62 | getActionType().getExecutor().accept(targetPlayer, getActionContent());
63 | }
64 |
65 | public GUIItem.GUIClickAction toClickAction() {
66 | return new GUIItem.GUIClickAction() {
67 | @Override
68 | public void run(ClickType type, Player player) {
69 | checkAction(player, type);
70 | }
71 | };
72 | }
73 |
74 | @Nullable
75 | @Contract("null->null")
76 | public static GUIActionConfiguration deserialize(@Nullable String actionString) {
77 | if (actionString == null) return null;
78 |
79 | int prefixStart = actionString.indexOf("[");
80 | int prefixEnd = actionString.indexOf("]");
81 | if (prefixStart < 0 || prefixEnd < 0) return null;
82 |
83 | String prefix = actionString.substring(prefixStart + 1, prefixEnd);
84 | ClickType clickType = null;
85 | GUIActionType actionType;
86 | if (prefix.contains(":")) {
87 | String[] args = prefix.split(":");
88 | clickType = GUIConfiguration.readClickType(args[0]);
89 | actionType = GUIActionType.readActionType(args[1]);
90 | } else {
91 | actionType = GUIActionType.readActionType(prefix);
92 | }
93 |
94 | if (actionType == null) return null;
95 |
96 | String content = actionString.substring(prefixEnd + 1).trim();
97 | return of(actionType, clickType, content);
98 | }
99 |
100 | public @NotNull String serialize() {
101 | String prefix = "[" + getActionType().name() + (getClickType() == null ? "" : ":" + getClickType().name()) + "]";
102 | String content = getActionContent() == null ? "" : " " + getActionContent();
103 | return prefix + content;
104 | }
105 |
106 | }
107 |
--------------------------------------------------------------------------------
/base/gui/src/main/java/cc/carm/lib/easyplugin/gui/configuration/GUIActionType.java:
--------------------------------------------------------------------------------
1 | package cc.carm.lib.easyplugin.gui.configuration;
2 |
3 | import cc.carm.lib.easyplugin.utils.MessageUtils;
4 | import org.bukkit.Bukkit;
5 | import org.bukkit.Sound;
6 | import org.bukkit.entity.Player;
7 | import org.jetbrains.annotations.NotNull;
8 | import org.jetbrains.annotations.Nullable;
9 |
10 | import java.util.Arrays;
11 | import java.util.Collections;
12 | import java.util.function.BiConsumer;
13 |
14 | public enum GUIActionType {
15 |
16 | /**
17 | * 以玩家聊天的形式执行
18 | * 若内容以 “/" 开头,则会以玩家身份执行命令。
19 | */
20 | CHAT((player, string) -> {
21 | if (string == null) return;
22 | MessageUtils.setPlaceholders(player, Collections.singletonList(string)).forEach(player::chat);
23 | }),
24 |
25 | /**
26 | * 以后台的形式执行指令
27 | * 指令内容不需要以“/”开头。
28 | */
29 | CONSOLE((player, string) -> {
30 | if (string == null) return;
31 | MessageUtils.setPlaceholders(player, Collections.singletonList(string))
32 | .forEach(message -> Bukkit.dispatchCommand(Bukkit.getConsoleSender(), message));
33 | }),
34 |
35 | /**
36 | * 向玩家发送消息。
37 | */
38 | MESSAGE(MessageUtils::send),
39 |
40 | /**
41 | * 向玩家发送声音。
42 | * 允许配置音量与音调
43 | *
44 | * - SOUND_NAME
45 | * - SOUND_NAME:VOLUME
46 | * - SOUND_NAME:VOLUME:PITCH
47 | *
48 | */
49 | SOUND((player, string) -> {
50 | if (string == null) return;
51 | try {
52 | String[] args = string.contains(":") ? string.split(":") : new String[]{string};
53 | Sound sound = Arrays.stream(Sound.values())
54 | .filter(s -> s.name().equals(args[0]))
55 | .findFirst().orElse(null);
56 |
57 | if (sound == null) return;
58 | float volume = args.length > 1 ? Float.parseFloat(args[1]) : 1F;
59 | float pitch = args.length > 2 ? Float.parseFloat(args[2]) : 1F;
60 |
61 | player.playSound(player.getLocation(), sound, volume, pitch);
62 | } catch (Exception ignored) {
63 | }
64 | }),
65 |
66 | /**
67 | * 为玩家关闭GUI。
68 | */
69 | CLOSE((player, string) -> player.closeInventory());
70 |
71 | BiConsumer<@NotNull Player, @Nullable String> executor;
72 |
73 |
74 | GUIActionType(BiConsumer<@NotNull Player, @Nullable String> executor) {
75 | this.executor = executor;
76 | }
77 |
78 | public BiConsumer<@NotNull Player, @Nullable String> getExecutor() {
79 | return executor;
80 | }
81 |
82 | public static GUIActionType readActionType(String string) {
83 | return Arrays.stream(GUIActionType.values())
84 | .filter(action -> action.name().equalsIgnoreCase(string))
85 | .findFirst().orElse(null);
86 | }
87 |
88 | }
--------------------------------------------------------------------------------
/base/gui/src/main/java/cc/carm/lib/easyplugin/gui/configuration/GUIConfiguration.java:
--------------------------------------------------------------------------------
1 | package cc.carm.lib.easyplugin.gui.configuration;
2 |
3 | import cc.carm.lib.easyplugin.gui.GUI;
4 | import cc.carm.lib.easyplugin.gui.GUIType;
5 | import cc.carm.lib.easyplugin.utils.ColorParser;
6 | import org.bukkit.configuration.ConfigurationSection;
7 | import org.bukkit.entity.Player;
8 | import org.bukkit.event.inventory.ClickType;
9 | import org.jetbrains.annotations.NotNull;
10 | import org.jetbrains.annotations.Nullable;
11 |
12 | import java.util.Arrays;
13 | import java.util.LinkedHashMap;
14 | import java.util.Map;
15 | import java.util.Optional;
16 |
17 | public class GUIConfiguration {
18 |
19 | protected String title;
20 | protected int lines;
21 |
22 | protected Map guiItems;
23 |
24 | public GUIConfiguration(String title, int lines) {
25 | this(title, lines, new LinkedHashMap<>(1));
26 | }
27 |
28 | public GUIConfiguration(String title, int lines, Map guiItems) {
29 | this.title = title;
30 | this.lines = lines;
31 | this.guiItems = guiItems;
32 | }
33 |
34 | public String getTitle() {
35 | return ColorParser.parse(title);
36 | }
37 |
38 | public int getLines() {
39 | return lines;
40 | }
41 |
42 | public GUIType getGUIType() {
43 | return Optional.of(GUIType.getByLines(lines))
44 | .map(type -> type == GUIType.CANCEL ? GUIType.SIX_BY_NINE : type)
45 | .get();
46 | }
47 |
48 | public Map getGUIItems() {
49 | return guiItems;
50 | }
51 |
52 | public void setupItems(Player player, GUI gui) {
53 | getGUIItems().values().forEach(itemConfiguration -> itemConfiguration.setupItems(player, gui));
54 | }
55 |
56 | public @NotNull Map serialize() {
57 | LinkedHashMap map = new LinkedHashMap<>();
58 |
59 | map.put("title", this.title);
60 | map.put("lines", this.lines);
61 | if (!this.guiItems.isEmpty()) {
62 | LinkedHashMap items = new LinkedHashMap<>();
63 | this.guiItems.forEach((key, value) -> items.put(key, value.serialize()));
64 | map.put("items", items);
65 | }
66 | return map;
67 | }
68 |
69 |
70 | public static GUIConfiguration readConfiguration(@Nullable ConfigurationSection section) {
71 | if (section == null) return new GUIConfiguration("name", 6);
72 |
73 | return new GUIConfiguration(
74 | section.getString("title", ""),
75 | section.getInt("lines", 6),
76 | readItems(section.getConfigurationSection("items"))
77 | );
78 | }
79 |
80 | public static Map readItems(ConfigurationSection itemsSection) {
81 | Map items = new LinkedHashMap<>();
82 | if (itemsSection == null) return items;
83 |
84 | for (String key : itemsSection.getKeys(false)) {
85 | GUIItemConfiguration item = GUIItemConfiguration.readFrom(itemsSection.getConfigurationSection(key));
86 | if (item != null) items.put(key, item);
87 | }
88 |
89 | return items;
90 | }
91 |
92 | public static ClickType readClickType(String type) {
93 | return Arrays.stream(ClickType.values())
94 | .filter(click -> click.name().equalsIgnoreCase(type))
95 | .findFirst().orElse(null);
96 | }
97 |
98 | }
99 |
--------------------------------------------------------------------------------
/base/gui/src/main/java/cc/carm/lib/easyplugin/gui/configuration/GUIItemConfiguration.java:
--------------------------------------------------------------------------------
1 | package cc.carm.lib.easyplugin.gui.configuration;
2 |
3 | import cc.carm.lib.easyplugin.gui.GUI;
4 | import cc.carm.lib.easyplugin.gui.GUIItem;
5 | import cc.carm.lib.easyplugin.utils.ColorParser;
6 | import cc.carm.lib.easyplugin.utils.ItemStackFactory;
7 | import cc.carm.lib.easyplugin.utils.MessageUtils;
8 | import org.bukkit.Material;
9 | import org.bukkit.configuration.ConfigurationSection;
10 | import org.bukkit.entity.Player;
11 | import org.bukkit.inventory.ItemStack;
12 | import org.bukkit.inventory.meta.ItemMeta;
13 | import org.jetbrains.annotations.NotNull;
14 | import org.jetbrains.annotations.Nullable;
15 |
16 | import java.util.*;
17 | import java.util.stream.Collectors;
18 |
19 | public class GUIItemConfiguration {
20 |
21 | @Nullable ItemStack original;
22 |
23 | @NotNull Material type;
24 | int amount;
25 | int data;
26 | @Nullable String name;
27 | @NotNull List lore;
28 |
29 | @NotNull List slots;
30 | @NotNull List actions;
31 |
32 | public GUIItemConfiguration(@NotNull Material type, int amount, int data,
33 | @Nullable String name, @NotNull List lore,
34 | @NotNull List actions,
35 | @NotNull List slots) {
36 | this(null, type, amount, data, name, lore, actions, slots);
37 | }
38 |
39 | public GUIItemConfiguration(@Nullable ItemStack original,
40 | @NotNull Material type, int amount, int data,
41 | @Nullable String name, @NotNull List lore,
42 | @NotNull List actions,
43 | @NotNull List slots) {
44 | this.original = original;
45 | this.type = type;
46 | this.amount = amount;
47 | this.data = data;
48 | this.name = name;
49 | this.lore = lore;
50 | this.slots = slots;
51 | this.actions = actions;
52 | }
53 |
54 | public void setupItems(Player player, GUI gui) {
55 | ItemStack itemStack;
56 | if (original != null) {
57 | ItemStack tmp = original.clone();
58 | ItemMeta originalMeta = original.getItemMeta();
59 | if (originalMeta != null) {
60 | if (originalMeta.hasDisplayName()) {
61 | originalMeta.setDisplayName(parseText(player, originalMeta.getDisplayName()));
62 | }
63 | if (originalMeta.getLore() != null) {
64 | originalMeta.setLore(parseTexts(player, originalMeta.getLore()));
65 | }
66 |
67 | }
68 |
69 | tmp.setItemMeta(originalMeta);
70 | itemStack = tmp;
71 | } else {
72 | ItemStackFactory icon = new ItemStackFactory(this.type, this.amount, this.data);
73 | if (this.name != null) {
74 | icon.setDisplayName(parseText(player, this.name));
75 | }
76 | if (!this.lore.isEmpty()) {
77 | icon.setLore(parseTexts(player, this.lore));
78 | }
79 | itemStack = icon.toItemStack();
80 | }
81 |
82 |
83 | GUIItem item = new GUIItem(itemStack);
84 | this.actions.stream().map(GUIActionConfiguration::toClickAction).forEach(item::addClickAction);
85 | this.slots.forEach(slot -> gui.setItem(slot, item));
86 | }
87 |
88 | private List parseTexts(Player player, List lore) {
89 | return ColorParser.parse(MessageUtils.setPlaceholders(player, lore));
90 | }
91 |
92 | @NotNull
93 | private String parseText(Player player, @NotNull String name) {
94 | return ColorParser.parse(MessageUtils.setPlaceholders(player, name));
95 | }
96 |
97 | public @NotNull Map serialize() {
98 | LinkedHashMap map = new LinkedHashMap<>();
99 | if (original != null) map.put("original", original);
100 | else {
101 | map.put("type", this.type.name());
102 | if (this.data != 0) map.put("data", this.data);
103 | }
104 |
105 | if (this.name != null) map.put("name", this.name);
106 | if (this.amount != 1) map.put("amount", this.amount);
107 | if (!this.lore.isEmpty()) map.put("lore", this.lore);
108 | if (this.slots.size() > 1) {
109 | map.put("slots", this.slots);
110 | } else if (slots.size() == 1) {
111 | map.put("slot", this.slots.get(0));
112 | }
113 | if (!this.actions.isEmpty()) {
114 | map.put("actions", this.actions.stream().map(GUIActionConfiguration::serialize).collect(Collectors.toList()));
115 | }
116 | return map;
117 | }
118 |
119 | @Nullable
120 | public static GUIItemConfiguration readFrom(@Nullable ConfigurationSection itemSection) {
121 | if (itemSection == null) return null;
122 |
123 | ItemStack original = null;
124 | if (itemSection.contains("original")) original = itemSection.getItemStack("original");
125 |
126 | String material = Optional.ofNullable(itemSection.getString("type")).orElse("STONE");
127 | Material type = Optional.ofNullable(Material.matchMaterial(material)).orElse(Material.STONE);
128 | int data = itemSection.getInt("data", 0);
129 | int amount = itemSection.getInt("amount", 1);
130 | String name = itemSection.getString("name");
131 | List lore = itemSection.getStringList("lore");
132 |
133 | List slots = itemSection.getIntegerList("slots");
134 | int slot = itemSection.getInt("slot", 0);
135 |
136 | List actionsString = itemSection.getStringList("actions");
137 | List actions = new ArrayList<>();
138 | for (String actionString : actionsString) {
139 | GUIActionConfiguration action = GUIActionConfiguration.deserialize(actionString);
140 | if (action == null) continue;
141 | actions.add(action);
142 | }
143 |
144 | return new GUIItemConfiguration(
145 | original, type, amount, data, name, lore, actions,
146 | slots.size() > 0 ? slots : Collections.singletonList(slot)
147 | );
148 | }
149 |
150 | }
151 |
--------------------------------------------------------------------------------
/base/gui/src/main/java/cc/carm/lib/easyplugin/gui/paged/AutoPagedGUI.java:
--------------------------------------------------------------------------------
1 | package cc.carm.lib.easyplugin.gui.paged;
2 |
3 | import cc.carm.lib.easyplugin.gui.GUIItem;
4 | import cc.carm.lib.easyplugin.gui.GUIType;
5 | import org.bukkit.entity.Player;
6 | import org.bukkit.event.inventory.ClickType;
7 | import org.bukkit.inventory.Inventory;
8 | import org.bukkit.inventory.ItemStack;
9 | import org.jetbrains.annotations.NotNull;
10 | import org.jetbrains.annotations.Nullable;
11 |
12 | import java.util.Arrays;
13 | import java.util.Optional;
14 | import java.util.Set;
15 | import java.util.function.Function;
16 | import java.util.stream.Collectors;
17 | import java.util.stream.IntStream;
18 |
19 | public class AutoPagedGUI extends CommonPagedGUI {
20 |
21 | public static Function defaultPreviousPage = null;
22 | public static Function defaultNextPage = null;
23 |
24 | protected ItemStack previousPageUI;
25 | protected ItemStack nextPageUI;
26 | protected ItemStack noPreviousPageUI;
27 | protected ItemStack noNextPageUI;
28 | protected int previousPageSlot = -1;
29 | protected int nextPageSlot = -1;
30 |
31 | public AutoPagedGUI(@NotNull GUIType type, @NotNull String title, int[] range) {
32 | super(type, title, range);
33 | }
34 |
35 | public AutoPagedGUI(@NotNull GUIType type, @NotNull String title, int a, int b) {
36 | super(type, title, a, b);
37 | }
38 |
39 | public void setPreviousPageUI(@Nullable ItemStack lastPageUI) {
40 | this.previousPageUI = lastPageUI;
41 | }
42 |
43 | public void setNoPreviousPageUI(@Nullable ItemStack noPreviousPageUI) {
44 | this.noPreviousPageUI = noPreviousPageUI;
45 | }
46 |
47 | public void setNextPageUI(@Nullable ItemStack nextPageUI) {
48 | this.nextPageUI = nextPageUI;
49 | }
50 |
51 | public void setNoNextPageUI(@Nullable ItemStack noNextPageUI) {
52 | this.noNextPageUI = noNextPageUI;
53 | }
54 |
55 | public void setPreviousPageSlot(int slot) {
56 | this.previousPageSlot = slot;
57 | }
58 |
59 | public void setNextPageSlot(int slot) {
60 | this.nextPageSlot = slot;
61 | }
62 |
63 | @Override
64 | protected void fillEmptySlots(@NotNull Inventory inventory) {
65 | if (emptyItem == null) return;
66 | Set rangeSet = Arrays.stream(this.range).boxed().collect(Collectors.toSet());
67 | if (previousPageSlot >= 0) rangeSet.add(previousPageSlot);
68 | if (nextPageSlot >= 0) rangeSet.add(nextPageSlot);
69 | IntStream.range(0, inventory.getSize())
70 | .filter(i -> inventory.getItem(i) == null)
71 | .filter(i -> !rangeSet.contains(i))
72 | .forEach(index -> inventory.setItem(index, emptyItem));
73 | }
74 |
75 |
76 | @Override
77 | public void openGUI(Player user) {
78 | if (previousPageSlot >= 0) {
79 | if (hasPreviousPage()) {
80 | ItemStack finalPreviousPageUI;
81 | if(previousPageUI != null)
82 | finalPreviousPageUI = previousPageUI;
83 | else if (defaultPreviousPage != null)
84 | finalPreviousPageUI = defaultPreviousPage.apply(user);
85 | else
86 | finalPreviousPageUI = null;
87 | setItem(previousPageSlot, new GUIItem(finalPreviousPageUI) {
88 | @Override
89 | public void onClick(Player clicker, ClickType type) {
90 | if (type == ClickType.RIGHT) {
91 | goFirstPage();
92 | } else {
93 | goPreviousPage();
94 | }
95 | openGUI(user);
96 | }
97 | });
98 | } else {
99 | setItem(previousPageSlot, this.noPreviousPageUI == null ? null : new GUIItem(noPreviousPageUI));
100 | }
101 | }
102 |
103 | if (nextPageSlot >= 0) {
104 | if (hasNextPage()) {
105 | ItemStack finalNextPageUI;
106 | if(previousPageUI != null)
107 | finalNextPageUI = nextPageUI;
108 | else if (defaultNextPage != null)
109 | finalNextPageUI = defaultNextPage.apply(user);
110 | else
111 | finalNextPageUI = null;
112 | setItem(nextPageSlot, new GUIItem(finalNextPageUI) {
113 | @Override
114 | public void onClick(Player clicker, ClickType type) {
115 | if (type == ClickType.RIGHT) {
116 | goLastPage();
117 | } else {
118 | goNextPage();
119 | }
120 | openGUI(user);
121 | }
122 | });
123 | } else {
124 | setItem(nextPageSlot, this.noNextPageUI == null ? null : new GUIItem(noNextPageUI));
125 | }
126 | }
127 |
128 | super.openGUI(user);
129 | }
130 |
131 |
132 | }
133 |
--------------------------------------------------------------------------------
/base/gui/src/main/java/cc/carm/lib/easyplugin/gui/paged/CommonPagedGUI.java:
--------------------------------------------------------------------------------
1 | package cc.carm.lib.easyplugin.gui.paged;
2 |
3 |
4 | import cc.carm.lib.easyplugin.gui.GUIItem;
5 | import cc.carm.lib.easyplugin.gui.GUIType;
6 | import org.bukkit.entity.Player;
7 | import org.bukkit.inventory.Inventory;
8 | import org.jetbrains.annotations.NotNull;
9 |
10 | import java.util.ArrayList;
11 | import java.util.Arrays;
12 | import java.util.List;
13 | import java.util.Set;
14 | import java.util.stream.Collectors;
15 | import java.util.stream.IntStream;
16 |
17 | public class CommonPagedGUI extends PagedGUI {
18 |
19 | protected final int[] range;
20 |
21 | public CommonPagedGUI(@NotNull GUIType type, @NotNull String title, int a, int b) {
22 | this(type, title, toRange(type, a, b));
23 | }
24 |
25 | public CommonPagedGUI(@NotNull GUIType type, @NotNull String title, int[] range) {
26 | super(type, title);
27 | Arrays.sort(range);
28 | this.range = range;
29 | }
30 |
31 | @Override
32 | protected void fillEmptySlots(@NotNull Inventory inventory) {
33 | if (emptyItem == null) return;
34 | Set rangeSet = Arrays.stream(this.range).boxed().collect(Collectors.toSet());
35 | IntStream.range(0, inventory.getSize())
36 | .filter(i -> inventory.getItem(i) == null)
37 | .filter(i -> !rangeSet.contains(i))
38 | .forEach(index -> inventory.setItem(index, emptyItem));
39 | }
40 |
41 | private static int getLine(int i) {
42 | return i / 9 + 1;
43 | }
44 |
45 | private static int getColumn(int i) {
46 | return i % 9 + 1;
47 | }
48 |
49 | @Override
50 | public boolean hasPreviousPage() {
51 | return page > 1;
52 | }
53 |
54 | @Override
55 | public boolean hasNextPage() {
56 | return page < getLastPageNumber();
57 | }
58 |
59 |
60 | /**
61 | * 前往第一页
62 | */
63 | public void goFirstPage() {
64 | this.page = 1;
65 | onPageChange(this.page);
66 | }
67 |
68 |
69 | /**
70 | * 前往最后一页
71 | */
72 | public void goLastPage() {
73 | this.page = getLastPageNumber();
74 | onPageChange(this.page);
75 | }
76 |
77 |
78 | /**
79 | * 得到最后一页的页码
80 | *
81 | * @return 最后一页的页码
82 | */
83 | @Override
84 | public int getLastPageNumber() {
85 | return getLastPageNumber(range.length);
86 | }
87 |
88 | /**
89 | * 得到第一页的页码
90 | *
91 | * @return 第一页页码(默认为1)
92 | */
93 | public int getFirstPageNumber() {
94 | return 1;
95 | }
96 |
97 |
98 | @Override
99 | public void openGUI(Player player) {
100 | if (container.isEmpty()) {
101 | super.openGUI(player);
102 | return;
103 | }
104 | if(page > getLastPageNumber())
105 | page = getLastPageNumber();
106 | List list = new ArrayList<>();
107 | int start = (page - 1) * range.length;
108 | for (int i = start; i < start + range.length; i++) {
109 | if (i < container.size()) {
110 | list.add(container.get(i));
111 | } else {
112 | break;
113 | }
114 | }
115 |
116 | int i = 0;
117 | Arrays.stream(range).forEach(index -> setItem(index, null));
118 | for (int index : range) {
119 | if (i < list.size()) {
120 | setItem(index, list.get(i));
121 | i++;
122 | } else {
123 | break;
124 | }
125 | }
126 | super.openGUI(player);
127 | }
128 |
129 |
130 | /*
131 | int[] matrix = new int[]{
132 | 0, 1, 2, 3, 4, 5, 6, 7, 8,
133 | 9, 10, 11, 12, 13, 14, 15, 16, 17,
134 | 18, 19, 20, 21, 22, 23, 24, 25, 26,
135 | 27, 28, 29, 30, 31, 32, 33, 34, 35,
136 | 36, 37, 38, 39, 40, 41, 42, 43, 44,
137 | 45, 46, 47, 48, 49, 50, 51, 52, 53
138 | }
139 | */
140 | private static int[] toRange(GUIType type, int a, int b) {
141 | if (a > b) {
142 | a = a ^ b;
143 | b = a ^ b;
144 | a = a ^ b;
145 | }
146 |
147 | int lineA = getLine(a);
148 | int columnA = getColumn(a);
149 | int lineB = getLine(b);
150 | int columnB = getColumn(b);
151 |
152 | if (lineB > type.getLines())
153 | throw new IndexOutOfBoundsException("页面内容范围超过了GUI的大小");
154 |
155 | int[] range = new int[(lineB - lineA + 1) * (columnB - columnA + 1)];
156 |
157 | int l = 0;
158 | for (int i = 0; i < type.getSize(); i++) {
159 | int li = getLine(i);
160 | int ci = getColumn(i);
161 | if (li >= lineA && li <= lineB && ci >= columnA && ci <= columnB) {
162 | range[l] = i;
163 | l++;
164 | }
165 | }
166 |
167 | return range;
168 | }
169 | }
170 |
--------------------------------------------------------------------------------
/base/gui/src/main/java/cc/carm/lib/easyplugin/gui/paged/PagedGUI.java:
--------------------------------------------------------------------------------
1 | package cc.carm.lib.easyplugin.gui.paged;
2 |
3 |
4 | import cc.carm.lib.easyplugin.gui.GUI;
5 | import cc.carm.lib.easyplugin.gui.GUIItem;
6 | import cc.carm.lib.easyplugin.gui.GUIType;
7 | import org.bukkit.inventory.ItemStack;
8 | import org.jetbrains.annotations.NotNull;
9 |
10 | import java.util.ArrayList;
11 | import java.util.List;
12 |
13 | public abstract class PagedGUI extends GUI {
14 |
15 | protected List container = new ArrayList<>();
16 | protected int page = 1;
17 |
18 | protected PagedGUI(@NotNull GUIType type, @NotNull String title) {
19 | super(type, title);
20 | }
21 |
22 | public int setCurrentPage(int page) {
23 | this.page = Math.max(1, page);
24 | return this.page;
25 | }
26 |
27 | public int getCurrentPage() {
28 | return page;
29 | }
30 |
31 |
32 | public int getLastPageNumber() {
33 | return getLastPageNumber(getGUIType().getSize());
34 | }
35 |
36 | /**
37 | * 得到最后一页的页码
38 | *
39 | * @return 最后一页的页码
40 | */
41 | public int getLastPageNumber(int size) {
42 | return (this.container.size() / size) + ((this.container.size() % size) == 0 ? 0 : 1);
43 | }
44 |
45 | public int addItem(@NotNull GUIItem i) {
46 | container.add(i);
47 | return container.size() - 1;
48 | }
49 |
50 | public int addItem(int index, @NotNull GUIItem i) {
51 | container.add(index, i);
52 | return container.size() - 1;
53 | }
54 |
55 | public int addItemStack(@NotNull ItemStack itemStack) {
56 | return addItem(new GUIItem(itemStack));
57 | }
58 |
59 | /**
60 | * 从GUI中移除一个物品
61 | *
62 | * @param item 物品
63 | */
64 | public void removeItem(@NotNull GUIItem item) {
65 | container.remove(item);
66 | }
67 |
68 | /**
69 | * 从GUI中移除一个物品
70 | *
71 | * @param index 物品格子数
72 | */
73 | public void removeItem(int index) {
74 | container.remove(index);
75 | }
76 |
77 | public @NotNull List getPagedContainer() {
78 | return this.container;
79 | }
80 |
81 | /**
82 | * 当GUI改变页码时执行的代码
83 | */
84 | public void onPageChange(int pageNum) {
85 | }
86 |
87 | /**
88 | * 前往上一页
89 | */
90 | public void goPreviousPage() {
91 | if (hasPreviousPage()) {
92 | this.page--;
93 | this.onPageChange(this.page);
94 | } else throw new IndexOutOfBoundsException();
95 | }
96 |
97 |
98 | /**
99 | * 前往下一页
100 | */
101 | public void goNextPage() {
102 | if (hasNextPage()) {
103 | this.page++;
104 | this.onPageChange(this.page);
105 | } else throw new IndexOutOfBoundsException();
106 | }
107 |
108 |
109 | /**
110 | * @return 是否有上一页
111 | */
112 | public abstract boolean hasPreviousPage();
113 |
114 | /**
115 | * @return 是否有下一页
116 | */
117 | public abstract boolean hasNextPage();
118 |
119 | }
120 |
--------------------------------------------------------------------------------
/base/gui/src/test/java/ActionReadTest.java:
--------------------------------------------------------------------------------
1 | import cc.carm.lib.easyplugin.gui.configuration.GUIActionType;
2 | import cc.carm.lib.easyplugin.gui.configuration.GUIConfiguration;
3 | import org.bukkit.event.inventory.ClickType;
4 | import org.junit.Test;
5 |
6 | import java.util.Arrays;
7 | import java.util.List;
8 |
9 | public class ActionReadTest {
10 |
11 |
12 | @Test
13 | public void test() {
14 |
15 | List actions = Arrays.asList(
16 | "[CHAT] 123123",
17 | "[SHIFT_LEFT:CHAT] /test qwq",
18 | "[CONSOLE] say hello",
19 | "[CLOSE]"
20 | );
21 |
22 | for (String actionString : actions) {
23 | int prefixStart = actionString.indexOf("[");
24 | int prefixEnd = actionString.indexOf("]");
25 | if (prefixStart < 0 || prefixEnd < 0) continue;
26 |
27 | String prefix = actionString.substring(prefixStart + 1, prefixEnd);
28 | ClickType clickType = null;
29 | GUIActionType actionType;
30 | if (prefix.contains(":")) {
31 | String[] args = prefix.split(":");
32 | clickType = GUIConfiguration.readClickType(args[0]);
33 | actionType = GUIActionType.readActionType(args[1]);
34 | } else {
35 | actionType = GUIActionType.readActionType(prefix);
36 | }
37 |
38 | if (actionType == null) {
39 | System.out.println("# " + actionString);
40 | System.out.println("- actionType is Null");
41 | continue;
42 | }
43 |
44 | System.out.println("# " + actionType.name() + " " + (clickType == null ? "" : clickType.name()));
45 | System.out.println("- " + actionString.substring(prefixEnd + 1).trim());
46 | }
47 |
48 |
49 | }
50 |
51 | }
52 |
--------------------------------------------------------------------------------
/base/gui/src/test/java/PageTest.java:
--------------------------------------------------------------------------------
1 | import org.junit.Test;
2 |
3 | public class PageTest {
4 |
5 |
6 | @Test
7 | public void test() {
8 | System.out.println(maxPage(0, 10));
9 | System.out.println(maxPage(10, 10));
10 | System.out.println(maxPage(5, 10));
11 | System.out.println(maxPage(15, 10));
12 | System.out.println(maxPage(19, 10));
13 | System.out.println(maxPage(20, 10));
14 |
15 | }
16 |
17 | int maxPage(int contents, int size) {
18 | if (contents == 0 || size == 0) return 1;
19 | return (contents / size) + ((contents % size) == 0 ? 0 : 1);
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/base/main/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | easyplugin-parent
7 | cc.carm.lib
8 | 1.5.13
9 | ../../pom.xml
10 |
11 | 4.0.0
12 |
13 |
14 | ${project.jdk.version}
15 | ${project.jdk.version}
16 | UTF-8
17 | UTF-8
18 |
19 |
20 |
21 | easyplugin-main
22 | jar
23 |
24 | EasyPlugin-Main
25 | 轻松插件主要接口模块,包含便捷的插件入口类与相关工具类。
26 | https://github.com/CarmJos/EasyPlugin
27 |
28 |
29 |
30 | CarmJos
31 | Carm Jos
32 | carm@carm.cc
33 | https://www.carm.cc
34 |
35 |
36 |
37 |
38 |
39 | The MIT License
40 | https://opensource.org/licenses/MIT
41 |
42 |
43 |
44 |
45 | GitHub Issues
46 | https://github.com/CarmJos/EasyPlugin/issues
47 |
48 |
49 |
50 | GitHub Actions
51 | https://github.com/CarmJos/EasyPlugin/actions/workflows/maven.yml
52 |
53 |
54 |
55 |
56 |
57 | ${project.groupId}
58 | easyplugin-utils
59 | ${project.version}
60 | compile
61 |
62 |
63 |
64 | me.clip
65 | placeholderapi
66 | provided
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 | org.apache.maven.plugins
75 | maven-javadoc-plugin
76 |
77 |
78 |
79 |
80 |
--------------------------------------------------------------------------------
/base/main/src/main/java/cc/carm/lib/easyplugin/EasyPlugin.java:
--------------------------------------------------------------------------------
1 | package cc.carm.lib.easyplugin;
2 |
3 | import cc.carm.lib.easyplugin.i18n.EasyPluginMessageProvider;
4 | import cc.carm.lib.easyplugin.utils.JarResourceUtils;
5 | import cc.carm.lib.easyplugin.utils.SchedulerUtils;
6 | import org.bukkit.Bukkit;
7 | import org.bukkit.command.CommandExecutor;
8 | import org.bukkit.command.PluginCommand;
9 | import org.bukkit.command.TabCompleter;
10 | import org.bukkit.event.Event;
11 | import org.bukkit.event.Listener;
12 | import org.bukkit.plugin.PluginDescriptionFile;
13 | import org.bukkit.plugin.java.JavaPlugin;
14 | import org.bukkit.plugin.java.JavaPluginLoader;
15 | import org.jetbrains.annotations.NotNull;
16 | import org.jetbrains.annotations.Nullable;
17 |
18 | import java.io.File;
19 | import java.lang.reflect.Method;
20 | import java.util.Arrays;
21 | import java.util.HashMap;
22 | import java.util.Map;
23 | import java.util.Optional;
24 | import java.util.concurrent.CompletableFuture;
25 | import java.util.function.Supplier;
26 |
27 | public abstract class EasyPlugin extends JavaPlugin {
28 |
29 | protected @NotNull EasyPluginMessageProvider messageProvider;
30 |
31 | public EasyPlugin() {
32 | this(EasyPluginMessageProvider.ZH_CN);
33 | }
34 |
35 | public EasyPlugin(@NotNull EasyPluginMessageProvider messageProvider) {
36 | this.messageProvider = messageProvider;
37 | }
38 |
39 | protected EasyPlugin(JavaPluginLoader loader, PluginDescriptionFile descriptionFile, File dataFolder, File file) {
40 | this(EasyPluginMessageProvider.ZH_CN, loader, descriptionFile, dataFolder, file);
41 | }
42 |
43 | protected EasyPlugin(@NotNull EasyPluginMessageProvider messageProvider,
44 | JavaPluginLoader loader, PluginDescriptionFile descriptionFile, File dataFolder, File file) {
45 | super(loader, descriptionFile, dataFolder, file);
46 | this.messageProvider = messageProvider;
47 | }
48 |
49 | protected SchedulerUtils scheduler;
50 | protected boolean initialized = false;
51 |
52 | @Override
53 | public final void onLoad() {
54 | scheduler = new SchedulerUtils(this);
55 | if (!hasOverride("load")) return;
56 |
57 | long startTime = System.currentTimeMillis();
58 |
59 | log(messageProvider.loading(this));
60 | load();
61 | log(messageProvider.loaded(this, startTime));
62 | }
63 |
64 | @Override
65 | public final void onEnable() {
66 | outputInfo();
67 |
68 | log(messageProvider.enabling(this));
69 | long startTime = System.currentTimeMillis();
70 |
71 | if (!(this.initialized = initialize())) {
72 | setEnabled(false);
73 | log(messageProvider.enableFailure(this, startTime));
74 | return;
75 | }
76 |
77 | log(messageProvider.enableSuccess(this, startTime));
78 | }
79 |
80 |
81 | @Override
82 | public final void onDisable() {
83 | if (!hasOverride("shutdown") || !this.initialized) return;
84 | outputInfo();
85 |
86 | log(messageProvider.disabling(this));
87 | long startTime = System.currentTimeMillis();
88 | shutdown();
89 | log(messageProvider.disabled(this, startTime));
90 | }
91 |
92 | protected void load() {
93 | }
94 |
95 | protected abstract boolean initialize();
96 |
97 | protected void shutdown() {
98 | }
99 |
100 | /**
101 | * 重写以展示插件的相关信息,如插件横幅、下载地址等。
102 | */
103 | public void outputInfo() {
104 | Optional.ofNullable(JarResourceUtils.readResource(this.getResource("PLUGIN_INFO"))).ifPresent(this::log);
105 | }
106 |
107 | public boolean isDebugging() {
108 | return false;
109 | }
110 |
111 | public SchedulerUtils getScheduler() {
112 | return scheduler;
113 | }
114 |
115 | public void registerListener(@NotNull Listener... listeners) {
116 | Arrays.stream(listeners).forEach(listener -> Bukkit.getPluginManager().registerEvents(listener, this));
117 | }
118 |
119 | public void registerCommand(String commandName,
120 | @NotNull CommandExecutor executor) {
121 | registerCommand(commandName, executor, executor instanceof TabCompleter ? (TabCompleter) executor : null);
122 | }
123 |
124 | public void registerCommand(String commandName,
125 | @NotNull CommandExecutor executor,
126 | @Nullable TabCompleter tabCompleter) {
127 | PluginCommand command = Bukkit.getPluginCommand(commandName);
128 | if (command == null) return;
129 | command.setExecutor(executor);
130 | if (tabCompleter != null) command.setTabCompleter(tabCompleter);
131 | }
132 |
133 | public void print(@Nullable String prefix, @Nullable String... messages) {
134 | messageProvider.print(this, prefix, messages);
135 | }
136 |
137 | public void log(@Nullable String... messages) {
138 | print(null, messages);
139 | }
140 |
141 | public void error(String... messages) {
142 | print("&c[ERROR] &r", messages);
143 | }
144 |
145 | public void debug(@Nullable String... messages) {
146 | if (isDebugging()) print("&8[DEBUG] &r", messages);
147 | }
148 |
149 | /**
150 | * 在主线程执行操作,并支持获取其结果。
151 | *
152 | * @param 结果类型
153 | * @param action 需要执行的内容
154 | * @return CompletableFuture
155 | */
156 | public @NotNull CompletableFuture supplySync(@NotNull Supplier action) {
157 | CompletableFuture future = new CompletableFuture<>();
158 | getScheduler().run(() -> future.complete(action.get()));
159 | return future;
160 | }
161 |
162 | /**
163 | * 在异步线程中执行一个操作,并获取操作的结果。
164 | *
165 | * @param 事件类型
166 | * @param action 需要执行的内容
167 | * @return CompletableFuture
168 | */
169 | public @NotNull CompletableFuture supplyAsync(@NotNull Supplier action) {
170 | CompletableFuture future = new CompletableFuture<>();
171 | getScheduler().runAsync(() -> future.complete(action.get()));
172 | return future;
173 | }
174 |
175 | /**
176 | * 在主线程唤起一个事件,并支持获取事件的结果。
177 | *
178 | * @param event 同步事件 (isAsync=false)
179 | * @param 事件类型
180 | * @return CompletableFuture
181 | */
182 | public @NotNull CompletableFuture callSync(T event) {
183 | return supplySync(() -> {
184 | Bukkit.getPluginManager().callEvent(event);
185 | return event;
186 | });
187 | }
188 |
189 | /**
190 | * 在异步线程中唤起一个事件,并支持获取事件的结果。
191 | *
192 | * @param event 异步事件 (isAsync=true)
193 | * @param 事件类型
194 | * @return CompletableFuture
195 | */
196 | public @NotNull CompletableFuture callAsync(T event) {
197 | return supplyAsync(() -> {
198 | Bukkit.getPluginManager().callEvent(event);
199 | return event;
200 | });
201 | }
202 |
203 | protected void setMessageProvider(@NotNull EasyPluginMessageProvider provider) {
204 | this.messageProvider = provider;
205 | }
206 |
207 | @SuppressWarnings("BooleanMethodIsAlwaysInverted")
208 | private boolean hasOverride(String methodName) {
209 | Map methodMap = new HashMap<>();
210 | Arrays.stream(EasyPlugin.class.getDeclaredMethods())
211 | .filter(method -> method.getName().equals(methodName))
212 | .forEach(method -> Arrays.stream(getClass().getDeclaredMethods())
213 | .filter(extend -> extend.getName().equals(methodName))
214 | .filter(extend -> extend.getReturnType().equals(method.getReturnType()))
215 | .filter(extend -> extend.getParameterTypes().length == method.getParameterTypes().length)
216 | .findFirst().ifPresent(extendMethod -> methodMap.put(method, extendMethod))
217 | );
218 | return !methodMap.isEmpty();
219 | }
220 |
221 | }
222 |
--------------------------------------------------------------------------------
/base/main/src/main/java/cc/carm/lib/easyplugin/i18n/EasyPluginMessageProvider.java:
--------------------------------------------------------------------------------
1 | package cc.carm.lib.easyplugin.i18n;
2 |
3 | import cc.carm.lib.easyplugin.utils.ColorParser;
4 | import org.bukkit.Bukkit;
5 | import org.bukkit.plugin.Plugin;
6 | import org.jetbrains.annotations.NotNull;
7 | import org.jetbrains.annotations.Nullable;
8 |
9 | import java.util.Arrays;
10 |
11 | public interface EasyPluginMessageProvider {
12 |
13 | EasyPluginMessageProvider ZH_CN = new zh_CN();
14 | EasyPluginMessageProvider EN_US = new en_US();
15 |
16 | String loading(Plugin plugin);
17 |
18 | String loaded(Plugin plugin, long startMillis);
19 |
20 | String enabling(Plugin plugin);
21 |
22 | String enableSuccess(Plugin plugin, long startMillis);
23 |
24 | String enableFailure(Plugin plugin, long startMillis);
25 |
26 | String disabling(Plugin plugin);
27 |
28 | String disabled(Plugin plugin, long startMillis);
29 |
30 | default void print(@NotNull Plugin plugin, @Nullable String prefix, @Nullable String... messages) {
31 | Arrays.stream(messages)
32 | .map(message -> "[" + plugin.getName() + "] " + (prefix == null ? "" : prefix) + message)
33 | .map(ColorParser::parse)
34 | .forEach(message -> Bukkit.getConsoleSender().sendMessage(message));
35 | }
36 |
37 | class zh_CN implements EasyPluginMessageProvider {
38 |
39 | @Override
40 | public String loading(Plugin plugin) {
41 | return "&f" + plugin.getName() + " " + plugin.getDescription().getVersion() + " 开始加载...";
42 | }
43 |
44 | @Override
45 | public String loaded(Plugin plugin, long startMillis) {
46 | return "&f加载完成 ,共耗时 " + (System.currentTimeMillis() - startMillis) + " ms 。";
47 | }
48 |
49 | @Override
50 | public String enabling(Plugin plugin) {
51 | return "&f" + plugin.getName() + " " + plugin.getDescription().getVersion() + " 开始启动...";
52 | }
53 |
54 | @Override
55 | public String enableSuccess(Plugin plugin, long startMillis) {
56 | return "&a启用完成! &f共耗时 " + (System.currentTimeMillis() - startMillis) + " ms 。";
57 | }
58 |
59 | @Override
60 | public String enableFailure(Plugin plugin, long startMillis) {
61 | return "&c启用失败! &f已耗时 " + (System.currentTimeMillis() - startMillis) + " ms 。";
62 | }
63 |
64 | @Override
65 | public String disabling(Plugin plugin) {
66 | return "&f" + plugin.getName() + " " + plugin.getDescription().getVersion() + " 开始卸载...";
67 | }
68 |
69 | @Override
70 | public String disabled(Plugin plugin, long startMillis) {
71 | return "&f卸载完成! 共耗时 " + (System.currentTimeMillis() - startMillis) + " ms 。";
72 | }
73 | }
74 |
75 | class en_US implements EasyPluginMessageProvider {
76 |
77 | @Override
78 | public String loading(Plugin plugin) {
79 | return "&f" + plugin.getName() + " " + plugin.getDescription().getVersion() + " loading...";
80 | }
81 |
82 | @Override
83 | public String loaded(Plugin plugin, long startMillis) {
84 | return "&fLoaded after " + (System.currentTimeMillis() - startMillis) + " ms.";
85 | }
86 |
87 | @Override
88 | public String enabling(Plugin plugin) {
89 | return "&f" + plugin.getName() + " " + plugin.getDescription().getVersion() + " enabling...";
90 | }
91 |
92 | @Override
93 | public String enableSuccess(Plugin plugin, long startMillis) {
94 | return "&aEnabled successfully!&f Cost " + (System.currentTimeMillis() - startMillis) + " ms.";
95 | }
96 |
97 | @Override
98 | public String enableFailure(Plugin plugin, long startMillis) {
99 | return "&cEnabled failed after " + (System.currentTimeMillis() - startMillis) + " ms.";
100 | }
101 |
102 | @Override
103 | public String disabling(Plugin plugin) {
104 | return "&f" + plugin.getName() + " " + plugin.getDescription().getVersion() + " begin to shutdown...";
105 | }
106 |
107 | @Override
108 | public String disabled(Plugin plugin, long startMillis) {
109 | return "&fShutdown successfully, cost " + (System.currentTimeMillis() - startMillis) + " ms.";
110 | }
111 | }
112 |
113 | }
114 |
--------------------------------------------------------------------------------
/base/main/src/main/java/cc/carm/lib/easyplugin/utils/ItemStackFactory.java:
--------------------------------------------------------------------------------
1 | package cc.carm.lib.easyplugin.utils;
2 |
3 |
4 | import org.bukkit.Material;
5 | import org.bukkit.enchantments.Enchantment;
6 | import org.bukkit.inventory.ItemFlag;
7 | import org.bukkit.inventory.ItemStack;
8 | import org.bukkit.inventory.meta.Damageable;
9 | import org.bukkit.inventory.meta.ItemMeta;
10 | import org.jetbrains.annotations.NotNull;
11 |
12 | import java.util.ArrayList;
13 | import java.util.List;
14 | import java.util.stream.Collectors;
15 |
16 | public class ItemStackFactory {
17 | ItemStack item;
18 |
19 | private ItemStackFactory() {
20 | }
21 |
22 | public ItemStackFactory(ItemStack is) {
23 | this.item = is.clone();
24 | }
25 |
26 | public ItemStackFactory(Material type) {
27 | this(type, 1);
28 | }
29 |
30 | public ItemStackFactory(Material type, int amount) {
31 | this(type, amount, (short) 0);
32 | }
33 |
34 | public ItemStackFactory(Material type, int amount, short data) {
35 | this.item = new ItemStack(type, amount, data);
36 | }
37 |
38 | public ItemStackFactory(Material type, int amount, int data) {
39 | this(type, amount, (short) data);
40 | }
41 |
42 | public ItemStack toItemStack() {
43 | return this.item;
44 | }
45 |
46 | public ItemStackFactory setType(Material type) {
47 | this.item.setType(type);
48 | return this;
49 | }
50 |
51 | public ItemStackFactory setDurability(int i) {
52 | ItemMeta im = this.item.getItemMeta();
53 | if (im instanceof Damageable) {
54 | ((Damageable) im).setDamage(i);
55 | this.item.setItemMeta(im);
56 | }
57 | return this;
58 | }
59 |
60 | public ItemStackFactory setAmount(int a) {
61 | this.item.setAmount(a);
62 | return this;
63 | }
64 |
65 | public ItemStackFactory setDisplayName(@NotNull String name) {
66 | ItemMeta im = this.item.getItemMeta();
67 | if (im != null) {
68 | im.setDisplayName(ColorParser.parse(name));
69 | this.item.setItemMeta(im);
70 | }
71 | return this;
72 | }
73 |
74 | public ItemStackFactory setLore(@NotNull List loreList) {
75 | ItemMeta im = this.item.getItemMeta();
76 | if (im != null) {
77 | im.setLore(
78 | loreList.stream()
79 | .map(ColorParser::parse)
80 | .collect(Collectors.toList())
81 | );
82 | this.item.setItemMeta(im);
83 | }
84 | return this;
85 | }
86 |
87 | public ItemStackFactory addLore(@NotNull String s) {
88 | ItemMeta im = this.item.getItemMeta();
89 | if (im != null) {
90 | List lore = im.getLore() != null ? im.getLore() : new ArrayList<>();
91 | lore.add(ColorParser.parse(s));
92 | im.setLore(lore);
93 | this.item.setItemMeta(im);
94 | }
95 | return this;
96 | }
97 |
98 | public ItemStackFactory addEnchant(@NotNull Enchantment enchant, int level, boolean ignoreLevelRestriction) {
99 | ItemMeta im = this.item.getItemMeta();
100 | if (im != null) {
101 | im.addEnchant(enchant, level, ignoreLevelRestriction);
102 | this.item.setItemMeta(im);
103 | }
104 |
105 | return this;
106 | }
107 |
108 | public ItemStackFactory removeEnchant(@NotNull Enchantment enchant) {
109 | ItemMeta im = this.item.getItemMeta();
110 | if (im != null) {
111 | im.removeEnchant(enchant);
112 | this.item.setItemMeta(im);
113 | }
114 | return this;
115 | }
116 |
117 | public ItemStackFactory addFlag(@NotNull ItemFlag flag) {
118 | ItemMeta im = this.item.getItemMeta();
119 | if (im != null) {
120 | im.addItemFlags(flag);
121 | this.item.setItemMeta(im);
122 | }
123 |
124 | return this;
125 | }
126 |
127 | public ItemStackFactory removeFlag(@NotNull ItemFlag flag) {
128 | ItemMeta im = this.item.getItemMeta();
129 | if (im != null) {
130 | im.removeItemFlags(flag);
131 | this.item.setItemMeta(im);
132 | }
133 | return this;
134 | }
135 |
136 | public ItemStackFactory setUnbreakable(boolean unbreakable) {
137 | ItemMeta im = this.item.getItemMeta();
138 | if (im != null) {
139 | im.setUnbreakable(unbreakable);
140 | this.item.setItemMeta(im);
141 | }
142 | return this;
143 | }
144 | }
145 |
--------------------------------------------------------------------------------
/base/main/src/main/java/cc/carm/lib/easyplugin/utils/MessageUtils.java:
--------------------------------------------------------------------------------
1 | package cc.carm.lib.easyplugin.utils;
2 |
3 | import me.clip.placeholderapi.PlaceholderAPI;
4 | import org.bukkit.Bukkit;
5 | import org.bukkit.command.CommandSender;
6 | import org.bukkit.entity.Player;
7 | import org.jetbrains.annotations.Contract;
8 | import org.jetbrains.annotations.NotNull;
9 | import org.jetbrains.annotations.Nullable;
10 |
11 | import java.util.Arrays;
12 | import java.util.HashMap;
13 | import java.util.List;
14 | import java.util.Map;
15 | import java.util.stream.Collectors;
16 |
17 | public class MessageUtils {
18 |
19 | public static boolean hasPlaceholderAPI() {
20 | return Bukkit.getPluginManager().getPlugin("PlaceholderAPI") != null;
21 | }
22 |
23 | public static void send(@Nullable CommandSender sender, String... messages) {
24 | send(sender, Arrays.asList(messages));
25 | }
26 |
27 | public static void send(@Nullable CommandSender sender, List messages) {
28 | if (messages == null || messages.isEmpty() || sender == null) return;
29 | for (String s : messages) {
30 | sender.sendMessage(ColorParser.parse(s));
31 | }
32 | }
33 |
34 | public static void sendWithPlaceholders(CommandSender sender, String... messages) {
35 | sendWithPlaceholders(sender, Arrays.asList(messages));
36 | }
37 |
38 | public static void sendWithPlaceholders(@Nullable CommandSender sender, List messages) {
39 | if (messages == null || messages.isEmpty() || sender == null) return;
40 | send(sender, setPlaceholders(sender, messages));
41 | }
42 |
43 | public static void sendWithPlaceholders(@Nullable CommandSender sender, List messages, String param, Object value) {
44 | sendWithPlaceholders(sender, messages, new String[]{param}, new Object[]{value});
45 | }
46 |
47 | public static void sendWithPlaceholders(@Nullable CommandSender sender, List messages, String[] params, Object[] values) {
48 | sendWithPlaceholders(sender, setCustomParams(messages, params, values));
49 | }
50 |
51 | public static String setPlaceholders(@Nullable CommandSender sender, @Nullable String message) {
52 | if (message == null || sender == null) return message;
53 | if (hasPlaceholderAPI() && sender instanceof Player) {
54 | return PlaceholderAPI.setPlaceholders((Player) sender, message);
55 | } else {
56 | return message;
57 | }
58 | }
59 |
60 |
61 | @Nullable
62 | @Contract("_, !null -> !null")
63 | public static List setPlaceholders(@Nullable CommandSender sender,
64 | @Nullable List messages) {
65 | if (messages == null || messages.isEmpty() || sender == null) return messages;
66 | if (hasPlaceholderAPI() && sender instanceof Player) {
67 | return PlaceholderAPI.setPlaceholders((Player) sender, messages);
68 | } else {
69 | return messages;
70 | }
71 | }
72 |
73 | public static String setPlaceholders(@Nullable CommandSender sender,
74 | @NotNull String message,
75 | @Nullable String[] params,
76 | @Nullable Object[] values) {
77 | return setPlaceholders(sender, setCustomParams(message, params, values));
78 | }
79 |
80 | public static List setPlaceholders(@Nullable CommandSender sender,
81 | @NotNull List messages,
82 | @Nullable String[] params,
83 | @Nullable Object[] values) {
84 | return setPlaceholders(sender, setCustomParams(messages, params, values));
85 | }
86 |
87 | public static String setCustomParams(@NotNull String message,
88 | @NotNull String param,
89 | @NotNull Object value) {
90 | return setCustomParams(message, new String[]{param}, new Object[]{value});
91 | }
92 |
93 | @Nullable
94 | @Contract("!null, _, _-> !null ; null, _, _->null ")
95 | public static String setCustomParams(@Nullable String message,
96 | @Nullable String[] params,
97 | @Nullable Object[] values) {
98 | if (message == null) return null;
99 | if (params == null || values == null) return message;
100 | if (params.length != values.length) return message;
101 |
102 | HashMap paramsMap = new HashMap<>();
103 | for (int i = 0; i < params.length; i++) {
104 | paramsMap.put(params[i], values[i]);
105 | }
106 | return setCustomParams(message, paramsMap);
107 | }
108 |
109 | @NotNull
110 | public static String setCustomParams(@NotNull String message, @NotNull HashMap params) {
111 | String afterMessage = message;
112 | for (Map.Entry entry : params.entrySet()) {
113 | afterMessage = afterMessage.replace(entry.getKey(), entry.getValue().toString());
114 | }
115 | return afterMessage;
116 | }
117 |
118 | @NotNull
119 | public static List setCustomParams(@NotNull List messages,
120 | @NotNull String param,
121 | @NotNull Object value) {
122 | return setCustomParams(messages, new String[]{param}, new Object[]{value});
123 | }
124 |
125 | @NotNull
126 | public static List setCustomParams(@NotNull List messages,
127 | @Nullable String[] params,
128 | @Nullable Object[] values) {
129 | if (params == null || values == null) return messages;
130 | if (params.length != values.length) return messages;
131 | HashMap paramsMap = new HashMap<>();
132 | for (int i = 0; i < params.length; i++) {
133 | paramsMap.put(params[i], values[i]);
134 | }
135 | return setCustomParams(messages, paramsMap);
136 | }
137 |
138 | @NotNull
139 | public static List setCustomParams(List messages, HashMap params) {
140 | return messages.stream()
141 | .map(message -> setCustomParams(message, params))
142 | .collect(Collectors.toList());
143 | }
144 |
145 |
146 | }
147 |
--------------------------------------------------------------------------------
/base/main/src/main/java/cc/carm/lib/easyplugin/utils/SchedulerUtils.java:
--------------------------------------------------------------------------------
1 | package cc.carm.lib.easyplugin.utils;
2 |
3 | import org.bukkit.Bukkit;
4 | import org.bukkit.plugin.java.JavaPlugin;
5 | import org.bukkit.scheduler.BukkitRunnable;
6 |
7 | import java.util.Arrays;
8 | import java.util.LinkedList;
9 | import java.util.Queue;
10 | import java.util.concurrent.Callable;
11 |
12 | @SuppressWarnings("DuplicatedCode")
13 | public class SchedulerUtils {
14 |
15 | private final JavaPlugin plugin;
16 |
17 | public SchedulerUtils(JavaPlugin plugin) {
18 | this.plugin = plugin;
19 | }
20 |
21 | private JavaPlugin getPlugin() {
22 | return plugin;
23 | }
24 |
25 |
26 | /**
27 | * 在服务端主线程中执行一个任务
28 | *
29 | * @param runnable 需要执行的任务
30 | */
31 | public void run(Runnable runnable) {
32 | Bukkit.getScheduler().runTask(getPlugin(), runnable);
33 | }
34 |
35 |
36 | /**
37 | * 异步执行一个任务。
38 | *
39 | * @param runnable 需要执行的任务
40 | */
41 | public void runAsync(Runnable runnable) {
42 | Bukkit.getScheduler().runTaskAsynchronously(getPlugin(), runnable);
43 | }
44 |
45 | /**
46 | * 在主线程延时执行一个任务。
47 | *
48 | * @param delay 延迟的ticks
49 | * @param runnable 需要执行的任务
50 | */
51 | public void runLater(long delay, Runnable runnable) {
52 | Bukkit.getScheduler().runTaskLater(getPlugin(), runnable, delay);
53 | }
54 |
55 | /**
56 | * 异步延时执行一个任务。
57 | *
58 | * @param delay 延迟的ticks
59 | * @param runnable 需要执行的任务
60 | */
61 | public void runLaterAsync(long delay, Runnable runnable) {
62 | Bukkit.getScheduler().runTaskLaterAsynchronously(getPlugin(), runnable, delay);
63 | }
64 |
65 | /**
66 | * 间隔一段时间按顺序执行列表中的任务
67 | *
68 | * @param interval 间隔时间
69 | * @param tasks 任务列表
70 | */
71 | public void runAtInterval(long interval, Runnable... tasks) {
72 | runAtInterval(0L, interval, tasks);
73 | }
74 |
75 |
76 | /**
77 | * 间隔一段时间按顺序执行列表中的任务
78 | *
79 | * @param delay 延迟时间
80 | * @param interval 间隔时间
81 | * @param tasks 任务列表
82 | */
83 | public void runAtInterval(long delay, long interval, Runnable... tasks) {
84 | new BukkitRunnable() {
85 | private int index;
86 |
87 | @Override
88 | public void run() {
89 | if (this.index >= tasks.length) {
90 | this.cancel();
91 | return;
92 | }
93 |
94 | tasks[index].run();
95 | index++;
96 | }
97 | }.runTaskTimer(getPlugin(), delay, interval);
98 | }
99 |
100 | /**
101 | * 间隔一段时间按顺序异步执行列表中的任务
102 | *
103 | * @param interval 间隔时间
104 | * @param tasks 任务列表
105 | */
106 | public void runAtIntervalAsync(long interval, Runnable... tasks) {
107 | runAtIntervalAsync(0L, interval, tasks);
108 | }
109 |
110 | /**
111 | * 间隔一段时间按顺序异步执行列表中的任务
112 | *
113 | * @param delay 延迟时间
114 | * @param interval 间隔时间
115 | * @param tasks 任务列表
116 | */
117 | public void runAtIntervalAsync(long delay, long interval, Runnable... tasks) {
118 | new BukkitRunnable() {
119 | private int index;
120 |
121 | @Override
122 | public void run() {
123 | if (this.index >= tasks.length) {
124 | this.cancel();
125 | return;
126 | }
127 |
128 | tasks[index].run();
129 | index++;
130 | }
131 | }.runTaskTimerAsynchronously(getPlugin(), delay, interval);
132 | }
133 |
134 | /**
135 | * 重复执行一个任务。
136 | *
137 | * @param repetitions 重复次数
138 | * @param interval 间隔时间
139 | * @param task 任务
140 | * @param onComplete 结束时执行的任务
141 | */
142 | public void repeat(int repetitions, long interval, Runnable task, Runnable onComplete) {
143 | new BukkitRunnable() {
144 | private int index;
145 |
146 | @Override
147 | public void run() {
148 | index++;
149 | if (this.index >= repetitions) {
150 | this.cancel();
151 | if (onComplete == null) {
152 | return;
153 | }
154 |
155 | onComplete.run();
156 | return;
157 | }
158 |
159 | task.run();
160 | }
161 | }.runTaskTimer(getPlugin(), 0L, interval);
162 | }
163 |
164 | /**
165 | * 重复执行一个任务。
166 | *
167 | * @param repetitions 重复次数
168 | * @param interval 间隔时间
169 | * @param task 任务
170 | * @param onComplete 结束时执行的任务
171 | */
172 | public void repeatAsync(int repetitions, long interval, Runnable task, Runnable onComplete) {
173 | new BukkitRunnable() {
174 | private int index;
175 |
176 | @Override
177 | public void run() {
178 | index++;
179 | if (this.index >= repetitions) {
180 | this.cancel();
181 | if (onComplete == null) {
182 | return;
183 | }
184 |
185 | onComplete.run();
186 | return;
187 | }
188 |
189 | task.run();
190 | }
191 | }.runTaskTimerAsynchronously(getPlugin(), 0L, interval);
192 | }
193 |
194 | /**
195 | * 在满足某个条件时,重复执行一个任务。
196 | *
197 | * @param interval 重复间隔时间
198 | * @param predicate 条件
199 | * @param task 任务
200 | * @param onComplete 结束时执行的任务
201 | */
202 | public void repeatWhile(long interval, Callable predicate, Runnable task, Runnable onComplete) {
203 | new BukkitRunnable() {
204 | @Override
205 | public void run() {
206 | try {
207 | if (!predicate.call()) {
208 | this.cancel();
209 | if (onComplete == null) {
210 | return;
211 | }
212 |
213 | onComplete.run();
214 | return;
215 | }
216 |
217 | task.run();
218 | } catch (Exception e) {
219 | e.printStackTrace();
220 | }
221 | }
222 | }.runTaskTimer(getPlugin(), 0L, interval);
223 | }
224 |
225 | /**
226 | * 在满足某个条件时,重复执行一个任务。
227 | *
228 | * @param interval 重复间隔时间
229 | * @param predicate 条件
230 | * @param task 任务
231 | * @param onComplete 结束时执行的任务
232 | */
233 | public void repeatWhileAsync(long interval, Callable predicate, Runnable task, Runnable onComplete) {
234 | new BukkitRunnable() {
235 | @Override
236 | public void run() {
237 | try {
238 | if (!predicate.call()) {
239 | this.cancel();
240 | if (onComplete == null) {
241 | return;
242 | }
243 |
244 | onComplete.run();
245 | return;
246 | }
247 |
248 | task.run();
249 | } catch (Exception e) {
250 | e.printStackTrace();
251 | }
252 | }
253 | }.runTaskTimerAsynchronously(getPlugin(), 0L, interval);
254 | }
255 |
256 | public interface Task {
257 | void start(Runnable onComplete);
258 | }
259 |
260 | public class TaskBuilder {
261 | private final Queue taskList;
262 |
263 | public TaskBuilder() {
264 | this.taskList = new LinkedList<>();
265 | }
266 |
267 | public TaskBuilder append(TaskBuilder builder) {
268 | this.taskList.addAll(builder.taskList);
269 | return this;
270 | }
271 |
272 | public TaskBuilder appendDelay(long delay) {
273 | this.taskList.add(onComplete -> SchedulerUtils.this.runLater(delay, onComplete));
274 | return this;
275 | }
276 |
277 | public TaskBuilder appendTask(Runnable task) {
278 | this.taskList.add(onComplete ->
279 | {
280 | task.run();
281 | onComplete.run();
282 | });
283 |
284 | return this;
285 | }
286 |
287 | public TaskBuilder appendTask(Task task) {
288 | this.taskList.add(task);
289 | return this;
290 | }
291 |
292 | public TaskBuilder appendDelayedTask(long delay, Runnable task) {
293 | this.taskList.add(onComplete -> SchedulerUtils.this.runLater(delay, () ->
294 | {
295 | task.run();
296 | onComplete.run();
297 | }));
298 |
299 | return this;
300 | }
301 |
302 | public TaskBuilder appendTasks(long delay, long interval, Runnable... tasks) {
303 | this.taskList.add(onComplete ->
304 | {
305 | Runnable[] runnables = Arrays.copyOf(tasks, tasks.length + 1);
306 | runnables[runnables.length - 1] = onComplete;
307 | SchedulerUtils.this.runAtInterval(delay, interval, runnables);
308 | });
309 |
310 | return this;
311 | }
312 |
313 | public TaskBuilder appendRepeatingTask(int repetitions, long interval, Runnable task) {
314 | this.taskList.add(onComplete -> SchedulerUtils.this.repeat(repetitions, interval, task, onComplete));
315 | return this;
316 | }
317 |
318 | public TaskBuilder appendConditionalRepeatingTask(long interval, Callable predicate, Runnable task) {
319 | this.taskList.add(onComplete -> SchedulerUtils.this.repeatWhile(interval, predicate, task, onComplete));
320 | return this;
321 | }
322 |
323 | public TaskBuilder waitFor(Callable predicate) {
324 | this.taskList.add(onComplete -> new BukkitRunnable() {
325 | @Override
326 | public void run() {
327 | try {
328 | if (!predicate.call()) {
329 | return;
330 | }
331 |
332 | this.cancel();
333 | onComplete.run();
334 | } catch (Exception e) {
335 | e.printStackTrace();
336 | }
337 | }
338 | }.runTaskTimer(getPlugin(), 0L, 1L));
339 | return this;
340 | }
341 |
342 | public void runTasks() {
343 | this.startNext();
344 | }
345 |
346 | private void startNext() {
347 | Task task = this.taskList.poll();
348 | if (task == null) {
349 | return;
350 | }
351 |
352 | task.start(this::startNext);
353 | }
354 | }
355 | }
356 |
--------------------------------------------------------------------------------
/base/messages/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | cc.carm.lib
8 | easyplugin-parent
9 | 1.5.13
10 | ../../pom.xml
11 |
12 |
13 | ${project.jdk.version}
14 | ${project.jdk.version}
15 | UTF-8
16 | UTF-8
17 | true
18 |
19 | easyplugin-messages
20 |
21 | EasyPlugin-Messages
22 |
23 | 轻松插件消息配置模块,支持多种消息配置,包括文本消息、ActionBar消息、Title消息、声音、粒子效果播放等。
24 |
25 | https://github.com/CarmJos/EasyPlugin
26 |
27 |
28 |
29 | ${project.groupId}
30 | easyplugin-color
31 | ${project.version}
32 | compile
33 |
34 |
35 |
36 |
37 |
38 | CarmJos
39 | Carm Jos
40 | carm@carm.cc
41 | https://www.carm.cc
42 |
43 |
44 |
45 |
46 |
47 | The MIT License
48 | https://opensource.org/licenses/MIT
49 |
50 |
51 |
52 |
53 | GitHub Issues
54 | https://github.com/CarmJos/EasyPlugin/issues
55 |
56 |
57 |
58 | GitHub Actions
59 | https://github.com/CarmJos/EasyPlugin/actions/workflows/maven.yml
60 |
61 |
62 |
63 |
64 |
65 | org.apache.maven.plugins
66 | maven-javadoc-plugin
67 |
68 |
69 |
70 |
71 |
--------------------------------------------------------------------------------
/base/storage/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | easyplugin-parent
7 | cc.carm.lib
8 | 1.5.13
9 | ../../pom.xml
10 |
11 | 4.0.0
12 |
13 | ${project.jdk.version}
14 | ${project.jdk.version}
15 | UTF-8
16 | UTF-8
17 |
18 |
19 | easyplugin-storage
20 | jar
21 |
22 | EasyPlugin-Storage
23 | 轻松插件存储接口模块,包含存储相关的统一接口。
24 | https://github.com/CarmJos/EasyPlugin
25 |
26 |
27 |
28 |
29 | org.apache.maven.plugins
30 | maven-javadoc-plugin
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/base/storage/src/main/java/cc/carm/lib/easyplugin/storage/DataStorage.java:
--------------------------------------------------------------------------------
1 | package cc.carm.lib.easyplugin.storage;
2 |
3 | import org.jetbrains.annotations.NotNull;
4 | import org.jetbrains.annotations.Nullable;
5 |
6 | public interface DataStorage {
7 |
8 | /**
9 | * 在插件加载存储源时执行。
10 | *
11 | * @throws Exception 当出现任何错误时抛出
12 | */
13 | void initialize() throws Exception;
14 |
15 | /**
16 | * 在插件被卸载时执行。
17 | */
18 | void shutdown();
19 |
20 | /**
21 | * 用于加载数据的方法。该方法应当异步运行!
22 | *
23 | *
若不存在对应数据,请返回 null 。
24 | *
若加载出现任何错误,请抛出异常。
25 | *
26 | * @param key 数据主键
27 | * @throws Exception 当出现任何错误时抛出
28 | */
29 | @Nullable
30 | T loadData(@NotNull K key) throws Exception;
31 |
32 | /**
33 | * 用于保存数据的方法。 该方法应当被异步运行!
34 | *
35 | * @param data 数据
36 | * @throws Exception 当出现任何错误时抛出
37 | */
38 | void saveData(@NotNull T data) throws Exception;
39 |
40 | }
41 |
--------------------------------------------------------------------------------
/base/storage/src/main/java/cc/carm/lib/easyplugin/storage/StorageType.java:
--------------------------------------------------------------------------------
1 | package cc.carm.lib.easyplugin.storage;
2 |
3 | import org.jetbrains.annotations.NotNull;
4 |
5 | import java.util.List;
6 |
7 | public interface StorageType> {
8 |
9 | int getID();
10 |
11 | @NotNull List getAlias();
12 |
13 | @NotNull Class extends S> getStorageClass();
14 |
15 | default @NotNull S createStorage() throws Exception {
16 | return getStorageClass().newInstance();
17 | }
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/base/storage/src/main/java/cc/carm/lib/easyplugin/storage/file/FileBasedStorage.java:
--------------------------------------------------------------------------------
1 | package cc.carm.lib.easyplugin.storage.file;
2 |
3 | import cc.carm.lib.easyplugin.storage.DataStorage;
4 | import org.jetbrains.annotations.NotNull;
5 |
6 | import java.io.File;
7 |
8 | public abstract class FileBasedStorage implements DataStorage {
9 |
10 |
11 | protected final String fileName;
12 | protected File dataFile;
13 |
14 | public FileBasedStorage(String fileName) {
15 | this.fileName = fileName;
16 | }
17 |
18 | protected @NotNull File initializeFile(@NotNull File parentFolder) throws Exception {
19 | this.dataFile = new File(parentFolder, fileName);
20 | if (!dataFile.exists()) {
21 | if (!dataFile.createNewFile()) throw new Exception("无法创建数据文件!");
22 | } else if (dataFile.isDirectory()) {
23 | throw new Exception("文件路径对应的不是一个文件!");
24 | }
25 | return dataFile;
26 | }
27 |
28 | public File getDataFile() {
29 | return dataFile;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/base/storage/src/main/java/cc/carm/lib/easyplugin/storage/file/FolderBasedStorage.java:
--------------------------------------------------------------------------------
1 | package cc.carm.lib.easyplugin.storage.file;
2 |
3 | import cc.carm.lib.easyplugin.storage.DataStorage;
4 | import org.jetbrains.annotations.NotNull;
5 |
6 | import java.io.File;
7 | import java.util.Arrays;
8 | import java.util.Collections;
9 | import java.util.List;
10 |
11 | public abstract class FolderBasedStorage implements DataStorage {
12 |
13 | protected final @NotNull String folderPath;
14 | protected File dataFolder;
15 |
16 | public FolderBasedStorage(@NotNull String folderPath) {
17 | this.folderPath = folderPath;
18 | }
19 |
20 | protected @NotNull File initializeFolder(@NotNull File parentFolder) throws Exception {
21 | this.dataFolder = new File(parentFolder, folderPath);
22 | if (!dataFolder.exists()) {
23 | if (!dataFolder.mkdir()) {
24 | throw new Exception("无法创建数据文件夹!");
25 | }
26 | } else if (!dataFolder.isDirectory()) {
27 | throw new Exception("数据文件夹路径对应的不是一个文件夹!");
28 | }
29 | return dataFolder;
30 | }
31 |
32 | protected @NotNull List listFiles() {
33 | if (this.dataFolder == null) return Collections.emptyList();
34 | if (!this.dataFolder.isDirectory()) return Collections.emptyList();
35 |
36 | File[] files = this.dataFolder.listFiles();
37 | if (files == null) return Collections.emptyList();
38 |
39 | return Arrays.asList(files);
40 | }
41 |
42 | public File getDataFolder() {
43 | return dataFolder;
44 | }
45 |
46 | }
47 |
--------------------------------------------------------------------------------
/base/storage/src/main/java/cc/carm/lib/easyplugin/storage/file/YAMLBasedStorage.java:
--------------------------------------------------------------------------------
1 | package cc.carm.lib.easyplugin.storage.file;
2 |
3 | import org.bukkit.configuration.file.FileConfiguration;
4 | import org.bukkit.configuration.file.YamlConfiguration;
5 | import org.jetbrains.annotations.NotNull;
6 |
7 | import java.io.File;
8 |
9 | public abstract class YAMLBasedStorage extends FileBasedStorage {
10 |
11 | protected FileConfiguration configuration;
12 |
13 | public YAMLBasedStorage(String fileName) {
14 | super(fileName);
15 | }
16 |
17 | protected @NotNull FileConfiguration initializeConfiguration(@NotNull File parentFolder) throws Exception {
18 | return this.configuration = YamlConfiguration.loadConfiguration(initializeFile(parentFolder));
19 | }
20 |
21 | public FileConfiguration getConfiguration() {
22 | return configuration;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/base/storage/src/main/java/cc/carm/lib/easyplugin/storage/plugin/PluginBasedStorage.java:
--------------------------------------------------------------------------------
1 | package cc.carm.lib.easyplugin.storage.plugin;
2 |
3 | import cc.carm.lib.easyplugin.storage.DataStorage;
4 | import org.bukkit.Bukkit;
5 | import org.bukkit.plugin.Plugin;
6 |
7 | public abstract class PluginBasedStorage implements DataStorage {
8 |
9 | protected Plugin dependPlugin;
10 |
11 | public PluginBasedStorage(String dependPluginName) {
12 | this(Bukkit.getPluginManager().getPlugin(dependPluginName));
13 | }
14 |
15 | public PluginBasedStorage(Plugin dependPlugin) {
16 | this.dependPlugin = dependPlugin;
17 | }
18 |
19 | @Override
20 | public void initialize() throws NullPointerException {
21 | if (dependPlugin == null) {
22 | throw new NullPointerException("该存储类型依赖的插件不存在,请检查配置文件");
23 | }
24 | }
25 |
26 | public Plugin getDependPlugin() {
27 | return dependPlugin;
28 | }
29 |
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/base/user/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | cc.carm.lib
8 | easyplugin-parent
9 | 1.5.13
10 | ../../pom.xml
11 |
12 |
13 | ${project.jdk.version}
14 | ${project.jdk.version}
15 | UTF-8
16 | UTF-8
17 |
18 | easyplugin-user
19 |
20 |
21 |
22 | CarmJos
23 | Carm Jos
24 | carm@carm.cc
25 | https://www.carm.cc
26 |
27 |
28 |
29 |
30 |
31 | The MIT License
32 | https://opensource.org/licenses/MIT
33 |
34 |
35 |
36 |
37 | GitHub Issues
38 | https://github.com/CarmJos/EasyPlugin/issues
39 |
40 |
41 |
42 | GitHub Actions
43 | https://github.com/CarmJos/EasyPlugin/actions/workflows/maven.yml
44 |
45 |
46 |
47 |
48 | ${project.parent.groupId}
49 | easyplugin-main
50 | ${project.parent.version}
51 | provided
52 |
53 |
54 |
55 |
56 |
57 |
58 | org.apache.maven.plugins
59 | maven-javadoc-plugin
60 |
61 |
62 |
63 |
64 |
--------------------------------------------------------------------------------
/base/user/src/main/java/cc/carm/lib/easyplugin/user/AbstractUserData.java:
--------------------------------------------------------------------------------
1 | package cc.carm.lib.easyplugin.user;
2 |
3 | import org.jetbrains.annotations.NotNull;
4 |
5 | import java.util.Objects;
6 |
7 | public abstract class AbstractUserData implements UserData {
8 |
9 | protected final @NotNull K key;
10 |
11 | /**
12 | * Used to mark the data as dropping (save and unload) status.
13 | */
14 | protected boolean dropping = false;
15 |
16 | protected AbstractUserData(@NotNull K key) {
17 | this.key = key;
18 | }
19 |
20 | /**
21 | * @param dropping true if the data is dropping, false otherwise
22 | */
23 | public void setDropping(boolean dropping) {
24 | this.dropping = dropping;
25 | }
26 |
27 | /**
28 | * @return true if the data is dropping, false otherwise
29 | */
30 | public boolean isDropping() {
31 | return dropping;
32 | }
33 |
34 | @Override
35 | public boolean equals(Object o) {
36 | if (this == o) return true;
37 | if (o == null || getClass() != o.getClass()) return false;
38 | AbstractUserData> abstractUserData = (AbstractUserData>) o;
39 | return key.equals(abstractUserData.key);
40 | }
41 |
42 | @Override
43 | public int hashCode() {
44 | return Objects.hash(key);
45 | }
46 |
47 | }
48 |
--------------------------------------------------------------------------------
/base/user/src/main/java/cc/carm/lib/easyplugin/user/UserData.java:
--------------------------------------------------------------------------------
1 | package cc.carm.lib.easyplugin.user;
2 |
3 | import org.jetbrains.annotations.NotNull;
4 |
5 | public interface UserData {
6 |
7 | @NotNull K key();
8 |
9 | @Deprecated
10 | default @NotNull K getKey() {
11 | return key();
12 | }
13 |
14 | /**
15 | * @param dropping true if the data is dropping, false otherwise
16 | */
17 | void setDropping(boolean dropping);
18 |
19 | /**
20 | * @return true if the data is dropping, false otherwise
21 | */
22 | boolean isDropping();
23 | }
24 |
--------------------------------------------------------------------------------
/base/user/src/main/java/cc/carm/lib/easyplugin/user/UserDataManager.java:
--------------------------------------------------------------------------------
1 | package cc.carm.lib.easyplugin.user;
2 |
3 | import cc.carm.lib.easyplugin.EasyPlugin;
4 | import org.jetbrains.annotations.NotNull;
5 | import org.jetbrains.annotations.Nullable;
6 |
7 | import java.util.Collection;
8 | import java.util.Collections;
9 | import java.util.Map;
10 | import java.util.concurrent.CompletableFuture;
11 | import java.util.concurrent.ConcurrentHashMap;
12 | import java.util.concurrent.ExecutorService;
13 | import java.util.concurrent.Executors;
14 | import java.util.function.Consumer;
15 | import java.util.function.Function;
16 | import java.util.function.Predicate;
17 | import java.util.function.Supplier;
18 | import java.util.logging.Logger;
19 | import java.util.stream.Collectors;
20 |
21 | public abstract class UserDataManager> implements UserDataRegistry {
22 |
23 | protected final @NotNull EasyPlugin plugin;
24 |
25 | protected final @NotNull ExecutorService executor;
26 | protected final @NotNull Map dataCache;
27 |
28 | public UserDataManager(@NotNull EasyPlugin plugin) {
29 | this(plugin, Executors.newCachedThreadPool((r) -> {
30 | Thread t = new Thread(r);
31 | t.setDaemon(true);
32 | t.setName(plugin.getName() + "-UserManager");
33 | return t;
34 | }), new ConcurrentHashMap<>());
35 | }
36 |
37 | public UserDataManager(@NotNull EasyPlugin plugin, @NotNull ExecutorService executor) {
38 | this(plugin, executor, new ConcurrentHashMap<>());
39 | }
40 |
41 | public UserDataManager(@NotNull EasyPlugin plugin, @NotNull ExecutorService executor, @NotNull Map cacheMap) {
42 | this.plugin = plugin;
43 | this.executor = executor;
44 | this.dataCache = cacheMap;
45 | }
46 |
47 | @Override
48 | public void shutdown() {
49 | this.executor.shutdown();
50 | }
51 |
52 | protected @NotNull EasyPlugin getPlugin() {
53 | return plugin;
54 | }
55 |
56 | @Override
57 | public @NotNull Logger getLogger() {
58 | return getPlugin().getLogger();
59 | }
60 |
61 | public abstract @NotNull U emptyUser(@NotNull K key);
62 |
63 | public @NotNull U errorUser(@NotNull K key) {
64 | return emptyUser(key);
65 | }
66 |
67 | protected abstract @Nullable U loadData(@NotNull K key) throws Exception;
68 |
69 | protected abstract void saveData(@NotNull U data) throws Exception;
70 |
71 | public @NotNull Map getDataCache() {
72 | return dataCache;
73 | }
74 |
75 | @Override
76 | public @NotNull CompletableFuture load(@NotNull K key, @NotNull Supplier cacheCondition) {
77 | U cached = getNullable(key);
78 | if (cached != null) {
79 | return CompletableFuture.supplyAsync(() -> cached); // Return cached data async.
80 | }
81 | return CompletableFuture.supplyAsync(() -> {
82 | String identifier = serializeKey(key);
83 |
84 | try {
85 | long s1 = System.currentTimeMillis();
86 | getPlugin().debug("开始加载用户 " + identifier + " 的数据...");
87 | U data = loadData(key);
88 | if (data == null) {
89 | getPlugin().debug("数据库内不存在用户 " + identifier + " 的数据,视作新档案。");
90 | return emptyUser(key);
91 | } else {
92 | getPlugin().debug("加载用户 " + identifier + " 的数据完成,耗时 " + (System.currentTimeMillis() - s1) + " ms.");
93 | return data;
94 | }
95 | } catch (Exception ex) {
96 | getPlugin().error("加载用户 " + serializeKey(key) + " 数据失败,请检查相关配置!");
97 | ex.printStackTrace();
98 | return errorUser(key);
99 | }
100 |
101 | }, executor).thenApply((data) -> {
102 | if (cacheCondition.get() && !data.isDropping()) dataCache.put(key, data);
103 | return data;
104 | });
105 | }
106 |
107 | @Override
108 | public @NotNull CompletableFuture save(@NotNull U user) {
109 | return CompletableFuture.supplyAsync(() -> {
110 | String identifier = serializeKey(user.getKey());
111 |
112 | try {
113 | long s1 = System.currentTimeMillis();
114 | getPlugin().debug("开始保存用户 " + identifier + " 的数据...");
115 | saveData(user);
116 | getPlugin().debug("保存用户 " + identifier + " 的数据完成,耗时 " + (System.currentTimeMillis() - s1) + " ms.");
117 | return true;
118 | } catch (Exception ex) {
119 | getPlugin().error("保存用户 " + identifier + " 数据失败,请检查相关配置!");
120 | ex.printStackTrace();
121 | return false;
122 | }
123 |
124 | }, executor);
125 | }
126 |
127 | @Override
128 | public @NotNull CompletableFuture unload(@NotNull K key, boolean save) {
129 | U data = getNullable(key);
130 | if (data == null) return CompletableFuture.completedFuture(false);
131 | data.setDropping(true); // Mark the data as unloading.
132 | if (save) {
133 | return save(data).thenApply(result -> {
134 | // Check if the data is still unloading,
135 | // which cloud be interrupted by the next load.
136 | if (data.isDropping()) {
137 | this.dataCache.remove(key);
138 | }
139 | return result;
140 | });
141 | } else {
142 | this.dataCache.remove(key);
143 | return CompletableFuture.completedFuture(true);
144 | }
145 |
146 | }
147 |
148 | @Override
149 | public @NotNull CompletableFuture modify(@NotNull K key, @NotNull Consumer consumer) {
150 | U cached = getNullable(key);
151 | if (cached != null) {
152 | return CompletableFuture.supplyAsync(() -> {
153 | consumer.accept(cached);
154 | return true;
155 | }, executor);
156 | } else {
157 | return load(key, true).thenApply((data) -> {
158 | consumer.accept(data);
159 | return data;
160 | }).thenCompose(data -> unload(key, true));
161 | }
162 | }
163 |
164 | @Override
165 | public @NotNull CompletableFuture peek(@NotNull K key, @NotNull Function function) {
166 | U cached = getNullable(key);
167 | if (cached != null) {
168 | return CompletableFuture.supplyAsync(() -> function.apply(cached), executor);
169 | } else {
170 | return load(key, false).thenApply(function);
171 | }
172 | }
173 |
174 | public @NotNull CompletableFuture