9
0
mirror of https://github.com/HibiscusMC/HMCCosmetics.git synced 2025-12-19 15:09:19 +00:00

Compare commits

...

137 Commits

Author SHA1 Message Date
HeroBrineGoat
2347c5175d Fixed offhand being weird in wardrobe mode 2022-02-06 23:15:40 -05:00
HeroBrineGoat
977b5e388a Added command to open other player's wardrobe 2022-02-06 18:59:55 -05:00
HeroBrineGoat
46c47b0a1f Fixed sound bug 2022-02-06 15:23:34 -05:00
HeroBrineGoat
4b8d2c1517 Added static wardrobe 2022-02-06 14:18:00 -05:00
HeroBrineGoat
4d632c612a Stopped displaying cosmetics if player is hidden 2022-02-06 12:35:45 -05:00
HeroBrineGoat
0b4e2a6966 Stopped displaying of cosmetics if player is hidden 2022-02-06 12:32:57 -05:00
HeroBrineGoat
23a5ffa068 Added PAPI expansion 2022-02-06 12:18:18 -05:00
HeroBrineGoat
56fa927ca6 Fixed needing permissions to view items in wardrobe 2022-02-03 00:01:10 -05:00
HeroBrineGoat
9c16008f29 Forgot to remove debug message 2022-02-02 22:19:35 -05:00
HeroBrineGoat
0dc69241f1 Improved messages 2022-02-02 22:17:43 -05:00
HeroBrineGoat
cf1e94e116 Finished wardrobe 2022-02-02 22:07:20 -05:00
HeroBrineGoat
72f92be6f6 Added wardrobe (Despawning and entity skin not functional) 2022-02-02 18:28:45 -05:00
HeroBrineGoat
39ac2713b7 Merge remote-tracking branch 'origin/master' 2022-02-02 16:12:39 -05:00
HeroBrineGoat
a07eec4ddc Added wardrobe (Need to fix imports) 2022-02-02 16:12:28 -05:00
Skyslycer
231dcc163e bump version 2022-02-02 19:01:49 +01:00
Skyslycer
d22b13fe05 Merge remote-tracking branch 'origin/master'
# Conflicts:
#	build.gradle.kts
#	src/main/java/io/github/fisher2911/hmccosmetics/user/UserManager.java
2022-02-02 18:58:24 +01:00
Skyslycer
0c54f52785 fix async event calling 2022-02-02 18:55:23 +01:00
lucian929
16ede1baab Modify default files 2022-02-02 00:27:47 -05:00
HeroBrineGoat
1ba07b87cf Fixed NPE when not specifying dye 2022-02-01 23:44:59 -05:00
HeroBrineGoat
11baacaa5f Added message config action 2022-02-01 22:35:08 -05:00
HeroBrineGoat
8b231535c9 Improved set cosmetic command to include dye 2022-02-01 22:00:50 -05:00
HeroBrineGoat
b81241a019 Added actions for different click types 2022-02-01 21:51:59 -05:00
HeroBrineGoat
4b64ee1b3e Added sounds to click items (Configs must use new format!) 2022-01-31 22:28:18 -05:00
HeroBrineGoat
d2592809c7 Merge remote-tracking branch 'origin/master'
# Conflicts:
#	src/main/java/io/github/fisher2911/hmccosmetics/gui/CosmeticsMenu.java
#	src/main/java/io/github/fisher2911/hmccosmetics/user/UserManager.java
2022-01-31 20:50:58 -05:00
HeroBrineGoat
ffae42c5a7 Added support for multiple dye menus 2022-01-31 20:50:12 -05:00
Skyslycer
7e4d9459ba idk 2022-01-31 16:36:37 +01:00
Skyslycer
30f5278ca9 gradle kotlin = best 2022-01-31 16:34:21 +01:00
Skyslycer
6e3de56071 refactor some stuff 2022-01-29 22:59:25 +01:00
HeroBrineGoat
3d9c006310 Bump version number 2022-01-29 16:42:18 -05:00
HeroBrineGoat
3d8b582a8f Bumped version 2022-01-28 22:24:10 -05:00
HeroBrineGoat
311253aed3 Fixed backpack being removed and not respawned on teleport 2022-01-28 22:21:13 -05:00
HeroBrineGoat
8912ab5b94 Removed paper requirement 2022-01-28 19:19:19 -05:00
HeroBrineGoat
136a5679a6 Bumped version number 2022-01-28 18:11:44 -05:00
HeroBrineGoat
e024d6420b Switched to using Spigot API 2022-01-28 18:10:46 -05:00
HeroBrineGoat
7a7ddf09fb Forgot to remove test listener 2022-01-28 17:29:56 -05:00
HeroBrineGoat
412688eebc Merge remote-tracking branch 'origin/master' 2022-01-28 17:29:36 -05:00
HeroBrineGoat
4aa3ecc147 Added API 2022-01-28 17:29:25 -05:00
lucian929
700ff1aa3f Modify main.yml to add baseball hat 2022-01-28 11:41:45 -05:00
lucian929
929fd84a74 Update to version 1.5.0 2022-01-28 11:41:26 -05:00
lucian929
cc768d8186 Modify main.yml for the lantern cosmetic 2022-01-27 23:43:20 -05:00
HeroBrineGoat
85fc9d9056 Added translations and toggling helmet / off hand requiring empty slots 2022-01-27 22:59:59 -05:00
HeroBrineGoat
538690612a Merge remote-tracking branch 'origin/master' 2022-01-25 23:15:53 -05:00
HeroBrineGoat
998cc02edd Fixed registering permissions 2022-01-25 23:15:09 -05:00
HeroBrineGoat
cfadc69a0d Added registering item permissions 2022-01-25 23:12:57 -05:00
HeroBrineGoat
a29d7f20d0 Added custom dye color command and message 2022-01-25 23:11:56 -05:00
lucian929
4460938dc3 Small changes to default files 2022-01-24 00:27:23 -05:00
lucian929
8a0ce58297 Bump Version Number 2022-01-23 21:50:31 -05:00
HeroBrineGoat
20efcb0b36 Fixed issue with hat disappearing when closing the inventory 2022-01-23 20:13:29 -05:00
HeroBrineGoat
af05a83451 Updated menu items 2022-01-23 18:41:16 -05:00
HeroBrineGoat
aed81b4a13 Added cool down of setting items 2022-01-23 16:37:47 -05:00
HeroBrineGoat
eec23ab54b Improved dye menu 2022-01-23 13:59:50 -05:00
HeroBrineGoat
45c51492a3 Fixed database 2022-01-22 01:10:59 -05:00
HeroBrineGoat
80f6e02598 Changed loading of database to be async 2022-01-21 18:39:45 -05:00
HeroBrineGoat
edc8724e90 Changed loading of database to be async 2022-01-21 18:35:03 -05:00
HeroBrineGoat
f7d8a7205f Changed sending packet to broadcast packet 2022-01-21 18:26:30 -05:00
HeroBrineGoat
3551de2cd3 Removed old database classes 2022-01-21 18:23:14 -05:00
HeroBrineGoat
3da27b0bf4 Added database converter 2022-01-21 18:21:51 -05:00
HeroBrineGoat
b63c130730 Off hand works now, and added some database converter stuff 2022-01-21 17:59:59 -05:00
HeroBrineGoat
81db0cab06 Merge remote-tracking branch 'origin/master' 2022-01-21 13:41:31 -05:00
HeroBrineGoat
f02871871c Fixed more gui stuff 2022-01-21 13:39:26 -05:00
HeroBrineGoat
7207974ada Merge branch 'master' into dev
# Conflicts:
#	src/main/java/io/github/fisher2911/hmccosmetics/gui/CosmeticGui.java
#	src/main/java/io/github/fisher2911/hmccosmetics/gui/DyeSelectorGui.java
2022-01-21 13:31:21 -05:00
HeroBrineGoat
7ea5cdcf46 Improved database handling, and added selecting which item to dye in menu 2022-01-21 13:29:29 -05:00
lucian929
512627d1c7 Bump Version Number 2022-01-20 22:38:08 -05:00
HeroBrineGoat
246fd3f9a6 Fixed (extremely) dumb runnable in GUI 2022-01-20 22:33:50 -05:00
HeroBrineGoat
70ee381f48 More database stuff 2022-01-20 19:09:54 -05:00
HeroBrineGoat
d78bf20ca9 Database stuff (incomplete) 2022-01-20 17:25:20 -05:00
HeroBrineGoat
1c7aa2c672 Cleaned up some code 2022-01-20 16:19:09 -05:00
lucian929
f0a046f434 Merge remote-tracking branch 'origin/master' 2022-01-19 23:15:38 -05:00
lucian929
22c4323a16 Bump Version Number 2022-01-19 23:15:21 -05:00
lucian929
77e913257e Merge pull request #5 from HibiscusMC/hooks
ItemsAdder Hook
2022-01-19 23:11:01 -05:00
HeroBrineGoat
2cc4fcc878 Fixed merging error 2022-01-19 21:21:38 -05:00
HeroBrineGoat
e35eee9937 Merge remote-tracking branch 'origin/hooks' into hooks
# Conflicts:
#	src/main/java/io/github/fisher2911/hmccosmetics/hook/item/ItemsAdderHook.java
2022-01-19 21:16:52 -05:00
HeroBrineGoat
941b82de08 Added support for item adder 2022-01-19 21:16:28 -05:00
Fisher
81bea485a5 Added debug messages 2022-01-19 09:54:13 -05:00
Fisher
cfa36dab4b Update DatabaseFactory.java 2022-01-19 09:48:21 -05:00
Fisher
14d1cf5d19 Added test to try to fix startup error 2022-01-19 08:00:11 -05:00
HeroBrineGoat
8455492322 Fix for backpack color not being set 2022-01-18 20:39:41 -05:00
HeroBrineGoat
c4dba6f9a8 Changed how ItemsAdder listener works 2022-01-18 19:53:33 -05:00
HeroBrineGoat
280018fb9f Changed all ItemAdder things to ItemsAdder 2022-01-18 19:45:02 -05:00
HeroBrineGoat
14617fdaff Changed class name to reflect plugin name 2022-01-18 19:43:13 -05:00
HeroBrineGoat
61f13eb3c1 Fixed ItemsAdder identifier 2022-01-18 19:42:34 -05:00
HeroBrineGoat
39b6a8de67 Fixed ItemsAdder support (Previously done directly on GitHub using a school computer) 2022-01-18 19:25:11 -05:00
lucian929
f19b4d359e Fixes for IA 2022-01-18 15:46:27 -05:00
Fisher
0ca9350ce6 Update HMCCosmetics.java 2022-01-18 12:54:51 -05:00
Fisher
d9f12db6dc Create ItemsAdderListener.java 2022-01-18 12:52:51 -05:00
Fisher
3c302e6c4b Update HMCCosmetics.java 2022-01-18 12:46:57 -05:00
Fisher
e22b776d13 Added ItemAdderHook
The format is "itemadder:item"
2022-01-18 12:05:34 -05:00
Fisher
915285c485 Create ItemAdderHook.java 2022-01-18 12:01:43 -05:00
HeroBrineGoat
02a3c96137 Merge remote-tracking branch 'origin/master' 2022-01-17 23:22:54 -05:00
HeroBrineGoat
7bb8fd79c2 Changed location of armor stand spawn 2022-01-17 23:22:30 -05:00
lucian929
27cab1fb4b Bump version number 2022-01-17 18:40:36 -05:00
HeroBrineGoat
3c8c585634 Added oraxen item support 2022-01-17 18:33:29 -05:00
lucian929
b2465c8b13 Bump version number according to Semver standards 2022-01-17 15:12:35 -05:00
HeroBrineGoat
3cc489cac9 Fixed MySQL issues 2022-01-17 14:45:44 -05:00
HeroBrineGoat
328aacb73d Merge remote-tracking branch 'origin/master' 2022-01-17 14:13:03 -05:00
HeroBrineGoat
3e609dac93 Added SQLite and MySQL support 2022-01-17 14:12:45 -05:00
lucian929
14eb7b8edc Update README.md 2022-01-16 20:34:24 -05:00
lucian929
5c9041acfc Update README.md 2022-01-16 20:33:00 -05:00
lucian929
f40c6fc292 Merge remote-tracking branch 'origin/master' 2022-01-16 20:11:48 -05:00
lucian929
f800ceaed6 Updated messages.yml (again) 2022-01-16 20:11:36 -05:00
HeroBrineGoat
a6c5761636 Merge remote-tracking branch 'origin/master' 2022-01-16 20:09:34 -05:00
HeroBrineGoat
bb812a180f Fixed teleport issue 2022-01-16 20:09:22 -05:00
lucian929
d69dd1afef Updated messages.yml 2022-01-16 19:04:44 -05:00
HeroBrineGoat
4a1eb8a85f Added remove cosmetic command 2022-01-16 16:06:10 -05:00
HeroBrineGoat
d6c052294a Merge branch 'dev' 2022-01-16 15:52:37 -05:00
HeroBrineGoat
32afd0d144 Fixed title PAPI placeholder 2022-01-16 15:52:05 -05:00
HeroBrineGoat
66ff99e657 Added placeholder api 2022-01-16 15:32:44 -05:00
HeroBrineGoat
67cf6d3267 Armor stands work now??? 2022-01-16 14:29:38 -05:00
HeroBrineGoat
1d988ed809 Added command to set other players cosmetics 2022-01-15 21:48:11 -05:00
HeroBrineGoat
22f095f236 Current state of trying to use packets (A giant mess) 2022-01-15 21:29:56 -05:00
HeroBrineGoat
cc0b2d75b7 Possible rotation working 2022-01-14 20:36:46 -05:00
HeroBrineGoat
520b9854f5 Potential sitting fix 2022-01-14 20:16:34 -05:00
HeroBrineGoat
f8bc649799 Fixed issue with odd behaviour when interacting with non-cosmetic helmet with a cosmetic applied 2022-01-14 17:42:40 -05:00
HeroBrineGoat
44c99ae6d2 Forgot to commit UserManager class in the previous commit 2022-01-13 21:14:08 -05:00
HeroBrineGoat
63681ab9cf Trying out armor stand packets 2022-01-13 21:12:50 -05:00
HeroBrineGoat
705664441a Merge remote-tracking branch 'origin/master' 2022-01-13 19:28:30 -05:00
HeroBrineGoat
d7a8cac2e2 Fixed color issue in dye menu 2022-01-13 19:28:02 -05:00
lucian929
3cef8dc0e3 Switched "Loaded GUI:" logger level to INFO 2022-01-13 16:40:33 -05:00
lucian929
b054207012 Merge pull request #4 from HibiscusMC/skyslycer/feature-1
Several features
2022-01-13 16:23:03 -05:00
Skyslycer
1bb80e0fcd Merge branch 'master' into skyslycer/feature-1 2022-01-13 22:12:28 +01:00
Skyslycer
e9694e77d7 update messages.yml 2022-01-13 22:11:05 +01:00
Skyslycer
a8ba081975 add different message for no permission for cosmetics and make the chat thing reload too 2022-01-13 22:08:10 +01:00
lucian929
6843373298 Update messages.yml 2022-01-13 13:35:55 -05:00
lucian929
c97c3650ea Added help command 2022-01-12 20:44:55 -05:00
lucian929
dbdc6a5c90 Added help command 2022-01-12 20:44:24 -05:00
lucian929
f507dea3c5 Bumped version number 2022-01-11 22:58:25 -05:00
lucian929
cb9c7fb551 Merge remote-tracking branch 'origin/master' 2022-01-11 22:57:17 -05:00
lucian929
e2406c697c Updated default configuration files 2022-01-11 22:57:03 -05:00
lucian929
0faf05fd61 Update README.md 2022-01-11 01:10:37 -05:00
lucian929
44da234779 Update README.md 2022-01-11 01:10:25 -05:00
lucian929
c7238bb375 Added demo video 2022-01-11 01:09:22 -05:00
lucian929
1ee6fc5f49 Update README.md 2022-01-08 12:13:00 -05:00
lucian929
fa95a6fa33 Update README.md 2022-01-07 21:36:46 -05:00
HeroBrineGoat
f21749a45d Merge remote-tracking branch 'origin/master' 2022-01-06 21:22:55 -05:00
HeroBrineGoat
eeb2ef960b Added bstats 2022-01-06 21:22:47 -05:00
lucian929
6e4c4bc127 Update requirements 2022-01-06 21:00:41 -05:00
lucian929
ca73a9e5b4 Create README.md 2022-01-06 20:40:23 -05:00
119 changed files with 5857 additions and 1829 deletions

36
.idea/codeStyles/Project.xml generated Normal file
View File

@@ -0,0 +1,36 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<option name="FORMATTER_TAGS_ENABLED" value="true" />
<JavaCodeStyleSettings>
<option name="GENERATE_FINAL_LOCALS" value="true" />
<option name="GENERATE_FINAL_PARAMETERS" value="true" />
<option name="REPLACE_NULL_CHECK" value="false" />
<option name="CLASS_COUNT_TO_USE_IMPORT_ON_DEMAND" value="999999999" />
<option name="NAMES_COUNT_TO_USE_IMPORT_ON_DEMAND" value="999999" />
</JavaCodeStyleSettings>
<JetCodeStyleSettings>
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</JetCodeStyleSettings>
<codeStyleSettings language="JAVA">
<arrangement>
<groups>
<group>
<type>GETTERS_AND_SETTERS</type>
<order>KEEP</order>
</group>
<group>
<type>OVERRIDDEN_METHODS</type>
<order>KEEP</order>
</group>
<group>
<type>DEPENDENT_METHODS</type>
<order>BREADTH_FIRST</order>
</group>
</groups>
</arrangement>
</codeStyleSettings>
<codeStyleSettings language="kotlin">
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</codeStyleSettings>
</code_scheme>
</component>

6
.idea/codeStyles/codeStyleConfig.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
<option name="PREFERRED_PROJECT_CODE_STYLE" value="GoogleStyle" />
</state>
</component>

4
.idea/compiler.xml generated
View File

@@ -1,6 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<bytecodeTargetLevel target="16" />
<bytecodeTargetLevel target="16">
<module name="common" target="1.8" />
</bytecodeTargetLevel>
</component>
</project>

7
.idea/encodings.xml generated Normal file
View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding">
<file url="file://$PROJECT_DIR$/common/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/common/src/main/resources" charset="UTF-8" />
</component>
</project>

7
.idea/gradle.xml generated
View File

@@ -4,11 +4,18 @@
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<option name="delegatedBuild" value="true" />
<option name="testRunner" value="GRADLE" />
<option name="distributionType" value="DEFAULT_WRAPPED" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="gradleJvm" value="openjdk-16" />
<option name="modules">
<set>
<option value="$PROJECT_DIR$" />
<option value="$PROJECT_DIR$/1.17" />
<option value="$PROJECT_DIR$/1.18" />
<option value="$PROJECT_DIR$/common" />
<option value="$PROJECT_DIR$/nms" />
</set>
</option>
</GradleProjectSettings>

View File

@@ -1,15 +1,75 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RemoteRepositoriesConfiguration">
<remote-repository>
<option name="id" value="maven2" />
<option name="name" value="maven2" />
<option name="url" value="https://repo.mattstudios.me/artifactory/public/" />
</remote-repository>
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Central Repository" />
<option name="url" value="https://repo.maven.apache.org/maven2" />
</remote-repository>
<remote-repository>
<option name="id" value="maven7" />
<option name="name" value="maven7" />
<option name="url" value="https://oss.sonatype.org/content/repositories/snapshots/" />
</remote-repository>
<remote-repository>
<option name="id" value="paperweightDecompilerRepository" />
<option name="name" value="paperweightDecompilerRepository" />
<option name="url" value="https://files.minecraftforge.net/maven/" />
</remote-repository>
<remote-repository>
<option name="id" value="sonatype" />
<option name="name" value="sonatype" />
<option name="url" value="https://oss.sonatype.org/content/groups/public/" />
</remote-repository>
<remote-repository>
<option name="id" value="paperweightParamMappingsRepository" />
<option name="name" value="paperweightParamMappingsRepository" />
<option name="url" value="https://maven.fabricmc.net/" />
</remote-repository>
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Maven Central repository" />
<option name="url" value="https://repo1.maven.org/maven2" />
</remote-repository>
<remote-repository>
<option name="id" value="jboss.community" />
<option name="name" value="JBoss Community repository" />
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
<option name="id" value="maven6" />
<option name="name" value="maven6" />
<option name="url" value="https://jitpack.io" />
</remote-repository>
<remote-repository>
<option name="id" value="maven7" />
<option name="name" value="maven7" />
<option name="url" value="https://repo.jeff-media.de/maven2/" />
</remote-repository>
<remote-repository>
<option name="id" value="maven8" />
<option name="name" value="maven8" />
<option name="url" value="https://maven.pkg.github.com/LoneDev6/API-ItemsAdder" />
</remote-repository>
<remote-repository>
<option name="id" value="maven" />
<option name="name" value="maven" />
<option name="url" value="https://papermc.io/repo/repository/maven-public/" />
</remote-repository>
<remote-repository>
<option name="id" value="maven5" />
<option name="name" value="maven5" />
<option name="url" value="https://repo.leonardobishop.com/releases/" />
</remote-repository>
<remote-repository>
<option name="id" value="maven6" />
<option name="name" value="maven6" />
<option name="url" value="https://mvnrepository.com/artifact/com.zaxxer/HikariCP" />
</remote-repository>
<remote-repository>
<option name="id" value="maven8" />
<option name="name" value="maven8" />
<option name="url" value="https://oss.sonatype.org/content/repositories/snapshots" />
</remote-repository>
<remote-repository>
<option name="id" value="maven4" />
@@ -21,35 +81,30 @@
<option name="name" value="MavenRepo" />
<option name="url" value="https://repo.maven.apache.org/maven2/" />
</remote-repository>
<remote-repository>
<option name="id" value="maven" />
<option name="name" value="maven" />
<option name="url" value="https://papermc.io/repo/repository/maven-public/" />
</remote-repository>
<remote-repository>
<option name="id" value="maven6" />
<option name="name" value="maven6" />
<option name="url" value="https://jitpack.io" />
</remote-repository>
<remote-repository>
<option name="id" value="maven2" />
<option name="name" value="maven2" />
<option name="url" value="https://repo.mattstudios.me/artifactory/public/" />
</remote-repository>
<remote-repository>
<option name="id" value="maven5" />
<option name="name" value="maven5" />
<option name="url" value="https://repo.leonardobishop.com/releases/" />
</remote-repository>
<remote-repository>
<option name="id" value="MavenLocal" />
<option name="name" value="MavenLocal" />
<option name="url" value="file:/$MAVEN_REPOSITORY$/" />
</remote-repository>
<remote-repository>
<option name="id" value="maven11" />
<option name="name" value="maven11" />
<option name="url" value="https://libraries.minecraft.net/" />
</remote-repository>
<remote-repository>
<option name="id" value="maven7" />
<option name="name" value="maven7" />
<option name="url" value="https://repo.dmulloy2.net/repository/public/" />
</remote-repository>
<remote-repository>
<option name="id" value="spigotmc-repo" />
<option name="name" value="spigotmc-repo" />
<option name="url" value="https://hub.spigotmc.org/nexus/content/repositories/snapshots/" />
</remote-repository>
<remote-repository>
<option name="id" value="jboss.community" />
<option name="name" value="JBoss Community repository" />
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
</remote-repository>
</component>
</project>

6
.idea/kotlinScripting.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="KotlinScriptingSettings">
<option name="suppressDefinitionsCheck" value="true" />
</component>
</project>

7
.idea/misc.xml generated
View File

@@ -1,10 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="EntryPointsManager">
<list size="1">
<item index="0" class="java.lang.String" itemvalue="org.bukkit.event.EventHandler" />
</list>
</component>
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="FrameworkDetectionExcludesConfiguration">
<file type="web" url="file://$PROJECT_DIR$" />
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_16" default="true" project-jdk-name="16" project-jdk-type="JavaSDK">
<component name="ProjectRootManager" version="2" languageLevel="JDK_16" project-jdk-name="temurin-17" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>

View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<module version="4">
<component name="FacetManager">
<facet type="minecraft" name="Minecraft">
<configuration>
<autoDetectTypes>
<platformType>SPIGOT</platformType>
<platformType>MCP</platformType>
</autoDetectTypes>
</configuration>
</facet>
</component>
<component name="McpModuleSettings">
<option name="srgType" value="SRG" />
</component>
</module>

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<module version="4">
<component name="FacetManager">
<facet type="minecraft" name="Minecraft">
<configuration>
<autoDetectTypes>
<platformType>ADVENTURE</platformType>
</autoDetectTypes>
</configuration>
</facet>
</component>
</module>

View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<module version="4">
<component name="FacetManager">
<facet type="minecraft" name="Minecraft">
<configuration>
<autoDetectTypes>
<platformType>SPIGOT</platformType>
<platformType>MCP</platformType>
</autoDetectTypes>
</configuration>
</facet>
</component>
<component name="McpModuleSettings">
<option name="srgType" value="SRG" />
</component>
</module>

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<module version="4">
<component name="FacetManager">
<facet type="minecraft" name="Minecraft">
<configuration>
<autoDetectTypes>
<platformType>ADVENTURE</platformType>
</autoDetectTypes>
</configuration>
</facet>
</component>
</module>

View File

@@ -4,6 +4,7 @@
<facet type="minecraft" name="Minecraft">
<configuration>
<autoDetectTypes>
<platformType>SPIGOT</platformType>
<platformType>MCP</platformType>
<platformType>ADVENTURE</platformType>
</autoDetectTypes>

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<module version="4">
<component name="FacetManager">
<facet type="minecraft" name="Minecraft">
<configuration>
<autoDetectTypes>
<platformType>ADVENTURE</platformType>
</autoDetectTypes>
</configuration>
</facet>
</component>
</module>

View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<module version="4">
<component name="FacetManager">
<facet type="minecraft" name="Minecraft">
<configuration>
<autoDetectTypes>
<platformType>SPIGOT</platformType>
<platformType>MCP</platformType>
</autoDetectTypes>
</configuration>
</facet>
</component>
<component name="McpModuleSettings">
<option name="srgType" value="SRG" />
</component>
</module>

23
1.17/build.gradle.kts Normal file
View File

@@ -0,0 +1,23 @@
plugins {
id("java")
}
//group = "io.github.fisher2911"
//version = "1.7.1"
//description = "Intuitive, easy-to-use cosmetics plugin, designed for servers using resource packs.\n"
repositories {
mavenCentral()
maven("https://papermc.io/repo/repository/maven-public/")
maven("https://oss.sonatype.org/content/repositories/snapshots")
maven("https://jitpack.io")
maven("https://repo.dmulloy2.net/repository/public/")
}
dependencies {
implementation(project(":nms"))
compileOnly("com.mojang:authlib:1.5.25")
compileOnly("org.spigotmc:spigot:1.17-R0.1-SNAPSHOT")
compileOnly("org.jetbrains:annotations:22.0.0")
compileOnly("com.comphenix.protocol:ProtocolLib:4.7.0")
}

View File

@@ -0,0 +1,17 @@
package io.github.fisher2911.nms;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.events.PacketContainer;
import org.bukkit.craftbukkit.libs.it.unimi.dsi.fastutil.ints.IntArrayList;
public class DestroyPacket_1_17_R1 implements DestroyPacket {
@Override
public PacketContainer get(final int entityId) {
final PacketContainer destroyPacket = new PacketContainer(
PacketType.Play.Server.ENTITY_DESTROY);
destroyPacket.getModifier().write(0, new IntArrayList(new int[]{entityId}));
return destroyPacket;
}
}

View File

@@ -0,0 +1,96 @@
package io.github.fisher2911.nms;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.wrappers.EnumWrappers;
import com.comphenix.protocol.wrappers.PlayerInfoData;
import com.comphenix.protocol.wrappers.WrappedChatComponent;
import com.comphenix.protocol.wrappers.WrappedGameProfile;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.properties.Property;
import org.bukkit.GameMode;
import org.bukkit.Location;
import org.bukkit.craftbukkit.v1_17_R1.entity.CraftPlayer;
import org.bukkit.entity.Player;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
public class PlayerPackets_1_17_R1 implements PlayerPackets {
@Override
public PacketContainer getSpawnPacket(final Location location, UUID uuid, final int entityId) {
final PacketContainer spawnPacket = new PacketContainer(PacketType.Play.Server.NAMED_ENTITY_SPAWN);
spawnPacket.getUUIDs().write(0, uuid);
spawnPacket.getIntegers().write(0, entityId);
spawnPacket.getDoubles().
write(0, location.getX()).
write(1, location.getY()).
write(2, location.getZ());
spawnPacket.getBytes().write(0, (byte)(((location.getYaw() * 256.0F) / 360.0F)));
return spawnPacket;
}
@Override
public PacketContainer getPlayerInfoPacket(final Player player, final UUID uuid) {
final GameProfile profile = this.getCopyProfile(player, uuid);
final PacketContainer playerInfoPacket = new PacketContainer(PacketType.Play.Server.PLAYER_INFO);
final StructureModifier<EnumWrappers.PlayerInfoAction> action = playerInfoPacket.getPlayerInfoAction();
final StructureModifier<List<PlayerInfoData>> infoData = playerInfoPacket.getPlayerInfoDataLists();
final List<PlayerInfoData> playerInfoData = new ArrayList<>();
playerInfoData.add(new PlayerInfoData(WrappedGameProfile
.fromHandle(profile),
0,
EnumWrappers.NativeGameMode.fromBukkit(GameMode.CREATIVE),
WrappedChatComponent.fromText(profile.getName())));
action.write(0, EnumWrappers.PlayerInfoAction.ADD_PLAYER);
infoData.write(0, playerInfoData);
return playerInfoPacket;
}
@Override
public PacketContainer getRemovePacket(final Player player, final UUID uuid, final int entityId) {
final PacketContainer playerPacket = new PacketContainer(PacketType.Play.Server.PLAYER_INFO);
final StructureModifier<EnumWrappers.PlayerInfoAction> action = playerPacket.getPlayerInfoAction();
final StructureModifier<List<PlayerInfoData>> infoData = playerPacket.getPlayerInfoDataLists();
final List<PlayerInfoData> playerInfoData = new ArrayList<>();
final GameProfile profile = this.getCopyProfile(player, uuid);
playerInfoData.add(new PlayerInfoData(WrappedGameProfile
.fromHandle(profile),
0,
EnumWrappers.NativeGameMode.fromBukkit(GameMode.CREATIVE),
WrappedChatComponent.fromText("")));
action.write(0, EnumWrappers.PlayerInfoAction.REMOVE_PLAYER);
infoData.write(0, playerInfoData);
return playerPacket;
}
private GameProfile getCopyProfile(final Player player, final UUID uuid) {
final GameProfile playerProfile = ((CraftPlayer) player).getProfile();
final GameProfile profile = new GameProfile(
uuid,
player.getDisplayName()
);
profile.getProperties().removeAll("textures");
Property textureProperty = playerProfile.getProperties().get("textures").iterator().next();
String texture = textureProperty.getValue();
String signature = textureProperty.getSignature();
profile.getProperties().put("textures", new Property("textures", texture, signature));
return profile;
}
}

23
1.18/build.gradle.kts Normal file
View File

@@ -0,0 +1,23 @@
plugins {
id("java")
}
//group = "io.github.fisher2911"
//version = "1.7.1"
//description = "Intuitive, easy-to-use cosmetics plugin, designed for servers using resource packs.\n"
repositories {
mavenCentral()
maven("https://papermc.io/repo/repository/maven-public/")
maven("https://oss.sonatype.org/content/repositories/snapshots")
maven("https://jitpack.io")
maven("https://repo.dmulloy2.net/repository/public/")
}
dependencies {
implementation(project(":nms"))
compileOnly("com.mojang:authlib:1.5.25")
compileOnly("org.spigotmc:spigot:1.18-R0.1-SNAPSHOT")
compileOnly("org.jetbrains:annotations:22.0.0")
compileOnly("com.comphenix.protocol:ProtocolLib:4.7.0")
}

View File

@@ -0,0 +1,17 @@
package io.github.fisher2911.nms;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.events.PacketContainer;
import it.unimi.dsi.fastutil.ints.IntArrayList;
public class DestroyPacket_1_18_R1 implements DestroyPacket {
@Override
public PacketContainer get(final int entityId) {
final PacketContainer destroyPacket = new PacketContainer(
PacketType.Play.Server.ENTITY_DESTROY);
destroyPacket.getModifier().write(0, new IntArrayList(new int[]{entityId}));
return destroyPacket;
}
}

View File

@@ -0,0 +1,97 @@
package io.github.fisher2911.nms;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.wrappers.EnumWrappers;
import com.comphenix.protocol.wrappers.PlayerInfoData;
import com.comphenix.protocol.wrappers.WrappedChatComponent;
import com.comphenix.protocol.wrappers.WrappedGameProfile;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.properties.Property;
import com.mojang.authlib.properties.PropertyMap;
import org.bukkit.GameMode;
import org.bukkit.Location;
import org.bukkit.craftbukkit.v1_18_R1.entity.CraftPlayer;
import org.bukkit.entity.Player;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
public class PlayerPackets_1_18_R1 implements PlayerPackets {
@Override
public PacketContainer getSpawnPacket(final Location location, UUID uuid, final int entityId) {
final PacketContainer spawnPacket = new PacketContainer(PacketType.Play.Server.NAMED_ENTITY_SPAWN);
spawnPacket.getUUIDs().write(0, uuid);
spawnPacket.getIntegers().write(0, entityId);
spawnPacket.getDoubles().
write(0, location.getX()).
write(1, location.getY()).
write(2, location.getZ());
spawnPacket.getBytes().write(0, (byte)(((location.getYaw() * 256.0F) / 360.0F)));
return spawnPacket;
}
@Override
public PacketContainer getPlayerInfoPacket(final Player player, final UUID uuid) {
final GameProfile profile = this.getCopyProfile(player, uuid);
final PacketContainer playerInfoPacket = new PacketContainer(PacketType.Play.Server.PLAYER_INFO);
final StructureModifier<EnumWrappers.PlayerInfoAction> action = playerInfoPacket.getPlayerInfoAction();
final StructureModifier<List<PlayerInfoData>> infoData = playerInfoPacket.getPlayerInfoDataLists();
final List<PlayerInfoData> playerInfoData = new ArrayList<>();
playerInfoData.add(new PlayerInfoData(WrappedGameProfile
.fromHandle(profile),
0,
EnumWrappers.NativeGameMode.fromBukkit(GameMode.CREATIVE),
WrappedChatComponent.fromText(profile.getName())));
action.write(0, EnumWrappers.PlayerInfoAction.ADD_PLAYER);
infoData.write(0, playerInfoData);
return playerInfoPacket;
}
@Override
public PacketContainer getRemovePacket(final Player player, final UUID uuid, final int entityId) {
final PacketContainer playerPacket = new PacketContainer(PacketType.Play.Server.PLAYER_INFO);
final StructureModifier<EnumWrappers.PlayerInfoAction> action = playerPacket.getPlayerInfoAction();
final StructureModifier<List<PlayerInfoData>> infoData = playerPacket.getPlayerInfoDataLists();
final List<PlayerInfoData> playerInfoData = new ArrayList<>();
final GameProfile profile = this.getCopyProfile(player, uuid);
playerInfoData.add(new PlayerInfoData(WrappedGameProfile
.fromHandle(profile),
0,
EnumWrappers.NativeGameMode.fromBukkit(GameMode.CREATIVE),
WrappedChatComponent.fromText("")));
action.write(0, EnumWrappers.PlayerInfoAction.REMOVE_PLAYER);
infoData.write(0, playerInfoData);
return playerPacket;
}
private GameProfile getCopyProfile(final Player player, final UUID uuid) {
final GameProfile playerProfile = ((CraftPlayer) player).getProfile();
final GameProfile profile = new GameProfile(
uuid,
player.getDisplayName()
);
profile.getProperties().removeAll("textures");
Property textureProperty = playerProfile.getProperties().get("textures").iterator().next();
String texture = textureProperty.getValue();
String signature = textureProperty.getSignature();
profile.getProperties().put("textures", new Property("textures", texture, signature));
return profile;
}
}

24
README.md Normal file
View File

@@ -0,0 +1,24 @@
# HMCCosmetics
----
<a href="https://img.shields.io/badge/License-gpl-v3"><img src="https://img.shields.io/badge/License-gpl-v3"></a>
## Table of Contents
- [Description](#description)
- [Installation](#installation)
- [Download](#download)
### Description
HMCCosmetics is an intuitive, easy-to-use cosmetics plugin, designed for servers using resource packs.
### Requirements
- ProtocolLib
### Download
If you would like to support the development of our plugin, please purchase it on [Polymart](https://polymart.org/resource/hmccosmetics.1879)

View File

@@ -1,49 +0,0 @@
plugins {
id 'java'
id 'com.github.johnrengelman.shadow' version '6.1.0'
}
group 'io.github.fisher2911'
version '1.0.0'
repositories {
mavenCentral()
mavenLocal()
maven { url = 'https://papermc.io/repo/repository/maven-public/' }
maven { url = 'https://repo.mattstudios.me/artifactory/public/' }
maven { url = 'https://jitpack.io' }
maven { url = 'https://repo.leonardobishop.com/releases/' }
maven { url = 'https://repo.dmulloy2.net/repository/public/' }
}
dependencies {
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1'
compileOnly 'io.papermc.paper:paper:1.17.1-R0.1-SNAPSHOT'
compileOnly 'org.jetbrains:annotations:22.0.0'
compileOnly 'com.comphenix.protocol:ProtocolLib:4.7.0'
compileOnly 'net.kyori:adventure-api:4.9.3'
implementation 'net.kyori:adventure-text-minimessage:4.2.0-SNAPSHOT'
implementation 'net.kyori:adventure-platform-bukkit:4.0.0'
implementation 'dev.triumphteam:triumph-gui:3.0.3'
implementation 'me.mattstudios.utils:matt-framework:1.4.6'
implementation 'org.spongepowered:configurate-yaml:4.1.2'
}
test {
useJUnitPlatform()
}
shadowJar {
relocate 'dev.triumphteam.gui', 'io.github.fisher2911.hmccosmetics.gui'
relocate 'me.mattstudios.mf', 'io.github.fisher2911.hmccosmetics.mf'
relocate 'net.kyori.adventure.text.minimessage', 'io.github.fisher2911.hmccosmetics.adventure.minimessage'
relocate 'net.kyori.adventure.platform', 'io.github.fisher2911.hmccosmetics.adventure.platform'
relocate 'org.spongepowered.configurate', 'io.github.fisher2911.hmccosmetics.configurate'
}
shadowJar {
archiveBaseName.set('HMCCosmetics')
archiveClassifier.set('')
archiveVersion.set('')
}

112
build.gradle.kts Normal file
View File

@@ -0,0 +1,112 @@
//import net.minecrell.pluginyml.bukkit.BukkitPluginDescription
//
plugins {
id("java")
}
// id("com.github.johnrengelman.shadow") version "7.1.1"
// id("net.minecrell.plugin-yml.bukkit") version "0.5.1"
//}
//
//group = "io.github.fisher2911"
//version = "1.7.1"
//description = "Intuitive, easy-to-use cosmetics plugin, designed for servers using resource packs.\n"
//
//repositories {
// mavenCentral()
// maven("https://papermc.io/repo/repository/maven-public/")
// maven("https://repo.mattstudios.me/artifactory/public/")
// maven("https://jitpack.io")
// maven("https://repo.dmulloy2.net/repository/public/")
// maven("https://repo.extendedclip.com/content/repositories/placeholderapi/")
// maven("https://mvnrepository.com/artifact/com.zaxxer/HikariCP")
// maven("https://repo.jeff-media.de/maven2/")
// maven("https://oss.sonatype.org/content/repositories/snapshots")
//}
//
//dependencies {
// compileOnly("com.mojang:authlib:1.5.25")
// compileOnly("org.spigotmc:spigot:1.17-R0.1-SNAPSHOT")
// compileOnly("org.jetbrains:annotations:22.0.0")
// compileOnly("com.comphenix.protocol:ProtocolLib:4.7.0")
// compileOnly("me.clip:placeholderapi:2.11.1")
// compileOnly("com.github.oraxen:oraxen:-SNAPSHOT")
// compileOnly("com.github.LoneDev6:API-ItemsAdder:2.5.4")
// implementation("net.kyori:adventure-api:4.9.3")
// implementation("net.kyori:adventure-text-minimessage:4.10.0-SNAPSHOT")
// implementation("net.kyori:adventure-platform-bukkit:4.0.1")
// implementation("dev.triumphteam:triumph-gui:3.1.1")
// implementation("me.mattstudios.utils:matt-framework:1.4.6")
// implementation("org.spongepowered:configurate-yaml:4.1.2")
// implementation("org.bstats:bstats-bukkit:2.2.1")
// implementation("com.zaxxer:HikariCP:5.0.0")
// implementation("com.j256.ormlite:ormlite-jdbc:6.1")
// implementation("com.j256.ormlite:ormlite-core:6.1")
//}
//
//tasks {
// build {
// dependsOn(shadowJar)
// }
//
// compileJava {
// options.encoding = Charsets.UTF_8.name()
// options.release.set(16)
// }
//
// shadowJar {
// relocate("dev.triumphteam.gui", "io.github.fisher2911.hmccosmetics.gui")
// relocate("me.mattstudios.mf", "io.github.fisher2911.hmccosmetics.mf")
// relocate("net.kyori.adventure.text.minimessage", "io.github.fisher2911.hmccosmetics.adventure.minimessage")
// relocate("net.kyori.adventure.platform", "io.github.fisher2911.hmccosmetics.adventure.platform")
// relocate("org.spongepowered.configurate", "io.github.fisher2911.hmccosmetics.configurate")
// relocate("org.bstats", "io.github.fisher2911.hmccosmetics.bstats")
// relocate("com.zaxxer.hikaricp", "io.github.fisher2911.hmccosmetics.hikaricp")
// relocate("com.j256.ormlite", "io.github.fisher2911.hmccosmetics.ormlite")
// archiveFileName.set("HMCCosmetics.jar")
// }
//
// javadoc {
// options.encoding = Charsets.UTF_8.name()
// }
//
// processResources {
// filteringCharset = Charsets.UTF_8.name()
// }
//}
//
//java {
// toolchain.languageVersion.set(JavaLanguageVersion.of(16))
//}
//
//bukkit {
// load = BukkitPluginDescription.PluginLoadOrder.STARTUP
// main = "io.github.fisher2911.hmccosmetics.HMCCosmetics"
// apiVersion = "1.17"
// name = "HMCCosmetics"
// authors = listOf("MasterOfTheFish")
// softDepend = listOf("Multiverse", "PlaceholderAPI", "Oraxen", "ItemsAdder")
// depend = listOf("ProtocolLib")
// permissions {
// register("hmccosmetics.cmd.default") {
// default = BukkitPluginDescription.Permission.Default.OP
// description = "Permission to execute the default command."
// }
// register("hmccosmetics.cmd.dye") {
// default = BukkitPluginDescription.Permission.Default.OP
// description = "Permission to dye armor."
// }
// register("hmccosmetics.cmd.reload") {
// default = BukkitPluginDescription.Permission.Default.OP
// description = "Permission to use the reload command."
// }
// register("hmccosmetics.cmd.set") {
// default = BukkitPluginDescription.Permission.Default.OP
// description = "Permission to set other users' cosmetics."
// }
// register("hmccosmetics.cmd.wardrobe") {
// default = BukkitPluginDescription.Permission.Default.OP
// description = "Permission to view the wardrobe"
// }
// }
//}

122
common/build.gradle.kts Normal file
View File

@@ -0,0 +1,122 @@
import net.minecrell.pluginyml.bukkit.BukkitPluginDescription
plugins {
id("java")
id("com.github.johnrengelman.shadow") version "7.1.1"
id("net.minecrell.plugin-yml.bukkit") version "0.5.1"
}
group = "io.github.fisher2911"
version = "1.7.1"
description = "Intuitive, easy-to-use cosmetics plugin, designed for servers using resource packs.\n"
repositories {
mavenCentral()
maven("https://papermc.io/repo/repository/maven-public/")
maven("https://oss.sonatype.org/content/repositories/snapshots")
maven("https://repo.mattstudios.me/artifactory/public/")
maven("https://jitpack.io")
maven("https://repo.dmulloy2.net/repository/public/")
maven("https://repo.extendedclip.com/content/repositories/placeholderapi/")
maven("https://mvnrepository.com/artifact/com.zaxxer/HikariCP")
maven("https://repo.jeff-media.de/maven2/")
}
dependencies {
implementation(project(":1.17"))
implementation(project(":1.18"))
implementation(project(":nms"))
compileOnly("com.mojang:authlib:1.5.25")
compileOnly("org.spigotmc:spigot:1.17-R0.1-SNAPSHOT")
compileOnly("org.jetbrains:annotations:22.0.0")
compileOnly("com.comphenix.protocol:ProtocolLib:4.7.0")
compileOnly("me.clip:placeholderapi:2.11.1")
compileOnly("com.github.oraxen:oraxen:-SNAPSHOT")
compileOnly("com.github.LoneDev6:API-ItemsAdder:2.5.4")
implementation("net.kyori:adventure-api:4.9.3")
implementation("net.kyori:adventure-text-minimessage:4.10.0-SNAPSHOT")
implementation("net.kyori:adventure-platform-bukkit:4.0.1")
implementation("dev.triumphteam:triumph-gui:3.1.1")
implementation("me.mattstudios.utils:matt-framework:1.4.6")
implementation("org.spongepowered:configurate-yaml:4.1.2")
implementation("org.bstats:bstats-bukkit:2.2.1")
implementation("com.zaxxer:HikariCP:5.0.0")
implementation("com.j256.ormlite:ormlite-jdbc:6.1")
implementation("com.j256.ormlite:ormlite-core:6.1")
}
tasks {
build {
dependsOn(shadowJar)
}
compileJava {
options.encoding = Charsets.UTF_8.name()
options.release.set(16)
}
shadowJar {
relocate("dev.triumphteam.gui", "io.github.fisher2911.hmccosmetics.gui")
relocate("me.mattstudios.mf", "io.github.fisher2911.hmccosmetics.mf")
relocate("net.kyori.adventure.text.minimessage", "io.github.fisher2911.hmccosmetics.adventure.minimessage")
relocate("net.kyori.adventure.platform", "io.github.fisher2911.hmccosmetics.adventure.platform")
relocate("org.spongepowered.configurate", "io.github.fisher2911.hmccosmetics.configurate")
relocate("org.bstats", "io.github.fisher2911.hmccosmetics.bstats")
relocate("com.zaxxer.hikaricp", "io.github.fisher2911.hmccosmetics.hikaricp")
relocate("com.j256.ormlite", "io.github.fisher2911.hmccosmetics.ormlite")
archiveFileName.set("HMCCosmetics.jar")
}
javadoc {
options.encoding = Charsets.UTF_8.name()
}
processResources {
duplicatesStrategy = DuplicatesStrategy.INCLUDE
filteringCharset = Charsets.UTF_8.name()
}
}
java {
toolchain.languageVersion.set(JavaLanguageVersion.of(16))
}
bukkit {
load = BukkitPluginDescription.PluginLoadOrder.STARTUP
main = "io.github.fisher2911.hmccosmetics.HMCCosmetics"
apiVersion = "1.17"
name = "HMCCosmetics"
authors = listOf("MasterOfTheFish")
softDepend = listOf("Multiverse", "PlaceholderAPI", "Oraxen", "ItemsAdder")
depend = listOf("ProtocolLib")
permissions {
register("hmccosmetics.cmd.default") {
default = BukkitPluginDescription.Permission.Default.OP
description = "Permission to execute the default command."
}
register("hmccosmetics.cmd.dye") {
default = BukkitPluginDescription.Permission.Default.OP
description = "Permission to dye armor."
}
register("hmccosmetics.cmd.reload") {
default = BukkitPluginDescription.Permission.Default.OP
description = "Permission to use the reload command."
}
register("hmccosmetics.cmd.set") {
default = BukkitPluginDescription.Permission.Default.OP
description = "Permission to set other users' cosmetics."
}
register("hmccosmetics.cmd.wardrobe.portable") {
default = BukkitPluginDescription.Permission.Default.OP
description = "Permission to use a portable wardrobe"
}
register("hmccosmetics.cmd.wardrobe") {
default = BukkitPluginDescription.Permission.Default.OP
description = "Permission to view the wardrobe"
}
register("hmccosmetics.cmd.wardrobe.other") {
default = BukkitPluginDescription.Permission.Default.OP
description = "Permission to open another player's wardrobe"
}
}
}

View File

@@ -0,0 +1,193 @@
package io.github.fisher2911.hmccosmetics;
import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.ProtocolManager;
import io.github.fisher2911.hmccosmetics.command.CosmeticsCommand;
import io.github.fisher2911.hmccosmetics.concurrent.Threads;
import io.github.fisher2911.hmccosmetics.config.Settings;
import io.github.fisher2911.hmccosmetics.cosmetic.CosmeticManager;
import io.github.fisher2911.hmccosmetics.database.Database;
import io.github.fisher2911.hmccosmetics.database.DatabaseFactory;
import io.github.fisher2911.hmccosmetics.gui.ArmorItem;
import io.github.fisher2911.hmccosmetics.gui.CosmeticsMenu;
import io.github.fisher2911.hmccosmetics.hook.HookManager;
import io.github.fisher2911.hmccosmetics.hook.item.ItemsAdderHook;
import io.github.fisher2911.hmccosmetics.listener.ClickListener;
import io.github.fisher2911.hmccosmetics.listener.CosmeticFixListener;
import io.github.fisher2911.hmccosmetics.listener.JoinListener;
import io.github.fisher2911.hmccosmetics.listener.PlayerShiftListener;
import io.github.fisher2911.hmccosmetics.listener.RespawnListener;
import io.github.fisher2911.hmccosmetics.listener.TeleportListener;
import io.github.fisher2911.hmccosmetics.message.MessageHandler;
import io.github.fisher2911.hmccosmetics.message.Messages;
import io.github.fisher2911.hmccosmetics.message.Translation;
import io.github.fisher2911.hmccosmetics.user.UserManager;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.stream.Collectors;
import me.mattstudios.mf.base.CommandManager;
import org.bstats.bukkit.Metrics;
import org.bukkit.Bukkit;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.scheduler.BukkitTask;
public class HMCCosmetics extends JavaPlugin {
public static final Path PLUGIN_FOLDER = Paths.get("plugins", "HMCCosmetics");
private ProtocolManager protocolManager;
private Settings settings;
private UserManager userManager;
private CosmeticManager cosmeticManager;
private MessageHandler messageHandler;
private CosmeticsMenu cosmeticsMenu;
private CommandManager commandManager;
private Database database;
private BukkitTask saveTask;
@Override
public void onEnable() {
final int pluginId = 13873;
final Metrics metrics = new Metrics(this, pluginId);
protocolManager = ProtocolLibrary.getProtocolManager();
this.settings = new Settings(this);
this.messageHandler = new MessageHandler(this);
this.userManager = new UserManager(this);
this.cosmeticManager = new CosmeticManager(new HashMap<>());
this.cosmeticsMenu = new CosmeticsMenu(this);
this.userManager.startTeleportTask();
try {
this.database = DatabaseFactory.create(this);
} catch (final SQLException exception) {
exception.printStackTrace();
}
this.registerCommands();
this.registerListeners();
if (!HookManager.getInstance().isEnabled(ItemsAdderHook.class)) {
this.load();
}
HookManager.getInstance().init();
this.saveTask = Bukkit.getScheduler().runTaskTimerAsynchronously(
this,
() -> Threads.getInstance().execute(
() -> this.database.saveAll()
),
20 * 60,
20 * 60
);
}
@Override
public void onDisable() {
this.saveTask.cancel();
this.database.saveAll();
this.messageHandler.close();
this.userManager.cancelTeleportTask();
this.userManager.removeAll();
Threads.getInstance().onDisable();
this.database.close();
}
private void registerListeners() {
List.of(
new JoinListener(this),
new ClickListener(this),
new TeleportListener(this),
new RespawnListener(this),
new CosmeticFixListener(this),
new PlayerShiftListener(this)
).
forEach(
listener -> this.getServer().getPluginManager()
.registerEvents(listener, this)
);
}
private void registerCommands() {
this.commandManager = new CommandManager(this, true);
this.commandManager.getMessageHandler().register(
"cmd.no.console", player ->
this.messageHandler.sendMessage(
player,
Messages.MUST_BE_PLAYER
)
);
this.commandManager.getCompletionHandler().register("#types",
resolver ->
Arrays.stream(ArmorItem.Type.
values()).
map(ArmorItem.Type::toString).
collect(Collectors.toList())
);
this.commandManager.getCompletionHandler().register("#ids",
resolver ->
this.cosmeticManager.getAll().stream().map(ArmorItem::getId)
.collect(Collectors.toList()));
this.commandManager.register(new CosmeticsCommand(this));
}
public void load() {
Bukkit.getScheduler().runTaskLaterAsynchronously(this,
() -> {
this.settings.load();
this.messageHandler.load();
this.cosmeticsMenu.load();
Translation.getInstance().load();
this.database.load();
}, 1);
}
public void reload() {
Bukkit.getScheduler().runTaskAsynchronously(this,
() -> {
this.settings.load();
this.messageHandler.load();
this.cosmeticsMenu.reload();
Translation.getInstance().load();
});
}
public Settings getSettings() {
return settings;
}
public MessageHandler getMessageHandler() {
return messageHandler;
}
public UserManager getUserManager() {
return userManager;
}
public CosmeticManager getCosmeticManager() {
return cosmeticManager;
}
public CosmeticsMenu getCosmeticsMenu() {
return cosmeticsMenu;
}
public ProtocolManager getProtocolManager() {
return protocolManager;
}
public Database getDatabase() {
return database;
}
}

View File

@@ -0,0 +1,94 @@
package io.github.fisher2911.hmccosmetics.api;
import io.github.fisher2911.hmccosmetics.gui.ArmorItem;
import java.util.ArrayList;
import org.bukkit.Color;
import org.bukkit.Material;
import org.bukkit.inventory.ItemStack;
/**
* Wrapper for ArmorItem used internally for convenience and safety
*/
public class CosmeticItem {
private final ArmorItem armorItem;
public CosmeticItem(final ArmorItem armorItem) {
this.armorItem = armorItem;
}
/**
* @param itemStack the {@link org.bukkit.inventory.ItemStack} display item
* @param id the id of the item
* @param type the cosmetic item type
* @param dyeable whether the item can be dyed
* @param rgb from Bukkit's {@link Color#asRGB()}
*/
public CosmeticItem(final ItemStack itemStack, final String id, final ArmorItem.Type type,
final boolean dyeable, final int rgb) {
this.armorItem = new ArmorItem(itemStack, id, new ArrayList<>(), "", type, dyeable, rgb);
}
/**
* @param material the {@link org.bukkit.Material} display item
* @param id the id of the item
* @param type the cosmetic item type
* @param dyeable whether the item can be dyed
* @param rgb from Bukkit's {@link Color#asRGB()}
*/
public CosmeticItem(final Material material, final String id, final ArmorItem.Type type,
final boolean dyeable, final int rgb) {
this.armorItem = new ArmorItem(material, id, new ArrayList<>(), "", type, dyeable, rgb);
}
/**
* @param itemStack the {@link org.bukkit.inventory.ItemStack} display item
* @param id the id of the item
* @param type the cosmetic item type
*/
public CosmeticItem(final ItemStack itemStack, final String id, final ArmorItem.Type type) {
this(itemStack, id, type, false, -1);
}
/**
* @param material the {@link org.bukkit.Material} display item
* @param id the id of the item
* @param type the cosmetic item type
*/
public CosmeticItem(final Material material, final String id, final ArmorItem.Type type) {
this(material, id, type, false, -1);
}
public ItemStack getColored() {
return this.armorItem.getColored();
}
public ItemStack getItemStack() {
return this.armorItem.getItemStack();
}
public String getId() {
return this.armorItem.getId();
}
public ArmorItem.Type getType() {
return this.armorItem.getType();
}
public boolean isDyeable() {
return this.armorItem.isDyeable();
}
public int getColor() {
return this.armorItem.getDye();
}
public ArmorItem getArmorItem() {
return this.armorItem;
}
}

View File

@@ -0,0 +1,79 @@
package io.github.fisher2911.hmccosmetics.api;
import io.github.fisher2911.hmccosmetics.HMCCosmetics;
import io.github.fisher2911.hmccosmetics.gui.ArmorItem;
import io.github.fisher2911.hmccosmetics.user.User;
import io.github.fisher2911.hmccosmetics.user.UserManager;
import java.util.Optional;
import java.util.UUID;
import org.jetbrains.annotations.Nullable;
public class HMCCosmeticsAPI {
private static final HMCCosmetics plugin;
static {
plugin = HMCCosmetics.getPlugin(HMCCosmetics.class);
}
/**
* This will attempt to get the {@link io.github.fisher2911.hmccosmetics.api.CosmeticItem} that
* the user is wearing. It returns an empty {@link io.github.fisher2911.hmccosmetics.api.CosmeticItem}
* if the user is not found, or if the user is not wearing a cosmetic
*
* @param uuid the uuid of the user
* @param type the type of cosmetic being retrieved
* @return the current cosmetic of the player
*/
public static CosmeticItem getUserCurrentItem(final UUID uuid, final ArmorItem.Type type) {
final Optional<User> userOptional = plugin.getUserManager().get(uuid);
if (userOptional.isEmpty()) {
return new CosmeticItem(ArmorItem.empty(type));
}
return new CosmeticItem(userOptional.get().getPlayerArmor().getItem(type));
}
/**
* @param uuid the uuid of the user whose cosmetic is being set
* @param cosmeticItem the cosmetic being set
* @return true if the cosmetic was set, or else false
*/
public static boolean setCosmeticItem(final UUID uuid, final CosmeticItem cosmeticItem) {
final UserManager userManager = plugin.getUserManager();
final Optional<User> userOptional = userManager.get(uuid);
if (userOptional.isEmpty()) {
return false;
}
userManager.setItem(userOptional.get(), cosmeticItem.getArmorItem());
return true;
}
/**
* @param id the id of the cosmetic item being retrieved
* @return null if the cosmetic was not found, or a copy of the cosmetic item
*/
@Nullable
public static CosmeticItem getCosmeticFromId(final String id) {
final ArmorItem armorItem = plugin.getCosmeticManager().getArmorItem(id);
if (armorItem == null) {
return null;
}
return new CosmeticItem(new ArmorItem(armorItem));
}
/**
* @param uuid the uuid of the user whose armor stand id is being retrieved
* @return the armor stand id, or -1 if the user is not found
*/
public static int getUserArmorStandId(final UUID uuid) {
final Optional<User> userOptional = plugin.getUserManager().get(uuid);
if (userOptional.isEmpty()) {
return -1;
}
return userOptional.get().getArmorStandId();
}
}

View File

@@ -0,0 +1,33 @@
package io.github.fisher2911.hmccosmetics.api.event;
import io.github.fisher2911.hmccosmetics.api.CosmeticItem;
import io.github.fisher2911.hmccosmetics.user.User;
/**
* Called when a user changes their equipped cosmetic
*/
public class CosmeticChangeEvent extends CosmeticItemEvent {
private final User user;
private CosmeticItem removed;
public CosmeticChangeEvent(final CosmeticItem cosmeticItem, final CosmeticItem removed,
final User user) {
super(cosmeticItem);
this.removed = removed;
this.user = user;
}
public User getUser() {
return user;
}
public CosmeticItem getRemoved() {
return removed;
}
public void setRemoved(final CosmeticItem removed) {
this.removed = removed;
}
}

View File

@@ -0,0 +1,47 @@
package io.github.fisher2911.hmccosmetics.api.event;
import io.github.fisher2911.hmccosmetics.api.CosmeticItem;
import org.bukkit.event.Cancellable;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
public abstract class CosmeticItemEvent extends Event implements Cancellable {
private static final HandlerList HANDLERS = new HandlerList();
private CosmeticItem cosmeticItem;
private boolean cancelled;
public CosmeticItemEvent(final CosmeticItem cosmeticItem) {
this.cosmeticItem = cosmeticItem;
this.cancelled = false;
}
public static HandlerList getHandlerList() {
return HANDLERS;
}
public CosmeticItem getCosmeticItem() {
return this.cosmeticItem;
}
public void setCosmeticItem(final CosmeticItem cosmeticItem) {
this.cosmeticItem = cosmeticItem;
}
@Override
public boolean isCancelled() {
return this.cancelled;
}
@Override
public void setCancelled(final boolean cancelled) {
this.cancelled = cancelled;
}
@Override
public HandlerList getHandlers() {
return HANDLERS;
}
}

View File

@@ -0,0 +1,299 @@
package io.github.fisher2911.hmccosmetics.command;
import io.github.fisher2911.hmccosmetics.HMCCosmetics;
import io.github.fisher2911.hmccosmetics.config.Settings;
import io.github.fisher2911.hmccosmetics.config.WardrobeSettings;
import io.github.fisher2911.hmccosmetics.gui.ArmorItem;
import io.github.fisher2911.hmccosmetics.gui.CosmeticsMenu;
import io.github.fisher2911.hmccosmetics.message.Message;
import io.github.fisher2911.hmccosmetics.message.MessageHandler;
import io.github.fisher2911.hmccosmetics.message.Messages;
import io.github.fisher2911.hmccosmetics.message.Placeholder;
import io.github.fisher2911.hmccosmetics.user.User;
import io.github.fisher2911.hmccosmetics.user.UserManager;
import io.github.fisher2911.hmccosmetics.user.Wardrobe;
import io.github.fisher2911.hmccosmetics.util.StringUtils;
import java.util.Map;
import java.util.Optional;
import me.mattstudios.mf.annotations.Command;
import me.mattstudios.mf.annotations.Completion;
import me.mattstudios.mf.annotations.Default;
import me.mattstudios.mf.annotations.Permission;
import me.mattstudios.mf.annotations.SubCommand;
import me.mattstudios.mf.base.CommandBase;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Color;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
@Command("cosmetics")
public class CosmeticsCommand extends CommandBase {
private final HMCCosmetics plugin;
private final UserManager userManager;
private final MessageHandler messageHandler;
private final CosmeticsMenu cosmeticsMenu;
private final Settings settings;
public CosmeticsCommand(final HMCCosmetics plugin) {
this.plugin = plugin;
this.userManager = this.plugin.getUserManager();
this.messageHandler = this.plugin.getMessageHandler();
this.cosmeticsMenu = this.plugin.getCosmeticsMenu();
this.settings = this.plugin.getSettings();
}
@Default
@Permission(io.github.fisher2911.hmccosmetics.message.Permission.DEFAULT_COMMAND)
public void defaultCommand(final Player player) {
final Optional<User> optionalUser = this.userManager.get(player.getUniqueId());
if (optionalUser.isEmpty()) {
this.cosmeticsMenu.openDefault(player);
return;
}
final User user = optionalUser.get();
final Wardrobe wardrobe = user.getWardrobe();
if (wardrobe.isActive() &&
!this.settings.getWardrobeSettings().inDistanceOfWardrobe(wardrobe.getCurrentLocation(), player.getLocation())) {
wardrobe.setActive(false);
wardrobe.despawnFakePlayer(player);
this.messageHandler.sendMessage(
player,
Messages.CLOSED_WARDROBE
);
}
this.cosmeticsMenu.openDefault(player);
}
@SubCommand("reload")
@Permission(io.github.fisher2911.hmccosmetics.message.Permission.RELOAD_COMMAND)
public void reloadCommand(final CommandSender sender) {
Bukkit.getScheduler().runTaskAsynchronously(
this.plugin,
() -> {
this.plugin.reload();
this.messageHandler.sendMessage(
sender,
Messages.RELOADED
);
}
);
}
@SubCommand("dye")
@Permission(io.github.fisher2911.hmccosmetics.message.Permission.DYE_COMMAND)
public void dyeArmor(final Player player, @Completion("#types") String typeString,
final @me.mattstudios.mf.annotations.Optional String dyeColor) {
final Optional<User> optionalUser = this.userManager.get(player.getUniqueId());
if (optionalUser.isEmpty()) {
return;
}
try {
final ArmorItem.Type type = ArmorItem.Type.valueOf(typeString.toUpperCase());
final User user = optionalUser.get();
if (dyeColor == null) {
this.cosmeticsMenu.openDyeSelectorGui(user, type);
return;
}
final ArmorItem armorItem = user.getPlayerArmor().getItem(type);
if (dyeColor != null) {
this.setDyeColor(dyeColor, armorItem, player);
}
this.userManager.setItem(user, armorItem);
this.messageHandler.sendMessage(
player,
Messages.SET_DYE_COLOR,
Map.of(Placeholder.ITEM, StringUtils.formatArmorItemType(typeString))
);
} catch (final IllegalArgumentException exception) {
this.messageHandler.sendMessage(
player,
Messages.INVALID_TYPE);
}
}
@SubCommand("help") // WORK IN PROGRESS (WIP)
@Permission(io.github.fisher2911.hmccosmetics.message.Permission.HELP_COMMAND)
public void helpCommand(final CommandSender sender) {
this.messageHandler.sendMessage(
sender,
Messages.HELP_COMMAND
);
}
@SubCommand("add")
@Permission(io.github.fisher2911.hmccosmetics.message.Permission.SET_COSMETIC_COMMAND)
public void setCommand(
final CommandSender sender,
@Completion("#players") final Player player,
@Completion("#ids") final String id,
final @me.mattstudios.mf.annotations.Optional String dyeColor) {
final Optional<User> userOptional = this.userManager.get(player.getUniqueId());
if (userOptional.isEmpty()) {
this.messageHandler.sendMessage(
sender,
Messages.INVALID_USER
);
return;
}
final User user = userOptional.get();
final ArmorItem armorItem = this.plugin.getCosmeticManager().getArmorItem(id);
if (armorItem == null) {
this.messageHandler.sendMessage(
sender,
Messages.ITEM_NOT_FOUND
);
return;
}
if (dyeColor != null) {
this.setDyeColor(dyeColor, armorItem, player);
}
final Message setMessage = Messages.getSetMessage(armorItem.getType());
final Message setOtherMessage = Messages.getSetOtherMessage(armorItem.getType());
this.userManager.setItem(user, armorItem);
this.messageHandler.sendMessage(
player,
setMessage
);
this.messageHandler.sendMessage(
sender,
setOtherMessage,
Map.of(Placeholder.PLAYER, player.getName(),
Placeholder.TYPE, id)
);
}
@SubCommand("remove")
@Permission(io.github.fisher2911.hmccosmetics.message.Permission.SET_COSMETIC_COMMAND)
public void removeCommand(final CommandSender sender,
@Completion("#players") final Player player, @Completion("#types") String typeString) {
final Optional<User> userOptional = this.userManager.get(player.getUniqueId());
if (userOptional.isEmpty()) {
this.messageHandler.sendMessage(
sender,
Messages.INVALID_USER
);
return;
}
final User user = userOptional.get();
try {
final ArmorItem.Type type = ArmorItem.Type.valueOf(typeString.toUpperCase());
final Message setOtherMessage = Messages.getSetOtherMessage(type);
this.userManager.removeItem(user, type);
this.messageHandler.sendMessage(
sender,
setOtherMessage,
Map.of(Placeholder.PLAYER, player.getName(),
Placeholder.TYPE, "none")
);
} catch (final IllegalArgumentException exception) {
this.messageHandler.sendMessage(player, Messages.INVALID_TYPE);
}
}
@SubCommand("wardrobe")
@Permission(io.github.fisher2911.hmccosmetics.message.Permission.WARDROBE)
public void openWardrobe(Player player, @me.mattstudios.mf.annotations.Optional final Player other) {
if (other != null) {
if (!player.hasPermission(io.github.fisher2911.hmccosmetics.message.Permission.OPEN_OTHER_WARDROBE)) {
this.messageHandler.sendMessage(
player,
Messages.NO_PERMISSION
);
return;
}
this.messageHandler.sendMessage(
player,
Messages.OPENED_OTHER_WARDROBE,
Map.of(Placeholder.PLAYER, other.getName())
);
player = other;
}
final Optional<User> optionalUser = this.plugin.getUserManager().get(player.getUniqueId());
if (optionalUser.isEmpty()) return;
final User user = optionalUser.get();
final Wardrobe wardrobe = user.getWardrobe();
if (wardrobe.isActive()) {
this.messageHandler.sendMessage(
player,
Messages.WARDROBE_ALREADY_OPEN
);
return;
}
final WardrobeSettings settings = this.settings.getWardrobeSettings();
final boolean inDistanceOfStatic = settings.inDistanceOfStatic(player.getLocation());
if (!settings.isPortable() && !inDistanceOfStatic) {
this.messageHandler.sendMessage(
player,
Messages.NOT_NEAR_WARDROBE
);
return;
}
if (settings.isPortable() && !inDistanceOfStatic) {
if (!player.hasPermission(io.github.fisher2911.hmccosmetics.message.Permission.PORTABLE_WARDROBE)) {
this.messageHandler.sendMessage(
player,
Messages.CANNOT_USE_PORTABLE_WARDROBE
);
return;
}
wardrobe.setCurrentLocation(null);
}
wardrobe.setActive(true);
final Player finalPlayer = player;
Bukkit.getScheduler().runTaskAsynchronously(
this.plugin,
() -> wardrobe.spawnFakePlayer(finalPlayer)
);
this.cosmeticsMenu.openDefault(player);
this.messageHandler.sendMessage(
player,
Messages.OPENED_WARDROBE
);
}
private void setDyeColor(final String dyeColor, final ArmorItem armorItem, final CommandSender sender) {
try {
final java.awt.Color awtColor = java.awt.Color.decode(dyeColor);
Color color = Color.fromRGB(awtColor.getRed(), awtColor.getGreen(), awtColor.getBlue());
armorItem.setDye(color.asRGB());
} catch (final NumberFormatException exception) {
this.messageHandler.sendMessage(
sender,
Messages.INVALID_COLOR,
Map.of(Placeholder.ITEM, dyeColor)
);
}
}
}

View File

@@ -0,0 +1,32 @@
package io.github.fisher2911.hmccosmetics.concurrent;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Threads {
private static final Threads INSTANCE;
static {
INSTANCE = new Threads();
}
private final ExecutorService service;
private Threads() {
this.service = Executors.newFixedThreadPool(1);
}
public static Threads getInstance() {
return INSTANCE;
}
public void execute(final Runnable runnable) {
this.service.execute(runnable);
}
public void onDisable() {
this.service.shutdownNow().forEach(Runnable::run);
}
}

View File

@@ -0,0 +1,121 @@
package io.github.fisher2911.hmccosmetics.config;
import com.comphenix.protocol.wrappers.EnumWrappers;
import dev.triumphteam.gui.components.GuiAction;
import io.github.fisher2911.hmccosmetics.HMCCosmetics;
import io.github.fisher2911.hmccosmetics.message.Message;
import io.github.fisher2911.hmccosmetics.util.Utils;
import org.bukkit.entity.Player;
import org.bukkit.event.inventory.ClickType;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.spongepowered.configurate.ConfigurationNode;
import org.spongepowered.configurate.serialize.SerializationException;
import org.spongepowered.configurate.serialize.TypeSerializer;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.function.Consumer;
public class ActionSerializer implements TypeSerializer<GuiAction<InventoryClickEvent>> {
public static final ActionSerializer INSTANCE = new ActionSerializer();
private static final HMCCosmetics plugin;
private static final String OPEN_MENU = "open-menu";
private static final String SEND_MESSAGE = "send-message";
private static final String SOUND = "sound";
private static final String SOUND_NAME = "name";
private static final String SOUND_VOLUME = "volume";
private static final String SOUND_PITCH = "pitch";
private static final String SOUND_CATEGORY = "category";
static {
plugin = HMCCosmetics.getPlugin(HMCCosmetics.class);
}
private ActionSerializer() {
}
private ConfigurationNode nonVirtualNode(final ConfigurationNode source, final Object... path)
throws SerializationException {
if (!source.hasChild(path)) {
throw new SerializationException(
"Required field " + Arrays.toString(path) + " was not present in node");
}
return source.node(path);
}
@Override
public GuiAction<InventoryClickEvent> deserialize(final Type type, final ConfigurationNode source) {
final var children = source.childrenMap();
final List<Consumer<InventoryClickEvent>> consumers = new ArrayList<>();
for (final var entry : children.entrySet()) {
final String clickType = entry.getKey().toString();
if (clickType == null) continue;
consumers.add(this.parseAction(entry.getValue(), clickType.toUpperCase(Locale.ROOT)));
}
return event -> {
for (final Consumer<InventoryClickEvent> consumer : consumers) {
consumer.accept(event);
}
};
}
private Consumer<InventoryClickEvent> parseAction(final ConfigurationNode node, final String clickType) {
final ConfigurationNode openMenuNode = node.node(OPEN_MENU);
final ConfigurationNode sendMessageNode = node.node(SEND_MESSAGE);
final ConfigurationNode soundNode = node.node(SOUND);
final ConfigurationNode soundNameNode = soundNode.node(SOUND_NAME);
final ConfigurationNode volumeNode = soundNode.node(SOUND_VOLUME);
final ConfigurationNode pitchNode = soundNode.node(SOUND_PITCH);
final ConfigurationNode categoryNode = soundNode.node(SOUND_CATEGORY);
final String openMenu = openMenuNode.getString();
final String sendMessage = sendMessageNode.getString();
final SoundData soundData;
final String soundName = soundNameNode.getString();
final String category = categoryNode.getString();
final int volume = volumeNode.getInt();
final int pitch = pitchNode.getInt();
if (soundName == null || category == null) {
soundData = null;
} else {
soundData = new SoundData(
soundName,
EnumWrappers.SoundCategory.valueOf(category),
volume,
pitch
);
}
final ClickType click = Utils.stringToEnum(clickType, ClickType.class, ClickType.UNKNOWN);
return event -> {
if (click != ClickType.UNKNOWN && event.getClick() != click) return;
if (!(event.getWhoClicked() instanceof final Player player)) return;
if (soundData != null) {
soundData.play(player);
}
if (sendMessage != null) plugin.getMessageHandler().sendMessage(
player,
new Message("", sendMessage)
);
if (openMenu != null) plugin.getCosmeticsMenu().openMenu(openMenu, event.getWhoClicked());
};
}
@Override
public void serialize(final Type type, @Nullable final GuiAction<InventoryClickEvent> obj, final ConfigurationNode node) throws SerializationException {
}
}

View File

@@ -0,0 +1,52 @@
package io.github.fisher2911.hmccosmetics.config;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.inventory.EquipmentSlot;
import org.spongepowered.configurate.objectmapping.ConfigSerializable;
@ConfigSerializable
public class CosmeticSettings {
private static final transient String COSMETIC_SETTINGS_PATH = "cosmetic-settings";
private static final transient String REQUIRE_EMPTY_HELMET_PATH = "require-empty-helmet";
private static final transient String REQUIRE_EMPTY_OFF_HAND_PATH = "require-empty-off-hand";
private static final transient String LOOK_DOWN_PITCH_PATH = "look-down-backpack-remove";
private boolean requireEmptyHelmet;
private boolean requireEmptyOffHand;
private int lookDownPitch;
public void load(final FileConfiguration config) {
this.requireEmptyHelmet = config.getBoolean(COSMETIC_SETTINGS_PATH + "." + REQUIRE_EMPTY_HELMET_PATH);
this.requireEmptyOffHand = config.getBoolean(COSMETIC_SETTINGS_PATH + "." + REQUIRE_EMPTY_OFF_HAND_PATH);
this.lookDownPitch = config.getInt(COSMETIC_SETTINGS_PATH + "." + LOOK_DOWN_PITCH_PATH);
}
public boolean isRequireEmptyHelmet() {
return requireEmptyHelmet;
}
public boolean isRequireEmptyOffHand() {
return requireEmptyOffHand;
}
public int getLookDownPitch() {
return lookDownPitch;
}
public void setRequireEmptyHelmet(final boolean requireEmptyHelmet) {
this.requireEmptyHelmet = requireEmptyHelmet;
}
public void setRequireEmptyOffHand(final boolean requireEmptyOffHand) {
this.requireEmptyOffHand = requireEmptyOffHand;
}
public boolean requireEmpty(final EquipmentSlot slot) {
return switch (slot) {
case OFF_HAND -> this.isRequireEmptyOffHand();
case HEAD -> this.isRequireEmptyHelmet();
default -> false;
};
}
}

View File

@@ -1,61 +1,66 @@
package io.github.fisher2911.hmccosmetics.config;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import dev.triumphteam.gui.guis.GuiItem;
import io.github.fisher2911.hmccosmetics.HMCCosmetics;
import io.github.fisher2911.hmccosmetics.gui.ArmorItem;
import io.github.fisher2911.hmccosmetics.gui.ColorItem;
import io.github.fisher2911.hmccosmetics.gui.DyeSelectorGui;
import io.github.fisher2911.hmccosmetics.message.Adventure;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import org.bukkit.Color;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.spongepowered.configurate.ConfigurationNode;
import org.spongepowered.configurate.serialize.SerializationException;
import org.spongepowered.configurate.serialize.TypeSerializer;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
public class DyeGuiSerializer implements TypeSerializer<DyeSelectorGui> {
private static final HMCCosmetics plugin;
static {
plugin = HMCCosmetics.getPlugin(HMCCosmetics.class);
}
public static final DyeGuiSerializer INSTANCE = new DyeGuiSerializer();
private DyeGuiSerializer() {}
private static final HMCCosmetics plugin;
private static final String TITLE = "title";
private static final String ROWS = "rows";
private static final String ITEMS = "items";
private static final String COSMETICS_SLOTS = "cosmetics-slots";
private static final String SET_COLOR = "set-color";
private static final String RED = "red";
private static final String GREEN = "green";
private static final String BLUE = "blue";
private ConfigurationNode nonVirtualNode(final ConfigurationNode source, final Object... path) throws SerializationException {
static {
plugin = HMCCosmetics.getPlugin(HMCCosmetics.class);
}
private DyeGuiSerializer() {
}
private ConfigurationNode nonVirtualNode(final ConfigurationNode source, final Object... path)
throws SerializationException {
if (!source.hasChild(path)) {
throw new SerializationException("Required field " + Arrays.toString(path) + " was not present in node");
throw new SerializationException(
"Required field " + Arrays.toString(path) + " was not present in node");
}
return source.node(path);
}
@Override
public DyeSelectorGui deserialize(final Type type, final ConfigurationNode source) throws SerializationException {
public DyeSelectorGui deserialize(final Type type, final ConfigurationNode source)
throws SerializationException {
final ConfigurationNode titleNode = this.nonVirtualNode(source, TITLE);
final ConfigurationNode rowsNode = this.nonVirtualNode(source, ROWS);
final ConfigurationNode itemsNode = source.node(ITEMS);
final ConfigurationNode cosmeticSlotsNode = source.node(COSMETICS_SLOTS);
final Map<Integer, GuiItem> guiItemMap = new HashMap<>();
final var map = itemsNode.childrenMap();
final var itemMap = itemsNode.childrenMap();
final var cosmeticSlotsMap = cosmeticSlotsNode.childrenMap();
for (final var entry : map.entrySet()) {
for (final var entry : itemMap.entrySet()) {
if (!(entry.getKey() instanceof final Integer slot)) {
continue;
}
@@ -78,19 +83,50 @@ public class DyeGuiSerializer implements TypeSerializer<DyeSelectorGui> {
final int green = colorNode.node(GREEN).getInt();
final int blue = colorNode.node(BLUE).getInt();
guiItemMap.put(slot, new ColorItem(guiItem.getItemStack(), Color.fromRGB(red, green, blue)));
guiItemMap.put(slot,
new ColorItem(guiItem.getItemStack(), Color.fromRGB(red, green, blue)));
}
final BiMap<Integer, ArmorItem.Type> cosmeticSlots = HashBiMap.create();
int selectedCosmetic = -1;
for (final var entry : cosmeticSlotsMap.entrySet()) {
if (!(entry.getKey() instanceof final Integer slot)) {
continue;
}
selectedCosmetic = selectedCosmetic == -1 ? slot : selectedCosmetic;
final var node = entry.getValue();
final String typeStr = node.getString();
try {
final ArmorItem.Type itemType = ArmorItem.Type.valueOf(typeStr);
cosmeticSlots.put(slot, itemType);
} catch (final IllegalArgumentException | NullPointerException exception) {
plugin.getLogger().severe(typeStr + " is not a valid ArmorItem type in DyeGui!");
}
}
String title = titleNode.getString();
if (title == null) {
title = "";
}
return new DyeSelectorGui(
plugin,
Adventure.SERIALIZER.serialize(
Adventure.MINI_MESSAGE.parse(titleNode.getString())),
Adventure.MINI_MESSAGE.deserialize(title)),
rowsNode.getInt(),
guiItemMap);
guiItemMap,
cosmeticSlots,
selectedCosmetic);
}
@Override
public void serialize(final Type type, @Nullable final DyeSelectorGui obj, final ConfigurationNode node) throws SerializationException {
}
}

View File

@@ -4,46 +4,47 @@ import dev.triumphteam.gui.guis.GuiItem;
import io.github.fisher2911.hmccosmetics.HMCCosmetics;
import io.github.fisher2911.hmccosmetics.gui.CosmeticGui;
import io.github.fisher2911.hmccosmetics.message.Adventure;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.spongepowered.configurate.ConfigurationNode;
import org.spongepowered.configurate.serialize.SerializationException;
import org.spongepowered.configurate.serialize.TypeSerializer;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
public class GuiSerializer implements TypeSerializer<CosmeticGui> {
public static final GuiSerializer INSTANCE = new GuiSerializer();
private static final HMCCosmetics plugin;
private static final String TITLE = "title";
private static final String ROWS = "rows";
private static final String ITEMS = "items";
static {
plugin = HMCCosmetics.getPlugin(HMCCosmetics.class);
}
public static final GuiSerializer INSTANCE = new GuiSerializer();
private GuiSerializer() {
}
private GuiSerializer() {}
private static final String TITLE = "title";
private static final String ROWS = "rows";
private static final String ITEMS = "items";
private ConfigurationNode nonVirtualNode(final ConfigurationNode source, final Object... path) throws SerializationException {
private ConfigurationNode nonVirtualNode(final ConfigurationNode source, final Object... path)
throws SerializationException {
if (!source.hasChild(path)) {
throw new SerializationException("Required field " + Arrays.toString(path) + " was not present in node");
throw new SerializationException(
"Required field " + Arrays.toString(path) + " was not present in node");
}
return source.node(path);
}
@Override
public CosmeticGui deserialize(final Type type, final ConfigurationNode source) throws SerializationException {
public CosmeticGui deserialize(final Type type, final ConfigurationNode source)
throws SerializationException {
final ConfigurationNode titleNode = this.nonVirtualNode(source, TITLE);
final ConfigurationNode rowsNode = this.nonVirtualNode(source, ROWS);
final ConfigurationNode itemsNode = source.node(ITEMS);
final var childrenMap = source.node(ITEMS).childrenMap();
final var childrenMap = itemsNode.childrenMap();
final Map<Integer, GuiItem> guiItemMap = new HashMap<>();
@@ -60,9 +61,15 @@ public class GuiSerializer implements TypeSerializer<CosmeticGui> {
guiItemMap.put(slot, guiItem);
}
String title = titleNode.getString();
if (title == null) {
title = "";
}
return new CosmeticGui(plugin,
Adventure.SERIALIZER.serialize(
Adventure.MINI_MESSAGE.parse(titleNode.getString())),
Adventure.MINI_MESSAGE.deserialize(title)),
rowsNode.getInt(),
guiItemMap);
}
@@ -71,4 +78,5 @@ public class GuiSerializer implements TypeSerializer<CosmeticGui> {
public void serialize(final Type type, @Nullable final CosmeticGui obj, final ConfigurationNode node) throws SerializationException {
}
}

View File

@@ -1,30 +1,16 @@
package io.github.fisher2911.hmccosmetics.config;
import dev.triumphteam.gui.components.GuiAction;
import dev.triumphteam.gui.guis.GuiItem;
import io.github.fisher2911.hmccosmetics.HMCCosmetics;
import io.github.fisher2911.hmccosmetics.gui.ArmorItem;
import io.github.fisher2911.hmccosmetics.message.Adventure;
import io.github.fisher2911.hmccosmetics.hook.HookManager;
import io.github.fisher2911.hmccosmetics.util.Keys;
import io.github.fisher2911.hmccosmetics.util.StringUtils;
import io.github.fisher2911.hmccosmetics.util.Utils;
import io.github.fisher2911.hmccosmetics.util.builder.ItemBuilder;
import io.github.fisher2911.hmccosmetics.util.builder.ColorBuilder;
import io.github.fisher2911.hmccosmetics.util.builder.ItemBuilder;
import io.github.fisher2911.hmccosmetics.util.builder.SkullBuilder;
import net.kyori.adventure.text.Component;
import org.bukkit.Bukkit;
import org.bukkit.Color;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.OfflinePlayer;
import org.bukkit.Registry;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.inventory.ItemFlag;
import org.bukkit.inventory.ItemStack;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.spongepowered.configurate.ConfigurationNode;
import org.spongepowered.configurate.serialize.SerializationException;
import org.spongepowered.configurate.serialize.TypeSerializer;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
@@ -33,11 +19,25 @@ import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import org.bukkit.Bukkit;
import org.bukkit.Color;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.OfflinePlayer;
import org.bukkit.Registry;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.inventory.ItemFlag;
import org.bukkit.inventory.ItemStack;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.spongepowered.configurate.ConfigurationNode;
import org.spongepowered.configurate.serialize.SerializationException;
import org.spongepowered.configurate.serialize.TypeSerializer;
public class ItemSerializer implements TypeSerializer<GuiItem> {
public static final ItemSerializer INSTANCE = new ItemSerializer();
private static final HMCCosmetics plugin;
private static final String MATERIAL = "material";
private static final String AMOUNT = "amount";
private static final String NAME = "name";
@@ -56,23 +56,30 @@ public class ItemSerializer implements TypeSerializer<GuiItem> {
private static final String BLUE = "blue";
private static final String PERMISSION = "permission";
private static final String TYPE = "type";
private static final String OPEN_MENU = "open-menu";
private static final String ACTION = "action";
private static final String ID = "id";
private static final String DYEABLE = "dyeable";
static {
plugin = HMCCosmetics.getPlugin(HMCCosmetics.class);
}
private ItemSerializer() {
}
private ConfigurationNode nonVirtualNode(final ConfigurationNode source, final Object... path) throws SerializationException {
private ConfigurationNode nonVirtualNode(final ConfigurationNode source, final Object... path)
throws SerializationException {
if (!source.hasChild(path)) {
throw new SerializationException("Required field " + Arrays.toString(path) + " was not present in node");
throw new SerializationException(
"Required field " + Arrays.toString(path) + " was not present in node");
}
return source.node(path);
}
@Override
public GuiItem deserialize(final Type type, final ConfigurationNode source) throws SerializationException {
public GuiItem deserialize(final Type type, final ConfigurationNode source)
throws SerializationException {
final ConfigurationNode materialNode = this.nonVirtualNode(source, MATERIAL);
final ConfigurationNode amountNode = source.node(AMOUNT);
final ConfigurationNode nameNode = source.node(NAME);
@@ -91,31 +98,41 @@ public class ItemSerializer implements TypeSerializer<GuiItem> {
final ConfigurationNode blueNode = colorNode.node(BLUE);
final ConfigurationNode permissionNode = source.node(PERMISSION);
final ConfigurationNode typeNode = source.node(TYPE);
final ConfigurationNode openMenuNode = source.node(OPEN_MENU);
final ConfigurationNode actionNode = source.node(ACTION);
final ConfigurationNode idNode = source.node(ID);
final ConfigurationNode dyeableNode = source.node(DYEABLE);
final Material material = Utils.stringToEnum(Utils.replaceIfNull(materialNode.getString(), ""),
Material.class, Material.AIR);
final String materialString = Utils.replaceIfNull(materialNode.getString(), "");
final int amount = amountNode.getInt();
final Component name = StringUtils.parse(nameNode.getString());
// Adventure.MINI_MESSAGE.parse(
// Utils.replaceIfNull(nameNode.getString(), "")
// );
ItemStack itemStack;
try {
itemStack = new ItemStack(Material.valueOf(materialString), amount);
} catch (final IllegalArgumentException exception) {
itemStack = HookManager.getInstance().getItemHooks().getItemStack(materialString);
if (itemStack == null) {
itemStack = new ItemStack(Material.AIR);
}
}
final String name = StringUtils.parseStringToString(nameNode.getString());
final boolean unbreakable = unbreakableNode.getBoolean();
final boolean glowing = glowingNode.getBoolean();
final List<String> lore = Utils.replaceIfNull(loreNode.getList(String.class), new ArrayList<String>()).
final List<String> lore = Utils.replaceIfNull(loreNode.getList(String.class),
new ArrayList<String>()).
stream().map(StringUtils::parseStringToString).collect(Collectors.toList());
final List<String> lockedLore = Utils.replaceIfNull(lockedLoreNode.getList(String.class), new ArrayList<String>()).
final List<String> lockedLore = Utils.replaceIfNull(lockedLoreNode.getList(String.class),
new ArrayList<String>()).
stream().map(StringUtils::parseStringToString).collect(Collectors.toList());
final int modelData = modelDataNode.getInt();
final Set<ItemFlag> itemFlags = Utils.replaceIfNull(itemFlagsNode.getList(String.class), new ArrayList<String>()).
final Set<ItemFlag> itemFlags = Utils.replaceIfNull(itemFlagsNode.getList(String.class),
new ArrayList<String>()).
stream().map(flag -> {
try {
return ItemFlag.valueOf(flag.toUpperCase());
@@ -133,7 +150,7 @@ public class ItemSerializer implements TypeSerializer<GuiItem> {
if (colorNode.virtual()) {
color = null;
} else {
color = Color.fromBGR(redNode.getInt(), greenNode.getInt(), blueNode.getInt());
color = Color.fromRGB(redNode.getInt(), greenNode.getInt(), blueNode.getInt());
}
final Map<Enchantment, Integer> enchantments =
@@ -146,9 +163,10 @@ public class ItemSerializer implements TypeSerializer<GuiItem> {
return null;
}
final NamespacedKey namespacedKey = NamespacedKey.minecraft(enchantmentString.
split(":")[0].
toLowerCase());
final NamespacedKey namespacedKey = NamespacedKey.minecraft(
enchantmentString.
split(":")[0].
toLowerCase());
return Registry.ENCHANTMENT.get(namespacedKey);
}, enchantmentString -> {
@@ -162,12 +180,10 @@ public class ItemSerializer implements TypeSerializer<GuiItem> {
}
}));
final ItemBuilder itemBuilder;
if (material == Material.PLAYER_HEAD) {
itemBuilder = SkullBuilder.
create();
if (itemStack.getType() == Material.PLAYER_HEAD) {
itemBuilder = SkullBuilder.create();
if (texture != null) {
((SkullBuilder) itemBuilder).texture(texture);
@@ -175,28 +191,30 @@ public class ItemSerializer implements TypeSerializer<GuiItem> {
final OfflinePlayer player = Bukkit.getOfflinePlayer(owner);
((SkullBuilder) itemBuilder).owner(player);
}
} else if (ColorBuilder.canBeColored(material)) {
itemBuilder = ColorBuilder.from(material);
} else if (ColorBuilder.canBeColored(itemStack)) {
itemBuilder = ColorBuilder.from(itemStack);
if (color != null) {
((ColorBuilder) itemBuilder).color(color);
}
} else {
itemBuilder = ItemBuilder.from(material);
itemBuilder = ItemBuilder.from(itemStack);
}
final ItemStack itemStack = itemBuilder.
if (itemStack.getItemMeta() != null && !itemStack.getItemMeta().hasCustomModelData()) {
itemBuilder.modelData(modelData);
}
itemStack = itemBuilder.
amount(amount).
name(name).
unbreakable(unbreakable).
glow(glowing).
lore(lore).
modelData(modelData).
enchants(enchantments, true).
itemFlags(itemFlags).
build();
final String openMenu = openMenuNode.getString(
Utils.replaceIfNull(OPEN_MENU, ""));
final GuiAction<InventoryClickEvent> action = ActionSerializer.INSTANCE.deserialize(GuiAction.class, actionNode);
Keys.setKey(itemStack);
@@ -211,23 +229,19 @@ public class ItemSerializer implements TypeSerializer<GuiItem> {
return new ArmorItem(
itemStack,
event -> {
final HMCCosmetics plugin = HMCCosmetics.getPlugin(HMCCosmetics.class);
plugin.getCosmeticsMenu().openMenu(openMenu, event.getWhoClicked());
},
action,
Utils.replaceIfNull(idNode.getString(), ""),
lockedLore,
permission,
cosmeticType,
dyeable);
dyeable,
-1
);
} catch (final IllegalArgumentException exception) {
return dev.triumphteam.gui.builder.item.ItemBuilder.from(
itemStack).
asGuiItem(event -> {
final HMCCosmetics plugin = HMCCosmetics.getPlugin(HMCCosmetics.class);
plugin.getCosmeticsMenu().openMenu(openMenu, event.getWhoClicked());
});
final GuiItem guiItem = dev.triumphteam.gui.builder.item.ItemBuilder.from(itemStack).asGuiItem();
guiItem.setAction(action);
return guiItem;
}
}
@@ -235,4 +249,5 @@ public class ItemSerializer implements TypeSerializer<GuiItem> {
public void serialize(final Type type, @Nullable final GuiItem obj, final ConfigurationNode node) throws SerializationException {
}
}

View File

@@ -0,0 +1,31 @@
package io.github.fisher2911.hmccosmetics.config;
import io.github.fisher2911.hmccosmetics.HMCCosmetics;
public class Settings {
private final HMCCosmetics plugin;
private final CosmeticSettings cosmeticSettings;
private final WardrobeSettings wardrobeSettings;
public Settings(final HMCCosmetics plugin) {
this.plugin = plugin;
this.cosmeticSettings = new CosmeticSettings();
this.wardrobeSettings = new WardrobeSettings(this.plugin);
}
public void load() {
this.plugin.saveDefaultConfig();
this.plugin.reloadConfig();
this.cosmeticSettings.load(this.plugin.getConfig());
this.wardrobeSettings.load();
}
public CosmeticSettings getCosmeticSettings() {
return cosmeticSettings;
}
public WardrobeSettings getWardrobeSettings() {
return wardrobeSettings;
}
}

View File

@@ -0,0 +1,61 @@
package io.github.fisher2911.hmccosmetics.config;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.wrappers.EnumWrappers;
import com.comphenix.protocol.wrappers.MinecraftKey;
import io.github.fisher2911.hmccosmetics.packet.PacketManager;
import org.bukkit.entity.Player;
public class SoundData {
private final String name;
private final EnumWrappers.SoundCategory soundCategory;
private final float volume;
private final float pitch;
public SoundData(final String name, final EnumWrappers.SoundCategory soundCategory, final float volume, final float pitch) {
this.name = name;
this.soundCategory = soundCategory;
this.volume = volume;
this.pitch = pitch;
}
public String getName() {
return name;
}
public float getVolume() {
return volume;
}
public float getPitch() {
return pitch;
}
public EnumWrappers.SoundCategory getSoundCategory() {
return soundCategory;
}
public void play(final Player player) {
final PacketContainer soundPacket = PacketManager.getSoundPacket(
player,
player.getLocation(),
this.getKey(this.name),
this.volume,
this.pitch,
this.soundCategory
);
PacketManager.sendPacket(player, soundPacket);
}
private MinecraftKey getKey(final String string) {
if (!string.contains(":")) {
return new MinecraftKey(string);
}
final String[] parts = string.split(":");
return new MinecraftKey(parts[0], parts[1]);
}
}

View File

@@ -0,0 +1,104 @@
package io.github.fisher2911.hmccosmetics.config;
import io.github.fisher2911.hmccosmetics.HMCCosmetics;
import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.FileConfiguration;
import org.checkerframework.checker.units.qual.A;
import org.jetbrains.annotations.Nullable;
import org.bukkit.Location;
public class WardrobeSettings {
private static final String WARDROBE_PATH = "wardrobe";
private static final String DISABLE_ON_DAMAGE_PATH = WARDROBE_PATH + ".disable-on-damage";
private static final String DISPLAY_RADIUS_PATH = WARDROBE_PATH + ".display-radius";
private static final String PORTABLE_PATH = WARDROBE_PATH + ".portable";
private static final String ALWAYS_DISPLAY_PATH = WARDROBE_PATH + ".always-display";
private static final String STATIC_RADIUS_PATH = WARDROBE_PATH + ".static-radius";
private static final String STATIC_LOCATION_PATH = WARDROBE_PATH + ".wardrobe-location";
private static final String WORLD_PATH = "world";
private static final String X_PATH = "x";
private static final String Y_PATH = "y";
private static final String Z_PATH = "z";
private static final String YAW_PATH = "yaw";
private static final String PITCH_PATH = "pitch";
private final HMCCosmetics plugin;
private boolean disableOnDamage;
private int displayRadius;
private boolean portable;
private boolean alwaysDisplay;
private int staticRadius;
private Location location;
public WardrobeSettings(final HMCCosmetics plugin) {
this.plugin = plugin;
}
public void load() {
final FileConfiguration config = this.plugin.getConfig();
this.disableOnDamage = config.getBoolean(DISABLE_ON_DAMAGE_PATH);
this.displayRadius = config.getInt(DISPLAY_RADIUS_PATH);
this.portable = config.getBoolean(PORTABLE_PATH);
this.staticRadius = config.getInt(STATIC_RADIUS_PATH);
this.alwaysDisplay = config.getBoolean(ALWAYS_DISPLAY_PATH);
final ConfigurationSection locationSection = config.getConfigurationSection(STATIC_LOCATION_PATH);
if (locationSection == null) return;
this.location = this.loadLocation(locationSection);
}
@Nullable
private Location loadLocation(final ConfigurationSection section) {
final String worldName = section.getString(WORLD_PATH);
final double x = section.getDouble(X_PATH);
final double y = section.getDouble(Y_PATH);
final double z = section.getDouble(Z_PATH);
final float yaw = (float) section.getDouble(YAW_PATH);
final float pitch = (float) section.getDouble(PITCH_PATH);
if (worldName == null || worldName.isBlank()) return null;
final World world = Bukkit.getWorld(worldName);
if (world == null) return null;
return new Location(world, x, y, z, yaw, pitch);
}
public boolean getDisableOnDamage() {
return disableOnDamage;
}
public int getDisplayRadius() {
return displayRadius;
}
public boolean isPortable() {
return portable;
}
public boolean isAlwaysDisplay() {
return alwaysDisplay;
}
public int getStaticRadius() {
return staticRadius;
}
public Location getLocation() {
return location.clone();
}
public boolean inDistanceOfWardrobe(final Location wardrobeLocation, final Location playerLocation) {
if (this.displayRadius == -1) return true;
return playerLocation.distanceSquared(wardrobeLocation) <= this.displayRadius * this.displayRadius;
}
public boolean inDistanceOfStatic(final Location location) {
if (this.location == null) return false;
if (this.staticRadius == -1) return false;
return this.location.distanceSquared(location) <= this.staticRadius * this.staticRadius;
}
}

View File

@@ -0,0 +1,33 @@
package io.github.fisher2911.hmccosmetics.cosmetic;
import io.github.fisher2911.hmccosmetics.gui.ArmorItem;
import java.util.Collection;
import java.util.Map;
import org.jetbrains.annotations.Nullable;
public class CosmeticManager {
private final Map<String, ArmorItem> armorItemMap;
public CosmeticManager(final Map<String, ArmorItem> armorItemMap) {
this.armorItemMap = armorItemMap;
}
@Nullable
public ArmorItem getArmorItem(final String id) {
return this.armorItemMap.get(id);
}
public void addArmorItem(final ArmorItem armorItem) {
this.armorItemMap.put(armorItem.getId(), armorItem);
}
public Collection<ArmorItem> getAll() {
return this.armorItemMap.values();
}
public Map<String, ArmorItem> getArmorItemMap() {
return armorItemMap;
}
}

View File

@@ -0,0 +1,169 @@
package io.github.fisher2911.hmccosmetics.database;
import com.j256.ormlite.dao.Dao;
import com.j256.ormlite.dao.DaoManager;
import com.j256.ormlite.support.ConnectionSource;
import com.j256.ormlite.table.TableUtils;
import io.github.fisher2911.hmccosmetics.HMCCosmetics;
import io.github.fisher2911.hmccosmetics.concurrent.Threads;
import io.github.fisher2911.hmccosmetics.database.dao.ArmorItemDAO;
import io.github.fisher2911.hmccosmetics.database.dao.UserDAO;
import io.github.fisher2911.hmccosmetics.gui.ArmorItem;
import io.github.fisher2911.hmccosmetics.inventory.PlayerArmor;
import io.github.fisher2911.hmccosmetics.user.User;
import java.sql.SQLException;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
import io.github.fisher2911.hmccosmetics.user.Wardrobe;
import org.bukkit.Bukkit;
public class Database {
protected final HMCCosmetics plugin;
final Dao<UserDAO, UUID> userDao;
final Dao<ArmorItemDAO, UUID> armorItemDao;
private final ConnectionSource dataSource;
private final DatabaseType databaseType;
AtomicInteger FAKE_ENTITY_ID = new AtomicInteger(Integer.MAX_VALUE);
String TABLE_NAME = "user";
String PLAYER_UUID_COLUMN = "uuid";
String BACKPACK_COLUMN = "backpack";
String HAT_COLUMN = "hat";
String DYE_COLOR_COLUMN = "dye";
String CREATE_TABLE_STATEMENT =
"CREATE TABLE IF NOT EXISTS " + TABLE_NAME + " (" +
PLAYER_UUID_COLUMN + " CHAR(36), " +
BACKPACK_COLUMN + " CHAR(50), " +
HAT_COLUMN + " CHAR(50), " +
DYE_COLOR_COLUMN + " INT, " +
"UNIQUE (" +
PLAYER_UUID_COLUMN +
"))";
public Database(
final HMCCosmetics plugin,
final ConnectionSource dataSource,
final DatabaseType databaseType) throws SQLException {
this.plugin = plugin;
this.dataSource = dataSource;
this.userDao = DaoManager.createDao(this.dataSource, UserDAO.class);
this.armorItemDao = DaoManager.createDao(this.dataSource, ArmorItemDAO.class);
this.databaseType = databaseType;
}
public void load() {
Threads.getInstance().execute(() -> new DatabaseConverter(this.plugin, this).convert());
}
protected void createTables() {
try {
TableUtils.createTableIfNotExists(this.dataSource, ArmorItemDAO.class);
TableUtils.createTableIfNotExists(this.dataSource, UserDAO.class);
} catch (final SQLException exception) {
exception.printStackTrace();
}
}
public void loadUser(final UUID uuid, final Consumer<User> onComplete) {
final int armorStandId = FAKE_ENTITY_ID.getAndDecrement();
final Wardrobe wardrobe = this.createNewWardrobe(uuid);
Threads.getInstance().execute(
() -> {
try {
UserDAO user = this.userDao.queryForId(uuid);
if (user == null) {
user = this.userDao.createIfNotExists(new UserDAO(uuid));
}
final List<ArmorItemDAO> armorItems = this.armorItemDao.queryForEq("uuid",
uuid.toString());
final User actualUser = user.toUser(
this.plugin.getCosmeticManager(),
armorItems,
wardrobe,
armorStandId);
Bukkit.getScheduler().runTask(this.plugin,
() -> {
this.plugin.getUserManager().add(
actualUser
);
onComplete.accept(actualUser);
}
);
} catch (final SQLException exception) {
exception.printStackTrace();
}
});
final User user = new User(uuid, PlayerArmor.empty(), wardrobe, armorStandId);
this.plugin.getUserManager().add(user);
onComplete.accept(user);
}
public void saveUser(final User user) {
try {
final UserDAO userDAO = new UserDAO(user.getUuid());
this.userDao.createOrUpdate(userDAO);
final String uuid = user.getUuid().toString();
for (final ArmorItem armorItem : user.getPlayerArmor().getArmorItems()) {
final ArmorItemDAO dao = ArmorItemDAO.fromArmorItem(armorItem);
dao.setUuid(uuid);
this.armorItemDao.createOrUpdate(dao);
}
} catch (final SQLException exception) {
exception.printStackTrace();
}
}
public void saveAll() {
for (final User user : this.plugin.getUserManager().getAll()) {
this.saveUser(user);
}
}
public void close() {
try {
this.dataSource.close();
} catch (final Exception exception) {
exception.printStackTrace();
}
}
protected ConnectionSource getDataSource() {
return dataSource;
}
public DatabaseType getDatabaseType() {
return databaseType;
}
public Dao<UserDAO, UUID> getUserDao() {
return userDao;
}
public Dao<ArmorItemDAO, UUID> getArmorItemDao() {
return armorItemDao;
}
public Wardrobe createNewWardrobe(final UUID ownerUUID) {
return new Wardrobe(
this.plugin,
UUID.randomUUID(),
ownerUUID,
PlayerArmor.empty(),
FAKE_ENTITY_ID.getAndDecrement(),
FAKE_ENTITY_ID.getAndDecrement(),
false
);
}
}

View File

@@ -0,0 +1,132 @@
package io.github.fisher2911.hmccosmetics.database;
import io.github.fisher2911.hmccosmetics.HMCCosmetics;
import io.github.fisher2911.hmccosmetics.gui.ArmorItem;
import io.github.fisher2911.hmccosmetics.inventory.PlayerArmor;
import io.github.fisher2911.hmccosmetics.user.User;
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;
import org.bukkit.configuration.file.YamlConfiguration;
import javax.swing.text.DateFormatter;
public class DatabaseConverter {
private static final int CURRENT_VERSION = 2;
private static final String FILE_NAME = "info.yml";
private final HMCCosmetics plugin;
private final Database database;
public DatabaseConverter(final HMCCosmetics plugin, final Database database) {
this.database = database;
this.plugin = plugin;
}
public void convert() {
final File folder = new File(this.plugin.getDataFolder(), "database");
final File file = Path.of(
folder.getPath(),
FILE_NAME
).toFile();
if (!file.exists()) {
this.plugin.saveResource("database" + File.separator + FILE_NAME, true);
}
final YamlConfiguration config = YamlConfiguration.loadConfiguration(file);
final int version = config.getInt("version") == 0 ? 1 : config.getInt("version");
final Set<User> users = new HashSet<>();
this.convert(version, users::add);
try {
config.set("version", CURRENT_VERSION);
config.save(file);
} catch (final IOException exception) {
exception.printStackTrace();
}
this.database.createTables();
for (final User user : users) {
database.saveUser(user);
}
}
private void convert(final int version, final Consumer<User> consumer) {
switch (version) {
case 1 -> this.convertVersionOne(consumer);
}
}
private void convertVersionOne(final Consumer<User> consumer) {
final String query = "SELECT * from user";
try (final PreparedStatement statement = this.database.getDataSource()
.getReadOnlyConnection("user").
getUnderlyingConnection().prepareStatement(query)) {
final ResultSet results = statement.executeQuery();
try {
final Map<String, ArmorItem> armorItems = new ConcurrentHashMap<>(
this.plugin.getCosmeticManager().getArmorItemMap());
while (results.next()) {
final PlayerArmor playerArmor = PlayerArmor.empty();
final UUID uuid = UUID.fromString(results.getString(1));
final User user = new User(
uuid,
playerArmor,
this.database.createNewWardrobe(uuid),
this.database.FAKE_ENTITY_ID.getAndDecrement()
);
final String backpackId = results.getString(2);
final String hatId = results.getString(3);
final int hatDye = results.getInt(4);
final ArmorItem backpack = armorItems.get(backpackId);
final ArmorItem hat = armorItems.get(hatId);
if (backpack != null) {
playerArmor.setItem(backpack);
}
if (hat != null) {
hat.setDye(hatDye);
playerArmor.setItem(hat);
}
consumer.accept(user);
}
} catch (final SQLException exception) {
exception.printStackTrace();
}
} catch (final SQLException exception) {
exception.printStackTrace();
}
try (final PreparedStatement dropStatement = this.database.getDataSource()
.getReadWriteConnection("user").
getUnderlyingConnection().prepareStatement("DROP TABLE user")) {
dropStatement.executeUpdate();
} catch (final SQLException exception) {
exception.printStackTrace();
}
}
}

View File

@@ -0,0 +1,91 @@
package io.github.fisher2911.hmccosmetics.database;
import com.j256.ormlite.jdbc.DataSourceConnectionSource;
import com.j256.ormlite.jdbc.JdbcPooledConnectionSource;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import io.github.fisher2911.hmccosmetics.HMCCosmetics;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import java.sql.SQLException;
import java.util.logging.Logger;
import org.bukkit.Bukkit;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
public class DatabaseFactory {
private static final Path FILE_PATH = HMCCosmetics.PLUGIN_FOLDER.resolve("database.yml");
private static final String TYPE_PATH = "type";
private static final String NAME_PATH = "name";
private static final String USERNAME_PATH = "username";
private static final String PASSWORD_PATH = "password";
private static final String IP_PATH = "ip";
private static final String PORT_PATH = "port";
public static Database create(final HMCCosmetics plugin) throws SQLException {
if (!Files.exists(FILE_PATH)) {
plugin.saveResource(FILE_PATH.getFileName().toString(), false);
}
final FileConfiguration config = YamlConfiguration.loadConfiguration(FILE_PATH.toFile());
final String type = config.getString(TYPE_PATH);
final Logger logger = plugin.getLogger();
if (type == null) {
logger.severe("Database type was null, disabling plugin.");
Bukkit.getPluginManager().disablePlugin(plugin);
throw new NullPointerException();
}
final Database database = switch (type.toLowerCase()) {
case "mysql" -> {
final String name = config.getString(NAME_PATH);
final String username = config.getString(USERNAME_PATH);
final String password = config.getString(PASSWORD_PATH);
final String ip = config.getString(IP_PATH);
final String port = config.getString(PORT_PATH);
final HikariConfig hikari = new HikariConfig();
final String jdbcUrl = "jdbc:mysql://" + ip + ":" + port + "/" + name;
hikari.setJdbcUrl(jdbcUrl);
hikari.setUsername(username);
hikari.setPassword(password);
hikari.setConnectionTimeout(1000000000);
final HikariDataSource source = new HikariDataSource(hikari);
yield new Database(
plugin,
new DataSourceConnectionSource(source, jdbcUrl),
DatabaseType.MYSQL
);
}
case "sqlite" -> {
final File folder = new File(plugin.getDataFolder().getPath(), "database");
folder.mkdirs();
yield new Database(plugin, new JdbcPooledConnectionSource("jdbc:sqlite:" + new File(
folder.getPath(),
"users.db"
).getPath()),
DatabaseType.SQLITE
);
}
default -> null;
};
if (database == null) {
logger.severe(
"Error loading database, type " + type + " is invalid! Disabling plugin.");
Bukkit.getPluginManager().disablePlugin(plugin);
}
return database;
}
}

View File

@@ -0,0 +1,8 @@
package io.github.fisher2911.hmccosmetics.database;
public enum DatabaseType {
MYSQL,
SQLITE
}

View File

@@ -0,0 +1,123 @@
package io.github.fisher2911.hmccosmetics.database.dao;
import com.j256.ormlite.field.DatabaseField;
import com.j256.ormlite.table.DatabaseTable;
import io.github.fisher2911.hmccosmetics.cosmetic.CosmeticManager;
import io.github.fisher2911.hmccosmetics.gui.ArmorItem;
import java.util.Objects;
import org.jetbrains.annotations.Nullable;
@DatabaseTable(tableName = "armor_items")
public class ArmorItemDAO {
@DatabaseField(columnName = "uuid", useGetSet = true, uniqueCombo = true)
private String uuid;
@DatabaseField
private String id;
@DatabaseField(id = true, useGetSet = true, columnName = "artificial_id")
private String artificialId;
@DatabaseField(uniqueCombo = true)
private String type;
@DatabaseField(columnName = "color")
private int rgbDye;
public ArmorItemDAO(final String id, final String type, final int rgbDye) {
this.id = id;
this.artificialId = this.getArtificialId();
this.type = type;
this.rgbDye = rgbDye;
}
public ArmorItemDAO() {
}
public static ArmorItemDAO fromArmorItem(final ArmorItem armorItem) {
return new ArmorItemDAO(armorItem.getId(), armorItem.getType().toString(),
armorItem.getDye());
}
@Nullable
public ArmorItem toArmorItem(final CosmeticManager cosmeticManager) {
final ArmorItem armorItem = cosmeticManager.getArmorItem(this.id);
if (armorItem == null) {
return null;
}
final ArmorItem copy = new ArmorItem(armorItem);
copy.setDye(this.rgbDye);
return copy;
}
public String getUuid() {
return uuid;
}
public void setUuid(final String uuid) {
this.uuid = uuid;
}
/**
* ORMLite does not allow more than one primary key (WHYYYY???????????)
*
* @return
*/
public String getArtificialId() {
return this.uuid + "-" + this.type;
}
public void setArtificialId(final String artificialId) {
this.artificialId = artificialId;
}
public void setId(final String id) {
this.id = id;
}
public String getType() {
return type;
}
public void setType(final String type) {
this.type = type;
}
public int getRgbDye() {
return rgbDye;
}
public void setRgbDye(final int rgbDye) {
this.rgbDye = rgbDye;
}
@Override
public boolean equals(final Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
final ArmorItemDAO that = (ArmorItemDAO) o;
return Objects.equals(getUuid(), that.getUuid()) && Objects.equals(getType(),
that.getType());
}
@Override
public int hashCode() {
return Objects.hash(getUuid(), getType());
}
@Override
public String toString() {
return "ArmorItemDAO{" +
"uuid='" + uuid + '\'' +
", id='" + id + '\'' +
", type='" + type + '\'' +
", rgbDye=" + rgbDye +
'}';
}
}

View File

@@ -0,0 +1,74 @@
package io.github.fisher2911.hmccosmetics.database.dao;
import com.j256.ormlite.field.DatabaseField;
import com.j256.ormlite.table.DatabaseTable;
import io.github.fisher2911.hmccosmetics.cosmetic.CosmeticManager;
import io.github.fisher2911.hmccosmetics.gui.ArmorItem;
import io.github.fisher2911.hmccosmetics.inventory.PlayerArmor;
import io.github.fisher2911.hmccosmetics.user.User;
import io.github.fisher2911.hmccosmetics.user.Wardrobe;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
@DatabaseTable(tableName = "user")
public class UserDAO {
@DatabaseField(id = true)
private UUID uuid;
public UserDAO() {
}
public UserDAO(final UUID uuid) {
this.uuid = uuid;
}
public void setUuid(final UUID uuid) {
this.uuid = uuid;
}
public User toUser(
final CosmeticManager cosmeticManager,
final List<ArmorItemDAO> armorItems,
final Wardrobe wardrobe,
final int armorStandId) {
final PlayerArmor playerArmor = PlayerArmor.empty();
for (final ArmorItemDAO armorItemDao : armorItems) {
final ArmorItem armorItem = armorItemDao.toArmorItem(cosmeticManager);
if (armorItem == null) {
continue;
}
playerArmor.setItem(armorItem);
}
return new User(this.uuid, playerArmor, wardrobe, armorStandId);
}
@Override
public String toString() {
return "UserDAO{" +
"uuid=" + uuid +
'}';
}
@Override
public boolean equals(final Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
final UserDAO userDAO = (UserDAO) o;
return Objects.equals(uuid, userDAO.uuid);
}
@Override
public int hashCode() {
return Objects.hash(uuid);
}
}

View File

@@ -2,81 +2,27 @@ package io.github.fisher2911.hmccosmetics.gui;
import dev.triumphteam.gui.components.GuiAction;
import dev.triumphteam.gui.guis.GuiItem;
import io.github.fisher2911.hmccosmetics.util.builder.ColorBuilder;
import io.github.fisher2911.hmccosmetics.util.builder.ItemBuilder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.bukkit.Color;
import org.bukkit.Material;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
public class ArmorItem extends GuiItem {
private final String id;
private final List<String> lockedLore;
private final GuiAction<InventoryClickEvent> action;
private final String permission;
private final Type type;
private GuiAction<InventoryClickEvent> action;
private boolean dyeable;
public ArmorItem(
@NotNull final ItemStack itemStack,
final GuiAction<InventoryClickEvent> action,
final String id,
final List<String> lockedLore,
final String permission,
final Type type) {
super(itemStack, action);
this.id = id;
this.lockedLore = lockedLore;
this.action = action;
this.permission = permission;
this.type = type;
}
public ArmorItem(
@NotNull final ItemStack itemStack,
final String id,
final List<String> lockedLore,
final String permission,
final Type type) {
super(itemStack);
this.id = id;
this.lockedLore = lockedLore;
this.action = null;
this.permission = permission;
this.type = type;
}
public ArmorItem(
@NotNull final Material material,
final String id,
final List<String> lockedLore,
final String permission,
final Type type) {
super(material);
this.id = id;
this.lockedLore = lockedLore;
this.action = null;
this.permission = permission;
this.type = type;
}
public ArmorItem(
@NotNull final Material material,
@Nullable final GuiAction<InventoryClickEvent> action,
final String id,
final List<String> lockedLore,
final String permission,
final Type type) {
super(material, action);
this.id = id;
this.lockedLore = lockedLore;
this.action = action;
this.permission = permission;
this.type = type;
}
private int dye;
public ArmorItem(
@NotNull final ItemStack itemStack,
@@ -85,14 +31,15 @@ public class ArmorItem extends GuiItem {
final List<String> lockedLore,
final String permission,
final Type type,
final boolean dyeable) {
final int dye) {
super(itemStack, action);
this.id = id;
this.lockedLore = lockedLore;
this.action = action;
this.permission = permission;
this.type = type;
this.dyeable = dyeable;
this.dye = dye;
}
public ArmorItem(
@@ -101,14 +48,14 @@ public class ArmorItem extends GuiItem {
final List<String> lockedLore,
final String permission,
final Type type,
final boolean dyeable) {
final int dye) {
super(itemStack);
this.id = id;
this.lockedLore = lockedLore;
this.action = null;
this.permission = permission;
this.type = type;
this.dyeable = dyeable;
this.dye = dye;
}
public ArmorItem(
@@ -117,14 +64,14 @@ public class ArmorItem extends GuiItem {
final List<String> lockedLore,
final String permission,
final Type type,
final boolean dyeable) {
final int dye) {
super(material);
this.id = id;
this.lockedLore = lockedLore;
this.action = null;
this.permission = permission;
this.type = type;
this.dyeable = dyeable;
this.dye = dye;
}
public ArmorItem(
@@ -134,7 +81,80 @@ public class ArmorItem extends GuiItem {
final List<String> lockedLore,
final String permission,
final Type type,
final boolean dyeable) {
final int dye) {
super(material, action);
this.id = id;
this.lockedLore = lockedLore;
this.action = action;
this.permission = permission;
this.type = type;
this.dye = dye;
}
public ArmorItem(
@NotNull final ItemStack itemStack,
final GuiAction<InventoryClickEvent> action,
final String id,
final List<String> lockedLore,
final String permission,
final Type type,
final boolean dyeable,
final int dye) {
super(itemStack, action);
this.id = id;
this.lockedLore = lockedLore;
this.action = action;
this.permission = permission;
this.type = type;
this.dyeable = dyeable;
this.dye = dye;
}
public ArmorItem(
@NotNull final ItemStack itemStack,
final String id,
final List<String> lockedLore,
final String permission,
final Type type,
final boolean dyeable,
final int dye) {
super(itemStack);
this.id = id;
this.lockedLore = lockedLore;
this.action = null;
this.permission = permission;
this.type = type;
this.dyeable = dyeable;
this.dye = dye;
}
public ArmorItem(
@NotNull final Material material,
final String id,
final List<String> lockedLore,
final String permission,
final Type type,
final boolean dyeable,
final int dye) {
super(material);
this.id = id;
this.lockedLore = lockedLore;
this.action = null;
this.permission = permission;
this.type = type;
this.dyeable = dyeable;
this.dye = dye;
}
public ArmorItem(
@NotNull final Material material,
@Nullable final GuiAction<InventoryClickEvent> action,
final String id,
final List<String> lockedLore,
final String permission,
final Type type,
final boolean dyeable,
final int dye) {
super(material, action);
this.id = id;
this.lockedLore = lockedLore;
@@ -142,6 +162,30 @@ public class ArmorItem extends GuiItem {
this.permission = permission;
this.type = type;
this.dyeable = dyeable;
this.dye = dye;
}
public ArmorItem(final ArmorItem armorItem) {
super(armorItem.getItemStack(), armorItem.getAction());
this.id = armorItem.getId();
this.lockedLore = new ArrayList<>();
Collections.copy(armorItem.getLockedLore(), this.lockedLore);
this.action = armorItem.getAction();
this.permission = armorItem.getPermission();
this.type = armorItem.getType();
this.dyeable = armorItem.isDyeable();
this.dye = armorItem.getDye();
}
public static ArmorItem empty(final Type type) {
return new ArmorItem(
new ItemStack(Material.AIR),
"",
new ArrayList<>(),
"",
type,
-1
);
}
public String getId() {
@@ -156,6 +200,12 @@ public class ArmorItem extends GuiItem {
return this.action;
}
@Override
public void setAction(final GuiAction<InventoryClickEvent> action) {
super.setAction(action);
this.action = action;
}
public String getPermission() {
return permission;
}
@@ -168,21 +218,56 @@ public class ArmorItem extends GuiItem {
return dyeable;
}
public int getDye() {
return dye;
}
public void setDye(final int dye) {
this.dye = dye;
}
public ItemStack getColored() {
return this.color(super.getItemStack());
}
public ItemStack getItemStack(final boolean allowed) {
final ItemStack itemStack;
if (allowed) {
return this.getItemStack();
itemStack = super.getItemStack();
} else {
itemStack = ItemBuilder.from(this.getItemStack()).
lore(this.lockedLore).
build();
}
return ItemBuilder.from(this.getItemStack()).
lore(lockedLore).
return this.color(itemStack);
}
private ItemStack color(final ItemStack itemStack) {
if (this.dye == -1 || !ColorBuilder.canBeColored(itemStack)) {
return itemStack;
}
return ColorBuilder.from(itemStack).
color(Color.fromRGB(this.dye)).
build();
}
public boolean isEmpty() {
return this.getItemStack().getType() == Material.AIR;
}
public ArmorItem copy() {
return new ArmorItem(this);
}
public enum Type {
HAT,
BACKPACK
BACKPACK,
OFF_HAND
}
}

View File

@@ -13,7 +13,9 @@ public class ColorItem extends GuiItem {
private final Color color;
public ColorItem(final @NotNull ItemStack itemStack, final GuiAction<InventoryClickEvent> action, final Color color) {
public ColorItem(final @NotNull ItemStack itemStack,
final GuiAction<InventoryClickEvent> action,
final Color color) {
super(itemStack, action);
this.color = color;
}
@@ -28,7 +30,8 @@ public class ColorItem extends GuiItem {
this.color = color;
}
public ColorItem(final @NotNull Material material, final @Nullable GuiAction<InventoryClickEvent> action, final Color color) {
public ColorItem(final @NotNull Material material,
final @Nullable GuiAction<InventoryClickEvent> action, final Color color) {
super(material, action);
this.color = color;
}
@@ -36,4 +39,5 @@ public class ColorItem extends GuiItem {
public Color getColor() {
return color;
}
}

View File

@@ -0,0 +1,219 @@
package io.github.fisher2911.hmccosmetics.gui;
import dev.triumphteam.gui.components.GuiAction;
import dev.triumphteam.gui.guis.Gui;
import dev.triumphteam.gui.guis.GuiItem;
import io.github.fisher2911.hmccosmetics.HMCCosmetics;
import io.github.fisher2911.hmccosmetics.inventory.PlayerArmor;
import io.github.fisher2911.hmccosmetics.message.Adventure;
import io.github.fisher2911.hmccosmetics.message.MessageHandler;
import io.github.fisher2911.hmccosmetics.message.Messages;
import io.github.fisher2911.hmccosmetics.message.Placeholder;
import io.github.fisher2911.hmccosmetics.user.User;
import io.github.fisher2911.hmccosmetics.util.builder.ItemBuilder;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import org.bukkit.entity.HumanEntity;
import org.bukkit.entity.Player;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.Nullable;
public class CosmeticGui {
private static final float COOL_DOWN = 0.5f;
protected final HMCCosmetics plugin;
protected final MessageHandler messageHandler;
protected final String title;
protected final int rows;
protected final Map<Integer, ItemStack> itemStackMap;
protected final Map<Integer, GuiItem> guiItemMap;
protected Gui gui;
private long lastClicked;
public CosmeticGui(
final HMCCosmetics plugin,
final String title,
final int rows,
final Map<Integer, GuiItem> guiItemMap) {
this.plugin = plugin;
this.messageHandler = this.plugin.getMessageHandler();
this.title = title;
this.rows = rows;
this.guiItemMap = guiItemMap;
this.itemStackMap = new HashMap<>();
this.guiItemMap.forEach((key, value) -> itemStackMap.put(key, value.getItemStack()));
}
private void setItems(final User user) {
final Player player = user.getPlayer();
if (player == null) {
return;
}
for (final var entry : guiItemMap.entrySet()) {
final int slot = entry.getKey();
final GuiItem guiItem = this.getGuiItem(user, player, slot);
if (guiItem == null) {
continue;
}
this.gui.setItem(slot, guiItem);
}
}
private void setUserArmor(
final HumanEntity human,
final User user,
final ArmorItem armorItem,
final InventoryClickEvent event,
final GuiAction<InventoryClickEvent> actionIfSet) {
final long current = System.currentTimeMillis();
if ((current - this.lastClicked) / 1000. < COOL_DOWN) {
return;
}
this.lastClicked = current;
if (!(human instanceof final Player player)) {
return;
}
final ArmorItem.Type type = armorItem.getType();
final ArmorItem setTo = this.plugin.getUserManager().setOrUnset(
user,
armorItem,
Messages.getRemovedMessage(type),
Messages.getSetMessage(type)
);
if (!setTo.isEmpty()) {
actionIfSet.execute(event);
}
final int slot = event.getSlot();
final GuiItem guiItem = this.getGuiItem(user, player, slot);
if (guiItem == null) {
return;
}
this.gui.updateItem(slot, guiItem);
}
public void open(final User user, final Player player) {
this.gui = Gui.gui().
title(Adventure.MINI_MESSAGE.deserialize(
Placeholder.applyPapiPlaceholders(player, this.title))).
rows(this.rows).
create();
this.gui.setDefaultClickAction(event -> event.setCancelled(true));
this.setItems(user);
this.gui.open(player);
}
@Nullable
private GuiItem getGuiItem(final User user, final Player player, final int slot) {
final GuiItem guiItem = this.guiItemMap.get(slot);
if (guiItem == null) {
return null;
}
final ItemStack itemStack = this.itemStackMap.get(slot);
if (itemStack == null) {
return null;
}
if (guiItem instanceof final ArmorItem armorItem) {
final String permission =
armorItem.getPermission() == null ? "" : armorItem.getPermission();
final boolean hasPermission = permission.isBlank() || user.hasPermissionToUse(armorItem);
return new GuiItem(
this.applyPlaceholders(user, player, armorItem, hasPermission),
event -> {
if (!hasPermission) {
this.messageHandler.sendMessage(
player,
Messages.NO_COSMETIC_PERMISSION
);
return;
}
final ArmorItem cosmeticItem = this.plugin.getCosmeticManager()
.getArmorItem(armorItem.getId());
if (cosmeticItem == null) {
return;
}
this.setUserArmor(player, user, cosmeticItem, event, armorItem.getAction());
}
);
}
guiItem.setItemStack(
ItemBuilder.from(itemStack.clone()).papiPlaceholders(player).build()
);
return guiItem;
}
protected ItemStack applyPlaceholders(final User user, final Player player,
final ArmorItem armorItem, final boolean hasPermission) {
final Map<String, String> placeholders = new HashMap<>();
final PlayerArmor playerArmor = user.getPlayerArmor();
final ArmorItem.Type type = armorItem.getType();
final String id = playerArmor.getItem(type).getId();
placeholders.put(
Placeholder.ENABLED,
String.valueOf(id.equals(armorItem.getId())).
toLowerCase(Locale.ROOT));
placeholders.put(
Placeholder.ALLOWED,
String.valueOf(hasPermission).
toLowerCase(Locale.ROOT));
final ItemStack itemStack;
if (!hasPermission) {
itemStack = armorItem.getItemStack(false);
} else {
itemStack = armorItem.getColored();
}
return ItemBuilder.from(
itemStack
).namePlaceholders(placeholders).
lorePlaceholders(placeholders).
papiPlaceholders(player).
build();
}
public CosmeticGui copy() {
return new CosmeticGui(
this.plugin,
this.title,
this.rows,
new HashMap<>(this.guiItemMap)
);
}
}

View File

@@ -0,0 +1,198 @@
package io.github.fisher2911.hmccosmetics.gui;
import dev.triumphteam.gui.guis.GuiItem;
import io.github.fisher2911.hmccosmetics.HMCCosmetics;
import io.github.fisher2911.hmccosmetics.config.DyeGuiSerializer;
import io.github.fisher2911.hmccosmetics.config.GuiSerializer;
import io.github.fisher2911.hmccosmetics.config.ItemSerializer;
import io.github.fisher2911.hmccosmetics.cosmetic.CosmeticManager;
import io.github.fisher2911.hmccosmetics.user.User;
import io.github.fisher2911.hmccosmetics.user.Wardrobe;
import org.bukkit.Bukkit;
import org.bukkit.entity.HumanEntity;
import org.bukkit.entity.Player;
import org.bukkit.permissions.Permission;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.configurate.ConfigurateException;
import org.spongepowered.configurate.ConfigurationNode;
import org.spongepowered.configurate.yaml.YamlConfigurationLoader;
import java.io.File;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
public class CosmeticsMenu {
public static final String DEFAULT_MAIN_MENU = "main";
public static final String DEFAULT_DYE_MENU = "dye-menu";
private final HMCCosmetics plugin;
private final CosmeticManager cosmeticManager;
private final Map<String, CosmeticGui> guiMap = new HashMap<>();
private final Set<String> registeredPermissions = new HashSet<>();
public CosmeticsMenu(final HMCCosmetics plugin) {
this.plugin = plugin;
this.cosmeticManager = this.plugin.getCosmeticManager();
}
public void openMenu(final String id, final HumanEntity humanEntity) {
if (!(humanEntity instanceof final Player player)) return;
final CosmeticGui cosmeticGui = this.getGui(id);
final Optional<User> optionalUser = this.plugin.getUserManager().get(humanEntity.getUniqueId());
if (optionalUser.isEmpty()) return;
User user = optionalUser.get();
final Wardrobe wardrobe = user.getWardrobe();
if (wardrobe.isActive()) user = wardrobe;
if (cosmeticGui instanceof final DyeSelectorGui dyeSelectorGui) {
dyeSelectorGui.getGui(user, user.getLastSetItem().getType()).open(humanEntity);
return;
}
if (cosmeticGui != null) cosmeticGui.open(user, player);
}
public void openDefault(final HumanEntity humanEntity) {
this.openMenu(DEFAULT_MAIN_MENU, humanEntity);
}
public void reload() {
for (final ArmorItem armorItem : this.cosmeticManager.getAll()) {
Bukkit.getPluginManager().removePermission(new Permission(armorItem.getPermission()));
}
this.load();
}
public void openDyeSelectorGui(
User user,
final ArmorItem.Type type) {
final Player player = user.getPlayer();
if (player == null) {
return;
}
final Wardrobe wardrobe = user.getWardrobe();
if (wardrobe.isActive()) user = wardrobe;
final CosmeticGui gui = this.getGui(DEFAULT_DYE_MENU);
if (gui instanceof final DyeSelectorGui dyeSelectorGui) {
dyeSelectorGui.getGui(user, type).open(player);
}
}
@Nullable
private CosmeticGui getGui(final String id) {
final CosmeticGui gui = this.guiMap.get(id);
if (gui == null) return null;
return gui.copy();
}
private static final String GUI_TYPE = "gui-type";
private static final String DYE_TYPE = "dye";
public void load() {
this.guiMap.clear();
final File file = Path.of(this.plugin.getDataFolder().getPath(),
"menus").toFile();
if (!Path.of(this.plugin.getDataFolder().getPath(),
"menus",
DEFAULT_MAIN_MENU + ".yml").toFile().exists()) {
this.plugin.saveResource(
new File("menus", DEFAULT_MAIN_MENU + ".yml").getPath(),
false
);
}
if (!Path.of(this.plugin.getDataFolder().getPath(),
"menus",
DEFAULT_DYE_MENU + ".yml").toFile().exists()) {
this.plugin.saveResource(
new File("menus", DEFAULT_DYE_MENU + ".yml").getPath(),
false
);
}
if (!file.exists() ||
!file.isDirectory()) {
this.plugin.getLogger().severe("No directory found");
return;
}
final File[] files = file.listFiles();
if (files == null) {
this.plugin.getLogger().severe("Files are null");
return;
}
for (final File guiFile : files) {
final String id = guiFile.getName().replace(".yml", "");
final YamlConfigurationLoader loader = YamlConfigurationLoader.
builder().
path(Path.of(guiFile.getPath())).
defaultOptions(opts ->
opts.serializers(build -> {
build.register(GuiItem.class, ItemSerializer.INSTANCE);
build.register(CosmeticGui.class, GuiSerializer.INSTANCE);
build.register(DyeSelectorGui.class, DyeGuiSerializer.INSTANCE);
}))
.build();
try {
final ConfigurationNode source = loader.load();
final ConfigurationNode typeNode = source.node(GUI_TYPE);
final String type;
if (typeNode != null) {
type = typeNode.getString();
} else {
type = "";
}
if (id.equals(DEFAULT_DYE_MENU) || DYE_TYPE.equals(type)) {
this.guiMap.put(id, DyeGuiSerializer.INSTANCE.deserialize(DyeSelectorGui.class, source));
this.plugin.getLogger().info("Loaded dye gui: " + id);
continue;
}
final CosmeticGui gui = source.get(CosmeticGui.class);
if (gui == null) continue;
for (final GuiItem guiItem : gui.guiItemMap.values()) {
if (guiItem instanceof final ArmorItem item) {
final ArmorItem copy = new ArmorItem(item);
copy.setAction(null);
this.cosmeticManager.addArmorItem(copy);
final String perm = copy.getPermission();
if (perm.isBlank() || this.registeredPermissions.contains(perm)) continue;
Bukkit.getPluginManager().addPermission(new Permission(perm));
}
}
this.guiMap.put(id, source.get(CosmeticGui.class));
this.plugin.getLogger().info("Loaded gui: " + id);
} catch (final ConfigurateException exception) {
exception.printStackTrace();
}
}
}
}

View File

@@ -0,0 +1,199 @@
package io.github.fisher2911.hmccosmetics.gui;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import dev.triumphteam.gui.guis.Gui;
import dev.triumphteam.gui.guis.GuiItem;
import io.github.fisher2911.hmccosmetics.HMCCosmetics;
import io.github.fisher2911.hmccosmetics.inventory.PlayerArmor;
import io.github.fisher2911.hmccosmetics.message.Placeholder;
import io.github.fisher2911.hmccosmetics.user.User;
import io.github.fisher2911.hmccosmetics.util.builder.ItemBuilder;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import net.kyori.adventure.text.Component;
import org.bukkit.Material;
import org.bukkit.entity.HumanEntity;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.Nullable;
public class DyeSelectorGui extends CosmeticGui {
private final BiMap<Integer, ArmorItem.Type> cosmeticsSlots;
private int selectedCosmetic;
public DyeSelectorGui(
final HMCCosmetics plugin,
final String title,
final int rows,
final Map<Integer, GuiItem> guiItemMap,
final BiMap<Integer, ArmorItem.Type> cosmeticsSlots,
final int selectedCosmetic
) {
super(plugin, title, rows, guiItemMap);
this.cosmeticsSlots = cosmeticsSlots;
this.selectedCosmetic = selectedCosmetic;
}
public Gui getGui(final User user) {
return this.getGui(user, null);
}
public Gui getGui(final User user, @Nullable final ArmorItem.Type type) {
this.gui = Gui.gui().
title(Component.text(
Placeholder.applyPapiPlaceholders(user.getPlayer(), this.title))).
rows(rows).
create();
final Player player = user.getPlayer();
if (type != null) {
final Integer selected = this.cosmeticsSlots.inverse().get(type);
this.selectedCosmetic = selected == null ? this.selectedCosmetic : selected;
}
for (final var entry : this.cosmeticsSlots.entrySet()) {
gui.setItem(
entry.getKey(),
new GuiItem(
this.applyPlaceholders(
user,
player,
user.getPlayerArmor().getItem(entry.getValue()),
true
)
)
);
}
for (final var entry : this.guiItemMap.entrySet()) {
final GuiItem guiItem = entry.getValue();
final ItemStack itemStack = this.itemStackMap.get(entry.getKey());
if (itemStack == null) {
continue;
}
guiItem.setItemStack(
ItemBuilder.from(itemStack.clone()).papiPlaceholders(player).build()
);
gui.setItem(entry.getKey(), guiItem);
}
final PlayerArmor playerArmor = user.getPlayerArmor();
this.select(this.selectedCosmetic, user, player);
gui.setDefaultClickAction(event -> {
event.setCancelled(true);
final ArmorItem armorItem = playerArmor.getItem(
this.cosmeticsSlots.get(this.selectedCosmetic)
);
if (armorItem == null) {
return;
}
final ItemStack itemStack = playerArmor.getItem(type).getItemStack();
if (itemStack == null) {
return;
}
final int slot = event.getSlot();
final ArmorItem.Type clickedType = this.cosmeticsSlots.get(slot);
if (clickedType != null) {
this.select(slot, user, player);
return;
}
if (!armorItem.isDyeable()) {
return;
}
final GuiItem guiItem = this.guiItemMap.get(slot);
if (!(guiItem instanceof ColorItem colorItem)) {
return;
}
armorItem.setDye(colorItem.getColor().asRGB());
this.plugin.getUserManager().setItem(user, armorItem);
this.updateSelected(user, player);
});
return gui;
}
private void select(final int slot, final User user, final Player player) {
final PlayerArmor playerArmor = user.getPlayerArmor();
final ItemStack previous = this.applyPlaceholders(
user,
player,
playerArmor.getItem(this.cosmeticsSlots.get(this.selectedCosmetic)),
true
);
if (previous != null && previous.getType() != Material.AIR) {
final ItemStack previousItem = dev.triumphteam.gui.builder.item.ItemBuilder.from(
previous
).glow(false).build();
this.gui.updateItem(this.selectedCosmetic, previousItem);
}
this.selectedCosmetic = slot;
this.updateSelected(user, player);
}
private void updateSelected(final User user, final Player player) {
final ArmorItem.Type type = this.cosmeticsSlots.get(this.selectedCosmetic);
if (type == null) {
return;
}
this.gui.updateItem(this.selectedCosmetic,
ItemBuilder.from(
this.applyPlaceholders(
user, player, user.getPlayerArmor().getItem(type), true
)
).glow(true).build());
}
@Override
public void open(final User user, final Player player) {
this.getGui(user, user.getLastSetItem().getType()).open(player);
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
public DyeSelectorGui copy() {
return new DyeSelectorGui(
this.plugin,
super.title,
super.rows,
new HashMap<>(super.guiItemMap),
HashBiMap.create(this.cosmeticsSlots),
this.selectedCosmetic
);
}
}

View File

@@ -0,0 +1,7 @@
package io.github.fisher2911.hmccosmetics.hook;
public interface Hook {
String getId();
}

View File

@@ -0,0 +1,94 @@
package io.github.fisher2911.hmccosmetics.hook;
import io.github.fisher2911.hmccosmetics.HMCCosmetics;
import io.github.fisher2911.hmccosmetics.hook.item.ItemHook;
import io.github.fisher2911.hmccosmetics.hook.item.ItemHooks;
import io.github.fisher2911.hmccosmetics.hook.item.ItemsAdderHook;
import io.github.fisher2911.hmccosmetics.hook.item.OraxenHook;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import io.github.fisher2911.hmccosmetics.hook.item.PAPIExpansion;
import org.bukkit.Bukkit;
import org.bukkit.event.Listener;
import org.bukkit.plugin.PluginManager;
import org.jetbrains.annotations.Nullable;
public class HookManager {
private static final HookManager INSTANCE;
static {
INSTANCE = new HookManager(HMCCosmetics.getPlugin(HMCCosmetics.class));
}
private final HMCCosmetics plugin;
private final ItemHooks itemHooks;
private final PAPIHook papiHook;
private final Set<Class<? extends Hook>> registeredHooks;
private final Set<Listener> listeners;
private HookManager(final HMCCosmetics plugin) {
this.plugin = plugin;
this.registeredHooks = new HashSet<>();
this.listeners = new HashSet<>();
final PluginManager pluginManager = Bukkit.getPluginManager();
if (pluginManager.getPlugin("PlaceholderApi") != null) {
this.registeredHooks.add(PAPIHook.class);
this.papiHook = new PAPIHook();
} else {
this.papiHook = null;
}
final Map<String, ItemHook> itemHookMap = new HashMap<>();
final OraxenHook oraxenHook = new OraxenHook();
final ItemsAdderHook itemsAdderHook = new ItemsAdderHook();
if (pluginManager.getPlugin("Oraxen") != null) {
itemHookMap.put(oraxenHook.getIdentifier(), oraxenHook);
}
if (pluginManager.getPlugin("ItemsAdder") != null) {
itemHookMap.put(itemsAdderHook.getIdentifier(), itemsAdderHook);
this.listeners.add(itemsAdderHook);
}
this.itemHooks = new ItemHooks(itemHookMap);
itemHookMap.values().forEach(hook -> this.registerHook(hook.getClass()));
}
public static HookManager getInstance() {
return INSTANCE;
}
protected void registerHook(final Class<? extends Hook> hook) {
this.registeredHooks.add(hook);
}
public boolean isEnabled(final Class<? extends Hook> hook) {
return this.registeredHooks.contains(hook);
}
public void init() {
if (this.isEnabled(PAPIHook.class)) {
new PAPIExpansion(this.plugin).register();
}
this.registerListeners(this.plugin);
}
public void registerListeners(final HMCCosmetics plugin) {
for (final Listener listener : this.listeners) {
plugin.getServer().getPluginManager().registerEvents(listener, plugin);
}
}
@Nullable
public PAPIHook getPapiHook() {
return papiHook;
}
public ItemHooks getItemHooks() {
return itemHooks;
}
}

View File

@@ -0,0 +1,19 @@
package io.github.fisher2911.hmccosmetics.hook;
import me.clip.placeholderapi.PlaceholderAPI;
import org.bukkit.entity.Player;
public class PAPIHook implements Hook {
private static final String ID = "PAPI";
@Override
public String getId() {
return ID;
}
public String parse(final Player player, final String string) {
return PlaceholderAPI.setPlaceholders(player, string);
}
}

View File

@@ -0,0 +1,11 @@
package io.github.fisher2911.hmccosmetics.hook.item;
import io.github.fisher2911.hmccosmetics.hook.Hook;
import org.bukkit.inventory.ItemStack;
public interface ItemHook extends Hook {
String getIdentifier();
ItemStack getItem(final String id);
}

View File

@@ -0,0 +1,42 @@
package io.github.fisher2911.hmccosmetics.hook.item;
import java.util.Map;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.Nullable;
public class ItemHooks {
private final Map<String, ItemHook> itemHookMap;
public ItemHooks(final Map<String, ItemHook> itemHookMap) {
this.itemHookMap = itemHookMap;
}
@Nullable
public ItemStack getItemStack(final String item) {
final String[] parts = item.split(":");
if (parts.length < 2) {
return null;
}
final String identifier = parts[0];
final StringBuilder itemId = new StringBuilder();
for (int i = 1; i < parts.length; i++) {
itemId.append(parts[i]);
if (i < parts.length - 1) {
itemId.append(":");
}
}
final ItemHook hook = this.itemHookMap.get(identifier);
if (hook == null) {
return null;
}
return hook.getItem(itemId.toString());
}
}

View File

@@ -0,0 +1,41 @@
package io.github.fisher2911.hmccosmetics.hook.item;
import dev.lone.itemsadder.api.CustomStack;
import dev.lone.itemsadder.api.Events.ItemsAdderLoadDataEvent;
import io.github.fisher2911.hmccosmetics.HMCCosmetics;
import org.bukkit.Bukkit;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.inventory.ItemStack;
public class ItemsAdderHook implements ItemHook, Listener {
public static final String ID = "ITEM_ADDER";
private static final String IDENTIFIER = "itemsadder";
@EventHandler
public void onItemsAdderLoad(final ItemsAdderLoadDataEvent event) {
final HMCCosmetics plugin = HMCCosmetics.getPlugin(HMCCosmetics.class);
Bukkit.getScheduler().runTaskAsynchronously(plugin, plugin::load);
}
@Override
public String getId() {
return ID;
}
@Override
public String getIdentifier() {
return IDENTIFIER;
}
@Override
public ItemStack getItem(final String id) {
final CustomStack stack = CustomStack.getInstance(id);
if (stack == null) {
return null;
}
return stack.getItemStack().clone();
}
}

View File

@@ -0,0 +1,31 @@
package io.github.fisher2911.hmccosmetics.hook.item;
import io.th0rgal.oraxen.items.ItemBuilder;
import io.th0rgal.oraxen.items.OraxenItems;
import org.bukkit.inventory.ItemStack;
public class OraxenHook implements ItemHook {
public static final String ID = "ORAXEN";
private static final String IDENTIFIER = "oraxen";
@Override
public String getId() {
return ID;
}
@Override
public String getIdentifier() {
return IDENTIFIER;
}
@Override
public ItemStack getItem(final String id) {
final ItemBuilder itemBuilder = OraxenItems.getItemById(id);
if (itemBuilder == null) {
return null;
}
return itemBuilder.build();
}
}

View File

@@ -0,0 +1,97 @@
package io.github.fisher2911.hmccosmetics.hook.item;
import io.github.fisher2911.hmccosmetics.HMCCosmetics;
import io.github.fisher2911.hmccosmetics.api.CosmeticItem;
import io.github.fisher2911.hmccosmetics.gui.ArmorItem;
import io.github.fisher2911.hmccosmetics.message.Placeholder;
import io.github.fisher2911.hmccosmetics.message.Translation;
import io.github.fisher2911.hmccosmetics.user.User;
import io.github.fisher2911.hmccosmetics.user.UserManager;
import me.clip.placeholderapi.expansion.PlaceholderExpansion;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
public class PAPIExpansion extends PlaceholderExpansion {
private final HMCCosmetics plugin;
private final UserManager userManager;
private static final String IDENTIFIER = "hmccosmetics";
private static final String AUTHOR = "MasterOfTheFish";
public PAPIExpansion(final HMCCosmetics plugin) {
this.plugin = plugin;
this.userManager = this.plugin.getUserManager();
}
@Override
public @NotNull String getIdentifier() {
return IDENTIFIER;
}
@Override
public @NotNull String getAuthor() {
return AUTHOR;
}
@Override
public @NotNull String getVersion() {
return this.plugin.getDescription().getVersion();
}
@Override
public @Nullable String onPlaceholderRequest(final Player player, @NotNull final String params) {
final String[] parts = params.split("_");
if (parts.length == 0) {
return null;
}
final Optional<User> optionalUser = this.userManager.get(player.getUniqueId());
if (optionalUser.isEmpty()) return null;
final User user = optionalUser.get();
// %hmccosmetics_using_id%
if (parts[0].equalsIgnoreCase("using")) {
if (parts.length < 2) return null;
final String id = this.getId(parts, 1);
for (final ArmorItem item : user.getPlayerArmor().getArmorItems()) {
if (item.getId().equals(id)) return Translation.translate(Translation.TRUE);
}
return Translation.translate(Translation.FALSE);
}
// %hmccosmetics_current_type%
if (parts[0].equals("current")) {
if (parts.length >= 2) {
final String typeStr = getId(parts, 1);
try {
final ArmorItem.Type type = ArmorItem.Type.valueOf(typeStr.toUpperCase());
for (final ArmorItem item : user.getPlayerArmor().getArmorItems()) {
if (item.getType().equals(type)) return item.getId();
}
return Translation.translate(Translation.NONE);
} catch (final IllegalArgumentException exception) {
return null;
}
}
}
return null;
}
private String getId(final String[] parts, final int fromIndex) {
final StringBuilder builder = new StringBuilder();
for (int i = fromIndex; i < parts.length; i++) {
builder.append(parts[i]);
if (i < parts.length - 1) builder.append("_");
}
return builder.toString();
}
}

View File

@@ -0,0 +1,71 @@
package io.github.fisher2911.hmccosmetics.inventory;
import io.github.fisher2911.hmccosmetics.gui.ArmorItem;
import java.util.Collection;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.Map;
public class PlayerArmor {
private final Map<ArmorItem.Type, ArmorItem> armorItems;
public PlayerArmor(ArmorItem hat, final ArmorItem backpack, final ArmorItem offHand) {
this.armorItems = new EnumMap<>(ArmorItem.Type.class);
this.armorItems.put(hat.getType(), hat);
this.armorItems.put(backpack.getType(), hat);
this.armorItems.put(offHand.getType(), offHand);
}
public PlayerArmor(final Map<ArmorItem.Type, ArmorItem> armorItems) {
this.armorItems = armorItems;
}
public static PlayerArmor empty() {
return new PlayerArmor(
ArmorItem.empty(ArmorItem.Type.HAT),
ArmorItem.empty(ArmorItem.Type.BACKPACK),
ArmorItem.empty(ArmorItem.Type.OFF_HAND)
);
}
public ArmorItem getHat() {
return this.getItem(ArmorItem.Type.HAT);
}
public ArmorItem getBackpack() {
return this.getItem(ArmorItem.Type.BACKPACK);
}
public ArmorItem getOffHand() {
return this.getItem(ArmorItem.Type.OFF_HAND);
}
public ArmorItem getItem(final ArmorItem.Type type) {
ArmorItem armorItem = this.armorItems.get(type);
if (armorItem == null) {
armorItem = ArmorItem.empty(type);
this.armorItems.put(type, armorItem);
}
return armorItem;
}
public ArmorItem setItem(final ArmorItem armorItem) {
return this.armorItems.put(armorItem.getType(), armorItem);
}
public Collection<ArmorItem> getArmorItems() {
return this.armorItems.values();
}
public PlayerArmor copy() {
return new PlayerArmor(new HashMap<>(this.armorItems));
}
public void clear() {
for (final ArmorItem.Type type : ArmorItem.Type.values()) {
this.setItem(ArmorItem.empty(type));
}
}
}

View File

@@ -0,0 +1,67 @@
package io.github.fisher2911.hmccosmetics.listener;
import io.github.fisher2911.hmccosmetics.HMCCosmetics;
import io.github.fisher2911.hmccosmetics.user.User;
import io.github.fisher2911.hmccosmetics.user.UserManager;
import java.util.Optional;
import org.bukkit.Bukkit;
import org.bukkit.entity.HumanEntity;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.event.inventory.InventoryCloseEvent;
import org.bukkit.event.inventory.InventoryDragEvent;
public class ClickListener implements Listener {
private final HMCCosmetics plugin;
private final UserManager userManager;
public ClickListener(final HMCCosmetics plugin) {
this.plugin = plugin;
this.userManager = this.plugin.getUserManager();
}
@EventHandler
public void onCosmeticClick(final InventoryClickEvent event) {
final HumanEntity player = event.getWhoClicked();
if (!(player instanceof Player)) {
return;
}
this.fixInventory((Player) player);
}
@EventHandler
public void onCosmeticClick(final InventoryDragEvent event) {
final HumanEntity player = event.getWhoClicked();
if (!(player instanceof Player)) {
return;
}
this.fixInventory((Player) player);
}
@EventHandler
public void onInventoryClose(final InventoryCloseEvent event) {
final HumanEntity player = event.getPlayer();
this.userManager.get(player.getUniqueId()).ifPresent(this::doRunnable);
}
private void fixInventory(final Player player) {
final Optional<User> optionalUser = this.userManager.get(player.getUniqueId());
if (optionalUser.isEmpty()) {
return;
}
this.doRunnable(optionalUser.get());
}
private void doRunnable(final User user) {
Bukkit.getScheduler().runTaskLaterAsynchronously(
this.plugin, () -> this.userManager.updateCosmetics(user),
1);
}
}

View File

@@ -0,0 +1,80 @@
package io.github.fisher2911.hmccosmetics.listener;
import io.github.fisher2911.hmccosmetics.HMCCosmetics;
import io.github.fisher2911.hmccosmetics.user.UserManager;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.block.Action;
import org.bukkit.event.block.BlockPlaceEvent;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.player.PlayerSwapHandItemsEvent;
import org.bukkit.inventory.EquipmentSlot;
import org.bukkit.inventory.ItemStack;
import org.spigotmc.event.entity.EntityMountEvent;
public class CosmeticFixListener implements Listener {
private final HMCCosmetics plugin;
private final UserManager userManager;
public CosmeticFixListener(final HMCCosmetics plugin) {
this.plugin = plugin;
this.userManager = this.plugin.getUserManager();
}
@EventHandler
public void onEntityMount(final EntityMountEvent event) {
if (!(event.getEntity() instanceof final Player player)) {
return;
}
this.fixCosmetics(player);
}
@EventHandler
public void onOffhandSwap(final PlayerSwapHandItemsEvent event) {
final ItemStack offHand = event.getOffHandItem();
if (offHand != null && offHand.getType() != Material.AIR) {
return;
}
this.fixCosmetics(event.getPlayer());
}
@EventHandler
public void onRightClick(final PlayerInteractEvent event) {
if (event.getAction() != Action.RIGHT_CLICK_BLOCK
&& event.getHand() != EquipmentSlot.OFF_HAND) {
return;
}
final Player player = event.getPlayer();
final ItemStack mainHand = event.getPlayer().getInventory().getItemInMainHand();
if (mainHand.getType().isBlock() && mainHand.getAmount() > 0) {
return;
}
this.userManager.updateCosmetics(player.getUniqueId());
}
@EventHandler(ignoreCancelled = true)
public void onBlockPlace(final BlockPlaceEvent event) {
if (event.getHand() != EquipmentSlot.OFF_HAND) {
return;
}
final ItemStack itemStack = event.getItemInHand();
if (itemStack.getAmount() > 1) {
return;
}
this.fixCosmetics(event.getPlayer());
}
private void fixCosmetics(final Player player) {
Bukkit.getScheduler().runTaskLaterAsynchronously(this.plugin,
() -> this.userManager.updateCosmetics(player.getUniqueId()), 2);
}
}

View File

@@ -0,0 +1,63 @@
package io.github.fisher2911.hmccosmetics.listener;
import io.github.fisher2911.hmccosmetics.HMCCosmetics;
import io.github.fisher2911.hmccosmetics.config.WardrobeSettings;
import io.github.fisher2911.hmccosmetics.database.Database;
import io.github.fisher2911.hmccosmetics.user.User;
import io.github.fisher2911.hmccosmetics.user.UserManager;
import io.github.fisher2911.hmccosmetics.user.Wardrobe;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import java.util.Optional;
public class JoinListener implements Listener {
private final HMCCosmetics plugin;
private final Database database;
private final UserManager userManager;
public JoinListener(final HMCCosmetics plugin) {
this.plugin = plugin;
this.database = this.plugin.getDatabase();
this.userManager = this.plugin.getUserManager();
}
@EventHandler
public void onJoin(final PlayerJoinEvent event) {
final Player player = event.getPlayer();
this.database.loadUser(player.getUniqueId(),
user -> Bukkit.getScheduler().runTaskAsynchronously(this.plugin,
() -> {
this.userManager.resendCosmetics(player);
final WardrobeSettings settings = this.plugin.getSettings().getWardrobeSettings();
if (settings.isAlwaysDisplay() && settings.getLocation() != null) {
final Wardrobe wardrobe = user.getWardrobe();
wardrobe.setCurrentLocation(settings.getLocation());
wardrobe.spawnFakePlayer(player);
}
}));
}
@EventHandler
public void onQuit(final PlayerQuitEvent event) {
final Player player = event.getPlayer();
final Optional<User> optionalUser = this.userManager.get(player.getUniqueId());
optionalUser.ifPresent(user -> {
final Wardrobe wardrobe = user.getWardrobe();
if (wardrobe.isActive()) {
Bukkit.getScheduler().runTaskAsynchronously(
this.plugin,
() -> wardrobe.despawnFakePlayer(player)
);
}
});
this.userManager.remove(player.getUniqueId());
}
}

View File

@@ -0,0 +1,43 @@
package io.github.fisher2911.hmccosmetics.listener;
import io.github.fisher2911.hmccosmetics.HMCCosmetics;
import io.github.fisher2911.hmccosmetics.message.Messages;
import io.github.fisher2911.hmccosmetics.user.User;
import io.github.fisher2911.hmccosmetics.user.UserManager;
import io.github.fisher2911.hmccosmetics.user.Wardrobe;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerToggleSneakEvent;
import java.util.Optional;
public class PlayerShiftListener implements Listener {
private final HMCCosmetics plugin;
private final UserManager userManager;
public PlayerShiftListener(final HMCCosmetics plugin) {
this.plugin = plugin;
this.userManager = this.plugin.getUserManager();
}
@EventHandler
public void onPlayerShift(final PlayerToggleSneakEvent event) {
final Player player = event.getPlayer();
final Optional<User> userOptional = this.userManager.get(player.getUniqueId());
if (userOptional.isEmpty()) return;
final User user = userOptional.get();
final Wardrobe wardrobe = user.getWardrobe();
if (!wardrobe.isActive()) return;
wardrobe.despawnFakePlayer(player);
this.plugin.getMessageHandler().sendMessage(
player,
Messages.CLOSED_WARDROBE
);
}
}

View File

@@ -3,14 +3,13 @@ package io.github.fisher2911.hmccosmetics.listener;
import io.github.fisher2911.hmccosmetics.HMCCosmetics;
import io.github.fisher2911.hmccosmetics.user.User;
import io.github.fisher2911.hmccosmetics.user.UserManager;
import java.util.Optional;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerRespawnEvent;
import java.util.Optional;
public class RespawnListener implements Listener {
private final HMCCosmetics plugin;
@@ -26,8 +25,12 @@ public class RespawnListener implements Listener {
Bukkit.getScheduler().runTaskLater(this.plugin, () -> {
final Player player = event.getPlayer();
final Optional<User> optionalUser = this.userManager.get(player.getUniqueId());
optionalUser.ifPresent(user -> user.setHat(user.getPlayerArmor().getHat(), this.userManager));
optionalUser.ifPresent(user -> {
user.despawnAttached();
this.userManager.updateCosmetics(user);
this.userManager.setItem(user, user.getPlayerArmor().getHat());
});
}, 1);
}
}

View File

@@ -34,8 +34,12 @@ public class Message {
@Override
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
final Message message = (Message) o;
return Objects.equals(key, message.key);
}
@@ -48,10 +52,9 @@ public class Message {
public enum Type {
MESSAGE,
ACTION_BAR,
TITLE
}
}

View File

@@ -1,21 +1,22 @@
package io.github.fisher2911.hmccosmetics.message;
import io.github.fisher2911.hmccosmetics.HMCCosmetics;
import io.github.fisher2911.hmccosmetics.util.StringUtils;
import io.github.fisher2911.hmccosmetics.util.Utils;
import net.kyori.adventure.platform.bukkit.BukkitAudiences;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.title.Title;
import org.bukkit.command.CommandSender;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Player;
import java.io.File;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Logger;
import net.kyori.adventure.platform.bukkit.BukkitAudiences;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.bungeecord.BungeeComponentSerializer;
import net.kyori.adventure.title.Title;
import net.md_5.bungee.api.ChatMessageType;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Player;
public class MessageHandler {
@@ -40,20 +41,22 @@ public class MessageHandler {
}
/**
*
* @param sender receiver of message
* @param key message key
* @param placeholders placeholders
*/
public void sendMessage(final CommandSender sender, final Message key, final Map<String, String> placeholders) {
final String message = StringUtils.applyPlaceholders(this.getMessage(key), placeholders);
final Component component = Adventure.MINI_MESSAGE.parse(message);
this.adventure.sender(sender).sendMessage(component);
public void sendMessage(final CommandSender sender, final Message key,
final Map<String, String> placeholders) {
final String message = this.getPapiPlaceholders(
sender,
Placeholder.applyPlaceholders(this.getMessage(key), placeholders)
);
final Component component = Adventure.MINI_MESSAGE.deserialize(message);
sender.spigot().sendMessage(BungeeComponentSerializer.get().serialize(component));
}
/**
*
* @param sender receiver of message
* @param key message key
*/
@@ -63,20 +66,22 @@ public class MessageHandler {
}
/**
*
* @param player receiver of message
* @param key message key
* @param placeholders placeholders
*/
public void sendActionBar(final Player player, final Message key, final Map<String, String> placeholders) {
final String message = StringUtils.applyPlaceholders(this.getMessage(key), placeholders);
Component component = Adventure.MINI_MESSAGE.parse(message);
this.adventure.player(player).sendActionBar(component);
public void sendActionBar(final Player player, final Message key,
final Map<String, String> placeholders) {
final String message = this.getPapiPlaceholders(
player,
Placeholder.applyPlaceholders(this.getMessage(key), placeholders)
);
Component component = Adventure.MINI_MESSAGE.deserialize(message);
player.spigot().sendMessage(ChatMessageType.ACTION_BAR, BungeeComponentSerializer.get().serialize(component));
}
/**
*
* @param player receiver of message
* @param key message key
*/
@@ -86,20 +91,22 @@ public class MessageHandler {
}
/**
*
* @param player receiver of message
* @param key message key
* @param placeholders placeholders
*/
public void sendTitle(final Player player, final Message key, final Map<String, String> placeholders) {
final String message = StringUtils.applyPlaceholders(this.getMessage(key), placeholders);
Component component = Adventure.MINI_MESSAGE.parse(message);
public void sendTitle(final Player player, final Message key,
final Map<String, String> placeholders) {
final String message = this.getPapiPlaceholders(
player,
Placeholder.applyPlaceholders(this.getMessage(key), placeholders)
);
Component component = Adventure.MINI_MESSAGE.deserialize(message);
this.adventure.player(player).showTitle(Title.title(component, Component.empty()));
}
/**
*
* @param player receiver of message
* @param key message key
*/
@@ -109,7 +116,6 @@ public class MessageHandler {
}
/**
*
* @param key message key
* @return message, or empty string if message not found
*/
@@ -140,19 +146,27 @@ public class MessageHandler {
}
for (final String key : config.getKeys(false)) {
final String message = Utils.replaceIfNull(config.getString(key), "", value -> {
if (value == null) {
this.logger.warning(String.format(ErrorMessages.ITEM_NOT_FOUND, "message", fileName));
this.logger.warning(
String.format(ErrorMessages.ITEM_NOT_FOUND, "message", fileName));
}
}).replace(Placeholder.PREFIX, prefix);
final Message.Type messageType = Utils.stringToEnum(
Utils.replaceIfNull(config.getString("type"), "")
, Message.Type.class, Message.Type.MESSAGE
Utils.replaceIfNull(config.getString("type"), ""), Message.Type.class,
Message.Type.MESSAGE
);
this.messageMap.put(key, new Message(key, message, messageType));
}
}
private String getPapiPlaceholders(final CommandSender sender, final String message) {
if (sender instanceof final Player player) {
return Placeholder.applyPapiPlaceholders(player, message);
}
return Placeholder.applyPapiPlaceholders(null, message);
}
}

View File

@@ -0,0 +1,103 @@
package io.github.fisher2911.hmccosmetics.message;
import io.github.fisher2911.hmccosmetics.gui.ArmorItem;
import org.bukkit.ChatColor;
public class Messages {
public static final Message INVALID_COLOR = new Message(
"","<red>" + Placeholder.ITEM + " is an invalid color.");
public static final Message NO_PERMISSION =
new Message("no-permission", "You do not have permission for this!");
public static final Message NO_COSMETIC_PERMISSION =
new Message("no-cosmetic-permission", "You do not have permission for this cosmetic!");
public static final Message SET_HAT =
new Message("set-hat", "Set hat");
public static final Message REMOVED_HAT =
new Message("removed-hat", "Removed hat");
public static final Message SET_BACKPACK =
new Message("set-backpack", "Set backpack");
public static final Message REMOVED_BACKPACK =
new Message("removed-backpack", "Removed backpack");
public static final Message SET_OFF_HAND =
new Message("set-off-hand", "Set off hand");
public static final Message REMOVED_OFF_HAND =
new Message("removed-off-hand", "Removed off hand");
public static final Message SET_DYE_COLOR =
new Message("set-dye-color", "Set dye color of " + Placeholder.ITEM);
public static final Message MUST_BE_PLAYER =
new Message("must-be-player", "You must be a player to do this!");
public static final Message RELOADED =
new Message("reloaded", "Config reloaded");
public static final Message INVALID_TYPE =
new Message("invalid-type", "Invalid type");
public static final Message INVALID_USER =
new Message("invalid-user", ChatColor.RED + "That user's data cannot be found!");
public static final Message ITEM_NOT_FOUND =
new Message("item-not-found", ChatColor.RED + "That item could not be found!");
public static final Message HELP_COMMAND =
new Message("help-command",
"""
<#6D9DC5><st> </st> <gradient:#40B7D6:#6D9DC5>HMCCosmetics - Help</gradient><#6D9DC5> <st> </st>
<#5AE4B5>• <#40B7D6>/cosmetics - <#6D9DC5>Opens cosmetics GUI.
<#5AE4B5>• <#40B7D6>/cosmetics dye <gray><BACKPACK/HAT></gray> - <#6D9DC5>Opens dye menu for specified cosmetic.
<#5AE4B5>• <#40B7D6>/cosmetics help - <#6D9DC5>Opens this menu.
<st> </st>""");
public static final Message OPENED_WARDROBE =
new Message("opened-wardrobe", ChatColor.GREEN + "Viewing wardrobe!");
public static final Message CLOSED_WARDROBE =
new Message("closed-wardrobe", ChatColor.GREEN + "Closing wardrobe!");
public static final Message WARDROBE_ALREADY_OPEN =
new Message("wardrobe-already-open", ChatColor.RED + "The wardrobe is already open!");
public static final Message NOT_NEAR_WARDROBE =
new Message("not-near-wardrobe", ChatColor.RED + "You are not near the wardrobe!");
public static final Message CANNOT_USE_PORTABLE_WARDROBE =
new Message("cannot-use-portable-wardrobe", ChatColor.RED + "You cannot use the portable wardrobe!");
public static final Message OPENED_OTHER_WARDROBE =
new Message("opened-other-wardrobe", ChatColor.GREEN + "Opening " + Placeholder.PLAYER + "'s wardrobe.");
public static final Message SET_OTHER_BACKPACK = new Message(
"set-other-backpack", ChatColor.GREEN + "You have set the backpack of " +
Placeholder.PLAYER + " to " + Placeholder.TYPE + "."
);
public static final Message SET_OTHER_HAT = new Message(
"set-other-backpack", ChatColor.GREEN + "You have set the helmet of " +
Placeholder.PLAYER + " to " + Placeholder.TYPE + "."
);
public static final Message SET_OTHER_OFF_HAND = new Message(
"set-other-off-hand", ChatColor.GREEN + "You have set the off hand of " +
Placeholder.PLAYER + " to " + Placeholder.TYPE + "."
);
public static Message getSetMessage(final ArmorItem.Type type) {
return switch (type) {
case HAT -> Messages.SET_HAT;
case BACKPACK -> Messages.SET_BACKPACK;
case OFF_HAND -> Messages.SET_OFF_HAND;
};
}
public static Message getRemovedMessage(final ArmorItem.Type type) {
return switch (type) {
case HAT -> Messages.REMOVED_HAT;
case BACKPACK -> Messages.REMOVED_BACKPACK;
case OFF_HAND -> Messages.REMOVED_OFF_HAND;
};
}
public static Message getSetOtherMessage(final ArmorItem.Type type) {
return switch (type) {
case HAT -> Messages.SET_OTHER_HAT;
case BACKPACK -> Messages.SET_OTHER_BACKPACK;
case OFF_HAND -> Messages.SET_OTHER_OFF_HAND;
};
}
}

View File

@@ -0,0 +1,14 @@
package io.github.fisher2911.hmccosmetics.message;
public class Permission {
public static final String DEFAULT_COMMAND = "hmccosmetics.cmd.default";
public static final String DYE_COMMAND = "hmccosmetics.cmd.dye";
public static final String RELOAD_COMMAND = "hmccosmetics.cmd.reload";
public static final String HELP_COMMAND = "hmccosmetics.cmd.help";
public static final String SET_COSMETIC_COMMAND = "hmccosmetics.cmd.set";
public static final String PORTABLE_WARDROBE = "hmccosmetics.cmd.wardrobe.portable";
public static final String WARDROBE = "hmccosmetics.cmd.wardrobe";
public static final String OPEN_OTHER_WARDROBE = "hmccosmetics.cmd.wardrobe.other";
}

View File

@@ -0,0 +1,42 @@
package io.github.fisher2911.hmccosmetics.message;
import io.github.fisher2911.hmccosmetics.hook.HookManager;
import io.github.fisher2911.hmccosmetics.hook.PAPIHook;
import java.util.Map;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.Nullable;
public class Placeholder {
public static final String PREFIX = "%prefix%";
public static final String TYPE = "%type%";
public static final String ITEM = "%item%";
public static final String FILE = "%file%";
public static final String PLAYER = "%player%";
public static final String ENABLED = "%enabled%";
public static final String ALLOWED = "%allowed%";
/**
* @param message message being translated
* @param placeholders placeholders applied
* @return message with placeholders applied
*/
public static String applyPlaceholders(String message, final Map<String, String> placeholders) {
for (final Map.Entry<String, String> entry : placeholders.entrySet()) {
message = message.replace(entry.getKey(), Translation.translate(entry.getValue()));
}
return message;
}
public static String applyPapiPlaceholders(@Nullable final Player player,
final String message) {
if (HookManager.getInstance().isEnabled(PAPIHook.class)) {
return HookManager.getInstance().getPapiHook().parse(player, message);
}
return message;
}
}

View File

@@ -0,0 +1,60 @@
package io.github.fisher2911.hmccosmetics.message;
import io.github.fisher2911.hmccosmetics.HMCCosmetics;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
public class Translation {
public static final String TRUE = "true";
public static final String FALSE = "false";
public static final String NONE = "none";
private static final Translation INSTANCE;
private static final String FILE_NAME = "translations.yml";
private static final String TRANSLATION_PATH = "translations";
static {
INSTANCE = new Translation(HMCCosmetics.getPlugin(HMCCosmetics.class));
}
private final HMCCosmetics plugin;
private final Map<String, String> translations;
public Translation(final HMCCosmetics plugin) {
this.translations = new HashMap<>();
this.plugin = plugin;
}
public static Translation getInstance() {
return INSTANCE;
}
public static String translate(final String key) {
return INSTANCE.translations.getOrDefault(key, key);
}
public void load() {
final File file = new File(this.plugin.getDataFolder(), FILE_NAME);
if (!file.exists()) {
this.plugin.saveResource(FILE_NAME, false);
}
final FileConfiguration config = YamlConfiguration.loadConfiguration(file);
final ConfigurationSection section = config.getConfigurationSection(TRANSLATION_PATH);
if (section == null) {
return;
}
for (final String key : section.getKeys(false)) {
this.translations.put(key, section.getString(key));
}
}
}

View File

@@ -0,0 +1,172 @@
package io.github.fisher2911.hmccosmetics.packet;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.ProtocolManager;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.wrappers.EnumWrappers;
import com.comphenix.protocol.wrappers.MinecraftKey;
import com.comphenix.protocol.wrappers.Pair;
import io.github.fisher2911.nms.DestroyPacket;
import io.github.fisher2911.nms.DestroyPacket_1_17_R1;
import io.github.fisher2911.nms.DestroyPacket_1_18_R1;
import io.github.fisher2911.nms.PlayerPackets_1_17_R1;
import io.github.fisher2911.nms.PlayerPackets_1_18_R1;
import io.github.fisher2911.nms.PlayerPackets;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
import java.util.UUID;
public class PacketManager {
private static final PlayerPackets playerPackets;
private static final DestroyPacket destroyPacket;
static {
final String version = Bukkit.getVersion();
System.out.println("Version: " + Bukkit.getVersion());
if (version.contains("1.17")) {
playerPackets = new PlayerPackets_1_17_R1();
destroyPacket = new DestroyPacket_1_17_R1();
} else if (version.contains("1.18")) {
playerPackets = new PlayerPackets_1_18_R1();
destroyPacket = new DestroyPacket_1_18_R1();
} else {
playerPackets = null;
destroyPacket = null;
}
}
public static PacketContainer getEntitySpawnPacket(final Location location, final int entityId,
final EntityType entityType) {
final PacketContainer packet = new PacketContainer(PacketType.Play.Server.SPAWN_ENTITY);
// Entity ID
packet.getIntegers().write(0, entityId);
// Entity Type
// packet.getIntegers().write(6, 78);
// Set yaw pitch
packet.getIntegers().write(4, (int) location.getPitch());
packet.getIntegers().write(5, (int) location.getYaw());
// Set location
packet.getDoubles().write(0, location.getX());
packet.getDoubles().write(1, location.getY());
packet.getDoubles().write(2, location.getZ());
// Set UUID
packet.getUUIDs().write(0, UUID.randomUUID());
packet.getEntityTypeModifier().write(0, entityType);
return packet;
}
public static PacketContainer getEquipmentPacket(
final List<Pair<EnumWrappers.ItemSlot, ItemStack>> equipmentList,
final int entityId
) {
final PacketContainer armorPacket = new PacketContainer(
PacketType.Play.Server.ENTITY_EQUIPMENT);
armorPacket.getIntegers().write(0, entityId);
armorPacket.getSlotStackPairLists().write(0, equipmentList);
return armorPacket;
}
public static PacketContainer getRotationPacket(final int entityId, final Location location) {
final PacketContainer rotationPacket = new PacketContainer(
PacketType.Play.Server.ENTITY_HEAD_ROTATION);
rotationPacket.getIntegers().write(0, entityId);
rotationPacket.getBytes().write(0, (byte) (location.getYaw() * 256 / 360));
return rotationPacket;
}
public static PacketContainer getRidingPacket(final int mountId, final int passengerId) {
final PacketContainer ridingPacket = new PacketContainer(PacketType.Play.Server.MOUNT);
ridingPacket.
getIntegers().
write(0, mountId);
ridingPacket.getIntegerArrays().write(0, new int[]{passengerId});
return ridingPacket;
}
public static PacketContainer getEntityDestroyPacket(final int entityId) {
return destroyPacket.get(entityId);
}
public static PacketContainer getSoundPacket(
final Player player,
final Location location,
final MinecraftKey name,
final float volume,
final float pitch,
final EnumWrappers.SoundCategory soundCategory
) {
final var manager = ProtocolLibrary.getProtocolManager();
final var packet = manager.createPacket(PacketType.Play.Server.CUSTOM_SOUND_EFFECT);
packet.getMinecraftKeys()
.write(
0,
name
);
packet.getSoundCategories()
.write(0, EnumWrappers.SoundCategory.valueOf(soundCategory.name()));
packet.getIntegers()
.write(0, location.getBlockX() * 8)
.write(
1, location.getBlockY() * 8
)
.write(2, location.getBlockZ() * 8);
packet.getFloat()
.write(0, volume)
.write(1, pitch);
return packet;
}
public static PacketContainer getFakePlayerSpawnPacket(final Location location, final UUID uuid, final int entityId) throws IllegalStateException {
if (playerPackets == null) throw new IllegalStateException("This cannot be used in version: " + Bukkit.getVersion());
return playerPackets.getSpawnPacket(location, uuid, entityId);
}
public static PacketContainer getFakePlayerInfoPacket(final Player player, final UUID uuid) throws IllegalStateException {
if (playerPackets == null) throw new IllegalStateException("This cannot be used in version: " + Bukkit.getVersion());
return playerPackets.getPlayerInfoPacket(player, uuid);
}
public static PacketContainer getRemovePlayerPacket(final Player player, final UUID uuid, final int entityId) {
if (playerPackets == null) throw new IllegalStateException("This cannot be used in version: " + Bukkit.getVersion());
return playerPackets.getRemovePacket(player, uuid, entityId);
}
public static void sendPacket(final Player to, final PacketContainer... packets) {
final ProtocolManager protocolManager = ProtocolLibrary.getProtocolManager();
try {
for (final PacketContainer packet : packets) {
protocolManager.sendServerPacket(to, packet);
}
} catch (final InvocationTargetException exception) {
exception.printStackTrace();
}
}
public static void sendPacketToOnline(final PacketContainer... packets) {
for (final Player player : Bukkit.getOnlinePlayers()) {
sendPacket(player, packets);
}
}
}

View File

@@ -0,0 +1,35 @@
package io.github.fisher2911.hmccosmetics.user;
import org.bukkit.inventory.EntityEquipment;
import org.bukkit.inventory.EquipmentSlot;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.Nullable;
import java.util.EnumMap;
import java.util.Map;
public class Equipment {
private static final EquipmentSlot[] VALUES = EquipmentSlot.values();
private final Map<EquipmentSlot, ItemStack> equipment = new EnumMap<>(EquipmentSlot.class);
public Equipment() {
}
public static Equipment fromEntityEquipment(final EntityEquipment entityEquipment) {
final Equipment equipment = new Equipment();
for (final EquipmentSlot slot : VALUES) {
equipment.setItem(slot, entityEquipment.getItem(slot));
}
return equipment;
}
@Nullable
public ItemStack getItem(final EquipmentSlot slot) {
return this.equipment.get(slot);
}
public void setItem(final EquipmentSlot slot, @Nullable final ItemStack itemStack) {
this.equipment.put(slot, itemStack);
}
}

View File

@@ -0,0 +1,213 @@
package io.github.fisher2911.hmccosmetics.user;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.wrappers.EnumWrappers;
import com.comphenix.protocol.wrappers.Pair;
import com.comphenix.protocol.wrappers.WrappedDataWatcher;
import com.comphenix.protocol.wrappers.WrappedDataWatcher.Serializer;
import io.github.fisher2911.hmccosmetics.config.Settings;
import io.github.fisher2911.hmccosmetics.gui.ArmorItem;
import io.github.fisher2911.hmccosmetics.inventory.PlayerArmor;
import io.github.fisher2911.hmccosmetics.packet.PacketManager;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.potion.PotionEffectType;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
public class User {
private final UUID uuid;
private final PlayerArmor playerArmor;
protected Wardrobe wardrobe;
private ArmorItem lastSetItem = ArmorItem.empty(ArmorItem.Type.HAT);
private boolean hasArmorStand;
private final int armorStandId;
public User(final UUID uuid, final PlayerArmor playerArmor, final Wardrobe wardrobe, final int armorStandId) {
this.uuid = uuid;
this.playerArmor = playerArmor;
this.wardrobe = wardrobe;
this.armorStandId = armorStandId;
}
protected User(final UUID uuid, final PlayerArmor playerArmor, final int armorStandId) {
this.uuid = uuid;
this.playerArmor = playerArmor;
this.armorStandId = armorStandId;
}
public @Nullable Player getPlayer() {
return Bukkit.getPlayer(this.uuid);
}
public UUID getUuid() {
return this.uuid;
}
public PlayerArmor getPlayerArmor() {
return playerArmor;
}
public Wardrobe getWardrobe() {
return wardrobe;
}
protected void setPlayerArmor(final PlayerArmor playerArmor) {
for (final ArmorItem armorItem : playerArmor.getArmorItems()) {
this.playerArmor.setItem(armorItem);
}
}
protected void removeAllCosmetics() {
for (final ArmorItem.Type type : ArmorItem.Type.values()) {
this.removeItem(type);
}
}
public int getArmorStandId() {
return armorStandId;
}
protected ArmorItem setItem(final ArmorItem armorItem) {
this.lastSetItem = armorItem;
return this.playerArmor.setItem(armorItem);
}
protected ArmorItem removeItem(final ArmorItem.Type type) {
return this.setItem(ArmorItem.empty(type));
}
public void spawnArmorStand(final Player other, final Location location) {
final PacketContainer packet = PacketManager.getEntitySpawnPacket(location, this.armorStandId, EntityType.ARMOR_STAND);
PacketManager.sendPacket(other, packet);
}
public void spawnArmorStand(final Settings settings) {
if (this.hasArmorStand) {
this.updateArmorStand(settings);
return;
}
final Player player = this.getPlayer();
if (player == null) return;
for (final Player p : Bukkit.getOnlinePlayers()) {
this.spawnArmorStand(p, player.getLocation());
}
this.hasArmorStand = true;
}
public void updateArmorStand(final Settings settings) {
if (!this.hasArmorStand) {
this.spawnArmorStand(settings);
}
for (final Player player : Bukkit.getOnlinePlayers()) {
this.updateArmorStand(player, settings);
}
}
public void updateArmorStand(final Player other, final Settings settings) {
final Player player = this.getPlayer();
if (player == null) return;
this.updateArmorStand(other, settings, player.getLocation());
}
public void updateArmorStand(final Player other, final Settings settings, final Location location) {
final List<Pair<EnumWrappers.ItemSlot, ItemStack>> equipmentList = new ArrayList<>();
final boolean hidden = !this.shouldShow(other);
if (hidden) {
equipmentList.add(new Pair<>(EnumWrappers.ItemSlot.HEAD,
new ItemStack(Material.AIR)
));
} else {
equipmentList.add(new Pair<>(EnumWrappers.ItemSlot.HEAD,
this.playerArmor.getBackpack().getColored()
));
}
final PacketContainer armorPacket = PacketManager.getEquipmentPacket(equipmentList, this.armorStandId);
final PacketContainer rotationPacket = PacketManager.getRotationPacket(this.armorStandId, location);
final PacketContainer ridingPacket = PacketManager.getRidingPacket(this.getEntityId(), this.armorStandId);
final PacketContainer metaContainer = new PacketContainer(PacketType.Play.Server.ENTITY_METADATA);
WrappedDataWatcher metaData = new WrappedDataWatcher();
final Serializer byteSerializer = WrappedDataWatcher.Registry.get(Byte.class);
metaData.setObject(new WrappedDataWatcher.WrappedDataWatcherObject(0, byteSerializer), (byte) (0x20));
metaData.setObject(new WrappedDataWatcher.WrappedDataWatcherObject(15, byteSerializer), (byte) (0x10));
metaContainer.getIntegers().write(0, this.armorStandId);
metaContainer.getWatchableCollectionModifier().write(0, metaData.getWatchableObjects());
PacketManager.sendPacket(other, armorPacket, metaContainer, rotationPacket, ridingPacket);
if (hidden) return;
final int lookDownPitch = settings.getCosmeticSettings().getLookDownPitch();
if (lookDownPitch != -1 &&
this.isFacingDown(location, lookDownPitch)) {
equipmentList.set(0, new Pair<>(EnumWrappers.ItemSlot.HEAD,
new ItemStack(Material.AIR)
));
if (!this.uuid.equals(other.getUniqueId())) return;
PacketManager.sendPacket(other, PacketManager.getEquipmentPacket(equipmentList, this.armorStandId));
}
}
public boolean shouldShow(final Player other) {
final Player player = this.getPlayer();
return player == null ||
(!player.hasPotionEffect(PotionEffectType.INVISIBILITY) &&
other.canSee(player) &&
!player.isSwimming());
}
private boolean isFacingDown(final Location location, final int pitchLimit) {
return location.getPitch() > pitchLimit;
}
public void despawnAttached() {
PacketManager.sendPacketToOnline(PacketManager.getEntityDestroyPacket(this.armorStandId));
this.hasArmorStand = false;
}
public boolean hasArmorStand() {
return hasArmorStand;
}
public ArmorItem getLastSetItem() {
return lastSetItem;
}
public int getEntityId() {
final Player player = this.getPlayer();
if (player == null) return -1;
return player.getEntityId();
}
public boolean hasPermissionToUse(final ArmorItem armorItem) {
final Player player = this.getPlayer();
if (player == null) return false;
return player.hasPermission(armorItem.getPermission());
}
}

View File

@@ -0,0 +1,276 @@
package io.github.fisher2911.hmccosmetics.user;
import com.comphenix.protocol.wrappers.EnumWrappers;
import com.comphenix.protocol.wrappers.Pair;
import com.comphenix.protocol.wrappers.WrappedDataWatcher;
import io.github.fisher2911.hmccosmetics.HMCCosmetics;
import io.github.fisher2911.hmccosmetics.api.CosmeticItem;
import io.github.fisher2911.hmccosmetics.api.event.CosmeticChangeEvent;
import io.github.fisher2911.hmccosmetics.concurrent.Threads;
import io.github.fisher2911.hmccosmetics.config.CosmeticSettings;
import io.github.fisher2911.hmccosmetics.config.Settings;
import io.github.fisher2911.hmccosmetics.gui.ArmorItem;
import io.github.fisher2911.hmccosmetics.inventory.PlayerArmor;
import io.github.fisher2911.hmccosmetics.message.Message;
import io.github.fisher2911.hmccosmetics.message.MessageHandler;
import io.github.fisher2911.hmccosmetics.message.Placeholder;
import io.github.fisher2911.hmccosmetics.message.Translation;
import io.github.fisher2911.hmccosmetics.packet.PacketManager;
import io.github.fisher2911.hmccosmetics.util.builder.ItemBuilder;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.inventory.EquipmentSlot;
import org.bukkit.inventory.ItemStack;
import org.bukkit.scheduler.BukkitTask;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
public class UserManager {
private final HMCCosmetics plugin;
private final Settings settings;
private final MessageHandler messageHandler;
private final Map<UUID, User> userMap = new HashMap<>();
private final Map<Integer, User> armorStandIdMap = new HashMap<>();
private BukkitTask teleportTask;
public UserManager(final HMCCosmetics plugin) {
this.plugin = plugin;
this.settings = this.plugin.getSettings();
this.messageHandler = this.plugin.getMessageHandler();
}
public void add(final User user) {
this.userMap.put(user.getUuid(), user);
this.armorStandIdMap.put(user.getArmorStandId(), user);
this.updateCosmetics(user);
}
public Optional<User> get(final UUID uuid) {
return Optional.ofNullable(this.userMap.get(uuid));
}
public Collection<User> getAll() {
return this.userMap.values();
}
public void remove(final UUID uuid) {
final User user = this.userMap.remove(uuid);
if (user == null) return;
this.armorStandIdMap.remove(user.getArmorStandId());
final PlayerArmor copy = user.getPlayerArmor().copy();
user.removeAllCosmetics();
this.updateCosmetics(user);
user.despawnAttached();
user.setPlayerArmor(copy);
Threads.getInstance().execute(() -> this.plugin.getDatabase().saveUser(user));
}
public void startTeleportTask() {
// throws an error on first load of registry if this isn't here
WrappedDataWatcher.Registry.get(Byte.class);
this.teleportTask = Bukkit.getScheduler().runTaskTimerAsynchronously(
this.plugin,
() -> {
for (final User user : this.userMap.values()) {
user.updateArmorStand(this.plugin.getSettings());
}
},
1,
1
);
}
public void resendCosmetics(final Player player) {
for (final User user : this.userMap.values()) {
final Player p = user.getPlayer();
if (p == null) continue;
user.spawnArmorStand(player, p.getLocation());
this.updateCosmetics(user, player);
}
}
public void updateCosmetics(final UUID uuid) {
this.get(uuid).ifPresent(this::updateCosmetics);
}
public void updateCosmetics(final User user) {
for (final Player player : Bukkit.getOnlinePlayers()) {
this.updateCosmetics(user, player);
}
}
public void updateCosmetics(final User user, final Player other) {
final Player player = user.getPlayer();
final Equipment equipment;
if (player == null) {
equipment = new Equipment();
} else {
equipment = Equipment.fromEntityEquipment(player.getEquipment());
}
final PlayerArmor playerArmor = user.getPlayerArmor();
final List<Pair<EnumWrappers.ItemSlot, ItemStack>> hatList = new ArrayList<>();
final List<Pair<EnumWrappers.ItemSlot, ItemStack>> offHandList = new ArrayList<>();
final boolean hidden = !user.shouldShow(other);
final ItemStack hat = this.getCosmeticItem(user, equipment, playerArmor.getHat(), EquipmentSlot.HEAD, hidden);
hatList.add(new Pair<>(EnumWrappers.ItemSlot.HEAD, hat));
final ItemStack offHand = this.getCosmeticItem(user, equipment, playerArmor.getOffHand(), EquipmentSlot.OFF_HAND, hidden);
offHandList.add(new Pair<>(EnumWrappers.ItemSlot.OFFHAND, offHand));
if (!hat.equals(equipment.getItem(EquipmentSlot.HEAD))) {
PacketManager.sendPacket(
other,
PacketManager.getEquipmentPacket(
hatList,
user.getEntityId()
)
);
}
if (!offHand.equals(equipment.getItem(EquipmentSlot.OFF_HAND))) {
PacketManager.sendPacket(
other,
PacketManager.getEquipmentPacket(
offHandList,
user.getEntityId()
)
);
}
}
private ItemStack getCosmeticItem(
final User user,
final Equipment equipment,
final ArmorItem armorItem,
final EquipmentSlot slot,
final boolean hidden) {
final CosmeticSettings cosmeticSettings = this.settings.getCosmeticSettings();
final Map<String, String> placeholders = Map.of(Placeholder.ALLOWED, Translation.TRUE,
Placeholder.ENABLED, Translation.TRUE);
ItemStack itemStack = ItemBuilder.from(armorItem.getColored()).
namePlaceholders(placeholders).
lorePlaceholders(placeholders).
build();
final boolean isAir = itemStack.getType().isAir();
final boolean requireEmpty = cosmeticSettings.requireEmpty(slot);
if (!isAir && (!requireEmpty || user instanceof Wardrobe) && !hidden) return itemStack;
if (equipment == null) return itemStack;
final ItemStack equipped = equipment.getItem(slot);
if (equipped != null && equipped.getType() != Material.AIR) return equipped;
return itemStack;
}
public void setItem(final User user, final ArmorItem armorItem) {
final Wardrobe wardrobe = user.getWardrobe();
final User setUser;
if (wardrobe.isActive()) {
setUser = wardrobe;
} else {
setUser = user;
}
ArmorItem previous = setUser.getPlayerArmor().getItem(armorItem.getType());
final CosmeticChangeEvent event =
new CosmeticChangeEvent(new CosmeticItem(armorItem.copy()), new CosmeticItem(previous.copy()), setUser);
Bukkit.getPluginManager().callEvent(event);
if (event.isCancelled()) return;
setUser.setItem(event.getCosmeticItem().getArmorItem());
Bukkit.getScheduler().runTaskAsynchronously(this.plugin, () -> {
switch (armorItem.getType()) {
case HAT, OFF_HAND -> this.updateCosmetics(setUser);
case BACKPACK -> {
if (wardrobe.isActive()) setUser.updateArmorStand(settings);
}
}
});
}
public void removeItem(final User user, final ArmorItem.Type type) {
this.setItem(user, ArmorItem.empty(type));
}
/**
* @param user
* @param armorItem
* @param removeMessage
* @param setMessage
* @return the item that was set
*/
public ArmorItem setOrUnset(
final User user,
final ArmorItem armorItem,
final Message removeMessage,
final Message setMessage) {
final Player player = user.getPlayer();
final ArmorItem.Type type = armorItem.getType();
final ArmorItem empty = ArmorItem.empty(type);
if (player == null) {
return empty;
}
final ArmorItem check = user.getPlayerArmor().getItem(type);
if (armorItem.getId().equals(check.getId())) {
this.setItem(user, ArmorItem.empty(type));
messageHandler.sendMessage(
player,
removeMessage
);
return empty;
}
this.setItem(user, armorItem);
messageHandler.sendMessage(
player,
setMessage
);
return armorItem;
}
public void removeAll() {
for (final var user : this.userMap.values()) {
user.despawnAttached();
}
this.userMap.clear();
}
public void cancelTeleportTask() {
this.teleportTask.cancel();
}
}

View File

@@ -0,0 +1,127 @@
package io.github.fisher2911.hmccosmetics.user;
import com.comphenix.protocol.events.PacketContainer;
import io.github.fisher2911.hmccosmetics.HMCCosmetics;
import io.github.fisher2911.hmccosmetics.config.Settings;
import io.github.fisher2911.hmccosmetics.config.WardrobeSettings;
import io.github.fisher2911.hmccosmetics.gui.ArmorItem;
import io.github.fisher2911.hmccosmetics.inventory.PlayerArmor;
import io.github.fisher2911.hmccosmetics.packet.PacketManager;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.Nullable;
import java.io.BufferedReader;
import java.util.UUID;
public class Wardrobe extends User {
private final HMCCosmetics plugin;
private final UUID ownerUUID;
private final int entityId;
private boolean active;
private boolean spawned;
private Location currentLocation;
public Wardrobe(
final HMCCosmetics plugin,
final UUID uuid,
final UUID ownerUUID,
final PlayerArmor playerArmor,
final int armorStandId,
final int entityId,
final boolean active) {
super(uuid, playerArmor, armorStandId);
this.plugin = plugin;
this.ownerUUID = ownerUUID;
this.entityId = entityId;
this.active = active;
this.wardrobe = this;
}
public void spawnFakePlayer(final Player viewer) {
final WardrobeSettings settings = this.plugin.getSettings().getWardrobeSettings();
if (settings.inDistanceOfStatic(viewer.getLocation())) {
this.currentLocation = settings.getLocation();
} else if (this.currentLocation == null) {
this.currentLocation = viewer.getLocation().clone();
this.currentLocation.setPitch(0);
this.currentLocation.setYaw(0);
} else if (this.spawned) {
return;
}
final PacketContainer playerSpawnPacket = PacketManager.getFakePlayerSpawnPacket(
this.currentLocation,
this.getUuid(),
this.entityId
);
final PacketContainer playerInfoPacket = PacketManager.getFakePlayerInfoPacket(
viewer,
this.getUuid()
);
PacketManager.sendPacket(viewer, playerInfoPacket, playerSpawnPacket);
this.spawnArmorStand(viewer, this.currentLocation);
this.updateArmorStand(viewer, plugin.getSettings(), this.currentLocation);
// PacketManager.sendPacket(viewer, PacketManager.getRotationPacket(this.getEntityId(), this.currentLocation));
this.spawned = true;
}
@Override
public void updateArmorStand(final Player player, final Settings settings) {
this.updateArmorStand(player, settings, this.currentLocation);
}
public void despawnFakePlayer(final Player viewer) {
final WardrobeSettings settings = this.plugin.getSettings().getWardrobeSettings();
PacketManager.sendPacket(viewer, PacketManager.getEntityDestroyPacket(this.getEntityId()));
this.despawnAttached();
this.active = false;
this.spawned = false;
this.currentLocation = null;
this.getPlayerArmor().clear();
if (settings.isAlwaysDisplay()) {
this.currentLocation = settings.getLocation();
if (this.currentLocation == null) return;
this.spawnFakePlayer(viewer);
}
}
@Override
public int getEntityId() {
return this.entityId;
}
@Override
public boolean hasPermissionToUse(final ArmorItem armorItem) {
return true;
}
public boolean isActive() {
return active;
}
public void setActive(final boolean active) {
this.active = active;
}
public void setCurrentLocation(final Location currentLocation) {
this.currentLocation = currentLocation;
}
@Nullable
public Location getCurrentLocation() {
return currentLocation;
}
@Override
@Nullable
public Player getPlayer() {
return Bukkit.getPlayer(this.ownerUUID);
}
}

View File

@@ -8,13 +8,9 @@ import org.bukkit.persistence.PersistentDataType;
public class Keys {
static HMCCosmetics plugin;
static {
plugin = HMCCosmetics.getPlugin(HMCCosmetics.class);
}
static HMCCosmetics plugin = HMCCosmetics.getPlugin(HMCCosmetics.class);
public static final NamespacedKey ITEM_KEY = new NamespacedKey(plugin, "cosmetic");
public static final NamespacedKey ARMOR_STAND_KEY = new NamespacedKey(plugin, "armor-stand");
public static void setKey(final ItemStack itemStack) {
final ItemMeta itemMeta = itemStack.getItemMeta();

View File

@@ -0,0 +1,41 @@
package io.github.fisher2911.hmccosmetics.util;
import io.github.fisher2911.hmccosmetics.HMCCosmetics;
import io.github.fisher2911.hmccosmetics.message.Adventure;
import net.kyori.adventure.text.Component;
public class StringUtils {
private static final HMCCosmetics plugin;
static {
plugin = HMCCosmetics.getPlugin(HMCCosmetics.class);
}
/**
* @param parsed message to be parsed
* @return MiniMessage parsed string
*/
public static Component parse(final String parsed) {
return Adventure.MINI_MESSAGE.deserialize(parsed);
}
public static String parseStringToString(final String parsed) {
return Adventure.SERIALIZER.serialize(Adventure.MINI_MESSAGE.deserialize(parsed));
}
public static String formatArmorItemType(String type) {
type = type.toLowerCase();
final String[] parts = type.split(" ");
final String firstPart = parts[0].substring(0, 1).toUpperCase() + parts[0].substring(1);
if (parts.length == 1) {
return firstPart;
}
return firstPart + parts[1].substring(0, 1).toUpperCase() + parts[1].substring(1);
}
}

View File

@@ -1,33 +1,33 @@
package io.github.fisher2911.hmccosmetics.util;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Function;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class Utils {
/**
* @param original Object to be checked if null
* @param original Object to be checked if null
* @param replacement Object returned if original is null
* @return original if not null, otherwise replacement
*/
public static <T> T replaceIfNull(final @Nullable T original, final @NotNull T replacement) {
return replaceIfNull(original, replacement, t -> {});
return replaceIfNull(original, replacement, t -> {
});
}
/**
*
* @param original Object to be checked if null
* @param original Object to be checked if null
* @param replacement Object returned if original is null
* @param consumer accepts the original object, can be used for logging
* @return original if not null, otherwise replacement
*/
public static <T> T replaceIfNull(final @Nullable T original, final T replacement, final @NotNull Consumer<T> consumer) {
public static <T> T replaceIfNull(final @Nullable T original, final T replacement,
final @NotNull Consumer<T> consumer) {
if (original == null) {
consumer.accept(replacement);
return replacement;
@@ -37,7 +37,6 @@ public class Utils {
}
/**
*
* @param t object being checked
* @param consumer accepted if t is not null
* @param <T> type
@@ -51,14 +50,14 @@ public class Utils {
}
/**
*
* @param t object being checked
* @param function applied if t is not null
* @param <T> type
* @return
*/
public static <T> Optional<T> returnIfNotNull(final @Nullable T t, final @NotNull Function<T, T> function) {
public static <T> Optional<T> returnIfNotNull(final @Nullable T t,
final @NotNull Function<T, T> function) {
if (t == null) {
return Optional.empty();
}
@@ -66,7 +65,6 @@ public class Utils {
}
/**
*
* @param enumAsString Enum value as a string to be parsed
* @param enumClass enum type enumAsString is to be converted to
* @param defaultEnum default value to be returned
@@ -74,13 +72,13 @@ public class Utils {
*/
public static <E extends Enum<E>> E stringToEnum(final @NotNull String enumAsString,
final @NotNull Class<E> enumClass,
E defaultEnum) {
return stringToEnum(enumAsString, enumClass, defaultEnum, e -> {});
final @NotNull Class<E> enumClass,
E defaultEnum) {
return stringToEnum(enumAsString, enumClass, defaultEnum, e -> {
});
}
/**
*
* @param enumAsString Enum value as a string to be parsed
* @param enumClass enum type enumAsString is to be converted to
* @param defaultEnum default value to be returned
@@ -89,9 +87,9 @@ public class Utils {
*/
public static <E extends Enum<E>> E stringToEnum(final @NotNull String enumAsString,
@NotNull final Class<E> enumClass,
final E defaultEnum,
final @NotNull Consumer<E> consumer) {
@NotNull final Class<E> enumClass,
final E defaultEnum,
final @NotNull Consumer<E> consumer) {
try {
final E value = Enum.valueOf(enumClass, enumAsString);
consumer.accept(value);

View File

@@ -7,13 +7,9 @@ import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.inventory.meta.LeatherArmorMeta;
import org.bukkit.inventory.meta.PotionMeta;
import java.util.EnumSet;
import java.util.Set;
public class ColorBuilder extends ItemBuilder{
public class ColorBuilder extends ItemBuilder {
/**
*
* @param material ItemStack material
*/
@@ -22,7 +18,6 @@ public class ColorBuilder extends ItemBuilder{
}
/**
*
* @param itemStack ItemStack
*/
@@ -31,7 +26,6 @@ public class ColorBuilder extends ItemBuilder{
}
/**
*
* @param material ItemStack material
* @return this
* @throws IllegalArgumentException thrown if itemStack's type can not change color
@@ -45,7 +39,6 @@ public class ColorBuilder extends ItemBuilder{
}
/**
*
* @param itemStack ItemStack
* @return this
* @throws IllegalArgumentException thrown if itemStack's type can not change color
@@ -59,8 +52,18 @@ public class ColorBuilder extends ItemBuilder{
return new ColorBuilder(itemStack);
}
public static boolean canBeColored(final Material material) {
return canBeColored(new ItemStack(material));
}
public static boolean canBeColored(final ItemStack itemStack) {
final ItemMeta itemMeta = itemStack.getItemMeta();
return (itemMeta instanceof LeatherArmorMeta ||
itemMeta instanceof PotionMeta);
}
/**
*
* @param color armor color
* @return this
*/
@@ -75,14 +78,4 @@ public class ColorBuilder extends ItemBuilder{
return this;
}
public static boolean canBeColored(final Material material) {
return canBeColored(new ItemStack(material));
}
public static boolean canBeColored(final ItemStack itemStack) {
final ItemMeta itemMeta = itemStack.getItemMeta();
return (itemMeta instanceof LeatherArmorMeta ||
itemMeta instanceof PotionMeta);
}
}

View File

@@ -1,19 +1,17 @@
package io.github.fisher2911.hmccosmetics.util.builder;
import io.github.fisher2911.hmccosmetics.message.Adventure;
import io.github.fisher2911.hmccosmetics.util.StringUtils;
import net.kyori.adventure.text.Component;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.inventory.ItemFlag;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import io.github.fisher2911.hmccosmetics.message.Placeholder;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemFlag;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
public class ItemBuilder {
@@ -36,7 +34,8 @@ public class ItemBuilder {
ItemBuilder(final ItemStack itemStack) {
this.material = itemStack.getType();
this.itemMeta = itemStack.hasItemMeta() ? itemStack.getItemMeta() : Bukkit.getItemFactory().getItemMeta(this.material);
this.itemMeta = itemStack.hasItemMeta() ? itemStack.getItemMeta()
: Bukkit.getItemFactory().getItemMeta(this.material);
}
/**
@@ -80,14 +79,6 @@ public class ItemBuilder {
return this;
}
public ItemBuilder name(final Component name) {
if (this.itemMeta == null) {
return this;
}
this.itemMeta.displayName(name);
return this;
}
/**
* Sets placeholders to the item's name
*
@@ -99,7 +90,7 @@ public class ItemBuilder {
return this;
}
final String name = StringUtils.
final String name = Placeholder.
applyPlaceholders(this.itemMeta.getDisplayName(), placeholders);
this.itemMeta.setDisplayName(name);
return this;
@@ -138,7 +129,7 @@ public class ItemBuilder {
}
for (final String line : previousLore) {
lore.add(StringUtils.applyPlaceholders(
lore.add(Placeholder.applyPlaceholders(
line, placeholders
));
}
@@ -147,6 +138,44 @@ public class ItemBuilder {
return this;
}
public ItemBuilder papiPlaceholders(final Player player) {
this.lorePapiPlaceholders(player);
this.namePapiPlaceholders(player);
return this;
}
private void lorePapiPlaceholders(final Player player) {
if (this.itemMeta == null) {
return;
}
final List<String> newLore = new ArrayList<>();
final List<String> lore = this.itemMeta.getLore();
if (lore == null) {
return;
}
for (final String line : this.itemMeta.getLore()) {
newLore.add(Placeholder.applyPapiPlaceholders(player, line));
}
this.itemMeta.setLore(newLore);
}
private void namePapiPlaceholders(final Player player) {
if (this.itemMeta == null) {
return;
}
this.itemMeta.setDisplayName(
Placeholder.applyPapiPlaceholders(
player,
this.itemMeta.getDisplayName()
)
);
}
/**
* @param unbreakable whether the ItemStack is unbreakable
* @return this
@@ -172,16 +201,18 @@ public class ItemBuilder {
}
/**
* @param enchantments enchants to be added to the ItemStack
* @param enchantments enchants to be added to the ItemStack
* @param ignoreLeveLRestrictions whether to ignore enchantment level restrictions
* @return this
*/
public ItemBuilder enchants(final Map<Enchantment, Integer> enchantments, boolean ignoreLeveLRestrictions) {
public ItemBuilder enchants(final Map<Enchantment, Integer> enchantments,
boolean ignoreLeveLRestrictions) {
if (this.itemMeta == null) {
return this;
}
enchantments.forEach((enchantment, level) -> this.itemMeta.addEnchant(enchantment, level, ignoreLeveLRestrictions));
enchantments.forEach((enchantment, level) -> this.itemMeta.addEnchant(enchantment, level,
ignoreLeveLRestrictions));
return this;
}

View File

@@ -2,15 +2,14 @@ package io.github.fisher2911.hmccosmetics.util.builder;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.properties.Property;
import java.lang.reflect.Field;
import java.util.UUID;
import org.bukkit.Material;
import org.bukkit.OfflinePlayer;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.SkullMeta;
import org.jetbrains.annotations.NotNull;
import java.lang.reflect.Field;
import java.util.UUID;
/**
* Some parts taken from https://github.com/TriumphTeam/triumph-gui/blob/master/core/src/main/java/dev/triumphteam/gui/builder/item/SkullBuilder.java
*/
@@ -23,7 +22,8 @@ public class SkullBuilder extends ItemBuilder {
Field field;
try {
final SkullMeta skullMeta = (SkullMeta) new ItemStack(Material.PLAYER_HEAD).getItemMeta();
final SkullMeta skullMeta = (SkullMeta) new ItemStack(
Material.PLAYER_HEAD).getItemMeta();
field = skullMeta.getClass().getDeclaredField("profile");
field.setAccessible(true);
} catch (NoSuchFieldException e) {
@@ -36,7 +36,6 @@ public class SkullBuilder extends ItemBuilder {
/**
*
* @param material The material
*/
@@ -46,6 +45,7 @@ public class SkullBuilder extends ItemBuilder {
/**
* Creates a new SkullBuilder instance
*
* @return this
*/
@@ -55,7 +55,6 @@ public class SkullBuilder extends ItemBuilder {
/**
*
* @param player skull owner
* @return this
*/
@@ -68,7 +67,6 @@ public class SkullBuilder extends ItemBuilder {
}
/**
*
* @param texture skull texture
* @return this
*/

View File

@@ -0,0 +1,25 @@
cosmetic-settings:
require-empty-helmet: false
require-empty-off-hand: true
# The pitch the player must look down for the backpack to be removed
# Set to -1 for no removal
look-down-backpack-remove: 70
wardrobe:
# if true, the wardrobe will be removed when the player is damaged
disable-on-damage: true
# removed if player leaves this radius, set to -1 for infinite radius
display-radius: -1
# if the player can use the wardrobe in other locations
portable: false
# if the wardrobe should always be displayed in the location below
always-display: false
# spawn static wardrobe if in this radius of wardrobe-location
static-radius: 10
# location of static wardrobe, remove for none
wardrobe-location:
world: "World"
x: 0
y: 0
z: 0
yaw: 0
pitch: 0

View File

@@ -0,0 +1,8 @@
# SQLite or MySQL (case-insensitive)
type: "sqlite"
# The rest of these are only required for MySQL
name: "name"
username: "username"
password: "password"
ip: "ip"
port: 3306

View File

@@ -0,0 +1 @@
version: 2

View File

@@ -0,0 +1,100 @@
title: "<white>"
rows: 4
gui-type: dye
cosmetics-slots:
27: HAT
28: BACKPACK
29: OFF_HAND
items:
10:
material: LEATHER_HORSE_ARMOR
model-data: 1
color:
red: 255
green: 255
blue: 255
name: "<gray>Set to <#FFFFFF>white"
set-color:
red: 255
green: 255
blue: 255
11:
material: LEATHER_HORSE_ARMOR
model-data: 1
color:
red: 190
green: 20
blue: 20
name: "<gray>Set to <#BE1414>red"
set-color:
red: 190
green: 20
blue: 20
12:
material: LEATHER_HORSE_ARMOR
model-data: 1
color:
red: 234
green: 92
blue: 43
name: "<gray>Set to <#EA5C2B>orange"
set-color:
red: 234
green: 92
blue: 43
13:
material: LEATHER_HORSE_ARMOR
model-data: 1
color:
red: 255
green: 201
blue: 0
name: "<gray>Set to <#FFC900>yellow"
set-color:
red: 255
green: 201
blue: 0
14:
material: LEATHER_HORSE_ARMOR
model-data: 1
color:
red: 163
green: 218
blue: 141
name: "<gray>Set to <#A3DA8D>green"
set-color:
red: 163
green: 218
blue: 141
15:
material: LEATHER_HORSE_ARMOR
model-data: 1
color:
red: 55
green: 49
blue: 181
name: "<gray>Set to <#3731B5>blue"
set-color:
red: 55
green: 49
blue: 181
16:
material: LEATHER_HORSE_ARMOR
model-data: 1
color:
red: 137
green: 70
blue: 166
name: "<gray>Set to <#8946A6>purple"
set-color:
red: 137
green: 70
blue: 166
31:
material: PAPER
name: "<#F7DCFA>Previous Page"
amount: 1
model-data: 1
action: # See how the action system works on the wiki
any:
open-menu: main

View File

@@ -0,0 +1,91 @@
title: "<white>" # GUI Title
rows: 5 # Rows in the GUI
items:
1: # GUI Slot number
material: LEATHER_HORSE_ARMOR # Also supports Oraxen items! Format: "oraxen:item_name"
name: "<rainbow>Colorful Hat</rainbow>"
lore: # Lore displayed when the player owns the item
- ""
- "<gray>Enabled: <#6D9DC5>%enabled%"
- "<gray>Allowed: <#6D9DC5>%allowed%"
locked-lore: # Lore displayed when the player does not have the correct permission.
- "<red>You do not own this item!"
amount: 1
model-data: 2 # CustomModelData Number
type: HAT
dyeable: true # Enables dyeable item feature
color: # Sets default color for item. Uses RGB format.
red: 5
green: 230
blue: 100
action: # See how the action system works on the wiki
any:
open-menu: dye-menu
permission: "cosmetics.colorful_hat" # Can be anything you want.
id: colorful_hat # Internal identifier. Can be anything you want.
2:
material: PAPER
name: "<blue>Backpack"
lore:
- ""
- "<gray>Enabled: <#6D9DC5>%enabled%"
- "<gray>Allowed: <#6D9DC5>%allowed%"
locked-lore:
- "<red>You do not own this item!"
amount: 1
model-data: 4
type: BACKPACK
permission: ""
id: backpack
3:
material: PAPER
name: "<blue>Lantern Cosmetic"
lore:
- ""
- "<gray>Enabled: <#6D9DC5>%enabled%"
- "<gray>Allowed: <#6D9DC5>%allowed%"
locked-lore:
- "<red>You do not own this item!"
amount: 1
model-data: 5
type: OFF_HAND
permission: ""
id: lantern_cosmetic
4:
material: PAPER
name: "<blue>Baseball Hat"
lore:
- ""
- "<gray>Enabled: <#6D9DC5>%enabled%"
- "<gray>Allowed: <#6D9DC5>%allowed%"
locked-lore:
- "<red>You do not own this item!"
amount: 1
model-data: 6
type: HAT
permission: ""
id: baseball_hat
37:
material: PAPER
name: "<#40B7D6>Previous Page"
amount: 1
model-data: 1
action:
any:
open-menu: menu-2
40:
material: PAPER
name: "<rainbow>Customization Menu</rainbow>"
amount: 1
model-data: 3
action:
any:
open-menu: dye-menu
43:
material: PAPER
name: "<#40B7D6>Next Page"
amount: 1
model-data: 2
action:
any:
open-menu: menu-2

View File

@@ -0,0 +1,37 @@
prefix: "<white>"
no-permission: "%prefix% <red>No Permission!"
no-cosmetic-permission: "%prefix% <red>You do not have permission for this cosmetic!"
set-hat: "%prefix% <gradient:#6D9DC5:#45CDE9>Applied hat!"
removed-hat: "%prefix% <gradient:#6D9DC5:#45CDE9>Removed hat!"
set-backpack: "%prefix% <gradient:#6D9DC5:#45CDE9>Applied backpack!"
removed-backpack: "%prefix% <gradient:#6D9DC5:#45CDE9>Removed backpack!"
set-off-hand: "%prefix% <gradient:#6D9DC5:#45CDE9>Applied offhand!"
removed-off-hand: "%prefix% <gradient:#6D9DC5:#45CDE9>Removed offhand!"
set-dye-color: "%prefix% <gradient:#6D9DC5:#45CDE9>Set color of %item%!"
must-be-player: "<red>You must be a player to do this!"
reloaded: "%prefix% <gradient:#00ff87:#60efff>Config files reloaded!"
invalid-type: "%prefix% <red>Invalid cosmetic type, please use <u>hat</u>, <u>backpack</u> or <u>off_hand</u>!"
set-other-backpack: "%prefix% <gradient:#6D9DC5:#45CDE9>You have set the backpack of %player% to %type%"
set-other-hat: "%prefix% <gradient:#6D9DC5:#45CDE9>You have set the hat of %player% to %type%"
set-other-off-hand: "%prefix% <gradient:#6D9DC5:#45CDE9>You have set the off hand of %player% to %type%"
opened-wardrobe: "%prefix% <gradient:#6D9DC5:#45CDE9>Viewing wardrobe!"
closed-wardrobe: "%prefix% <gradient:#6D9DC5:#45CDE9>Closed wardrobe!"
wardrobe-already-open: "%prefix% <gradient:#6D9DC5:#45CDE9><red>The wardrobe is already open!"
not-near-wardrobe: "%prefix% <gradient:#6D9DC5:#45CDE9><red>You are not near the wardrobe!"
cannot-use-portable-wardrobe: "%prefix% <gradient:#6D9DC5:#45CDE9><red>You cannot use the portable wardrobe!"
opened-other-wardrobe: "%prefix% <gradient:#6D9DC5:#45CDE9><red>Opening %player%'s wardrobe."
help-command: "<#6D9DC5><st> </st> %prefix% <gradient:#40B7D6:#6D9DC5>HMCCosmetics - Help</gradient> %prefix% <#6D9DC5> <st> </st>
<#5AE4B5>- <#40B7D6>/cosmetics - <#6D9DC5>Opens cosmetics GUI.
<#5AE4B5>- <#40B7D6>/cosmetics add <gray><USERNAME> <ID> (#HEX)</gray> - <#6D9DC5>Adds cosmetic to player.
<#5AE4B5>- <#40B7D6>/cosmetics remove <gray><USERNAME> <BACKPACK/HAT></gray> - <#6D9DC5>Removes cosmetic from a player.
<#5AE4B5>- <#40B7D6>/cosmetics dye <gray><BACKPACK/HAT></gray> - <#6D9DC5>Opens dye menu for the specified cosmetic type.
<#5AE4B5>- <#40B7D6>/cosmetics help - <#6D9DC5>Opens this menu.
<st> </st>"

View File

@@ -0,0 +1,3 @@
translations:
true: "true"
false: "false"

View File

@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.1-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

269
gradlew vendored
View File

@@ -1,7 +1,7 @@
#!/usr/bin/env sh
#!/bin/sh
#
# Copyright 2015 the original author or authors.
# Copyright <20> 2015-2021 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -17,67 +17,101 @@
#
##############################################################################
##
## Gradle start up script for UN*X
##
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions <20>$var<61>, <20>${var}<7D>, <20>${var:-default}<7D>, <20>${var+SET}<7D>,
# <20>${var#prefix}<7D>, <20>${var%suffix}<7D>, and <20>$( cmd )<29>;
# * compound commands having a testable exit status, especially <20>case<73>;
# * various built-in commands including <20>command<6E>, <20>set<65>, and <20>ulimit<69>.
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
APP_BASE_NAME=${0##*/}
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
MAX_FD=maximum
warn () {
echo "$*"
}
} >&2
die () {
echo
echo "$*"
echo
exit 1
}
} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MSYS* | MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
case "$( uname )" in #(
CYGWIN* ) cygwin=true ;; #(
Darwin* ) darwin=true ;; #(
MSYS* | MINGW* ) msys=true ;; #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
@@ -87,9 +121,9 @@ CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
JAVACMD=$JAVA_HOME/jre/sh/java
else
JAVACMD="$JAVA_HOME/bin/java"
JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
@@ -98,7 +132,7 @@ Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
JAVACMD=java
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
@@ -106,80 +140,95 @@ location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin or MSYS, switch paths to Windows format before running java
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=`expr $i + 1`
done
case $i in
0) set -- ;;
1) set -- "$args0" ;;
2) set -- "$args0" "$args1" ;;
3) set -- "$args0" "$args1" "$args2" ;;
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=`save "$@"`
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@"

22
nms/build.gradle.kts Normal file
View File

@@ -0,0 +1,22 @@
plugins {
id("java")
}
//group = "io.github.fisher2911"
//version = "1.7.1"
//description = "Intuitive, easy-to-use cosmetics plugin, designed for servers using resource packs.\n"
repositories {
mavenCentral()
maven("https://papermc.io/repo/repository/maven-public/")
maven("https://oss.sonatype.org/content/repositories/snapshots")
maven("https://jitpack.io")
maven("https://repo.dmulloy2.net/repository/public/")
}
dependencies {
compileOnly("com.mojang:authlib:1.5.25")
compileOnly("org.spigotmc:spigot:1.18-R0.1-SNAPSHOT")
compileOnly("org.jetbrains:annotations:22.0.0")
compileOnly("com.comphenix.protocol:ProtocolLib:4.7.0")
}

View File

@@ -0,0 +1,9 @@
package io.github.fisher2911.nms;
import com.comphenix.protocol.events.PacketContainer;
public interface DestroyPacket {
PacketContainer get(final int entityId);
}

View File

@@ -0,0 +1,15 @@
package io.github.fisher2911.nms;
import com.comphenix.protocol.events.PacketContainer;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import java.util.UUID;
public interface PlayerPackets {
PacketContainer getSpawnPacket(final Location location, UUID uuid, final int entityId);
PacketContainer getPlayerInfoPacket(final Player player, final UUID uuid);
PacketContainer getRemovePacket(final Player player, final UUID uuid, final int entityId);
}

View File

@@ -1,2 +0,0 @@
rootProject.name = 'HMCCosmetics'

Some files were not shown because too many files have changed in this diff Show More