├── .gitattributes ├── .github ├── FUNDING.yml └── workflows │ └── gradle.yml ├── .gitignore ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── src └── main ├── java └── xyz │ └── oribuin │ └── eternaltags │ ├── EternalAPI.java │ ├── EternalTags.java │ ├── action │ ├── Action.java │ ├── BroadcastAction.java │ ├── CloseAction.java │ ├── ConsoleAction.java │ ├── MessageAction.java │ ├── PlayerAction.java │ ├── PluginAction.java │ └── SoundAction.java │ ├── command │ ├── argument │ │ ├── CategoryArgumentHandler.java │ │ ├── MaterialArgumentHandler.java │ │ ├── PluginArgumentHandler.java │ │ └── TagsArgumentHandler.java │ └── impl │ │ ├── CategoriesCommand.java │ │ ├── ClearCommand.java │ │ ├── ConvertCommand.java │ │ ├── CreateCommand.java │ │ ├── DeleteCommand.java │ │ ├── EditCommand.java │ │ ├── FavoriteCommand.java │ │ ├── RandomCommand.java │ │ ├── ReloadCommand.java │ │ ├── SearchCommand.java │ │ ├── SetAllCommand.java │ │ ├── SetCommand.java │ │ ├── TagsCommand.java │ │ ├── def │ │ └── HelpCommand.java │ │ └── edit │ │ ├── EditCategoryCommand.java │ │ ├── EditDescriptionCommand.java │ │ ├── EditIconCommand.java │ │ ├── EditNameCommand.java │ │ ├── EditOrderCommand.java │ │ ├── EditPermissionCommand.java │ │ └── EditTagCommand.java │ ├── config │ └── Setting.java │ ├── conversion │ ├── AlonsoConversion.java │ ├── CIFYConversion.java │ ├── ConversionPlugin.java │ ├── DeluxeConversion.java │ └── ValidPlugin.java │ ├── database │ └── migration │ │ ├── _1_CreateInitialTables.java │ │ ├── _2_CreateNewTagTables.java │ │ ├── _3_ModifyTagDataItems.java │ │ └── _4_DeleteOldData.java │ ├── gui │ ├── MenuItem.java │ ├── MenuProvider.java │ ├── PluginMenu.java │ ├── enums │ │ └── SortType.java │ └── menu │ │ ├── CategoryGUI.java │ │ ├── FavouritesGUI.java │ │ └── TagsGUI.java │ ├── hook │ ├── Expansion.java │ └── VaultHook.java │ ├── listener │ ├── BungeeListener.java │ ├── ChatListener.java │ └── PlayerListeners.java │ ├── manager │ ├── CategoryManager.java │ ├── CommandManager.java │ ├── DataManager.java │ ├── LocaleManager.java │ ├── PluginConversionManager.java │ └── TagsManager.java │ ├── obj │ ├── Category.java │ ├── CategoryType.java │ ├── Tag.java │ ├── TagDescription.java │ └── TagUser.java │ └── util │ ├── EventWaiter.java │ ├── ItemBuilder.java │ ├── TagsUtils.java │ └── nms │ └── SkullUtils.java └── resources ├── categories.yml ├── locale └── en_US.yml ├── menus ├── category-gui.yml ├── favorites-gui.yml └── tags-gui.yml ├── plugin.yml └── tags.yml /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | patreon: RosewoodDevelopment 4 | -------------------------------------------------------------------------------- /.github/workflows/gradle.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | types: [ opened, synchronize, reopened ] 9 | 10 | jobs: 11 | build: 12 | name: Build 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v3 16 | - name: Restore gradle.properties 17 | env: 18 | GRADLE_PROPERTIES: ${{ secrets.GRADLE_PROPERTIES }} 19 | shell: bash 20 | run: | 21 | mkdir -p ~/.gradle/ 22 | echo "GRADLE_USER_HOME=${HOME}/.gradle" >> $GITHUB_ENV 23 | echo "${GRADLE_PROPERTIES}" > ~/.gradle/gradle.properties 24 | - name: Set up JDK 21 25 | uses: actions/setup-java@v2.5.1 26 | with: 27 | distribution: 'temurin' 28 | java-version: '21' 29 | - name: Cache 30 | uses: actions/cache@v3 31 | with: 32 | path: | 33 | ~/.gradle/caches 34 | ~/.gradle/wrapper 35 | key: ${{ runner.os }}-gradle-${{ hashFiles('**/gradle-wrapper.properties') }} 36 | restore-keys: | 37 | ${{ runner.os }}-gradle- 38 | - name: Change wrapper permissions 39 | run: chmod +x ./gradlew 40 | - name: Build artifacts 41 | run: ./gradlew build 42 | - name: Upload a Build Artifact 43 | uses: actions/upload-artifact@v4 44 | with: 45 | name: EternalTags 46 | path: ./build/libs/EternalTags-**.jar -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled class file 2 | *.class 3 | 4 | # Log file 5 | *.log 6 | 7 | # BlueJ files 8 | *.ctxt 9 | 10 | # Mobile Tools for Java (J2ME) 11 | .mtj.tmp/ 12 | # Gradle Files 13 | .gradle 14 | /build/ 15 | 16 | # Ignore Gradle GUI Config 17 | gradle.app.setting 18 | 19 | # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) 20 | !gradle-wrapper.jar 21 | 22 | # Package Files # 23 | *.war 24 | *.nar 25 | *.ear 26 | *.zip 27 | *.tar.gz 28 | *.rar 29 | 30 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 31 | hs_err_pid* 32 | .idea -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | import org.apache.tools.ant.filters.ReplaceTokens 2 | 3 | plugins { 4 | id "java-library" 5 | id "com.github.johnrengelman.shadow" version "7.1.0" 6 | id "maven-publish" 7 | } 8 | 9 | group "xyz.oribuin" 10 | version "1.3.3" 11 | 12 | java { 13 | toolchain { 14 | languageVersion = JavaLanguageVersion.of(17) 15 | } 16 | } 17 | 18 | compileJava { 19 | options.compilerArgs += ['-parameters'] 20 | options.fork = true 21 | options.encoding = 'UTF-8' 22 | } 23 | 24 | repositories { 25 | mavenCentral() 26 | mavenLocal() 27 | 28 | maven { url = "https://libraries.minecraft.net" } 29 | maven { url = "https://repo.papermc.io/repository/maven-public/" } 30 | maven { url = "https://hub.spigotmc.org/nexus/content/repositories/snapshots/" } 31 | maven { url = "https://repo.rosewooddev.io/repository/public/" } 32 | maven { url = "https://repo.extendedclip.com/content/repositories/placeholderapi/" } 33 | maven { url = "https://repo.codemc.org/repository/maven-public/" } 34 | maven { url = "https://jitpack.io" } 35 | maven { url = "https://repo.mattstudios.me/artifactory/public/" } 36 | 37 | } 38 | 39 | dependencies { 40 | // General Dependencies 41 | compileOnly 'io.papermc.paper:paper-api:1.20.1-R0.1-SNAPSHOT' 42 | compileOnly 'org.jetbrains:annotations:23.0.0' 43 | 44 | // Plugins 45 | compileOnly 'com.github.MilkBowl:VaultAPI:1.7' 46 | compileOnly 'me.clip:placeholderapi:2.11.6' 47 | compileOnly 'com.arcaniax:HeadDatabase-API:1.3.1', { 48 | exclude group: 'org.spigotmc' 49 | } 50 | 51 | // Mojang dependencies 52 | compileOnly 'com.mojang:authlib:1.5.21' 53 | // compileOnly 'net.kyori:adventure-text-minimessage:4.14.0' 54 | 55 | // Frameworks & APIs 56 | api('dev.rosewood:rosegarden:1.4.7-SNAPSHOT') 57 | api('dev.triumphteam:triumph-gui:3.1.7') { // https://mf.mattstudios.me/triumph-gui/introduction 58 | exclude group: "com.google.code.gson", module: "gson" // Remove GSON, Already included in spigot api 59 | exclude group: "net.kyori", module: "*" // Remove kyori, we're using plugin.yml libraries 60 | } 61 | } 62 | 63 | 64 | shadowJar { 65 | archiveClassifier.set(null) 66 | 67 | relocate("dev.rosewood.rosegarden", "${project.group}.eternaltags.libs.rosegarden") 68 | relocate("com.zaxxer", "${project.group}.eternaltags.libs.zaxxer") 69 | relocate("org.slf4j", "${project.group}.eternaltags.libs.slf4j") 70 | relocate("dev.triumphteam.gui", "${project.group}.eternaltags.libs.gui") 71 | } 72 | 73 | processResources { 74 | from(sourceSets.main.resources.srcDirs) { 75 | include '**/*.yml' 76 | filter ReplaceTokens, tokens: ["version": project.property("version")] 77 | duplicatesStrategy DuplicatesStrategy.INCLUDE 78 | } 79 | } 80 | 81 | publishing { 82 | publications { 83 | shadow(MavenPublication) { publication -> 84 | project.shadow.component(publication) 85 | artifactId = 'eternaltags' 86 | pom { 87 | name = 'eternaltags' 88 | } 89 | } 90 | } 91 | repositories { 92 | if (project.hasProperty('mavenUser') && project.hasProperty('mavenPassword')) { 93 | maven { 94 | credentials { 95 | username project.mavenUser 96 | password project.mavenPassword 97 | } 98 | 99 | def releasesRepoUrl = 'https://repo.rosewooddev.io/repository/public-releases/' 100 | def snapshotsRepoUrl = 'https://repo.rosewooddev.io/repository/public-snapshots/' 101 | url = version.endsWith('SNAPSHOT') ? snapshotsRepoUrl : releasesRepoUrl 102 | } 103 | } 104 | } 105 | } 106 | 107 | build.dependsOn shadowJar 108 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Gradle Settings 2 | org.gradle.jvmargs='-Dfile.encoding=UTF-8' 3 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Oribuin/EternalTags/c27c29cc35f0104ff2c433a2eda991a6498892f8/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Thu Apr 02 14:53:59 BST 2020 2 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip 3 | distributionBase=GRADLE_USER_HOME 4 | distributionPath=wrapper/dists 5 | zipStorePath=wrapper/dists 6 | zipStoreBase=GRADLE_USER_HOME 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS='"-Xmx64m"' 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS="-Xmx64m" 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'EternalTags' 2 | 3 | -------------------------------------------------------------------------------- /src/main/java/xyz/oribuin/eternaltags/EternalAPI.java: -------------------------------------------------------------------------------- 1 | package xyz.oribuin.eternaltags; 2 | 3 | import org.bukkit.OfflinePlayer; 4 | import org.bukkit.entity.Player; 5 | import org.jetbrains.annotations.NotNull; 6 | import org.jetbrains.annotations.Nullable; 7 | import xyz.oribuin.eternaltags.manager.CategoryManager; 8 | import xyz.oribuin.eternaltags.manager.DataManager; 9 | import xyz.oribuin.eternaltags.manager.TagsManager; 10 | import xyz.oribuin.eternaltags.obj.Category; 11 | import xyz.oribuin.eternaltags.obj.Tag; 12 | 13 | import java.util.UUID; 14 | 15 | /** 16 | * @author Oribuin 17 | * @since v1.0.11 18 | */ 19 | public class EternalAPI { 20 | 21 | private static EternalAPI instance; 22 | private final TagsManager tagManager; 23 | private final CategoryManager categoryManager; 24 | 25 | private EternalAPI() { 26 | this.tagManager = EternalTags.getInstance().getManager(TagsManager.class); 27 | this.categoryManager = EternalTags.getInstance().getManager(CategoryManager.class); 28 | } 29 | 30 | public static EternalAPI getInstance() { 31 | if (instance == null) 32 | instance = new EternalAPI(); 33 | 34 | return instance; 35 | } 36 | 37 | /** 38 | * Get an offline player's active tag 39 | * 40 | * @param uuid The player's UUID 41 | * @return The [Tag] belonging to the player, This tag is nullable 42 | * @since v1.1.4 43 | */ 44 | @Nullable 45 | public Tag getUserTag(UUID uuid) { 46 | return this.getTagManager().getUserTag(uuid); 47 | } 48 | 49 | /** 50 | * Get a player's active tag if they are online. 51 | * 52 | * @param player The player to get the tag of 53 | * @return The [Tag] belonging to the player, This tag is nullable 54 | * @since v1.1.4 55 | */ 56 | @Nullable 57 | public Tag getOnlineTag(Player player) { 58 | return this.tagManager.getUserTag(player); 59 | } 60 | 61 | /** 62 | * Set a player's active tag 63 | * 64 | * @param player The player 65 | * @param tag The tag, Set this to null to remove the tag. 66 | */ 67 | public void setTag(@NotNull OfflinePlayer player, @NotNull Tag tag) { 68 | if (player.isOnline()) { 69 | tag.equip((Player) player); 70 | return; 71 | } 72 | 73 | DataManager dataManager = EternalTags.getInstance().getManager(DataManager.class); 74 | dataManager.saveUser(player.getUniqueId(), tag.getId()); 75 | } 76 | 77 | /** 78 | * Get a category from the id 79 | * 80 | * @param id The id of the tag 81 | * @return The [Category] belonging to the id, This category is nullable 82 | */ 83 | @Nullable 84 | public Category getCategory(String id) { 85 | return this.categoryManager.getCategory(id); 86 | } 87 | 88 | /** 89 | * Get the category of a tag 90 | * 91 | * @param tag The tag 92 | * @return The [Category] belonging to the tag, This category is nullable 93 | */ 94 | @Nullable 95 | public Category getCategory(Tag tag) { 96 | return this.categoryManager.getCategory(tag.getCategory()); 97 | } 98 | 99 | public TagsManager getTagManager() { 100 | return tagManager; 101 | } 102 | 103 | } 104 | -------------------------------------------------------------------------------- /src/main/java/xyz/oribuin/eternaltags/EternalTags.java: -------------------------------------------------------------------------------- 1 | package xyz.oribuin.eternaltags; 2 | 3 | import dev.rosewood.rosegarden.RosePlugin; 4 | import dev.rosewood.rosegarden.config.RoseSetting; 5 | import dev.rosewood.rosegarden.manager.Manager; 6 | import org.bukkit.plugin.PluginManager; 7 | import org.jetbrains.annotations.NotNull; 8 | import xyz.oribuin.eternaltags.config.Setting; 9 | import xyz.oribuin.eternaltags.gui.MenuProvider; 10 | import xyz.oribuin.eternaltags.hook.Expansion; 11 | import xyz.oribuin.eternaltags.listener.BungeeListener; 12 | import xyz.oribuin.eternaltags.listener.ChatListener; 13 | import xyz.oribuin.eternaltags.listener.PlayerListeners; 14 | import xyz.oribuin.eternaltags.manager.CommandManager; 15 | import xyz.oribuin.eternaltags.manager.DataManager; 16 | import xyz.oribuin.eternaltags.manager.LocaleManager; 17 | import xyz.oribuin.eternaltags.manager.PluginConversionManager; 18 | import xyz.oribuin.eternaltags.manager.TagsManager; 19 | import xyz.oribuin.eternaltags.util.EventWaiter; 20 | 21 | import java.util.Arrays; 22 | import java.util.List; 23 | 24 | public class EternalTags extends RosePlugin { 25 | 26 | private static EternalTags instance; 27 | private static EventWaiter eventWaiter; 28 | 29 | public EternalTags() { 30 | super(91842, 11508,DataManager.class, LocaleManager.class, CommandManager.class); 31 | instance = this; 32 | } 33 | 34 | public static EternalTags getInstance() { 35 | return instance; 36 | } 37 | 38 | public static EventWaiter getEventWaiter() { 39 | return eventWaiter; 40 | } 41 | 42 | @Override 43 | public void enable() { 44 | PluginManager pluginManager = this.getServer().getPluginManager(); 45 | 46 | // Register Plugin Listeners 47 | pluginManager.registerEvents(new PlayerListeners(), this); 48 | 49 | // Enable Placeholder Formatting :-) 50 | if (Setting.CHAT_PLACEHOLDERS.get()) { 51 | pluginManager.registerEvents(new ChatListener(), this); 52 | } 53 | 54 | // Register Event Waiter 55 | eventWaiter = new EventWaiter(); 56 | 57 | // Register PlaceholderAPI Expansion 58 | new Expansion(this).register(); 59 | } 60 | 61 | @Override 62 | public void reload() { 63 | super.reload(); // Reload the managers 64 | 65 | MenuProvider.reload(); // Reload the menu provider 66 | 67 | // Register Plugin Messaging Channels 68 | if (Setting.PLUGIN_MESSAGING_RELOAD.get()) { 69 | this.getServer().getMessenger().unregisterIncomingPluginChannel(this); 70 | this.getServer().getMessenger().unregisterOutgoingPluginChannel(this); 71 | 72 | this.getServer().getMessenger().registerOutgoingPluginChannel(this, "BungeeCord"); 73 | this.getServer().getMessenger().registerIncomingPluginChannel(this, "BungeeCord", new BungeeListener(this)); 74 | } 75 | } 76 | 77 | @Override 78 | public void disable() { 79 | 80 | } 81 | 82 | @Override 83 | protected List> getManagerLoadPriority() { 84 | return Arrays.asList( 85 | TagsManager.class, 86 | PluginConversionManager.class 87 | ); 88 | } 89 | 90 | @Override 91 | protected @NotNull List> getRoseConfigSettings() { 92 | return Setting.getKeys(); 93 | } 94 | 95 | @Override 96 | protected @NotNull String[] getRoseConfigHeader() { 97 | return Setting.getHeader(); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/main/java/xyz/oribuin/eternaltags/action/Action.java: -------------------------------------------------------------------------------- 1 | package xyz.oribuin.eternaltags.action; 2 | 3 | import dev.rosewood.rosegarden.utils.StringPlaceholders; 4 | import org.bukkit.entity.Player; 5 | import org.jetbrains.annotations.NotNull; 6 | 7 | public abstract class Action { 8 | 9 | private final @NotNull String name; 10 | private String message = ""; 11 | 12 | protected Action(@NotNull String name) { 13 | this.name = name; 14 | } 15 | 16 | /** 17 | * Execute the action function 18 | * 19 | * @param player The player 20 | * @param placeholders Message placeholders 21 | */ 22 | public abstract void execute(@NotNull Player player, @NotNull StringPlaceholders placeholders); 23 | 24 | public String getName() { 25 | return name; 26 | } 27 | 28 | public String getMessage() { 29 | return message; 30 | } 31 | 32 | public void setMessage(String message) { 33 | this.message = message; 34 | } 35 | 36 | } -------------------------------------------------------------------------------- /src/main/java/xyz/oribuin/eternaltags/action/BroadcastAction.java: -------------------------------------------------------------------------------- 1 | package xyz.oribuin.eternaltags.action; 2 | 3 | import dev.rosewood.rosegarden.utils.HexUtils; 4 | import dev.rosewood.rosegarden.utils.StringPlaceholders; 5 | import me.clip.placeholderapi.PlaceholderAPI; 6 | import org.bukkit.Bukkit; 7 | import org.bukkit.entity.Player; 8 | import org.jetbrains.annotations.NotNull; 9 | 10 | public class BroadcastAction extends Action { 11 | 12 | public BroadcastAction() { 13 | super("broadcast"); 14 | } 15 | 16 | @Override 17 | @SuppressWarnings("deprecation") 18 | public void execute(@NotNull Player player, @NotNull StringPlaceholders placeholders) { 19 | if (this.getMessage().length() == 0) 20 | return; 21 | 22 | Bukkit.broadcast(HexUtils.colorify(PlaceholderAPI.setPlaceholders(player, placeholders.apply(this.getMessage()))), ""); 23 | } 24 | 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/xyz/oribuin/eternaltags/action/CloseAction.java: -------------------------------------------------------------------------------- 1 | package xyz.oribuin.eternaltags.action; 2 | 3 | import dev.rosewood.rosegarden.utils.StringPlaceholders; 4 | import org.bukkit.entity.Player; 5 | import org.jetbrains.annotations.NotNull; 6 | 7 | public class CloseAction extends Action { 8 | 9 | public CloseAction() { 10 | super("close"); 11 | } 12 | 13 | @Override 14 | @SuppressWarnings("deprecation") 15 | public void execute(@NotNull Player player, @NotNull StringPlaceholders placeholders) { 16 | player.closeInventory(); 17 | } 18 | 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/xyz/oribuin/eternaltags/action/ConsoleAction.java: -------------------------------------------------------------------------------- 1 | package xyz.oribuin.eternaltags.action; 2 | 3 | import dev.rosewood.rosegarden.utils.StringPlaceholders; 4 | import me.clip.placeholderapi.PlaceholderAPI; 5 | import org.bukkit.Bukkit; 6 | import org.bukkit.entity.Player; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | public class ConsoleAction extends Action { 10 | 11 | public ConsoleAction() { 12 | super("console"); 13 | } 14 | 15 | @Override 16 | public void execute(@NotNull Player player, @NotNull StringPlaceholders placeholders) { 17 | if (this.getMessage().length() == 0) 18 | return; 19 | 20 | Bukkit.dispatchCommand(Bukkit.getConsoleSender(), PlaceholderAPI.setPlaceholders(player, placeholders.apply(this.getMessage()))); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/xyz/oribuin/eternaltags/action/MessageAction.java: -------------------------------------------------------------------------------- 1 | package xyz.oribuin.eternaltags.action; 2 | 3 | import dev.rosewood.rosegarden.utils.HexUtils; 4 | import dev.rosewood.rosegarden.utils.StringPlaceholders; 5 | import me.clip.placeholderapi.PlaceholderAPI; 6 | import org.bukkit.entity.Player; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | public class MessageAction extends Action { 10 | 11 | public MessageAction() { 12 | super("message"); 13 | } 14 | 15 | @Override 16 | public void execute(@NotNull Player player, @NotNull StringPlaceholders placeholders) { 17 | if (this.getMessage().length() == 0) 18 | return; 19 | 20 | player.sendMessage(HexUtils.colorify(PlaceholderAPI.setPlaceholders(player, placeholders.apply(this.getMessage())))); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/xyz/oribuin/eternaltags/action/PlayerAction.java: -------------------------------------------------------------------------------- 1 | package xyz.oribuin.eternaltags.action; 2 | 3 | import dev.rosewood.rosegarden.utils.StringPlaceholders; 4 | import me.clip.placeholderapi.PlaceholderAPI; 5 | import org.bukkit.Bukkit; 6 | import org.bukkit.entity.Player; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | public class PlayerAction extends Action { 10 | 11 | public PlayerAction() { 12 | super("player"); 13 | } 14 | 15 | @Override 16 | public void execute(@NotNull Player player, @NotNull StringPlaceholders placeholders) { 17 | if (this.getMessage().length() == 0) 18 | return; 19 | 20 | Bukkit.dispatchCommand(player, PlaceholderAPI.setPlaceholders(player, placeholders.apply(this.getMessage()))); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/xyz/oribuin/eternaltags/action/PluginAction.java: -------------------------------------------------------------------------------- 1 | package xyz.oribuin.eternaltags.action; 2 | 3 | import org.jetbrains.annotations.NotNull; 4 | import org.jetbrains.annotations.Nullable; 5 | 6 | import java.util.HashMap; 7 | import java.util.Locale; 8 | import java.util.Map; 9 | import java.util.function.Function; 10 | import java.util.function.Supplier; 11 | import java.util.regex.Matcher; 12 | import java.util.regex.Pattern; 13 | 14 | /** 15 | * @author HSGamer 16 | */ 17 | public final class PluginAction { 18 | private static final Pattern ACTION_PATTERN = Pattern.compile("\\[(\\w+)]\\W?(.*)"); 19 | private static final Map> ACTIONS = new HashMap<>(); 20 | 21 | static { 22 | registerAction("broadcast", BroadcastAction::new); 23 | registerAction("close", CloseAction::new); 24 | registerAction("console", ConsoleAction::new); 25 | registerAction("message", MessageAction::new); 26 | registerAction("player", PlayerAction::new); 27 | registerAction("sound", SoundAction::new); 28 | } 29 | 30 | /** 31 | * Register an action 32 | * 33 | * @param name Name of the action 34 | * @param actionFunction Function to create the action, with the message as a parameter 35 | */ 36 | public static void registerAction(String name, Function actionFunction) { 37 | // toLowerCase to avoid case-sensitive issues 38 | ACTIONS.put(name.toLowerCase(Locale.ROOT), actionFunction); 39 | } 40 | 41 | /** 42 | * Register an action 43 | * 44 | * @param name Name of the action 45 | * @param actionSupplier Supplier to create the action 46 | */ 47 | public static void registerAction(String name, Supplier actionSupplier) { 48 | registerAction(name, s -> { 49 | Action action = actionSupplier.get(); 50 | action.setMessage(s); 51 | return action; 52 | }); 53 | } 54 | 55 | /** 56 | * Parse the action from text 57 | * 58 | * @param text Text to parse 59 | * @return Action associated with the text 60 | */ 61 | public static @Nullable Action parse(String text) { 62 | // Check if the text matches the pattern ("[] ") and get the action and message 63 | Matcher matcher = ACTION_PATTERN.matcher(text); 64 | if (!matcher.find()) { 65 | return null; 66 | } 67 | 68 | String actionName = matcher.group(1).toLowerCase(Locale.ROOT); // toLowerCase to avoid case-sensitive issues 69 | String actionText = matcher.group(2); 70 | 71 | Function action = ACTIONS.get(actionName); 72 | 73 | if (action == null) 74 | return null; 75 | 76 | return action.apply(actionText); 77 | } 78 | 79 | @Nullable 80 | public static String getName(Action action) { 81 | for (Map.Entry> entry : ACTIONS.entrySet()) { 82 | if (entry.getKey().equalsIgnoreCase(action.getName())) { 83 | return entry.getKey(); 84 | } 85 | } 86 | 87 | return null; 88 | } 89 | 90 | public static @NotNull String serialize(Action action) { 91 | return "[" + action.getName() + "] " + action.getMessage(); 92 | } 93 | 94 | } -------------------------------------------------------------------------------- /src/main/java/xyz/oribuin/eternaltags/action/SoundAction.java: -------------------------------------------------------------------------------- 1 | package xyz.oribuin.eternaltags.action; 2 | 3 | import dev.rosewood.rosegarden.utils.StringPlaceholders; 4 | import org.bukkit.Sound; 5 | import org.bukkit.entity.Player; 6 | import org.jetbrains.annotations.NotNull; 7 | import xyz.oribuin.eternaltags.util.TagsUtils; 8 | 9 | public class SoundAction extends Action { 10 | 11 | public SoundAction() { 12 | super("sound"); 13 | } 14 | 15 | @Override 16 | public void execute(@NotNull Player player, @NotNull StringPlaceholders placeholders) { 17 | if (this.getMessage().length() == 0) 18 | return; 19 | 20 | String[] args = this.getMessage().split(" "); 21 | Sound sound = null; 22 | if (args.length >= 1) { 23 | sound = TagsUtils.getEnum(Sound.class, args[0]); 24 | } 25 | 26 | if (sound == null) 27 | return; 28 | 29 | float volume = 100f; 30 | if (args.length >= 2) { 31 | volume = Float.parseFloat(args[1]); 32 | } 33 | 34 | if (volume > 100f) 35 | volume = 100f; 36 | 37 | float pitch = 1f; 38 | if (args.length >= 3) { 39 | pitch = Float.parseFloat(args[2]); 40 | } 41 | 42 | player.playSound(player.getLocation(), sound, volume, pitch); 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/xyz/oribuin/eternaltags/command/argument/CategoryArgumentHandler.java: -------------------------------------------------------------------------------- 1 | package xyz.oribuin.eternaltags.command.argument; 2 | 3 | import dev.rosewood.rosegarden.command.framework.Argument; 4 | import dev.rosewood.rosegarden.command.framework.ArgumentHandler; 5 | import dev.rosewood.rosegarden.command.framework.CommandContext; 6 | import dev.rosewood.rosegarden.command.framework.InputIterator; 7 | import dev.rosewood.rosegarden.utils.StringPlaceholders; 8 | import xyz.oribuin.eternaltags.EternalTags; 9 | import xyz.oribuin.eternaltags.manager.CategoryManager; 10 | import xyz.oribuin.eternaltags.obj.Category; 11 | import xyz.oribuin.eternaltags.obj.CategoryType; 12 | 13 | import java.util.List; 14 | 15 | public class CategoryArgumentHandler extends ArgumentHandler { 16 | 17 | public CategoryArgumentHandler() { 18 | super(Category.class); 19 | } 20 | 21 | @Override 22 | public Category handle(CommandContext context, Argument argument, InputIterator inputIterator) throws HandledArgumentException { 23 | String input = inputIterator.next(); 24 | Category value = EternalTags.getInstance().getManager(CategoryManager.class).getCategory(input.toLowerCase()); 25 | if (value == null || value.getType() == CategoryType.GLOBAL) 26 | throw new HandledArgumentException("argument-handler-category", StringPlaceholders.of("input", input)); 27 | 28 | return value; 29 | } 30 | 31 | @Override 32 | public List suggest(CommandContext context, Argument argument, String[] args) { 33 | return EternalTags.getInstance().getManager(CategoryManager.class).getCategories() 34 | .stream() 35 | .filter(category -> category.getType() != CategoryType.GLOBAL) 36 | .map(Category::getId) 37 | .toList(); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/xyz/oribuin/eternaltags/command/argument/MaterialArgumentHandler.java: -------------------------------------------------------------------------------- 1 | package xyz.oribuin.eternaltags.command.argument; 2 | 3 | import dev.rosewood.rosegarden.command.framework.Argument; 4 | import dev.rosewood.rosegarden.command.framework.ArgumentHandler; 5 | import dev.rosewood.rosegarden.command.framework.CommandContext; 6 | import dev.rosewood.rosegarden.command.framework.InputIterator; 7 | import dev.rosewood.rosegarden.utils.StringPlaceholders; 8 | import org.bukkit.Material; 9 | 10 | import java.util.Arrays; 11 | import java.util.List; 12 | import java.util.stream.Collectors; 13 | 14 | public class MaterialArgumentHandler extends ArgumentHandler { 15 | 16 | public MaterialArgumentHandler() { 17 | super(Material.class); 18 | } 19 | 20 | @Override 21 | public Material handle(CommandContext context, Argument argument, InputIterator inputIterator) throws HandledArgumentException { 22 | String input = inputIterator.next(); 23 | Material value = Material.matchMaterial(input); 24 | if (value == null) 25 | throw new HandledArgumentException("argument-handler-material", StringPlaceholders.of("input", input)); 26 | 27 | return value; 28 | } 29 | 30 | @Override 31 | public List suggest(CommandContext context, Argument argument, String[] args) { 32 | return Arrays.stream(Material.values()) 33 | .filter(Material::isItem) 34 | .map(material -> material.name().toLowerCase()) 35 | .collect(Collectors.toList()); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/xyz/oribuin/eternaltags/command/argument/PluginArgumentHandler.java: -------------------------------------------------------------------------------- 1 | package xyz.oribuin.eternaltags.command.argument; 2 | 3 | import dev.rosewood.rosegarden.command.framework.Argument; 4 | import dev.rosewood.rosegarden.command.framework.ArgumentHandler; 5 | import dev.rosewood.rosegarden.command.framework.CommandContext; 6 | import dev.rosewood.rosegarden.command.framework.InputIterator; 7 | import dev.rosewood.rosegarden.utils.StringPlaceholders; 8 | import xyz.oribuin.eternaltags.conversion.ConversionPlugin; 9 | import xyz.oribuin.eternaltags.conversion.ValidPlugin; 10 | 11 | import java.util.List; 12 | import java.util.Optional; 13 | 14 | public class PluginArgumentHandler extends ArgumentHandler { 15 | 16 | public PluginArgumentHandler() { 17 | super(ConversionPlugin.class); 18 | } 19 | 20 | @Override 21 | public ConversionPlugin handle(CommandContext context, Argument argument, InputIterator inputIterator) throws HandledArgumentException { 22 | String input = inputIterator.next(); 23 | 24 | Optional conversion = ValidPlugin.match(input); 25 | if (conversion.isEmpty()) 26 | throw new HandledArgumentException("argument-handler-plugins", StringPlaceholders.of("input", input)); 27 | 28 | return conversion.get(); 29 | } 30 | 31 | @Override 32 | public List suggest(CommandContext context, Argument argument, String[] args) { 33 | return ValidPlugin.PLUGINS 34 | .keySet() 35 | .stream() 36 | .toList(); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/xyz/oribuin/eternaltags/command/argument/TagsArgumentHandler.java: -------------------------------------------------------------------------------- 1 | package xyz.oribuin.eternaltags.command.argument; 2 | 3 | import dev.rosewood.rosegarden.command.framework.Argument; 4 | import dev.rosewood.rosegarden.command.framework.ArgumentHandler; 5 | import dev.rosewood.rosegarden.command.framework.CommandContext; 6 | import dev.rosewood.rosegarden.command.framework.InputIterator; 7 | import dev.rosewood.rosegarden.utils.StringPlaceholders; 8 | import org.bukkit.entity.Player; 9 | import xyz.oribuin.eternaltags.EternalTags; 10 | import xyz.oribuin.eternaltags.manager.TagsManager; 11 | import xyz.oribuin.eternaltags.obj.Tag; 12 | 13 | import java.util.ArrayList; 14 | import java.util.Collections; 15 | import java.util.List; 16 | 17 | public class TagsArgumentHandler extends ArgumentHandler { 18 | 19 | public TagsArgumentHandler() { 20 | super(Tag.class); 21 | } 22 | 23 | @Override 24 | public Tag handle(CommandContext context, Argument argument, InputIterator inputIterator) throws HandledArgumentException { 25 | String input = inputIterator.next(); 26 | Tag value = EternalTags.getInstance().getManager(TagsManager.class).getTagFromId(input.toLowerCase()); 27 | if (value == null) 28 | throw new HandledArgumentException("argument-handler-tags", StringPlaceholders.of("input", input)); 29 | 30 | return value; 31 | } 32 | 33 | @Override 34 | public List suggest(CommandContext context, Argument argument, String[] args) { 35 | TagsManager manager = EternalTags.getInstance().getManager(TagsManager.class); 36 | 37 | if (!(context.getSender() instanceof Player player)) { 38 | List tags = new ArrayList<>(manager.getCachedTags().keySet()); 39 | 40 | if (tags.isEmpty()) 41 | return Collections.singletonList(""); 42 | 43 | return tags; 44 | } 45 | 46 | List tags = new ArrayList<>(manager.getPlayerTags(player)).stream().map(Tag::getId).toList(); 47 | if (tags.isEmpty()) 48 | return Collections.singletonList(""); 49 | 50 | return tags; 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/xyz/oribuin/eternaltags/command/impl/CategoriesCommand.java: -------------------------------------------------------------------------------- 1 | package xyz.oribuin.eternaltags.command.impl; 2 | 3 | import dev.rosewood.rosegarden.RosePlugin; 4 | import dev.rosewood.rosegarden.command.framework.ArgumentsDefinition; 5 | import dev.rosewood.rosegarden.command.framework.BaseRoseCommand; 6 | import dev.rosewood.rosegarden.command.framework.CommandContext; 7 | import dev.rosewood.rosegarden.command.framework.CommandInfo; 8 | import dev.rosewood.rosegarden.command.framework.annotation.RoseExecutable; 9 | import org.bukkit.entity.Player; 10 | import xyz.oribuin.eternaltags.gui.MenuProvider; 11 | import xyz.oribuin.eternaltags.gui.menu.CategoryGUI; 12 | 13 | public class CategoriesCommand extends BaseRoseCommand { 14 | 15 | public CategoriesCommand(RosePlugin rosePlugin) { 16 | super(rosePlugin); 17 | } 18 | 19 | @RoseExecutable 20 | public void execute(CommandContext context) { 21 | MenuProvider.get(CategoryGUI.class).open((Player) context.getSender()); 22 | } 23 | 24 | @Override 25 | protected CommandInfo createCommandInfo() { 26 | return CommandInfo.builder("categories") 27 | .descriptionKey("command-categories-description") 28 | .permission("eternaltags.categories") 29 | .playerOnly(true) 30 | .build(); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/xyz/oribuin/eternaltags/command/impl/ClearCommand.java: -------------------------------------------------------------------------------- 1 | package xyz.oribuin.eternaltags.command.impl; 2 | 3 | import dev.rosewood.rosegarden.RosePlugin; 4 | import dev.rosewood.rosegarden.command.argument.ArgumentHandlers; 5 | import dev.rosewood.rosegarden.command.framework.ArgumentsDefinition; 6 | import dev.rosewood.rosegarden.command.framework.BaseRoseCommand; 7 | import dev.rosewood.rosegarden.command.framework.CommandContext; 8 | import dev.rosewood.rosegarden.command.framework.CommandInfo; 9 | import dev.rosewood.rosegarden.command.framework.annotation.RoseExecutable; 10 | import dev.rosewood.rosegarden.utils.StringPlaceholders; 11 | import org.bukkit.command.CommandSender; 12 | import org.bukkit.entity.Player; 13 | import xyz.oribuin.eternaltags.manager.LocaleManager; 14 | import xyz.oribuin.eternaltags.manager.TagsManager; 15 | 16 | public class ClearCommand extends BaseRoseCommand { 17 | 18 | public ClearCommand(RosePlugin rosePlugin) { 19 | super(rosePlugin); 20 | } 21 | 22 | @RoseExecutable 23 | public void execute(CommandContext context, Player target, String silent) { 24 | LocaleManager locale = this.rosePlugin.getManager(LocaleManager.class); 25 | TagsManager manager = this.rosePlugin.getManager(TagsManager.class); 26 | CommandSender sender = context.getSender(); 27 | 28 | // Check if the player arg was provided. 29 | if (target != null) { 30 | if (!sender.hasPermission("eternaltags.clear.other")) { 31 | locale.sendMessage(sender, "no-permission"); 32 | return; 33 | } 34 | 35 | manager.clearTag(target.getUniqueId()); 36 | 37 | if (silent != null) 38 | locale.sendMessage(target, "command-clear-cleared"); 39 | 40 | locale.sendMessage(sender, "command-clear-cleared-other", StringPlaceholders.of("player", target.getName())); 41 | return; 42 | } 43 | 44 | if (!(sender instanceof Player playerSender)) { 45 | locale.sendMessage(sender, "only-player"); 46 | return; 47 | } 48 | 49 | manager.clearTag(playerSender.getUniqueId()); 50 | locale.sendMessage(sender, "command-clear-cleared"); 51 | } 52 | 53 | @Override 54 | protected CommandInfo createCommandInfo() { 55 | return CommandInfo.builder("clear") 56 | .descriptionKey("command-clear-description") 57 | .permission("eternaltags.clear") 58 | .arguments(this.createArguments()) 59 | .build(); 60 | } 61 | 62 | private ArgumentsDefinition createArguments() { 63 | return ArgumentsDefinition.builder() 64 | .optional("target", ArgumentHandlers.PLAYER) 65 | .optional("silent", ArgumentHandlers.BOOLEAN) 66 | .build(); 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/xyz/oribuin/eternaltags/command/impl/ConvertCommand.java: -------------------------------------------------------------------------------- 1 | package xyz.oribuin.eternaltags.command.impl; 2 | 3 | import dev.rosewood.rosegarden.RosePlugin; 4 | import dev.rosewood.rosegarden.command.framework.ArgumentsDefinition; 5 | import dev.rosewood.rosegarden.command.framework.BaseRoseCommand; 6 | import dev.rosewood.rosegarden.command.framework.CommandContext; 7 | import dev.rosewood.rosegarden.command.framework.CommandInfo; 8 | import dev.rosewood.rosegarden.command.framework.annotation.RoseExecutable; 9 | import dev.rosewood.rosegarden.utils.StringPlaceholders; 10 | import org.bukkit.command.CommandSender; 11 | import xyz.oribuin.eternaltags.command.argument.PluginArgumentHandler; 12 | import xyz.oribuin.eternaltags.conversion.ConversionPlugin; 13 | import xyz.oribuin.eternaltags.conversion.ValidPlugin; 14 | import xyz.oribuin.eternaltags.manager.LocaleManager; 15 | import xyz.oribuin.eternaltags.manager.PluginConversionManager; 16 | import xyz.oribuin.eternaltags.util.TagsUtils; 17 | 18 | import java.util.stream.Collectors; 19 | 20 | public class ConvertCommand extends BaseRoseCommand { 21 | 22 | public ConvertCommand(RosePlugin rosePlugin) { 23 | super(rosePlugin); 24 | } 25 | 26 | @RoseExecutable 27 | public void execute(CommandContext context, ConversionPlugin plugin) { 28 | LocaleManager locale = this.rosePlugin.getManager(LocaleManager.class); 29 | PluginConversionManager manager = this.rosePlugin.getManager(PluginConversionManager.class); 30 | CommandSender sender = context.getSender(); 31 | 32 | // Check if the player arg was provided. 33 | if (plugin == null) { 34 | locale.sendMessage(sender, "command-convert-invalid-plugin", StringPlaceholders.of("options", TagsUtils.formatList(ValidPlugin.PLUGINS.values() 35 | .stream() 36 | .map(ConversionPlugin::getPluginName) 37 | .collect(Collectors.toList()), ", "))); 38 | return; 39 | } 40 | 41 | int total = manager.convertPlugin(plugin).size(); 42 | locale.sendMessage(sender, "command-convert-converted", StringPlaceholders.of("total", total)); 43 | } 44 | 45 | @Override 46 | protected CommandInfo createCommandInfo() { 47 | return CommandInfo.builder("convert") 48 | .descriptionKey("command-convert-description") 49 | .permission("eternaltags.convert") 50 | .arguments(this.createArguments()) 51 | .build(); 52 | } 53 | 54 | private ArgumentsDefinition createArguments() { 55 | return ArgumentsDefinition.builder() 56 | .required("plugin", new PluginArgumentHandler()) 57 | .build(); 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/xyz/oribuin/eternaltags/command/impl/CreateCommand.java: -------------------------------------------------------------------------------- 1 | package xyz.oribuin.eternaltags.command.impl; 2 | 3 | import dev.rosewood.rosegarden.RosePlugin; 4 | import dev.rosewood.rosegarden.command.argument.ArgumentHandlers; 5 | import dev.rosewood.rosegarden.command.framework.ArgumentsDefinition; 6 | import dev.rosewood.rosegarden.command.framework.BaseRoseCommand; 7 | import dev.rosewood.rosegarden.command.framework.CommandContext; 8 | import dev.rosewood.rosegarden.command.framework.CommandInfo; 9 | import dev.rosewood.rosegarden.command.framework.annotation.RoseExecutable; 10 | import dev.rosewood.rosegarden.utils.StringPlaceholders; 11 | import org.bukkit.command.CommandSender; 12 | import xyz.oribuin.eternaltags.manager.LocaleManager; 13 | import xyz.oribuin.eternaltags.manager.TagsManager; 14 | import xyz.oribuin.eternaltags.obj.Tag; 15 | 16 | import java.util.Collections; 17 | 18 | public class CreateCommand extends BaseRoseCommand { 19 | 20 | public CreateCommand(RosePlugin rosePlugin) { 21 | super(rosePlugin); 22 | } 23 | 24 | @RoseExecutable 25 | public void execute(CommandContext context, String name, String tag) { 26 | LocaleManager locale = this.rosePlugin.getManager(LocaleManager.class); 27 | TagsManager manager = this.rosePlugin.getManager(TagsManager.class); 28 | CommandSender sender = context.getSender(); 29 | 30 | if (manager.checkTagExists(name)) { 31 | locale.sendMessage(sender, "command-create-tag-exists"); 32 | return; 33 | } 34 | 35 | String id = name.toLowerCase().replace(".", "_"); 36 | 37 | Tag newTag = new Tag(id, name, tag); 38 | newTag.setDescription(Collections.singletonList("None")); 39 | manager.saveTag(newTag); 40 | 41 | locale.sendMessage(sender, "command-create-created", StringPlaceholders.of("tag", manager.getDisplayTag(newTag, null))); 42 | } 43 | 44 | @Override 45 | protected CommandInfo createCommandInfo() { 46 | return CommandInfo.builder("create") 47 | .descriptionKey("command-create-description") 48 | .permission("eternaltags.create") 49 | .arguments(this.createArguments()) 50 | .build(); 51 | } 52 | 53 | private ArgumentsDefinition createArguments() { 54 | return ArgumentsDefinition.builder() 55 | .required("name", ArgumentHandlers.STRING) 56 | .required("tag", ArgumentHandlers.GREEDY_STRING) 57 | .build(); 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/xyz/oribuin/eternaltags/command/impl/DeleteCommand.java: -------------------------------------------------------------------------------- 1 | package xyz.oribuin.eternaltags.command.impl; 2 | 3 | import dev.rosewood.rosegarden.RosePlugin; 4 | import dev.rosewood.rosegarden.command.framework.ArgumentsDefinition; 5 | import dev.rosewood.rosegarden.command.framework.BaseRoseCommand; 6 | import dev.rosewood.rosegarden.command.framework.CommandContext; 7 | import dev.rosewood.rosegarden.command.framework.CommandInfo; 8 | import dev.rosewood.rosegarden.command.framework.annotation.RoseExecutable; 9 | import dev.rosewood.rosegarden.utils.StringPlaceholders; 10 | import org.bukkit.command.CommandSender; 11 | import xyz.oribuin.eternaltags.command.argument.TagsArgumentHandler; 12 | import xyz.oribuin.eternaltags.manager.LocaleManager; 13 | import xyz.oribuin.eternaltags.manager.TagsManager; 14 | import xyz.oribuin.eternaltags.obj.Tag; 15 | 16 | public class DeleteCommand extends BaseRoseCommand { 17 | 18 | public DeleteCommand(RosePlugin rosePlugin) { 19 | super(rosePlugin); 20 | } 21 | 22 | @RoseExecutable 23 | public void execute(CommandContext context, Tag tag) { 24 | LocaleManager locale = this.rosePlugin.getManager(LocaleManager.class); 25 | TagsManager manager = this.rosePlugin.getManager(TagsManager.class); 26 | CommandSender sender = context.getSender(); 27 | 28 | if (tag == null) { 29 | locale.sendMessage(sender, "tag-doesnt-exist"); 30 | return; 31 | } 32 | 33 | manager.deleteTag(tag); 34 | locale.sendMessage(sender, "command-delete-deleted", StringPlaceholders.of("tag", manager.getDisplayTag(tag, null))); 35 | } 36 | 37 | @Override 38 | protected CommandInfo createCommandInfo() { 39 | return CommandInfo.builder("delete") 40 | .descriptionKey("command-delete-description") 41 | .permission("eternaltags.delete") 42 | .arguments(this.createArguments()) 43 | .build(); 44 | } 45 | 46 | private ArgumentsDefinition createArguments() { 47 | return ArgumentsDefinition.builder() 48 | .required("tag", new TagsArgumentHandler()) 49 | .build(); 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/xyz/oribuin/eternaltags/command/impl/EditCommand.java: -------------------------------------------------------------------------------- 1 | package xyz.oribuin.eternaltags.command.impl; 2 | 3 | import dev.rosewood.rosegarden.RosePlugin; 4 | import dev.rosewood.rosegarden.command.framework.ArgumentsDefinition; 5 | import dev.rosewood.rosegarden.command.framework.BaseRoseCommand; 6 | import dev.rosewood.rosegarden.command.framework.CommandContext; 7 | import dev.rosewood.rosegarden.command.framework.CommandInfo; 8 | import dev.rosewood.rosegarden.command.framework.annotation.RoseExecutable; 9 | import xyz.oribuin.eternaltags.command.impl.edit.EditCategoryCommand; 10 | import xyz.oribuin.eternaltags.command.impl.edit.EditDescriptionCommand; 11 | import xyz.oribuin.eternaltags.command.impl.edit.EditIconCommand; 12 | import xyz.oribuin.eternaltags.command.impl.edit.EditNameCommand; 13 | import xyz.oribuin.eternaltags.command.impl.edit.EditOrderCommand; 14 | import xyz.oribuin.eternaltags.command.impl.edit.EditPermissionCommand; 15 | import xyz.oribuin.eternaltags.command.impl.edit.EditTagCommand; 16 | 17 | public class EditCommand extends BaseRoseCommand { 18 | 19 | public EditCommand(RosePlugin rosePlugin) { 20 | super(rosePlugin); 21 | } 22 | 23 | @RoseExecutable 24 | public void execute(CommandContext context) { 25 | // Has no functionality, just used to pass the tag to the subcommand 26 | } 27 | 28 | @Override 29 | protected CommandInfo createCommandInfo() { 30 | return CommandInfo.builder("edit") 31 | .descriptionKey("command-edit-description") 32 | .permission("eternaltags.edit") 33 | .arguments(this.createArguments()) 34 | .build(); 35 | } 36 | 37 | private ArgumentsDefinition createArguments() { 38 | return ArgumentsDefinition.builder().requiredSub( 39 | new EditCategoryCommand(this.rosePlugin), 40 | new EditDescriptionCommand(this.rosePlugin), 41 | new EditIconCommand(this.rosePlugin), 42 | new EditNameCommand(this.rosePlugin), 43 | new EditOrderCommand(this.rosePlugin), 44 | new EditPermissionCommand(this.rosePlugin), 45 | new EditTagCommand(this.rosePlugin) 46 | ); 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/xyz/oribuin/eternaltags/command/impl/FavoriteCommand.java: -------------------------------------------------------------------------------- 1 | package xyz.oribuin.eternaltags.command.impl; 2 | 3 | import dev.rosewood.rosegarden.RosePlugin; 4 | import dev.rosewood.rosegarden.command.framework.ArgumentsDefinition; 5 | import dev.rosewood.rosegarden.command.framework.BaseRoseCommand; 6 | import dev.rosewood.rosegarden.command.framework.CommandContext; 7 | import dev.rosewood.rosegarden.command.framework.CommandInfo; 8 | import dev.rosewood.rosegarden.command.framework.annotation.RoseExecutable; 9 | import dev.rosewood.rosegarden.utils.StringPlaceholders; 10 | import org.bukkit.entity.Player; 11 | import xyz.oribuin.eternaltags.command.argument.TagsArgumentHandler; 12 | import xyz.oribuin.eternaltags.gui.MenuProvider; 13 | import xyz.oribuin.eternaltags.gui.menu.FavouritesGUI; 14 | import xyz.oribuin.eternaltags.manager.LocaleManager; 15 | import xyz.oribuin.eternaltags.manager.TagsManager; 16 | import xyz.oribuin.eternaltags.obj.Tag; 17 | 18 | public class FavoriteCommand extends BaseRoseCommand { 19 | 20 | public FavoriteCommand(RosePlugin rosePlugin) { 21 | super(rosePlugin); 22 | } 23 | 24 | @RoseExecutable 25 | public void execute(CommandContext context, Tag tag) { 26 | LocaleManager locale = this.rosePlugin.getManager(LocaleManager.class); 27 | TagsManager manager = this.rosePlugin.getManager(TagsManager.class); 28 | Player sender = (Player) context.getSender(); 29 | 30 | if (tag == null) { 31 | MenuProvider.get(FavouritesGUI.class).open(sender); 32 | return; 33 | } 34 | 35 | if (!manager.canUseTag(sender, tag)) { 36 | locale.sendMessage(sender, "command-favorite-no-permission"); 37 | return; 38 | } 39 | 40 | boolean isFavourite = manager.isFavourite(sender.getUniqueId(), tag); 41 | 42 | if (isFavourite) 43 | manager.removeFavourite(sender.getUniqueId(), tag); 44 | else 45 | manager.addFavourite(sender.getUniqueId(), tag); 46 | 47 | String on = locale.getLocaleMessage("command-favorite-on"); 48 | String off = locale.getLocaleMessage("command-favorite-off"); 49 | 50 | locale.sendMessage(sender, "command-favorite-toggled", StringPlaceholders.builder("tag", manager.getDisplayTag(tag, sender)) 51 | .add("toggled", !isFavourite ? on : off) 52 | .build()); 53 | } 54 | 55 | @Override 56 | protected CommandInfo createCommandInfo() { 57 | return CommandInfo.builder("favorite") 58 | .descriptionKey("command-favorite-description") 59 | .permission("eternaltags.favorite") 60 | .aliases("favourite") 61 | .playerOnly(true) 62 | .arguments(this.createArguments()) 63 | .build(); 64 | } 65 | 66 | private ArgumentsDefinition createArguments() { 67 | return ArgumentsDefinition.builder() 68 | .optional("tag", new TagsArgumentHandler()) 69 | .build(); 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/xyz/oribuin/eternaltags/command/impl/RandomCommand.java: -------------------------------------------------------------------------------- 1 | package xyz.oribuin.eternaltags.command.impl; 2 | 3 | import dev.rosewood.rosegarden.RosePlugin; 4 | import dev.rosewood.rosegarden.command.framework.ArgumentsDefinition; 5 | import dev.rosewood.rosegarden.command.framework.CommandContext; 6 | import dev.rosewood.rosegarden.command.framework.BaseRoseCommand; 7 | import dev.rosewood.rosegarden.command.framework.CommandInfo; 8 | import dev.rosewood.rosegarden.command.framework.annotation.RoseExecutable; 9 | import dev.rosewood.rosegarden.utils.StringPlaceholders; 10 | import org.bukkit.entity.Player; 11 | import xyz.oribuin.eternaltags.manager.LocaleManager; 12 | import xyz.oribuin.eternaltags.manager.TagsManager; 13 | import xyz.oribuin.eternaltags.obj.Tag; 14 | 15 | public class RandomCommand extends BaseRoseCommand { 16 | 17 | public RandomCommand(RosePlugin rosePlugin) { 18 | super(rosePlugin); 19 | } 20 | 21 | @RoseExecutable 22 | public void execute(CommandContext context) { 23 | LocaleManager locale = this.rosePlugin.getManager(LocaleManager.class); 24 | TagsManager manager = this.rosePlugin.getManager(TagsManager.class); 25 | Player sender = (Player) context.getSender(); 26 | 27 | Tag randomTag = manager.getRandomTag(sender); 28 | if (randomTag == null) { 29 | locale.sendMessage(sender, "no-tags"); 30 | return; 31 | } 32 | 33 | randomTag.equip(sender); 34 | locale.sendMessage(sender, "command-set-changed", StringPlaceholders.of("tag", manager.getDisplayTag(randomTag, sender))); 35 | } 36 | 37 | @Override 38 | protected CommandInfo createCommandInfo() { 39 | return CommandInfo.builder("random") 40 | .descriptionKey("command-random-description") 41 | .permission("eternaltags.random") 42 | .playerOnly(true) 43 | .build(); 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/xyz/oribuin/eternaltags/command/impl/ReloadCommand.java: -------------------------------------------------------------------------------- 1 | package xyz.oribuin.eternaltags.command.impl; 2 | 3 | import dev.rosewood.rosegarden.RosePlugin; 4 | import dev.rosewood.rosegarden.command.framework.BaseRoseCommand; 5 | import dev.rosewood.rosegarden.command.framework.CommandContext; 6 | import dev.rosewood.rosegarden.command.framework.CommandInfo; 7 | import dev.rosewood.rosegarden.command.framework.annotation.RoseExecutable; 8 | import xyz.oribuin.eternaltags.config.Setting; 9 | import xyz.oribuin.eternaltags.gui.MenuProvider; 10 | import xyz.oribuin.eternaltags.listener.BungeeListener; 11 | import xyz.oribuin.eternaltags.manager.LocaleManager; 12 | 13 | public class ReloadCommand extends BaseRoseCommand { 14 | 15 | public ReloadCommand(RosePlugin rosePlugin) { 16 | super(rosePlugin); 17 | } 18 | 19 | @RoseExecutable 20 | public void execute(CommandContext context) { 21 | if (Setting.PLUGIN_MESSAGING_RELOAD.get()) { 22 | BungeeListener.sendReload(); // Send reload message to BungeeCord 23 | } 24 | 25 | MenuProvider.reload(); // Reload all menus 26 | this.rosePlugin.reload(); 27 | this.rosePlugin.getManager(LocaleManager.class).sendCommandMessage(context.getSender(), "command-reload-reloaded"); 28 | } 29 | 30 | @Override 31 | protected CommandInfo createCommandInfo() { 32 | return CommandInfo.builder("reload") 33 | .descriptionKey("command-reload-description") 34 | .permission("eternaltags.reload") 35 | .build(); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/xyz/oribuin/eternaltags/command/impl/SearchCommand.java: -------------------------------------------------------------------------------- 1 | package xyz.oribuin.eternaltags.command.impl; 2 | 3 | import dev.rosewood.rosegarden.RosePlugin; 4 | import dev.rosewood.rosegarden.command.argument.ArgumentHandlers; 5 | import dev.rosewood.rosegarden.command.framework.ArgumentsDefinition; 6 | import dev.rosewood.rosegarden.command.framework.BaseRoseCommand; 7 | import dev.rosewood.rosegarden.command.framework.CommandContext; 8 | import dev.rosewood.rosegarden.command.framework.CommandInfo; 9 | import dev.rosewood.rosegarden.command.framework.annotation.RoseExecutable; 10 | import org.bukkit.entity.Player; 11 | import xyz.oribuin.eternaltags.gui.MenuProvider; 12 | import xyz.oribuin.eternaltags.gui.menu.TagsGUI; 13 | import xyz.oribuin.eternaltags.obj.Tag; 14 | 15 | import java.util.function.Predicate; 16 | 17 | public class SearchCommand extends BaseRoseCommand { 18 | 19 | public SearchCommand(RosePlugin rosePlugin) { 20 | super(rosePlugin); 21 | } 22 | 23 | @RoseExecutable 24 | public void execute(CommandContext context, String keyword) { 25 | MenuProvider.get(TagsGUI.class).open((Player) context.getSender(), this.containsKeyword(keyword)); 26 | } 27 | 28 | /** 29 | * Predicate to check if a tag contains a keyword 30 | * 31 | * @param keyword The keyword to check 32 | * @return The predicate 33 | */ 34 | private Predicate containsKeyword(String keyword) { 35 | return tag -> tag.getId().contains(keyword) 36 | || tag.getName().contains(keyword) 37 | || tag.getDescription().contains(keyword); 38 | } 39 | 40 | @Override 41 | protected CommandInfo createCommandInfo() { 42 | return CommandInfo.builder("search") 43 | .descriptionKey("command-search-description") 44 | .permission("eternaltags.search") 45 | .playerOnly(true) 46 | .arguments(this.createArguments()) 47 | .build(); 48 | } 49 | 50 | private ArgumentsDefinition createArguments() { 51 | return ArgumentsDefinition.builder() 52 | .required("keyword", ArgumentHandlers.GREEDY_STRING) 53 | .build(); 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/xyz/oribuin/eternaltags/command/impl/SetAllCommand.java: -------------------------------------------------------------------------------- 1 | package xyz.oribuin.eternaltags.command.impl; 2 | 3 | import dev.rosewood.rosegarden.RosePlugin; 4 | import dev.rosewood.rosegarden.command.argument.ArgumentHandlers; 5 | import dev.rosewood.rosegarden.command.framework.ArgumentsDefinition; 6 | import dev.rosewood.rosegarden.command.framework.CommandContext; 7 | import dev.rosewood.rosegarden.command.framework.BaseRoseCommand; 8 | import dev.rosewood.rosegarden.command.framework.CommandInfo; 9 | import dev.rosewood.rosegarden.command.framework.annotation.RoseExecutable; 10 | import dev.rosewood.rosegarden.utils.StringPlaceholders; 11 | import org.bukkit.Bukkit; 12 | import org.bukkit.command.CommandSender; 13 | import xyz.oribuin.eternaltags.command.argument.TagsArgumentHandler; 14 | import xyz.oribuin.eternaltags.manager.LocaleManager; 15 | import xyz.oribuin.eternaltags.manager.TagsManager; 16 | import xyz.oribuin.eternaltags.obj.Tag; 17 | 18 | public class SetAllCommand extends BaseRoseCommand { 19 | 20 | public SetAllCommand(RosePlugin rosePlugin) { 21 | super(rosePlugin); 22 | } 23 | 24 | @RoseExecutable 25 | public void execute(CommandContext context, Tag tag, String silent) { 26 | LocaleManager locale = this.rosePlugin.getManager(LocaleManager.class); 27 | TagsManager manager = this.rosePlugin.getManager(TagsManager.class); 28 | CommandSender sender = context.getSender(); 29 | manager.setEveryone(tag); 30 | 31 | if (silent == null) { 32 | Bukkit.getOnlinePlayers().forEach(player -> locale.sendMessage(player, "command-set-changed", StringPlaceholders.of("tag", manager.getDisplayTag(tag, player)))); 33 | } 34 | 35 | locale.sendMessage(sender, "command-setall-changed", StringPlaceholders.of("tag", manager.getDisplayTag(tag, null))); 36 | } 37 | 38 | @Override 39 | protected CommandInfo createCommandInfo() { 40 | return CommandInfo.builder("setall") 41 | .descriptionKey("command-setall-description") 42 | .permission("eternaltags.setall") 43 | .arguments(this.createArguments()) 44 | .build(); 45 | } 46 | 47 | private ArgumentsDefinition createArguments() { 48 | return ArgumentsDefinition.builder() 49 | .required("tag", new TagsArgumentHandler()) 50 | .optional("silent", ArgumentHandlers.STRING) 51 | .build(); 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/xyz/oribuin/eternaltags/command/impl/SetCommand.java: -------------------------------------------------------------------------------- 1 | package xyz.oribuin.eternaltags.command.impl; 2 | 3 | import dev.rosewood.rosegarden.RosePlugin; 4 | import dev.rosewood.rosegarden.command.argument.ArgumentHandlers; 5 | import dev.rosewood.rosegarden.command.framework.ArgumentsDefinition; 6 | import dev.rosewood.rosegarden.command.framework.BaseRoseCommand; 7 | import dev.rosewood.rosegarden.command.framework.CommandContext; 8 | import dev.rosewood.rosegarden.command.framework.CommandInfo; 9 | import dev.rosewood.rosegarden.command.framework.annotation.RoseExecutable; 10 | import dev.rosewood.rosegarden.utils.StringPlaceholders; 11 | import org.bukkit.command.CommandSender; 12 | import org.bukkit.entity.Player; 13 | import xyz.oribuin.eternaltags.command.argument.TagsArgumentHandler; 14 | import xyz.oribuin.eternaltags.manager.LocaleManager; 15 | import xyz.oribuin.eternaltags.manager.TagsManager; 16 | import xyz.oribuin.eternaltags.obj.Tag; 17 | 18 | public class SetCommand extends BaseRoseCommand { 19 | 20 | public SetCommand(RosePlugin rosePlugin) { 21 | super(rosePlugin); 22 | } 23 | 24 | @RoseExecutable 25 | public void execute(CommandContext context, Tag tag, Player target, String silent) { 26 | LocaleManager locale = this.rosePlugin.getManager(LocaleManager.class); 27 | TagsManager manager = this.rosePlugin.getManager(TagsManager.class); 28 | CommandSender sender = context.getSender(); 29 | 30 | // may need to check if tag == null? 31 | if (tag == null) { 32 | locale.sendMessage(sender, "tag-doesnt-exist"); 33 | return; 34 | } 35 | 36 | // Setting another player's tag 37 | if (target != null) { 38 | if (!sender.hasPermission("eternaltags.set.other")) { 39 | locale.sendMessage(sender, "no-permission"); 40 | return; 41 | } 42 | 43 | tag.equip(target); 44 | 45 | if (silent == null) { 46 | locale.sendMessage(target, "command-set-changed", StringPlaceholders.of("tag", manager.getDisplayTag(tag, target))); 47 | } 48 | 49 | locale.sendMessage(sender, "command-set-changed-other", StringPlaceholders.builder("tag", manager.getDisplayTag(tag, target)) 50 | .add("player", target.getName()) 51 | .build()); 52 | 53 | return; 54 | } 55 | 56 | // Setting own tag 57 | if (!(sender instanceof final Player pl)) { 58 | locale.sendMessage(sender, "only-player"); 59 | return; 60 | } 61 | 62 | if (!manager.canUseTag(pl, tag)) { 63 | locale.sendMessage(pl, "command-set-no-permission"); 64 | return; 65 | } 66 | 67 | tag.equip(pl); 68 | locale.sendMessage(sender, "command-set-changed", StringPlaceholders.of("tag", manager.getDisplayTag(tag, pl))); 69 | } 70 | 71 | @Override 72 | protected CommandInfo createCommandInfo() { 73 | return CommandInfo.builder("set") 74 | .descriptionKey("command-set-description") 75 | .permission("eternaltags.set") 76 | .arguments(this.createArguments()) 77 | .build(); 78 | } 79 | 80 | private ArgumentsDefinition createArguments() { 81 | return ArgumentsDefinition.builder() 82 | .required("tag", new TagsArgumentHandler()) 83 | .optional("target", ArgumentHandlers.PLAYER) 84 | .optional("silent", ArgumentHandlers.STRING) 85 | .build(); 86 | } 87 | 88 | } 89 | -------------------------------------------------------------------------------- /src/main/java/xyz/oribuin/eternaltags/command/impl/TagsCommand.java: -------------------------------------------------------------------------------- 1 | package xyz.oribuin.eternaltags.command.impl; 2 | 3 | import dev.rosewood.rosegarden.RosePlugin; 4 | import dev.rosewood.rosegarden.command.HelpCommand; 5 | import dev.rosewood.rosegarden.command.framework.ArgumentsDefinition; 6 | import dev.rosewood.rosegarden.command.framework.BaseRoseCommand; 7 | import dev.rosewood.rosegarden.command.framework.CommandContext; 8 | import dev.rosewood.rosegarden.command.framework.CommandInfo; 9 | import dev.rosewood.rosegarden.command.framework.annotation.RoseExecutable; 10 | import org.bukkit.entity.Player; 11 | import xyz.oribuin.eternaltags.config.Setting; 12 | import xyz.oribuin.eternaltags.gui.MenuProvider; 13 | import xyz.oribuin.eternaltags.gui.menu.CategoryGUI; 14 | import xyz.oribuin.eternaltags.gui.menu.TagsGUI; 15 | import xyz.oribuin.eternaltags.manager.LocaleManager; 16 | 17 | public class TagsCommand extends BaseRoseCommand { 18 | 19 | public TagsCommand(RosePlugin rosePlugin) { 20 | super(rosePlugin); 21 | } 22 | 23 | @RoseExecutable 24 | public void execute(CommandContext context) { 25 | LocaleManager locale = this.rosePlugin.getManager(LocaleManager.class); 26 | 27 | // Make sure the sender is a player. 28 | if (!(context.getSender() instanceof Player player)) { 29 | locale.sendMessage(context.getSender(), "only-player"); 30 | return; 31 | } 32 | 33 | if (Setting.OPEN_CATEGORY_GUI_FIRST.get()) 34 | MenuProvider.get(CategoryGUI.class).open(player); 35 | else 36 | MenuProvider.get(TagsGUI.class).open(player, null); 37 | } 38 | 39 | @Override 40 | protected CommandInfo createCommandInfo() { 41 | return CommandInfo.builder("tags") 42 | .descriptionKey("command-tags-description") 43 | .permission("eternaltags.use") 44 | .arguments(this.createArguments()) 45 | .build(); 46 | } 47 | 48 | private ArgumentsDefinition createArguments() { 49 | return ArgumentsDefinition.builder().optionalSub( 50 | new HelpCommand(this.rosePlugin, this), 51 | new CategoriesCommand(this.rosePlugin), 52 | new ClearCommand(this.rosePlugin), 53 | new ConvertCommand(this.rosePlugin), 54 | new CreateCommand(this.rosePlugin), 55 | new DeleteCommand(this.rosePlugin), 56 | new EditCommand(this.rosePlugin), 57 | new FavoriteCommand(this.rosePlugin), 58 | new RandomCommand(this.rosePlugin), 59 | new ReloadCommand(this.rosePlugin), 60 | new SearchCommand(this.rosePlugin), 61 | new SetAllCommand(this.rosePlugin), 62 | new SetCommand(this.rosePlugin) 63 | ); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/xyz/oribuin/eternaltags/command/impl/def/HelpCommand.java: -------------------------------------------------------------------------------- 1 | package xyz.oribuin.eternaltags.command.impl.def; 2 | 3 | import dev.rosewood.rosegarden.RosePlugin; 4 | import dev.rosewood.rosegarden.command.framework.BaseRoseCommand; 5 | import dev.rosewood.rosegarden.command.framework.CommandInfo; 6 | 7 | public class HelpCommand extends dev.rosewood.rosegarden.command.HelpCommand { 8 | 9 | public HelpCommand(RosePlugin rosePlugin, BaseRoseCommand parent, CommandInfo commandInfo) { 10 | super(rosePlugin, parent, commandInfo); 11 | } 12 | 13 | @Override 14 | protected CommandInfo createCommandInfo() { 15 | return CommandInfo.builder("help") 16 | .descriptionKey("command-help-description") 17 | .permission("eternaltags.help") 18 | .build(); 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/xyz/oribuin/eternaltags/command/impl/edit/EditCategoryCommand.java: -------------------------------------------------------------------------------- 1 | package xyz.oribuin.eternaltags.command.impl.edit; 2 | 3 | import dev.rosewood.rosegarden.RosePlugin; 4 | import dev.rosewood.rosegarden.command.framework.ArgumentsDefinition; 5 | import dev.rosewood.rosegarden.command.framework.BaseRoseCommand; 6 | import dev.rosewood.rosegarden.command.framework.CommandContext; 7 | import dev.rosewood.rosegarden.command.framework.CommandInfo; 8 | import dev.rosewood.rosegarden.command.framework.annotation.RoseExecutable; 9 | import dev.rosewood.rosegarden.utils.StringPlaceholders; 10 | import org.bukkit.entity.Player; 11 | import xyz.oribuin.eternaltags.command.argument.CategoryArgumentHandler; 12 | import xyz.oribuin.eternaltags.command.argument.TagsArgumentHandler; 13 | import xyz.oribuin.eternaltags.manager.LocaleManager; 14 | import xyz.oribuin.eternaltags.manager.TagsManager; 15 | import xyz.oribuin.eternaltags.obj.Category; 16 | import xyz.oribuin.eternaltags.obj.Tag; 17 | 18 | public class EditCategoryCommand extends BaseRoseCommand { 19 | 20 | public EditCategoryCommand(RosePlugin rosePlugin) { 21 | super(rosePlugin); 22 | } 23 | 24 | @RoseExecutable 25 | public void execute(CommandContext context, Tag tag, Category category) { 26 | TagsManager manager = this.rosePlugin.getManager(TagsManager.class); 27 | LocaleManager locale = this.rosePlugin.getManager(LocaleManager.class); 28 | 29 | tag.setCategory(category.getId()); 30 | manager.saveTag(tag); 31 | manager.updateActiveTag(tag); 32 | 33 | StringPlaceholders placeholders = StringPlaceholders.builder() 34 | .add("tag", manager.getDisplayTag(tag, context.getSender() instanceof Player player ? player : null)) 35 | .add("option", "category") 36 | .add("id", tag.getId()) 37 | .add("name", tag.getName()) 38 | .add("value", category.getId()) 39 | .build(); 40 | 41 | locale.sendMessage(context.getSender(), "command-edit-edited", placeholders); 42 | } 43 | 44 | @Override 45 | protected CommandInfo createCommandInfo() { 46 | return CommandInfo.builder("category") 47 | .permission("eternaltags.edit") 48 | .arguments(this.createArguments()) 49 | .build(); 50 | } 51 | 52 | private ArgumentsDefinition createArguments() { 53 | return ArgumentsDefinition.builder() 54 | .required("tag", new TagsArgumentHandler()) 55 | .required("category", new CategoryArgumentHandler()) 56 | .build(); 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/xyz/oribuin/eternaltags/command/impl/edit/EditDescriptionCommand.java: -------------------------------------------------------------------------------- 1 | package xyz.oribuin.eternaltags.command.impl.edit; 2 | 3 | import dev.rosewood.rosegarden.RosePlugin; 4 | import dev.rosewood.rosegarden.command.argument.ArgumentHandlers; 5 | import dev.rosewood.rosegarden.command.framework.ArgumentsDefinition; 6 | import dev.rosewood.rosegarden.command.framework.BaseRoseCommand; 7 | import dev.rosewood.rosegarden.command.framework.CommandContext; 8 | import dev.rosewood.rosegarden.command.framework.CommandInfo; 9 | import dev.rosewood.rosegarden.command.framework.annotation.RoseExecutable; 10 | import dev.rosewood.rosegarden.utils.StringPlaceholders; 11 | import org.bukkit.entity.Player; 12 | import xyz.oribuin.eternaltags.command.argument.TagsArgumentHandler; 13 | import xyz.oribuin.eternaltags.manager.LocaleManager; 14 | import xyz.oribuin.eternaltags.manager.TagsManager; 15 | import xyz.oribuin.eternaltags.obj.Tag; 16 | 17 | import java.util.ArrayList; 18 | import java.util.List; 19 | 20 | public class EditDescriptionCommand extends BaseRoseCommand { 21 | 22 | public EditDescriptionCommand(RosePlugin rosePlugin) { 23 | super(rosePlugin); 24 | } 25 | 26 | @RoseExecutable 27 | public void execute(CommandContext context, Tag tag, int order, String line) { 28 | TagsManager manager = this.rosePlugin.getManager(TagsManager.class); 29 | LocaleManager locale = this.rosePlugin.getManager(LocaleManager.class); 30 | 31 | List description = new ArrayList<>(tag.getDescription()); 32 | 33 | if (line.equalsIgnoreCase("remove") && description.remove(order) != null) { 34 | StringPlaceholders placeholders = StringPlaceholders.builder("tag", manager.getDisplayTag(tag, null)) 35 | .add("id", tag.getId()) 36 | .add("name", tag.getName()) 37 | .add("value", order) 38 | .build(); 39 | 40 | locale.sendMessage(context.getSender(), "command-edit-description-removed", placeholders); 41 | tag.setDescription(description); 42 | manager.saveTag(tag); 43 | manager.updateActiveTag(tag); 44 | return; 45 | } 46 | 47 | description.set(order, line); 48 | tag.setDescription(description); 49 | manager.saveTag(tag); 50 | manager.updateActiveTag(tag); 51 | 52 | StringPlaceholders placeholders = StringPlaceholders.builder() 53 | .add("tag", manager.getDisplayTag(tag, context.getSender() instanceof Player ? (Player) context.getSender() : null)) 54 | .add("option", "description") 55 | .add("id", tag.getId()) 56 | .add("name", tag.getName()) 57 | .add("value", "line " + order + " set to " + line) 58 | .build(); 59 | 60 | locale.sendMessage(context.getSender(), "command-edit-edited", placeholders); 61 | } 62 | 63 | @Override 64 | protected CommandInfo createCommandInfo() { 65 | return CommandInfo.builder("description") 66 | .permission("eternaltags.edit") 67 | .arguments(this.createArguments()) 68 | .build(); 69 | } 70 | 71 | private ArgumentsDefinition createArguments() { 72 | return ArgumentsDefinition.builder() 73 | .required("tag", new TagsArgumentHandler()) 74 | .required("order", ArgumentHandlers.INTEGER) 75 | .required("line", ArgumentHandlers.GREEDY_STRING) 76 | .build(); 77 | } 78 | 79 | 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/xyz/oribuin/eternaltags/command/impl/edit/EditIconCommand.java: -------------------------------------------------------------------------------- 1 | package xyz.oribuin.eternaltags.command.impl.edit; 2 | 3 | import dev.rosewood.rosegarden.RosePlugin; 4 | import dev.rosewood.rosegarden.command.argument.ArgumentHandlers; 5 | import dev.rosewood.rosegarden.command.framework.ArgumentsDefinition; 6 | import dev.rosewood.rosegarden.command.framework.BaseRoseCommand; 7 | import dev.rosewood.rosegarden.command.framework.CommandContext; 8 | import dev.rosewood.rosegarden.command.framework.CommandInfo; 9 | import dev.rosewood.rosegarden.command.framework.annotation.RoseExecutable; 10 | import dev.rosewood.rosegarden.utils.StringPlaceholders; 11 | import org.bukkit.entity.Player; 12 | import org.bukkit.inventory.ItemStack; 13 | import xyz.oribuin.eternaltags.command.argument.TagsArgumentHandler; 14 | import xyz.oribuin.eternaltags.manager.LocaleManager; 15 | import xyz.oribuin.eternaltags.manager.TagsManager; 16 | import xyz.oribuin.eternaltags.obj.Tag; 17 | 18 | public class EditIconCommand extends BaseRoseCommand { 19 | 20 | public EditIconCommand(RosePlugin rosePlugin) { 21 | super(rosePlugin); 22 | } 23 | 24 | @RoseExecutable 25 | public void execute(CommandContext context, Tag tag, Boolean remove) { 26 | TagsManager manager = this.rosePlugin.getManager(TagsManager.class); 27 | LocaleManager locale = this.rosePlugin.getManager(LocaleManager.class); 28 | 29 | Player player = (Player) context.getSender(); 30 | ItemStack item = player.getInventory().getItemInMainHand(); 31 | 32 | boolean shouldRemove = remove != null && remove; 33 | 34 | if (item.getType().isAir() || !item.getType().isItem() && !shouldRemove) { 35 | locale.sendMessage(context.getSender(), "command-edit-invalid-item"); 36 | return; 37 | } 38 | 39 | if (shouldRemove) { 40 | item = null; 41 | } 42 | 43 | tag.setIcon(item); // If the material is null, then the player is holding the item. 44 | tag.setHandIcon(item != null); // If the material is null, then the player is holding the item. 45 | 46 | manager.saveTag(tag); 47 | manager.updateActiveTag(tag); 48 | 49 | StringPlaceholders placeholders = StringPlaceholders.builder() 50 | .add("tag", manager.getDisplayTag(tag, player)) 51 | .add("option", "icon") 52 | .add("id", tag.getId()) 53 | .add("name", tag.getName()) 54 | .add("value", (item == null ? "None" : item.getType().name().toLowerCase())) 55 | .build(); 56 | 57 | locale.sendMessage(context.getSender(), "command-edit-edited", placeholders); 58 | } 59 | 60 | @Override 61 | protected CommandInfo createCommandInfo() { 62 | return CommandInfo.builder("icon") 63 | .permission("eternaltags.edit") 64 | .playerOnly(true) 65 | .arguments(this.createArguments()) 66 | .build(); 67 | } 68 | 69 | private ArgumentsDefinition createArguments() { 70 | return ArgumentsDefinition.builder() 71 | .required("tag", new TagsArgumentHandler()) 72 | .optional("remove", ArgumentHandlers.BOOLEAN) 73 | .build(); 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/xyz/oribuin/eternaltags/command/impl/edit/EditNameCommand.java: -------------------------------------------------------------------------------- 1 | package xyz.oribuin.eternaltags.command.impl.edit; 2 | 3 | import dev.rosewood.rosegarden.RosePlugin; 4 | import dev.rosewood.rosegarden.command.argument.ArgumentHandlers; 5 | import dev.rosewood.rosegarden.command.framework.ArgumentsDefinition; 6 | import dev.rosewood.rosegarden.command.framework.BaseRoseCommand; 7 | import dev.rosewood.rosegarden.command.framework.CommandContext; 8 | import dev.rosewood.rosegarden.command.framework.CommandInfo; 9 | import dev.rosewood.rosegarden.command.framework.annotation.RoseExecutable; 10 | import dev.rosewood.rosegarden.utils.StringPlaceholders; 11 | import org.bukkit.entity.Player; 12 | import xyz.oribuin.eternaltags.command.argument.TagsArgumentHandler; 13 | import xyz.oribuin.eternaltags.manager.LocaleManager; 14 | import xyz.oribuin.eternaltags.manager.TagsManager; 15 | import xyz.oribuin.eternaltags.obj.Tag; 16 | 17 | public class EditNameCommand extends BaseRoseCommand { 18 | 19 | public EditNameCommand(RosePlugin rosePlugin) { 20 | super(rosePlugin); 21 | } 22 | 23 | @RoseExecutable 24 | public void execute(CommandContext context, Tag tag, String name) { 25 | TagsManager manager = this.rosePlugin.getManager(TagsManager.class); 26 | LocaleManager locale = this.rosePlugin.getManager(LocaleManager.class); 27 | 28 | tag.setName(name); 29 | manager.saveTag(tag); 30 | manager.updateActiveTag(tag); 31 | 32 | StringPlaceholders placeholders = StringPlaceholders.builder() 33 | .add("tag", manager.getDisplayTag(tag, context.getSender() instanceof Player ? (Player) context.getSender() : null)) 34 | .add("option", "name") 35 | .add("id", tag.getId()) 36 | .add("name", tag.getName()) 37 | .add("value", name) 38 | .build(); 39 | 40 | locale.sendMessage(context.getSender(), "command-edit-edited", placeholders); 41 | } 42 | 43 | @Override 44 | protected CommandInfo createCommandInfo() { 45 | return CommandInfo.builder("name") 46 | .permission("eternaltags.edit") 47 | .arguments(this.createArguments()) 48 | .build(); 49 | } 50 | 51 | private ArgumentsDefinition createArguments() { 52 | return ArgumentsDefinition.builder() 53 | .required("tag", new TagsArgumentHandler()) 54 | .optional("name", ArgumentHandlers.GREEDY_STRING) 55 | .build(); 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/xyz/oribuin/eternaltags/command/impl/edit/EditOrderCommand.java: -------------------------------------------------------------------------------- 1 | package xyz.oribuin.eternaltags.command.impl.edit; 2 | 3 | import dev.rosewood.rosegarden.RosePlugin; 4 | import dev.rosewood.rosegarden.command.argument.ArgumentHandlers; 5 | import dev.rosewood.rosegarden.command.framework.ArgumentsDefinition; 6 | import dev.rosewood.rosegarden.command.framework.BaseRoseCommand; 7 | import dev.rosewood.rosegarden.command.framework.CommandContext; 8 | import dev.rosewood.rosegarden.command.framework.CommandInfo; 9 | import dev.rosewood.rosegarden.command.framework.annotation.RoseExecutable; 10 | import dev.rosewood.rosegarden.utils.StringPlaceholders; 11 | import org.bukkit.entity.Player; 12 | import xyz.oribuin.eternaltags.command.argument.TagsArgumentHandler; 13 | import xyz.oribuin.eternaltags.manager.LocaleManager; 14 | import xyz.oribuin.eternaltags.manager.TagsManager; 15 | import xyz.oribuin.eternaltags.obj.Tag; 16 | 17 | public class EditOrderCommand extends BaseRoseCommand { 18 | 19 | public EditOrderCommand(RosePlugin rosePlugin) { 20 | super(rosePlugin); 21 | } 22 | 23 | @RoseExecutable 24 | public void execute(CommandContext context, Tag tag, int order) { 25 | TagsManager manager = this.rosePlugin.getManager(TagsManager.class); 26 | LocaleManager locale = this.rosePlugin.getManager(LocaleManager.class); 27 | 28 | tag.setOrder(order); 29 | manager.saveTag(tag); 30 | manager.updateActiveTag(tag); 31 | 32 | StringPlaceholders placeholders = StringPlaceholders.builder() 33 | .add("tag", manager.getDisplayTag(tag, context.getSender() instanceof Player ? (Player) context.getSender() : null)) 34 | .add("option", "order") 35 | .add("id", tag.getId()) 36 | .add("name", tag.getName()) 37 | .add("value", order) 38 | .build(); 39 | 40 | locale.sendMessage(context.getSender(), "command-edit-edited", placeholders); 41 | } 42 | 43 | @Override 44 | protected CommandInfo createCommandInfo() { 45 | return CommandInfo.builder("order") 46 | .permission("eternaltags.edit") 47 | .arguments(this.createArguments()) 48 | .build(); 49 | } 50 | 51 | private ArgumentsDefinition createArguments() { 52 | return ArgumentsDefinition.builder() 53 | .required("tag", new TagsArgumentHandler()) 54 | .required("order", ArgumentHandlers.INTEGER) 55 | .build(); 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/xyz/oribuin/eternaltags/command/impl/edit/EditPermissionCommand.java: -------------------------------------------------------------------------------- 1 | package xyz.oribuin.eternaltags.command.impl.edit; 2 | 3 | import dev.rosewood.rosegarden.RosePlugin; 4 | import dev.rosewood.rosegarden.command.argument.ArgumentHandlers; 5 | import dev.rosewood.rosegarden.command.framework.ArgumentsDefinition; 6 | import dev.rosewood.rosegarden.command.framework.BaseRoseCommand; 7 | import dev.rosewood.rosegarden.command.framework.CommandContext; 8 | import dev.rosewood.rosegarden.command.framework.CommandInfo; 9 | import dev.rosewood.rosegarden.command.framework.annotation.RoseExecutable; 10 | import dev.rosewood.rosegarden.utils.StringPlaceholders; 11 | import org.bukkit.entity.Player; 12 | import xyz.oribuin.eternaltags.command.argument.TagsArgumentHandler; 13 | import xyz.oribuin.eternaltags.manager.LocaleManager; 14 | import xyz.oribuin.eternaltags.manager.TagsManager; 15 | import xyz.oribuin.eternaltags.obj.Tag; 16 | 17 | public class EditPermissionCommand extends BaseRoseCommand { 18 | 19 | public EditPermissionCommand(RosePlugin rosePlugin) { 20 | super(rosePlugin); 21 | } 22 | 23 | @RoseExecutable 24 | public void execute(CommandContext context, Tag tag, String permission) { 25 | TagsManager manager = this.rosePlugin.getManager(TagsManager.class); 26 | LocaleManager locale = this.rosePlugin.getManager(LocaleManager.class); 27 | 28 | tag.setPermission(permission); 29 | manager.saveTag(tag); 30 | manager.updateActiveTag(tag); 31 | 32 | StringPlaceholders placeholders = StringPlaceholders.builder() 33 | .add("tag", manager.getDisplayTag(tag, context.getSender() instanceof Player ? (Player) context.getSender() : null)) 34 | .add("option", "permission") 35 | .add("id", tag.getId()) 36 | .add("name", tag.getName()) 37 | .add("value", permission) 38 | .build(); 39 | 40 | locale.sendMessage(context.getSender(), "command-edit-edited", placeholders); 41 | } 42 | 43 | @Override 44 | protected CommandInfo createCommandInfo() { 45 | return CommandInfo.builder("permission") 46 | .permission("eternaltags.edit") 47 | .arguments(this.createArguments()) 48 | .arguments(this.createArguments()) 49 | .build(); 50 | } 51 | 52 | private ArgumentsDefinition createArguments() { 53 | return ArgumentsDefinition.builder() 54 | .required("tag", new TagsArgumentHandler()) 55 | .required("permission", ArgumentHandlers.GREEDY_STRING) 56 | .build(); 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/xyz/oribuin/eternaltags/command/impl/edit/EditTagCommand.java: -------------------------------------------------------------------------------- 1 | package xyz.oribuin.eternaltags.command.impl.edit; 2 | 3 | import dev.rosewood.rosegarden.RosePlugin; 4 | import dev.rosewood.rosegarden.command.argument.ArgumentHandlers; 5 | import dev.rosewood.rosegarden.command.framework.ArgumentsDefinition; 6 | import dev.rosewood.rosegarden.command.framework.BaseRoseCommand; 7 | import dev.rosewood.rosegarden.command.framework.CommandContext; 8 | import dev.rosewood.rosegarden.command.framework.CommandInfo; 9 | import dev.rosewood.rosegarden.command.framework.annotation.RoseExecutable; 10 | import dev.rosewood.rosegarden.utils.StringPlaceholders; 11 | import org.bukkit.entity.Player; 12 | import xyz.oribuin.eternaltags.command.argument.TagsArgumentHandler; 13 | import xyz.oribuin.eternaltags.manager.LocaleManager; 14 | import xyz.oribuin.eternaltags.manager.TagsManager; 15 | import xyz.oribuin.eternaltags.obj.Tag; 16 | 17 | public class EditTagCommand extends BaseRoseCommand { 18 | 19 | public EditTagCommand(RosePlugin rosePlugin) { 20 | super(rosePlugin); 21 | } 22 | 23 | @RoseExecutable 24 | public void execute(CommandContext context, Tag tag, String newTag) { 25 | TagsManager manager = this.rosePlugin.getManager(TagsManager.class); 26 | LocaleManager locale = this.rosePlugin.getManager(LocaleManager.class); 27 | 28 | tag.setTag(newTag); 29 | manager.saveTag(tag); 30 | manager.updateActiveTag(tag); 31 | 32 | StringPlaceholders placeholders = StringPlaceholders.builder() 33 | .add("tag", manager.getDisplayTag(tag, context.getSender() instanceof Player ? (Player) context.getSender() : null)) 34 | .add("option", "tag") 35 | .add("id", tag.getId()) 36 | .add("name", tag.getName()) 37 | .add("value", newTag) 38 | .build(); 39 | 40 | locale.sendMessage(context.getSender(), "command-edit-edited", placeholders); 41 | } 42 | 43 | @Override 44 | protected CommandInfo createCommandInfo() { 45 | return CommandInfo.builder("tag") 46 | .permission("eternaltags.edit") 47 | .arguments(this.createArguments()) 48 | .build(); 49 | } 50 | 51 | private ArgumentsDefinition createArguments() { 52 | return ArgumentsDefinition.builder() 53 | .required("tag", new TagsArgumentHandler()) 54 | .required("newTag", ArgumentHandlers.GREEDY_STRING) 55 | .build(); 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/xyz/oribuin/eternaltags/config/Setting.java: -------------------------------------------------------------------------------- 1 | package xyz.oribuin.eternaltags.config; 2 | 3 | import dev.rosewood.rosegarden.config.CommentedConfigurationSection; 4 | import dev.rosewood.rosegarden.config.RoseSetting; 5 | import dev.rosewood.rosegarden.config.RoseSettingSerializer; 6 | import dev.rosewood.rosegarden.config.RoseSettingSerializers; 7 | import xyz.oribuin.eternaltags.EternalTags; 8 | 9 | import javax.print.DocFlavor; 10 | import java.security.PrivateKey; 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | 14 | import static dev.rosewood.rosegarden.config.RoseSettingSerializers.*; 15 | 16 | /** 17 | * The general settings for the plugin. 18 | */ 19 | public class Setting { 20 | 21 | private static final List> KEYS = new ArrayList<>(); 22 | 23 | public static final RoseSetting DEFAULT_TAG = create("default-tag", STRING, "none", "The tag that will show when player does not have an active tag.", "Set to 'none' to disable.", "Set to 'random' to apply a random tag"); 24 | 25 | public static final RoseSetting DEFAULT_TAG_GROUPS = create("default-tag-groups", "The groups that will be applied to the player when they join the server.", "Set to 'none' to disable.", "Set to 'random' to apply a random tag", "This requires vault and a vault supported permission plugin."); 26 | private static final RoseSetting DEFAULT_TAG_GROUP_DEFAULT = create("default-tag-groups.default", STRING, "none"); 27 | 28 | public static final RoseSetting REMOVE_TAGS = create("remove-inaccessible-tags", BOOLEAN, false, "Should a tag be automatically removed if the player doesn't have permission to use it?"); 29 | 30 | // Formatting 31 | public static final RoseSetting CHAT_PLACEHOLDERS = create("chat-placeholders", BOOLEAN, false, "Should the plugin change the Chat Format to allow PlaceholderAPI placeholders to be used?", 32 | "It is recommended to enable this if you are using EssentialsXChat or a chat plugin that does not support PlaceholderAPI.", 33 | "It's not recommended for you to enable this if your chat plugin already supports PlaceholderAPI (Most do)."); 34 | 35 | public static final RoseSetting FORMATTED_PLACEHOLDER = create("formatted-placeholder", STRING, "None", "The placeholder that will show when the player has no active tag."); 36 | 37 | public static final RoseSetting TAG_UNLOCKED_FORMAT = create("tag-unlocked-format", STRING, "&a&lUnlocked", "The format that will show when the player has the tag unlocked."); 38 | public static final RoseSetting TAG_LOCKED_FORMAT = create("tag-locked-format", STRING, "&c&lLocked", "The format that will show when the player has the tag locked."); 39 | public static final RoseSetting TAG_PREFIX = create("tag-prefix", STRING, "", "The prefix that will be added in front of the tag in the placeholder"); 40 | public static final RoseSetting TAG_SUFFIX = create("tag-suffix", STRING, "", "The suffix that will be added after the tag in the placeholder"); 41 | public static final RoseSetting DESCRIPTION_DELIMITER = create("description-delimiter", STRING, "\n", "The delimiter that will be used for %eternaltags_tag_description%"); 42 | 43 | // Other Options 44 | public static final RoseSetting RE_EQUIP_CLEAR = create("reequip-clear", BOOLEAN, false, "Should the player's tag be cleared when they re-equip the same tag?"); 45 | public static final RoseSetting CACHE_GUI_TAGS = create("cache-gui-tags", BOOLEAN, true, "Should the tag items be cached? (Keeps the items in memory instead of creating them every time the GUI is opened)", 46 | "This will reduce the amount of lag when opening the GUI, but will use more memory.", 47 | "This will also make tags with placeholders not update until the plugin is reloaded." 48 | ); 49 | public static final RoseSetting OPEN_CATEGORY_GUI_FIRST = create("open-category-gui-first", BOOLEAN, false, "Should the category GUI be opened first when a player types /tags?"); 50 | public static final RoseSetting CACHE_GUI_CATEGORIES = create("cache-gui-categories", BOOLEAN, false, "Should the category items be cached? (Keeps the items in memory instead of creating them every time the GUI is opened)", 51 | "This will reduce the amount of lag when opening the GUI, but will use more memory.", 52 | "This will also make categories with placeholders not update until the plugin is reloaded."); 53 | 54 | 55 | // Data Systems 56 | public static final RoseSetting MYSQL_TAGDATA = create("save-tagdata-sql", BOOLEAN, false, "Should the tag data be stored in a MySQL/SQLite database? (Tags that would be saved in tags.yml)"); 57 | private static final RoseSetting PLUGIN_MESSAGING = create("plugin-messaging", "Should the plugin use plugin messaging to communicate between servers? (Requires BungeeCord)"); 58 | public static final RoseSetting PLUGIN_MESSAGING_RELOAD = create("plugin-messaging.reload", BOOLEAN, false, "Should /tags reload run on all servers? (Requires BungeeCord)"); 59 | 60 | /** 61 | * Establishes a configuration setting for the plugin which will be generated on reload. 62 | * 63 | * @param key The key (path) of the setting 64 | * @param serializer The {@link dev.rosewood.rosegarden.config.RoseSettingSerializers} for the setting 65 | * @param defaultValue The default value of the setting 66 | * @param comments The comments for the setting 67 | * @param The type of the setting 68 | * @return The generated {@link dev.rosewood.rosegarden.config.RoseSetting} 69 | */ 70 | private static RoseSetting create(String key, RoseSettingSerializer serializer, T defaultValue, String... comments) { 71 | RoseSetting setting = RoseSetting.backed(EternalTags.getInstance(), key, serializer, defaultValue, comments); 72 | KEYS.add(setting); 73 | return setting; 74 | } 75 | 76 | /** 77 | * Establishes a configuration setting for the plugin which will be generated on reload. 78 | * 79 | * @param key The key (path) of the setting 80 | * @param comments The comments for the setting 81 | * @return The generated {@link dev.rosewood.rosegarden.config.RoseSetting} 82 | */ 83 | private static RoseSetting create(String key, String... comments) { 84 | RoseSetting setting = RoseSetting.backedSection(EternalTags.getInstance(), key, comments); 85 | KEYS.add(setting); 86 | return setting; 87 | } 88 | 89 | /** 90 | * All the general settings for the plugin. 91 | * 92 | * @return The generated {@link dev.rosewood.rosegarden.config.RoseSetting} for the plugin. 93 | */ 94 | public static List> getKeys() { 95 | return KEYS; 96 | } 97 | 98 | /** 99 | * Get the header for the configuration file. 100 | * 101 | * @return The header for the configuration file.\ 102 | */ 103 | public static String[] getHeader() { 104 | return new String[]{ 105 | "___________ __ ._____________", 106 | "\\_ _____// |_ ___________ ____ _____ | \\__ ___/____ ____ ______", 107 | " | __)_\\ __\\/ __ \\_ __ \\/ \\\\__ \\ | | | | \\__ \\ / ___\\/ ___/", 108 | " | \\| | \\ ___/| | \\/ | \\/ __ \\| |_| | / __ \\_/ /_/ >___ \\ ", 109 | "/_______ /|__| \\___ >__| |___| (____ /____/____| (____ /\\___ /____ >", 110 | " \\/ \\/ \\/ \\/ \\//_____/ \\/ " 111 | }; 112 | } 113 | } -------------------------------------------------------------------------------- /src/main/java/xyz/oribuin/eternaltags/conversion/AlonsoConversion.java: -------------------------------------------------------------------------------- 1 | package xyz.oribuin.eternaltags.conversion; 2 | 3 | import dev.rosewood.rosegarden.RosePlugin; 4 | import org.bukkit.Material; 5 | import org.bukkit.configuration.ConfigurationSection; 6 | import org.bukkit.configuration.file.FileConfiguration; 7 | import org.bukkit.configuration.file.YamlConfiguration; 8 | import xyz.oribuin.eternaltags.manager.TagsManager; 9 | import xyz.oribuin.eternaltags.obj.Tag; 10 | 11 | import java.io.File; 12 | import java.util.HashMap; 13 | import java.util.Map; 14 | 15 | public class AlonsoConversion extends ConversionPlugin { 16 | 17 | @Override 18 | public Map getPluginTags(RosePlugin plugin) { 19 | TagsManager manager = plugin.getManager(TagsManager.class); 20 | Map convertedTags = new HashMap<>(); 21 | 22 | FileConfiguration config = YamlConfiguration.loadConfiguration(this.getTagsFile()); 23 | ConfigurationSection section = config.getConfigurationSection("Tags"); 24 | 25 | if (section == null) 26 | return convertedTags; 27 | 28 | section.getKeys(false) 29 | .stream() 30 | .filter(s -> !manager.checkTagExists(s)) 31 | .forEach(key -> { 32 | Tag tag = new Tag(key, section.getString("Displayname"), section.getString(key + ".Tag")); 33 | 34 | if (section.get(key + ".Lore.Unlocked") != null) 35 | tag.setDescription(section.getStringList(key + ".Lore.Unlocked")); 36 | 37 | if (section.get(key + ".Permission") != null) 38 | tag.setPermission(section.getString(key + ".Permission")); 39 | 40 | if (section.get(key + ".Material") != null) { 41 | String icon = section.getString(key + ".Material"); 42 | assert icon != null; 43 | if (icon.equalsIgnoreCase("CUSTOM_HEAD")) 44 | icon = "PLAYER_HEAD"; 45 | 46 | 47 | tag.setIcon(Material.matchMaterial(icon)); 48 | } 49 | 50 | convertedTags.put(key, tag); 51 | }); 52 | 53 | return convertedTags; 54 | } 55 | 56 | @Override 57 | public String getPluginName() { 58 | return "AlonsoTags"; 59 | } 60 | 61 | @Override 62 | public File getTagsFile() { 63 | return new File(this.getPluginsFolder(), "tags.yml"); 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/xyz/oribuin/eternaltags/conversion/CIFYConversion.java: -------------------------------------------------------------------------------- 1 | package xyz.oribuin.eternaltags.conversion; 2 | 3 | import dev.rosewood.rosegarden.RosePlugin; 4 | import org.apache.commons.lang3.StringUtils; 5 | import org.bukkit.configuration.ConfigurationSection; 6 | import org.bukkit.configuration.file.FileConfiguration; 7 | import org.bukkit.configuration.file.YamlConfiguration; 8 | import xyz.oribuin.eternaltags.manager.TagsManager; 9 | import xyz.oribuin.eternaltags.obj.Tag; 10 | 11 | import java.io.File; 12 | import java.util.Collections; 13 | import java.util.HashMap; 14 | import java.util.Map; 15 | 16 | public class CIFYConversion extends ConversionPlugin { 17 | 18 | @Override 19 | public Map getPluginTags(RosePlugin plugin) { 20 | TagsManager manager = plugin.getManager(TagsManager.class); 21 | 22 | Map convertedTags = new HashMap<>(); 23 | FileConfiguration config = YamlConfiguration.loadConfiguration(this.getTagsFile()); 24 | ConfigurationSection section = config.getConfigurationSection("tags"); 25 | if (section == null) 26 | return convertedTags; 27 | 28 | section.getKeys(false) 29 | .stream() 30 | .filter(s -> !manager.checkTagExists(s)) 31 | .forEach(key -> { 32 | Tag tag = new Tag(key, StringUtils.capitalize(key), section.getString(key + ".prefix", "")); 33 | 34 | if (section.get(key + ".description") != null) 35 | tag.setDescription(Collections.singletonList(section.getString(key + ".description"))); 36 | 37 | if (section.getBoolean(key + ".permission")) 38 | tag.setPermission("cifytags.use." + key.toLowerCase()); 39 | 40 | convertedTags.put(key, tag); 41 | }); 42 | 43 | return convertedTags; 44 | } 45 | 46 | @Override 47 | public String getPluginName() { 48 | return "CIFYTags"; 49 | } 50 | 51 | @Override 52 | public File getTagsFile() { 53 | return new File(this.getPluginsFolder(), "config.yml"); 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/xyz/oribuin/eternaltags/conversion/ConversionPlugin.java: -------------------------------------------------------------------------------- 1 | package xyz.oribuin.eternaltags.conversion; 2 | 3 | import dev.rosewood.rosegarden.RosePlugin; 4 | import org.bukkit.Bukkit; 5 | import xyz.oribuin.eternaltags.obj.Tag; 6 | 7 | import java.io.File; 8 | import java.util.Map; 9 | 10 | public abstract class ConversionPlugin { 11 | 12 | /** 13 | * All the tags found in the file. 14 | * 15 | * @return A map of converted tags. 16 | */ 17 | public abstract Map getPluginTags(RosePlugin plugin); 18 | 19 | /** 20 | * The name of the plugin being converted 21 | * 22 | * @return The plugin name. 23 | */ 24 | public abstract String getPluginName(); 25 | 26 | /** 27 | * Get the file where all the tags are stored. 28 | * 29 | * @return The file. 30 | */ 31 | public abstract File getTagsFile(); 32 | 33 | /** 34 | * Get the main 'plugins' folder. 35 | * 36 | * @return The folder. 37 | */ 38 | public File getPluginsFolder() { 39 | return new File(Bukkit.getServer().getUpdateFolderFile().getParentFile(), this.getPluginName()); 40 | } 41 | 42 | 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/xyz/oribuin/eternaltags/conversion/DeluxeConversion.java: -------------------------------------------------------------------------------- 1 | package xyz.oribuin.eternaltags.conversion; 2 | 3 | import dev.rosewood.rosegarden.RosePlugin; 4 | import org.apache.commons.lang3.StringUtils; 5 | import org.bukkit.configuration.ConfigurationSection; 6 | import org.bukkit.configuration.file.FileConfiguration; 7 | import org.bukkit.configuration.file.YamlConfiguration; 8 | import xyz.oribuin.eternaltags.manager.TagsManager; 9 | import xyz.oribuin.eternaltags.obj.Tag; 10 | 11 | import java.io.File; 12 | import java.util.Collections; 13 | import java.util.HashMap; 14 | import java.util.Map; 15 | 16 | public class DeluxeConversion extends ConversionPlugin { 17 | 18 | @Override 19 | public Map getPluginTags(RosePlugin plugin) { 20 | TagsManager manager = plugin.getManager(TagsManager.class); 21 | 22 | Map convertedTags = new HashMap<>(); 23 | FileConfiguration config = YamlConfiguration.loadConfiguration(this.getTagsFile()); 24 | ConfigurationSection section = config.getConfigurationSection("deluxetags"); 25 | if (section == null) 26 | return convertedTags; 27 | 28 | section.getKeys(false) 29 | .stream() 30 | .filter(s -> !manager.checkTagExists(s)) 31 | .forEach(key -> { 32 | Tag tag = new Tag(key, StringUtils.capitalize(key), section.getString(key + ".tag", "")); 33 | 34 | if (section.get(key + ".description") != null) 35 | tag.setDescription(Collections.singletonList(section.getString(key + ".description"))); 36 | 37 | if (section.getString(key + ".permission") != null) 38 | tag.setPermission(section.getString(key + ".permission", "")); 39 | 40 | if (section.get(key + ".order") != null) 41 | tag.setOrder(section.getInt(key + ".order")); 42 | 43 | convertedTags.put(key, tag); 44 | }); 45 | 46 | return convertedTags; 47 | } 48 | 49 | @Override 50 | public String getPluginName() { 51 | return "DeluxeTags"; 52 | } 53 | 54 | @Override 55 | public File getTagsFile() { 56 | return new File(this.getPluginsFolder(), "config.yml"); 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/xyz/oribuin/eternaltags/conversion/ValidPlugin.java: -------------------------------------------------------------------------------- 1 | package xyz.oribuin.eternaltags.conversion; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | import java.util.Optional; 6 | 7 | public final class ValidPlugin { 8 | 9 | public static final Map PLUGINS = new HashMap<>(); 10 | 11 | static { 12 | register("AlonsoTags", new AlonsoConversion()); 13 | register("CIFYTags", new CIFYConversion()); 14 | register("DeluxeTags", new DeluxeConversion()); 15 | } 16 | 17 | /** 18 | * Register a plugin to be converted. 19 | * 20 | * @param name The name of the plugin. 21 | * @param plugin The plugin instance. 22 | */ 23 | public static void register(String name, ConversionPlugin plugin) { 24 | PLUGINS.put(name.toLowerCase(), plugin); 25 | } 26 | 27 | /** 28 | * Match a plugin name to a plugin converter 29 | * 30 | * @param name The name of the plugin. 31 | * @return The plugin converter. 32 | */ 33 | public static Optional match(String name) { 34 | return Optional.ofNullable(PLUGINS.get(name.toLowerCase())); 35 | } 36 | 37 | 38 | } -------------------------------------------------------------------------------- /src/main/java/xyz/oribuin/eternaltags/database/migration/_1_CreateInitialTables.java: -------------------------------------------------------------------------------- 1 | package xyz.oribuin.eternaltags.database.migration; 2 | 3 | import dev.rosewood.rosegarden.database.DataMigration; 4 | import dev.rosewood.rosegarden.database.DatabaseConnector; 5 | 6 | import java.sql.Connection; 7 | import java.sql.SQLException; 8 | import java.sql.Statement; 9 | 10 | public class _1_CreateInitialTables extends DataMigration { 11 | 12 | public _1_CreateInitialTables() { 13 | super(1); 14 | } 15 | 16 | @Override 17 | public void migrate(DatabaseConnector connector, Connection connection, String tablePrefix) throws SQLException { 18 | try (Statement statement = connection.createStatement()) { 19 | statement.executeUpdate("CREATE TABLE IF NOT EXISTS " + tablePrefix + "tags (" + 20 | "player VARCHAR(36), " + 21 | "tagID TEXT, " + 22 | "PRIMARY KEY(player))"); 23 | } 24 | 25 | try (Statement statement = connection.createStatement()) { 26 | statement.executeUpdate("CREATE TABLE IF NOT EXISTS " + tablePrefix + "favourites (" + 27 | "player VARCHAR(36), " + 28 | "tagID TEXT)"); 29 | } 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/xyz/oribuin/eternaltags/database/migration/_2_CreateNewTagTables.java: -------------------------------------------------------------------------------- 1 | package xyz.oribuin.eternaltags.database.migration; 2 | 3 | import dev.rosewood.rosegarden.database.DataMigration; 4 | import dev.rosewood.rosegarden.database.DatabaseConnector; 5 | 6 | import java.sql.Connection; 7 | import java.sql.SQLException; 8 | import java.sql.Statement; 9 | 10 | public class _2_CreateNewTagTables extends DataMigration { 11 | 12 | public _2_CreateNewTagTables() { 13 | super(2); 14 | } 15 | 16 | @Override 17 | public void migrate(DatabaseConnector connector, Connection connection, String tablePrefix) throws SQLException { 18 | try (Statement statement = connection.createStatement()) { 19 | statement.executeUpdate("CREATE TABLE IF NOT EXISTS " + tablePrefix + "tag_data (" + 20 | "tagId VARCHAR(256), " + 21 | "`name` TEXT, " + 22 | "tag TEXT, " + 23 | "permission TEXT, " + 24 | "description TEXT, " + 25 | "`order` INTEGER DEFAULT 0, " + 26 | "`icon` TEXT NULL, " + 27 | "PRIMARY KEY(tagId))"); 28 | } 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/xyz/oribuin/eternaltags/database/migration/_3_ModifyTagDataItems.java: -------------------------------------------------------------------------------- 1 | package xyz.oribuin.eternaltags.database.migration; 2 | 3 | import dev.rosewood.rosegarden.database.DataMigration; 4 | import dev.rosewood.rosegarden.database.DatabaseConnector; 5 | import dev.rosewood.rosegarden.database.SQLiteConnector; 6 | 7 | import java.sql.Connection; 8 | import java.sql.SQLException; 9 | import java.sql.Statement; 10 | 11 | public class _3_ModifyTagDataItems extends DataMigration { 12 | 13 | public _3_ModifyTagDataItems() { 14 | super(3); 15 | } 16 | 17 | @Override 18 | public void migrate(DatabaseConnector connector, Connection connection, String tablePrefix) throws SQLException { 19 | 20 | try (Statement statement = connection.createStatement()) { 21 | if (connector instanceof SQLiteConnector) { 22 | 23 | // thank you chatgpt for this whole section 24 | 25 | // Rename old table to a temporary table 26 | statement.addBatch("ALTER TABLE " + tablePrefix + "tag_data RENAME TO " + tablePrefix + "tag_data_old"); 27 | 28 | // Create new table with modified columns 29 | statement.addBatch("CREATE TABLE " + tablePrefix + "tag_data (" 30 | + "`tagId` VARCHAR(256) NOT NULL," 31 | + "`name` TEXT NOT NULL," 32 | + "`tag` TEXT NOT NULL," 33 | + "`permission` TEXT," 34 | + "`description` TEXT NOT NULL," 35 | + "`order` INTEGER NOT NULL," 36 | + "`icon` VARBINARY(2456)," 37 | + "`category` TEXT, " 38 | + "PRIMARY KEY (tagId))"); 39 | 40 | // Copy data from old table to new table 41 | statement.addBatch("INSERT INTO " + tablePrefix + "tag_data (tagId, `name`, `tag`, `permission`, `description`, `order`) " 42 | + "SELECT `tagId`, `name`, `tag`, `permission`, `description`, `order` FROM " + tablePrefix + "tag_data_old " 43 | + "WHERE EXISTS (SELECT 1 FROM " + tablePrefix + "tag_data_old)"); 44 | 45 | } else { 46 | // Modify columns to the desired types and nullability 47 | // Add category text column 48 | statement.addBatch("ALTER TABLE " + tablePrefix + "tag_data ADD COLUMN `category` TEXT NULL"); 49 | statement.addBatch("ALTER TABLE " + tablePrefix + "tag_data MODIFY COLUMN `permission` TEXT NULL"); 50 | statement.addBatch("UPDATE " + tablePrefix + "tag_data SET `icon` = NULL"); 51 | statement.addBatch("ALTER TABLE " + tablePrefix + "tag_data MODIFY COLUMN `icon` VARBINARY(2456) NULL"); 52 | } 53 | 54 | // Execute all batched statements 55 | statement.executeBatch(); 56 | } 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/xyz/oribuin/eternaltags/database/migration/_4_DeleteOldData.java: -------------------------------------------------------------------------------- 1 | package xyz.oribuin.eternaltags.database.migration; 2 | 3 | import dev.rosewood.rosegarden.database.DataMigration; 4 | import dev.rosewood.rosegarden.database.DatabaseConnector; 5 | 6 | import java.sql.Connection; 7 | import java.sql.SQLException; 8 | import java.sql.Statement; 9 | 10 | public class _4_DeleteOldData extends DataMigration { 11 | 12 | public _4_DeleteOldData() { 13 | super(4); 14 | } 15 | 16 | @Override 17 | public void migrate(DatabaseConnector connector, Connection connection, String tablePrefix) throws SQLException { 18 | try (Statement statement = connection.createStatement()) { 19 | statement.addBatch("DROP TABLE IF EXISTS " + tablePrefix + "tag_data_old"); 20 | statement.executeBatch(); 21 | } 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/xyz/oribuin/eternaltags/gui/MenuItem.java: -------------------------------------------------------------------------------- 1 | package xyz.oribuin.eternaltags.gui; 2 | 3 | import dev.rosewood.rosegarden.config.CommentedConfigurationSection; 4 | import dev.rosewood.rosegarden.config.CommentedFileConfiguration; 5 | import dev.rosewood.rosegarden.utils.StringPlaceholders; 6 | import dev.triumphteam.gui.guis.BaseGui; 7 | import dev.triumphteam.gui.guis.GuiItem; 8 | import org.bukkit.Sound; 9 | import org.bukkit.entity.Player; 10 | import org.bukkit.event.inventory.ClickType; 11 | import org.bukkit.event.inventory.InventoryClickEvent; 12 | import org.bukkit.inventory.ItemStack; 13 | import xyz.oribuin.eternaltags.EternalTags; 14 | import xyz.oribuin.eternaltags.action.Action; 15 | import xyz.oribuin.eternaltags.action.PluginAction; 16 | import xyz.oribuin.eternaltags.util.TagsUtils; 17 | 18 | import java.util.ArrayList; 19 | import java.util.HashMap; 20 | import java.util.List; 21 | import java.util.Map; 22 | import java.util.Objects; 23 | import java.util.function.BiConsumer; 24 | import java.util.function.Consumer; 25 | import java.util.function.Predicate; 26 | 27 | public class MenuItem { 28 | 29 | private final Map> customActions; // The actions to be performed when the item is clicked 30 | private CommentedFileConfiguration config; // The config file for the menu 31 | private ItemStack customItem; // The item to be displayed 32 | private String itemPath; // The path to the item in the config 33 | private StringPlaceholders placeholders; // The custom placeholders for the item 34 | private Player player; // The player who is viewing the menu, this is used for placeholders 35 | private Sound clickSound; // The sound to be played when the item is clicked 36 | private BiConsumer action; // The action to be performed when the item is clicked 37 | private List slots; // The slots the item should be placed in 38 | private Predicate condition; // The condition for the item to be displayed 39 | 40 | public MenuItem() { 41 | throw new UnsupportedOperationException("This class cannot be instantiated, Use MenuItem.create() instead."); 42 | } 43 | 44 | private MenuItem(CommentedFileConfiguration config) { 45 | this.config = config; 46 | this.customItem = null; 47 | this.itemPath = null; 48 | this.placeholders = StringPlaceholders.empty(); 49 | this.player = null; 50 | this.clickSound = null; 51 | this.action = (menuItem, inventoryClickEvent) -> { 52 | // do nothing 53 | }; 54 | this.slots = new ArrayList<>(); 55 | this.condition = menuItem -> true; 56 | this.customActions = new HashMap<>(); 57 | } 58 | 59 | /** 60 | * Create a new MenuItem instance 61 | * 62 | * @param config The config file for the menu 63 | * @return A new MenuItem instance 64 | */ 65 | public static MenuItem create(CommentedFileConfiguration config) { 66 | return new MenuItem(config); 67 | } 68 | 69 | public final void place(BaseGui gui) { 70 | 71 | // Make sure there is a config file 72 | if (this.config == null) { 73 | throw new IllegalArgumentException("Config cannot be null"); 74 | } 75 | 76 | // Make sure the item is not null 77 | if (this.customItem == null && this.itemPath == null) { 78 | throw new IllegalArgumentException("Item path and custom item cannot both be null"); 79 | } 80 | 81 | // Check if the item path is null 82 | if (this.config.get(this.itemPath) == null && this.customItem == null) { 83 | // throw new IllegalArgumentException(this.itemPath + ": Item path in config is null and custom item is null"); 84 | return; 85 | } 86 | 87 | // Check if the item is enabled 88 | if (!this.config.getBoolean(this.itemPath + ".enabled", true)) { 89 | return; 90 | } 91 | 92 | if (!this.isConditional()) 93 | return; 94 | 95 | if (this.clickSound == null && this.customItem == null) { 96 | this.clickSound = TagsUtils.getEnum(Sound.class, this.config.getString(this.itemPath + ".sound", "")); 97 | } 98 | 99 | // Add any slots that were not added 100 | if (this.slots.isEmpty()) { 101 | // We check for the singular slot first 102 | int slot = (int) this.config.get(this.itemPath + ".slot", -1); 103 | if (slot != -1) { 104 | this.slot(slot); 105 | } 106 | 107 | // Then we check for the multiple slots 108 | boolean hasMultiSlots = this.config.get(this.itemPath + ".slots") != null; 109 | if (hasMultiSlots) { 110 | this.slots(TagsUtils.parseList(this.config.getStringList(this.itemPath + ".slots"))); 111 | } 112 | } 113 | 114 | ItemStack item = this.customItem != null 115 | ? this.customItem 116 | : TagsUtils.deserialize(this.config, this.player, this.itemPath, this.placeholders); 117 | 118 | if (item == null) { 119 | EternalTags.getInstance().getLogger().warning("Item [" + this.itemPath + "] in the [" + this.config.getName() + "] menu is invalid."); 120 | return; 121 | } 122 | 123 | this.addActions(); 124 | this.slots.forEach(slot -> gui.setItem(slot, new GuiItem(item, event -> this.action.accept(this, event)))); 125 | gui.update(); 126 | } 127 | 128 | /** 129 | * Add all the custom actions to the item 130 | * 131 | * @since 1.1.7 132 | */ 133 | private void addActions() { 134 | CommentedConfigurationSection customActions = this.config.getConfigurationSection(this.itemPath + ".commands"); 135 | if (customActions == null) 136 | return; 137 | 138 | for (String key : customActions.getKeys(false)) { 139 | ClickType clickType = TagsUtils.getEnum(ClickType.class, key.toUpperCase()); 140 | if (clickType == null) { 141 | EternalTags.getInstance().getLogger().warning("Invalid click type [" + key + "] in the " + this.itemPath + ".commands section of the [" + this.config.getName() + "] menu."); 142 | continue; 143 | } 144 | 145 | List actionList = new ArrayList<>(); 146 | this.config.getStringList(this.itemPath + ".commands." + key) 147 | .stream() 148 | .map(PluginAction::parse) 149 | .filter(Objects::nonNull) 150 | .forEach(actionList::add); 151 | 152 | this.customActions.put(clickType, actionList); 153 | } 154 | 155 | if (this.customActions.isEmpty()) 156 | return; 157 | 158 | this.action = (menuItem, event) -> { 159 | List actions = this.customActions.get(event.getClick()); 160 | if (actions == null) 161 | return; 162 | 163 | actions.forEach(action -> action.execute((Player) event.getWhoClicked(), this.placeholders)); 164 | }; 165 | } 166 | 167 | public void sound(Player player) { 168 | if (this.clickSound != null) { 169 | player.playSound(player.getLocation(), this.clickSound, 75, 1); 170 | } 171 | } 172 | 173 | /** 174 | * Get the path to the item in the config 175 | * 176 | * @return The path 177 | */ 178 | public String getItemPath() { 179 | return itemPath; 180 | } 181 | 182 | /** 183 | * Set the path to the item in the config 184 | * 185 | * @param path The path 186 | * @return The MenuItem 187 | */ 188 | public final MenuItem path(String path) { 189 | this.itemPath = path; 190 | return this; 191 | } 192 | 193 | public ItemStack getCustomItem() { 194 | return customItem; 195 | } 196 | 197 | public final MenuItem item(ItemStack item) { 198 | this.customItem = item; 199 | return this; 200 | } 201 | 202 | public Player getPlayer() { 203 | return player; 204 | } 205 | 206 | public final MenuItem player(Player player) { 207 | this.player = player; 208 | return this; 209 | } 210 | 211 | public StringPlaceholders getPlaceholders() { 212 | return placeholders; 213 | } 214 | 215 | public final MenuItem placeholders(StringPlaceholders placeholders) { 216 | this.placeholders = placeholders; 217 | return this; 218 | } 219 | 220 | public BiConsumer getAction() { 221 | return action; 222 | } 223 | 224 | public final MenuItem action(Consumer action) { 225 | this.action = (item, event) -> action.accept(event); 226 | return this; 227 | } 228 | 229 | public final MenuItem action(BiConsumer action) { 230 | this.action = action; 231 | return this; 232 | } 233 | 234 | public List getSlots() { 235 | return slots; 236 | } 237 | 238 | public final MenuItem slots(List slots) { 239 | this.slots = slots; 240 | return this; 241 | } 242 | 243 | public final MenuItem slot(int slot) { 244 | this.slots = List.of(slot); 245 | return this; 246 | } 247 | 248 | public CommentedFileConfiguration getConfig() { 249 | return config; 250 | } 251 | 252 | public final MenuItem config(CommentedFileConfiguration config) { 253 | this.config = config; 254 | return this; 255 | } 256 | 257 | public boolean isConditional() { 258 | return condition.test(this); 259 | } 260 | 261 | public final MenuItem condition(Predicate condition) { 262 | this.condition = condition; 263 | return this; 264 | } 265 | 266 | public Map> getCustomActions() { 267 | return customActions; 268 | } 269 | 270 | } 271 | -------------------------------------------------------------------------------- /src/main/java/xyz/oribuin/eternaltags/gui/MenuProvider.java: -------------------------------------------------------------------------------- 1 | package xyz.oribuin.eternaltags.gui; 2 | 3 | import xyz.oribuin.eternaltags.gui.menu.CategoryGUI; 4 | import xyz.oribuin.eternaltags.gui.menu.FavouritesGUI; 5 | import xyz.oribuin.eternaltags.gui.menu.TagsGUI; 6 | 7 | import java.util.HashMap; 8 | import java.util.Map; 9 | 10 | public enum MenuProvider { 11 | ; 12 | 13 | private final static Map, PluginMenu> menuCache = new HashMap<>(); 14 | 15 | static { 16 | menuCache.put(TagsGUI.class, new TagsGUI()); 17 | menuCache.put(FavouritesGUI.class, new FavouritesGUI()); 18 | menuCache.put(CategoryGUI.class, new CategoryGUI()); 19 | 20 | menuCache.forEach((aClass, pluginMenu) -> pluginMenu.load()); 21 | } 22 | 23 | public static void reload() { 24 | menuCache.forEach((aClass, pluginMenu) -> pluginMenu.load()); 25 | } 26 | 27 | /** 28 | * Get the instance of the menu. 29 | * 30 | * @param the type of the menu. 31 | * @return the instance of the menu. 32 | */ 33 | @SuppressWarnings("unchecked") 34 | public static T get(Class menuClass) { 35 | if (menuCache.containsKey(menuClass)) { 36 | return (T) menuCache.get(menuClass); 37 | } 38 | 39 | try { 40 | T menu = menuClass.getDeclaredConstructor().newInstance(); 41 | menu.load(); 42 | menuCache.put(menuClass, menu); 43 | return menu; 44 | } catch (Exception e) { 45 | throw new RuntimeException("Failed to create instance of " + menuClass.getName(), e); 46 | } 47 | } 48 | 49 | } -------------------------------------------------------------------------------- /src/main/java/xyz/oribuin/eternaltags/gui/enums/SortType.java: -------------------------------------------------------------------------------- 1 | package xyz.oribuin.eternaltags.gui.enums; 2 | 3 | import xyz.oribuin.eternaltags.obj.Category; 4 | import xyz.oribuin.eternaltags.obj.Tag; 5 | 6 | import java.util.Collections; 7 | import java.util.Comparator; 8 | import java.util.List; 9 | import java.util.Objects; 10 | 11 | public enum SortType { 12 | ALPHABETICAL, 13 | CUSTOM, 14 | NONE, 15 | RANDOM; 16 | 17 | public void sort(List tags) { 18 | tags.removeIf(Objects::isNull); // Remove null tags. 19 | 20 | switch (this) { 21 | case ALPHABETICAL -> tags.sort(Comparator.comparing(Tag::getName)); 22 | case CUSTOM -> tags.sort(Comparator.comparingInt(Tag::getOrder)); 23 | case RANDOM -> Collections.shuffle(tags); 24 | } 25 | } 26 | 27 | public void sortCategories(List categories) { 28 | categories.removeIf(Objects::isNull); // Remove null categories. 29 | 30 | switch (this) { 31 | case ALPHABETICAL -> categories.sort(Comparator.comparing(Category::getDisplayName)); 32 | case CUSTOM -> categories.sort(Comparator.comparingInt(Category::getOrder)); 33 | case RANDOM -> Collections.shuffle(categories); 34 | } 35 | 36 | } 37 | 38 | } -------------------------------------------------------------------------------- /src/main/java/xyz/oribuin/eternaltags/gui/menu/CategoryGUI.java: -------------------------------------------------------------------------------- 1 | package xyz.oribuin.eternaltags.gui.menu; 2 | 3 | import dev.rosewood.rosegarden.config.CommentedConfigurationSection; 4 | import dev.rosewood.rosegarden.utils.StringPlaceholders; 5 | import dev.triumphteam.gui.components.GuiAction; 6 | import dev.triumphteam.gui.components.ScrollType; 7 | import dev.triumphteam.gui.guis.BaseGui; 8 | import dev.triumphteam.gui.guis.GuiItem; 9 | import dev.triumphteam.gui.guis.PaginatedGui; 10 | import org.bukkit.Material; 11 | import org.bukkit.entity.Player; 12 | import org.bukkit.event.inventory.InventoryClickEvent; 13 | import org.bukkit.inventory.ItemStack; 14 | import org.jetbrains.annotations.NotNull; 15 | import xyz.oribuin.eternaltags.EternalTags; 16 | import xyz.oribuin.eternaltags.config.Setting; 17 | import xyz.oribuin.eternaltags.gui.MenuItem; 18 | import xyz.oribuin.eternaltags.gui.MenuProvider; 19 | import xyz.oribuin.eternaltags.gui.PluginMenu; 20 | import xyz.oribuin.eternaltags.gui.enums.SortType; 21 | import xyz.oribuin.eternaltags.manager.CategoryManager; 22 | import xyz.oribuin.eternaltags.manager.TagsManager; 23 | import xyz.oribuin.eternaltags.obj.Category; 24 | import xyz.oribuin.eternaltags.obj.CategoryType; 25 | import xyz.oribuin.eternaltags.util.ItemBuilder; 26 | import xyz.oribuin.eternaltags.util.TagsUtils; 27 | 28 | import java.util.ArrayList; 29 | import java.util.LinkedHashMap; 30 | import java.util.List; 31 | import java.util.Map; 32 | 33 | public class CategoryGUI extends PluginMenu { 34 | 35 | private final TagsManager manager = this.rosePlugin.getManager(TagsManager.class); 36 | private final CategoryManager categoryManager = this.rosePlugin.getManager(CategoryManager.class); 37 | private final Map categoryIcons = new LinkedHashMap<>(); // Cache the tag items, so we don't have to create them every time. 38 | 39 | public CategoryGUI() { 40 | super(EternalTags.getInstance()); 41 | } 42 | 43 | @Override 44 | public void load() { 45 | super.load(); 46 | 47 | this.categoryIcons.clear(); 48 | } 49 | 50 | public void open(@NotNull Player player) { 51 | // Check if categories are enabled. 52 | if (!categoryManager.isEnabled()) { 53 | MenuProvider.get(TagsGUI.class).open(player); 54 | return; 55 | } 56 | 57 | String menuTitle = this.config.getString("gui-settings.title"); 58 | if (menuTitle == null) 59 | menuTitle = "Category Menu"; 60 | 61 | String finalMenuTitle = menuTitle; 62 | 63 | boolean scrollingGui = this.config.getBoolean("gui-settings.scrolling-gui", false); 64 | ScrollType scrollingType = TagsUtils.getEnum( 65 | ScrollType.class, 66 | this.config.getString("gui-settings.scrolling-type"), 67 | ScrollType.VERTICAL 68 | ); 69 | 70 | PaginatedGui gui = (scrollingGui && scrollingType != null) ? this.createScrollingGui(player, scrollingType) : this.createPagedGUI(player); 71 | 72 | CommentedConfigurationSection extraItems = this.config.getConfigurationSection("extra-items"); 73 | if (extraItems != null) { 74 | for (String key : extraItems.getKeys(false)) { 75 | MenuItem.create(this.config) 76 | .path("extra-items." + key) 77 | .player(player) 78 | .place(gui); 79 | } 80 | } 81 | 82 | MenuItem.create(this.config) 83 | .path("next-page") 84 | .player(player) 85 | .action(event -> { 86 | gui.next(); 87 | this.sync(() -> gui.updateTitle(this.formatString(player, finalMenuTitle, this.getPagePlaceholders(gui)))); 88 | }) 89 | .player(player) 90 | .place(gui); 91 | 92 | MenuItem.create(this.config) 93 | .path("previous-page") 94 | .player(player) 95 | .action(event -> { 96 | gui.previous(); 97 | this.sync(() -> gui.updateTitle(this.formatString(player, finalMenuTitle, this.getPagePlaceholders(gui)))); 98 | }) 99 | .place(gui); 100 | 101 | MenuItem.create(this.config) 102 | .path("clear-tag") 103 | .player(player) 104 | .action(event -> this.clearTag(player)) 105 | .place(gui); 106 | 107 | MenuItem.create(this.config) 108 | .path("favorite-tags") 109 | .player(player) 110 | .action(event -> MenuProvider.get(FavouritesGUI.class).open(player)) 111 | .place(gui); 112 | 113 | MenuItem.create(this.config) 114 | .path("search") 115 | .player(player) 116 | .action(event -> this.searchTags(player, gui)) 117 | .place(gui); 118 | 119 | MenuItem.create(this.config) 120 | .path("main-menu") 121 | .player(player) 122 | .action(event -> MenuProvider.get(TagsGUI.class).open(player, null)) 123 | .place(gui); 124 | 125 | gui.open(player); 126 | 127 | Runnable task = () -> { 128 | this.addCategories(gui, player); 129 | 130 | if (this.reloadTitle()) 131 | this.sync(() -> gui.updateTitle(this.formatString(player, finalMenuTitle, this.getPagePlaceholders(gui)))); 132 | }; 133 | 134 | if (this.addPagesAsynchronously()) this.async(task); 135 | else task.run(); 136 | 137 | } 138 | 139 | /** 140 | * Add the categories to the gui. 141 | * 142 | * @param gui The gui to add the categories to. 143 | * @param player The player to add the categories for. 144 | */ 145 | public void addCategories(@NotNull BaseGui gui, @NotNull Player player) { 146 | if (gui instanceof PaginatedGui paginated) 147 | paginated.clearPageItems(); 148 | 149 | TagsGUI tagsGUI = MenuProvider.get(TagsGUI.class); 150 | if (tagsGUI == null) // This should never happen, but just in case. 151 | return; 152 | 153 | this.getCategories(player).forEach(category -> { 154 | 155 | GuiAction action = event -> { 156 | // Filter out tags that are not in the category. 157 | if (category.getType() == CategoryType.GLOBAL) { 158 | tagsGUI.open(player); 159 | return; 160 | } 161 | 162 | tagsGUI.open(player, tag -> tag.getCategory() != null && tag.getCategory().equalsIgnoreCase(category.getId())); 163 | }; 164 | 165 | if (Setting.CACHE_GUI_CATEGORIES.get() && this.categoryIcons.containsKey(category)) { 166 | GuiItem item = this.categoryIcons.get(category); 167 | item.setAction(action); 168 | 169 | gui.addItem(item); 170 | return; 171 | } 172 | 173 | StringPlaceholders.Builder placeholders = StringPlaceholders.builder() 174 | .add("category", category.getDisplayName()) 175 | .add("total", this.manager.getTagsInCategory(category).size()); 176 | 177 | if (this.config.getBoolean("gui-settings.only-unlocked-categories")) 178 | placeholders.add("unlocked", this.manager.getCategoryTags(category, player).size()); 179 | 180 | ItemStack item = TagsUtils.deserialize(this.config, player, "categories." + category.getId() + ".display-item", placeholders.build()); 181 | if (item == null) { 182 | // this.rosePlugin.getLogger().info("Failed to load category " + category.getId() + " for the gui, the display item is invalid, Using default value."); 183 | 184 | item = new ItemBuilder(Material.OAK_SIGN) 185 | .name(formatString(player, "#00B4DB" + category.getDisplayName())) 186 | .build(); 187 | } 188 | 189 | GuiItem guiItem = new GuiItem(item, action); 190 | gui.addItem(guiItem); 191 | 192 | if (Setting.CACHE_GUI_CATEGORIES.get()) 193 | this.categoryIcons.put(category, guiItem); 194 | }); 195 | 196 | } 197 | 198 | /** 199 | * Get a list of categories 200 | * 201 | * @param player The player to get the categories for 202 | * @return A list of categories 203 | */ 204 | public List getCategories(@NotNull Player player) { 205 | List categories = new ArrayList<>(this.categoryManager.getCategories()); 206 | SortType sortType = TagsUtils.getEnum( 207 | SortType.class, 208 | this.config.getString("gui-settings.sort-type"), 209 | SortType.ALPHABETICAL 210 | ); 211 | 212 | if (this.config.getBoolean("gui-settings.use-category-permissions", false)) { 213 | categories.removeIf(category -> !category.canUse(player)); 214 | } 215 | 216 | if (this.config.getBoolean("gui-settings.only-unlocked-categories", false)) { 217 | categories.removeIf(category -> { 218 | if (category.getType() == CategoryType.GLOBAL) return false; 219 | 220 | return this.manager.getCategoryTags(category, player).isEmpty(); 221 | }); 222 | } 223 | 224 | // TODO: 2023-05-07 Fix this code 225 | // categories.removeIf(category -> !category.isGlobal() || this.config.getBoolean("categories." + category.getId() + ".hidden")); 226 | // categories.removeIf(category -> { 227 | // if (category.isGlobal()) 228 | // return false; 229 | // 230 | // return this.config.getBoolean("categories." + category.getId() + ".hidden"); 231 | // }); 232 | 233 | sortType.sortCategories(categories); 234 | 235 | return categories; 236 | } 237 | 238 | @Override 239 | public String getMenuName() { 240 | return "category-gui"; 241 | } 242 | 243 | } 244 | -------------------------------------------------------------------------------- /src/main/java/xyz/oribuin/eternaltags/hook/Expansion.java: -------------------------------------------------------------------------------- 1 | package xyz.oribuin.eternaltags.hook; 2 | 3 | import dev.rosewood.rosegarden.utils.HexUtils; 4 | import me.clip.placeholderapi.expansion.PlaceholderExpansion; 5 | import org.bukkit.OfflinePlayer; 6 | import org.bukkit.entity.Player; 7 | import org.jetbrains.annotations.NotNull; 8 | import org.jetbrains.annotations.Nullable; 9 | import xyz.oribuin.eternaltags.EternalTags; 10 | import xyz.oribuin.eternaltags.config.Setting; 11 | import xyz.oribuin.eternaltags.manager.TagsManager; 12 | import xyz.oribuin.eternaltags.obj.Tag; 13 | import xyz.oribuin.eternaltags.util.TagsUtils; 14 | 15 | import java.util.List; 16 | 17 | public class Expansion extends PlaceholderExpansion { 18 | 19 | private final EternalTags plugin; 20 | private final TagsManager manager; 21 | private final String formattedPlaceholder; 22 | 23 | public Expansion(final EternalTags plugin) { 24 | this.plugin = plugin; 25 | this.manager = plugin.getManager(TagsManager.class); 26 | this.formattedPlaceholder = Setting.FORMATTED_PLACEHOLDER.get(); 27 | } 28 | 29 | @Override 30 | public @Nullable String onPlaceholderRequest(Player player, @NotNull String params) { 31 | 32 | // Regular placeholders :) 33 | if (params.equalsIgnoreCase("total")) return String.valueOf(this.manager.getCachedTags().size()); 34 | if (params.equalsIgnoreCase("joined")) return this.joinTags(this.manager.getPlayerTags(player)); 35 | if (params.equalsIgnoreCase("unlocked")) return String.valueOf(this.manager.getPlayerTags(player).size()); 36 | if (params.equalsIgnoreCase("favorites")) return String.valueOf(this.manager.getUsersFavourites(player.getUniqueId()).size()); 37 | 38 | // Allow the ability to get any tag from the id 39 | String[] args = params.split("_"); 40 | Tag activeTag = this.manager.getUserTag(player); 41 | 42 | // Add new specific tags here 43 | if (args.length >= 2) { 44 | String tagId = params.substring(args[0].length() + 1); 45 | Tag tag = this.manager.getTagFromId(tagId); // args[1] 46 | 47 | if (tag != null) { 48 | return switch (args[0].toLowerCase()) { 49 | case "get" -> this.manager.getDisplayTag(tag, player, ""); 50 | case "get-formatted" -> this.manager.getDisplayTag(tag, player, this.formattedPlaceholder); 51 | case "has" -> String.valueOf(this.manager.canUseTag(player, tag)); 52 | case "has-unlocked" -> this.manager.canUseTag(player, tag) ? Setting.TAG_UNLOCKED_FORMAT.get() : Setting.TAG_LOCKED_FORMAT.get(); 53 | case "active" -> String.valueOf(activeTag != null && activeTag.getId().equalsIgnoreCase(tag.getId())); 54 | case "description" -> TagsUtils.formatList(tag.getDescription(), Setting.DESCRIPTION_DELIMITER.get()); 55 | default -> null; 56 | }; 57 | } 58 | } 59 | 60 | // Return the result of the placeholder 61 | return this.result(params, player, activeTag); 62 | } 63 | 64 | /** 65 | * Parse all the placeholders and return the result 66 | * 67 | * @param param The placeholder to parse 68 | * @param offlinePlayer The player to parse the placeholder for 69 | * @param tag The tag to parse the placeholder for 70 | * 71 | * @return The result of the placeholder 72 | */ 73 | public String result(String param, OfflinePlayer offlinePlayer, Tag tag) { 74 | if (offlinePlayer == null) return ""; // Require a player for these placeholders 75 | Player player = offlinePlayer.getPlayer(); 76 | 77 | if (player == null) return ""; // Require a player for these placeholders 78 | 79 | // These tags are different and dont always want formattedPlaceholder 80 | if (param.equalsIgnoreCase("active")) return String.valueOf(tag != null); // Return true if the tag is not null 81 | if (param.equalsIgnoreCase("tag")) return this.manager.getDisplayTag(tag, player, ""); // Return the tag with the player's tag 82 | if (param.equalsIgnoreCase("tag_stripped")) return tag != null ? tag.getTag() : ""; // Return nothing when the tag is null 83 | 84 | // Has unlocked is a double special case 85 | if (param.equalsIgnoreCase("has-unlocked")) { 86 | if (tag == null) return Setting.TAG_LOCKED_FORMAT.get(); 87 | 88 | return this.manager.canUseTag(player, tag) ? Setting.TAG_UNLOCKED_FORMAT.get() : Setting.TAG_LOCKED_FORMAT.get(); 89 | } 90 | 91 | if (tag == null) return this.formattedPlaceholder; // Return the formatted placeholder if the tag is null 92 | 93 | // Regular tag placeholders 94 | return switch (param) { 95 | case "tag_formatted" -> this.manager.getDisplayTag(tag, player, this.formattedPlaceholder); 96 | case "tag_stripped_formatted" -> tag.getTag(); 97 | case "tag_name" -> tag.getName(); 98 | case "tag_id" -> tag.getId(); 99 | case "tag_permission" -> tag.getPermission(); 100 | case "tag_description" -> TagsUtils.formatList(tag.getDescription(), Setting.DESCRIPTION_DELIMITER.get()); 101 | case "tag_order" -> String.valueOf(tag.getOrder()); 102 | default -> null; 103 | }; 104 | 105 | } 106 | 107 | 108 | /** 109 | * Join all the tags in a single string 110 | * 111 | * @param tags The tags to join 112 | * 113 | * @return The joined tags 114 | */ 115 | public String joinTags(List tags) { 116 | return tags.stream().map(Tag::getTag).map(HexUtils::colorify).reduce("", (a, b) -> a + b); 117 | } 118 | 119 | @Override 120 | public @NotNull String getIdentifier() { 121 | return "eternaltags"; 122 | } 123 | 124 | @Override 125 | public @NotNull String getAuthor() { 126 | return "Oribuin"; 127 | } 128 | 129 | @Override 130 | public @NotNull String getVersion() { 131 | return this.plugin.getDescription().getVersion(); 132 | } 133 | 134 | @Override 135 | public boolean canRegister() { 136 | return true; 137 | } 138 | 139 | @Override 140 | public boolean persist() { 141 | return true; 142 | } 143 | 144 | } 145 | -------------------------------------------------------------------------------- /src/main/java/xyz/oribuin/eternaltags/hook/VaultHook.java: -------------------------------------------------------------------------------- 1 | package xyz.oribuin.eternaltags.hook; 2 | 3 | import net.milkbowl.vault.permission.Permission; 4 | import org.bukkit.Bukkit; 5 | import org.bukkit.entity.Player; 6 | import org.bukkit.plugin.RegisteredServiceProvider; 7 | 8 | import javax.annotation.Nullable; 9 | 10 | public class VaultHook { 11 | 12 | private static final boolean enabled; 13 | 14 | static { 15 | enabled = Bukkit.getPluginManager().isPluginEnabled("Vault"); 16 | } 17 | 18 | /** 19 | * Get the vault permission instance 20 | * 21 | * @return The permission instance 22 | */ 23 | @Nullable 24 | public static Permission getPermission() { 25 | if (!enabled) 26 | return null; 27 | 28 | RegisteredServiceProvider rsp = Bukkit.getServicesManager().getRegistration(Permission.class); 29 | if (rsp == null) 30 | return null; 31 | 32 | return rsp.getProvider(); 33 | } 34 | 35 | /** 36 | * Get the highest group of a player 37 | * 38 | * @param player The player to get the group of 39 | * @return The highest group of the player 40 | */ 41 | public static String getPrimaryGroup(Player player) { 42 | Permission permission = getPermission(); // Get the permission instance 43 | if (permission == null) 44 | return null; 45 | 46 | return permission.getPrimaryGroup(player); // Get the highest group (This is the group with the highest priority 47 | } 48 | 49 | /** 50 | * @return If vault is enabled or not 51 | */ 52 | public static boolean isEnabled() { 53 | return enabled; 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/xyz/oribuin/eternaltags/listener/BungeeListener.java: -------------------------------------------------------------------------------- 1 | package xyz.oribuin.eternaltags.listener; 2 | 3 | import org.bukkit.Bukkit; 4 | import org.bukkit.entity.Player; 5 | import org.bukkit.plugin.messaging.PluginMessageListener; 6 | import org.jetbrains.annotations.NotNull; 7 | import xyz.oribuin.eternaltags.EternalTags; 8 | import xyz.oribuin.eternaltags.config.Setting; 9 | import xyz.oribuin.eternaltags.manager.TagsManager; 10 | 11 | import java.io.ByteArrayInputStream; 12 | import java.io.ByteArrayOutputStream; 13 | import java.io.DataInputStream; 14 | import java.io.DataOutputStream; 15 | import java.io.IOException; 16 | import java.util.logging.Logger; 17 | 18 | public class BungeeListener implements PluginMessageListener { 19 | 20 | private static final Logger LOGGER = Logger.getLogger("EternalTags/PluginMessenger"); 21 | private final TagsManager manager; 22 | 23 | public BungeeListener(EternalTags plugin) { 24 | this.manager = plugin.getManager(TagsManager.class); 25 | } 26 | 27 | /** 28 | * Force all eternaltags servers to reload the plugin 29 | */ 30 | public static void sendReload() { 31 | if (!Setting.PLUGIN_MESSAGING_RELOAD.get()) return; 32 | 33 | try ( 34 | ByteArrayOutputStream stream = new ByteArrayOutputStream(); 35 | DataOutputStream out = new DataOutputStream(stream) 36 | ) { 37 | out.writeUTF("Forward"); 38 | out.writeUTF("ALL"); 39 | out.writeUTF("eternaltags:reload"); 40 | 41 | Bukkit.getServer().sendPluginMessage(EternalTags.getInstance(), "BungeeCord", stream.toByteArray()); 42 | } catch (IOException ex) { 43 | LOGGER.severe("Failed to send reload message to BungeeCord: " + ex.getMessage()); 44 | } 45 | } 46 | 47 | @Override 48 | public void onPluginMessageReceived(@NotNull String channel, @NotNull Player player, byte[] message) { 49 | if (!channel.equalsIgnoreCase("BungeeCord")) return; 50 | if (!Setting.PLUGIN_MESSAGING_RELOAD.get()) return; 51 | 52 | try ( 53 | ByteArrayInputStream bytes = new ByteArrayInputStream(message); 54 | DataInputStream in = new DataInputStream(bytes)) { 55 | 56 | String commandInfo = in.readUTF(); 57 | String[] commandInfoSplit = commandInfo.split(":"); 58 | String namespace = commandInfoSplit[0]; 59 | String command = commandInfoSplit.length > 1 ? commandInfoSplit[1] : null; 60 | 61 | if (!namespace.equalsIgnoreCase("eternaltags")) return; 62 | if (command == null) return; 63 | 64 | if (command.equalsIgnoreCase("reload")) { 65 | this.manager.getCachedTags().clear(); // Clear the cached tags 66 | this.manager.reload(); // Reload the plugin 67 | } 68 | 69 | } catch (IOException ex) { 70 | LOGGER.severe("Failed to receive message from BungeeCord: " + ex.getMessage()); 71 | } 72 | } 73 | 74 | 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/xyz/oribuin/eternaltags/listener/ChatListener.java: -------------------------------------------------------------------------------- 1 | package xyz.oribuin.eternaltags.listener; 2 | 3 | import me.clip.placeholderapi.PlaceholderAPI; 4 | import org.bukkit.entity.Player; 5 | import org.bukkit.event.EventHandler; 6 | import org.bukkit.event.EventPriority; 7 | import org.bukkit.event.Listener; 8 | import org.bukkit.event.player.AsyncPlayerChatEvent; 9 | 10 | import java.util.regex.Matcher; 11 | 12 | /** 13 | * Adds PlaceholderAPI Support to the chat within the messages sent. 14 | */ 15 | @SuppressWarnings("deprecation") // thank you paper 16 | public class ChatListener implements Listener { 17 | 18 | /** 19 | * Thank you PacksGamingHD for creating the original code. 20 | */ 21 | @EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR) 22 | public void onChat(AsyncPlayerChatEvent event) { 23 | Player player = event.getPlayer(); 24 | String format = event.getFormat(); 25 | Matcher matcher = PlaceholderAPI.getBracketPlaceholderPattern().matcher(format); 26 | if (!matcher.find()) return; // No placeholders found 27 | 28 | format = PlaceholderAPI.setBracketPlaceholders(player, format); 29 | 30 | event.setFormat(format); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/xyz/oribuin/eternaltags/listener/PlayerListeners.java: -------------------------------------------------------------------------------- 1 | package xyz.oribuin.eternaltags.listener; 2 | 3 | import org.bukkit.event.EventHandler; 4 | import org.bukkit.event.EventPriority; 5 | import org.bukkit.event.Listener; 6 | import org.bukkit.event.player.PlayerJoinEvent; 7 | import org.bukkit.event.player.PlayerQuitEvent; 8 | import xyz.oribuin.eternaltags.EternalTags; 9 | import xyz.oribuin.eternaltags.manager.DataManager; 10 | import xyz.oribuin.eternaltags.manager.TagsManager; 11 | 12 | import java.util.concurrent.CompletableFuture; 13 | 14 | public class PlayerListeners implements Listener { 15 | 16 | private final TagsManager manager = EternalTags.getInstance().getManager(TagsManager.class); 17 | private final DataManager dataManager = EternalTags.getInstance().getManager(DataManager.class); 18 | 19 | @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGH) 20 | public void onPlayerJoin(PlayerJoinEvent event) { 21 | // Load the user from the database 22 | this.dataManager.loadUser(event.getPlayer().getUniqueId()).thenRun(() -> { 23 | // Update the player's tag 24 | this.manager.getUserTag(event.getPlayer()); 25 | }); 26 | } 27 | 28 | @EventHandler 29 | public void onQuit(PlayerQuitEvent event) { 30 | this.dataManager.getCachedUsers().remove(event.getPlayer().getUniqueId()); // Remove the user from the cache 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/xyz/oribuin/eternaltags/manager/CategoryManager.java: -------------------------------------------------------------------------------- 1 | package xyz.oribuin.eternaltags.manager; 2 | 3 | import dev.rosewood.rosegarden.RosePlugin; 4 | import dev.rosewood.rosegarden.config.CommentedConfigurationSection; 5 | import dev.rosewood.rosegarden.config.CommentedFileConfiguration; 6 | import dev.rosewood.rosegarden.manager.Manager; 7 | import xyz.oribuin.eternaltags.obj.Category; 8 | import xyz.oribuin.eternaltags.obj.CategoryType; 9 | import xyz.oribuin.eternaltags.util.TagsUtils; 10 | 11 | import java.io.File; 12 | import java.util.HashMap; 13 | import java.util.List; 14 | import java.util.Map; 15 | 16 | public class CategoryManager extends Manager { 17 | 18 | private final Map cachedCategories = new HashMap<>(); 19 | private File categoryFile; 20 | private CommentedFileConfiguration categoryConfig; 21 | 22 | public CategoryManager(RosePlugin rosePlugin) { 23 | super(rosePlugin); 24 | } 25 | 26 | @Override 27 | public void reload() { 28 | this.categoryFile = TagsUtils.createFile(this.rosePlugin, "categories.yml"); 29 | this.categoryConfig = CommentedFileConfiguration.loadConfiguration(this.categoryFile); 30 | this.cachedCategories.clear(); 31 | 32 | CommentedConfigurationSection section = this.categoryConfig.getConfigurationSection("categories"); 33 | if (section == null) return; 34 | 35 | for (String key : section.getKeys(false)) { 36 | this.loadCategory(this.categoryConfig, key); 37 | } 38 | 39 | List global = this.getByType(CategoryType.GLOBAL); 40 | if (global.size() > 1) { 41 | this.rosePlugin.getLogger().severe("You have more than one global category, this is not allowed. Please remove the extra global categories."); 42 | } 43 | 44 | List defaults = this.getByType(CategoryType.DEFAULT); 45 | if (defaults.size() > 1) { 46 | this.rosePlugin.getLogger().severe("You have more than one default category, this is not allowed. Please remove the extra default categories."); 47 | } 48 | } 49 | 50 | /** 51 | * Load a specific category from a configuration section 52 | * 53 | * @param section The configuration section 54 | * @param key The key to load. 55 | */ 56 | private void loadCategory(CommentedConfigurationSection section, String key) { 57 | String newKey = "categories." + key; 58 | 59 | boolean global = section.getBoolean(newKey + ".global", false); 60 | boolean defaultCategory = section.getBoolean(newKey + ".default", false); 61 | CategoryType categoryType = TagsUtils.getEnum(CategoryType.class, section.getString(newKey + ".type", "CUSTOM")); 62 | if (global) categoryType = CategoryType.GLOBAL; 63 | if (defaultCategory) categoryType = CategoryType.DEFAULT; 64 | 65 | Category category = new Category(key.toLowerCase()); 66 | category.setDisplayName(section.getString(newKey + ".display-name", newKey)); 67 | category.setType(categoryType); 68 | category.setOrder(section.getInt(newKey + ".order", -1)); 69 | category.setPermission(section.getString(newKey + ".permission", null)); 70 | category.setBypassPermission(section.getBoolean(newKey + ".unlocks-all-tags", false)); 71 | 72 | this.cachedCategories.put(key.toLowerCase(), category); 73 | } 74 | 75 | /** 76 | * Get the first category of a specific type 77 | * 78 | * @param categoryType The type of category 79 | * @return The first category 80 | */ 81 | public Category getFirst(CategoryType categoryType) { 82 | if (this.cachedCategories.isEmpty()) return null; 83 | 84 | return this.cachedCategories.values() 85 | .stream() 86 | .filter(category -> category.getType() == categoryType) 87 | .findFirst() 88 | .orElse(null); 89 | } 90 | 91 | /** 92 | * Get a category from the cache. 93 | * 94 | * @param id The id of the category 95 | * @return The category 96 | */ 97 | public Category getCategory(String id) { 98 | if (this.cachedCategories.isEmpty() || id == null) return null; 99 | 100 | return this.cachedCategories.get(id.toLowerCase()); 101 | } 102 | 103 | /** 104 | * Save a category with the configuration file 105 | * 106 | * @param category The category to save 107 | */ 108 | public void save(Category category) { 109 | if (category == null || this.categoryConfig == null) return; 110 | 111 | this.categoryConfig.set("categories.display-name", category.getDisplayName()); 112 | this.categoryConfig.set("categories.type", category.getType().name()); 113 | this.categoryConfig.set("categories.order", category.getOrder()); 114 | this.categoryConfig.set("categories.permission", category.getPermission()); 115 | this.categoryConfig.set("categories.unlocks-all-tags", category.isBypassPermission()); 116 | 117 | this.categoryConfig.save(this.categoryFile); 118 | this.cachedCategories.put(category.getId().toLowerCase(), category); 119 | } 120 | 121 | /** 122 | * Get a list of categories associated with a specific type 123 | * 124 | * @param type The type of category 125 | * @return A list of categories 126 | */ 127 | public List getByType(CategoryType type) { 128 | if (this.cachedCategories.isEmpty()) return null; 129 | 130 | return this.cachedCategories.values() 131 | .stream() 132 | .filter(category -> category.getType() == type) 133 | .toList(); 134 | } 135 | 136 | /** 137 | * Get all saved categories in the cache 138 | * 139 | * @return A list of categories 140 | */ 141 | public List getCategories() { 142 | return this.cachedCategories.values().stream().toList(); 143 | } 144 | 145 | /** 146 | * Check if the category manager is enabled 147 | * 148 | * @return If categories are enabled 149 | */ 150 | public boolean isEnabled() { 151 | return !this.cachedCategories.isEmpty(); 152 | } 153 | 154 | @Override 155 | public void disable() { 156 | 157 | } 158 | 159 | } 160 | -------------------------------------------------------------------------------- /src/main/java/xyz/oribuin/eternaltags/manager/CommandManager.java: -------------------------------------------------------------------------------- 1 | package xyz.oribuin.eternaltags.manager; 2 | 3 | import dev.rosewood.rosegarden.RosePlugin; 4 | import dev.rosewood.rosegarden.command.framework.BaseRoseCommand; 5 | import dev.rosewood.rosegarden.manager.AbstractCommandManager; 6 | import xyz.oribuin.eternaltags.command.impl.TagsCommand; 7 | 8 | import java.util.List; 9 | import java.util.function.Function; 10 | 11 | public class CommandManager extends AbstractCommandManager { 12 | 13 | public CommandManager(RosePlugin rosePlugin) { 14 | super(rosePlugin); 15 | } 16 | 17 | @Override 18 | public List> getRootCommands() { 19 | return List.of(TagsCommand::new); 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/xyz/oribuin/eternaltags/manager/LocaleManager.java: -------------------------------------------------------------------------------- 1 | package xyz.oribuin.eternaltags.manager; 2 | 3 | import dev.rosewood.rosegarden.RosePlugin; 4 | import dev.rosewood.rosegarden.manager.AbstractLocaleManager; 5 | import dev.rosewood.rosegarden.utils.HexUtils; 6 | import dev.rosewood.rosegarden.utils.StringPlaceholders; 7 | import org.bukkit.command.CommandSender; 8 | import org.jetbrains.annotations.NotNull; 9 | 10 | import java.util.ArrayList; 11 | import java.util.List; 12 | import java.util.stream.Collectors; 13 | 14 | public class LocaleManager extends AbstractLocaleManager { 15 | 16 | public LocaleManager(RosePlugin rosePlugin) { 17 | super(rosePlugin); 18 | } 19 | 20 | /** 21 | * Send a message to a CommandSender 22 | * 23 | * @param sender The CommandSender to send the message to 24 | * @param messageKey The message key to send 25 | * @param placeholders The placeholders to apply to the message 26 | */ 27 | public void sendMessages(CommandSender sender, String messageKey, StringPlaceholders placeholders) { 28 | String prefix = this.getLocaleMessage("prefix"); 29 | List messages = this.getLocaleMessages(messageKey, placeholders); 30 | 31 | if (messages.isEmpty() || messages.stream().allMatch(String::isEmpty)) 32 | return; 33 | 34 | for (String message : messages) { 35 | this.sendParsedMessage(sender, prefix + message); 36 | } 37 | } 38 | 39 | /** 40 | * Get a string list from a locale key 41 | * 42 | * @param messageKey The key to get the message from 43 | * @return The message 44 | */ 45 | public List getLocaleMessages(String messageKey, StringPlaceholders placeholders) { 46 | return this.getLocaleStringList(messageKey) 47 | .stream() 48 | .map(message -> HexUtils.colorify(placeholders.apply(message))) 49 | .collect(Collectors.toList()); 50 | } 51 | 52 | /** 53 | * Get a list of strings from a locale key 54 | * 55 | * @param key The key to get the string list from 56 | * @return The string list 57 | */ 58 | public List getLocaleStringList(String key) { 59 | Object value = this.loadedLocale.getLocaleValues().get(key); 60 | if (value instanceof String str) 61 | return List.of(str); 62 | 63 | if (value instanceof List list) { 64 | List stringList = new ArrayList<>(); 65 | for (Object obj : list) { 66 | if (obj instanceof String str) 67 | stringList.add(str); 68 | } 69 | return stringList; 70 | } 71 | 72 | return List.of(); 73 | } 74 | 75 | /** 76 | * Send a custom message to a CommandSender with placeholders 77 | * 78 | * @param sender The CommandSender to send the message to 79 | * @param message The message to send 80 | * @param placeholders The placeholders to apply to the message 81 | */ 82 | public void sendCustomMessage(CommandSender sender, String message, StringPlaceholders placeholders) { 83 | if (message.isEmpty()) 84 | return; 85 | 86 | if (placeholders == null) 87 | placeholders = StringPlaceholders.empty(); 88 | 89 | this.handleMessage(sender, HexUtils.colorify(this.parsePlaceholders(sender, placeholders.apply(message)))); 90 | } 91 | 92 | /** 93 | * Send a custom message to a CommandSender with placeholders 94 | * 95 | * @param sender The CommandSender to send the message to 96 | * @param messages The messages to send 97 | * @param placeholders The placeholders to apply to the messages 98 | */ 99 | public void sendCustomMessage(CommandSender sender, List messages, StringPlaceholders placeholders) { 100 | if (messages.isEmpty()) 101 | return; 102 | 103 | if (placeholders == null) 104 | placeholders = StringPlaceholders.empty(); 105 | 106 | for (String message : messages) { 107 | this.handleMessage(sender, HexUtils.colorify(this.parsePlaceholders(sender, placeholders.apply(message)))); 108 | } 109 | } 110 | 111 | /** 112 | * Format a string with placeholders 113 | * 114 | * @param sender The CommandSender to send the message to 115 | * @param message The message to send 116 | * @param placeholders The placeholders to apply to the message 117 | * @return The formatted string 118 | */ 119 | @NotNull 120 | public String format(CommandSender sender, String message, StringPlaceholders placeholders) { 121 | if (message == null || message.isEmpty()) 122 | return ""; 123 | 124 | if (placeholders == null) 125 | placeholders = StringPlaceholders.empty(); 126 | 127 | return HexUtils.colorify(this.parsePlaceholders(sender, placeholders.apply(message))); 128 | } 129 | 130 | /** 131 | * Format a list of strings with placeholders 132 | * 133 | * @param sender The CommandSender to send the message to 134 | * @param messages The messages to send 135 | * @param placeholders The placeholders to apply to the messages 136 | * @return The formatted string 137 | */ 138 | @NotNull 139 | public List format(CommandSender sender, List messages, StringPlaceholders placeholders) { 140 | if (messages.isEmpty()) 141 | return List.of(); 142 | 143 | if (placeholders == null) 144 | placeholders = StringPlaceholders.empty(); 145 | 146 | List formattedMessages = new ArrayList<>(); 147 | for (String message : messages) { 148 | formattedMessages.add(HexUtils.colorify(this.parsePlaceholders(sender, placeholders.apply(message)))); 149 | } 150 | return formattedMessages; 151 | } 152 | 153 | } 154 | -------------------------------------------------------------------------------- /src/main/java/xyz/oribuin/eternaltags/manager/PluginConversionManager.java: -------------------------------------------------------------------------------- 1 | package xyz.oribuin.eternaltags.manager; 2 | 3 | import dev.rosewood.rosegarden.RosePlugin; 4 | import dev.rosewood.rosegarden.manager.Manager; 5 | import xyz.oribuin.eternaltags.conversion.ConversionPlugin; 6 | import xyz.oribuin.eternaltags.obj.Category; 7 | import xyz.oribuin.eternaltags.obj.Tag; 8 | 9 | import java.util.HashMap; 10 | import java.util.Map; 11 | 12 | public class PluginConversionManager extends Manager { 13 | 14 | public PluginConversionManager(RosePlugin rosePlugin) { 15 | super(rosePlugin); 16 | } 17 | 18 | @Override 19 | public void reload() { 20 | // Unused 21 | } 22 | 23 | /** 24 | * Convert a plugin into EternalTags and create a category for it. 25 | * 26 | * @param conversionPlugin The plugin to convert 27 | */ 28 | public Map convertPlugin(ConversionPlugin conversionPlugin) { 29 | if (conversionPlugin == null) 30 | return new HashMap<>(); 31 | 32 | TagsManager manager = this.rosePlugin.getManager(TagsManager.class); 33 | CategoryManager categoryManager = this.rosePlugin.getManager(CategoryManager.class); 34 | Map tags = conversionPlugin.getPluginTags(this.rosePlugin); 35 | 36 | // Create a category for the plugin 37 | Category category = new Category(conversionPlugin.getPluginName().toLowerCase()); 38 | category.setDisplayName(conversionPlugin.getPluginName()); 39 | category.setPermission("eternaltags.category." + category.getId()); 40 | categoryManager.save(category); 41 | 42 | // Set the category for each tag 43 | tags.values().forEach(tag -> tag.setCategory(category.getId())); 44 | 45 | // filter out tags that have name or tag null 46 | tags.entrySet().removeIf(entry -> entry.getKey() == null); 47 | manager.saveTags(tags); 48 | 49 | return tags; 50 | } 51 | 52 | @Override 53 | public void disable() { 54 | // Unused 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/xyz/oribuin/eternaltags/obj/Category.java: -------------------------------------------------------------------------------- 1 | package xyz.oribuin.eternaltags.obj; 2 | 3 | import org.apache.commons.lang.StringUtils; 4 | import org.bukkit.entity.Player; 5 | import org.jetbrains.annotations.NotNull; 6 | import org.jetbrains.annotations.Nullable; 7 | 8 | public class Category { 9 | 10 | private final @NotNull String id; // The id of the category 11 | private @NotNull String displayName; // The display name of the category 12 | private CategoryType type; // The type of the category 13 | private int order; // The order of the category 14 | private boolean bypassPermission; // If the category bypasses the permission check for tags. 15 | private @Nullable String permission; // The permission required to view the category 16 | 17 | public Category(@NotNull String id) { 18 | this.id = id; 19 | this.displayName = StringUtils.capitalize(id.toLowerCase()); 20 | this.type = CategoryType.CUSTOM; 21 | this.order = -1; 22 | this.bypassPermission = false; 23 | this.permission = null; 24 | } 25 | 26 | /** 27 | * Check if a player has access to the category 28 | * 29 | * @param player The player 30 | * @return If the player has access to the category 31 | */ 32 | public boolean canUse(Player player) { 33 | if (this.permission == null) return true; 34 | 35 | return player.hasPermission(this.permission); 36 | } 37 | 38 | @NotNull 39 | public String getId() { 40 | return id; 41 | } 42 | 43 | @NotNull 44 | public String getDisplayName() { 45 | return displayName; 46 | } 47 | 48 | public void setDisplayName(@NotNull String displayName) { 49 | this.displayName = displayName; 50 | } 51 | 52 | public CategoryType getType() { 53 | return type; 54 | } 55 | 56 | public void setType(CategoryType categoryType) { 57 | this.type = categoryType; 58 | } 59 | 60 | public int getOrder() { 61 | return this.order; 62 | } 63 | 64 | public void setOrder(int order) { 65 | this.order = order; 66 | } 67 | 68 | public boolean isBypassPermission() { 69 | return this.bypassPermission; 70 | } 71 | 72 | public void setBypassPermission(boolean bypassPermission) { 73 | this.bypassPermission = bypassPermission; 74 | } 75 | 76 | @Nullable 77 | public String getPermission() { 78 | return this.permission; 79 | } 80 | 81 | public void setPermission(@Nullable String permission) { 82 | this.permission = permission; 83 | } 84 | 85 | } 86 | -------------------------------------------------------------------------------- /src/main/java/xyz/oribuin/eternaltags/obj/CategoryType.java: -------------------------------------------------------------------------------- 1 | package xyz.oribuin.eternaltags.obj; 2 | 3 | public enum CategoryType { 4 | CUSTOM, // A custom category 5 | DEFAULT, // If any uncategorized tags are found, they will be placed in this category. 6 | GLOBAL, // All tags are placed in this category. 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/xyz/oribuin/eternaltags/obj/Tag.java: -------------------------------------------------------------------------------- 1 | package xyz.oribuin.eternaltags.obj; 2 | 3 | import org.bukkit.Material; 4 | import org.bukkit.entity.Player; 5 | import org.bukkit.inventory.ItemStack; 6 | import org.jetbrains.annotations.NotNull; 7 | import org.jetbrains.annotations.Nullable; 8 | import xyz.oribuin.eternaltags.EternalTags; 9 | import xyz.oribuin.eternaltags.config.Setting; 10 | import xyz.oribuin.eternaltags.manager.DataManager; 11 | 12 | import java.util.ArrayList; 13 | import java.util.List; 14 | 15 | public class Tag { 16 | 17 | private final @NotNull String id; // The id of the tag 18 | private @NotNull String name; // The name of the tag 19 | private @NotNull String tag; // The tag to be added to the player 20 | private @Nullable String permission; // The permission required to use the tag 21 | private @NotNull List description; // The description of the tag 22 | private int order; // The order of the tag 23 | private @Nullable ItemStack icon; // The icon of the tag 24 | private @Nullable String category; // The category the tag is in 25 | private boolean handIcon; // Whether the icon is from the player's hand or not, internal method for tag saving 26 | 27 | public Tag(@NotNull String id, @NotNull String name, @NotNull String tag) { 28 | this.id = id; 29 | this.name = name; 30 | this.tag = tag; 31 | this.description = new ArrayList<>(); 32 | this.permission = "eternaltags.tag." + id.toLowerCase(); 33 | this.order = -1; 34 | this.icon = null; 35 | this.category = null; 36 | this.handIcon = false; 37 | } 38 | 39 | /** 40 | * Equip a tag to a specific player. 41 | * 42 | * @param player The player to equip the tag to 43 | */ 44 | public void equip(Player player) { 45 | DataManager dataManager = EternalTags.getInstance().getManager(DataManager.class); 46 | 47 | // Remove the tag if the player does not have permission 48 | if (Setting.REMOVE_TAGS.get() && this.permission != null && !player.hasPermission(this.permission)) { 49 | dataManager.removeUser(player.getUniqueId()); 50 | return; 51 | } 52 | 53 | // Set the player's tag 54 | dataManager.saveUser(player.getUniqueId(), this.id.toLowerCase()); 55 | } 56 | 57 | /** 58 | * Unequip a tag from a specific player. 59 | * 60 | * @param player The player to unequip the tag from 61 | */ 62 | public void unequip(Player player) { 63 | DataManager dataManager = EternalTags.getInstance().getManager(DataManager.class); 64 | dataManager.removeUser(player.getUniqueId()); 65 | } 66 | 67 | /** 68 | * Check if a player has permission to use the tag. 69 | * 70 | * @param player The player to check 71 | * 72 | * @return Whether the player has permission or not 73 | */ 74 | public boolean hasPermission(Player player) { 75 | if (this.permission == null) return true; // No permission required 76 | if (player.hasPermission("eternaltags.tags.*")) return true; // Bypass all permissions 77 | 78 | return player.hasPermission(this.permission); 79 | } 80 | 81 | public @NotNull String getId() { 82 | return id; 83 | } 84 | 85 | public @NotNull String getName() { 86 | return name; 87 | } 88 | 89 | public void setName(@NotNull String name) { 90 | this.name = name; 91 | } 92 | 93 | public @NotNull String getTag() { 94 | return tag; 95 | } 96 | 97 | public void setTag(@NotNull String tag) { 98 | this.tag = tag; 99 | } 100 | 101 | public @Nullable String getPermission() { 102 | return permission; 103 | } 104 | 105 | public void setPermission(@Nullable String permission) { 106 | this.permission = permission; 107 | } 108 | 109 | public @NotNull List getDescription() { 110 | return description; 111 | } 112 | 113 | public void setDescription(@NotNull List description) { 114 | this.description = description; 115 | } 116 | 117 | public int getOrder() { 118 | return order; 119 | } 120 | 121 | public void setOrder(int order) { 122 | this.order = order; 123 | } 124 | 125 | public @Nullable ItemStack getIcon() { 126 | return icon; 127 | } 128 | 129 | public void setIcon(@Nullable ItemStack icon) { 130 | this.icon = icon; 131 | } 132 | 133 | public void setIcon(@Nullable Material material) { 134 | if (material == null) { 135 | this.icon = null; 136 | return; 137 | } 138 | 139 | this.icon = new ItemStack(material); 140 | } 141 | 142 | public @Nullable String getCategory() { 143 | return category; 144 | } 145 | 146 | public void setCategory(@Nullable String category) { 147 | this.category = category; 148 | } 149 | 150 | public boolean isHandIcon() { 151 | return handIcon; 152 | } 153 | 154 | public void setHandIcon(boolean handIcon) { 155 | this.handIcon = handIcon; 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /src/main/java/xyz/oribuin/eternaltags/obj/TagDescription.java: -------------------------------------------------------------------------------- 1 | package xyz.oribuin.eternaltags.obj; 2 | 3 | import java.util.List; 4 | 5 | // This will have to stay as it is for compatibility reasons. 6 | public record TagDescription(List description) { 7 | 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/xyz/oribuin/eternaltags/obj/TagUser.java: -------------------------------------------------------------------------------- 1 | package xyz.oribuin.eternaltags.obj; 2 | 3 | import org.bukkit.entity.Player; 4 | import org.jetbrains.annotations.NotNull; 5 | import org.jetbrains.annotations.Nullable; 6 | 7 | import java.util.HashSet; 8 | import java.util.Set; 9 | import java.util.UUID; 10 | 11 | public class TagUser { 12 | 13 | private final @NotNull UUID player; 14 | private @Nullable String activeTag; 15 | private boolean usingDefaultTag; 16 | private @NotNull Set favourites; 17 | 18 | /** 19 | * Create a new TagUser object. 20 | * 21 | * @param player The player to create the object for. 22 | */ 23 | public TagUser(@NotNull UUID player) { 24 | this.player = player; 25 | this.activeTag = null; 26 | this.usingDefaultTag = false; 27 | this.favourites = new HashSet<>(); 28 | } 29 | 30 | /** 31 | * Create a new TagUser object. 32 | * 33 | * @param player The player to create the object for. 34 | */ 35 | public TagUser(@NotNull Player player) { 36 | this.player = player.getUniqueId(); 37 | this.activeTag = null; 38 | this.usingDefaultTag = false; 39 | this.favourites = new HashSet<>(); 40 | } 41 | 42 | /** 43 | * @return The UUID of the player this object represents. 44 | */ 45 | @NotNull 46 | public UUID getPlayer() { 47 | return this.player; 48 | } 49 | 50 | 51 | /** 52 | * @return The active tag of the player. 53 | */ 54 | @Nullable 55 | public String getActiveTag() { 56 | return this.activeTag; 57 | } 58 | 59 | /** 60 | * @param activeTag Set the active tag of the player. 61 | */ 62 | public void setActiveTag(@Nullable String activeTag) { 63 | this.activeTag = activeTag; 64 | } 65 | 66 | /** 67 | * @return Whether the player is using the default tag. 68 | */ 69 | public boolean isUsingDefaultTag() { 70 | return this.usingDefaultTag; 71 | } 72 | 73 | /** 74 | * @param usingDefaultTag Set whether the player is using the default tag. 75 | */ 76 | public void setUsingDefaultTag(boolean usingDefaultTag) { 77 | this.usingDefaultTag = usingDefaultTag; 78 | } 79 | 80 | /** 81 | * @return The player's favourite tags. 82 | */ 83 | public @NotNull Set getFavourites() { 84 | return this.favourites; 85 | } 86 | 87 | /** 88 | * @param favourites Set the player's favourite tags. 89 | */ 90 | public void setFavourites(@NotNull Set favourites) { 91 | this.favourites = favourites; 92 | } 93 | 94 | } 95 | -------------------------------------------------------------------------------- /src/main/java/xyz/oribuin/eternaltags/util/EventWaiter.java: -------------------------------------------------------------------------------- 1 | package xyz.oribuin.eternaltags.util; 2 | 3 | import org.bukkit.Bukkit; 4 | import org.bukkit.event.Event; 5 | import org.bukkit.event.EventPriority; 6 | import org.bukkit.event.HandlerList; 7 | import org.bukkit.event.Listener; 8 | import xyz.oribuin.eternaltags.EternalTags; 9 | 10 | import java.util.HashMap; 11 | import java.util.HashSet; 12 | import java.util.Map; 13 | import java.util.Set; 14 | import java.util.concurrent.TimeUnit; 15 | import java.util.function.Consumer; 16 | import java.util.function.Predicate; 17 | 18 | /** 19 | * A class that allows you to wait for an event to occur. 20 | *

21 | * Taken from JDA Utilities 22 | */ 23 | @SuppressWarnings("unchecked") 24 | public class EventWaiter implements Listener { 25 | 26 | private final Map, Set> waitingEvents = new HashMap<>(); 27 | 28 | /** 29 | * Waits for an event to occur. 30 | * 31 | * @param eventClass The class of the event to wait for. 32 | * @param condition The condition that must be met for the event to be accepted. 33 | * @param action The action to perform when the event is accepted. 34 | * @param The type of the event. 35 | */ 36 | public void waitForEvent(Class eventClass, Predicate condition, Consumer action) { 37 | this.waitForEvent(eventClass, condition, action, -1, null, null); 38 | } 39 | 40 | /** 41 | * Waits for an event to occur. 42 | * 43 | * @param eventClass The class of the event to wait for. 44 | * @param predicate The check to perform on the event. 45 | * @param action The action to perform on the event. 46 | * @param timeout The timeout for the event to occur. 47 | * @param unit The unit of the timeout. 48 | * @param timeoutAction The action to perform if the event does not occur within the timeout. 49 | * @param The type of the event. 50 | */ 51 | public void waitForEvent(Class eventClass, Predicate predicate, Consumer action, long timeout, TimeUnit unit, Runnable timeoutAction) { 52 | var we = new WaitingEvent<>(predicate, action); 53 | var set = this.waitingEvents.computeIfAbsent(eventClass, k -> new HashSet<>()); 54 | set.add(we); 55 | 56 | // Register the event if it hasn't been registered yet. 57 | Bukkit.getPluginManager().registerEvent(eventClass, this, EventPriority.LOWEST, (listener, event) -> { 58 | if (predicate.test((T) event)) { 59 | action.accept((T) event); 60 | HandlerList.unregisterAll(this); 61 | 62 | } 63 | 64 | }, EternalTags.getInstance(), false); 65 | 66 | 67 | if (timeout > 0 && unit != null) { 68 | EternalTags.getInstance().getScheduler().runTaskLater(() -> { 69 | if (set.remove(we) && timeoutAction != null) { 70 | timeoutAction.run(); 71 | HandlerList.unregisterAll(this); 72 | } 73 | 74 | }, timeout, unit); 75 | } 76 | } 77 | 78 | private record WaitingEvent(Predicate predicate, Consumer action) { 79 | boolean attempt(T event) { 80 | if (predicate.test(event)) { 81 | action.accept(event); 82 | return true; 83 | } 84 | 85 | return false; 86 | } 87 | } 88 | 89 | } 90 | -------------------------------------------------------------------------------- /src/main/java/xyz/oribuin/eternaltags/util/ItemBuilder.java: -------------------------------------------------------------------------------- 1 | package xyz.oribuin.eternaltags.util; 2 | 3 | import org.bukkit.Color; 4 | import org.bukkit.Material; 5 | import org.bukkit.OfflinePlayer; 6 | import org.bukkit.enchantments.Enchantment; 7 | import org.bukkit.inventory.ItemFlag; 8 | import org.bukkit.inventory.ItemStack; 9 | import org.bukkit.inventory.meta.ItemMeta; 10 | import org.bukkit.inventory.meta.LeatherArmorMeta; 11 | import org.bukkit.inventory.meta.PotionMeta; 12 | import org.bukkit.inventory.meta.SkullMeta; 13 | import org.bukkit.potion.PotionEffect; 14 | import org.bukkit.potion.PotionEffectType; 15 | import org.jetbrains.annotations.Nullable; 16 | import xyz.oribuin.eternaltags.util.nms.SkullUtils; 17 | 18 | import java.util.Arrays; 19 | import java.util.List; 20 | import java.util.Map; 21 | 22 | @SuppressWarnings({"unused", "deprecation"}) 23 | public class ItemBuilder { 24 | 25 | private final ItemStack item; 26 | private final ItemMeta meta; 27 | 28 | /** 29 | * Create a new Item Builder with a Material. 30 | * 31 | * @param material The Material. 32 | */ 33 | public ItemBuilder(Material material) { 34 | this.item = new ItemStack(material); 35 | this.meta = this.item.getItemMeta(); 36 | } 37 | 38 | /** 39 | * Create a new Item Builder with an existing ItemStack. 40 | * 41 | * @param item The ItemStack. 42 | */ 43 | public ItemBuilder(ItemStack item) { 44 | this.item = item.clone(); 45 | this.meta = this.item.getItemMeta(); 46 | } 47 | 48 | /** 49 | * Set the ItemStack's Material. 50 | * 51 | * @param material The Material. 52 | * @return Item.Builder. 53 | */ 54 | public ItemBuilder material(Material material) { 55 | this.item.setType(material); 56 | return this; 57 | } 58 | 59 | /** 60 | * Set the ItemStack's Display Name. 61 | * 62 | * @param text The text. 63 | * @return Item.Builder. 64 | */ 65 | public ItemBuilder name(@Nullable String text) { 66 | this.meta.setDisplayName(text); 67 | return this; 68 | } 69 | 70 | /** 71 | * Set the ItemStack's Lore 72 | * 73 | * @param lore The lore 74 | * @return Item.Builder. 75 | */ 76 | public ItemBuilder lore(@Nullable List lore) { 77 | this.meta.setLore(lore); 78 | return this; 79 | } 80 | 81 | /** 82 | * Set the ItemStack's Lore 83 | * 84 | * @param lore The lore 85 | * @return Item.Builder. 86 | */ 87 | public ItemBuilder lore(@Nullable String... lore) { 88 | return this.lore(Arrays.asList(lore)); 89 | } 90 | 91 | /** 92 | * Set the ItemStack amount. 93 | * 94 | * @param amount The amount of items. 95 | * @return Item.Builder 96 | */ 97 | public ItemBuilder amount(int amount) { 98 | this.item.setAmount(amount); 99 | return this; 100 | } 101 | 102 | /** 103 | * Add an enchantment to an item. 104 | * 105 | * @param ench The enchantment. 106 | * @param level The level of the enchantment 107 | * @return Item.Builder 108 | */ 109 | public ItemBuilder enchant(Enchantment ench, int level) { 110 | this.meta.addEnchant(ench, level, true); 111 | return this; 112 | } 113 | 114 | /** 115 | * Add multiple enchantments to an item. 116 | * 117 | * @param enchantments The enchantments. 118 | * @return Item.Builder 119 | */ 120 | 121 | public ItemBuilder enchant(Map enchantments) { 122 | enchantments.forEach(this::enchant); 123 | return this; 124 | } 125 | 126 | /** 127 | * Remove an enchantment from an Item 128 | * 129 | * @param ench The enchantment. 130 | * @return Item.Builder 131 | */ 132 | public ItemBuilder remove(Enchantment ench) { 133 | this.item.removeEnchantment(ench); 134 | return this; 135 | } 136 | 137 | /** 138 | * Remove and reset the ItemStack's Flags 139 | * 140 | * @param flags The ItemFlags. 141 | * @return Item.Builder 142 | */ 143 | public ItemBuilder flags(ItemFlag[] flags) { 144 | this.meta.addItemFlags(ItemFlag.values()); 145 | return this; 146 | } 147 | 148 | 149 | /** 150 | * Change the item's unbreakable status. 151 | * 152 | * @param unbreakable true if unbreakable 153 | * @return Item.Builder 154 | */ 155 | public ItemBuilder unbreakable(boolean unbreakable) { 156 | this.meta.setUnbreakable(unbreakable); 157 | return this; 158 | } 159 | 160 | /** 161 | * Set an item to glow. 162 | * 163 | * @return Item.Builder 164 | */ 165 | public ItemBuilder glow(boolean b) { 166 | if (!b) return this; 167 | 168 | this.meta.addEnchant(Enchantment.PROTECTION_ENVIRONMENTAL, 1, true); 169 | this.meta.addItemFlags(ItemFlag.HIDE_ENCHANTS); 170 | return this; 171 | } 172 | 173 | /** 174 | * Apply a texture to a skull. 175 | * 176 | * @param texture The texture. 177 | * @return Item.Builder 178 | */ 179 | public ItemBuilder texture(@Nullable String texture) { 180 | if (item.getType() != Material.PLAYER_HEAD || texture == null) 181 | return this; 182 | 183 | if (!(this.meta instanceof SkullMeta skullMeta)) return this; 184 | 185 | SkullUtils.setSkullTexture(skullMeta, texture); 186 | return this; 187 | } 188 | 189 | /** 190 | * Set the owner of a skull. 191 | * 192 | * @param owner The owner. 193 | * @return Item.Builder 194 | */ 195 | public ItemBuilder owner(OfflinePlayer owner) { 196 | if (item.getType() != Material.PLAYER_HEAD) return this; 197 | if (owner == null) return this; 198 | if (!(this.meta instanceof SkullMeta skullMeta)) return this; 199 | 200 | skullMeta.setOwningPlayer(owner); 201 | return this; 202 | } 203 | 204 | public ItemBuilder model(int model) { 205 | this.meta.setCustomModelData(model); 206 | return this; 207 | } 208 | 209 | public ItemBuilder potion(PotionEffectType effectType, int duration, int amp) { 210 | if (!(this.meta instanceof PotionMeta potionMeta)) return this; 211 | 212 | potionMeta.addCustomEffect(new PotionEffect(effectType, duration, amp), true); 213 | return this; 214 | } 215 | 216 | public ItemBuilder color(Color color) { 217 | if (this.meta instanceof PotionMeta potionMeta) { 218 | potionMeta.setColor(color); 219 | } 220 | 221 | if (this.item.getItemMeta() instanceof LeatherArmorMeta leatherArmorMeta) { 222 | leatherArmorMeta.setColor(color); 223 | } 224 | 225 | return this; 226 | } 227 | 228 | /** 229 | * Finalize the Item Builder and create the stack. 230 | * 231 | * @return The ItemStack 232 | */ 233 | public ItemStack build() { 234 | this.item.setItemMeta(this.meta); 235 | return this.item; 236 | } 237 | 238 | } -------------------------------------------------------------------------------- /src/main/java/xyz/oribuin/eternaltags/util/nms/SkullUtils.java: -------------------------------------------------------------------------------- 1 | package xyz.oribuin.eternaltags.util.nms; 2 | 3 | import com.mojang.authlib.GameProfile; 4 | import com.mojang.authlib.properties.Property; 5 | import dev.rosewood.rosegarden.utils.NMSUtil; 6 | import me.arcaniax.hdb.api.HeadDatabaseAPI; 7 | import org.bukkit.Bukkit; 8 | import org.bukkit.inventory.meta.SkullMeta; 9 | import org.bukkit.profile.PlayerProfile; 10 | import org.bukkit.profile.PlayerTextures; 11 | 12 | import java.lang.reflect.Method; 13 | import java.net.MalformedURLException; 14 | import java.net.URL; 15 | import java.util.Base64; 16 | import java.util.UUID; 17 | 18 | public final class SkullUtils { 19 | 20 | private static Method method_SkullMeta_setProfile; 21 | 22 | private SkullUtils() { 23 | 24 | } 25 | 26 | /** 27 | * Applies a base64 encoded texture to an item's SkullMeta 28 | * 29 | * @param skullMeta The ItemMeta for the Skull 30 | * @param texture The texture to apply to the skull 31 | */ 32 | @SuppressWarnings("deprecation") 33 | public static void setSkullTexture(SkullMeta skullMeta, String texture) { 34 | 35 | if (texture != null && texture.startsWith("hdb:") && Bukkit.getPluginManager().isPluginEnabled("HeadDatabase")) { 36 | texture = new HeadDatabaseAPI().getBase64(texture.substring(4)); 37 | } 38 | 39 | if (texture == null || texture.isEmpty()) 40 | return; 41 | 42 | if (NMSUtil.getVersionNumber() >= 18) { // No need to use NMS on 1.18.1+ 43 | if (NMSUtil.isPaper()) { 44 | setTexturesPaper(skullMeta, texture); 45 | return; 46 | } 47 | 48 | PlayerProfile profile = Bukkit.createPlayerProfile(UUID.nameUUIDFromBytes(texture.getBytes()), ""); 49 | PlayerTextures textures = profile.getTextures(); 50 | 51 | String decodedTextureJson = new String(Base64.getDecoder().decode(texture)); 52 | String decodedTextureUrl = decodedTextureJson.substring(28, decodedTextureJson.length() - 4); 53 | 54 | try { 55 | textures.setSkin(new URL(decodedTextureUrl)); 56 | } catch (MalformedURLException e) { 57 | Bukkit.getLogger().severe("Failed to set skull texture: " + e.getMessage()); 58 | } 59 | 60 | profile.setTextures(textures); 61 | skullMeta.setOwnerProfile(profile); 62 | return; 63 | } 64 | 65 | // 1.17 and below 66 | GameProfile profile = new GameProfile(UUID.nameUUIDFromBytes(texture.getBytes()), ""); 67 | profile.getProperties().put("textures", new Property("textures", texture)); 68 | 69 | try { 70 | if (method_SkullMeta_setProfile == null) { 71 | method_SkullMeta_setProfile = skullMeta.getClass().getDeclaredMethod("setProfile", GameProfile.class); 72 | method_SkullMeta_setProfile.setAccessible(true); 73 | } 74 | 75 | method_SkullMeta_setProfile.invoke(skullMeta, profile); 76 | } catch (ReflectiveOperationException e) { 77 | Bukkit.getLogger().severe("Failed to set skull texture: " + e.getMessage()); 78 | } 79 | } 80 | 81 | /** 82 | * Set the texture using the paper api for 1.18+ 83 | * 84 | * @param meta The skull meta 85 | * @param texture The texture 86 | */ 87 | private static void setTexturesPaper(SkullMeta meta, String texture) { 88 | if (texture == null || texture.isEmpty()) 89 | return; 90 | 91 | com.destroystokyo.paper.profile.PlayerProfile profile = Bukkit.createProfile(UUID.nameUUIDFromBytes(texture.getBytes()), ""); 92 | PlayerTextures textures = profile.getTextures(); 93 | 94 | String decodedTextureJson = new String(Base64.getDecoder().decode(texture)); 95 | String decodedTextureUrl = decodedTextureJson.substring(28, decodedTextureJson.length() - 4); 96 | 97 | try { 98 | textures.setSkin(new URL(decodedTextureUrl)); 99 | } catch (MalformedURLException e) { 100 | e.printStackTrace(); 101 | } 102 | 103 | profile.setTextures(textures); 104 | meta.setPlayerProfile(profile); 105 | } 106 | 107 | } -------------------------------------------------------------------------------- /src/main/resources/categories.yml: -------------------------------------------------------------------------------- 1 | # Here are all the categories you can use 2 | # categories: 3 | # test: # The ID of the category (Must be unique) 4 | # display-name: Test # The display name of the category 5 | # order: 1 # The order of the category in the GUI 6 | # default: false # Wether all tags with an undefined category should be in this category by default 7 | # global: false # Whether this category will contain all tags 8 | # permission: # The permission required to see this category (only applies with options in the category gui config) 9 | # unlocks-all-tags: false # Will allow player to use all tags in this category (Overrides the tag's permission, requires the category permission) 10 | # tags: # The tags in this category 11 | # - test # The ID of the tag 12 | # - test2 # The ID of the tag 13 | categories: 14 | animated: 15 | display-name: 'Animated Tags' 16 | order: 1 17 | default: false 18 | global: false 19 | permission: 'eternaltags.category.animated' 20 | unlocks-all-tags: false 21 | static: 22 | display-name: 'Static Tags' 23 | order: 2 24 | default: false 25 | global: false 26 | permission: 'eternaltags.category.static' 27 | unlocks-all-tags: false 28 | placeholders: 29 | display-name: 'PlaceholderAPI Tags' 30 | order: 3 31 | default: false 32 | global: false 33 | permission: 'eternaltags.category.placeholders' 34 | unlocks-all-tags: false 35 | undefined: 36 | display-name: 'Uncategorized Tags' 37 | order: 4 38 | default: true 39 | global: false 40 | permission: 'eternaltags.category.undefined' 41 | unlocks-all-tags: false 42 | all-tags: 43 | display-name: 'All Tags' 44 | order: 5 45 | default: false 46 | global: true 47 | permission: 'eternaltags.category.all-tags' 48 | unlocks-all-tags: false -------------------------------------------------------------------------------- /src/main/resources/locale/en_US.yml: -------------------------------------------------------------------------------- 1 | # en_US translation by Oribuin 2 | # Plugin Message Prefix 3 | prefix: &l{{PLUGIN_NAME}} &8| &f 4 | 5 | # Generic Command Messages 6 | no-permission: You don't have permission for that. 7 | only-player: This command can only be executed by a player. 8 | only-console: This command can only be executed by console. 9 | unknown-command: 'Unknown command, use #00B4DB/%cmd%&f help for more info' 10 | 11 | # Main Tags Command 12 | command-tags-description: Displays the tags menu. 13 | 14 | # Categories Command 15 | command-categories-description: Displays the categories menu. 16 | 17 | # Clear Command 18 | command-clear-description: Clears your active tag. 19 | command-clear-cleared: You have cleared your current active tag. 20 | command-clear-cleared-other: You have cleared %player%'s active tag. 21 | 22 | # Convert Command 23 | command-convert-description: Converts tags into EternalTags. 24 | command-convert-converted: You have converted %total% tags into EternalTags 25 | command-convert-invalid-plugin: Please provide a valid plugin to convert from. [%options%] 26 | 27 | # Create Command 28 | command-create-description: Creates a new tag ingame. 29 | command-create-created: You have successfully created a new tag, %tag%&f! 30 | command-create-tag-exists: This tag already exists. 31 | command-create-tag-missing: Please provide a display tag. 32 | 33 | # Delete Command 34 | command-delete-description: Deletes a tag from the config. 35 | command-delete-deleted: You have successfully deleted the tag, %tag%&f! 36 | 37 | # Favorite Command 38 | command-favorite-description: Toggles a tag as a favourite. 39 | command-favorite-no-permission: You do not have permission to favorite this tag. 40 | command-favorite-toggled: You have toggled the tag %tag% &fas a favorite %toggled%! 41 | command-favorite-cleared: You have cleared all your favorite tags. 42 | command-favorite-on: 'on' 43 | command-favorite-off: 'off' 44 | 45 | # Random Command 46 | command-random-description: Automatically equips a random tag you own. 47 | 48 | # Reload Command 49 | command-reload-description: Reloads the plugin. 50 | command-reload-reloaded: Configuration and locale files were reloaded 51 | 52 | # Search Command 53 | command-search-description: Search for a tag that you own. 54 | command-search-start: Type the name of the tag you want to search for. 55 | command-search-timeout: You took too long to respond, please try again. 56 | 57 | # Set Command 58 | command-set-description: Change your current active tag. 59 | command-set-changed: Your active tag has been changed to %tag%&f! 60 | command-set-changed-other: You have changed %player%'s active tag to %tag%&f! 61 | command-set-no-permission: You do not have permission to use this tag. 62 | 63 | # Set All Command 64 | command-setall-description: Change all player's active tag. 65 | command-setall-changed: You have changed everyone's tag to %tag% 66 | 67 | # Help Command 68 | command-help-title: '&fAvailable Commands:' 69 | command-help-description: Displays the help menu. 70 | command-help-list-description: '&8 - #00B4DB/%cmd% %subcmd% %args% &7- %desc%' 71 | command-help-list-description-no-args: '&8 - #00B4DB/%cmd% %subcmd% &7- %desc%' 72 | 73 | # Edit Command 74 | command-edit-description: Edit any tag's values 75 | command-edit-edited: You have changed the tag's %option% to %value%&f! 76 | command-edit-invalid-item: Please hold a valid item in your hand. 77 | 78 | # Plugin Error Messages 79 | no-tags: You currently do not own any tags. 80 | tag-doesnt-exist: The tag you have provided doesn't exist. 81 | 82 | # Argument Handler Error Messages 83 | argument-handler-player: Please provide an online username. 84 | argument-handler-plugins: Please provide a valid input for valid plugins. 85 | argument-handler-tags: '%input% is not a valid tag id.' 86 | argument-handler-edit-option: Please provide a valid tag value option. 87 | argument-handler-category: Please provide a valid category. 88 | argument-handler-material: Please provide a valid material. 89 | -------------------------------------------------------------------------------- /src/main/resources/menus/category-gui.yml: -------------------------------------------------------------------------------- 1 | # pre-title: The title of the GUI before the page number is added 2 | # title - The title of the GUI 3 | # rows - The amount of rows in the GUI 4 | # sort-type - The type of sorting to use in the GUI [ALPHABETICAL, CUSTOM, NONE, RANDOM, TAG_COUNT] 5 | # only-unlocked-categories - Whether to only show categories that the player has unlocked tags in (This may have a performance impact) 6 | # use-category-permissions - Whether to use category permissions (Categories in the categories.yml must have a permission set) 7 | # scrolling-gui - Whether to use a scrolling GUI 8 | # scrolling-type - The type of scrolling for the GUI [HORIZONTAL, VERTICAL] 9 | # update-title - Whether to update the title of the GUI on load, If this is set to false, the title will be the "pre-title" option until the page is changed. 10 | # add-pages-asynchronously - Whether to add pages asynchronously (This will improve performance, however, it may take a few seconds for the GUI to load) 11 | # 12 | # The general options for the customising itemstacks. 13 | # 14 | # material - The material of the reward. 15 | # amount - The amount of the reward. 16 | # name - The name of the reward. 17 | # lore - The lore of the reward. 18 | # glow - Whether the reward item should glow. 19 | # texture - The base64 texture of the reward item (Only for skulls), You can also use hdbLoader to load a texture if you have HeadDatabase installed. 20 | # potion-color - The color of the potion reward. (Only for potions) 21 | # model-data - The model data of the reward item. (Requires texture packs) 22 | # owner - The uuid of the player for the reward item (Only for skulls) 23 | # flags - The item flags for the reward item. 24 | # enchants - The enchantments for the reward item. 25 | # 26 | # GUI Settings 27 | gui-settings: 28 | pre-title: 'EternalTags Loading...' 29 | title: EternalTags Categories | %page%/%total% 30 | rows: 4 31 | sort-type: CUSTOM 32 | only-unlocked-categories: false 33 | use-category-permissions: true 34 | scrolling-gui: false 35 | scrolling-type: HORIZONTAL 36 | update-title: true 37 | add-pages-asynchronously: true 38 | 39 | # Categories - These are all the categories that will be shown in the GUI 40 | # Default: [animated, static, placeholders, symbols, all-tags] 41 | # categories: 42 | # : 43 | # hidden: Whether the category should be shown in the GUI 44 | # display-item: The item to display in the GUI 45 | categories: 46 | animated: 47 | hidden: false 48 | display-item: 49 | material: PLAYER_HEAD 50 | name: '&#FF416C&lAnimated Tags' 51 | lore: 52 | - ' &f| &7Animated tags that can be used' 53 | - ' &f| &7in chat. These tags are' 54 | - ' &f| &7will update every time' 55 | - ' &f| &7you send a message.' 56 | - ' &f| ' 57 | - ' &f| &7Total: %total%' 58 | texture: 'eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNjE4NTZjN2IzNzhkMzUwMjYyMTQzODQzZDFmOWZiYjIxOTExYTcxOTgzYmE3YjM5YTRkNGJhNWI2NmJlZGM2In19fQ==' 59 | 60 | static: 61 | hidden: false 62 | display-item: 63 | material: PLAYER_HEAD 64 | name: '&#f12711&lStatic Tags' 65 | lore: 66 | - ' &f| &7Static tags that can be used' 67 | - ' &f| &7in chat. These tags will' 68 | - ' &f| &7stay the same every time' 69 | - ' &f| &7you send a message.' 70 | - ' &f| ' 71 | - ' &f| &7Total: %total%' 72 | texture: 'eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNjNmMTNlMjNlYzIzNDY3YWM1ZTZmNjVhODNmMjY4NmViZWNkOTk4NmRmNWY4Y2JjZDZmYWZjNDJlNjYyYjM4In19fQ==' 73 | 74 | placeholders: 75 | hidden: false 76 | display-item: 77 | material: PLAYER_HEAD 78 | name: '&#FFE000&lPlaceholder Tags' 79 | lore: 80 | - ' &f| &7Placeholder tags that can be used' 81 | - ' &f| &7in chat. These tags will' 82 | - ' &f| &7show everyone your stats' 83 | - ' &f| ' 84 | - ' &f| &7Total: %total%' 85 | texture: 'eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZmNhNWJmZjMyNWVkNzFkOTdhMmRkZmM4M2FjZjA1ZmU3ZmQ5Y2I3Y2JkYjE1ZWJiNGYwNTYyMTkwN2U5ZjJiIn19fQ==' 86 | 87 | undefined: 88 | hidden: false 89 | display-item: 90 | material: PLAYER_HEAD 91 | name: '&#a8ff78&lUncategorized Tags' 92 | lore: 93 | - ' &f| &7Uncategorized tags that can be used' 94 | - ' &f| &7in chat. These tags do not' 95 | - ' &f| &7have a category set.' 96 | - ' &f| ' 97 | - ' &f| &7Total: %total%' 98 | texture: 'eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNGI1OTljNjE4ZTkxNGMyNWEzN2Q2OWY1NDFhMjJiZWJiZjc1MTYxNTI2Mzc1NmYyNTYxZmFiNGNmYTM5ZSJ9fX0=' 99 | 100 | all-tags: 101 | hidden: false 102 | display-item: 103 | material: PLAYER_HEAD 104 | name: '࢑b0&lAll Tags' 105 | lore: 106 | - ' &f| &7Show all the tags' 107 | - ' &f| &7that you have unlocked!' 108 | - ' &f| ' 109 | - ' &f| &7Total: %total%' 110 | texture: 'eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZjQ3N2Y0NDM4OTM2MmM0Yzc2NGM4NDdhOTczOWJjNzhjMzI0NjdlYWI0ZTM4MzBhZTRjOGJlYWMzNDQyZWY5In19fQ==' 111 | 112 | # Next Page Item - Changes the current page to the next page 113 | next-page: 114 | material: PAPER 115 | name: '#00B4DB&lNext Page' 116 | lore: 117 | - '' 118 | - '&f| #00B4DBLeft-Click &7to change ' 119 | - '&f| &7to the next page' 120 | - '' 121 | slot: 7 122 | 123 | # Previous Page Item - Changes the current page to the previous page 124 | previous-page: 125 | material: PAPER 126 | name: '#00B4DB&lPrevious Page' 127 | lore: 128 | - '' 129 | - '&f| #00B4DBLeft-Click &7to change ' 130 | - '&f| &7to the previous page' 131 | - '' 132 | slot: 1 133 | 134 | # Clear Tag Item - Clears the player's active tag 135 | clear-tag: 136 | enabled: true 137 | material: PLAYER_HEAD 138 | name: '#00B4DB&lClear Tag' 139 | lore: 140 | - '' 141 | - ' &f| #00B4DBLeft-Click &7to clear your' 142 | - ' &f| &7current active tag.' 143 | - ' &f| &7' 144 | - ' &f| &7Current Tag: #00B4DB%eternaltags_tag_formatted%' 145 | - '' 146 | texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZTljZGI5YWYzOGNmNDFkYWE1M2JjOGNkYTc2NjVjNTA5NjMyZDE0ZTY3OGYwZjE5ZjI2M2Y0NmU1NDFkOGEzMCJ9fX0= 147 | slot: 3 148 | 149 | # Search Item - Allows the player to search for tags 150 | search: 151 | enabled: true 152 | material: OAK_SIGN 153 | name: '#00B4DB&lSearch' 154 | lore: 155 | - '' 156 | - ' &f| #00B4DBLeft-Click &7to search' 157 | - ' &f| &7for a new tag in the menu.' 158 | - '' 159 | slot: 4 160 | 161 | # Favourites Tag Item - Shows the player's favourite tags 162 | favorite-tags: 163 | enabled: true 164 | material: PLAYER_HEAD 165 | name: '#00B4DB&lFavourite Tags' 166 | lore: 167 | - ' ' 168 | - ' &f| �B4DBLeft-Click &7to view' 169 | - ' &f| &7your #00B4DBfavourite tags&7.' 170 | - ' ' 171 | texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZDVjNmRjMmJiZjUxYzM2Y2ZjNzcxNDU4NWE2YTU2ODNlZjJiMTRkNDdkOGZmNzE0NjU0YTg5M2Y1ZGE2MjIifX19 172 | slot: 5 173 | 174 | # Extra Items - Allows you to add extra items to the GUI [These are placed in the gui first] 175 | extra-items: 176 | border-item: 177 | enabled: true 178 | material: GRAY_STAINED_GLASS_PANE 179 | name: ' ' 180 | slots: 181 | - 0-8 182 | - 27-35 -------------------------------------------------------------------------------- /src/main/resources/menus/favorites-gui.yml: -------------------------------------------------------------------------------- 1 | # pre-title - The title of the GUI before the page numbers are loaded 2 | # title - The title of the GUI 3 | # rows - The amount of rows in the GUI 4 | # sort-type - The type of sorting to use in the GUI [ALPHABETICAL, CUSTOM, NONE, RANDOM] 5 | # description-format - The format of the %description% placeholder 6 | # scrolling-gui - Whether to use a scrolling GUI 7 | # scrolling-type - The type of scrolling for the GUI [HORIZONTAL, VERTICAL] 8 | # update-title - Whether to update the title of the GUI on load, If this is set to false, the title will be the "pre-title" option until the page is changed. 9 | # add-pages-asynchronously - Whether to add pages asynchronously (This will improve performance, however, it may take a few seconds for the GUI to load) 10 | # 11 | # The general options for the customising itemstacks. 12 | # 13 | # material - The material of the reward. 14 | # amount - The amount of the reward. 15 | # name - The name of the reward. 16 | # lore - The lore of the reward. 17 | # glow - Whether the reward item should glow. 18 | # texture - The base64 texture of the reward item (Only for skulls), You can also use hdbLoader to load a texture if you have HeadDatabase installed. 19 | # potion-color - The color of the potion reward. (Only for potions) 20 | # model-data - The model data of the reward item. (Requires texture packs) 21 | # owner - The uuid of the player for the reward item (Only for skulls) 22 | # flags - The item flags for the reward item. 23 | # enchants - The enchantments for the reward item. 24 | # 25 | # Icon Actions 26 | # 27 | # Actions is an optional configuration option that can replace an item's functionality with a new one. 28 | # Available Actions: [BROADCAST, CLOSE, CONSOLE, MESSAGE, PLAYER, SOUND] 29 | # These actions can be defined in the `commands` section of the item, They require a ClickType to be defined. 30 | # Available ClickTypes: https://hub.spigotmc.org/javadocs/bukkit/org/bukkit/event/inventory/ClickType.html 31 | # Here is an example of how to use actions: 32 | # commands: 33 | # LEFT: 34 | # - '[message] You clicked the left button!' 35 | # RIGHT: 36 | # - '[message] You clicked the right button!' 37 | # MIDDLE: 38 | # - '[console] ban %player_name%' 39 | # 40 | # GUI Settings 41 | gui-settings: 42 | pre-title: 'EternalTags Loading...' 43 | title: Favourite Tags | %page%/%total% 44 | rows: 5 45 | sort-type: CUSTOM 46 | description-format: ' &f| &7' 47 | scrolling-gui: false 48 | scrolling-type: HORIZONTAL 49 | update-title: true 50 | add-pages-asynchronously: true 51 | 52 | # Tag Item - The item that represents each tag in the GUI 53 | tag-item: 54 | material: NAME_TAG 55 | amount: 1 56 | name: '%tag%' 57 | lore: 58 | - '' 59 | - '&f| #00B4DBLeft-Click &7on this' 60 | - '&f| &7icon to change your' 61 | - '&f| &7active tag!' 62 | - '&f| ' 63 | - '&f| #00B4DBShift-Click &7to add' 64 | - '&f| &7this tag to your favorites' 65 | - '' 66 | glow: true 67 | 68 | # Next Page Item - Changes the current page to the next page 69 | next-page: 70 | material: PAPER 71 | name: '#00B4DB&lNext Page' 72 | lore: 73 | - '' 74 | - '&f| #00B4DBLeft-Click &7to change ' 75 | - '&f| &7to the next page' 76 | - '' 77 | slot: 7 78 | 79 | # Previous Page Item - Changes the current page to the previous page 80 | previous-page: 81 | material: PAPER 82 | name: '#00B4DB&lPrevious Page' 83 | lore: 84 | - '' 85 | - '&f| #00B4DBLeft-Click &7to change ' 86 | - '&f| &7to the previous page' 87 | - '' 88 | slot: 1 89 | 90 | # Clear Tag Item - Clears the player's active tag 91 | clear-tag: 92 | enabled: true 93 | material: PLAYER_HEAD 94 | name: '#00B4DB&lClear Tag' 95 | lore: 96 | - '' 97 | - ' &f| #00B4DBLeft-Click &7to clear your' 98 | - ' &f| &7current active tag.' 99 | - ' &f| &7' 100 | - ' &f| &7Current Tag: #00B4DB%eternaltags_tag_formatted%' 101 | - '' 102 | texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZTljZGI5YWYzOGNmNDFkYWE1M2JjOGNkYTc2NjVjNTA5NjMyZDE0ZTY3OGYwZjE5ZjI2M2Y0NmU1NDFkOGEzMCJ9fX0= 103 | slot: 3 104 | 105 | # Main Menu Item - Goes back to the main menu 106 | main-menu: 107 | enabled: true 108 | material: BARRIER 109 | name: '#00B4DB&lMain Menu' 110 | lore: 111 | - ' ' 112 | - ' &f| �B4DBLeft-Click &7to go back' 113 | - ' &f| &7to the #00B4DBmain-menu.' 114 | - ' ' 115 | slot: 4 116 | 117 | # Reset Favourites - Allows the player to remove all of their favourite tags 118 | reset-favourites: 119 | enabled: true 120 | material: OAK_SIGN 121 | name: '#00B4DB&lReset Favourites' 122 | lore: 123 | - '' 124 | - ' &f| #00B4DBDouble-Click &7to reset' 125 | - ' &f| &7all of your favourite tags.' 126 | - ' &f| &7' 127 | - ' &f| &7You #00B4DBCANNOT &7undo this action.' 128 | - '' 129 | slot: 5 130 | 131 | # Extra Items - Allows you to add extra items to the GUI [These are placed in the gui first] 132 | extra-items: 133 | border-item: 134 | enabled: true 135 | material: GRAY_STAINED_GLASS_PANE 136 | name: ' ' 137 | slots: 138 | - 0-8 139 | - 36-44 140 | -------------------------------------------------------------------------------- /src/main/resources/menus/tags-gui.yml: -------------------------------------------------------------------------------- 1 | # pre-title - The title of the GUI before the page numbers are loaded 2 | # title - The title of the GUI 3 | # rows - The amount of rows in the GUI 4 | # sort-type - The type of sorting to use in the GUI [ALPHABETICAL, CUSTOM, NONE, RANDOM] 5 | # favourite-first - Whether to show favourite tags first 6 | # add-all-tags - Whether to add all tags to the GUI (Favourite tags will be shown first, Then the player's tags, Then the remaining tags) 7 | # description-format - The format of the %description% placeholder 8 | # scrolling-gui - Whether to use a scrolling GUI 9 | # scrolling-type - The type of scrolling for the GUI [HORIZONTAL, VERTICAL] 10 | # update-title - Whether to update the title of the GUI on load, If this is set to false, the title will be the "pre-title" option until the page is changed. 11 | # add-pages-asynchronously - Whether to add pages asynchronously (This will improve performance, however, it may take a few seconds for the GUI to load) 12 | # 13 | # The general options for the customising itemstacks. 14 | # 15 | # material - The material of the reward. 16 | # amount - The amount of the reward. 17 | # name - The name of the reward. 18 | # lore - The logre of the reward, You can use the %description% placeholder to add the description of the tag. 19 | # glow - Whether the reward item should glow. 20 | # texture - The base64 texture of the reward item (Only for skulls), You can also use hdbLoader to load a texture if you have HeadDatabase installed. 21 | # potion-color - The color of the potion reward. (Only for potions) 22 | # model-data - The model data of the reward item. (Requires texture packs) 23 | # owner - The uuid of the player for the reward item (Only for skulls) 24 | # flags - The item flags for the reward item. 25 | # enchants - The enchantments for the reward item. 26 | # 27 | # Icon Actions 28 | # 29 | # Actions is an optional configuration option that can replace an item's functionality with a new one. 30 | # Available Actions: [BROADCAST, CLOSE, CONSOLE, MESSAGE, PLAYER, SOUND] 31 | # These actions can be defined in the `commands` section of the item, They require a ClickType to be defined. 32 | # Available ClickTypes: https://hub.spigotmc.org/javadocs/bukkit/org/bukkit/event/inventory/ClickType.html 33 | # Here is an example of how to use actions: 34 | # commands: 35 | # LEFT: 36 | # - '[message] You clicked the left button!' 37 | # RIGHT: 38 | # - '[message] You clicked the right button!' 39 | # MIDDLE: 40 | # - '[console] ban %player_name%' 41 | # GUI Settings 42 | gui-settings: 43 | pre-title: 'EternalTags Loading...' 44 | title: EternalTags | %page%/%total% 45 | rows: 5 46 | sort-type: CUSTOM 47 | favourite-first: true 48 | add-all-tags: false 49 | description-format: ' &f| &7' 50 | scrolling-gui: false 51 | scrolling-type: HORIZONTAL 52 | update-title: true 53 | add-pages-asynchronously: true 54 | 55 | # Tag Item - The item that represents each tag in the GUI 56 | tag-item: 57 | material: NAME_TAG 58 | amount: 1 59 | name: '%tag%' 60 | lore: 61 | - '' 62 | - '&f| #00B4DBLeft-Click &7on this' 63 | - '&f| &7icon to change your' 64 | - '&f| &7active tag!' 65 | - '&f| ' 66 | - '&f| #00B4DBShift-Click &7to add' 67 | - '&f| &7this tag to your favorites' 68 | - '' 69 | glow: true 70 | 71 | # Next Page Item - Changes the current page to the next page 72 | next-page: 73 | material: PAPER 74 | name: '#00B4DB&lNext Page' 75 | lore: 76 | - '' 77 | - '&f| #00B4DBLeft-Click &7to change ' 78 | - '&f| &7to the next page' 79 | - '' 80 | slot: 7 81 | 82 | # Previous Page Item - Changes the current page to the previous page 83 | previous-page: 84 | material: PAPER 85 | name: '#00B4DB&lPrevious Page' 86 | lore: 87 | - '' 88 | - '&f| #00B4DBLeft-Click &7to change ' 89 | - '&f| &7to the previous page' 90 | - '' 91 | slot: 1 92 | 93 | # Clear Tag Item - Clears the player's active tag 94 | clear-tag: 95 | enabled: true 96 | material: PLAYER_HEAD 97 | name: '#00B4DB&lClear Tag' 98 | lore: 99 | - '' 100 | - ' &f| #00B4DBLeft-Click &7to clear your' 101 | - ' &f| &7current active tag.' 102 | - ' &f| &7' 103 | - ' &f| &7Current Tag: #00B4DB%eternaltags_tag_formatted%' 104 | - '' 105 | texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZTljZGI5YWYzOGNmNDFkYWE1M2JjOGNkYTc2NjVjNTA5NjMyZDE0ZTY3OGYwZjE5ZjI2M2Y0NmU1NDFkOGEzMCJ9fX0= 106 | slot: 3 107 | 108 | # Search Item - Allows the player to search for tags 109 | search: 110 | enabled: true 111 | material: OAK_SIGN 112 | name: '#00B4DB&lSearch' 113 | lore: 114 | - '' 115 | - ' &f| #00B4DBLeft-Click &7to search' 116 | - ' &f| &7for a new tag in the menu.' 117 | - '' 118 | slot: 4 119 | 120 | # Favourites Tag Item - Shows the player's favourite tags 121 | favorite-tags: 122 | enabled: true 123 | material: PLAYER_HEAD 124 | name: '#00B4DB&lFavourite Tags' 125 | lore: 126 | - ' ' 127 | - ' &f| �B4DBLeft-Click &7to view' 128 | - ' &f| &7your #00B4DBfavourite tags&7.' 129 | - ' ' 130 | texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZDVjNmRjMmJiZjUxYzM2Y2ZjNzcxNDU4NWE2YTU2ODNlZjJiMTRkNDdkOGZmNzE0NjU0YTg5M2Y1ZGE2MjIifX19 131 | slot: 5 132 | 133 | # Extra Items - Allows you to add extra items to the GUI [These are placed in the gui first] 134 | extra-items: 135 | border-item: 136 | enabled: true 137 | material: GRAY_STAINED_GLASS_PANE 138 | name: ' ' 139 | slots: 140 | - 0-8 141 | - 36-44 142 | -------------------------------------------------------------------------------- /src/main/resources/plugin.yml: -------------------------------------------------------------------------------- 1 | name: EternalTags 2 | main: xyz.oribuin.eternaltags.EternalTags 3 | version: '@version@' 4 | author: Oribuin 5 | api-version: "1.16" 6 | description: A simple tag plugin alternative to other Tag plugins (With Hex Support) 7 | website: https://www.spigotmc.org/resources/eternaltags.91842/ 8 | libraries: 9 | - "net.kyori:adventure-api:4.14.0" 10 | - "net.kyori:adventure-text-minimessage:4.14.0" 11 | - "net.kyori:adventure-text-serializer-legacy:4.14.0" 12 | - "net.kyori:adventure-text-serializer-gson:4.14.0" 13 | folia-supported: true 14 | depend: 15 | - PlaceholderAPI 16 | softdepend: 17 | - Vault 18 | - Oraxen 19 | - HeadDatabase 20 | permissions: 21 | eternaltags.*: 22 | description: Allow the player to use all commands 23 | default: op 24 | children: 25 | - eternaltags.use 26 | - eternaltags.reload 27 | - eternaltags.categories 28 | - eternaltags.clear 29 | - eternaltags.clear.other 30 | - eternaltags.convert 31 | - eternaltags.create 32 | - eternaltags.delete 33 | - eternaltags.edit 34 | - eternaltags.favorite 35 | - eternaltags.random 36 | - eternaltags.search 37 | - eternaltags.set 38 | - eternaltags.set.other 39 | - eternaltags.setall 40 | - eternaltags.tags.* 41 | eternaltags.user: 42 | description: Allow the player to use all non-admin commands 43 | default: false 44 | children: 45 | - eternaltags.use 46 | - eternaltags.categories 47 | - eternaltags.clear 48 | - eternaltags.favorite 49 | - eternaltags.random 50 | - eternaltags.search 51 | - eternaltags.set 52 | eternaltags.tag.*: 53 | description: Allow the player to use all tags 54 | default: op 55 | eternaltags.use: 56 | description: Allow the player to open the tag menu 57 | default: true 58 | eternaltags.reload: 59 | description: Allow the player to reload the plugin 60 | default: op 61 | eternaltags.categories: 62 | description: Allow the player to view the categories 63 | default: false 64 | eternaltags.clear: 65 | description: Allow the player to clear their tag 66 | default: false 67 | eternaltags.clear.other: 68 | description: Allow the player to clear another player's tag 69 | default: op 70 | eternaltags.convert: 71 | description: Allow the player to convert their tags from another plugin 72 | default: op 73 | eternaltags.create: 74 | description: Allow the player to create a tag 75 | default: op 76 | eternaltags.delete: 77 | description: Allow the player to delete a tag 78 | default: op 79 | eternaltags.edit: 80 | description: Allow the player to edit a tag 81 | default: op 82 | eternaltags.favorite: 83 | description: Allow the player to favorite a tag 84 | default: false 85 | eternaltags.random: 86 | description: Allow the player to get a random tag 87 | default: false 88 | eternaltags.search: 89 | description: Allow the player to search for a tag 90 | default: false 91 | eternaltags.set: 92 | description: Allow the player to set their tag 93 | default: false 94 | eternaltags.set.other: 95 | description: Allow the player to set another player's tag 96 | default: op 97 | eternaltags.setall: 98 | description: Allow the player to set all tags 99 | default: op -------------------------------------------------------------------------------- /src/main/resources/tags.yml: -------------------------------------------------------------------------------- 1 | # Configure all the tags you want to use in your server here. 2 | # Here are all the options you can use 3 | # tags: 4 | # test: # The ID of the tag 5 | # name: Test # The name of the tag 6 | # description: # The description of the tag 7 | # - '&7This is a test tag' 8 | # - '&7It has multiple lines' 9 | # permission: # The permission required to use this tag 10 | # order: 1 # The order of the tag in the GUI 11 | # icon: # The icon of the tag, Item Names, Lore, Are Overwritten by the tag 12 | # material: PLAYER_HEAD # The material of the icon 13 | # texture: You can use a base64 texture here # The texture of the icon, if using HeadDB you can use 'hdb:' 14 | # glow: true # Whether the icon should glow (You can use a placeholder here too) 15 | # owner: # The owner of the icon, if using a player head (Set to 'self' to use the player's name) 16 | # model-data: 1 # The model data of the icon 17 | # enchantments: # The enchantments of the icon 18 | # DURABILITY: 1 # The enchantment and level 19 | # flags: # The item flags of the icon 20 | # - HIDE_ENCHANTS # The item flags 21 | tags: 22 | static-rainbow: 23 | name: 'Static Rainbow' 24 | tag: '&f&l<&lRainbow&f&l>' 25 | description: 26 | - ' &f| &7This tag is static!' 27 | - ' &f| &7It will always be the same' 28 | - ' &f| &7every time you send a message.' 29 | permission: 'eternaltags.tag.static-rainbow' 30 | order: 3 31 | category: static 32 | 33 | static-gradient: 34 | name: 'Static Gradient' 35 | tag: '&f&l<&lGradient&f&l>' 36 | description: 37 | - ' ' 38 | - ' &f| &7This tag is static!' 39 | - ' &f| &7It will always be the same' 40 | - ' &f| &7every time you send a message.' 41 | permission: 'eternaltags.tag.static-gradient' 42 | order: 4 43 | category: static 44 | 45 | blocks-broken: 46 | name: 'Blocks Broken' 47 | tag: '&f&l<<#20BDFF>Mined %statistic_mine_block% Blocks&f&l>' 48 | description: 49 | - ' ' 50 | - ' &f| &7This will display the amount of' 51 | - ' &f| &7blocks the player has broken' 52 | - ' &f| &7in the tag.' 53 | - ' ' 54 | - ' &f| &7This tag uses PlaceholderAPI' 55 | permission: 'eternaltags.tag.blocks-broken' 56 | order: 5 57 | category: placeholders 58 | 59 | mob-kills: 60 | name: 'Mob Kills' 61 | tag: '&f&l<<#20BDFF>Killed %statistic_mob_kills% Mobs&f&l>' 62 | description: 63 | - ' ' 64 | - ' &f| &7This will display the amount of' 65 | - ' &f| &7mobs the player has killed' 66 | - ' &f| &7in the tag.' 67 | - ' ' 68 | - ' &f| &7This tag uses PlaceholderAPI' 69 | permission: 'eternaltags.tag.mob-kills' 70 | order: 6 71 | category: placeholders 72 | 73 | playtime: 74 | name: 'Playtime' 75 | tag: '&f&l<<#20BDFF>%statistic_hours_played%h Playtime&f&l>' 76 | description: 77 | - ' ' 78 | - ' &f| &7This will display the amount of' 79 | - ' &f| &hours the player has played' 80 | - ' &f| &7in the tag.' 81 | - ' ' 82 | - ' &f| &7This tag uses PlaceholderAPI' 83 | permission: 'eternaltags.tag.playtime' 84 | order: 7 85 | category: placeholders 86 | 87 | smiley_face: 88 | name: 'Smiley Face' 89 | tag: '&f&l<#FFD700^-^&f&l>' 90 | description: 91 | - ' ' 92 | - ' &f| &7This tag uses unicode characters' 93 | - ' &f| &7to display a smiley face in the tag.' 94 | permission: 'eternaltags.tag.smiley_face' 95 | order: 8 96 | category: undefined 97 | --------------------------------------------------------------------------------