From b74ef3f36988cdf46b207bfd502155c8d76c6548 Mon Sep 17 00:00:00 2001 From: flurbudurbur <69259138+flurbudurbur@users.noreply.github.com> Date: Wed, 12 May 2021 22:11:59 +0200 Subject: [PATCH 01/27] Rankups.yml suggestion This is my version of the auto generated rankups.yml file that I feel better shows how to use Rankup and its features to new users. It shows the new users the basics. --- src/main/resources/rankups.yml | 70 +++++++++++++++++++--------------- 1 file changed, 40 insertions(+), 30 deletions(-) diff --git a/src/main/resources/rankups.yml b/src/main/resources/rankups.yml index a2ba234..4d540c1 100644 --- a/src/main/resources/rankups.yml +++ b/src/main/resources/rankups.yml @@ -5,40 +5,50 @@ # Join the discord server for live support: https://discord.gg/maB4382 (buyers only) # -# this name doesn't matter -Aexample: - # the name of the group - # players have to be in this rank to rankup - rank: 'A' - # the name of the rank a player can rankup to - next: 'B' - # List of requirements to go to the next rank - # This example will charge 1000 money to rankup from A to B. - # https://github.com/okx-code/Rankup3/wiki/List-of-Requirements - # custom requirements can also be added by other plugins. - requirements: - - 'money 1000' - # the console will run these commands when a player ranks up - # nb: groups are automatically changed with vault - #commands: - # this will run when a player ranks up from A to B. - #- 'say {PLAYER} well done for ranking up from {OLD_RANK} to {RANK}!' -Bexample: +ExampleA: # This name doesn't matter, it can be anything you want! + rank: 'A' # This is the rank where you start from. Use the displayname if you have it! + next: 'B' # This is the rank where you go next. Read it as "From A to B" + requirements: # Here go your requirements. You can have as many as you want. +# List of requirements: https://github.com/okx-code/Rankup3/wiki/List-of-Requirements + - 'money 5000' + - 'xp-level 5' + +ExampleB: rank: 'B' next: 'C' requirements: - - 'money 2500' -Cexample: + - 'money 10000' + - 'xp-level 10' + rankup: # requirements-not-met is a custom message that gets displayed when you DON'T meet the requirements. + requirements-not-met: "&3You need &d&n$10K &3and &d&n10 XP levels&3!" # It supports (RGB if 1.16+) color codes. + +ExampleC: rank: 'C' next: 'D' requirements: - - 'money 5000' - - 'xp-level 2' - # you can have a custom messages for each rank - # the paths of these messages are the same as in the messages for your locale + - 'money 15000' + - 'xp-level 12' + - 'total-mob-kills 15' + rankup: # Introduction to placeholders and multi-line messages! + requirements-not-met: |- # These 2 characters indicate to Rankup that it's a multi-line message. +# List of placeholders: https://github.com/okx-code/Rankup3/wiki/Config-Placeholders + &3You need: + &c- &d&n${AMOUNT money} + &c- &d&n{AMOUNT xp-level) &3XP levels + &c- &d&n{AMOUNT total-mob-kills} &3mob kills + &3 to reach &c&l{RANK}&3! + +ExampleD: + rank: 'D' + next: 'E' + requirements: + - 'moneyh 30000' # This requirement doesn't remove the money from the player's balance. + - 'mob-kills zombie 20' # This requirement has a sub-requirement. + - 'advancement story/iron_tools' rankup: - requirements-not-met: '&cYou need 5000 money and 2 levels of XP to rankup to D.' - list: - complete: "&7{OLD_RANK} &8\xbb &7{RANK} &e(5000 money, 2 XP levels)" - current: "&c{OLD_RANK} &e\xbb &c{RANK} &e(5000 money, 2 XP levels)" - incomplete: "&r{OLD_RANK} &e\xbb &r{RANK} &e(5000 money, 2 XP levels)" \ No newline at end of file + requirements-not-met: |- + &3You need: + &c- &d&n${AMOUNT moneyh} + &c- &d&n{AMOUNT mob-kills#zombie} &3zombie kills + &c- &d&nThe "isn't it an iron pick" &3advancement. + &3 to reach &c&l{RANK}&3! From cfa7ec2ce1b2876d452ad3210536472654f1ce30 Mon Sep 17 00:00:00 2001 From: NicBOMB <19939315+NicBOMB@users.noreply.github.com> Date: Wed, 26 Oct 2022 15:50:33 -0700 Subject: [PATCH 02/27] suggestion fixes --- README.md | 2 +- src/main/resources/config.yml | 7 ++-- src/main/resources/rankups.yml | 63 ++++++++++++++++++---------------- 3 files changed, 38 insertions(+), 34 deletions(-) diff --git a/README.md b/README.md index fcaf10e..ee0cced 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ ## Installation -Rankup can be compiled with the `gradlew build` command. The compiled plugin jar will be in the `/build/libs` directory. A gradle wrapper is included, so installation of Gradle is not necessary. +Compilation requires the jre8 (java runtime environment) and jdk8 (java development kit). Rankup can be compiled with the `gradlew build` command. The compiled plugin jar will be in the `/build/libs` directory. A gradle wrapper is included, so installation of Gradle is not necessary. ## Translation diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index beb731f..6b80070 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -42,8 +42,9 @@ notify-update: true # if false, players will be checked for if they have a group of the same name as in rankups.yml, # and automatically added and taken away from those groups. # if true, players will be checked for the permission rankup.rank.RANK, where RANK -# is the rankup in rankups.yml. Nothing will automatically happen on rankup, so you must -# use commands to change a player's group or permission. +# is the rankup in rankups.yml. +# true also enables MANUAL MODE: Nothing will automatically change on rankup. +# You must use 'commands:' to change a player's group or permission when true. https://github.com/okx-code/Rankup3/wiki/How-to-rankups.yml-and-prestiges.yml#1-commands permission-rankup: false # how people should confirm ranking up @@ -107,4 +108,4 @@ shorten: - 'T' - 'Q' - 'Qu' -- 'S' \ No newline at end of file +- 'S' diff --git a/src/main/resources/rankups.yml b/src/main/resources/rankups.yml index 4d540c1..4ea147d 100644 --- a/src/main/resources/rankups.yml +++ b/src/main/resources/rankups.yml @@ -1,27 +1,32 @@ # # If you are adding your own ranks, make sure to delete the example ranks! # Need help setting the plugin up? -# Read an example: https://github.com/okx-code/Rankup3/wiki/Configuration-Example # Join the discord server for live support: https://discord.gg/maB4382 (buyers only) +# Read a Configuration Example: https://github.com/okx-code/Rankup3/wiki/Configuration-Example#minimum-requirements-to-use-rankup +# Documentation of this file: https://github.com/okx-code/Rankup3/wiki/How-to-rankups.yml-and-prestiges.yml#how-to-rankups +# Read an Advanced Example: https://github.com/okx-code/Rankup3/wiki/Advanced-Configuration-Example#what-more # - -ExampleA: # This name doesn't matter, it can be anything you want! - rank: 'A' # This is the rank where you start from. Use the displayname if you have it! - next: 'B' # This is the rank where you go next. Read it as "From A to B" +# The following name is never seen in-game. https://github.com/okx-code/Rankup3/wiki/How-to-rankups.yml-and-prestiges.yml#1-heading +ExampleA: # It can say anything, but must be unique from all other section names. Headings with duplicate names may cause a multiple root nodes error. + rank: 'A' # This is the group/permission players must start at. https://github.com/okx-code/Rankup3/wiki/How-to-rankups.yml-and-prestiges.yml#2-rank + next: 'B' # This is the group/permission players move up to. Use the part from "displayname." if you use displaynames!https://github.com/okx-code/Rankup3/wiki/How-to-rankups.yml-and-prestiges.yml#3-next + # Read it as "Rankup from A to B". Luckperms starts players in group 'default'. requirements: # Here go your requirements. You can have as many as you want. -# List of requirements: https://github.com/okx-code/Rankup3/wiki/List-of-Requirements - - 'money 5000' - - 'xp-level 5' - -ExampleB: + - 'money 5000' # player must pay 5000 + - 'xp-level 5' # player must pay 5 levels + # List of requirements: https://github.com/okx-code/Rankup3/wiki/List-of-Requirements +ExampleBee: # mob-kills requirement example rank: 'B' next: 'C' - requirements: - - 'money 10000' - - 'xp-level 10' - rankup: # requirements-not-met is a custom message that gets displayed when you DON'T meet the requirements. - requirements-not-met: "&3You need &d&n$10K &3and &d&n10 XP levels&3!" # It supports (RGB if 1.16+) color codes. - + requirements: # https://github.com/okx-code/Rankup3/wiki/How-to-rankups.yml-and-prestiges.yml#4-requirements + # https://github.com/okx-code/Rankup3/wiki/List-of-Requirements#how-to-specify-sub-requirements-in-placeholders + - 'mob-kills Bee 1' # https://hub.spigotmc.org/javadocs/spigot/org/bukkit/entity/package-summary.html + # https://github.com/okx-code/Rankup3/wiki/List-of-Requirements#list + - 'moneyh 10000' # This requirement only checks the player's balance rather than require the value as payment. + - 'xp-levelh 10' # This requirement only checks the player's level rather than require the value as payment. + rankup: # https://github.com/okx-code/Rankup3/wiki/How-to-rankups.yml-and-prestiges.yml#3-rankup + requirements-not-met: "&3You need &d&n$10K &3and &d&n10 XP levels&3!" # Supports RGB color codes like #&hexnum if 1.16+. Otherwise, use old native or bukkit codes like §x§9§D§6§5§F§F + # requirements-not-met is a custom message displayed when the player fails to meet all the prior listed requirements. ExampleC: rank: 'C' next: 'D' @@ -29,26 +34,24 @@ ExampleC: - 'money 15000' - 'xp-level 12' - 'total-mob-kills 15' - rankup: # Introduction to placeholders and multi-line messages! - requirements-not-met: |- # These 2 characters indicate to Rankup that it's a multi-line message. -# List of placeholders: https://github.com/okx-code/Rankup3/wiki/Config-Placeholders + rankup: # Information on Customized Messsages https://github.com/okx-code/Rankup3/wiki/Configuration-Example#wrong-message + requirements-not-met: |- # These 2 characters indicate a multi-line message. &3You need: &c- &d&n${AMOUNT money} - &c- &d&n{AMOUNT xp-level) &3XP levels + &c- &d&n{AMOUNT xp-level} &3XP levels &c- &d&n{AMOUNT total-mob-kills} &3mob kills &3 to reach &c&l{RANK}&3! - + # All correctly indented lines following those 2 characters, will be included in the message. + # These comments are not identically indented, and therefore will not appear in the message output! + # FAQ for multi-line messages: https://github.com/okx-code/Rankup3/wiki/FAQ#how-do-i-write-multi-line-messages + # We used config-placeholders here to autofill the message with the requirement values! + # This lists all placeholders: https://github.com/okx-code/Rankup3/wiki/Config-Placeholders ExampleD: rank: 'D' next: 'E' requirements: - - 'moneyh 30000' # This requirement doesn't remove the money from the player's balance. - - 'mob-kills zombie 20' # This requirement has a sub-requirement. - - 'advancement story/iron_tools' + - 'advancement story/iron_tools' # https://minecraft.fandom.com/wiki/Advancement#List_of_advancements + - 'block-break STONE 1000' # only available in versions 1.13+ https://github.com/okx-code/Rankup3/wiki/FAQ#what-rankup-version-do-i-need-for-minecraft-version-117116115114113112111110191817 rankup: - requirements-not-met: |- - &3You need: - &c- &d&n${AMOUNT moneyh} - &c- &d&n{AMOUNT mob-kills#zombie} &3zombie kills - &c- &d&nThe "isn't it an iron pick" &3advancement. - &3 to reach &c&l{RANK}&3! + requirements-not-met: 'You have {AMOUNT_DONE block-break#STONE}/{AMOUNT block-break#stone}.\n{AMOUNT_NEEDED block-break#STONE} stone left to break.' + # FAQ for multi-line messages: https://github.com/okx-code/Rankup3/wiki/FAQ#how-do-i-write-multi-line-messages From 7eab157127df452f839164ce89343ad6d0c7649e Mon Sep 17 00:00:00 2001 From: NicBOMB <19939315+NicBOMB@users.noreply.github.com> Date: Sat, 5 Feb 2022 12:57:25 -0800 Subject: [PATCH 03/27] Merge branch 'master' into new-rankups.yml --- .github/workflows/pitest.yml | 28 ++ .github/workflows/test.yml | 28 ++ build.gradle | 86 +++- gradle/wrapper/gradle-wrapper.jar | Bin 55616 -> 59203 bytes gradle/wrapper/gradle-wrapper.properties | 11 +- gradlew | 373 +++++++++--------- gradlew.bat | 27 +- src/main/java/sh/okx/rankup/RankupHelper.java | 65 +-- src/main/java/sh/okx/rankup/RankupPlugin.java | 150 +++---- .../sh/okx/rankup/commands/InfoCommand.java | 56 ++- .../okx/rankup/commands/PrestigeCommand.java | 7 +- .../okx/rankup/commands/PrestigesCommand.java | 7 +- .../sh/okx/rankup/commands/RankupCommand.java | 13 +- src/main/java/sh/okx/rankup/gui/Gui.java | 63 ++- .../java/sh/okx/rankup/messages/Message.java | 4 +- .../okx/rankup/messages/MessageBuilder.java | 157 +------- .../rankup/messages/NullMessageBuilder.java | 35 +- .../rankup/messages/StringMessageBuilder.java | 125 ++++++ .../java/sh/okx/rankup/messages/Variable.java | 10 +- .../pebble/InvalidRequirementException.java | 22 ++ .../messages/pebble/PebbleMessageBuilder.java | 142 +++++++ .../messages/pebble/PrestigeContext.java | 24 ++ .../rankup/messages/pebble/RankContext.java | 82 ++++ .../messages/pebble/RequirementContext.java | 47 +++ .../okx/rankup/placeholders/Placeholders.java | 19 + .../rankup/placeholders/RankupExpansion.java | 46 ++- .../java/sh/okx/rankup/prestige/Prestige.java | 15 +- .../sh/okx/rankup/prestige/Prestiges.java | 17 +- src/main/java/sh/okx/rankup/ranks/Rank.java | 10 +- .../java/sh/okx/rankup/ranks/RankList.java | 49 +-- src/main/java/sh/okx/rankup/ranks/Rankup.java | 26 +- .../java/sh/okx/rankup/ranks/Rankups.java | 16 +- .../requirements/LastRankRequirements.java | 5 +- .../requirements/ListRankRequirements.java | 8 +- .../PrestigeListRankRequirements.java | 8 +- .../ranks/requirements/RankRequirements.java | 3 +- .../requirements/RankRequirementsFactory.java | 23 +- .../java/sh/okx/rankup/ranksgui/RanksGui.java | 7 +- .../requirements/ProgressiveRequirement.java | 8 +- .../requirements/RequirementRegistry.java | 6 +- .../ItemDeductibleRequirement.java | 15 +- .../requirement/ItemRequirement.java | 24 +- .../requirement/PlaceholderRequirement.java | 20 +- .../requirement/mcmmo/McMMOSkillUtil.java | 3 +- .../serialization/PrestigeSerialized.java | 25 ++ .../rankup/serialization/RankSerialized.java | 31 ++ .../serialization/ShadowDeserializer.java | 69 ++++ .../serialization/YamlDeserializer.java | 59 +++ .../okx/rankup/text/ChainedTextProcessor.java | 17 + .../okx/rankup/text/ColourTextProcessor.java | 11 + .../okx/rankup/text/LegacyTextProcessor.java | 107 +++++ .../text/PlaceholderApiTextProcessor.java | 23 ++ .../sh/okx/rankup/text/TextProcessor.java | 5 + .../okx/rankup/text/TextProcessorBuilder.java | 38 ++ .../text/pebble/DecimalFormatFilter.java | 38 ++ .../rankup/text/pebble/MoneyShortFilter.java | 38 ++ .../text/pebble/PebbleTextProcessor.java | 82 ++++ src/main/resources/config.yml | 5 +- src/main/resources/locale/en.yml | 66 ++-- src/main/resources/locale/es.yml | 59 ++- src/main/resources/locale/fr.yml | 59 ++- src/main/resources/locale/it.yml | 63 ++- src/main/resources/locale/nl.yml | 118 ++++++ src/main/resources/locale/pt_br.yml | 59 ++- src/main/resources/locale/ru.yml | 57 ++- src/main/resources/locale/zh_cn.yml | 63 ++- src/main/resources/plugin.yml | 5 +- src/main/resources/prestiges.yml | 1 - src/main/resources/rankups.yml | 79 ++-- .../java/sh/okx/rankup/RankupBasicsTest.java | 68 ++++ .../sh/okx/rankup/RankupCommandsTest.java | 5 + .../sh/okx/rankup/RankupPlaceholderTest.java | 5 +- src/test/java/sh/okx/rankup/RankupTest.java | 132 +++---- .../rankup/commands/ComandPlaytimeTest.java | 56 +++ .../CommandInfoTest.java} | 12 +- .../rankup/legacy/LegacyPlaceholderTest.java | 27 ++ .../rankup/messages/MessageBuilderTest.java | 8 +- .../messages/RankupPlaceholderTest.java | 50 +++ .../java/sh/okx/rankup/pebble/PebbleTest.java | 45 +++ .../rankup/prestige/BrokenPrestigeTest.java | 25 ++ .../{economy => providers}/TestEconomy.java | 5 +- .../TestEconomyProvider.java | 5 +- .../TestGroupProvider.java | 3 +- .../TestPermissionManager.java | 5 +- .../sh/okx/rankup/ranksgui/RanksGuiTest.java | 40 ++ .../PrestigeRequirementsTest.java | 29 ++ .../java/sh/okx/rankup/toml/TomlTest.java | 55 +++ src/test/resources/brokenprestige/config.yml | 36 ++ src/test/resources/brokenprestige/rankups.yml | 10 + src/test/resources/legacy/rankups.yml | 10 + src/test/resources/placeholder/rankups.yml | 7 + .../resources/prestigerequirements/config.yml | 111 ++++++ .../prestigerequirements/prestiges.yml | 6 + .../prestigerequirements/rankups.yml | 8 + src/test/resources/ranksgui/config.yml | 111 ++++++ src/test/resources/ranksgui/rankups.yml | 6 + src/test/resources/toml/rankups.toml | 25 ++ 97 files changed, 2942 insertions(+), 1030 deletions(-) create mode 100644 .github/workflows/pitest.yml create mode 100644 .github/workflows/test.yml create mode 100644 src/main/java/sh/okx/rankup/messages/StringMessageBuilder.java create mode 100644 src/main/java/sh/okx/rankup/messages/pebble/InvalidRequirementException.java create mode 100644 src/main/java/sh/okx/rankup/messages/pebble/PebbleMessageBuilder.java create mode 100644 src/main/java/sh/okx/rankup/messages/pebble/PrestigeContext.java create mode 100644 src/main/java/sh/okx/rankup/messages/pebble/RankContext.java create mode 100644 src/main/java/sh/okx/rankup/messages/pebble/RequirementContext.java create mode 100644 src/main/java/sh/okx/rankup/serialization/PrestigeSerialized.java create mode 100644 src/main/java/sh/okx/rankup/serialization/RankSerialized.java create mode 100644 src/main/java/sh/okx/rankup/serialization/ShadowDeserializer.java create mode 100644 src/main/java/sh/okx/rankup/serialization/YamlDeserializer.java create mode 100644 src/main/java/sh/okx/rankup/text/ChainedTextProcessor.java create mode 100644 src/main/java/sh/okx/rankup/text/ColourTextProcessor.java create mode 100644 src/main/java/sh/okx/rankup/text/LegacyTextProcessor.java create mode 100644 src/main/java/sh/okx/rankup/text/PlaceholderApiTextProcessor.java create mode 100644 src/main/java/sh/okx/rankup/text/TextProcessor.java create mode 100644 src/main/java/sh/okx/rankup/text/TextProcessorBuilder.java create mode 100644 src/main/java/sh/okx/rankup/text/pebble/DecimalFormatFilter.java create mode 100644 src/main/java/sh/okx/rankup/text/pebble/MoneyShortFilter.java create mode 100644 src/main/java/sh/okx/rankup/text/pebble/PebbleTextProcessor.java create mode 100644 src/main/resources/locale/nl.yml create mode 100644 src/test/java/sh/okx/rankup/RankupBasicsTest.java create mode 100644 src/test/java/sh/okx/rankup/RankupCommandsTest.java create mode 100644 src/test/java/sh/okx/rankup/commands/ComandPlaytimeTest.java rename src/test/java/sh/okx/rankup/{RankupCommandTest.java => commands/CommandInfoTest.java} (77%) create mode 100644 src/test/java/sh/okx/rankup/legacy/LegacyPlaceholderTest.java create mode 100644 src/test/java/sh/okx/rankup/messages/RankupPlaceholderTest.java create mode 100644 src/test/java/sh/okx/rankup/pebble/PebbleTest.java create mode 100644 src/test/java/sh/okx/rankup/prestige/BrokenPrestigeTest.java rename src/test/java/sh/okx/rankup/{economy => providers}/TestEconomy.java (81%) rename src/test/java/sh/okx/rankup/{economy => providers}/TestEconomyProvider.java (56%) rename src/test/java/sh/okx/rankup/{hook => providers}/TestGroupProvider.java (89%) rename src/test/java/sh/okx/rankup/{hook => providers}/TestPermissionManager.java (78%) create mode 100644 src/test/java/sh/okx/rankup/ranksgui/RanksGuiTest.java create mode 100644 src/test/java/sh/okx/rankup/requirements/PrestigeRequirementsTest.java create mode 100644 src/test/java/sh/okx/rankup/toml/TomlTest.java create mode 100644 src/test/resources/brokenprestige/config.yml create mode 100644 src/test/resources/brokenprestige/rankups.yml create mode 100644 src/test/resources/legacy/rankups.yml create mode 100644 src/test/resources/placeholder/rankups.yml create mode 100644 src/test/resources/prestigerequirements/config.yml create mode 100644 src/test/resources/prestigerequirements/prestiges.yml create mode 100644 src/test/resources/prestigerequirements/rankups.yml create mode 100644 src/test/resources/ranksgui/config.yml create mode 100644 src/test/resources/ranksgui/rankups.yml create mode 100644 src/test/resources/toml/rankups.toml diff --git a/.github/workflows/pitest.yml b/.github/workflows/pitest.yml new file mode 100644 index 0000000..45fee65 --- /dev/null +++ b/.github/workflows/pitest.yml @@ -0,0 +1,28 @@ +name: PIT test +on: + push: + paths: + - 'src/**' + +jobs: + test: + if: github.ref == 'refs/heads/master' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Setup JDK + uses: actions/setup-java@v2 + with: + java-version: 11 + distribution: 'adopt' + - name: Gradle Test + run: ./gradlew pitest --no-daemon + - name: Upload Tests + uses: easingthemes/ssh-deploy@v2 + env: + SSH_PRIVATE_KEY: ${{ secrets.SERVER_SSH_KEY }} + ARGS: "-rvzt --delete" + SOURCE: "build/reports/pitest/" + REMOTE_HOST: ${{ secrets.REMOTE_HOST }} + REMOTE_USER: ${{ secrets.REMOTE_USER }} + TARGET: ${{ secrets.PITEST_TARGET }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..b05ace7 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,28 @@ +name: Test +on: + push: + paths: + - 'src/**' + +jobs: + pitest: + if: github.ref == 'refs/heads/master' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Setup JDK + uses: actions/setup-java@v2 + with: + java-version: 11 + distribution: 'adopt' + - name: Gradle Test + run: ./gradlew test --no-daemon + - name: Upload Tests + uses: easingthemes/ssh-deploy@v2 + env: + SSH_PRIVATE_KEY: ${{ secrets.SERVER_SSH_KEY }} + ARGS: "-rvzt --delete" + SOURCE: "build/reports/tests/test/" + REMOTE_HOST: ${{ secrets.REMOTE_HOST }} + REMOTE_USER: ${{ secrets.REMOTE_USER }} + TARGET: ${{ secrets.REMOTE_TARGET }} diff --git a/build.gradle b/build.gradle index 425c0b2..4dde130 100644 --- a/build.gradle +++ b/build.gradle @@ -1,10 +1,25 @@ plugins { id 'java' - id "io.freefair.lombok" version "5.1.0" + id 'com.github.johnrengelman.shadow' version '6.1.0' + id "io.freefair.lombok" version "6.0.0-m2" + id "maven-publish" + id 'info.solidsoft.pitest' version '1.7.0' +} + +pitest { + junit5PluginVersion = '0.15' + excludedClasses = ["sh.okx.rankup.Metrics*"] + timestampedReports = false + threads = 2 } group 'sh.okx' -version '3.11.3' +version '3.13.2' + +java { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 +} repositories { mavenCentral() @@ -23,34 +38,56 @@ repositories { } dependencies { - testImplementation group: 'junit', name: 'junit', version: '4.12' - testImplementation 'com.github.seeseemelk:MockBukkit-v1.15:0.3.0' + testImplementation 'org.junit.platform:junit-platform-launcher:1.8.2' + testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.8.2' + testImplementation 'com.github.MockBukkit:MockBukkit:04889261630cd6f5aaebd86a576bbcd12c442ea7' + + implementation group: 'org.slf4j', name: 'slf4j-nop', version: '1.7.30' + implementation group: 'org.slf4j', name: 'slf4j-api', version: '1.7.30' - compileOnly 'org.jetbrains:annotations:16.0.2' - implementation 'org.spigotmc:spigot-api:1.16.4-R0.1-SNAPSHOT' - implementation('com.github.Realizedd:TokenManager:3.2.4') { + compileOnly 'org.jetbrains:annotations:22.0.0' + compileOnly 'org.spigotmc:spigot-api:1.18-R0.1-SNAPSHOT' + compileOnly('com.github.Realizedd:TokenManager:3.2.4') { transitive = false } - implementation('com.github.MilkBowl:VaultAPI:1.7') { + compileOnly('com.github.MilkBowl:VaultAPI:1.7') { exclude group: 'org.bukkit' } - implementation ('me.clip:placeholderapi:2.10.9') { + compileOnly ('me.clip:placeholderapi:2.10.9') { exclude group: 'org.bstats' } - implementation 'com.github.pyvesb:advanced-achievements:6.7.2' - implementation 'com.github.astei:Superbvote:700fca43659b438cb9bb36c218a7646d2f2ef315' - implementation('com.github.mcMMO-Dev:mcMMO:601297') { + compileOnly 'com.github.pyvesb:advanced-achievements:6.7.2' + compileOnly 'com.github.astei:Superbvote:700fca43659b438cb9bb36c218a7646d2f2ef315' + compileOnly('com.github.mcMMO-Dev:mcMMO:601297') { exclude group: 'com.sk89q.worldguard' } - implementation 'com.github.BenCodez:VotingPlugin:6.0' - implementation 'com.github.LlmDl:Towny:25fc18a' + compileOnly 'com.github.BenCodez:VotingPlugin:6.8.1' + + compileOnly 'com.github.LlmDl:Towny:25fc18a' + testImplementation 'com.github.LlmDl:Towny:25fc18a' + + implementation ('io.pebbletemplates:pebble:3.1.5') { + exclude group: 'org.slf4j' + } + implementation 'com.electronwill.night-config:toml:3.6.4' +} + +artifacts { + archives shadowJar +} + +shadowJar { + archiveClassifier.set('') + minimize() + relocate 'org.slf4j', 'sh.okx.rankup.export.org.slf4j' } // automatically copy the version to plugin.yml processResources { // do this again whenever version changes inputs.property 'version', project.version + duplicatesStrategy = 'include' // copy plugin.yml and replace version from(sourceSets.main.resources.srcDirs) { @@ -64,8 +101,25 @@ processResources { } } -task spigot(type: Jar) { +task spigot(type: com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { from sourceSets.main.runtimeClasspath destinationDirectory = file("./spigot/plugins/") archiveFileName = "Rankup.jar" -} \ No newline at end of file +} + +test { + useJUnitPlatform() + testLogging { + events "passed", "skipped", "failed" + } +} + + +publishing { + publications { + maven(MavenPublication) { + from components.java + } + } +} + diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 5c2d1cf016b3885f6930543d57b744ea8c220a1a..e708b1c023ec8b20f512888fe07c5bd3ff77bb8f 100644 GIT binary patch delta 23334 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 WDWbsF$p{Gi@ZUasP|4|kdH)CXgbPdn delta 19998 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 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 4c9e569..69a9715 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,5 @@ -#Sun Apr 05 14:05:23 BST 2020 -distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-all.zip -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -zipStorePath=wrapper/dists -zipStoreBase=GRADLE_USER_HOME +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.1-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index cba68cc..4f906e0 100755 --- a/gradlew +++ b/gradlew @@ -1,188 +1,185 @@ -#!/usr/bin/env sh - -# -# Copyright 2015 the original author or authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -############################################################################## -## -## Gradle start up script for UN*X -## -############################################################################## - -# Attempt to set APP_HOME -# Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi -done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null - -APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" - -warn () { - echo "$*" -} - -die () { - echo - echo "$*" - echo - exit 1 -} - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -nonstop=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; - NONSTOP* ) - nonstop=true - ;; -esac - -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" - else - JAVACMD="$JAVA_HOME/bin/java" - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD="java" - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." -fi - -# Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi -fi - -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi - -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi - # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" - fi - i=$((i+1)) - done - case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac -fi - -# Escape application args -save () { - for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done - echo " " -} -APP_ARGS=$(save "$@") - -# Collect all arguments for the java command, following the shell quoting and substitution rules -eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" - -# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong -if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then - cd "$(dirname "$0")" -fi - -exec "$JAVACMD" "$@" +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index 15e1ee3..ac1b06f 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -5,7 +5,7 @@ @rem you may not use this file except in compliance with the License. @rem You may obtain a copy of the License at @rem -@rem http://www.apache.org/licenses/LICENSE-2.0 +@rem https://www.apache.org/licenses/LICENSE-2.0 @rem @rem Unless required by applicable law or agreed to in writing, software @rem distributed under the License is distributed on an "AS IS" BASIS, @@ -29,6 +29,9 @@ if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" @@ -37,7 +40,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init +if "%ERRORLEVEL%" == "0" goto execute echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. @@ -51,7 +54,7 @@ goto fail set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe -if exist "%JAVA_EXE%" goto init +if exist "%JAVA_EXE%" goto execute echo. echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% @@ -61,28 +64,14 @@ echo location of your Java installation. goto fail -:init -@rem Get command-line arguments, handling Windows variants - -if not "%OS%" == "Windows_NT" goto win9xME_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* - :execute @rem Setup the command line set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* :end @rem End local scope for the variables with windows NT shell diff --git a/src/main/java/sh/okx/rankup/RankupHelper.java b/src/main/java/sh/okx/rankup/RankupHelper.java index c6800e0..d96ec20 100644 --- a/src/main/java/sh/okx/rankup/RankupHelper.java +++ b/src/main/java/sh/okx/rankup/RankupHelper.java @@ -10,7 +10,6 @@ import sh.okx.rankup.events.PlayerRankupEvent; import sh.okx.rankup.hook.GroupProvider; import sh.okx.rankup.messages.Message; -import sh.okx.rankup.messages.Variable; import sh.okx.rankup.prestige.Prestige; import sh.okx.rankup.prestige.Prestiges; import sh.okx.rankup.ranks.Rank; @@ -51,11 +50,15 @@ public void doRankup(Player player, RankElement rank) { public void sendRankupMessages(Player player, RankElement rank) { plugin.getMessage(rank.getRank(), Message.SUCCESS_PUBLIC) .failIfEmpty() - .replaceRanks(player, rank.getRank(), rank.getNext().getRank()) + .replacePlayer(player) + .replaceOldRank(rank.getRank()) + .replaceRank(rank.getNext().getRank()) .broadcast(); plugin.getMessage(rank.getRank(), Message.SUCCESS_PRIVATE) .failIfEmpty() - .replaceRanks(player, rank.getRank(), rank.getNext().getRank()) + .replacePlayer(player) + .replaceOldRank(rank.getRank()) + .replaceRank(rank.getNext().getRank()) .send(player); } @@ -81,13 +84,15 @@ public void sendPrestigeMessages(Player player, RankElement prestige) plugin.getMessage(prestige.getRank(), Message.PRESTIGE_SUCCESS_PUBLIC) .failIfEmpty() - .replaceRanks(player, prestige.getRank(), prestige.getNext().getRank()) - .replaceFromTo(prestige.getRank()) + .replacePlayer(player) + .replaceOldRank(prestige.getRank()) + .replaceRank(prestige.getNext().getRank()) .broadcast(); plugin.getMessage(prestige.getRank(), Message.PRESTIGE_SUCCESS_PRIVATE) .failIfEmpty() - .replaceRanks(player, prestige.getRank(), prestige.getNext().getRank()) - .replaceFromTo(prestige.getRank()) + .replacePlayer(player) + .replaceOldRank(prestige.getRank()) + .replaceRank(prestige.getNext().getRank()) .send(player); } @@ -102,10 +107,9 @@ private boolean checkCooldown(Player player, Rank rank) { plugin .getMessage(rank, secondsLeft > 1 ? Message.COOLDOWN_PLURAL : Message.COOLDOWN_SINGULAR) .failIfEmpty() - .replaceRanks(player, rank) - .replaceFromTo(rank) - .replace(Variable.SECONDS, cooldownSeconds) - .replace(Variable.SECONDS_LEFT, secondsLeft) + .replacePlayer(player) + .replaceRank(rank) + .replaceSeconds(cooldownSeconds, secondsLeft) .send(player); return true; } @@ -151,22 +155,32 @@ public boolean checkRankup(Player player, boolean message) { if (rankElement == null) { // check if in ladder plugin.getMessage(Message.NOT_IN_LADDER) .failIf(!message) - .replace(Variable.PLAYER, player.getName()) + .replacePlayer(player) .send(player); return false; } Rank rank = rankElement.getRank(); if (!rankElement.hasNext()) { Prestiges prestiges = plugin.getPrestiges(); - plugin.getMessage(prestiges == null || !prestiges.getByPlayer(player).hasNext() ? Message.NO_RANKUP : Message.MUST_PRESTIGE) + Message pMessage = Message.NO_RANKUP; + if (prestiges != null) { + RankElement byPlayer = prestiges.getByPlayer(player); + if (byPlayer != null && byPlayer.hasNext()) { + pMessage = Message.MUST_PRESTIGE; + } + } + plugin.getMessage(pMessage) .failIf(!message) - .replaceRanks(player, rankups.getTree().last().getRank()) + .replacePlayer(player) + .replaceRank(rankups.getTree().last().getRank()) .send(player); return false; } else if (!rank.hasRequirements(player)) { // check if they can afford it if (message) { - plugin.replaceMoneyRequirements(plugin.getMessage(rank, Message.REQUIREMENTS_NOT_MET) - .replaceRanks(player, rank, rankElement.getNext().getRank()), player, rank) + plugin.getMessage(rank, Message.REQUIREMENTS_NOT_MET) + .replacePlayer(player) + .replaceOldRank(rank) + .replaceRank(rankElement.getNext().getRank()) .send(player); } return false; @@ -198,25 +212,26 @@ public boolean checkPrestige(Player player) { public boolean checkPrestige(Player player, boolean message) { Prestiges prestiges = plugin.getPrestiges(); RankElement prestigeElement = prestiges.getByPlayer(player); - if (prestigeElement == null || !prestigeElement.getRank().isEligible(player)) { // check if in ladder + if (prestigeElement == null + || !prestigeElement.getRank().isEligible(player)) { // check if in ladder plugin.getMessage(Message.NOT_HIGH_ENOUGH) .failIf(!message) - .replace(Variable.PLAYER, player.getName()) + .replacePlayer(player) .send(player); return false; } else if (!prestigeElement.hasNext()) { // check if they are at the highest rank plugin.getMessage(prestigeElement.getRank(), Message.PRESTIGE_NO_PRESTIGE) .failIf(!message) - .replaceRanks(player, prestigeElement.getRank()) - .replaceFromTo(prestigeElement.getRank()) + .replacePlayer(player) + .replaceRank(prestigeElement.getRank()) .send(player); return false; } else if (!prestigeElement.getRank().hasRequirements(player)) { // check if they can afford it - plugin.replaceMoneyRequirements( - plugin.getMessage(prestigeElement.getRank(), Message.PRESTIGE_REQUIREMENTS_NOT_MET) - .failIf(!message) - .replaceRanks(player, prestigeElement.getRank(), prestigeElement.getNext().getRank()), player, prestigeElement.getRank()) - .replaceFromTo(prestigeElement.getRank()) + plugin.getMessage(prestigeElement.getRank(), Message.PRESTIGE_REQUIREMENTS_NOT_MET) + .failIf(!message) + .replacePlayer(player) + .replaceOldRank(prestigeElement.getRank()) + .replaceRank(prestigeElement.getNext().getRank()) .send(player); return false; } else if (checkCooldown(player, prestigeElement.getRank())) { diff --git a/src/main/java/sh/okx/rankup/RankupPlugin.java b/src/main/java/sh/okx/rankup/RankupPlugin.java index f7a042f..aa25570 100644 --- a/src/main/java/sh/okx/rankup/RankupPlugin.java +++ b/src/main/java/sh/okx/rankup/RankupPlugin.java @@ -1,11 +1,12 @@ package sh.okx.rankup; +import com.electronwill.nightconfig.toml.TomlFormat; import java.io.File; -import java.text.DecimalFormat; +import java.io.FileNotFoundException; +import java.io.FileReader; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.function.Supplier; import lombok.Getter; import org.bukkit.Bukkit; import org.bukkit.ChatColor; @@ -37,8 +38,7 @@ import sh.okx.rankup.hook.VaultPermissionManager; import sh.okx.rankup.messages.Message; import sh.okx.rankup.messages.MessageBuilder; -import sh.okx.rankup.messages.NullMessageBuilder; -import sh.okx.rankup.messages.Variable; +import sh.okx.rankup.messages.pebble.PebbleMessageBuilder; import sh.okx.rankup.placeholders.Placeholders; import sh.okx.rankup.prestige.Prestige; import sh.okx.rankup.prestige.Prestiges; @@ -83,12 +83,15 @@ import sh.okx.rankup.requirements.requirement.votingplugin.VotingPluginPointsDeductibleRequirement; import sh.okx.rankup.requirements.requirement.votingplugin.VotingPluginPointsRequirement; import sh.okx.rankup.requirements.requirement.votingplugin.VotingPluginVotesRequirement; +import sh.okx.rankup.serialization.RankSerialized; +import sh.okx.rankup.serialization.ShadowDeserializer; +import sh.okx.rankup.serialization.YamlDeserializer; import sh.okx.rankup.util.UpdateNotifier; import sh.okx.rankup.util.VersionChecker; public class RankupPlugin extends JavaPlugin { - public static final int CONFIG_VERSION = 8; + public static final int CONFIG_VERSION = 10; @Getter private GroupProvider permissions; @@ -132,7 +135,7 @@ public void onEnable() { reload(true); - if (System.getProperty("TEST") == null) { + if (System.getProperty("RANKUP_TEST") == null) { Metrics metrics = new Metrics(this); metrics.addCustomChart(new Metrics.SimplePie("confirmation", () -> config.getString("confirmation-type", "unknown"))); @@ -235,6 +238,10 @@ public void reload(boolean init) { helper = new RankupHelper(this); } + public MessageBuilder newMessageBuilder(String message) { + return new PebbleMessageBuilder(this, message); + } + public boolean error() { return error(null); } @@ -314,7 +321,7 @@ public void refreshRanks() { prestiges = null; } - rankups = new Rankups(this, loadConfig("rankups.yml")); + rankups = new Rankups(this, loadRankupConfig("rankups")); // check rankups are not in an infinite loop // rankups.getOrderedList(); @@ -334,6 +341,7 @@ private void saveLocales() { saveLocale("fr"); saveLocale("it"); saveLocale("es"); + saveLocale("nl"); } private void saveLocale(String locale) { @@ -344,6 +352,21 @@ private void saveLocale(String locale) { } } + private List loadRankupConfig(String name) { + File ymlFile = new File(getDataFolder(), name + ".yml"); + File tomlFile = new File(getDataFolder(), name + ".toml"); + if (tomlFile.exists()) { + try { + return ShadowDeserializer.deserialize(TomlFormat.instance().createParser().parse(new FileReader(tomlFile))); + } catch (FileNotFoundException ignored) { + } + } + if (!ymlFile.exists()) { + saveResource(ymlFile.getName(), false); + } + return YamlDeserializer.deserialize(YamlConfiguration.loadConfiguration(ymlFile)); + } + private FileConfiguration loadConfig(String name) { File file = new File(getDataFolder(), name); if (!file.exists()) { @@ -408,7 +431,7 @@ private void registerRequirements() { new TokensRequirement(this, "tokenmanager-tokensh"), new TokensDeductibleRequirement(this, "tokenmanager-tokens")); } - if (Bukkit.getPluginManager().isPluginEnabled("SuperbVotes")) { + if (Bukkit.getPluginManager().isPluginEnabled("SuperbVote")) { requirements.addRequirements(new SuperbVoteVotesRequirement(this)); } } @@ -416,22 +439,6 @@ private void setupEconomy() { economy = economyProvider.getEconomy(); } - public String formatMoney(double money) { - List shortened = config.getStringList("shorten"); - String suffix = ""; - - for (int i = shortened.size(); i > 0; i--) { - double value = Math.pow(10, 3 * i); - if (money >= value) { - money /= value; - suffix = shortened.get(i - 1); - break; - } - } - - return placeholders.getMoneyFormat().format(money) + suffix; - } - public ConfigurationSection getSection(Rank rank, String path) { ConfigurationSection rankSection = rank.getSection(); if (rankSection == null || !rankSection.isConfigurationSection(path)) { @@ -445,94 +452,25 @@ public MessageBuilder getMessage(Rank rank, Message message) { if (messages == null || !messages.isSet(message.getName())) { messages = this.messages; } - return MessageBuilder.of(messages, message); + return newMessageBuilder(messages.getString(message.getName())); } public MessageBuilder getMessage(Message message) { - return MessageBuilder.of(messages, message); - } - - public MessageBuilder replaceMoneyRequirements(MessageBuilder builder, CommandSender sender, - Rank rank) { - if (builder instanceof NullMessageBuilder || rank == null) { - return builder; - } - - Requirement money = rank.getRequirement(sender instanceof Player ? (Player) sender : null, "money"); - if (money != null) { - Double amount = null; - Double total = null; - if (sender instanceof Player && rank.isIn((Player) sender)) { - if (economy != null) { - amount = money.getRemaining((Player) sender); - total = money.getTotal((Player) sender); - } - } else { - amount = money.getValueDouble(); - total = 0D; - } - if (amount != null && economy != null) { - builder.replace(Variable.MONEY_NEEDED, formatMoney(amount)); - builder.replace(Variable.MONEY, formatMoney(money.getValueDouble())); - builder.replace(Variable.MONEY_DONE, formatMoney(total - amount)); - } - } - if (sender instanceof Player) { - replaceRequirements(builder, (Player) sender, rank); - } - return builder; - } - - public MessageBuilder replaceRequirements(MessageBuilder builder, Player player, Rank rank) { - DecimalFormat simpleFormat = placeholders.getSimpleFormat(); - DecimalFormat percentFormat = placeholders.getPercentFormat(); - for (Requirement requirement : rank.getRequirements().getRequirements(player)) { - try { - replaceRequirements(builder, Variable.AMOUNT, requirement, - () -> simpleFormat.format(requirement.getTotal(player))); - if (rank.isIn(player)) { - replaceRequirements(builder, Variable.AMOUNT_NEEDED, requirement, - () -> simpleFormat.format(requirement.getRemaining(player))); - replaceRequirements(builder, Variable.PERCENT_LEFT, requirement, - () -> percentFormat.format(Math.max(0, - (requirement.getRemaining(player) / requirement.getTotal(player)) * 100))); - replaceRequirements(builder, Variable.PERCENT_DONE, requirement, - () -> percentFormat.format(Math.min(100, - (1 - (requirement.getRemaining(player) / requirement.getTotal(player))) * 100))); - replaceRequirements(builder, Variable.AMOUNT_DONE, requirement, - () -> simpleFormat - .format(requirement.getTotal(player) - requirement.getRemaining(player))); - } - } catch (NumberFormatException ignored) { - } - } - return builder; - } - - private void replaceRequirements(MessageBuilder builder, Variable variable, Requirement requirement, Supplier value) { - try { - builder.replace(variable + " " + requirement.getFullName(), value.get()); - } catch (Exception e) { - e.printStackTrace(); - } + return newMessageBuilder(messages.getString(message.getName())); } - public MessageBuilder getMessage(CommandSender player, Message message, Rank oldRank, Rank rankName) { - String oldRankName; - String oldRankDisplayName; + public MessageBuilder getMessage(CommandSender player, Message message, Rank oldRank, Rank rank) { + Rank actualOldRank; if (oldRank instanceof Prestige && oldRank.getRank() == null) { - oldRankName = ((Prestige) oldRank).getFrom(); - oldRankDisplayName = rankups.getTree().last().getRank().getDisplayName(); + actualOldRank = rankups.getByName(((Prestige) oldRank).getFrom()).getRank(); } else { - oldRankName = oldRank.getRank(); - oldRankDisplayName = oldRank.getDisplayName(); + actualOldRank = oldRank; } - return replaceMoneyRequirements(getMessage(oldRank, message) - .replaceRanks(player, rankName) - .replace(Variable.OLD_RANK, oldRankName) - .replace(Variable.OLD_RANK_NAME, oldRankDisplayName), player, oldRank) - .replaceFromTo(oldRank); + return getMessage(oldRank, message) + .replacePlayer(player) + .replaceRank(rank) + .replaceOldRank(actualOldRank); } public void sendHeaderFooter(CommandSender sender, Rank rank, Message type) { @@ -540,12 +478,12 @@ public void sendHeaderFooter(CommandSender sender, Rank rank, Message type) { if (rank == null) { builder = getMessage(type) .failIfEmpty() - .replace(Variable.PLAYER, sender.getName()); + .replacePlayer(sender); } else { builder = getMessage(rank, type) .failIfEmpty() - .replaceRanks(sender, rank) - .replaceFromTo(rank); + .replacePlayer(sender) + .replaceRank(rank); } builder.send(sender); } diff --git a/src/main/java/sh/okx/rankup/commands/InfoCommand.java b/src/main/java/sh/okx/rankup/commands/InfoCommand.java index 0e7d3b2..ec99df8 100644 --- a/src/main/java/sh/okx/rankup/commands/InfoCommand.java +++ b/src/main/java/sh/okx/rankup/commands/InfoCommand.java @@ -201,7 +201,7 @@ public boolean onCommand(CommandSender sender, Command command, String label, St element = next; } return true; - } else if (args[0].equalsIgnoreCase("playtime") && (sender.hasPermission("rankup.playtime.get") || sender.hasPermission("rankup.playtime.set"))) { + } else if (args[0].equalsIgnoreCase("playtime") && (sender.hasPermission("rankup.playtime.get") || sender.hasPermission("rankup.playtime"))) { Statistic playOneTick; try { playOneTick = Statistic.valueOf("PLAY_ONE_MINUTE"); @@ -241,7 +241,7 @@ public boolean onCommand(CommandSender sender, Command command, String label, St } player.sendMessage(ChatColor.LIGHT_PURPLE + who + " played for " + minutes + " minutes."); return true; - } else if (args[1].equalsIgnoreCase("set") && sender.hasPermission("rankup.playtime.set")) { + } else if (args[1].equalsIgnoreCase("set") && sender.hasPermission("rankup.playtime")) { if (args.length < 4) { sender.sendMessage(ChatColor.GREEN + "/" + label + " " + args[0] + " set " + ChatColor.YELLOW + " Update the playtime statistic for a player"); return true; @@ -264,6 +264,39 @@ public boolean onCommand(CommandSender sender, Command command, String label, St player.setStatistic(playOneTick, minutes * 20 * 60); player.sendMessage(ChatColor.LIGHT_PURPLE + "Updated playtime for " + player.getName() + " to " + minutes + " minutes"); return true; + } else if (args[1].equalsIgnoreCase("add") && sender.hasPermission("rankup.playtime")) { + if (args.length < 4) { + sender.sendMessage(ChatColor.GREEN + "/" + label + " " + args[0] + " add " + ChatColor.YELLOW + " Increase the playtime statistic for a player"); + return true; + } + + Player player = Bukkit.getPlayer(args[2]); + if (player == null) { + sender.sendMessage(ChatColor.GRAY + "Player not found"); + return true; + } + + int minutes; + try { + minutes = Integer.parseInt(args[3]); + } catch (NumberFormatException e) { + sender.sendMessage(ChatColor.GRAY + "Invalid number: " + args[3]); + return true; + } + + int oldMinutes = player.getStatistic(playOneTick) / 20 / 60; + if (minutes > 0) { + player.incrementStatistic(playOneTick, minutes * 20 * 60); + } else if (minutes < 0) { + if (oldMinutes + minutes < 0) { + player.sendMessage(ChatColor.GRAY + "Playtime cannot be negative"); + return true; + } + player.decrementStatistic(playOneTick, -minutes * 20 * 60); + } + int newMinutes = oldMinutes + minutes; + player.sendMessage(ChatColor.LIGHT_PURPLE + "Increased playtime for " + player.getName() + " to " + oldMinutes + (minutes >= 0 ? "+" : "") + minutes + "=" + newMinutes + " minutes"); + return true; } } if (sender.hasPermission("rankup.playtime.get")) { @@ -271,17 +304,18 @@ public boolean onCommand(CommandSender sender, Command command, String label, St ChatColor.GREEN + "/" + label + " " + args[0] + " get [player] " + ChatColor.YELLOW + " Get amount of minutes played"); } - if (sender.hasPermission("rankup.playtime.set")) { + if (sender.hasPermission("rankup.playtime")) { sender.sendMessage( ChatColor.GREEN + "/" + label + " " + args[0] + " set " + ChatColor.YELLOW + " Update the playtime statistic for a player"); + sender.sendMessage( + ChatColor.GREEN + "/" + label + " " + args[0] + " add " + + ChatColor.YELLOW + " Increase the playtime statistic for a player"); } return true; } } - // Set playtime & get playtime for other players? - PluginDescriptionFile description = plugin.getDescription(); String version = description.getVersion(); sender.sendMessage( @@ -322,7 +356,7 @@ public boolean onCommand(CommandSender sender, Command command, String label, St list.add("forceprestige"); list.add("rankdown"); } - if (sender.hasPermission("rankup.playtime")) { + if (sender.hasPermission("rankup.playtime.get") || sender.hasPermission("rankup.playtime")) { list.add("playtime"); } return StringUtil.copyPartialMatches(args[0], list, new ArrayList<>()); @@ -333,6 +367,16 @@ public boolean onCommand(CommandSender sender, Command command, String label, St return StringUtil.copyPartialMatches(args[1], players(), new ArrayList<>()); } else if (args[0].equalsIgnoreCase("rankdown") && sender.hasPermission("rankup.force")) { return StringUtil.copyPartialMatches(args[1], players(), new ArrayList<>()); + } else if (args[0].equalsIgnoreCase("playtime")) { + List options = new ArrayList<>(); + if (sender.hasPermission("rankup.playtime.get")) { + options.add("get"); + } + if (sender.hasPermission("rankup.playtime")) { + options.add("set"); + options.add("add"); + } + return StringUtil.copyPartialMatches(args[1], options, new ArrayList<>()); } } return Collections.emptyList(); diff --git a/src/main/java/sh/okx/rankup/commands/PrestigeCommand.java b/src/main/java/sh/okx/rankup/commands/PrestigeCommand.java index 375ca03..8155561 100644 --- a/src/main/java/sh/okx/rankup/commands/PrestigeCommand.java +++ b/src/main/java/sh/okx/rankup/commands/PrestigeCommand.java @@ -58,9 +58,10 @@ public boolean onCommand(CommandSender sender, Command command, String label, St Prestige next = rankElement.getNext().getRank(); Rank nextRank = next == null ? prestiges.getTree().last().getRank() : next; - plugin.replaceMoneyRequirements(plugin.getMessage(prestige, Message.PRESTIGE_CONFIRMATION) - .replaceRanks(player, prestige, nextRank), player, prestige) - .replaceFromTo(prestige) + plugin.getMessage(prestige, Message.PRESTIGE_CONFIRMATION) + .replacePlayer(player) + .replaceOldRank(prestige) + .replaceRank(nextRank) .send(player); break; case "gui": diff --git a/src/main/java/sh/okx/rankup/commands/PrestigesCommand.java b/src/main/java/sh/okx/rankup/commands/PrestigesCommand.java index 210bf64..83a0e38 100644 --- a/src/main/java/sh/okx/rankup/commands/PrestigesCommand.java +++ b/src/main/java/sh/okx/rankup/commands/PrestigesCommand.java @@ -7,6 +7,7 @@ import org.bukkit.entity.Player; import sh.okx.rankup.RankupPlugin; import sh.okx.rankup.messages.Message; +import sh.okx.rankup.messages.MessageBuilder; import sh.okx.rankup.prestige.Prestige; import sh.okx.rankup.prestige.Prestiges; import sh.okx.rankup.ranks.RankElement; @@ -38,9 +39,9 @@ public boolean onCommand(CommandSender sender, Command command, String label, St .send(sender); message = Message.PRESTIGES_INCOMPLETE; } else { - plugin.getMessage(sender, message, prestige.getRank(), next.getRank()) - .replaceFirstPrestige(prestige.getRank(), prestiges, prestige.getRank().getFrom()) - .send(sender); + MessageBuilder builder = plugin + .getMessage(sender, message, prestige.getRank(), next.getRank()); + builder.send(sender); } prestige = next; } diff --git a/src/main/java/sh/okx/rankup/commands/RankupCommand.java b/src/main/java/sh/okx/rankup/commands/RankupCommand.java index 18fbe37..b24fd68 100644 --- a/src/main/java/sh/okx/rankup/commands/RankupCommand.java +++ b/src/main/java/sh/okx/rankup/commands/RankupCommand.java @@ -1,5 +1,7 @@ package sh.okx.rankup.commands; +import java.util.Map; +import java.util.WeakHashMap; import lombok.RequiredArgsConstructor; import org.bukkit.ChatColor; import org.bukkit.command.Command; @@ -14,9 +16,6 @@ import sh.okx.rankup.ranks.RankElement; import sh.okx.rankup.ranks.Rankups; -import java.util.Map; -import java.util.WeakHashMap; - @RequiredArgsConstructor public class RankupCommand implements CommandExecutor { // weak hash maps so players going offline are automatically removed. @@ -49,7 +48,7 @@ public boolean onCommand(CommandSender sender, Command command, String label, St // clicking on the gui cannot confirm a rankup if (confirmationType.equals("text") && confirming.containsKey(player) && !(args.length > 0 && args[0].equalsIgnoreCase("gui"))) { long time = System.currentTimeMillis() - confirming.remove(player); - if (time < config.getInt("text.timeout") * 1000) { + if (time < config.getInt("text.timeout") * 1000L) { plugin.getHelper().rankup(player); return true; } @@ -58,8 +57,10 @@ public boolean onCommand(CommandSender sender, Command command, String label, St switch (confirmationType) { case "text": confirming.put(player, System.currentTimeMillis()); - plugin.replaceMoneyRequirements(plugin.getMessage(rankElement.getRank(), Message.CONFIRMATION) - .replaceRanks(player, rankElement.getRank(), rankElement.getNext().getRank()), player, rankElement.getRank()) + plugin.getMessage(rankElement.getRank(), Message.CONFIRMATION) + .replacePlayer(player) + .replaceOldRank(rankElement.getRank()) + .replaceRank(rankElement.getNext().getRank()) .send(player); break; case "gui": diff --git a/src/main/java/sh/okx/rankup/gui/Gui.java b/src/main/java/sh/okx/rankup/gui/Gui.java index c77fb5c..05f7102 100644 --- a/src/main/java/sh/okx/rankup/gui/Gui.java +++ b/src/main/java/sh/okx/rankup/gui/Gui.java @@ -26,6 +26,7 @@ @RequiredArgsConstructor(access = AccessLevel.PRIVATE) public class Gui implements InventoryHolder { + @Getter private final boolean returnToRanksGui; @Getter @@ -37,7 +38,8 @@ public class Gui implements InventoryHolder { @Getter private boolean prestige; - public static Gui of(Player player, Rank oldRank, Rank rank, RankupPlugin plugin, boolean returnToRanksGui) { + public static Gui of(Player player, Rank oldRank, Rank rank, RankupPlugin plugin, + boolean returnToRanksGui) { Gui gui = new Gui(returnToRanksGui); gui.prestige = oldRank instanceof Prestige; @@ -45,15 +47,20 @@ public static Gui of(Player player, Rank oldRank, Rank rank, RankupPlugin plugin String basePath = type + ".gui"; ConfigurationSection config = plugin.getSection(oldRank, basePath); if (config == null) { - plugin.getLogger().severe("You must update your config.yml and locale/en.yml to be able to use the GUI! Your configuration files are outdated."); + plugin.getLogger().severe( + "You must update your config.yml and locale/en.yml to be able to use the GUI! Your configuration files are outdated."); return null; } - ItemStack[] items = new ItemStack[config.getInt("rows", 1) * 9]; + Integer rows = Gui.getInt(config, "rows"); + ItemStack[] items = new ItemStack[(rows == null ? 1 : rows) * 9]; - ItemStack fill = getItem(plugin, plugin.getSection(oldRank, basePath + ".fill"), player, oldRank, rank); - ItemStack cancel = getItem(plugin, plugin.getSection(oldRank, basePath + ".cancel"), player, oldRank, rank); - ItemStack rankup = getItem(plugin, plugin.getSection(oldRank, basePath + ".rankup"), player, oldRank, rank); + ItemStack fill = getItem(plugin, plugin.getSection(oldRank, basePath + ".fill"), player, + oldRank, rank); + ItemStack cancel = getItem(plugin, plugin.getSection(oldRank, basePath + ".cancel"), player, + oldRank, rank); + ItemStack rankup = getItem(plugin, plugin.getSection(oldRank, basePath + ".rankup"), player, + oldRank, rank); addItem(items, plugin.getSection(oldRank, basePath + ".rankup"), rankup); addItem(items, plugin.getSection(oldRank, basePath + ".cancel"), cancel); @@ -63,26 +70,30 @@ public static Gui of(Player player, Rank oldRank, Rank rank, RankupPlugin plugin gui.cancel = cancel; Inventory inventory = Bukkit.createInventory(gui, items.length, - Colour.translate(plugin.replaceMoneyRequirements( + Colour.translate( plugin.getMessage(oldRank, gui.prestige ? Message.PRESTIGE_TITLE : Message.TITLE) - .replaceRanks(player, oldRank, rank) - .replaceFromTo(oldRank), player, oldRank).toString())); + .replacePlayer(player) + .replaceOldRank(oldRank) + .replaceRank(rank).toString(player))); inventory.setContents(items); gui.inventory = inventory; return gui; } - public static ItemStack getItem(RankupPlugin plugin, ConfigurationSection section, Player player, RankElement element) { + public static ItemStack getItem(RankupPlugin plugin, ConfigurationSection section, Player player, + RankElement element) { if (element == null) { return getItem(plugin, section, player, null, null); } else { RankElement next = element.getNext(); - return getItem(plugin, section, player, element.getRank(), (next == null ? element : next).getRank()); + return getItem(plugin, section, player, element.getRank(), + (next == null ? element : next).getRank()); } } @SuppressWarnings("deprecation") - public static ItemStack getItem(RankupPlugin plugin, ConfigurationSection section, Player player, Rank oldRank, Rank rank) { + public static ItemStack getItem(RankupPlugin plugin, ConfigurationSection section, Player player, + Rank oldRank, Rank rank) { if (section == null) { return null; } @@ -111,23 +122,26 @@ public static ItemStack getItem(RankupPlugin plugin, ConfigurationSection sectio ItemMeta meta = item.getItemMeta(); if (section.contains("lore")) { - meta.setLore(Arrays.stream(format(plugin, section.getString("lore"), player, oldRank, rank).split("\n")) + meta.setLore(Arrays + .stream(format(plugin, section.getString("lore"), player, oldRank, rank).split("\n")) .map(string -> ChatColor.RESET + string) .collect(Collectors.toList())); } if (section.contains("name")) { - meta.setDisplayName(ChatColor.RESET + format(plugin, section.getString("name"), player, oldRank, rank)); + meta.setDisplayName( + ChatColor.RESET + format(plugin, section.getString("name"), player, oldRank, rank)); } item.setItemMeta(meta); return item; } - private static String format(RankupPlugin plugin, String message, Player player, Rank oldRank, Rank rank) { - MessageBuilder builder = new MessageBuilder(message); + private static String format(RankupPlugin plugin, String message, Player player, Rank oldRank, + Rank rank) { + MessageBuilder builder = plugin.newMessageBuilder(message); if (oldRank != null && rank != null) { - builder = builder.replaceRanks(player, oldRank, rank); + builder = builder.replacePlayer(player).replaceOldRank(oldRank).replaceRank(rank); } - return Colour.translate(plugin.replaceMoneyRequirements(builder, player, oldRank).toString()); + return builder.toString(player); } private static void addItem(ItemStack[] items, ConfigurationSection section, ItemStack item) { @@ -154,6 +168,19 @@ private static void addItem(ItemStack[] items, ConfigurationSection section, Ite } } + public static Integer getInt(ConfigurationSection section, String key) { + String string = section.getString(key); + if (string == null) { + return null; + } else { + try { + return Integer.parseInt(string); + } catch (IllegalArgumentException ex) { + return null; + } + } + } + public void open(Player player) { player.openInventory(inventory); } diff --git a/src/main/java/sh/okx/rankup/messages/Message.java b/src/main/java/sh/okx/rankup/messages/Message.java index dc61eaa..784d9e5 100644 --- a/src/main/java/sh/okx/rankup/messages/Message.java +++ b/src/main/java/sh/okx/rankup/messages/Message.java @@ -4,6 +4,7 @@ public enum Message { NOT_IN_LADDER("not-in-ladder"), + NOT_HIGH_ENOUGH("not-high-enough"), REQUIREMENTS_NOT_MET("rankup.requirements-not-met"), NO_RANKUP("rankup.no-rankup"), SUCCESS_PUBLIC("rankup.success-public"), @@ -26,11 +27,10 @@ public enum Message { COOLDOWN_SINGULAR("rankup.cooldown.singular"), COOLDOWN_PLURAL("rankup.cooldown.plural"), MUST_PRESTIGE("rankup.must-prestige"), - NOT_HIGH_ENOUGH("not-high-enough"), PRESTIGE_SUCCESS_PUBLIC("prestige.success-public"), PRESTIGE_SUCCESS_PRIVATE("prestige.success-private"), PRESTIGE_CONFIRMATION("prestige.confirmation"), - INVALID_RANKUP("invalid-rankup"); + ; @Getter private final String name; diff --git a/src/main/java/sh/okx/rankup/messages/MessageBuilder.java b/src/main/java/sh/okx/rankup/messages/MessageBuilder.java index 1841fd2..379898d 100644 --- a/src/main/java/sh/okx/rankup/messages/MessageBuilder.java +++ b/src/main/java/sh/okx/rankup/messages/MessageBuilder.java @@ -1,156 +1,25 @@ package sh.okx.rankup.messages; -import java.util.Objects; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import me.clip.placeholderapi.PlaceholderAPI; -import org.bukkit.Bukkit; import org.bukkit.command.CommandSender; -import org.bukkit.configuration.ConfigurationSection; import org.bukkit.entity.Player; -import sh.okx.rankup.prestige.Prestige; -import sh.okx.rankup.prestige.Prestiges; import sh.okx.rankup.ranks.Rank; -import sh.okx.rankup.util.Colour; -public class MessageBuilder { - private String message; +public interface MessageBuilder { + MessageBuilder replaceKey(String key, Object value); + MessageBuilder replacePlayer(CommandSender sender); + MessageBuilder replaceRank(Rank rank); + MessageBuilder replaceOldRank(Rank rank); + MessageBuilder replaceSeconds(long seconds, long secondsLeft); - public MessageBuilder(String message) { - this.message = message; - } - - public static MessageBuilder of(ConfigurationSection config, Message message) { - return MessageBuilder.of(config, message.getName()); - } - - private static MessageBuilder of(ConfigurationSection config, String message) { - String string = config.getString(message); - Objects.requireNonNull(string, "Configuration message '" + message + "' not found!"); - return new MessageBuilder(Colour.translate(string)); - } - - public MessageBuilder replace(Variable variable, Object value) { - return replace(variable.name(), value); - } - - public MessageBuilder replace(String name, Object value) { - if (value == null) { - return this; - } - Pattern pattern = Pattern.compile("\\{" + name + "}", Pattern.CASE_INSENSITIVE); - Matcher matcher = pattern.matcher(message); - this.message = matcher.replaceAll(String.valueOf(value)); - return this; - } + void send(CommandSender sender); + void broadcast(); - public MessageBuilder replaceFirstPrestige(Rank rank, Prestiges prestiges, String with) { - if (prestiges != null && prestiges.getFirst().equals(rank)) { - replace(Variable.OLD_RANK, with); - } - return this; - } - - @Deprecated - public MessageBuilder replaceRanks(CommandSender player, String rankName) { - replace(Variable.PLAYER, player.getName()); - replaceRanks(rankName); - return this; - } - - @Deprecated - public MessageBuilder replaceRanks(CommandSender player, Rank oldRank, String rankName) { - replace(Variable.PLAYER, player.getName()); - replaceRanks(oldRank, rankName); - return this; - } - - public MessageBuilder replaceRanks(CommandSender player, Rank rank) { - replace(Variable.PLAYER, player.getName()); - replaceRanks(rank); - return this; - } - - public MessageBuilder replaceRanks(CommandSender player, Rank oldRank, Rank rank) { - replace(Variable.PLAYER, player.getName()); - replaceRanks(oldRank, rank); - return this; - } + String toString(); - @Deprecated - public MessageBuilder replaceRanks(String rankName) { - replace(Variable.RANK, rankName); - return this; - } - - public MessageBuilder replaceRanks(Rank rank) { - replace(Variable.RANK, rank.getRank()); - replace(Variable.RANK_NAME, rank.getDisplayName()); - return this; - } - - @Deprecated - public MessageBuilder replaceRanks(Rank oldRank, String rankName) { - replaceRanks(rankName); - replace(Variable.OLD_RANK, oldRank.getRank()); - replace(Variable.OLD_RANK_NAME, oldRank.getDisplayName()); - return this; - } - - public MessageBuilder replaceRanks(Rank oldRank, Rank rank) { - replaceRanks(rank); - replace(Variable.OLD_RANK, oldRank.getRank()); - replace(Variable.OLD_RANK_NAME, oldRank.getDisplayName()); - return this; - } - - public MessageBuilder replaceFromTo(Rank rank) { - if (rank instanceof Prestige) { - Prestige prestige = (Prestige) rank; - replace(Variable.FROM, prestige.getFrom()); - replace(Variable.TO, prestige.getTo()); - } - return this; - } - - /** - * Fails the MessageBuilder if the message is empty. - * if this fails, all subsequent calls to that MessageBuilder will do nothing - * @return a NullMessageBuilder if the message is empty, itself otherwise - */ - public MessageBuilder failIfEmpty() { - return failIf(message.isEmpty()); - } - - public MessageBuilder failIf(boolean value) { - if (value) { - return new NullMessageBuilder(); - } else { - return this; - } - } - - public void send(CommandSender sender) { - String msg = message; - if (sender instanceof Player && Bukkit.getPluginManager().isPluginEnabled("PlaceholderAPI")) { - msg = PlaceholderAPI.setPlaceholders((Player) sender, msg); - } - sender.sendMessage(msg); - } - - /** - * Sends the message to all players - * ie, calls MessageBuilder#send(Player) for all players online, and sends the message in the console. - */ - public void broadcast() { - for (Player player : Bukkit.getOnlinePlayers()) { - send(player); - } - send(Bukkit.getConsoleSender()); - } + MessageBuilder failIfEmpty(); + MessageBuilder failIf(boolean b); - @Override - public String toString() { - return message; + default String toString(Player player) { + return toString(); } } diff --git a/src/main/java/sh/okx/rankup/messages/NullMessageBuilder.java b/src/main/java/sh/okx/rankup/messages/NullMessageBuilder.java index 373402f..dbea283 100644 --- a/src/main/java/sh/okx/rankup/messages/NullMessageBuilder.java +++ b/src/main/java/sh/okx/rankup/messages/NullMessageBuilder.java @@ -1,35 +1,60 @@ package sh.okx.rankup.messages; import org.bukkit.command.CommandSender; +import sh.okx.rankup.ranks.Rank; /** * A no-op implementation of MessageBuilder */ -public class NullMessageBuilder extends MessageBuilder { - NullMessageBuilder() { - super(null); +public class NullMessageBuilder implements MessageBuilder { + + @Override + public MessageBuilder replaceKey(String key, Object value) { + return this; } @Override - public MessageBuilder failIf(boolean value) { + public MessageBuilder replacePlayer(CommandSender sender) { return this; } @Override - public MessageBuilder replace(Variable variable, Object value) { + public MessageBuilder replaceRank(Rank rank) { return this; } + @Override + public MessageBuilder replaceOldRank(Rank rank) { + return this; + } + + @Override + public MessageBuilder replaceSeconds(long seconds, long secondsLeft) { + return null; + } + @Override public void send(CommandSender sender) { + } @Override public void broadcast() { + } @Override public String toString() { return null; } + + @Override + public MessageBuilder failIfEmpty() { + return this; + } + + @Override + public MessageBuilder failIf(boolean b) { + return this; + } } diff --git a/src/main/java/sh/okx/rankup/messages/StringMessageBuilder.java b/src/main/java/sh/okx/rankup/messages/StringMessageBuilder.java new file mode 100644 index 0000000..5119d45 --- /dev/null +++ b/src/main/java/sh/okx/rankup/messages/StringMessageBuilder.java @@ -0,0 +1,125 @@ +package sh.okx.rankup.messages; + +import java.util.Objects; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import me.clip.placeholderapi.PlaceholderAPI; +import org.bukkit.Bukkit; +import org.bukkit.command.CommandSender; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.entity.Player; +import sh.okx.rankup.prestige.Prestige; +import sh.okx.rankup.prestige.Prestiges; +import sh.okx.rankup.ranks.Rank; +import sh.okx.rankup.util.Colour; + +public class StringMessageBuilder implements MessageBuilder { + + private String message; + + public StringMessageBuilder(String message) { + this.message = message; + } + + public static StringMessageBuilder of(ConfigurationSection config, Message message) { + return StringMessageBuilder.of(config, message.getName()); + } + + private static StringMessageBuilder of(ConfigurationSection config, String message) { + String string = config.getString(message); + Objects.requireNonNull(string, "Configuration message '" + message + "' not found!"); + return new StringMessageBuilder(Colour.translate(string)); + } + + public StringMessageBuilder replace(Variable variable, Object value) { + return replaceKey(variable.name(), value); + } + + @Override + public StringMessageBuilder replaceKey(String name, Object value) { + if (value == null) { + return this; + } + Pattern pattern = Pattern.compile("\\{" + name + "}", Pattern.CASE_INSENSITIVE); + Matcher matcher = pattern.matcher(message); + this.message = matcher.replaceAll(String.valueOf(value)); + return this; + } + + public StringMessageBuilder replaceFirstPrestige(Rank rank, Prestiges prestiges, String with) { + if (prestiges != null && prestiges.getFirst().equals(rank)) { + replace(Variable.OLD_RANK, with); + } + return this; + } + + /** + * Fails the MessageBuilder if the message is empty. if this fails, all subsequent calls to that + * MessageBuilder will do nothing + * + * @return a NullMessageBuilder if the message is empty, itself otherwise + */ + public MessageBuilder failIfEmpty() { + return failIf(message.isEmpty()); + } + + public MessageBuilder failIf(boolean value) { + if (value) { + return new NullMessageBuilder(); + } else { + return this; + } + } + + @Override + public MessageBuilder replacePlayer(CommandSender sender) { + return replace(Variable.PLAYER, sender.getName()); + } + + @Override + public MessageBuilder replaceRank(Rank rank) { + return replace(Variable.RANK, rank.getRank()) + .replace(Variable.RANK_NAME, rank.getDisplayName()); + } + + @Override + public MessageBuilder replaceOldRank(Rank rank) { + if (rank instanceof Prestige) { + Prestige prestige = (Prestige) rank; + replace(Variable.FROM, prestige.getFrom()); + replace(Variable.TO, prestige.getTo()); + } + return replace(Variable.OLD_RANK, rank.getRank()) + .replace(Variable.OLD_RANK_NAME, rank.getDisplayName()); + } + + @Override + public MessageBuilder replaceSeconds(long seconds, long secondsLeft) { + return replace(Variable.SECONDS, seconds) + .replace(Variable.SECONDS_LEFT, secondsLeft); + } + + public void send(CommandSender sender) { + String msg = message; + if (sender instanceof Player && Bukkit.getPluginManager().isPluginEnabled("PlaceholderAPI")) { + msg = PlaceholderAPI.setPlaceholders((Player) sender, msg); + } + sender.sendMessage(msg); + } + + /** + * Sends the message to all players ie, calls MessageBuilder#send(Player) for all players online, + * and sends the message in the console. + */ + public void broadcast() { + for (Player player : Bukkit.getOnlinePlayers()) { + send(player); + } + send(Bukkit.getConsoleSender()); + } + + @Override + public String toString() { + return message; + } +} diff --git a/src/main/java/sh/okx/rankup/messages/Variable.java b/src/main/java/sh/okx/rankup/messages/Variable.java index 1ff5bf6..0d3320d 100644 --- a/src/main/java/sh/okx/rankup/messages/Variable.java +++ b/src/main/java/sh/okx/rankup/messages/Variable.java @@ -1,6 +1,6 @@ package sh.okx.rankup.messages; -public enum Variable { +enum Variable { PLAYER, OLD_RANK_NAME, OLD_RANK, @@ -8,14 +8,6 @@ public enum Variable { RANK, FROM, TO, - MONEY, - MONEY_NEEDED, - MONEY_DONE, - AMOUNT, - AMOUNT_NEEDED, - AMOUNT_DONE, - PERCENT_DONE, - PERCENT_LEFT, SECONDS, SECONDS_LEFT } diff --git a/src/main/java/sh/okx/rankup/messages/pebble/InvalidRequirementException.java b/src/main/java/sh/okx/rankup/messages/pebble/InvalidRequirementException.java new file mode 100644 index 0000000..03e8c8d --- /dev/null +++ b/src/main/java/sh/okx/rankup/messages/pebble/InvalidRequirementException.java @@ -0,0 +1,22 @@ +package sh.okx.rankup.messages.pebble; + +import sh.okx.rankup.ranks.Rank; + +public class InvalidRequirementException extends RuntimeException { + private final String requirement; + private final Rank rank; + + public InvalidRequirementException(String requirement, Rank rank) { + super("Invalid requirement: " + requirement + " for rank " + rank.getRank()); + this.requirement = requirement; + this.rank = rank; + } + + public String getRequirement() { + return requirement; + } + + public Rank getRank() { + return rank; + } +} diff --git a/src/main/java/sh/okx/rankup/messages/pebble/PebbleMessageBuilder.java b/src/main/java/sh/okx/rankup/messages/pebble/PebbleMessageBuilder.java new file mode 100644 index 0000000..0110c42 --- /dev/null +++ b/src/main/java/sh/okx/rankup/messages/pebble/PebbleMessageBuilder.java @@ -0,0 +1,142 @@ +package sh.okx.rankup.messages.pebble; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import org.bukkit.Bukkit; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import sh.okx.rankup.RankupPlugin; +import sh.okx.rankup.messages.MessageBuilder; +import sh.okx.rankup.messages.NullMessageBuilder; +import sh.okx.rankup.prestige.Prestige; +import sh.okx.rankup.ranks.Rank; +import sh.okx.rankup.text.TextProcessor; +import sh.okx.rankup.text.TextProcessorBuilder; + +public class PebbleMessageBuilder implements MessageBuilder { + + private final RankupPlugin plugin; + private final String message; + private final Map context = new HashMap<>(); + private final Map> lastMinuteContext = new HashMap<>(); + + public PebbleMessageBuilder(RankupPlugin plugin, String message) { + this.plugin = plugin; + this.message = message; + replaceInitial(); + } + + private void replaceInitial() { + lastMinuteContext.put("ranks", player -> { + List ranks = new ArrayList<>(); + for (Rank rank : plugin.getRankups().getTree()) { + ranks.add(new RankContext(plugin, player, rank)); + } + return ranks; + }); + } + + @Override + public PebbleMessageBuilder replaceKey(String key, Object value) { + context.put(key, value); + return this; + } + + @Override + public PebbleMessageBuilder replacePlayer(CommandSender sender) { + context.put("player", sender.getName()); + return this; + } + + @Override + public PebbleMessageBuilder replaceRank(Rank rank) { + Function fun = player -> new RankContext(plugin, player, rank); + context.put("next", fun.apply(null)); // for console + lastMinuteContext.put("next", fun); + return this; + } + + @Override + public PebbleMessageBuilder replaceOldRank(Rank rank) { + Function object; + if (rank instanceof Prestige) { + object = player -> new PrestigeContext(plugin, player, (Prestige) rank); + } else { + object = player -> new RankContext(plugin, player, rank); + } + context.put("rank", object.apply(null)); // for console + lastMinuteContext.put("rank", object); + return this; + } + + @Override + public MessageBuilder replaceSeconds(long seconds, long secondsLeft) { + context.put("seconds", seconds); + context.put("seconds_left", secondsLeft); + return this; + } + + @Override + public void send(CommandSender sender) { + Player player = null; + if (sender instanceof Player) { + player = (Player) sender; + } + sender.sendMessage(processor(player).process(message)); + } + + @Override + public void broadcast() { + for (Player player : Bukkit.getOnlinePlayers()) { + send(player); + } + send(Bukkit.getConsoleSender()); + } + + @Override + public String toString() { + return processor(null).process(message); + } + + @Override + public String toString(Player player) { + return processor(player).process(message); + } + + private TextProcessor processor(Player player) { + Map context = getContext(player); + return new TextProcessorBuilder() + .legacy(context, plugin.getPlaceholders()) + .papi(player) + .pebble(context, plugin.getPlaceholders()) + .papi(player) + .colour() + .create(); + } + + private Map getContext(Player player) { + Map context = new HashMap<>(this.context); + if (player != null) { + for (Map.Entry> lastMinute : lastMinuteContext.entrySet()) { + context.put(lastMinute.getKey(), lastMinute.getValue().apply(player)); + } + if (!context.containsKey("player")) { + context.put("player", player.getName()); + } + } + return context; + } + + @Override + public MessageBuilder failIfEmpty() { + return failIf(message.isEmpty()); + } + + @Override + public MessageBuilder failIf(boolean b) { + return b ? new NullMessageBuilder() : this; + } +} diff --git a/src/main/java/sh/okx/rankup/messages/pebble/PrestigeContext.java b/src/main/java/sh/okx/rankup/messages/pebble/PrestigeContext.java new file mode 100644 index 0000000..60af03d --- /dev/null +++ b/src/main/java/sh/okx/rankup/messages/pebble/PrestigeContext.java @@ -0,0 +1,24 @@ +package sh.okx.rankup.messages.pebble; + + +import org.bukkit.entity.Player; +import sh.okx.rankup.RankupPlugin; +import sh.okx.rankup.prestige.Prestige; + +public class PrestigeContext extends RankContext { + + private final Prestige rank; + + public PrestigeContext(RankupPlugin plugin, Player player, Prestige rank) { + super(plugin, player, rank); + this.rank = rank; + } + + public String getFrom() { + return rank.getFrom(); + } + + public String getTo() { + return rank.getTo(); + } +} diff --git a/src/main/java/sh/okx/rankup/messages/pebble/RankContext.java b/src/main/java/sh/okx/rankup/messages/pebble/RankContext.java new file mode 100644 index 0000000..8589647 --- /dev/null +++ b/src/main/java/sh/okx/rankup/messages/pebble/RankContext.java @@ -0,0 +1,82 @@ +package sh.okx.rankup.messages.pebble; + +import java.util.ArrayList; +import java.util.List; +import org.bukkit.entity.Player; +import sh.okx.rankup.RankupPlugin; +import sh.okx.rankup.ranks.Rank; +import sh.okx.rankup.ranks.RankTree; +import sh.okx.rankup.requirements.Requirement; + +public class RankContext { + private final RankupPlugin plugin; + private final Player player; + private final Rank rank; + + public RankContext(RankupPlugin plugin, Player player, Rank rank) { + this.plugin = plugin; + this.player = player; + this.rank = rank; + } + + public String getRank() { + return rank.getRank(); + } + + public String getName() { + return rank.getDisplayName(); + } + + public RequirementContext getRequirement(String requirement) { + Requirement context = rank.getRequirement(player, requirement); + if (context == null) { + throw new InvalidRequirementException(requirement, rank); + } + return new RequirementContext(player, context); + } + + public RequirementContext getRequirement(String requirement, String sub) { + return getRequirement(requirement + "#" + sub); + } + + public RequirementContext getReq(String requirement) { + return getRequirement(requirement); + } + + public RequirementContext getReq(String requirement, String sub) { + return getRequirement(requirement, sub); + } + + public List getRequirements() { + List list = new ArrayList<>(); + for (Requirement requirement : rank.getRequirements().getRequirements(player)) { + list.add(new RequirementContext(player, requirement)); + } + return list; + } + + public boolean getDone() { + for (RequirementContext context : getRequirements()) { + if (!context.getDone()) { + return false; + } + } + return true; + } + + public int getIndex() { + RankTree tree = plugin.getRankups().getTree(); + int index = 0; + for (Rank rank : tree) { + if (rank == this.rank) { + return index; + } + index++; + } + return -1; + } + + public String toString() { + return rank.getRank(); + } +} diff --git a/src/main/java/sh/okx/rankup/messages/pebble/RequirementContext.java b/src/main/java/sh/okx/rankup/messages/pebble/RequirementContext.java new file mode 100644 index 0000000..edcf1b3 --- /dev/null +++ b/src/main/java/sh/okx/rankup/messages/pebble/RequirementContext.java @@ -0,0 +1,47 @@ +package sh.okx.rankup.messages.pebble; + +import org.bukkit.entity.Player; +import sh.okx.rankup.requirements.Requirement; + +public class RequirementContext { + + private final Player player; + private final Requirement requirement; + + public RequirementContext(Player player, Requirement requirement) { + this.player = player; + this.requirement = requirement; + } + + public double getTotal() { + return requirement.getTotal(player); + } + + public boolean getDone() { + return requirement.check(player); + } + + public double getRemaining() { + return requirement.getRemaining(player); + } + + public double getProgress() { + return requirement.getTotal(player) - requirement.getRemaining(player); + } + + public String getName() { + return requirement.getName(); + } + + public double getQuotient() { + return getProgress() / getTotal(); + } + + public double getPercent() { + return getProgress() / getTotal() * 100; + } + + public String toString() { + return "Requirement[" + requirement.getFullName() + "]"; + } +} diff --git a/src/main/java/sh/okx/rankup/placeholders/Placeholders.java b/src/main/java/sh/okx/rankup/placeholders/Placeholders.java index a6262c6..699bba5 100644 --- a/src/main/java/sh/okx/rankup/placeholders/Placeholders.java +++ b/src/main/java/sh/okx/rankup/placeholders/Placeholders.java @@ -1,6 +1,7 @@ package sh.okx.rankup.placeholders; import java.text.DecimalFormat; +import java.util.List; import lombok.Getter; import org.bukkit.Bukkit; import sh.okx.rankup.RankupPlugin; @@ -13,6 +14,7 @@ public class Placeholders { private final DecimalFormat percentFormat; @Getter private final DecimalFormat simpleFormat; + private final List shortened; @Getter private RankupExpansion expansion; @@ -20,11 +22,28 @@ public class Placeholders { public Placeholders(RankupPlugin plugin) { this.plugin = plugin; + this.shortened = plugin.getConfig().getStringList("shorten"); this.moneyFormat = new DecimalFormat(plugin.getConfig().getString("placeholders.money-format")); this.percentFormat = new DecimalFormat(plugin.getConfig().getString("placeholders.percent-format")); this.simpleFormat = new DecimalFormat(plugin.getConfig().getString("placeholders.simple-format")); } + public String formatMoney(double money) { + String suffix = ""; + + for (int i = shortened.size(); i > 0; i--) { + double value = Math.pow(10, 3 * i); + if (money >= value) { + money /= value; + suffix = shortened.get(i - 1); + break; + } + } + + return moneyFormat.format(money) + suffix; + } + + public void register() { expansion = new RankupExpansion(plugin, this); if (Bukkit.getPluginManager().isPluginEnabled("PlaceholderAPI")) { diff --git a/src/main/java/sh/okx/rankup/placeholders/RankupExpansion.java b/src/main/java/sh/okx/rankup/placeholders/RankupExpansion.java index 2e56ce1..d2cfd03 100644 --- a/src/main/java/sh/okx/rankup/placeholders/RankupExpansion.java +++ b/src/main/java/sh/okx/rankup/placeholders/RankupExpansion.java @@ -47,19 +47,18 @@ public String placeholder(Player player, String params) { replacePattern(parts[1]), parts.length > 2 ? parts[2] : ""); } else if (params.startsWith("rank_requirement_")) { String[] parts = params.split("_", 5); - return getPlaceholderRequirement(player, rankups.getByName(parts[2]), + return getPlaceholderRequirement(player, rankups.getRankByName(parts[2]), replacePattern(parts[3]), parts.length > 4 ? parts[4] : ""); -// return placeholders.getSimpleFormat().format(orElse(rankups.getByName(parts[2]).getRequirement(parts[3]), Requirement::getValueDouble, 0)); } else if (params.startsWith("rank_money_")) { String[] parts = params.split("_", 4); - double amount = Objects.requireNonNull(rankups.getByName(parts[2]), "Rankup " + parts[2] + " does not exist").getRequirement(player, "money").getValueDouble(); + double amount = Objects.requireNonNull(rankups.getRankByName(parts[2]), "Rankup " + parts[2] + " does not exist").getRequirement(player, "money").getValueDouble(); if (parts.length > 3 && parts[3].equalsIgnoreCase("left")) { amount = amount - plugin.getEconomy().getBalance(player); } - return plugin.formatMoney(Math.max(0, amount)); + return plugin.getPlaceholders().formatMoney(Math.max(0, amount)); } else if (params.startsWith("status_")) { String[] parts = params.split("_", 2); - Rank statusRank = rankups.getByName(parts[1]); + Rank statusRank = rankups.getRankByName(parts[1]); if (statusRank == null) { return null; @@ -92,37 +91,56 @@ public String placeholder(Player player, String params) { } else { return prestige.getRank(); } + case "current_prestige_name": + requirePrestiging(prestiges, params); + if (prestige == null || prestige.getRank() == null) { + return getPlaceholder("no-prestige"); + } else { + return prestige.getDisplayName(); + } case "next_prestige": requirePrestiging(prestiges, params); if (prestigeElement != null && !prestigeElement.hasNext()) { return getPlaceholder("highest-rank"); } - return orElse(prestige, Prestige::getNext, prestiges.getFirst().getNext()); + return orElse(prestigeElement, e -> e.getNext().getRank().getRank(), prestiges.getTree().getFirst().getNext().getRank().getRank()); + case "next_prestige_name": + requirePrestiging(prestiges, params); + if (prestigeElement != null && !prestigeElement.hasNext()) { + return getPlaceholder("highest-rank"); + } else { + return orElse(prestigeElement, e -> e.getNext().getRank().getDisplayName(), prestiges.getTree().getFirst().getNext().getRank().getDisplayName()); + } case "prestige_money": requirePrestiging(prestiges, params); return String.valueOf(simplify(orElse(prestige, r -> r.isIn(player) ? r.getRequirement(player, "money").getValueDouble() : 0, 0))); case "prestige_money_formatted": requirePrestiging(prestiges, params); - return plugin.formatMoney(orElse(prestige, r -> r.isIn(player) ? r.getRequirement(player, "money").getValueDouble() : 0, 0D)); + return plugin.getPlaceholders().formatMoney(orElse(prestige, r -> r.isIn(player) ? r.getRequirement(player, "money").getValueDouble() : 0, 0D)); case "current_rank": - if (rank == null) { - return getPlaceholder("not-in-ladder"); + return orElse(rank, Rank::getRank, getPlaceholder("not-in-ladder")); + case "current_rank_name": + return orElse(rank, Rank::getDisplayName, getPlaceholder("not-in-ladder")); + case "next_rank": + if (rankElement != null && !rankElement.hasNext()) { + return getPlaceholder("highest-rank"); } else { - return rank.getRank(); + return orElsePlaceholder(rankElement, e -> e.getNext().getRank().getRank(), "not-in-ladder"); } - case "next_rank": + case "next_rank_name": if (rankElement != null && !rankElement.hasNext()) { return getPlaceholder("highest-rank"); + } else { + return orElsePlaceholder(rankElement, e -> e.getNext().getRank().getDisplayName(), "not-in-ladder"); } - return orElsePlaceholder(rank, r -> orElsePlaceholder(rank, Rank::getNext, "highest-rank"), "not-in-ladder"); case "money": return String.valueOf(getMoney(player, rank)); case "money_formatted": - return plugin.formatMoney(getMoney(player, rank).doubleValue()); + return placeholders.formatMoney(getMoney(player, rank).doubleValue()); case "money_left": return String.valueOf(Math.max(0, orElse(rank, r -> simplify(r.getRequirement(player, "money").getValueDouble() - plugin.getEconomy().getBalance(player)), 0).doubleValue())); case "money_left_formatted": - return plugin.formatMoney(Math.max(0D, orElse(rank, r -> r.getRequirement(player, "money").getValueDouble() - plugin.getEconomy().getBalance(player), 0D))); + return placeholders.formatMoney(Math.max(0D, orElse(rank, r -> r.getRequirement(player, "money").getValueDouble() - plugin.getEconomy().getBalance(player), 0D))); case "percent_left": return String.valueOf(Math.max(0D, orElse(rank, r -> (1 - (plugin.getEconomy().getBalance(player) / r.getRequirement(player, "money").getValueDouble())) * 100, 0).doubleValue())); case "percent_left_formatted": diff --git a/src/main/java/sh/okx/rankup/prestige/Prestige.java b/src/main/java/sh/okx/rankup/prestige/Prestige.java index 9e08303..034efcb 100644 --- a/src/main/java/sh/okx/rankup/prestige/Prestige.java +++ b/src/main/java/sh/okx/rankup/prestige/Prestige.java @@ -1,5 +1,6 @@ package sh.okx.rankup.prestige; +import java.util.List; import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.ToString; @@ -8,15 +9,12 @@ import org.bukkit.configuration.ConfigurationSection; import org.bukkit.entity.Player; import sh.okx.rankup.RankupPlugin; -import sh.okx.rankup.messages.MessageBuilder; +import sh.okx.rankup.messages.pebble.PebbleMessageBuilder; import sh.okx.rankup.ranks.Rank; import sh.okx.rankup.ranks.requirements.ListRankRequirements; import sh.okx.rankup.ranks.requirements.RankRequirements; import sh.okx.rankup.requirements.Requirement; -import java.util.List; -import java.util.Set; - @EqualsAndHashCode(callSuper = true) @ToString(callSuper = true) public class Prestige extends Rank { @@ -33,7 +31,7 @@ protected Prestige(ConfigurationSection section, RankupPlugin plugin, String nex public static Prestige deserialize(RankupPlugin plugin, ConfigurationSection section) { List requirementsList = section.getStringList("requirements"); - Set requirements = plugin.getRequirements().getRequirements(requirementsList); + List requirements = plugin.getRequirements().getRequirements(requirementsList); return new Prestige(section, plugin, section.getString("next"), @@ -47,9 +45,10 @@ public static Prestige deserialize(RankupPlugin plugin, ConfigurationSection sec @Override public void runCommands(Player player, Rank next) { for (String command : commands) { - String string = new MessageBuilder(command) - .replaceRanks(player, this, next) - .replaceFromTo(this) + String string = new PebbleMessageBuilder(this.plugin, command) + .replacePlayer(player) + .replaceOldRank(this) + .replaceRank(next) .toString(); if (Bukkit.getPluginManager().isPluginEnabled("PlaceholderAPI")) { string = PlaceholderAPI.setPlaceholders(player, string); diff --git a/src/main/java/sh/okx/rankup/prestige/Prestiges.java b/src/main/java/sh/okx/rankup/prestige/Prestiges.java index 8514cff..1880da8 100644 --- a/src/main/java/sh/okx/rankup/prestige/Prestiges.java +++ b/src/main/java/sh/okx/rankup/prestige/Prestiges.java @@ -1,13 +1,26 @@ package sh.okx.rankup.prestige; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.file.FileConfiguration; +import sh.okx.rankup.RankupPlugin; import sh.okx.rankup.ranks.RankElement; import sh.okx.rankup.ranks.RankList; -import sh.okx.rankup.RankupPlugin; public class Prestiges extends RankList { public Prestiges(RankupPlugin plugin, FileConfiguration config) { - super(plugin, config, section -> Prestige.deserialize(plugin, section)); + super(plugin, convert(plugin, config)); + } + + private static List convert(RankupPlugin plugin, FileConfiguration config) { + Map values = config.getValues(false); + List prestiges = new ArrayList<>(values.size()); + for (Map.Entry entry : values.entrySet()) { + prestiges.add(Prestige.deserialize(plugin, (ConfigurationSection) entry.getValue())); + } + return prestiges; } @Override diff --git a/src/main/java/sh/okx/rankup/ranks/Rank.java b/src/main/java/sh/okx/rankup/ranks/Rank.java index b2f81c9..9fed31e 100644 --- a/src/main/java/sh/okx/rankup/ranks/Rank.java +++ b/src/main/java/sh/okx/rankup/ranks/Rank.java @@ -1,20 +1,17 @@ package sh.okx.rankup.ranks; +import java.util.List; import lombok.AccessLevel; import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.RequiredArgsConstructor; -import me.clip.placeholderapi.PlaceholderAPI; import org.bukkit.Bukkit; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.entity.Player; import sh.okx.rankup.RankupPlugin; -import sh.okx.rankup.messages.MessageBuilder; import sh.okx.rankup.ranks.requirements.RankRequirements; import sh.okx.rankup.requirements.Requirement; -import java.util.List; - @EqualsAndHashCode @RequiredArgsConstructor(access = AccessLevel.PROTECTED) public class Rank { @@ -50,10 +47,7 @@ public void applyRequirements(Player player) { public void runCommands(Player player, Rank next) { for (String command : commands) { - String string = new MessageBuilder(command).replaceRanks(player, this, next).toString(); - if (Bukkit.getPluginManager().isPluginEnabled("PlaceholderAPI")) { - string = PlaceholderAPI.setPlaceholders(player, string); - } + String string = plugin.newMessageBuilder(command).replacePlayer(player).replaceOldRank(this).replaceRank(next).toString(player); Bukkit.dispatchCommand(Bukkit.getConsoleSender(), string); } } diff --git a/src/main/java/sh/okx/rankup/ranks/RankList.java b/src/main/java/sh/okx/rankup/ranks/RankList.java index f0546d8..24d6341 100644 --- a/src/main/java/sh/okx/rankup/ranks/RankList.java +++ b/src/main/java/sh/okx/rankup/ranks/RankList.java @@ -1,14 +1,11 @@ package sh.okx.rankup.ranks; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.List; -import java.util.Map; import java.util.Objects; -import java.util.function.Function; import lombok.Getter; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.entity.Player; import sh.okx.rankup.RankupPlugin; @@ -16,25 +13,17 @@ public abstract class RankList { protected RankupPlugin plugin; @Getter - protected FileConfiguration config; - @Getter private RankTree tree; - public RankList(RankupPlugin plugin, FileConfiguration config, - Function deserializer) { + public RankList(RankupPlugin plugin, Collection ranks) { this.plugin = plugin; - this.config = config; List> rankElements = new ArrayList<>(); - for (Map.Entry entry : config.getValues(false).entrySet()) { - ConfigurationSection rankSection = (ConfigurationSection) entry.getValue(); - if (validateSection(rankSection)) { - T apply = deserializer.apply(rankSection); - if (apply != null) { - // find next - rankElements.add(findNext(apply, rankElements)); - } + for (T rank : ranks) { + if (rank != null && validateSection(rank)) { + // find next + rankElements.add(findNext(rank, rankElements)); } else { - plugin.getLogger().warning("Ignoring rank: " + entry.getKey()); + plugin.getLogger().warning("Ignoring rank: " + rank); } } @@ -72,15 +61,15 @@ private RankElement findNext(T rank, List> rankElements) { return currentElement; } - protected boolean validateSection(ConfigurationSection section) { - String name = "'" + section.getName() + "'"; - String nextField = section.getString("next"); + protected boolean validateSection(T rank) { + String name = rank.getRank() == null ? "rank" : rank.getRank(); + String nextField = rank.getNext(); if (nextField == null || nextField.isEmpty()) { plugin.getLogger().warning("Rankup section " + name + " does not have a 'next' field."); plugin.getLogger().warning("Having a final rank (for example: \"Z: rank: 'Z'\") from 3.4.2 or earlier should no longer be used."); plugin.getLogger().warning("If this is intended as a final rank, you should delete " + name); return false; - } else if (!section.contains("requirements")) { + } else if (rank.getRequirements() == null) { plugin.getLogger().warning("Rank " + name + " does not have any requirements."); return false; } @@ -91,7 +80,7 @@ public T getFirst() { return tree.getFirst().getRank(); } - public T getByName(String name) { + public T getRankByName(String name) { if (name == null) { return null; } @@ -103,6 +92,20 @@ public T getByName(String name) { return null; } + public RankElement getByName(String name) { + if (name == null) { + return null; + } + List> rankElements = tree.asList(); + for (RankElement rank : rankElements) { + if (name.equalsIgnoreCase(rank.getRank().getRank())) { + return rank; + } + } + return null; + } + + public RankElement getByPlayer(Player player) { List> list = tree.asList(); Collections.reverse(list); diff --git a/src/main/java/sh/okx/rankup/ranks/Rankup.java b/src/main/java/sh/okx/rankup/ranks/Rankup.java index 1709e12..8a29f45 100644 --- a/src/main/java/sh/okx/rankup/ranks/Rankup.java +++ b/src/main/java/sh/okx/rankup/ranks/Rankup.java @@ -1,32 +1,30 @@ package sh.okx.rankup.ranks; +import java.util.Objects; import org.bukkit.configuration.ConfigurationSection; import sh.okx.rankup.RankupPlugin; import sh.okx.rankup.ranks.requirements.RankRequirements; import sh.okx.rankup.ranks.requirements.RankRequirementsFactory; import java.util.List; +import sh.okx.rankup.serialization.RankSerialized; public class Rankup extends Rank { - public static Rankup deserialize(RankupPlugin plugin, ConfigurationSection section) { - String next = section.getString("next"); - String rank = section.getString("rank"); - String displayName = section.getString("display-name"); - - if (next == null || next.isEmpty()) { + public static Rankup deserialize(RankupPlugin plugin, RankSerialized serialized) { + if (serialized.getNext() == null || serialized.getNext().isEmpty()) { plugin.getLogger().warning("Having a final rank (for example: \"Z: rank: 'Z'\") from 3.4.2 or earlier should no longer be used."); - plugin.getLogger().warning("It is safe to just delete the final rank " + section.getName() + ""); - plugin.getLogger().warning("Rankup section '" + section.getName() + "' has a blank 'next' field, will be ignored."); + plugin.getLogger().warning("It is safe to just delete the final rank " + serialized.getRank() + ""); + plugin.getLogger().warning("Rankup section '" + serialized.getRank() + "' has a blank 'next' field, will be ignored."); return null; } - return new Rankup(section, + return new Rankup(serialized.getMessagesAsSection(), plugin, - next, - rank, - displayName, - RankRequirementsFactory.getRequirements(plugin, section), - section.getStringList("commands")); + serialized.getNext(), + serialized.getRank(), + serialized.getDisplayName(), + RankRequirementsFactory.getRequirements(plugin, serialized.getRequirements(), serialized.getPrestigeRequirements()), + Objects.requireNonNull(serialized.getCommands(), "rank commands are null")); } protected Rankup(ConfigurationSection section, RankupPlugin plugin, String next, String rank, String displayName, diff --git a/src/main/java/sh/okx/rankup/ranks/Rankups.java b/src/main/java/sh/okx/rankup/ranks/Rankups.java index ebb2c45..958c42a 100644 --- a/src/main/java/sh/okx/rankup/ranks/Rankups.java +++ b/src/main/java/sh/okx/rankup/ranks/Rankups.java @@ -1,12 +1,22 @@ package sh.okx.rankup.ranks; -import org.bukkit.configuration.file.FileConfiguration; +import java.util.ArrayList; +import java.util.List; import sh.okx.rankup.RankupPlugin; +import sh.okx.rankup.serialization.RankSerialized; public class Rankups extends RankList { - public Rankups(RankupPlugin plugin, FileConfiguration config) { - super(plugin, config, section -> Rankup.deserialize(plugin, section)); + public Rankups(RankupPlugin plugin, List serializedRanks) { + super(plugin, convert(plugin, serializedRanks)); + } + + private static List convert(RankupPlugin plugin, List ranks) { + List rankups = new ArrayList<>(ranks.size()); + for (RankSerialized rank : ranks) { + rankups.add(Rankup.deserialize(plugin, rank)); + } + return rankups; } @Override diff --git a/src/main/java/sh/okx/rankup/ranks/requirements/LastRankRequirements.java b/src/main/java/sh/okx/rankup/ranks/requirements/LastRankRequirements.java index c05f680..35d58a2 100644 --- a/src/main/java/sh/okx/rankup/ranks/requirements/LastRankRequirements.java +++ b/src/main/java/sh/okx/rankup/ranks/requirements/LastRankRequirements.java @@ -1,15 +1,14 @@ package sh.okx.rankup.ranks.requirements; import java.util.Collections; -import java.util.Set; import org.bukkit.entity.Player; import sh.okx.rankup.requirements.Requirement; public class LastRankRequirements implements RankRequirements { @Override - public Set getRequirements(Player player) { - return Collections.emptySet(); + public Iterable getRequirements(Player player) { + return Collections.emptyList(); } @Override diff --git a/src/main/java/sh/okx/rankup/ranks/requirements/ListRankRequirements.java b/src/main/java/sh/okx/rankup/ranks/requirements/ListRankRequirements.java index 0be4d86..38a139b 100644 --- a/src/main/java/sh/okx/rankup/ranks/requirements/ListRankRequirements.java +++ b/src/main/java/sh/okx/rankup/ranks/requirements/ListRankRequirements.java @@ -1,19 +1,19 @@ package sh.okx.rankup.ranks.requirements; -import java.util.Set; +import java.util.List; import org.bukkit.entity.Player; import sh.okx.rankup.requirements.DeductibleRequirement; import sh.okx.rankup.requirements.Requirement; public class ListRankRequirements implements RankRequirements { - private final Set requirements; + private final List requirements; - public ListRankRequirements(Set requirements) { + public ListRankRequirements(List requirements) { this.requirements = requirements; } @Override - public Set getRequirements(Player player) { + public Iterable getRequirements(Player player) { return requirements; } diff --git a/src/main/java/sh/okx/rankup/ranks/requirements/PrestigeListRankRequirements.java b/src/main/java/sh/okx/rankup/ranks/requirements/PrestigeListRankRequirements.java index a14b7ef..6d68a58 100644 --- a/src/main/java/sh/okx/rankup/ranks/requirements/PrestigeListRankRequirements.java +++ b/src/main/java/sh/okx/rankup/ranks/requirements/PrestigeListRankRequirements.java @@ -1,15 +1,13 @@ package sh.okx.rankup.ranks.requirements; +import java.util.Map; +import java.util.Objects; import org.bukkit.entity.Player; import sh.okx.rankup.RankupPlugin; import sh.okx.rankup.prestige.Prestige; import sh.okx.rankup.prestige.Prestiges; import sh.okx.rankup.requirements.Requirement; -import java.util.Map; -import java.util.Objects; -import java.util.Set; - public class PrestigeListRankRequirements implements RankRequirements { private final RankupPlugin plugin; private final RankRequirements defaultRequirements; @@ -25,7 +23,7 @@ public PrestigeListRankRequirements(RankupPlugin plugin, RankRequirements defaul } @Override - public Set getRequirements(Player player) { + public Iterable getRequirements(Player player) { return getRankRequirements(player).getRequirements(player); } diff --git a/src/main/java/sh/okx/rankup/ranks/requirements/RankRequirements.java b/src/main/java/sh/okx/rankup/ranks/requirements/RankRequirements.java index f8c0696..c3be109 100644 --- a/src/main/java/sh/okx/rankup/ranks/requirements/RankRequirements.java +++ b/src/main/java/sh/okx/rankup/ranks/requirements/RankRequirements.java @@ -1,11 +1,10 @@ package sh.okx.rankup.ranks.requirements; -import java.util.Set; import org.bukkit.entity.Player; import sh.okx.rankup.requirements.Requirement; public interface RankRequirements { - Set getRequirements(Player player); + Iterable getRequirements(Player player); boolean hasRequirements(Player player); Requirement getRequirement(Player player, String name); diff --git a/src/main/java/sh/okx/rankup/ranks/requirements/RankRequirementsFactory.java b/src/main/java/sh/okx/rankup/ranks/requirements/RankRequirementsFactory.java index 742aab1..3cb5a8b 100644 --- a/src/main/java/sh/okx/rankup/ranks/requirements/RankRequirementsFactory.java +++ b/src/main/java/sh/okx/rankup/ranks/requirements/RankRequirementsFactory.java @@ -3,9 +3,10 @@ import java.util.Collection; import java.util.Collections; import java.util.HashMap; +import java.util.List; import java.util.Map; -import java.util.Set; import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.MemoryConfiguration; import sh.okx.rankup.RankupPlugin; import sh.okx.rankup.requirements.Requirement; @@ -20,6 +21,22 @@ public static RankRequirements getRequirements(RankupPlugin plugin, Configuratio } } + public static RankRequirements getRequirements(RankupPlugin plugin, List requirements, + Map> prestigeRequirements) { + if (prestigeRequirements != null) { + ConfigurationSection section = new MemoryConfiguration(); + for (Map.Entry> entry : prestigeRequirements.entrySet()) { + section.set(entry.getKey(), entry.getValue()); + } + return getPrestigeListRequirements(plugin, section); + } else if (requirements != null) { + return getListRequirements(plugin, requirements); + } else { +// throw new IllegalArgumentException("No requirements set."); + return null; + } + } + private static Collection getRequirementStrings(ConfigurationSection section, String key) { if (section.isList(key)) { return section.getStringList(key); @@ -33,12 +50,12 @@ private static Collection getRequirementStrings(ConfigurationSection sec } } - private static Set stringsToRequirements(RankupPlugin plugin, Iterable strings) { + private static List stringsToRequirements(RankupPlugin plugin, Iterable strings) { return plugin.getRequirements().getRequirements(strings); } private static RankRequirements getListRequirements(RankupPlugin plugin, Iterable list) { - Set requirements = stringsToRequirements(plugin, list); + List requirements = stringsToRequirements(plugin, list); return new ListRankRequirements(requirements); } diff --git a/src/main/java/sh/okx/rankup/ranksgui/RanksGui.java b/src/main/java/sh/okx/rankup/ranksgui/RanksGui.java index 1a94fe4..c792865 100644 --- a/src/main/java/sh/okx/rankup/ranksgui/RanksGui.java +++ b/src/main/java/sh/okx/rankup/ranksgui/RanksGui.java @@ -35,9 +35,9 @@ public void open() { ConfigurationSection basePath = plugin.getMessages().getConfigurationSection("rankup.ranksgui"); String title = get(ConfigurationSection::getString, "title", playerPath, basePath, "Ranks"); - int rows = get(ConfigurationSection::getInt, "rows", playerPath, basePath, 3); - int offset = get(ConfigurationSection::getInt, "offset", playerPath, basePath, 10); - int width = get(ConfigurationSection::getInt, "width", playerPath, basePath, 7); + int rows = get(Gui::getInt, "rows", playerPath, basePath, 3); + int offset = get(Gui::getInt, "offset", playerPath, basePath, 10); + int width = get(Gui::getInt, "width", playerPath, basePath, 7); inventory = Bukkit.createInventory(null, rows * 9, Colour.translate(title)); @@ -87,6 +87,7 @@ public void open() { player.openInventory(inventory); } + private T get(BiFunction fun, String path, ConfigurationSection primary, ConfigurationSection secondary, T def) { T get = null; if (primary != null) { diff --git a/src/main/java/sh/okx/rankup/requirements/ProgressiveRequirement.java b/src/main/java/sh/okx/rankup/requirements/ProgressiveRequirement.java index 0a6fcec..17e0e9e 100644 --- a/src/main/java/sh/okx/rankup/requirements/ProgressiveRequirement.java +++ b/src/main/java/sh/okx/rankup/requirements/ProgressiveRequirement.java @@ -22,12 +22,8 @@ public boolean check(Player player) { } @Override - public final double getRemaining(Player player) { - return getRemaining(player, 1); - } - - public double getRemaining(Player player, double multiplier) { - return Math.max(0, (multiplier * getTotal(player)) - getProgress(player)); + public double getRemaining(Player player) { + return Math.max(0, getTotal(player) - getProgress(player)); } @Override diff --git a/src/main/java/sh/okx/rankup/requirements/RequirementRegistry.java b/src/main/java/sh/okx/rankup/requirements/RequirementRegistry.java index 437294a..868f93b 100644 --- a/src/main/java/sh/okx/rankup/requirements/RequirementRegistry.java +++ b/src/main/java/sh/okx/rankup/requirements/RequirementRegistry.java @@ -1,7 +1,9 @@ package sh.okx.rankup.requirements; +import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; +import java.util.List; import java.util.Objects; import java.util.Set; @@ -29,8 +31,8 @@ public Requirement newRequirement(String name, String value) { return null; } - public Set getRequirements(Iterable list) { - Set requirements = new HashSet<>(); + public List getRequirements(Iterable list) { + List requirements = new ArrayList<>(); for (String req : list) { String[] parts = req.split(" ", 2); diff --git a/src/main/java/sh/okx/rankup/requirements/requirement/ItemDeductibleRequirement.java b/src/main/java/sh/okx/rankup/requirements/requirement/ItemDeductibleRequirement.java index 2168421..c8a5424 100644 --- a/src/main/java/sh/okx/rankup/requirements/requirement/ItemDeductibleRequirement.java +++ b/src/main/java/sh/okx/rankup/requirements/requirement/ItemDeductibleRequirement.java @@ -2,6 +2,7 @@ import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.PlayerInventory; import sh.okx.rankup.RankupPlugin; import sh.okx.rankup.requirements.DeductibleRequirement; import sh.okx.rankup.requirements.Requirement; @@ -20,7 +21,13 @@ public ItemDeductibleRequirement(ItemDeductibleRequirement clone) { public void apply(Player player, double multiplier) { int count = (int) (getTotal(player) * multiplier); - ItemStack[] contents = player.getInventory().getStorageContents(); + PlayerInventory inventory = player.getInventory(); + ItemStack[] contents; + if (ItemRequirement.USE_STORAGE_CONTENTS) { + contents = inventory.getStorageContents(); + } else { + contents = inventory.getContents(); + } for (int i = 0; i < contents.length && count > 0; i++) { ItemStack item = contents[i]; @@ -35,7 +42,11 @@ public void apply(Player player, double multiplier) { } } - player.getInventory().setStorageContents(contents); + if (ItemRequirement.USE_STORAGE_CONTENTS) { + inventory.setStorageContents(contents); + } else { + inventory.setContents(contents); + } if (count > 0) { throw new IllegalStateException("REPORT THIS ERROR TO THE DEV - COULD NOT DEDUCT ALL ITEMS"); diff --git a/src/main/java/sh/okx/rankup/requirements/requirement/ItemRequirement.java b/src/main/java/sh/okx/rankup/requirements/requirement/ItemRequirement.java index cbd9748..487557e 100644 --- a/src/main/java/sh/okx/rankup/requirements/requirement/ItemRequirement.java +++ b/src/main/java/sh/okx/rankup/requirements/requirement/ItemRequirement.java @@ -2,7 +2,9 @@ import org.bukkit.Material; import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.PlayerInventory; import sh.okx.rankup.RankupPlugin; import sh.okx.rankup.requirements.ProgressiveRequirement; import sh.okx.rankup.requirements.Requirement; @@ -10,6 +12,19 @@ import java.util.Arrays; public class ItemRequirement extends ProgressiveRequirement { + public static final boolean USE_STORAGE_CONTENTS; + + static { + boolean getStorageContentsMethodExists; + try { + Inventory.class.getMethod("getStorageContents"); + getStorageContentsMethodExists = true; + } catch (NoSuchMethodException e) { + getStorageContentsMethodExists = false; + } + USE_STORAGE_CONTENTS = getStorageContentsMethodExists; + } + public ItemRequirement(RankupPlugin plugin, String name) { super(plugin, name, true); } @@ -25,7 +40,14 @@ public Requirement clone() { @Override public double getProgress(Player player) { - return Arrays.stream(player.getInventory().getStorageContents()) + PlayerInventory inventory = player.getInventory(); + ItemStack[] contents; + if (USE_STORAGE_CONTENTS) { + contents = inventory.getStorageContents(); + } else { + contents = inventory.getContents(); + } + return Arrays.stream(contents) .filter(this::matchItem) .mapToInt(ItemStack::getAmount).sum(); } diff --git a/src/main/java/sh/okx/rankup/requirements/requirement/PlaceholderRequirement.java b/src/main/java/sh/okx/rankup/requirements/requirement/PlaceholderRequirement.java index b727e6e..583833f 100644 --- a/src/main/java/sh/okx/rankup/requirements/requirement/PlaceholderRequirement.java +++ b/src/main/java/sh/okx/rankup/requirements/requirement/PlaceholderRequirement.java @@ -28,6 +28,8 @@ public double getProgress(Player player) { switch (parts[1]) { case "=": return parsed.equals(value) ? 1 : 0; + case "!=": + return parsed.equals(value) ? 0 : 1; } // numeric operations @@ -35,15 +37,15 @@ public double getProgress(Player player) { double v = Double.parseDouble(value.replace(",", "")); switch (parts[1]) { case ">": - return p > v ? v : 0; + return p > v ? 1 : 0; case ">=": return Math.min(p, v); case "<": - return p < v ? v : 0; + return p < v ? 1 : 0; case "<=": return p <= v ? 1 : 0; case "==": - return p == v ? v : 0; + return p == v ? 1 : 0; } throw new IllegalArgumentException("Invalid operation: " + parts[1]); } @@ -52,17 +54,19 @@ public double getProgress(Player player) { public double getTotal(Player player) { String[] parts = getParts(player); - if (parts[1].equalsIgnoreCase("=")) { - return 1; - } else { - return Double.parseDouble(parts[2]); + switch (parts[1]) { + case ">=": + return Double.parseDouble(parts[2]); + default: + return 1; } } private String[] getParts(Player player) { String[] parts = getValueString().split(" "); if (parts.length < 3) { - throw new IllegalArgumentException("Placeholder requirements must be in the form %placeholder% string"); + throw new IllegalArgumentException( + "Placeholder requirements must be in the form %placeholder% string"); } String parsed = PlaceholderAPI.setPlaceholders(player, parts[0]); if (!PlaceholderAPI.containsPlaceholders(parts[0]) || parsed.equals(parts[0])) { diff --git a/src/main/java/sh/okx/rankup/requirements/requirement/mcmmo/McMMOSkillUtil.java b/src/main/java/sh/okx/rankup/requirements/requirement/mcmmo/McMMOSkillUtil.java index b792433..3b9d974 100644 --- a/src/main/java/sh/okx/rankup/requirements/requirement/mcmmo/McMMOSkillUtil.java +++ b/src/main/java/sh/okx/rankup/requirements/requirement/mcmmo/McMMOSkillUtil.java @@ -2,10 +2,9 @@ import com.gmail.nossr50.datatypes.player.McMMOPlayer; import com.gmail.nossr50.util.player.UserManager; -import org.bukkit.entity.Player; - import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import org.bukkit.entity.Player; /** * Because mcMMO like changing the name of their skill types. diff --git a/src/main/java/sh/okx/rankup/serialization/PrestigeSerialized.java b/src/main/java/sh/okx/rankup/serialization/PrestigeSerialized.java new file mode 100644 index 0000000..df219d2 --- /dev/null +++ b/src/main/java/sh/okx/rankup/serialization/PrestigeSerialized.java @@ -0,0 +1,25 @@ +package sh.okx.rankup.serialization; + +import java.util.List; +import java.util.Map; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.ToString; + +@Getter +@ToString(callSuper = true) +@EqualsAndHashCode(callSuper = true) +public class PrestigeSerialized extends RankSerialized { + + private final String from; + private final String to; + + public PrestigeSerialized(String rank, String next, String displayName, + List commands, List requirements, + Map> prestigeRequirements, + Map messages, String from, String to) { + super(rank, next, displayName, commands, requirements, prestigeRequirements, messages); + this.from = from; + this.to = to; + } +} diff --git a/src/main/java/sh/okx/rankup/serialization/RankSerialized.java b/src/main/java/sh/okx/rankup/serialization/RankSerialized.java new file mode 100644 index 0000000..c4bdb5b --- /dev/null +++ b/src/main/java/sh/okx/rankup/serialization/RankSerialized.java @@ -0,0 +1,31 @@ +package sh.okx.rankup.serialization; + +import java.util.List; +import java.util.Map; +import lombok.Data; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.MemoryConfiguration; + +@Data +public class RankSerialized { + + private final String rank; + private final String next; + + private final String displayName; + + private final List commands; + + private final List requirements; + private final Map> prestigeRequirements; + + private final Map messages; + + public ConfigurationSection getMessagesAsSection() { + ConfigurationSection section = new MemoryConfiguration(); + for (Map.Entry entry : messages.entrySet()) { + section.set(entry.getKey(), entry.getValue()); + } + return section; + } +} diff --git a/src/main/java/sh/okx/rankup/serialization/ShadowDeserializer.java b/src/main/java/sh/okx/rankup/serialization/ShadowDeserializer.java new file mode 100644 index 0000000..2936367 --- /dev/null +++ b/src/main/java/sh/okx/rankup/serialization/ShadowDeserializer.java @@ -0,0 +1,69 @@ +package sh.okx.rankup.serialization; + +import com.electronwill.nightconfig.core.UnmodifiableConfig; +import com.electronwill.nightconfig.core.UnmodifiableConfig.Entry; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class ShadowDeserializer { + + public static List deserialize(UnmodifiableConfig ranks) { + List ranksList = new ArrayList<>(ranks.size()); + for (Entry entry : ranks.entrySet()) { + UnmodifiableConfig value = entry.getValue(); + if (value == null) continue; + String rank = value.get("rank"); + String next = value.get("next"); + String displayName = value.get("display-name"); + List commands = value.getOrElse("commands", Collections.emptyList()); + List requirements; + Map> prestigeRequirements; + Object requirementsObject = value.get("requirements"); + if (requirementsObject instanceof UnmodifiableConfig) { + requirements = null; + UnmodifiableConfig requirementsConfig = (UnmodifiableConfig) requirementsObject; + prestigeRequirements = new HashMap<>(requirementsConfig.size()); + for (Entry requirementEntry : requirementsConfig.entrySet()) { + prestigeRequirements.put(requirementEntry.getKey(), requirementEntry.getValue()); + } + } else { + prestigeRequirements = null; + if (requirementsObject instanceof String) { + requirements = Collections.singletonList((String) requirementsObject); + } else if (requirementsObject instanceof List) { + requirements = (List) requirementsObject; + } else { + requirements = Collections.emptyList(); + } + } + + UnmodifiableConfig messagesConfig = value.get("rankup"); + Map messages; + if (messagesConfig != null) { + messages = new HashMap<>(); + updateMap(messages, messagesConfig, "rankup."); + } else { + messages = Collections.emptyMap(); + } + + ranksList.add(new RankSerialized(rank, next, displayName, commands, requirements, prestigeRequirements, messages)); + } + return ranksList; + } + + private static void updateMap(Map map, UnmodifiableConfig config, String prefix) { + for (Entry message : config.entrySet()) { + Object value = message.getValue(); + if (value != null) { + if (value instanceof UnmodifiableConfig) { + updateMap(map, (UnmodifiableConfig) value, prefix + message.getKey() + "."); + } else { + map.put(prefix + message.getKey(), String.valueOf(value)); + } + } + } + } +} diff --git a/src/main/java/sh/okx/rankup/serialization/YamlDeserializer.java b/src/main/java/sh/okx/rankup/serialization/YamlDeserializer.java new file mode 100644 index 0000000..6c04b27 --- /dev/null +++ b/src/main/java/sh/okx/rankup/serialization/YamlDeserializer.java @@ -0,0 +1,59 @@ +package sh.okx.rankup.serialization; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.MemorySection; + +public class YamlDeserializer { + + public static List deserialize(ConfigurationSection ranks) { + Set rankKeys = ranks.getKeys(false); + List ranksList = new ArrayList<>(rankKeys.size()); + for (String rankKey : rankKeys) { + ConfigurationSection section = ranks.getConfigurationSection(rankKey); + if (section == null) continue; + String rank = section.getString("rank"); + String next = section.getString("next"); + String displayName = section.getString("display-name"); + List commands = section.getStringList("commands"); + List requirements; + Map> prestigeRequirements; + if (section.isConfigurationSection("requirements")) { + requirements = null; + ConfigurationSection requirementsSection = section.getConfigurationSection("requirements"); + Set keys = requirementsSection.getKeys(false); + prestigeRequirements = new HashMap<>(keys.size()); + for (String key : keys) { + prestigeRequirements.put(key, requirementsSection.getStringList(key)); + } + } else { + prestigeRequirements = null; + requirements = section.getStringList("requirements"); + } + + ConfigurationSection rankupSection = section.getConfigurationSection("rankup"); + Map messages; + if (rankupSection != null) { + + Set rankup = rankupSection.getKeys(true); + messages = new HashMap<>(rankup.size()); + for (String key : rankup) { + if (!rankupSection.isConfigurationSection(key)) { + messages.put(MemorySection.createPath(rankupSection, key, section), rankupSection.getString(key)); + } + } + } else { + messages = Collections.emptyMap(); + } + + ranksList.add(new RankSerialized(rank, next, displayName, commands, requirements, prestigeRequirements, messages)); + } + return ranksList; + } + +} diff --git a/src/main/java/sh/okx/rankup/text/ChainedTextProcessor.java b/src/main/java/sh/okx/rankup/text/ChainedTextProcessor.java new file mode 100644 index 0000000..8e9c303 --- /dev/null +++ b/src/main/java/sh/okx/rankup/text/ChainedTextProcessor.java @@ -0,0 +1,17 @@ +package sh.okx.rankup.text; + +public class ChainedTextProcessor implements TextProcessor { + private final TextProcessor[] processors; + + public ChainedTextProcessor(TextProcessor... processors) { + this.processors = processors; + } + + @Override + public String process(String string) { + for (TextProcessor processor : processors) { + string = processor.process(string); + } + return string; + } +} diff --git a/src/main/java/sh/okx/rankup/text/ColourTextProcessor.java b/src/main/java/sh/okx/rankup/text/ColourTextProcessor.java new file mode 100644 index 0000000..47047a0 --- /dev/null +++ b/src/main/java/sh/okx/rankup/text/ColourTextProcessor.java @@ -0,0 +1,11 @@ +package sh.okx.rankup.text; + +import sh.okx.rankup.util.Colour; + +public class ColourTextProcessor implements TextProcessor { + + @Override + public String process(String string) { + return Colour.translate(string); + } +} diff --git a/src/main/java/sh/okx/rankup/text/LegacyTextProcessor.java b/src/main/java/sh/okx/rankup/text/LegacyTextProcessor.java new file mode 100644 index 0000000..8881ac2 --- /dev/null +++ b/src/main/java/sh/okx/rankup/text/LegacyTextProcessor.java @@ -0,0 +1,107 @@ +package sh.okx.rankup.text; + +import java.util.Map; +import java.util.function.Function; +import sh.okx.rankup.messages.pebble.RankContext; +import sh.okx.rankup.placeholders.Placeholders; + +public class LegacyTextProcessor implements TextProcessor { + + private final Map pebbleContext; + private final Placeholders options; + + public LegacyTextProcessor(Map pebbleContext, Placeholders options) { + this.pebbleContext = pebbleContext; + this.options = options; + } + + + @Override + public String process(String string) { + StringBuilder output = new StringBuilder(); + StringBuilder buffer = new StringBuilder(); + boolean isPlaceholder = false; + + char[] chars = string.toCharArray(); + for (int i = 0; i < chars.length; i++) { + char c = chars[i]; + if (c == '{') { + if (i + 1 < chars.length) { + if (chars[i + 1] != '{') { + isPlaceholder = true; + } else { + output.append(c).append(chars[i + 1]); + i++; + } + } else { + output.append(c); + } + } else if (c == '}' && isPlaceholder) { + output.append(replacePlaceholder(buffer.toString())); + buffer.delete(0, buffer.length()); + isPlaceholder = false; + } else if (isPlaceholder) { + buffer.append(c); + } else { + output.append(c); + } + } + + return output.toString(); + } + + private String replacePlaceholder(String p) { + if ("player".equalsIgnoreCase(p)) { + return get("player", p); + } else if ("old_rank".equalsIgnoreCase(p)) { + return get("rank", p, o -> ((RankContext) o).getRank()); + } else if ("rank".equalsIgnoreCase(p)) { + return get("next", p, o -> ((RankContext) o).getRank()); + } else if ("old_rank_name".equalsIgnoreCase(p)) { + return get("rank", p, o -> ((RankContext) o).getName()); + } else if ("rank_name".equalsIgnoreCase(p)) { + return get("next", p, o -> ((RankContext) o).getName()); + } else if ("money".equalsIgnoreCase(p)) { + return get("rank", p, o -> this.options.getMoneyFormat().format( + ((RankContext) o).getReq("money").getTotal())); + } else if ("money_needed".equalsIgnoreCase(p)) { + return get("rank", p, o -> this.options.getMoneyFormat().format( + ((RankContext) o).getReq("money").getRemaining())); + } else if (p.toLowerCase().startsWith("amount ")) { + String requirement = p.substring("amount ".length()); + return get("rank", p, o -> this.options.getSimpleFormat() + .format(((RankContext) o).getReq(requirement).getTotal())); + } else if (p.toLowerCase().startsWith("amount_done ")) { + String requirement = p.substring("amount_done ".length()); + return get("rank", p, o -> this.options.getSimpleFormat() + .format(((RankContext) o).getReq(requirement).getProgress())); + } else if (p.toLowerCase().startsWith("amount_needed ")) { + String requirement = p.substring("amount_needed ".length()); + return get("rank", p, o -> this.options.getSimpleFormat() + .format(((RankContext) o).getReq(requirement).getRemaining())); + } else if (p.toLowerCase().startsWith("percent_done ")) { + String requirement = p.substring("percent_done ".length()); + return get("rank", p, o -> this.options.getPercentFormat() + .format(((RankContext) o).getReq(requirement).getPercent())); + } else if (p.toLowerCase().startsWith("percent_left ")) { + String requirement = p.substring("percent_left ".length()); + return get("rank", p, o -> this.options.getPercentFormat() + .format(100 - ((RankContext) o).getReq(requirement).getPercent())); + } + + return get(p, "{" + p + "}"); + } + + private String get(String key, String def) { + return get(key, def, String::valueOf); + } + + private String get(String key, String def, Function mapper) { + Object val = pebbleContext.get(key); + if (val == null) { + return def; + } else { + return mapper.apply(val); + } + } +} diff --git a/src/main/java/sh/okx/rankup/text/PlaceholderApiTextProcessor.java b/src/main/java/sh/okx/rankup/text/PlaceholderApiTextProcessor.java new file mode 100644 index 0000000..79707fa --- /dev/null +++ b/src/main/java/sh/okx/rankup/text/PlaceholderApiTextProcessor.java @@ -0,0 +1,23 @@ +package sh.okx.rankup.text; + +import me.clip.placeholderapi.PlaceholderAPI; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; + +public class PlaceholderApiTextProcessor implements TextProcessor { + + private final Player player; + + public PlaceholderApiTextProcessor(Player player) { + this.player = player; + } + + @Override + public String process(String string) { + if (player == null || !Bukkit.getPluginManager().isPluginEnabled("PlaceholderAPI")) { + return string; + } else { + return PlaceholderAPI.setPlaceholders(player, string); + } + } +} diff --git a/src/main/java/sh/okx/rankup/text/TextProcessor.java b/src/main/java/sh/okx/rankup/text/TextProcessor.java new file mode 100644 index 0000000..2a44a51 --- /dev/null +++ b/src/main/java/sh/okx/rankup/text/TextProcessor.java @@ -0,0 +1,5 @@ +package sh.okx.rankup.text; + +public interface TextProcessor { + String process(String string); +} diff --git a/src/main/java/sh/okx/rankup/text/TextProcessorBuilder.java b/src/main/java/sh/okx/rankup/text/TextProcessorBuilder.java new file mode 100644 index 0000000..1baa96d --- /dev/null +++ b/src/main/java/sh/okx/rankup/text/TextProcessorBuilder.java @@ -0,0 +1,38 @@ +package sh.okx.rankup.text; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.Nullable; +import sh.okx.rankup.placeholders.Placeholders; +import sh.okx.rankup.text.pebble.PebbleTextProcessor; + +public class TextProcessorBuilder { + + private final List processors = new ArrayList<>(); + + public TextProcessorBuilder colour() { + processors.add(new ColourTextProcessor()); + return this; + } + + public TextProcessorBuilder pebble(Map context, Placeholders options) { + processors.add(new PebbleTextProcessor(context, options)); + return this; + } + + public TextProcessorBuilder papi(@Nullable Player player) { + processors.add(new PlaceholderApiTextProcessor(player)); + return this; + } + + public TextProcessorBuilder legacy(Map context, Placeholders options) { + processors.add(new LegacyTextProcessor(context, options)); + return this; + } + + public TextProcessor create() { + return new ChainedTextProcessor(processors.toArray(new TextProcessor[0])); + } +} diff --git a/src/main/java/sh/okx/rankup/text/pebble/DecimalFormatFilter.java b/src/main/java/sh/okx/rankup/text/pebble/DecimalFormatFilter.java new file mode 100644 index 0000000..ff7a5c5 --- /dev/null +++ b/src/main/java/sh/okx/rankup/text/pebble/DecimalFormatFilter.java @@ -0,0 +1,38 @@ +package sh.okx.rankup.text.pebble; + +import com.mitchellbosecke.pebble.error.PebbleException; +import com.mitchellbosecke.pebble.extension.Filter; +import com.mitchellbosecke.pebble.template.EvaluationContext; +import com.mitchellbosecke.pebble.template.PebbleTemplate; +import java.text.DecimalFormat; +import java.util.List; +import java.util.Map; + +public class DecimalFormatFilter implements Filter { + + private final DecimalFormat format; + + public DecimalFormatFilter(DecimalFormat format) { + this.format = format; + } + + @Override + public List getArgumentNames() { + return null; + } + + @Override + public Object apply(Object input, Map args, PebbleTemplate self, + EvaluationContext context, int lineNumber) throws PebbleException { + if (input == null) { + return null; + } + if (!(input instanceof Number)) { + throw new PebbleException(null, "The input for the 'DecimalFormatFilter' filter has to be a number: " + input, + lineNumber, self.getName()); + } + + Number number = (Number) input; + return this.format.format(number); + } +} diff --git a/src/main/java/sh/okx/rankup/text/pebble/MoneyShortFilter.java b/src/main/java/sh/okx/rankup/text/pebble/MoneyShortFilter.java new file mode 100644 index 0000000..804e29d --- /dev/null +++ b/src/main/java/sh/okx/rankup/text/pebble/MoneyShortFilter.java @@ -0,0 +1,38 @@ +package sh.okx.rankup.text.pebble; + +import com.mitchellbosecke.pebble.error.PebbleException; +import com.mitchellbosecke.pebble.extension.Filter; +import com.mitchellbosecke.pebble.template.EvaluationContext; +import com.mitchellbosecke.pebble.template.PebbleTemplate; +import java.util.List; +import java.util.Map; +import sh.okx.rankup.placeholders.Placeholders; + +public class MoneyShortFilter implements Filter { + + private final Placeholders placeholders; + + public MoneyShortFilter(Placeholders placeholders) { + this.placeholders = placeholders; + } + + @Override + public List getArgumentNames() { + return null; + } + + @Override + public Object apply(Object input, Map args, PebbleTemplate self, + EvaluationContext context, int lineNumber) throws PebbleException { + if (input == null) { + return null; + } + if (!(input instanceof Number)) { + throw new PebbleException(null, "The input for the 'MoneyShortFilter' filter has to be a number: " + input, + lineNumber, self.getName()); + } + + Number number = (Number) input; + return placeholders.formatMoney(number.doubleValue()); + } +} diff --git a/src/main/java/sh/okx/rankup/text/pebble/PebbleTextProcessor.java b/src/main/java/sh/okx/rankup/text/pebble/PebbleTextProcessor.java new file mode 100644 index 0000000..2a2878c --- /dev/null +++ b/src/main/java/sh/okx/rankup/text/pebble/PebbleTextProcessor.java @@ -0,0 +1,82 @@ +package sh.okx.rankup.text.pebble; + +import com.mitchellbosecke.pebble.PebbleEngine; +import com.mitchellbosecke.pebble.extension.AbstractExtension; +import com.mitchellbosecke.pebble.extension.Filter; +import com.mitchellbosecke.pebble.loader.StringLoader; +import java.io.IOException; +import java.io.StringWriter; +import java.lang.reflect.InvocationTargetException; +import java.text.DecimalFormat; +import java.util.HashMap; +import java.util.Map; +import java.util.logging.Logger; +import org.bukkit.plugin.java.JavaPlugin; +import sh.okx.rankup.RankupPlugin; +import sh.okx.rankup.messages.pebble.InvalidRequirementException; +import sh.okx.rankup.placeholders.Placeholders; +import sh.okx.rankup.text.TextProcessor; + +public class PebbleTextProcessor implements TextProcessor { + + private final Map context; + private final Placeholders options; + + public PebbleTextProcessor(Map context, Placeholders options) { + this.context = context; + this.options = options; + } + + @Override + public String process(String string) { + PebbleEngine engine = new PebbleEngine.Builder().autoEscaping(false).extension( + new AbstractExtension() { + @Override + public Map getFilters() { + Map filters = new HashMap<>(); + if (options != null) { + DecimalFormat moneyFormat = options.getMoneyFormat(); + if (moneyFormat != null) { + filters.put("money", new DecimalFormatFilter(moneyFormat)); + filters.put("shortmoney", new MoneyShortFilter(options)); + } + + DecimalFormat percentFormat = options.getPercentFormat(); + if (percentFormat != null) filters.put("percent", new DecimalFormatFilter(percentFormat)); + + DecimalFormat simpleFormat = options.getSimpleFormat(); + if (simpleFormat != null) filters.put("simple", new DecimalFormatFilter(simpleFormat)); + } + return filters; + } + }) + .loader(new StringLoader()).build(); + StringWriter writer = new StringWriter(); + try { + try { + engine.getTemplate(string).evaluate(writer, context); + return writer.toString(); + } catch (RuntimeException ex) { + if (ex.getCause() instanceof InvocationTargetException) { + if (ex.getCause().getCause() instanceof InvalidRequirementException) { + InvalidRequirementException cause = (InvalidRequirementException) ex.getCause().getCause(); + Logger logger = JavaPlugin.getPlugin(RankupPlugin.class).getLogger(); + logger.severe("Unknown requirement \"" + cause.getRequirement() + "\" on rank \"" + cause.getRank().getRank() + "\" in message:"); + for (String line : string.split("\n")) { + logger.severe(line); + } + logger.severe("Change the message to not use that requirement, or add the requirement to the rank in the config."); + } else { + ex.printStackTrace(); + } + } else { + ex.printStackTrace(); + } + return "Unable to parse message, please check console"; + } + } catch (IOException e) { + e.printStackTrace(); + return string; + } + } +} diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 6b80070..4787fbe 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -1,5 +1,5 @@ # this is used for letting you know that you need to update/change your config file -version: 9 +version: 10 # the locale to use for messages # all messages can be customised but this allows you to @@ -75,7 +75,7 @@ text: timeout: 10 # placeholders: -# https://github.com/okx-code/Rankup3/wiki/PAPI-Placeholders +# https://okx.sh/rankup/Placeholders.html placeholders: # format for money. for more information, see # https://docs.oracle.com/javase/8/docs/api/java/text/DecimalFormat.html @@ -101,6 +101,7 @@ placeholders: # set to an empty list to disable # for each entry here, it counts as increasing by a factor of 1,000 # the first represents thousands (1,000) then millions (1,000,000) then billions (1,000,000,000) etc. +# this is used in the "| shortmoney" filter shorten: - 'K' - 'M' diff --git a/src/main/resources/locale/en.yml b/src/main/resources/locale/en.yml index 8900d05..b7b781b 100644 --- a/src/main/resources/locale/en.yml +++ b/src/main/resources/locale/en.yml @@ -1,23 +1,20 @@ # the messages in this section can be customised for each rankup in rankups.yml. rankup: - requirements-not-met: "&cYou need {MONEY} money to rankup." + requirements-not-met: "&cYou need {{rank.requirement('money').total | money}} money to rankup." no-rankup: "&eYou are at the highest rank." # set to an empty string, ie: success-public: "" # to hide that message. - success-public: "&a{PLAYER} &ehas ranked up to: &d{RANK}" - success-private: "&aYou have ranked up to: &d{RANK}" + success-public: "&a{{player}} &ehas ranked up to: &d{{next.rank}}" + success-private: "&aYou have ranked up to: &d{{next.rank}}" # used for the text confirmation confirmation: |- - &eAre you sure you want to rankup to &a{RANK}&e? + &eAre you sure you want to rankup to &a{{next.rank}}&e? &eType &c/rankup &eagain to confirm. - # used for the GUI confirmation - title: "Rankup to {RANK}" - must-prestige: "&cYou must prestige to /rankup further!" gui: rows: 1 - title: "Rankup to {RANK}" + title: "Rankup to {{next.rank}}" rankup: material: EMERALD_BLOCK # index can be separated by spaces to show in multiple groups @@ -26,7 +23,7 @@ rankup: index: 0-3 name: '&a&lConfirm' # lore is optional - lore: '&6Rankup to &b{RANK}' + lore: '&6Rankup to &b{{next.rank}}' cancel: material: REDSTONE_BLOCK index: 5-8 @@ -45,13 +42,13 @@ rankup: width: 7 complete: material: GREEN_STAINED_GLASS_PANE - name: "&aRank &7{RANK} &a(completed)" + name: "&aRank &7{{next.rank}} &a(completed)" current: material: ORANGE_STAINED_GLASS_PANE - name: "&dRankup to &7{RANK}" + name: "&dRankup to &7{{next.rank}}" incomplete: material: RED_STAINED_GLASS_PANE - name: "&cRank &7{RANK} &c(requires rankup)" + name: "&cRank &7{{next.rank}} &c(requires rankup)" fill: material: BLACK_STAINED_GLASS_PANE name: ' ' @@ -60,43 +57,43 @@ rankup: # you can (and probably should) you override these in rankups.yml # to show the specific requirements for each rank. # however if you are just using money or don't need to change the message per rank, you can use any combination of: - # {MONEY} {MONEY_NEEDED} {PERCENT_DONE } {PERCENT_LEFT } {AMOUNT } {AMOUNT_NEEDED } - # {MONEY} and {MONEY_NEEDED} are different from {AMOUNT money} and {AMOUNT_NEEDED money} in that they use a different format. + # {{rank.requirement('money').total | simple}} {MONEY_NEEDED} {PERCENT_DONE } {PERCENT_LEFT } {AMOUNT } {AMOUNT_NEEDED } + # {{rank.requirement('money').total | simple}} and {MONEY_NEEDED} are different from {AMOUNT money} and {AMOUNT_NEEDED money} in that they use a different format. # here is an example of showing the requirements for just money: #list: - # complete: "&7{OLD_RANK} &8\xbb &7{RANK} &efor &7${MONEY}" - # current: "&c{OLD_RANK} &e\xbb &c{RANK} &efor &a${MONEY} &e{PERCENT_DONE money}%" - # incomplete: "&r{OLD_RANK} &e\xbb &r{RANK} &efor &a${MONEY}" + # complete: "&7{{rank.rank}} &8\xbb &7{{next.rank}} &efor &7${{rank.requirement('money').total | simple}}" + # current: "&c{{rank.rank}} &e\xbb &c{{next.rank}} &efor &a${{rank.requirement('money').total | simple}} &e{PERCENT_DONE money}%" + # incomplete: "&r{{rank.rank}} &e\xbb &r{{next.rank}} &efor &a${{rank.requirement('money').total | simple}}" list: - complete: "&7{OLD_RANK} &8\xbb &7{RANK}" - current: "&c{OLD_RANK} &e\xbb &c{RANK}" - incomplete: "&r{OLD_RANK} &e\xbb &r{RANK}" + complete: "&7{{rank.rank}} &8\xbb &7{{next.rank}}" + current: "&c{{rank.rank}} &e\xbb &c{{next.rank}}" + incomplete: "&r{{rank.rank}} &e\xbb &r{{next.rank}}" # an empty string disables the header/footer header: "" footer: "" # sent when a player tries to rankup when they are on cooldown cooldown: - singular: "&cYou must wait {SECONDS_LEFT} more second to rankup again." - plural: "&cYou must wait {SECONDS_LEFT} more seconds to rankup again." + singular: "&cYou must wait {{seconds_left}} more second to rankup again." + plural: "&cYou must wait {{seconds_left}} more seconds to rankup again." # prestige messages can also be customised prestige: - requirements-not-met: "&cYou need {MONEY} money to prestige." + requirements-not-met: "&cYou need {{rank.requirement('money').total | money}} money to prestige." no-prestige: "&eYou are at the highest prestige." - success-public: "&a{PLAYER} &ehas prestiged to: &d{RANK}" - success-private: "&aYou have prestiged to: &d{RANK}" + success-public: "&a{{player}} &ehas prestiged to: &d{{next.rank}}" + success-private: "&aYou have prestiged to: &d{{next.rank}}" confirmation: |- - &eAre you sure you want to prestige to &a{RANK}&e? + &eAre you sure you want to prestige to &a{{next.rank}}&e? &eType &c/prestige &eagain to confirm. gui: - title: "Prestige to {RANK}" + title: "Prestige to {{next.rank}}" rankup: material: GOLD_BLOCK index: 0-3 name: '&a&lConfirm' - lore: '&6Prestige to &b{RANK}' + lore: '&6Prestige to &b{{next.rank}}' cancel: material: REDSTONE_BLOCK index: 5-8 @@ -109,15 +106,14 @@ prestige: material: BLACK_STAINED_GLASS_PANE list: - complete: "&7{OLD_RANK} &8\xbb &7{RANK}" - current: "&c{OLD_RANK} &e\xbb &c{RANK}" - incomplete: "&r{OLD_RANK} &e\xbb &r{RANK}" + complete: "&7{{rank.rank}} &8\xbb &7{{next.rank}}" + current: "&c{{rank.rank}} &e\xbb &c{{next.rank}}" + incomplete: "&r{{rank.rank}} &e\xbb &r{{next.rank}}" header: "" footer: "" cooldown: - singular: "&cYou must wait {SECONDS_LEFT} second to prestige again." - plural: "&cYou must wait {SECONDS_LEFT} more seconds to prestige again." + singular: "&cYou must wait {{seconds_left}} second to prestige again." + plural: "&cYou must wait {{seconds_left}} more seconds to prestige again." not-high-enough: "&cYou cannot prestige at your rank!" -not-in-ladder: "&cSorry, but we could not find any rankups for the group(s) you are in. Use /ranks to list the rankups." -invalid-rankup: "&cInvalid rankup defined in config, please check console." \ No newline at end of file +not-in-ladder: "&cSorry, but we could not find any rankups for the group(s) you are in. Use /ranks to list the rankups." \ No newline at end of file diff --git a/src/main/resources/locale/es.yml b/src/main/resources/locale/es.yml index 14e2980..0ab1afd 100644 --- a/src/main/resources/locale/es.yml +++ b/src/main/resources/locale/es.yml @@ -1,23 +1,21 @@ -# the messages in this section can be customised for each rankup in rankups.yml. + money# the messages in this section can be customised for each rankup in rankups.yml. rankup: requirements-not-met: "&cNo satisfaces los requisitos para seguir al siguiente rango." no-rankup: "&eEstás en el último rango." # set to an empty string, ie: success-public: "" # to hide that message. - success-public: "&a{PLAYER} &eha subido de rango a: &d{RANK}" + success-public: "&a{{player}} &eha subido de rango a: &d{{next.rank}}" success-private: "" # used for the text confirmation confirmation: |- - &e¿Estás seguro de que quires subir de rango a &a{RANK}&e? + &e¿Estás seguro de que quires subir de rango a &a{{next.rank}}&e? &eEscribe /rankup para confirmar. - # used for the GUI confirmation - title: "Subir de rango a {RANK}" must-prestige: "&c¡Tienes que prestigiar para seguir subiendo de rango!" gui: rows: 1 - title: "Subir de rango a {RANK}" + title: "Subir de rango a {{next.rank}}" rankup: material: EMERALD_BLOCK # index can be separated by spaces to show in multiple groups @@ -26,7 +24,7 @@ rankup: index: 0-3 name: '&a&lConfirmar' # lore is optional - lore: '&6Subir de rango a {RANK}' + lore: '&6Subir de rango a {{next.rank}}' cancel: material: REDSTONE_BLOCK index: 5-8 @@ -45,56 +43,56 @@ rankup: width: 7 complete: material: GREEN_STAINED_GLASS_PANE - name: "&aRango &7{RANK} &a(completado)" + name: "&aRango &7{{next.rank}} &a(completado)" current: material: ORANGE_STAINED_GLASS_PANE - name: "&dSubir a &7{RANK}" + name: "&dSubir a &7{{next.rank}}" incomplete: material: RED_STAINED_GLASS_PANE - name: "&cRango &7{RANK} &c(requiere ascenso)" + name: "&cRango &7{{next.rank}} &c(requiere ascenso)" fill: material: BLACK_STAINED_GLASS_PANE name: ' ' # you can (and probably should) you override these in rankups.yml # to show the specific requirements for each rank. # however if you are just using money or don't need to change the message per rank, you can use any combination of: - # {MONEY} {MONEY_NEEDED} {PERCENT_DONE } {PERCENT_LEFT } {AMOUNT } {AMOUNT_NEEDED } - # {MONEY} and {MONEY_NEEDED} are different from {AMOUNT money} and {AMOUNT_NEEDED money} in that they use a different format. + # {{rank.requirement('money').total | simple}} {MONEY_NEEDED} {PERCENT_DONE } {PERCENT_LEFT } {AMOUNT } {AMOUNT_NEEDED } + # {{rank.requirement('money').total | simple}} and {MONEY_NEEDED} are different from {AMOUNT money} and {AMOUNT_NEEDED money} in that they use a different format. # here is an example of showing the requirements for just money: #list: - # complete: "&7{OLD_RANK} &8\xbb &7{RANK} &efor &7${MONEY}" - # current: "&c{OLD_RANK} &e\xbb &c{RANK} &efor &a${MONEY} &e{PERCENT_DONE money}%" - # incomplete: "&r{OLD_RANK} &e\xbb &r{RANK} &efor &a${MONEY}" + # complete: "&7{{rank.rank}} &8\xbb &7{{next.rank}} &efor &7${{rank.requirement('money').total | simple}}" + # current: "&c{{rank.rank}} &e\xbb &c{{next.rank}} &efor &a${{rank.requirement('money').total | simple}} &e{PERCENT_DONE money}%" + # incomplete: "&r{{rank.rank}} &e\xbb &r{{next.rank}} &efor &a${{rank.requirement('money').total | simple}}" list: - complete: "&7{OLD_RANK} &8\xbb &7{RANK}" - current: "&c{OLD_RANK} &e\xbb &c{RANK}" - incomplete: "&r{OLD_RANK} &e\xbb &r{RANK}" + complete: "&7{{rank.rank}} &8\xbb &7{{next.rank}}" + current: "&c{{rank.rank}} &e\xbb &c{{next.rank}}" + incomplete: "&r{{rank.rank}} &e\xbb &r{{next.rank}}" # an empty string disables the header/footer header: "" footer: "" # sent when a player tries to rankup when they are on cooldown cooldown: - singular: "&cTienes que esperar {SECONDS_LEFT} segundo más para subir de rango otra vez." - plural: "&cTienes que esperar {SECONDS_LEFT} segundos más para subir de rango otra vez." + singular: "&cTienes que esperar {{seconds_left}} segundo más para subir de rango otra vez." + plural: "&cTienes que esperar {{seconds_left}} segundos más para subir de rango otra vez." # prestige messages can also be customised prestige: - requirements-not-met: "&cNecesitas {MONEY} más dinero para prestigiar." + requirements-not-met: "&cNecesitas {{rank.requirement('money').total | simple}} más dinero para prestigiar." no-prestige: "&eYa estás en el prestigio más alto." - success-public: "&a{PLAYER} &eha prestigiado a: &d{RANK}" + success-public: "&a{{player}} &eha prestigiado a: &d{{next.rank}}" success-private: "" confirmation: |- - &eEstás seguro de que quieres prestigiar a &a{RANK}&e? + &eEstás seguro de que quieres prestigiar a &a{{next.rank}}&e? &eEscribe &c/prestige &otra vez para confirmar. gui: - title: "Prestigiar a {RANK}" + title: "Prestigiar a {{next.rank}}" rankup: material: GOLD_BLOCK index: 0-3 name: '&a&lConfirmar' - lore: '&6Prestigiar a &b{RANK}' + lore: '&6Prestigiar a &b{{next.rank}}' cancel: material: REDSTONE_BLOCK index: 5-8 @@ -107,15 +105,14 @@ prestige: material: BLACK_STAINED_GLASS_PANE list: - complete: "&7{OLD_RANK} &8\xbb &7{RANK}" - current: "&c{OLD_RANK} &e\xbb &c{RANK}" - incomplete: "&r{OLD_RANK} &e\xbb &r{RANK}" + complete: "&7{{rank.rank}} &8\xbb &7{{next.rank}}" + current: "&c{{rank.rank}} &e\xbb &c{{next.rank}}" + incomplete: "&r{{rank.rank}} &e\xbb &r{{next.rank}}" header: "" footer: "" cooldown: - singular: "&cTienes que esperar {SECONDS_LEFT} segundo más para prestigiar otra vez." - plural: "&cTienes que esperar {SECONDS_LEFT} segundos más para prestigiar otra vez." + singular: "&cTienes que esperar {{seconds_left}} segundo más para prestigiar otra vez." + plural: "&cTienes que esperar {{seconds_left}} segundos más para prestigiar otra vez." not-high-enough: "&c¡No puedes prestigiar a tu rango!" not-in-ladder: "&cPerdón, pero no hemos podido encontrar rangos en los grupos que perteneces. Usa /ranks para ver los rangos." -invalid-rankup: "&cRango inválido definido en la configuración, verifica que todo esté correcto y revisa la consola." diff --git a/src/main/resources/locale/fr.yml b/src/main/resources/locale/fr.yml index b00de9f..2dd312a 100644 --- a/src/main/resources/locale/fr.yml +++ b/src/main/resources/locale/fr.yml @@ -1,19 +1,19 @@ # the messages in this section can be customised for each rankup in rankups.yml. rankup: - requirements-not-met: "&cIl vous faut {MONEY} d'argent pour passer au rang suivant." + requirements-not-met: "&cIl vous faut {{rank.requirement('money').total | simple}} d'argent pour passer au rang suivant." no-rankup: "&eVous avez atteint le rang maximum." # set to an empty string, ie: success-public: "" # to hide that message. - success-public: "&a{PLAYER} &eest passé(e) au rang: &d{RANK}" - success-private: "&aVous êtes passé(e) au rang: &d{RANK}" + success-public: "&a{{player}} &eest passé(e) au rang: &d{{next.rank}}" + success-private: "&aVous êtes passé(e) au rang: &d{{next.rank}}" # used for the text confirmation confirmation: |- - &eÊtes-vous sûr(e) de vouloir passer au rang &a{RANK}&e? + &eÊtes-vous sûr(e) de vouloir passer au rang &a{{next.rank}}&e? &eTapez &c/rankup &eà nouveau pour confirmer. gui: rows: 1 - title: "Passer au rang {RANK}" + title: "Passer au rang {{next.rank}}" rankup: material: EMERALD_BLOCK # index can be separated by spaces to show in multiple groups @@ -22,7 +22,7 @@ rankup: index: 0-3 name: '&a&lConfirm' # lore is optional - lore: '&6Rankup to &b{RANK}' + lore: '&6Rankup to &b{{next.rank}}' cancel: material: REDSTONE_BLOCK index: 5-8 @@ -39,45 +39,45 @@ rankup: # you can (and probably should) you override these in rankups.yml # to show the specific requirements for each rank. # however if you are just using money or don't need to change the message per rank, you can use any combination of: - # {MONEY} {MONEY_NEEDED} {PERCENT_DONE } {PERCENT_LEFT } {AMOUNT } {AMOUNT_NEEDED } - # {MONEY} and {MONEY_NEEDED} are different from {AMOUNT money} and {AMOUNT_NEEDED money} in that they use a different format. + # {{rank.requirement('money').total | simple}} {MONEY_NEEDED} {PERCENT_DONE } {PERCENT_LEFT } {AMOUNT } {AMOUNT_NEEDED } + # {{rank.requirement('money').total | simple}} and {MONEY_NEEDED} are different from {AMOUNT money} and {AMOUNT_NEEDED money} in that they use a different format. # here is an example of showing the requirements for just money: #list: - # complete: "&7{OLD_RANK} &8\xbb &7{RANK} &efor &7${MONEY}" - # current: "&c{OLD_RANK} &e\xbb &c{RANK} &efor &a${MONEY} &e{PERCENT_DONE money}%" - # incomplete: "&r{OLD_RANK} &e\xbb &r{RANK} &efor &a${MONEY}" + # complete: "&7{{rank.rank}} &8\xbb &7{{next.rank}} &efor &7${{rank.requirement('money').total | simple}}" + # current: "&c{{rank.rank}} &e\xbb &c{{next.rank}} &efor &a${{rank.requirement('money').total | simple}} &e{PERCENT_DONE money}%" + # incomplete: "&r{{rank.rank}} &e\xbb &r{{next.rank}} &efor &a${{rank.requirement('money').total | simple}}" list: - complete: "&7{OLD_RANK} &8\xbb &7{RANK}" - current: "&c{OLD_RANK} &e\xbb &c{RANK}" - incomplete: "&r{OLD_RANK} &e\xbb &r{RANK}" + complete: "&7{{rank.rank}} &8\xbb &7{{next.rank}}" + current: "&c{{rank.rank}} &e\xbb &c{{next.rank}}" + incomplete: "&r{{rank.rank}} &e\xbb &r{{next.rank}}" # an empty string disables the header/footer header: "" footer: "" # sent when a player tries to rankup when they are on cooldown cooldown: - singular: "&cVous devez attendre encore {SECONDS_LEFT} seconde pour de nouveau passer un rang." - plural: "&cVous devez attendre encore {SECONDS_LEFT} secondes pour de nouveau passer un rang." + singular: "&cVous devez attendre encore {{seconds_left}} seconde pour de nouveau passer un rang." + plural: "&cVous devez attendre encore {{seconds_left}} secondes pour de nouveau passer un rang." # prestige messages can also be customised prestige: - requirements-not-met: "&cIl vous faut {MONEY} d'argent pour passer au rang suivant." + requirements-not-met: "&cIl vous faut {{rank.requirement('money').total | simple}} d'argent pour passer au rang suivant." no-prestige: "&eVous avez atteint le prestige maximum." - success-public: "&a{PLAYER} &eest passé(e) au prestige: &d{RANK}" - success-private: "&aVous êtes passé(e) au prestige: &d{RANK}" + success-public: "&a{{player}} &eest passé(e) au prestige: &d{{next.rank}}" + success-private: "&aVous êtes passé(e) au prestige: &d{{next.rank}}" confirmation: |- - &eÊtes-vous sûr(e) de vouloir passer au prestige &a{RANK}&e? + &eÊtes-vous sûr(e) de vouloir passer au prestige &a{{next.rank}}&e? &eTapez &c/prestige &eà nouveau pour confirmer. - title: "Passer au prestige {RANK}" + title: "Passer au prestige {{next.rank}}" gui: rows: 1 - title: "Prestige to {RANK}" + title: "Prestige to {{next.rank}}" rankup: material: GOLD_BLOCK index: 0-3 name: '&a&lConfirm' - lore: '&6Prestige to &b{RANK}' + lore: '&6Prestige to &b{{next.rank}}' cancel: material: REDSTONE_BLOCK index: 5-8 @@ -90,15 +90,14 @@ prestige: material: BLACK_STAINED_GLASS_PANE list: - complete: "&7{OLD_RANK} &8\xbb &7{RANK}" - current: "&c{OLD_RANK} &e\xbb &c{RANK}" - incomplete: "&r{OLD_RANK} &e\xbb &r{RANK}" + complete: "&7{{rank.rank}} &8\xbb &7{{next.rank}}" + current: "&c{{rank.rank}} &e\xbb &c{{next.rank}}" + incomplete: "&r{{rank.rank}} &e\xbb &r{{next.rank}}" header: "" footer: "" cooldown: - singular: "&cVous devez attendre encore {SECONDS_LEFT} seconde pour de nouveau passer un rang." - plural: "&cVous devez attendre encore {SECONDS_LEFT} secondes pour de nouveau passer un rang." + singular: "&cVous devez attendre encore {{seconds_left}} seconde pour de nouveau passer un rang." + plural: "&cVous devez attendre encore {{seconds_left}} secondes pour de nouveau passer un rang." not-high-enough: "&cVous ne pouvez pas passer de prestige à votre rang !" -not-in-ladder: "&cDésolé, mais nous n'avons pas trouvé de rang pour le(s) groupe(s) dans lequel(s) vous êtes. Tapez /ranks pour voir la liste des rangs." -invalid-rankup: "La configuration de Rankup est invalide, veuillez vérifier la console." \ No newline at end of file +not-in-ladder: "&cDésolé, mais nous n'avons pas trouvé de rang pour le(s) groupe(s) dans lequel(s) vous êtes. Tapez /ranks pour voir la liste des rangs." \ No newline at end of file diff --git a/src/main/resources/locale/it.yml b/src/main/resources/locale/it.yml index 97e8432..1a47d1d 100644 --- a/src/main/resources/locale/it.yml +++ b/src/main/resources/locale/it.yml @@ -1,21 +1,19 @@ #traduzione italiana del locale per rankup rankup: - requirements-not-met: "&cHai bisogno di {MONEY} soldi per avanzare di rank." + requirements-not-met: "&cHai bisogno di {{rank.requirement('money').total | simple}} soldi per avanzare di rank." no-rankup: "&eSei al rank più alto." # imposta stringa vuota per nascondere il messaggio - success-public: "&a{PLAYER} &e\u00E8 avanzato di rank a: &d{RANK}" - success-private: "&aHai avanzato di rank a: &d{RANK}" + success-public: "&a{{player}} &e\u00E8 avanzato di rank a: &d{{next.rank}}" + success-private: "&aHai avanzato di rank a: &d{{next.rank}}" # conferma testuale confirmation: |- - &eSei sicuro di voler avanzare di rank a &a{RANK}&e? + &eSei sicuro di voler avanzare di rank a &a{{next.rank}}&e? &eScrivi &c/rankup &edi nuovo per confermare. - # conferma gui - title: "Avanzamento di rank a {RANK}" must-prestige: "&cDevi effettuare un prestige prima di avanzare di rank!" gui: - title: "Avanzamento di rank a {RANK}" + title: "Avanzamento di rank a {{next.rank}}" rankup: material: EMERALD_BLOCK # l'indice può essere separato da spazi per mostrarlo in più gruppi @@ -24,7 +22,7 @@ rankup: index: 0-3 name: '&a&lConferma' # lore opzionale - lore: '&6Avanzamento a &b{RANK}' + lore: '&6Avanzamento a &b{{next.rank}}' cancel: material: REDSTONE_BLOCK index: 5-8 @@ -43,13 +41,13 @@ rankup: width: 7 complete: material: GREEN_STAINED_GLASS_PANE - name: "&aRank &7{RANK} &a(completed)" + name: "&aRank &7{{next.rank}} &a(completed)" current: material: ORANGE_STAINED_GLASS_PANE - name: "&dRankup to &7{RANK}" + name: "&dRankup to &7{{next.rank}}" incomplete: material: RED_STAINED_GLASS_PANE - name: "&cRank &7{RANK} &c(requires rankup)" + name: "&cRank &7{{next.rank}} &c(requires rankup)" fill: material: BLACK_STAINED_GLASS_PANE name: ' ' @@ -57,43 +55,43 @@ rankup: # potresti e dovresti configurare questi in rankup.yml # per visualizzare i requisiti di ogni rank # ma se stai usando solo i soldi e non ti serve modificare il mesaggio puoi usapre questi - # {MONEY} {MONEY_NEEDED} {PERCENT_DONE } {PERCENT_LEFT } {AMOUNT } {AMOUNT_NEEDED } - # {MONEY} e {MONEY_NEEDED} sono diversi da {AMOUNT money} e {AMOUNT_NEEDED money}, usano un formato diverso + # {{rank.requirement('money').total | simple}} {MONEY_NEEDED} {PERCENT_DONE } {PERCENT_LEFT } {AMOUNT } {AMOUNT_NEEDED } + # {{rank.requirement('money').total | simple}} e {MONEY_NEEDED} sono diversi da {AMOUNT money} e {AMOUNT_NEEDED money}, usano un formato diverso # ESEMPIO: #list: - # complete: "&7{OLD_RANK} &8\xbb &7{RANK} &efor &7${MONEY}" - # current: "&c{OLD_RANK} &e\xbb &c{RANK} &efor &a${MONEY} &e{PERCENT_DONE money}%" - # incomplete: "&r{OLD_RANK} &e\xbb &r{RANK} &efor &a${MONEY}" + # complete: "&7{{rank.rank}} &8\xbb &7{{next.rank}} &efor &7${{rank.requirement('money').total | simple}}" + # current: "&c{{rank.rank}} &e\xbb &c{{next.rank}} &efor &a${{rank.requirement('money').total | simple}} &e{PERCENT_DONE money}%" + # incomplete: "&r{{rank.rank}} &e\xbb &r{{next.rank}} &efor &a${{rank.requirement('money').total | simple}}" list: - complete: "&7{OLD_RANK} &8\xbb &7{RANK}" - current: "&c{OLD_RANK} &e\xbb &c{RANK}" - incomplete: "&r{OLD_RANK} &e\xbb &r{RANK}" + complete: "&7{{rank.rank}} &8\xbb &7{{next.rank}}" + current: "&c{{rank.rank}} &e\xbb &c{{next.rank}}" + incomplete: "&r{{rank.rank}} &e\xbb &r{{next.rank}}" # stringa vuota per disattivare header: "" footer: "" # messaggi cooldown cooldown: - singular: "&cDevi aspettare ancora {SECONDS_LEFT} secondo per avanzare di rank." - plural: "&cDevi aspettare ancora {SECONDS_LEFT} secondi per avanzare di rank." + singular: "&cDevi aspettare ancora {{seconds_left}} secondo per avanzare di rank." + plural: "&cDevi aspettare ancora {{seconds_left}} secondi per avanzare di rank." # messaggi prestige prestige: - requirements-not-met: "&cHai bisogno di {MONEY} soldi per effettuare un prestige." + requirements-not-met: "&cHai bisogno di {{rank.requirement('money').total | simple}} soldi per effettuare un prestige." no-prestige: "&eSei al prestige più alto." - success-public: "&a{PLAYER} &eha effettuato un prestige a: &d{RANK}" - success-private: "&aHai effettuato un prestige a: &d{RANK}" + success-public: "&a{{player}} &eha effettuato un prestige a: &d{{next.rank}}" + success-private: "&aHai effettuato un prestige a: &d{{next.rank}}" confirmation: |- - &eSei sicuro di voler effettuare un prestige a &a{RANK}&e? + &eSei sicuro di voler effettuare un prestige a &a{{next.rank}}&e? &eScrivi &c/prestige &edi nuovo per confermare. gui: - title: "Prestige a {RANK}" + title: "Prestige a {{next.rank}}" rankup: material: GOLD_BLOCK index: 0-3 name: '&a&lConferma' - lore: '&6Prestige a &b{RANK}' + lore: '&6Prestige a &b{{next.rank}}' cancel: material: REDSTONE_BLOCK index: 5-8 @@ -104,15 +102,14 @@ prestige: material: BLACK_STAINED_GLASS_PANE list: - complete: "&7{OLD_RANK} &8\xbb &7{RANK}" - current: "&c{OLD_RANK} &e\xbb &c{RANK}" - incomplete: "&r{OLD_RANK} &e\xbb &r{RANK}" + complete: "&7{{rank.rank}} &8\xbb &7{{next.rank}}" + current: "&c{{rank.rank}} &e\xbb &c{{next.rank}}" + incomplete: "&r{{rank.rank}} &e\xbb &r{{next.rank}}" header: "" footer: "" cooldown: - singular: "&cDevi aspettare {SECONDS_LEFT} secondo per effettuare un altro prestige." - plural: "&cDevi aspettare {SECONDS_LEFT} secondi per effettuare un altro prestige." + singular: "&cDevi aspettare {{seconds_left}} secondo per effettuare un altro prestige." + plural: "&cDevi aspettare {{seconds_left}} secondi per effettuare un altro prestige." not-high-enough: "&cNon puoi effettuare un prestige al tuo rank!" not-in-ladder: "&cNon riusciamo a trovare nessun avanzamento di rank per il tuo gruppo. Scrivi /ranks per ottenere una lista degli avanzamenti disponibili." -invalid-rankup: "&cRankup invalido definito nel file di configurazione, controlla la console." diff --git a/src/main/resources/locale/nl.yml b/src/main/resources/locale/nl.yml new file mode 100644 index 0000000..a31149b --- /dev/null +++ b/src/main/resources/locale/nl.yml @@ -0,0 +1,118 @@ +# De berichten in deze afdeling kunnen worden aangepast voor elke Rankup in rankups.yml +rankup: + requirements-not-met: "&cJe hebt {{rank.requirement('money').total | simple }} geld nodig om te ranken." + no-rankup: "&eJe bent op de hoogste rang." + # Zet de string leeg, bijvoorbeeld: success-public: "" + # Om het bericht te verbergen. + success-public: "&a{{player}} &eis gerankt naar: &d{{next.rank}}" + success-private: "&aJe bent gerankt naar: &d{{next.rank}}" + # Gebruikt voor tekst bevestiging. + confirmation: |- + &eWeet je zeker dat je de volgende rang &a{{next.rank}} &ewilt? + &eType nogmaals &c/rankup &eom te bevestigen. + must-prestige: "&cJe moet prestigen om verder te ranken!" + + gui: + rows: 1 + title: "Rank naar {{next.rank}}" + rankup: + material: EMERALD_BLOCK + # "index" kan worden verdeeld tot groupen door spaties te gebruiken. + # Bijvoorbeeld: 0-3 9-12 18-21 + # Je kan ook apparte getallen gebruiken in plaats van een bereik. + index: 0-3 + name: '&a&lBevestigen' + # "lore" is optioneel. + lore: '&6Rank naar &b{{next.rank}}' + cancel: + material: REDSTONE_BLOCK + index: 5-8 + name: '&c&lAnnuleren' + fill: + name: ' ' + # Als je versies 1.8-1.12 gebruikt en je wilt dit veranderen, + # moet je gebruiken, bijvoorbeeld STAINED_GLASS_PANE:8 + # Dit werkt voor zowel "rankup" als "cancel". + material: BLACK_STAINED_GLASS_PANE + + ranksgui: + title: "Rangen" + rows: 3 + offset: 10 + width: 7 + complete: + material: GREEN_STAINED_GLASS_PANE + name: "&aRang &7{{next.rank}} &a(compleet)" + current: + material: ORANGE_STAINED_GLASS_PANE + name: "&dRank naar &7{{next.rank}}" + incomplete: + material: RED_STAINED_GLASS_PANE + name: "&cRang &7{{next.rank}} &c(vereist hogere rang)" + fill: + material: BLACK_STAINED_GLASS_PANE + name: ' ' + + + # Je kan (en waarschijnlijk moet) deze overschrijven in de rankups.yml. + # Om specifieke eisen te weergeven voor elke rang. + # Hoewel als je de "money" eis gebruikt, hoef je niet het bericht te veranderen voor elke rang. Je kan een combinatie gebruiken van: + # {{rank.requirement('money').total | simple}} {MONEY_NEEDED} {PERCENT_DONE } {PERCENT_LEFT } {AMOUNT } {AMOUNT_NEEDED } + # {{rank.requirement('money').total | simple}} en {MONEY_NEEDED} zijn anders dan {AMOUNT money} en {AMOUNT_NEEDED money} want ze gebruiken een verschillend formaat. + # Hier is een voorbeeld met alleen de "money" eis: + #list: + # complete: "&7{{rank.rank}} &8\xbb &7{{next.rank}} &evoor &7${{rank.requirement('money').total | simple}}" + # current: "&c{{rank.rank}} &e\xbb &c{{next.rank}} &evoor &a${{rank.requirement('money').total | simple}} &e{PERCENT_DONE money}%" + # incomplete: "&r{{rank.rank}} &e\xbb &r{{next.rank}} &evoor &a${{rank.requirement('money').total | simple}}" + list: + complete: "&7{{rank.rank}} &8\xbb &7{{next.rank}}" + current: "&c{{rank.rank}} &e\xbb &c{{next.rank}}" + incomplete: "&r{{rank.rank}} &e\xbb &r{{next.rank}}" + # Een lege string ("") schakelt het bericht uit. + header: "" + footer: "" + # Verzonden wanneer een speler probeert te ranken terwijl ze in cooldown zitten. + cooldown: + singular: "&cJe moet {{seconds_left}} seconde wachten om weer te ranken." + plural: "&cJe moet {{seconds_left}} secondes wachten om weer te ranken." +# Prestige berichten kunnen ook worden aangepast. +prestige: + requirements-not-met: "&cJe hebt {{rank.requirement('money').total | simple}} geld nodig om te prestigen." + no-prestige: "&eJe bent op de hoogste prestige." + + success-public: "&a{{player}} &eis geprestiged naar: &d{{next.rank}}" + success-private: "&aJe bent geprestiged naar: &d{{next.rank}}" + + confirmation: |- + &eWeet je zeker dat je wilt prestigen naar &a{{next.rank}}&e? + &eType nogmaals &c/prestige &eom te bevestigen. + gui: + title: "Prestige naar {{next.rank}}" + rankup: + material: GOLD_BLOCK + index: 0-3 + name: '&a&lBevestigen' + lore: '&6Prestige naar &b{{next.rank}}' + cancel: + material: REDSTONE_BLOCK + index: 5-8 + name: '&c&lAnnuleren' + fill: + name: ' ' + # Als je versies 1.8-1.12 gebruikt en je wilt dit veranderen, + # moet je gebruiken, bijvoorbeeld STAINED_GLASS_PANE:8 + # Dit werkt voor zowel "rankup" als "cancel". + material: BLACK_STAINED_GLASS_PANE + + list: + complete: "&7{{rank.rank}} &8\xbb &7{{next.rank}}" + current: "&c{{rank.rank}} &e\xbb &c{{next.rank}}" + incomplete: "&r{{rank.rank}} &e\xbb &r{{next.rank}}" + header: "" + footer: "" + cooldown: + singular: "&cJe moet {{seconds_left}} seconde wachten om weer te prestigen." + plural: "&cJe moet {{seconds_left}} secondes wachten om weer te prestigen." + +not-high-enough: "&cJe kan niet prestigen in jouw rang!" +not-in-ladder: "&cSorry, maar we konden geen rangen vinden voor de group(en) waar jij in zit. Gebruik /ranks om de lijst van rangen te weergeven." \ No newline at end of file diff --git a/src/main/resources/locale/pt_br.yml b/src/main/resources/locale/pt_br.yml index 3a3b50a..a45ee07 100644 --- a/src/main/resources/locale/pt_br.yml +++ b/src/main/resources/locale/pt_br.yml @@ -1,21 +1,21 @@ # As mensagens nessa sessão podem ser customizadas para cada rankup em rankups.yml. rankup: - requirements-not-met: "&cVocê {MONEY} para poder dar subir de rank." + requirements-not-met: "&cVocê {{rank.requirement('money').total | simple}} para poder dar subir de rank." no-rankup: "&eVocê já está no maior rank." # coloque como uma String vazia, ex: success-public: "" # para ocultar a mensagem. - success-public: "&a{PLAYER} &eacaba de subir para: &d{RANK}" - success-private: "&aVocê subiu para: &d{RANK}" + success-public: "&a{{player}} &eacaba de subir para: &d{{next.rank}}" + success-private: "&aVocê subiu para: &d{{next.rank}}" # Utilizado para a confirmação via mensagem. confirmation: |- - &eVocê tem certeza que deseja subir para &a{RANK}&e? + &eVocê tem certeza que deseja subir para &a{{next.rank}}&e? &eDigite &c/rankup &enovamente para confirmar. must-prestige: "&cVocê deve subir de prestígio para dar /rankup a frente!" gui: rows: 1 - title: "Rankup to {RANK}" + title: "Rankup to {{next.rank}}" rankup: material: EMERALD_BLOCK # index can be separated by spaces to show in multiple groups @@ -24,7 +24,7 @@ rankup: index: 0-3 name: '&a&lConfirm' # lore is optional - lore: '&6Rankup to &b{RANK}' + lore: '&6Rankup to &b{{next.rank}}' cancel: material: REDSTONE_BLOCK index: 5-8 @@ -39,48 +39,48 @@ rankup: # Você pode (e deveria) substituir isto em rankups.yml # para mostrar os requerimentos específicos para cada rank. # contudo, se você está apenas usando dinheiro ou não precisa mudar as mensagens por rank, você pode usar qualquer combinação de: - # {MONEY} {MONEY_NEEDED} {PERCENT_DONE } {PERCENT_LEFT } {AMOUNT } {AMOUNT_NEEDED } - # {MONEY} e {MONEY_NEEDED} são diferentes de {AMOUNT money} e {AMOUNT_NEEDED money} pois eles usam diferentes formatos. + # {{rank.requirement('money').total | simple}} {MONEY_NEEDED} {PERCENT_DONE } {PERCENT_LEFT } {AMOUNT } {AMOUNT_NEEDED } + # {{rank.requirement('money').total | simple}} e {MONEY_NEEDED} são diferentes de {AMOUNT money} e {AMOUNT_NEEDED money} pois eles usam diferentes formatos. # here is an example of showing the requirements for just money: # Segue um exemplo de como mostrar um requerimento para apenas dinheiro. #list: - # complete: "&7{OLD_RANK} &8\xbb &7{RANK} &efor &7${MONEY}" - # current: "&c{OLD_RANK} &e\xbb &c{RANK} &efor &a${MONEY} &e{PERCENT_DONE money}%" - # incomplete: "&r{OLD_RANK} &e\xbb &r{RANK} &efor &a${MONEY}" + # complete: "&7{{rank.rank}} &8\xbb &7{{next.rank}} &efor &7${{rank.requirement('money').total | simple}}" + # current: "&c{{rank.rank}} &e\xbb &c{{next.rank}} &efor &a${{rank.requirement('money').total | simple}} &e{PERCENT_DONE money}%" + # incomplete: "&r{{rank.rank}} &e\xbb &r{{next.rank}} &efor &a${{rank.requirement('money').total | simple}}" list: - complete: "&7{OLD_RANK} &8\xbb &7{RANK}" - current: "&c{OLD_RANK} &e\xbb &c{RANK}" - incomplete: "&r{OLD_RANK} &e\xbb &r{RANK}" + complete: "&7{{rank.rank}} &8\xbb &7{{next.rank}}" + current: "&c{{rank.rank}} &e\xbb &c{{next.rank}}" + incomplete: "&r{{rank.rank}} &e\xbb &r{{next.rank}}" # Uma string vázia desativa o cabeçalho/rodapé header: "" footer: "" # sent when a player tries to rankup when they are on cooldown. # Enviado quando um jogador tenta dar rankup enquanto ele está em um cooldown (tempo de espera entre comandos, freeze). cooldown: - singular: "&cVocê precisa esperar {SECONDS_LEFT} segundo para dar rankup novamente." - plural: "&cVocê precisa {SECONDS_LEFT} segundos para dar rankup novamente." + singular: "&cVocê precisa esperar {{seconds_left}} segundo para dar rankup novamente." + plural: "&cVocê precisa {{seconds_left}} segundos para dar rankup novamente." # prestige messages can also be customised # Mensagens de prestígio também podem ser customizadas prestige: - requirements-not-met: "&cVocê precisa {MONEY} para dar prestigiar." + requirements-not-met: "&cVocê precisa {{rank.requirement('money').total | simple}} para dar prestigiar." no-prestige: "&eVocê está no maior prestígio." - success-public: "&a{PLAYER} &esubiu de prestígio para: &d{RANK}" - success-private: "&aVocê subiu de prestígio para: &d{RANK}" + success-public: "&a{{player}} &esubiu de prestígio para: &d{{next.rank}}" + success-private: "&aVocê subiu de prestígio para: &d{{next.rank}}" confirmation: |- - &eVocê tem certeza que deseja subir de prestígio para &a{RANK}&e? + &eVocê tem certeza que deseja subir de prestígio para &a{{next.rank}}&e? &eDigite &c/prestige &enovamente para confimar. - title: "Subiu de prestígio para {RANK}" + title: "Subiu de prestígio para {{next.rank}}" gui: rows: 1 - title: "Prestige to {RANK}" + title: "Prestige to {{next.rank}}" rankup: material: GOLD_BLOCK index: 0-3 name: '&a&lConfirm' - lore: '&6Prestige to &b{RANK}' + lore: '&6Prestige to &b{{next.rank}}' cancel: material: REDSTONE_BLOCK index: 5-8 @@ -93,15 +93,14 @@ prestige: material: BLACK_STAINED_GLASS_PANE list: - complete: "&7{OLD_RANK} &8\xbb &7{RANK}" - current: "&c{OLD_RANK} &e\xbb &c{RANK}" - incomplete: "&r{OLD_RANK} &e\xbb &r{RANK}" + complete: "&7{{rank.rank}} &8\xbb &7{{next.rank}}" + current: "&c{{rank.rank}} &e\xbb &c{{next.rank}}" + incomplete: "&r{{rank.rank}} &e\xbb &r{{next.rank}}" header: "" footer: "" cooldown: - singular: "&cVocê deve esperar {SECONDS_LEFT} segundo para subir de prestígio novamente." - plural: "&cVocê deve esperar {SECONDS_LEFT} segundos para subir de prestígio novamente." + singular: "&cVocê deve esperar {{seconds_left}} segundo para subir de prestígio novamente." + plural: "&cVocê deve esperar {{seconds_left}} segundos para subir de prestígio novamente." not-high-enough: "&cVocê não pode subir de prestígio nesse rank!" -not-in-ladder: "&cDesculpa, não conseguimos achar nenhum rank para você subir." -invalid-rankup: "Rankup inválido definido na config, por favor confira o console." \ No newline at end of file +not-in-ladder: "&cDesculpa, não conseguimos achar nenhum rank para você subir." \ No newline at end of file diff --git a/src/main/resources/locale/ru.yml b/src/main/resources/locale/ru.yml index e050121..b6197de 100644 --- a/src/main/resources/locale/ru.yml +++ b/src/main/resources/locale/ru.yml @@ -1,18 +1,18 @@ # Сообщения в этой секции могут быть изменены для каждого повышения уровня в rankups.yml rankup: - requirements-not-met: "&cВам нужно ещё {MONEY} денег для повышения уровня." + requirements-not-met: "&cВам нужно ещё {{rank.requirement('money').total | simple}} денег для повышения уровня." no-rankup: "&eУ Вас уже самый высокий ранг." # Оставьте это поле пустым, чтобы скрыть сообщение (success-public: '') - success-public: "&eРанг игрока &a{PLAYER} &eбыл повышен до: &d{RANK}" - success-private: "&aВаш ранг повысился до: &d{RANK}" + success-public: "&eРанг игрока &a{{player}} &eбыл повышен до: &d{{next.rank}}" + success-private: "&aВаш ранг повысился до: &d{{next.rank}}" # Используется как текст подтверждения confirmation: |- - &eВы уверены, что хотите ранг до &a{RANK}&e? + &eВы уверены, что хотите ранг до &a{{next.rank}}&e? &eВведите &c/rankup &eещё раз, чтобы подтвердить. gui: rows: 1 - title: "Повысить до {RANK}" + title: "Повысить до {{next.rank}}" rankup: material: EMERALD_BLOCK # index can be separated by spaces to show in multiple groups @@ -21,7 +21,7 @@ rankup: index: 0-3 name: '&a&lConfirm' # lore is optional - lore: '&6Rankup to &b{RANK}' + lore: '&6Rankup to &b{{next.rank}}' cancel: material: REDSTONE_BLOCK index: 5-8 @@ -37,47 +37,47 @@ rankup: # Вы можете (вам стоит ;D) переписать это в rankups.yml, чтобы показать специфичные требования для каждого ранга. # Тем не менее, если Вы просто используете деньги или не нуждаетесь в смене сообщений для кажого ранга - Вы - # можете использовать комбинации из: {MONEY} {MONEY_NEEDED} {PERCENT_DONE <требование>} {PERCENT_LEFT <требование>} - # {AMOUNT <требование>} {AMOUNT_NEDDED <требование>}. "{MONEY}" и "{MONEY_NEEDED}" отличаются от + # можете использовать комбинации из: {{rank.requirement('money').total | simple}} {MONEY_NEEDED} {PERCENT_DONE <требование>} {PERCENT_LEFT <требование>} + # {AMOUNT <требование>} {AMOUNT_NEDDED <требование>}. "{{rank.requirement('money').total | simple}}" и "{MONEY_NEEDED}" отличаются от # {AMOUNT сумма} {AMOUNT_NEDDED сумма} тем, что они используют разный формат. # Вот пример показа требований только для денег # P.S. \xbb - "»" в Unicode. #list: - # complete: "&7{OLD_RANK} &8\xbb &7{RANK} &eза &7${MONEY}" - # current: "&c{OLD_RANK} &e\xbb &c{RANK} &eза &a${MONEY} &e{PERCENT_DONE money}%" - # incomplete: "&r{OLD_RANK} &e\xbb &r{RANK} &eза &a${MONEY}" + # complete: "&7{{rank.rank}} &8\xbb &7{{next.rank}} &eза &7${{rank.requirement('money').total | simple}}" + # current: "&c{{rank.rank}} &e\xbb &c{{next.rank}} &eза &a${{rank.requirement('money').total | simple}} &e{PERCENT_DONE money}%" + # incomplete: "&r{{rank.rank}} &e\xbb &r{{next.rank}} &eза &a${{rank.requirement('money').total | simple}}" list: - complete: "&7{OLD_RANK} &8\xbb &7{RANK}" - current: "&c{OLD_RANK} &e\xbb &c{RANK}" - incomplete: "&r{OLD_RANK} &e\xbb &r{RANK}" + complete: "&7{{rank.rank}} &8\xbb &7{{next.rank}}" + current: "&c{{rank.rank}} &e\xbb &c{{next.rank}}" + incomplete: "&r{{rank.rank}} &e\xbb &r{{next.rank}}" # Пустая строка выключает заголовок/нижнюю часть header: '' footer: '' # Отправляется игроку, который попытался повысить уровень, когда ещё не прошёл кулдаун (откат/перезарядка) cooldown: - singular: "&cПожалуйста, подождите {SECONDS_LEFT} сек., чтобы повысить ранг снова." - plural: "&cПожалуйста, подождите {SECONDS_LEFT} сек., чтобы повысить ранг снова." + singular: "&cПожалуйста, подождите {{seconds_left}} сек., чтобы повысить ранг снова." + plural: "&cПожалуйста, подождите {{seconds_left}} сек., чтобы повысить ранг снова." # Сообщения престижа тоже могут быть изменены prestige: - requirements-not-met: "&cВам нужно {MONEY} денг для повышения престижа." + requirements-not-met: "&cВам нужно {{rank.requirement('money').total | simple}} денг для повышения престижа." no-prestige: "&eУ Вас уже самый высокий уровень престижа." - success-public: "Престиж игрока &a{PLAYER} &eповышен в: &d{RANK}" - success-private: "&aВаш престиж повышен в: &d{RANK}" + success-public: "Престиж игрока &a{{player}} &eповышен в: &d{{next.rank}}" + success-private: "&aВаш престиж повышен в: &d{{next.rank}}" confirmation: |- - &eВы уверены, что хотите престиж в &a{RANK}&e? + &eВы уверены, что хотите престиж в &a{{next.rank}}&e? &eВведите &c/prestige &eснова для подтверждения. - title: "Повысить престиж в {RANK}" + title: "Повысить престиж в {{next.rank}}" gui: rows: 1 - title: "Prestige to {RANK}" + title: "Prestige to {{next.rank}}" rankup: material: GOLD_BLOCK index: 0-3 name: '&a&lConfirm' - lore: '&6Prestige to &b{RANK}' + lore: '&6Prestige to &b{{next.rank}}' cancel: material: REDSTONE_BLOCK index: 5-8 @@ -90,15 +90,14 @@ prestige: material: BLACK_STAINED_GLASS_PANE list: - complete: "&7{OLD_RANK} &8\xbb &7{RANK}" - current: "&c{OLD_RANK} &e\xbb &c{RANK}" - incomplete: "&r{OLD_RANK} &e\xbb &r{RANK}" + complete: "&7{{rank.rank}} &8\xbb &7{{next.rank}}" + current: "&c{{rank.rank}} &e\xbb &c{{next.rank}}" + incomplete: "&r{{rank.rank}} &e\xbb &r{{next.rank}}" header: "" footer: "" cooldown: - singular: "&cПожалуйста, подождите {SECONDS_LEFT} сек., чтобы повысить ранг снова." - plural: "&cПожалуйста, подождите {SECONDS_LEFT} сек., чтобы повысить ранг снова." + singular: "&cПожалуйста, подождите {{seconds_left}} сек., чтобы повысить ранг снова." + plural: "&cПожалуйста, подождите {{seconds_left}} сек., чтобы повысить ранг снова." not-high-enough: "&cВы не можете поднять уровень престижа в этом ранге" not-in-ladder: "&cПростите, но мне не можем найти какие-нибудь повышения для Вашей группы." -invalid-rankup: "В конфигурации определён неверное повышение, пожалуйста, проверьте консоль." diff --git a/src/main/resources/locale/zh_cn.yml b/src/main/resources/locale/zh_cn.yml index ff02dfa..180ea5a 100644 --- a/src/main/resources/locale/zh_cn.yml +++ b/src/main/resources/locale/zh_cn.yml @@ -1,24 +1,22 @@ # 这部分的信息能在 rankups.yml 对不同的段位进行自定义。 rankup: - requirements-not-met: "&c您需要 {MONEY} 游戏币才能晋级。" + requirements-not-met: "&c您需要 {{rank.requirement('money').total | simple}} 游戏币才能晋级。" no-rankup: "&e您目前已处于段位的顶端。" # 想设置字符串为空? # 一个例子: success-public: "" # 这将隐藏那条信息。 - success-public: "&a{PLAYER} &e已经晋升至新的段位: &d{RANK}" - success-private: "&a您成功晋升到新的段位: &d{RANK}" + success-public: "&a{{player}} &e已经晋升至新的段位: &d{{next.rank}}" + success-private: "&a您成功晋升到新的段位: &d{{next.rank}}" # 用于二次确认的文本信息 confirmation: |- - &e您确定您要晋级至 &a{RANK}&e? + &e您确定您要晋级至 &a{{next.rank}}&e? &e再次输入 &c/rankup &e以确认操作。 - # 用于二次确认的 GUI 信息 - title: "晋级至 {RANK}" must-prestige: "&c您必须再次输入 /rankup !" gui: rows: 1 - title: "晋级至 {RANK}" + title: "晋级至 {{next.rank}}" rankup: material: EMERALD_BLOCK # 可以用空格分隔序号以显示在多个槽位组中 @@ -27,7 +25,7 @@ rankup: index: 0-3 name: '&a&l确认' # lore 可有可无 - lore: '&6晋级至 &b{RANK}' + lore: '&6晋级至 &b{{next.rank}}' cancel: material: REDSTONE_BLOCK index: 5-8 @@ -46,13 +44,13 @@ rankup: width: 7 complete: material: GREEN_STAINED_GLASS_PANE - name: "&a段位等级 &7{RANK} &a(已达成)" + name: "&a段位等级 &7{{next.rank}} &a(已达成)" current: material: ORANGE_STAINED_GLASS_PANE - name: "&d晋升段位等级至 &7{RANK}" + name: "&d晋升段位等级至 &7{{next.rank}}" incomplete: material: RED_STAINED_GLASS_PANE - name: "&c段位等级 &7{RANK} &c(需要晋升)" + name: "&c段位等级 &7{{next.rank}} &c(需要晋升)" fill: material: BLACK_STAINED_GLASS_PANE name: ' ' @@ -61,42 +59,42 @@ rankup: # 您可以(也可能应该)在 rankups.yml # 显示每个等级的具体要求。 # 但是,如果您只想使用游戏币作为晋升需求或不需要更改每个级别的消息,则可以使用以下任意组合: - # {MONEY} {MONEY_NEEDED} {PERCENT_DONE } {PERCENT_LEFT } {AMOUNT } {AMOUNT_NEEDED } - # {MONEY} 和 {MONEY_NEEDED} 相较于 {AMOUNT money} 和 {AMOUNT_NEEDED money} 在使用的格式上不尽相同。 + # {{rank.requirement('money').total | simple}} {MONEY_NEEDED} {PERCENT_DONE } {PERCENT_LEFT } {AMOUNT } {AMOUNT_NEEDED } + # {{rank.requirement('money').total | simple}} 和 {MONEY_NEEDED} 相较于 {AMOUNT money} 和 {AMOUNT_NEEDED money} 在使用的格式上不尽相同。 # 这里有一个只使用游戏币作为晋升条件的示例: #list: - # complete: "&7{OLD_RANK} &8\xbb &7{RANK} &e花费 &7${MONEY}" - # current: "&c{OLD_RANK} &e\xbb &c{RANK} &e花费 &a${MONEY} &e{PERCENT_DONE money}%" - # incomplete: "&r{OLD_RANK} &e\xbb &r{RANK} &e花费 &a${MONEY}" + # complete: "&7{{rank.rank}} &8\xbb &7{{next.rank}} &e花费 &7${{rank.requirement('money').total | simple}}" + # current: "&c{{rank.rank}} &e\xbb &c{{next.rank}} &e花费 &a${{rank.requirement('money').total | simple}} &e{PERCENT_DONE money}%" + # incomplete: "&r{{rank.rank}} &e\xbb &r{{next.rank}} &e花费 &a${{rank.requirement('money').total | simple}}" list: - complete: "&7{OLD_RANK} &8\xbb &7{RANK}" - current: "&c{OLD_RANK} &e\xbb &c{RANK}" - incomplete: "&r{OLD_RANK} &e\xbb &r{RANK}" + complete: "&7{{rank.rank}} &8\xbb &7{{next.rank}}" + current: "&c{{rank.rank}} &e\xbb &c{{next.rank}}" + incomplete: "&r{{rank.rank}} &e\xbb &r{{next.rank}}" # 一个空字符串可以关闭 header(页眉)/footer(页脚) header: "" footer: "" # 这些信息将在玩家处于晋升冷却期间尝试升级后发送 cooldown: - singular: "&c您必须等待 {SECONDS_LEFT} 秒才能进行晋升段位等级操作。" - plural: "&c您必须等待 {SECONDS_LEFT} 秒才能进行晋升段位等级操作。" + singular: "&c您必须等待 {{seconds_left}} 秒才能进行晋升段位等级操作。" + plural: "&c您必须等待 {{seconds_left}} 秒才能进行晋升段位等级操作。" # 声望信息也可以定制 prestige: - requirements-not-met: "&c您需要 {MONEY} 游戏币才能进行声望等级升级。" + requirements-not-met: "&c您需要 {{rank.requirement('money').total | simple}} 游戏币才能进行声望等级升级。" no-prestige: "&e您目前已经处在最高的声望等级了。" - success-public: "&a{PLAYER} &e已经晋升至: &d{RANK}" - success-private: "&a您成功晋升声望至: &d{RANK}" + success-public: "&a{{player}} &e已经晋升至: &d{{next.rank}}" + success-private: "&a您成功晋升声望至: &d{{next.rank}}" confirmation: |- - &e您确定要晋升声望至 &a{RANK}&e? + &e您确定要晋升声望至 &a{{next.rank}}&e? &e再次输入 &c/prestige &e确认操作。 gui: - title: "声望晋升至 {RANK}" + title: "声望晋升至 {{next.rank}}" rankup: material: GOLD_BLOCK index: 0-3 name: '&a&l确认' - lore: '&6声望晋升至 &b{RANK}' + lore: '&6声望晋升至 &b{{next.rank}}' cancel: material: REDSTONE_BLOCK index: 5-8 @@ -109,15 +107,14 @@ prestige: material: BLACK_STAINED_GLASS_PANE list: - complete: "&7{OLD_RANK} &8\xbb &7{RANK}" - current: "&c{OLD_RANK} &e\xbb &c{RANK}" - incomplete: "&r{OLD_RANK} &e\xbb &r{RANK}" + complete: "&7{{rank.rank}} &8\xbb &7{{next.rank}}" + current: "&c{{rank.rank}} &e\xbb &c{{next.rank}}" + incomplete: "&r{{rank.rank}} &e\xbb &r{{next.rank}}" header: "" footer: "" cooldown: - singular: "&c您必须等待 {SECONDS_LEFT} 秒才能再次晋升声望等级。" - plural: "&c您必须等待 {SECONDS_LEFT} 秒才能再次晋升声望等级。" + singular: "&c您必须等待 {{seconds_left}} 秒才能再次晋升声望等级。" + plural: "&c您必须等待 {{seconds_left}} 秒才能再次晋升声望等级。" not-high-enough: "&c您无法在您当前等级上进行声望晋升!" not-in-ladder: "&c抱歉,我们找不到您所在小组的任何段位等级。使用 /ranks 列出所有段位等级。" -invalid-rankup: "&c配置中定义了无效的 段位晋升,请检查控制台获取更多信息。" diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index a666f17..5b95e1b 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -61,7 +61,10 @@ permissions: rankup.maxrankup: default: true rankup.playtime: + description: Use all /rankup3 playtime subcommands for anyone. children: rankup.playtime.get: true - rankup.playtime.set: true + default: op + rankup.playtime.get: + description: Use /rankup3 playtime get for anyone. default: op \ No newline at end of file diff --git a/src/main/resources/prestiges.yml b/src/main/resources/prestiges.yml index 606bd0b..8929e2f 100644 --- a/src/main/resources/prestiges.yml +++ b/src/main/resources/prestiges.yml @@ -1,4 +1,3 @@ - first: # the rank people must be to use this prestige from: 'D' diff --git a/src/main/resources/rankups.yml b/src/main/resources/rankups.yml index 4ea147d..1eb94fb 100644 --- a/src/main/resources/rankups.yml +++ b/src/main/resources/rankups.yml @@ -1,57 +1,44 @@ # # If you are adding your own ranks, make sure to delete the example ranks! # Need help setting the plugin up? +# Read the wiki: https://okx.sh/rankup/Home.html # Join the discord server for live support: https://discord.gg/maB4382 (buyers only) -# Read a Configuration Example: https://github.com/okx-code/Rankup3/wiki/Configuration-Example#minimum-requirements-to-use-rankup -# Documentation of this file: https://github.com/okx-code/Rankup3/wiki/How-to-rankups.yml-and-prestiges.yml#how-to-rankups -# Read an Advanced Example: https://github.com/okx-code/Rankup3/wiki/Advanced-Configuration-Example#what-more # -# The following name is never seen in-game. https://github.com/okx-code/Rankup3/wiki/How-to-rankups.yml-and-prestiges.yml#1-heading -ExampleA: # It can say anything, but must be unique from all other section names. Headings with duplicate names may cause a multiple root nodes error. - rank: 'A' # This is the group/permission players must start at. https://github.com/okx-code/Rankup3/wiki/How-to-rankups.yml-and-prestiges.yml#2-rank - next: 'B' # This is the group/permission players move up to. Use the part from "displayname." if you use displaynames!https://github.com/okx-code/Rankup3/wiki/How-to-rankups.yml-and-prestiges.yml#3-next - # Read it as "Rankup from A to B". Luckperms starts players in group 'default'. - requirements: # Here go your requirements. You can have as many as you want. - - 'money 5000' # player must pay 5000 - - 'xp-level 5' # player must pay 5 levels - # List of requirements: https://github.com/okx-code/Rankup3/wiki/List-of-Requirements -ExampleBee: # mob-kills requirement example + +# this name doesn't matter +Aexample: + # the name of the group + # players have to be in this rank to rankup + rank: 'A' + # the name of the rank a player can rankup to + next: 'B' + # List of requirements to go to the next rank + # This example will charge 1000 money to rankup from A to B. + # https://okx.sh/rankup/List-of-Requirements.html + # custom requirements can also be added by other plugins. + requirements: + - 'money 1000' + # the console will run these commands when a player ranks up + # nb: groups are automatically changed with vault + #commands: + # this will run when a player ranks up from A to B. + #- 'say {{player}} well done for ranking up from {{rank.rank}} to {{next.rank}}!' +Bexample: rank: 'B' next: 'C' - requirements: # https://github.com/okx-code/Rankup3/wiki/How-to-rankups.yml-and-prestiges.yml#4-requirements - # https://github.com/okx-code/Rankup3/wiki/List-of-Requirements#how-to-specify-sub-requirements-in-placeholders - - 'mob-kills Bee 1' # https://hub.spigotmc.org/javadocs/spigot/org/bukkit/entity/package-summary.html - # https://github.com/okx-code/Rankup3/wiki/List-of-Requirements#list - - 'moneyh 10000' # This requirement only checks the player's balance rather than require the value as payment. - - 'xp-levelh 10' # This requirement only checks the player's level rather than require the value as payment. - rankup: # https://github.com/okx-code/Rankup3/wiki/How-to-rankups.yml-and-prestiges.yml#3-rankup - requirements-not-met: "&3You need &d&n$10K &3and &d&n10 XP levels&3!" # Supports RGB color codes like #&hexnum if 1.16+. Otherwise, use old native or bukkit codes like §x§9§D§6§5§F§F - # requirements-not-met is a custom message displayed when the player fails to meet all the prior listed requirements. -ExampleC: + requirements: + - 'money 2500' +Cexample: rank: 'C' next: 'D' requirements: - - 'money 15000' - - 'xp-level 12' - - 'total-mob-kills 15' - rankup: # Information on Customized Messsages https://github.com/okx-code/Rankup3/wiki/Configuration-Example#wrong-message - requirements-not-met: |- # These 2 characters indicate a multi-line message. - &3You need: - &c- &d&n${AMOUNT money} - &c- &d&n{AMOUNT xp-level} &3XP levels - &c- &d&n{AMOUNT total-mob-kills} &3mob kills - &3 to reach &c&l{RANK}&3! - # All correctly indented lines following those 2 characters, will be included in the message. - # These comments are not identically indented, and therefore will not appear in the message output! - # FAQ for multi-line messages: https://github.com/okx-code/Rankup3/wiki/FAQ#how-do-i-write-multi-line-messages - # We used config-placeholders here to autofill the message with the requirement values! - # This lists all placeholders: https://github.com/okx-code/Rankup3/wiki/Config-Placeholders -ExampleD: - rank: 'D' - next: 'E' - requirements: - - 'advancement story/iron_tools' # https://minecraft.fandom.com/wiki/Advancement#List_of_advancements - - 'block-break STONE 1000' # only available in versions 1.13+ https://github.com/okx-code/Rankup3/wiki/FAQ#what-rankup-version-do-i-need-for-minecraft-version-117116115114113112111110191817 + - 'money 5000' + - 'xp-level 2' + # you can have a custom messages for each rank + # the paths of these messages are the same as in the messages for your locale rankup: - requirements-not-met: 'You have {AMOUNT_DONE block-break#STONE}/{AMOUNT block-break#stone}.\n{AMOUNT_NEEDED block-break#STONE} stone left to break.' - # FAQ for multi-line messages: https://github.com/okx-code/Rankup3/wiki/FAQ#how-do-i-write-multi-line-messages + requirements-not-met: '&cYou need 5000 money and 2 levels of XP to rankup to D.' + list: + complete: "&7{{rank.rank}} &8\xbb &7{{next.rank}} &e(5000 money, 2 XP levels)" + current: "&c{{rank.rank}} &e\xbb &c{{next.rank}} &e(5000 money, 2 XP levels)" + incomplete: "&r{{rank.rank}} &e\xbb &r{{next.rank}} &e(5000 money, 2 XP levels)" \ No newline at end of file diff --git a/src/test/java/sh/okx/rankup/RankupBasicsTest.java b/src/test/java/sh/okx/rankup/RankupBasicsTest.java new file mode 100644 index 0000000..78ed6d8 --- /dev/null +++ b/src/test/java/sh/okx/rankup/RankupBasicsTest.java @@ -0,0 +1,68 @@ +package sh.okx.rankup; + + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import be.seeseemelk.mockbukkit.entity.PlayerMock; +import org.junit.jupiter.api.Test; +import sh.okx.rankup.messages.Message; +import sh.okx.rankup.ranks.Rank; +import sh.okx.rankup.ranks.RankElement; + +public class RankupBasicsTest extends RankupTest { + @Test + public void testAutoRankup() { + PlayerMock player = server.addPlayer(); + + // requirement of $1000 + plugin.getEconomy().setPlayer(player, 1000); + // give them group A + groupProvider.addGroup(player.getUniqueId(), "A"); + // give the permission to auto rankup + player.addAttachment(plugin, "rankup.auto", true); + + plugin.autoRankup.run(); + assertTrue(groupProvider.inGroup(player.getUniqueId(), "B")); + assertEquals(0, plugin.getEconomy().getBalance(player), 0); + } + + @Test + public void testNotInLadder() { + PlayerMock player = server.addPlayer(); + + plugin.getHelper().rankup(player); + + player.assertSaid(plugin.getMessage(Message.NOT_IN_LADDER).replacePlayer(player).toString()); + player.assertNoMoreSaid(); + } + + @Test + public void testLastRank() { + PlayerMock player = server.addPlayer(); + + groupProvider.addGroup(player.getUniqueId(), "D"); + + plugin.getHelper().rankup(player); + + player.assertSaid(plugin.getMessage(Message.NO_RANKUP).replacePlayer(player) + .replaceRank(plugin.getRankups().getTree().last().getRank()).toString()); + player.assertNoMoreSaid(); + } + + @Test + public void testMoneyRequirement() { + PlayerMock player = server.addPlayer(); + + plugin.getEconomy().setPlayer(player, 500); + + groupProvider.addGroup(player.getUniqueId(), "A"); + plugin.getHelper().rankup(player); + + RankElement element = plugin.getRankups().getTree().getFirst(); + Rank rank = element.getRank(); + + player.assertSaid(plugin.getMessage(rank, Message.REQUIREMENTS_NOT_MET).replacePlayer(player).replaceOldRank(rank).replaceRank(element.getNext().getRank()).toString(player)); + player.assertNoMoreSaid(); + } +} diff --git a/src/test/java/sh/okx/rankup/RankupCommandsTest.java b/src/test/java/sh/okx/rankup/RankupCommandsTest.java new file mode 100644 index 0000000..db09752 --- /dev/null +++ b/src/test/java/sh/okx/rankup/RankupCommandsTest.java @@ -0,0 +1,5 @@ +package sh.okx.rankup; + +public class RankupCommandsTest extends RankupTest { + +} diff --git a/src/test/java/sh/okx/rankup/RankupPlaceholderTest.java b/src/test/java/sh/okx/rankup/RankupPlaceholderTest.java index f5fd920..2277728 100644 --- a/src/test/java/sh/okx/rankup/RankupPlaceholderTest.java +++ b/src/test/java/sh/okx/rankup/RankupPlaceholderTest.java @@ -1,9 +1,10 @@ package sh.okx.rankup; -import static org.junit.Assert.assertEquals; + +import static org.junit.jupiter.api.Assertions.assertEquals; import be.seeseemelk.mockbukkit.entity.PlayerMock; -import org.junit.Test; +import org.junit.jupiter.api.Test; import sh.okx.rankup.placeholders.RankupExpansion; public class RankupPlaceholderTest extends RankupTest { diff --git a/src/test/java/sh/okx/rankup/RankupTest.java b/src/test/java/sh/okx/rankup/RankupTest.java index 012b1f7..80b7d5a 100644 --- a/src/test/java/sh/okx/rankup/RankupTest.java +++ b/src/test/java/sh/okx/rankup/RankupTest.java @@ -1,101 +1,85 @@ package sh.okx.rankup; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - import be.seeseemelk.mockbukkit.MockBukkit; import be.seeseemelk.mockbukkit.ServerMock; -import be.seeseemelk.mockbukkit.entity.PlayerMock; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import sh.okx.rankup.economy.TestEconomyProvider; +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.nio.file.FileVisitResult; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import sh.okx.rankup.providers.TestEconomyProvider; import sh.okx.rankup.hook.GroupProvider; -import sh.okx.rankup.hook.TestGroupProvider; -import sh.okx.rankup.hook.TestPermissionManager; -import sh.okx.rankup.messages.Message; -import sh.okx.rankup.messages.Variable; -import sh.okx.rankup.ranks.Rank; -import sh.okx.rankup.ranks.RankElement; +import sh.okx.rankup.providers.TestGroupProvider; +import sh.okx.rankup.providers.TestPermissionManager; + +public abstract class RankupTest { + private final File testResourceFolder; + + public RankupTest() { + this("default"); + } + + public RankupTest(String testResourceFolder) { + URL resource = this.getClass().getResource("/" + testResourceFolder); + if (resource != null) { + this.testResourceFolder = new File(resource.getPath()); + } else { + this.testResourceFolder = null; + } + } -public class RankupTest { protected GroupProvider groupProvider; protected ServerMock server; protected RankupPlugin plugin; - @Before + @BeforeEach public void setup() { - System.setProperty("TEST", "true"); + System.setProperty("RANKUP_TEST", "true"); try { groupProvider = new TestGroupProvider(); server = MockBukkit.mock(); - plugin = MockBukkit.load(RankupPlugin.class, new TestPermissionManager(groupProvider), new TestEconomyProvider()); + plugin = (RankupPlugin) server.getPluginManager() + .loadPlugin(RankupPlugin.class, new Object[]{ + new TestPermissionManager(groupProvider), + new TestEconomyProvider() + }); + + if (this.testResourceFolder != null) { + Path testPath = this.testResourceFolder.toPath(); + Path pluginPath = plugin.getDataFolder().toPath(); + Files.walkFileTree(testPath, new SimpleFileVisitor() { + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) + throws IOException { + Path out = pluginPath.resolve(testPath.relativize(file)); + System.out.println("Copy " + file + " to " + out); + out.getParent().toFile().mkdirs(); + Files.copy(file, out); + return super.visitFile(file, attrs); + } + }); + } + + server.getPluginManager().enablePlugin(plugin); + // let rankup finish setting up server.getScheduler().performTicks(1); + } catch (Exception e) { e.printStackTrace(); } } - @Test - public void testAutoRankup() { - PlayerMock player = server.addPlayer(); - - // requirement of $1000 - plugin.getEconomy().setPlayer(player, 1000); - // give them group A - groupProvider.addGroup(player.getUniqueId(), "A"); - // give the permission to auto rankup - player.addAttachment(plugin, "rankup.auto", true); - - plugin.autoRankup.run(); - assertTrue(groupProvider.inGroup(player.getUniqueId(), "B")); - assertEquals(0, plugin.getEconomy().getBalance(player), 0); - } - - @Test - public void testNotInLadder() { - PlayerMock player = server.addPlayer(); - - plugin.getHelper().rankup(player); - - player.assertSaid(plugin.getMessage(Message.NOT_IN_LADDER).replace(Variable.PLAYER, player.getName()).toString()); - player.assertNoMoreSaid(); - } - - @Test - public void testLastRank() { - PlayerMock player = server.addPlayer(); - - groupProvider.addGroup(player.getUniqueId(), "D"); - - plugin.getHelper().rankup(player); - - player.assertSaid(plugin.getMessage(Message.NO_RANKUP).replaceRanks(player, plugin.getRankups().getTree().last().getRank()).toString()); - player.assertNoMoreSaid(); - } - - @Test - public void testMoneyRequirement() { - PlayerMock player = server.addPlayer(); - - plugin.getEconomy().setPlayer(player, 500); - - groupProvider.addGroup(player.getUniqueId(), "A"); - plugin.getHelper().rankup(player); - - RankElement element = plugin.getRankups().getTree().getFirst(); - Rank rank = element.getRank(); - - player.assertSaid(plugin.replaceMoneyRequirements(plugin.getMessage(rank, Message.REQUIREMENTS_NOT_MET).replaceRanks(player, rank, element.getNext().getRank()), player, rank).toString()); - player.assertNoMoreSaid(); - } - - @After + @AfterEach public void tearDown() { MockBukkit.unmock(); - System.clearProperty("TEST"); + System.clearProperty("RANKUP_TEST"); } } diff --git a/src/test/java/sh/okx/rankup/commands/ComandPlaytimeTest.java b/src/test/java/sh/okx/rankup/commands/ComandPlaytimeTest.java new file mode 100644 index 0000000..19fe3aa --- /dev/null +++ b/src/test/java/sh/okx/rankup/commands/ComandPlaytimeTest.java @@ -0,0 +1,56 @@ +package sh.okx.rankup.commands; + +import static org.junit.jupiter.api.Assertions.*; + +import be.seeseemelk.mockbukkit.entity.PlayerMock; +import org.bukkit.ChatColor; +import org.bukkit.Statistic; +import org.junit.jupiter.api.Test; +import sh.okx.rankup.RankupTest; + +public class ComandPlaytimeTest extends RankupTest { + @Test + public void testAdd() { + PlayerMock player = server.addPlayer(); + + player.setStatistic(Statistic.PLAY_ONE_MINUTE, ticks(10)); + + player.addAttachment(plugin, "rankup.playtime", true); + player.performCommand("pru playtime add " + player.getName() + " 20"); + + assertEquals(ticks(30), player.getStatistic(Statistic.PLAY_ONE_MINUTE)); + } + + @Test + public void testSet() { + PlayerMock player = server.addPlayer(); + + player.setStatistic(Statistic.PLAY_ONE_MINUTE, ticks(20)); + + player.addAttachment(plugin, "rankup.playtime", true); + player.performCommand("pru playtime set " + player.getName() + " 25"); + + assertEquals(ticks(25), player.getStatistic(Statistic.PLAY_ONE_MINUTE)); + } + + @Test + public void testGetSelf() { + PlayerMock player = server.addPlayer(); + + player.setStatistic(Statistic.PLAY_ONE_MINUTE, ticks(5)); + + player.addAttachment(plugin, "rankup.playtime.get", true); + player.performCommand("pru playtime get " + player.getName()); + + player.assertSaid(ChatColor.LIGHT_PURPLE + "You have played for 5 minutes."); + player.assertNoMoreSaid(); + } + + private int ticks(int minutes) { + return minutes * 20 * 60; + } + + private int minutes(int ticks) { + return ticks / 20 / 60; + } +} diff --git a/src/test/java/sh/okx/rankup/RankupCommandTest.java b/src/test/java/sh/okx/rankup/commands/CommandInfoTest.java similarity index 77% rename from src/test/java/sh/okx/rankup/RankupCommandTest.java rename to src/test/java/sh/okx/rankup/commands/CommandInfoTest.java index 5ddb358..ce82a37 100644 --- a/src/test/java/sh/okx/rankup/RankupCommandTest.java +++ b/src/test/java/sh/okx/rankup/commands/CommandInfoTest.java @@ -1,12 +1,14 @@ -package sh.okx.rankup; +package sh.okx.rankup.commands; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; import be.seeseemelk.mockbukkit.entity.PlayerMock; -import org.junit.Test; +import org.junit.jupiter.api.Test; +import sh.okx.rankup.RankupTest; -public class RankupCommandTest extends RankupTest { +public class CommandInfoTest extends RankupTest { @Test public void testPlaceholders() { // placeholders command should never throw an exception diff --git a/src/test/java/sh/okx/rankup/legacy/LegacyPlaceholderTest.java b/src/test/java/sh/okx/rankup/legacy/LegacyPlaceholderTest.java new file mode 100644 index 0000000..dd63082 --- /dev/null +++ b/src/test/java/sh/okx/rankup/legacy/LegacyPlaceholderTest.java @@ -0,0 +1,27 @@ +package sh.okx.rankup.legacy; + +import be.seeseemelk.mockbukkit.entity.PlayerMock; +import java.text.DecimalFormat; +import org.junit.jupiter.api.Test; +import sh.okx.rankup.RankupTest; + +public class LegacyPlaceholderTest extends RankupTest { + public LegacyPlaceholderTest() { + super("legacy"); + } + + @Test + public void testLegacy() { + PlayerMock player = server.addPlayer("testPlayer"); + + plugin.getEconomy().setPlayer(player, 100); + player.setLevel(1); + + groupProvider.addGroup(player.getUniqueId(), "A"); + plugin.getHelper().rankup(player); + + DecimalFormat moneyFormat = new DecimalFormat("#,##0.##"); + player.assertSaid("testPlayer A B A-display last rank " + moneyFormat.format(1_000) + " 900 4 1 3 25 75"); + player.assertNoMoreSaid(); + } +} diff --git a/src/test/java/sh/okx/rankup/messages/MessageBuilderTest.java b/src/test/java/sh/okx/rankup/messages/MessageBuilderTest.java index f48d205..fd448a0 100644 --- a/src/test/java/sh/okx/rankup/messages/MessageBuilderTest.java +++ b/src/test/java/sh/okx/rankup/messages/MessageBuilderTest.java @@ -1,13 +1,13 @@ package sh.okx.rankup.messages; -import org.junit.Test; -import static org.hamcrest.CoreMatchers.instanceOf; -import static org.junit.Assert.assertThat; +import static org.junit.jupiter.api.Assertions.*; + +import org.junit.jupiter.api.Test; public class MessageBuilderTest { @Test public void testFailIfEmpty() { - assertThat(new MessageBuilder("").failIfEmpty(), instanceOf(NullMessageBuilder.class)); + assertTrue(new StringMessageBuilder("").failIfEmpty() instanceof NullMessageBuilder); } } \ No newline at end of file diff --git a/src/test/java/sh/okx/rankup/messages/RankupPlaceholderTest.java b/src/test/java/sh/okx/rankup/messages/RankupPlaceholderTest.java new file mode 100644 index 0000000..3597f8d --- /dev/null +++ b/src/test/java/sh/okx/rankup/messages/RankupPlaceholderTest.java @@ -0,0 +1,50 @@ +package sh.okx.rankup.messages; + + +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import be.seeseemelk.mockbukkit.entity.PlayerMock; +import org.junit.jupiter.api.Test; +import sh.okx.rankup.RankupTest; + +import java.text.DecimalFormat; + +public class RankupPlaceholderTest extends RankupTest { + public RankupPlaceholderTest() { + super("placeholder"); + } + + @Test + public void testReceivesSuccessMessages() { + PlayerMock player = server.addPlayer(); + PlayerMock receiver = server.addPlayer(); + + plugin.getEconomy().setPlayer(player, 1000); + + groupProvider.addGroup(player.getUniqueId(), "A"); + plugin.getHelper().rankup(player); + + // success-public message must be the same for both players + player.assertSaid(receiver.nextMessage()); + + // receiver does not receive success-private + receiver.assertNoMoreSaid(); + + // player receives success-private and nothing else + assertNotNull(player.nextMessage()); + player.assertNoMoreSaid(); + } + + @Test + public void testQuotientAndPercent() { + PlayerMock player = server.addPlayer(); + + plugin.getEconomy().setPlayer(player, 100); + + groupProvider.addGroup(player.getUniqueId(), "A"); + plugin.getHelper().rankup(player); + + DecimalFormat decimal = new DecimalFormat("#.#"); + player.assertSaid(decimal.format(0.1) + " 10"); + } +} diff --git a/src/test/java/sh/okx/rankup/pebble/PebbleTest.java b/src/test/java/sh/okx/rankup/pebble/PebbleTest.java new file mode 100644 index 0000000..e0d3fde --- /dev/null +++ b/src/test/java/sh/okx/rankup/pebble/PebbleTest.java @@ -0,0 +1,45 @@ +package sh.okx.rankup.pebble; + +import static org.junit.jupiter.api.Assertions.*; + +import be.seeseemelk.mockbukkit.entity.PlayerMock; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.junit.jupiter.api.Test; +import sh.okx.rankup.RankupTest; +import sh.okx.rankup.ranks.Rank; +import sh.okx.rankup.ranks.RankElement; +import sh.okx.rankup.text.pebble.PebbleTextProcessor; + +public class PebbleTest extends RankupTest { + @Test + public void testIndex() { + Map ctx = new HashMap<>(); + ctx.put("one", "2"); + List list = new ArrayList<>(); + list.add("L0"); + list.add("L1"); + list.add("L2"); + list.add("L3"); + ctx.put("list", list); + PebbleTextProcessor processor = new PebbleTextProcessor(ctx, null); + assertEquals("L2", processor.process("{{ list[one] }}")); + } + + @Test + public void testIterable() { + PlayerMock player = server.addPlayer(); + + plugin.getPermissions().addGroup(player.getUniqueId(), "C"); + RankElement rankElement = plugin.getRankups().getByPlayer(player); + + plugin.newMessageBuilder("{{ rank.requirements is iterable }}") + .replacePlayer(player) + .replaceOldRank(rankElement.getRank()) + .send(player); + + player.assertSaid("true"); + } +} diff --git a/src/test/java/sh/okx/rankup/prestige/BrokenPrestigeTest.java b/src/test/java/sh/okx/rankup/prestige/BrokenPrestigeTest.java new file mode 100644 index 0000000..ae5227e --- /dev/null +++ b/src/test/java/sh/okx/rankup/prestige/BrokenPrestigeTest.java @@ -0,0 +1,25 @@ +package sh.okx.rankup.prestige; + +import static org.junit.jupiter.api.Assertions.*; + +import be.seeseemelk.mockbukkit.entity.PlayerMock; +import org.bukkit.ChatColor; +import org.junit.jupiter.api.Test; +import sh.okx.rankup.RankupTest; + +public class BrokenPrestigeTest extends RankupTest { + + public BrokenPrestigeTest() { + super("brokenprestige"); + } + + @Test + public void testPrestige() { + PlayerMock player = server.addPlayer(); + plugin.getPermissions().addGroup(player.getUniqueId(), "C"); + + assertNull(plugin.getPrestiges().getByPlayer(player)); + plugin.getHelper().rankup(player); + player.assertSaid(ChatColor.YELLOW + "You are at the highest rank."); + } +} diff --git a/src/test/java/sh/okx/rankup/economy/TestEconomy.java b/src/test/java/sh/okx/rankup/providers/TestEconomy.java similarity index 81% rename from src/test/java/sh/okx/rankup/economy/TestEconomy.java rename to src/test/java/sh/okx/rankup/providers/TestEconomy.java index c7bc5e8..a011382 100644 --- a/src/test/java/sh/okx/rankup/economy/TestEconomy.java +++ b/src/test/java/sh/okx/rankup/providers/TestEconomy.java @@ -1,17 +1,18 @@ -package sh.okx.rankup.economy; +package sh.okx.rankup.providers; import org.bukkit.entity.Player; import java.util.HashMap; import java.util.Map; import java.util.UUID; +import sh.okx.rankup.economy.Economy; public class TestEconomy implements Economy { private final Map balances = new HashMap<>(); @Override public double getBalance(Player player) { - return balances.get(player.getUniqueId()); + return balances.getOrDefault(player.getUniqueId(), 0D); } @Override diff --git a/src/test/java/sh/okx/rankup/economy/TestEconomyProvider.java b/src/test/java/sh/okx/rankup/providers/TestEconomyProvider.java similarity index 56% rename from src/test/java/sh/okx/rankup/economy/TestEconomyProvider.java rename to src/test/java/sh/okx/rankup/providers/TestEconomyProvider.java index 1de1970..0f390c2 100644 --- a/src/test/java/sh/okx/rankup/economy/TestEconomyProvider.java +++ b/src/test/java/sh/okx/rankup/providers/TestEconomyProvider.java @@ -1,4 +1,7 @@ -package sh.okx.rankup.economy; +package sh.okx.rankup.providers; + +import sh.okx.rankup.economy.Economy; +import sh.okx.rankup.economy.EconomyProvider; public class TestEconomyProvider implements EconomyProvider { @Override diff --git a/src/test/java/sh/okx/rankup/hook/TestGroupProvider.java b/src/test/java/sh/okx/rankup/providers/TestGroupProvider.java similarity index 89% rename from src/test/java/sh/okx/rankup/hook/TestGroupProvider.java rename to src/test/java/sh/okx/rankup/providers/TestGroupProvider.java index a8734b0..b8b0d96 100644 --- a/src/test/java/sh/okx/rankup/hook/TestGroupProvider.java +++ b/src/test/java/sh/okx/rankup/providers/TestGroupProvider.java @@ -1,9 +1,10 @@ -package sh.okx.rankup.hook; +package sh.okx.rankup.providers; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Multimap; import java.util.UUID; +import sh.okx.rankup.hook.GroupProvider; public class TestGroupProvider implements GroupProvider { private Multimap groups = ArrayListMultimap.create(); diff --git a/src/test/java/sh/okx/rankup/hook/TestPermissionManager.java b/src/test/java/sh/okx/rankup/providers/TestPermissionManager.java similarity index 78% rename from src/test/java/sh/okx/rankup/hook/TestPermissionManager.java rename to src/test/java/sh/okx/rankup/providers/TestPermissionManager.java index c79977e..9ac5172 100644 --- a/src/test/java/sh/okx/rankup/hook/TestPermissionManager.java +++ b/src/test/java/sh/okx/rankup/providers/TestPermissionManager.java @@ -1,4 +1,7 @@ -package sh.okx.rankup.hook; +package sh.okx.rankup.providers; + +import sh.okx.rankup.hook.GroupProvider; +import sh.okx.rankup.hook.PermissionManager; public class TestPermissionManager implements PermissionManager { private final GroupProvider groupProvider; diff --git a/src/test/java/sh/okx/rankup/ranksgui/RanksGuiTest.java b/src/test/java/sh/okx/rankup/ranksgui/RanksGuiTest.java new file mode 100644 index 0000000..bb72604 --- /dev/null +++ b/src/test/java/sh/okx/rankup/ranksgui/RanksGuiTest.java @@ -0,0 +1,40 @@ +package sh.okx.rankup.ranksgui; + +import static org.junit.jupiter.api.Assertions.*; + +import be.seeseemelk.mockbukkit.entity.PlayerMock; +import org.bukkit.inventory.Inventory; +import org.junit.jupiter.api.Test; +import sh.okx.rankup.RankupTest; + +public class RanksGuiTest extends RankupTest { + + public RanksGuiTest() { + super("ranksgui"); + } + + @Test + public void testRowsWithGroup() { + PlayerMock player = server.addPlayer(); + plugin.getPermissions().addGroup(player.getUniqueId(), "a"); + + player.addAttachment(plugin, "rankup.ranks", true); + server.dispatchCommand(player, "ranks"); + + Inventory top = player.getOpenInventory().getTopInventory(); + assertNotNull(top, "ranks gui has not opened"); + assertEquals(36, top.getSize(), "ranks gui is configured to have 4 rows"); + } + + @Test + public void testRowsWithoutGroup() { + PlayerMock player = server.addPlayer(); + + player.addAttachment(plugin, "rankup.ranks", true); + server.dispatchCommand(player, "ranks"); + + Inventory top = player.getOpenInventory().getTopInventory(); + assertNotNull(top, "ranks gui has not opened"); + assertEquals(27, top.getSize(), "ranks gui is configured to have 3 rows"); + } +} diff --git a/src/test/java/sh/okx/rankup/requirements/PrestigeRequirementsTest.java b/src/test/java/sh/okx/rankup/requirements/PrestigeRequirementsTest.java new file mode 100644 index 0000000..b68bab0 --- /dev/null +++ b/src/test/java/sh/okx/rankup/requirements/PrestigeRequirementsTest.java @@ -0,0 +1,29 @@ +package sh.okx.rankup.requirements; + +import static org.junit.jupiter.api.Assertions.*; + +import be.seeseemelk.mockbukkit.entity.PlayerMock; +import org.junit.jupiter.api.Test; +import sh.okx.rankup.RankupTest; + +public class PrestigeRequirementsTest extends RankupTest { + + public PrestigeRequirementsTest() { + super("prestigerequirements"); + } + + @Test + public void testPrestigeRequirements() { + PlayerMock player = server.addPlayer(); + + plugin.getPermissions().addGroup(player.getUniqueId(), "p1"); + plugin.getPermissions().addGroup(player.getUniqueId(), "a"); + + plugin.getEconomy().setPlayer(player, 200); + + plugin.getHelper().rankup(player); + + assertTrue(plugin.getPermissions().inGroup(player.getUniqueId(), "b"), "player is not in group b"); + assertEquals(0, plugin.getEconomy().getBalance(player), "prestige requirements did not take correct amount of money"); + } +} diff --git a/src/test/java/sh/okx/rankup/toml/TomlTest.java b/src/test/java/sh/okx/rankup/toml/TomlTest.java new file mode 100644 index 0000000..e769a04 --- /dev/null +++ b/src/test/java/sh/okx/rankup/toml/TomlTest.java @@ -0,0 +1,55 @@ +package sh.okx.rankup.toml; + +import static org.junit.jupiter.api.Assertions.*; + +import be.seeseemelk.mockbukkit.entity.PlayerMock; +import org.bukkit.ChatColor; +import org.junit.jupiter.api.Test; +import sh.okx.rankup.RankupTest; +import sh.okx.rankup.ranks.Rankups; + +public class TomlTest extends RankupTest { + + public TomlTest() { + super("toml"); + } + + @Test + public void testRequirementsNotMet() { + PlayerMock player = server.addPlayer(); + + Rankups ranks = plugin.getRankups(); + assertEquals(1500, ranks.getFirst().getRequirement(null, "money").getValueDouble()); + + plugin.getPermissions().addGroup(player.getUniqueId(), "C"); + player.addAttachment(plugin, "rankup.rankup", true); + plugin.getHelper().rankup(player); + + player.assertSaid("toml"); + } + + @Test + public void testRankup() { + PlayerMock player = server.addPlayer(); + plugin.getPermissions().addGroup(player.getUniqueId(), "B"); + plugin.getEconomy().setPlayer(player, 10000); + player.addAttachment(plugin, "rankup.rankup", true); + + plugin.getHelper().rankup(player); + + assertTrue(plugin.getPermissions().inGroup(player.getUniqueId(), "C")); + } + + @Test + public void testRanks() { + PlayerMock player = server.addPlayer(); + plugin.getPermissions().addGroup(player.getUniqueId(), "C"); + + player.addAttachment(plugin, "rankup.ranks", true); + plugin.getCommand("ranks").execute(player, "ranks", new String[0]); + player.assertSaid(ChatColor.GRAY + "A " + ChatColor.DARK_GRAY + "\u00bb " + ChatColor.GRAY + "B"); + player.assertSaid(ChatColor.GRAY + "B " + ChatColor.DARK_GRAY + "\u00bb " + ChatColor.GRAY + "C"); + player.assertSaid(ChatColor.RED + "C " + ChatColor.YELLOW + "\u00bb " + ChatColor.RED + "D o"); + player.assertNoMoreSaid(); + } +} diff --git a/src/test/resources/brokenprestige/config.yml b/src/test/resources/brokenprestige/config.yml new file mode 100644 index 0000000..b3907bc --- /dev/null +++ b/src/test/resources/brokenprestige/config.yml @@ -0,0 +1,36 @@ +version: 10 +locale: en +autorankup-interval: 0 +ranks: true +prestiges: true +ranks-gui: false +prestige: true # enable prestige +notify-update: true +permission-rankup: false +confirmation-type: 'gui' +cooldown: 1 +max-rankup: + enabled: false + individual-messages: true +text: + timeout: 10 +placeholders: + money-format: "#,##0.##" + percent-format: "0.##" + simple-format: "#.##" + not-in-ladder: "None" + no-prestige: "None" + highest-rank: "None" + status: + complete: "Complete" + current: "Current" + incomplete: "Incomplete" + last-rank-display-name: "last rank" +shorten: + - 'K' + - 'M' + - 'B' + - 'T' + - 'Q' + - 'Qu' + - 'S' diff --git a/src/test/resources/brokenprestige/rankups.yml b/src/test/resources/brokenprestige/rankups.yml new file mode 100644 index 0000000..4170025 --- /dev/null +++ b/src/test/resources/brokenprestige/rankups.yml @@ -0,0 +1,10 @@ +Arank: + rank: 'A' + next: 'B' + requirements: + - 'money 1000' +Brank: + rank: 'B' + next: 'C' + requirements: + - 'money 2500' \ No newline at end of file diff --git a/src/test/resources/legacy/rankups.yml b/src/test/resources/legacy/rankups.yml new file mode 100644 index 0000000..46d7bfd --- /dev/null +++ b/src/test/resources/legacy/rankups.yml @@ -0,0 +1,10 @@ +A: + rank: 'A' + next: 'B' + display-name: 'A-display' + requirements: + - 'money 1000' + - 'xp-level 4' + rankup: + # Test legacy placeholders + requirements-not-met: '{PLAYER} {OLD_RANK} {RANK} {OLD_RANK_NAME} {RANK_NAME} {MONEY} {MONEY_NEEDED} {AMOUNT xp-level} {AMOUNT_DONE xp-level} {AMOUNT_NEEDED xp-level} {PERCENT_DONE xp-level} {PERCENT_LEFT xp-level}' \ No newline at end of file diff --git a/src/test/resources/placeholder/rankups.yml b/src/test/resources/placeholder/rankups.yml new file mode 100644 index 0000000..8afed18 --- /dev/null +++ b/src/test/resources/placeholder/rankups.yml @@ -0,0 +1,7 @@ +A: + rank: 'A' + next: 'B' + requirements: + - 'money 1000' + rankup: + requirements-not-met: '{{rank.requirement("money").quotient | simple}} {{rank.requirement("money").percent | simple}}' \ No newline at end of file diff --git a/src/test/resources/prestigerequirements/config.yml b/src/test/resources/prestigerequirements/config.yml new file mode 100644 index 0000000..e62e5b3 --- /dev/null +++ b/src/test/resources/prestigerequirements/config.yml @@ -0,0 +1,111 @@ +# this is used for letting you know that you need to update/change your config file +version: 10 + +# the locale to use for messages +# all messages can be customised but this allows you to +# choose messages that are already translated +# locales can be found in the locale/ folder +locale: en + +# interval (in minutes) to check to autorankup players +# ranking up manually will always be enabled +# set to 0 to disable +autorankup-interval: 0 + +# whether /ranks and /prestiges should be enabled (true) or disabled (false) +# /rankup3 reload will not do anything if this is changed, +# you will have to restart your server. +ranks: true +# you can alternatively negate the permission rankup.prestiges +# this will also make the command not autocomplete in 1.13 +prestiges: true + +# whether to enable the /ranks GUI. +# will override the /ranks command +ranks-gui: false + +# whether or not /prestige and /prestiges should be enabled. +# when a player reaches the top rank, they can do /prestige to return to the first rank, +# but you will be able to grant them an additional "prestige" group or additional items. +# +# if you do not want this command to autocomplete, make sure +# you negate the permission rankup.prestige with your permissions plugin. +# if enabled, a prestiges.yml file will be generated with some example prestiges +# You must restart your server when you change this for it to work! +prestige: true + +# if true, players with the permission rankup.notify will receive notifications when they join +# to update if the server is on an older version of Rankup. +notify-update: true + +# if rankups and prestiges should be by permissions +# if false, players will be checked for if they have a group of the same name as in rankups.yml, +# and automatically added and taken away from those groups. +# if true, players will be checked for the permission rankup.rank.RANK, where RANK +# is the rankup in rankups.yml. Nothing will automatically happen on rankup, so you must +# use commands to change a player's group or permission. +permission-rankup: false + +# how people should confirm ranking up +# options are: gui, text or none +confirmation-type: 'gui' + +# how long, in seconds, people have to wait between a successful /rankup or /prestige +# set to 0 to disable. +cooldown: 1 + +# if enabled, players can run /maxrankup to rankup as many times as possible, +# before they fail the requirements for the next rank. +# the permission rankup.maxrankup is used for this command, but it is given by default. +# note that /maxrankup, if enabled, has no confirmation. +max-rankup: + # You must restart your server if you enable or disable /maxrankup! + enabled: false + # whether to send a message for each rankup a player does + # if set to true, the chat may be spammed for each rankup a player goes through with /maxrankup + # if set to false, only the last rankup will be shown (if a player starts on rank A, then does + # /maxrankup and ranks up to B and then C, it will just say "player has ranked up to C") + individual-messages: true + +# options when using the text rankup confirmation +text: + # the time in seconds for a player to + # confirm by typing /rankup again + timeout: 10 + +# placeholders: +# https://okx.sh/rankup/Placeholders.html +placeholders: + # format for money. for more information, see + # https://docs.oracle.com/javase/8/docs/api/java/text/DecimalFormat.html + money-format: "#,##0.##" + percent-format: "0.##" + # the format used for requirements + simple-format: "#.##" + # used for current_rank and next_rank placeholders when a player is not in anything in rankups.yml + not-in-ladder: "None" + # used in the current_prestige placeholders when a player hasn't prestiged yet + no-prestige: "None" + # used in the next_rank and next_prestige placeholders when a player is at the highest rank or prestige + highest-rank: "None" + # used in the %rankup_status_[rank]% placeholders + status: + complete: "Complete" + current: "Current" + incomplete: "Incomplete" + last-rank-display-name: "last rank" + +# what to shorten money by. +# ie 1000 -> 1k +# set to an empty list to disable +# for each entry here, it counts as increasing by a factor of 1,000 +# the first represents thousands (1,000) then millions (1,000,000) then billions (1,000,000,000) etc. +# this is used in the "| shortmoney" filter +shorten: + - 'K' + - 'M' + - 'B' + - 'T' + - 'Q' + - 'Qu' + - 'S' \ No newline at end of file diff --git a/src/test/resources/prestigerequirements/prestiges.yml b/src/test/resources/prestigerequirements/prestiges.yml new file mode 100644 index 0000000..b0c296d --- /dev/null +++ b/src/test/resources/prestigerequirements/prestiges.yml @@ -0,0 +1,6 @@ +first: + from: 'B' + to: 'A' + next: 'P1' + requirements: + - 'money 10000' \ No newline at end of file diff --git a/src/test/resources/prestigerequirements/rankups.yml b/src/test/resources/prestigerequirements/rankups.yml new file mode 100644 index 0000000..9051682 --- /dev/null +++ b/src/test/resources/prestigerequirements/rankups.yml @@ -0,0 +1,8 @@ +a: + rank: 'A' + next: 'B' + requirements: + default: + - 'money 100' + p1: + - 'money 200' \ No newline at end of file diff --git a/src/test/resources/ranksgui/config.yml b/src/test/resources/ranksgui/config.yml new file mode 100644 index 0000000..d68eeb4 --- /dev/null +++ b/src/test/resources/ranksgui/config.yml @@ -0,0 +1,111 @@ +# this is used for letting you know that you need to update/change your config file +version: 10 + +# the locale to use for messages +# all messages can be customised but this allows you to +# choose messages that are already translated +# locales can be found in the locale/ folder +locale: en + +# interval (in minutes) to check to autorankup players +# ranking up manually will always be enabled +# set to 0 to disable +autorankup-interval: 0 + +# whether /ranks and /prestiges should be enabled (true) or disabled (false) +# /rankup3 reload will not do anything if this is changed, +# you will have to restart your server. +ranks: true +# you can alternatively negate the permission rankup.prestiges +# this will also make the command not autocomplete in 1.13 +prestiges: true + +# whether to enable the /ranks GUI. +# will override the /ranks command +ranks-gui: true + +# whether or not /prestige and /prestiges should be enabled. +# when a player reaches the top rank, they can do /prestige to return to the first rank, +# but you will be able to grant them an additional "prestige" group or additional items. +# +# if you do not want this command to autocomplete, make sure +# you negate the permission rankup.prestige with your permissions plugin. +# if enabled, a prestiges.yml file will be generated with some example prestiges +# You must restart your server when you change this for it to work! +prestige: false + +# if true, players with the permission rankup.notify will receive notifications when they join +# to update if the server is on an older version of Rankup. +notify-update: true + +# if rankups and prestiges should be by permissions +# if false, players will be checked for if they have a group of the same name as in rankups.yml, +# and automatically added and taken away from those groups. +# if true, players will be checked for the permission rankup.rank.RANK, where RANK +# is the rankup in rankups.yml. Nothing will automatically happen on rankup, so you must +# use commands to change a player's group or permission. +permission-rankup: false + +# how people should confirm ranking up +# options are: gui, text or none +confirmation-type: 'gui' + +# how long, in seconds, people have to wait between a successful /rankup or /prestige +# set to 0 to disable. +cooldown: 1 + +# if enabled, players can run /maxrankup to rankup as many times as possible, +# before they fail the requirements for the next rank. +# the permission rankup.maxrankup is used for this command, but it is given by default. +# note that /maxrankup, if enabled, has no confirmation. +max-rankup: + # You must restart your server if you enable or disable /maxrankup! + enabled: false + # whether to send a message for each rankup a player does + # if set to true, the chat may be spammed for each rankup a player goes through with /maxrankup + # if set to false, only the last rankup will be shown (if a player starts on rank A, then does + # /maxrankup and ranks up to B and then C, it will just say "player has ranked up to C") + individual-messages: true + +# options when using the text rankup confirmation +text: + # the time in seconds for a player to + # confirm by typing /rankup again + timeout: 10 + +# placeholders: +# https://okx.sh/rankup/Placeholders.html +placeholders: + # format for money. for more information, see + # https://docs.oracle.com/javase/8/docs/api/java/text/DecimalFormat.html + money-format: "#,##0.##" + percent-format: "0.##" + # the format used for requirements + simple-format: "#.##" + # used for current_rank and next_rank placeholders when a player is not in anything in rankups.yml + not-in-ladder: "None" + # used in the current_prestige placeholders when a player hasn't prestiged yet + no-prestige: "None" + # used in the next_rank and next_prestige placeholders when a player is at the highest rank or prestige + highest-rank: "None" + # used in the %rankup_status_[rank]% placeholders + status: + complete: "Complete" + current: "Current" + incomplete: "Incomplete" + last-rank-display-name: "last rank" + +# what to shorten money by. +# ie 1000 -> 1k +# set to an empty list to disable +# for each entry here, it counts as increasing by a factor of 1,000 +# the first represents thousands (1,000) then millions (1,000,000) then billions (1,000,000,000) etc. +# this is used in the "| shortmoney" filter +shorten: + - 'K' + - 'M' + - 'B' + - 'T' + - 'Q' + - 'Qu' + - 'S' diff --git a/src/test/resources/ranksgui/rankups.yml b/src/test/resources/ranksgui/rankups.yml new file mode 100644 index 0000000..6b1fd97 --- /dev/null +++ b/src/test/resources/ranksgui/rankups.yml @@ -0,0 +1,6 @@ +a: + rank: 'a' + next: 'b' + rankup: + ranksgui: + rows: 4 \ No newline at end of file diff --git a/src/test/resources/toml/rankups.toml b/src/test/resources/toml/rankups.toml new file mode 100644 index 0000000..eb30668 --- /dev/null +++ b/src/test/resources/toml/rankups.toml @@ -0,0 +1,25 @@ +[A] +rank = "A" +next = "B" +requirements = [ + "money 1500" +] +[B] +rank = "B" +next = "C" +requirements = [ + "money 2500" +] +[C] +rank = "C" +next = "D" +requirements = [ + "money 6000", + "xp-level 2" +] +[C.rankup] + requirements-not-met = "toml" + [C.rankup.list] + complete = "&7{{rank.rank}} &8\u00bb &7{{next.rank}} t" + current = "&c{{rank.rank}} &e\u00bb &c{{next.rank}} o" + incomplete = "&r{{rank.rank}} &e\u00bb &r{{next.rank}} m" \ No newline at end of file From e50f04f2ceac833d324663754fbab0cbe5d7280c Mon Sep 17 00:00:00 2001 From: NicBOMB <19939315+NicBOMB@users.noreply.github.com> Date: Sat, 5 Feb 2022 13:02:50 -0800 Subject: [PATCH 04/27] add null checks to prevent log spam when tree is null (failed to build) If there's a possibility that the tree is going to be null we should account for it rather than let the plugin spam like: Caused by: java.lang.NullPointerException: Cannot invoke "sh.okx.rankup.ranks.RankTree.asList()" because "this.tree" is null at sh.okx.rankup.ranks.RankList.getByPlayer(RankList.java:110) ~[Rankup-3.13.2.jar:?] --- src/main/java/sh/okx/rankup/ranks/RankList.java | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/main/java/sh/okx/rankup/ranks/RankList.java b/src/main/java/sh/okx/rankup/ranks/RankList.java index 24d6341..e6165c8 100644 --- a/src/main/java/sh/okx/rankup/ranks/RankList.java +++ b/src/main/java/sh/okx/rankup/ranks/RankList.java @@ -81,9 +81,7 @@ public T getFirst() { } public T getRankByName(String name) { - if (name == null) { - return null; - } + if (name == null || tree == null){return null;} for (T rank : tree) { if (name.equalsIgnoreCase(rank.getRank())) { return rank; @@ -93,9 +91,7 @@ public T getRankByName(String name) { } public RankElement getByName(String name) { - if (name == null) { - return null; - } + if (name == null || tree == null){return null;} List> rankElements = tree.asList(); for (RankElement rank : rankElements) { if (name.equalsIgnoreCase(rank.getRank().getRank())) { @@ -107,6 +103,7 @@ public RankElement getByName(String name) { public RankElement getByPlayer(Player player) { + if (tree == null){return null;} List> list = tree.asList(); Collections.reverse(list); for (RankElement t : list) { @@ -118,6 +115,7 @@ public RankElement getByPlayer(Player player) { } public T getRankByPlayer(Player player) { + if (tree == null){return null;} List> list = tree.asList(); Collections.reverse(list); for (RankElement t : list) { From d55e38c0f0671c4bc674eae2574a484d76acf2a4 Mon Sep 17 00:00:00 2001 From: NicBOMB <19939315+NicBOMB@users.noreply.github.com> Date: Sun, 6 Feb 2022 08:55:07 -0800 Subject: [PATCH 05/27] implement getSub for pebble to use getSub already defined in requirement --- .../sh/okx/rankup/messages/pebble/RequirementContext.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/sh/okx/rankup/messages/pebble/RequirementContext.java b/src/main/java/sh/okx/rankup/messages/pebble/RequirementContext.java index edcf1b3..4dedb10 100644 --- a/src/main/java/sh/okx/rankup/messages/pebble/RequirementContext.java +++ b/src/main/java/sh/okx/rankup/messages/pebble/RequirementContext.java @@ -33,6 +33,10 @@ public String getName() { return requirement.getName(); } + public String getSub(){ + return requirement.getSub(); + } + public double getQuotient() { return getProgress() / getTotal(); } From cba3736af2d601179a1a71b354c35d9dac61a126 Mon Sep 17 00:00:00 2001 From: NicBOMB <19939315+NicBOMB@users.noreply.github.com> Date: Sun, 6 Feb 2022 08:57:23 -0800 Subject: [PATCH 06/27] prevent NaN by checking total and replace duplicate calls with 1 call --- .../sh/okx/rankup/messages/pebble/RequirementContext.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/sh/okx/rankup/messages/pebble/RequirementContext.java b/src/main/java/sh/okx/rankup/messages/pebble/RequirementContext.java index 4dedb10..db7160b 100644 --- a/src/main/java/sh/okx/rankup/messages/pebble/RequirementContext.java +++ b/src/main/java/sh/okx/rankup/messages/pebble/RequirementContext.java @@ -38,11 +38,11 @@ public String getSub(){ } public double getQuotient() { - return getProgress() / getTotal(); + return (getTotal() > 0 ? (getProgress() / getTotal()) : 1); } public double getPercent() { - return getProgress() / getTotal() * 100; + return getQuotient() * 100; } public String toString() { From 1e49aec2a57ddc05d01e0a5b8ead08b41a20d013 Mon Sep 17 00:00:00 2001 From: NicBOMB <19939315+NicBOMB@users.noreply.github.com> Date: Mon, 7 Feb 2022 16:59:01 -0800 Subject: [PATCH 07/27] consistency, transparency, and indentation recommendations some requirements had their names listed in RankupPlugin.java this is useful, but was not consistent. Also, indentation was incorrect for all `new *Requirement` lines --- src/main/java/sh/okx/rankup/RankupPlugin.java | 68 +++++++++++-------- .../requirement/AdvancementRequirement.java | 4 +- .../requirement/BlockBreakRequirement.java | 4 +- .../requirement/CraftItemRequirement.java | 4 +- .../requirement/GroupRequirement.java | 4 +- .../requirement/MobKillsRequirement.java | 4 +- .../requirement/PermissionRequirement.java | 4 +- .../requirement/PlaceholderRequirement.java | 4 +- .../requirement/PlayerKillsRequirement.java | 4 +- .../PlaytimeMinutesRequirement.java | 4 +- .../requirement/TotalMobKillsRequirement.java | 4 +- .../requirement/UseItemRequirement.java | 4 +- .../requirement/WorldRequirement.java | 4 +- ...cedAchievementsAchievementRequirement.java | 4 +- .../AdvancedAchievementsTotalRequirement.java | 4 +- .../mcmmo/McMMOPowerLevelRequirement.java | 4 +- .../mcmmo/McMMOSkillRequirement.java | 4 +- .../SuperbVoteVotesRequirement.java | 4 +- .../TownyKingNumberResidentsRequirement.java | 4 +- .../TownyKingNumberTownsRequirement.java | 4 +- .../towny/TownyKingRequirement.java | 4 +- .../TownyMayorNumberResidentsRequirement.java | 4 +- .../towny/TownyMayorRequirement.java | 4 +- .../towny/TownyResidentRequirement.java | 4 +- .../VotingPluginVotesRequirement.java | 4 +- 25 files changed, 86 insertions(+), 78 deletions(-) diff --git a/src/main/java/sh/okx/rankup/RankupPlugin.java b/src/main/java/sh/okx/rankup/RankupPlugin.java index aa25570..9d0d579 100644 --- a/src/main/java/sh/okx/rankup/RankupPlugin.java +++ b/src/main/java/sh/okx/rankup/RankupPlugin.java @@ -380,59 +380,67 @@ private void registerRequirements() { requirements.addRequirements( new XpLevelRequirement(this, "xp-levelh"), new XpLevelDeductibleRequirement(this, "xp-level"), - new PlaytimeMinutesRequirement(this), - new AdvancementRequirement(this), - new GroupRequirement(this), - new PermissionRequirement(this), - new PlaceholderRequirement(this), - new WorldRequirement(this), - new BlockBreakRequirement(this), - new PlayerKillsRequirement(this), - new MobKillsRequirement(this), + new PlaytimeMinutesRequirement(this, "playtime-minutes"), + new AdvancementRequirement(this, "advancement"), + new GroupRequirement(this, "group"), + new PermissionRequirement(this, "permission"), + new PlaceholderRequirement(this, "placeholder"), + new WorldRequirement(this, "world"), + new BlockBreakRequirement(this, "block-break"), + new PlayerKillsRequirement(this, "player-kills"), + new MobKillsRequirement(this, "mob-kills"), new ItemRequirement(this, "itemh"), new ItemDeductibleRequirement(this, "item"), - new UseItemRequirement(this), - new TotalMobKillsRequirement(this), - new CraftItemRequirement(this)); + new UseItemRequirement(this, "use-item"), + new TotalMobKillsRequirement(this, "total-mob-kills"), + new CraftItemRequirement(this, "craft-item")); if (economy != null) { requirements.addRequirements( - new MoneyRequirement(this, "moneyh"), - new MoneyDeductibleRequirement(this, "money")); + new MoneyRequirement(this, "moneyh"), + new MoneyDeductibleRequirement(this, "money") + ); } PluginManager pluginManager = Bukkit.getPluginManager(); if (pluginManager.isPluginEnabled("mcMMO")) { requirements.addRequirements( - new McMMOSkillRequirement(this), - new McMMOPowerLevelRequirement(this)); + new McMMOSkillRequirement(this, "mcmmo"), + new McMMOPowerLevelRequirement(this, "mcmmo-power-level") + ); } if (pluginManager.isPluginEnabled("AdvancedAchievements")) { requirements.addRequirements( - new AdvancedAchievementsAchievementRequirement(this), - new AdvancedAchievementsTotalRequirement(this)); + new AdvancedAchievementsAchievementRequirement(this, "advancedachievements-achievement"), + new AdvancedAchievementsTotalRequirement(this, "advancedachievements-total") + ); } if (pluginManager.isPluginEnabled("VotingPlugin")) { requirements.addRequirements( - new VotingPluginVotesRequirement(this), - new VotingPluginPointsRequirement(this, "votingplugin-pointsh"), - new VotingPluginPointsDeductibleRequirement(this, "votingplugin-points")); + new VotingPluginVotesRequirement(this, "votingplugin-votes"), + new VotingPluginPointsRequirement(this, "votingplugin-pointsh"), + new VotingPluginPointsDeductibleRequirement(this, "votingplugin-points") + ); } if (Bukkit.getPluginManager().isPluginEnabled("Towny")) { requirements.addRequirements( - new TownyResidentRequirement(this), - new TownyMayorRequirement(this), - new TownyMayorNumberResidentsRequirement(this), - new TownyKingRequirement(this), - new TownyKingNumberResidentsRequirement(this), - new TownyKingNumberTownsRequirement(this)); + new TownyResidentRequirement(this, "towny-resident"), + new TownyMayorRequirement(this, "towny-mayor"), + new TownyMayorNumberResidentsRequirement(this, "towny-mayor-residents"), + new TownyKingRequirement(this, "towny-king"), + new TownyKingNumberResidentsRequirement(this, "towny-king-residents"), + new TownyKingNumberTownsRequirement(this, "towny-king-towns") + ); } if (Bukkit.getPluginManager().isPluginEnabled("TokenManager")) { requirements.addRequirements( - new TokensRequirement(this, "tokenmanager-tokensh"), - new TokensDeductibleRequirement(this, "tokenmanager-tokens")); + new TokensRequirement(this, "tokenmanager-tokensh"), + new TokensDeductibleRequirement(this, "tokenmanager-tokens") + ); } if (Bukkit.getPluginManager().isPluginEnabled("SuperbVote")) { - requirements.addRequirements(new SuperbVoteVotesRequirement(this)); + requirements.addRequirements( + new SuperbVoteVotesRequirement(this, "superbvote-votes") + ); } } private void setupEconomy() { diff --git a/src/main/java/sh/okx/rankup/requirements/requirement/AdvancementRequirement.java b/src/main/java/sh/okx/rankup/requirements/requirement/AdvancementRequirement.java index ad75b90..52128e1 100644 --- a/src/main/java/sh/okx/rankup/requirements/requirement/AdvancementRequirement.java +++ b/src/main/java/sh/okx/rankup/requirements/requirement/AdvancementRequirement.java @@ -15,8 +15,8 @@ * @author Link, with modifications from Okx */ public class AdvancementRequirement extends Requirement { - public AdvancementRequirement(RankupPlugin plugin) { - super(plugin, "advancement"); + public AdvancementRequirement(RankupPlugin plugin, String name) { + super(plugin, name); } protected AdvancementRequirement(Requirement clone) { diff --git a/src/main/java/sh/okx/rankup/requirements/requirement/BlockBreakRequirement.java b/src/main/java/sh/okx/rankup/requirements/requirement/BlockBreakRequirement.java index fded98f..3f7d068 100644 --- a/src/main/java/sh/okx/rankup/requirements/requirement/BlockBreakRequirement.java +++ b/src/main/java/sh/okx/rankup/requirements/requirement/BlockBreakRequirement.java @@ -8,8 +8,8 @@ import sh.okx.rankup.requirements.Requirement; public class BlockBreakRequirement extends ProgressiveRequirement { - public BlockBreakRequirement(RankupPlugin plugin) { - super(plugin, "block-break", true); + public BlockBreakRequirement(RankupPlugin plugin, String name) { + super(plugin, name, true); } protected BlockBreakRequirement(BlockBreakRequirement clone) { diff --git a/src/main/java/sh/okx/rankup/requirements/requirement/CraftItemRequirement.java b/src/main/java/sh/okx/rankup/requirements/requirement/CraftItemRequirement.java index 9499b16..8cf934e 100644 --- a/src/main/java/sh/okx/rankup/requirements/requirement/CraftItemRequirement.java +++ b/src/main/java/sh/okx/rankup/requirements/requirement/CraftItemRequirement.java @@ -8,8 +8,8 @@ import sh.okx.rankup.requirements.Requirement; public class CraftItemRequirement extends ProgressiveRequirement { - public CraftItemRequirement(RankupPlugin plugin) { - super(plugin, "craft-item", true); + public CraftItemRequirement(RankupPlugin plugin, String name) { + super(plugin, name, true); } protected CraftItemRequirement(CraftItemRequirement clone) { diff --git a/src/main/java/sh/okx/rankup/requirements/requirement/GroupRequirement.java b/src/main/java/sh/okx/rankup/requirements/requirement/GroupRequirement.java index 757fa37..c8a8318 100644 --- a/src/main/java/sh/okx/rankup/requirements/requirement/GroupRequirement.java +++ b/src/main/java/sh/okx/rankup/requirements/requirement/GroupRequirement.java @@ -5,8 +5,8 @@ import sh.okx.rankup.requirements.Requirement; public class GroupRequirement extends Requirement { - public GroupRequirement(RankupPlugin plugin) { - super(plugin, "group"); + public GroupRequirement(RankupPlugin plugin, String name) { + super(plugin, name); } protected GroupRequirement(Requirement clone) { diff --git a/src/main/java/sh/okx/rankup/requirements/requirement/MobKillsRequirement.java b/src/main/java/sh/okx/rankup/requirements/requirement/MobKillsRequirement.java index 77ef457..c3ede7a 100644 --- a/src/main/java/sh/okx/rankup/requirements/requirement/MobKillsRequirement.java +++ b/src/main/java/sh/okx/rankup/requirements/requirement/MobKillsRequirement.java @@ -10,8 +10,8 @@ import java.util.Objects; public class MobKillsRequirement extends ProgressiveRequirement { - public MobKillsRequirement(RankupPlugin plugin) { - super(plugin, "mob-kills", true); + public MobKillsRequirement(RankupPlugin plugin, String name) { + super(plugin, name, true); } protected MobKillsRequirement(Requirement clone) { diff --git a/src/main/java/sh/okx/rankup/requirements/requirement/PermissionRequirement.java b/src/main/java/sh/okx/rankup/requirements/requirement/PermissionRequirement.java index 5546af6..96c2212 100644 --- a/src/main/java/sh/okx/rankup/requirements/requirement/PermissionRequirement.java +++ b/src/main/java/sh/okx/rankup/requirements/requirement/PermissionRequirement.java @@ -5,8 +5,8 @@ import sh.okx.rankup.requirements.Requirement; public class PermissionRequirement extends Requirement { - public PermissionRequirement(RankupPlugin plugin) { - super(plugin, "permission"); + public PermissionRequirement(RankupPlugin plugin, String name) { + super(plugin, name); } protected PermissionRequirement(Requirement clone) { diff --git a/src/main/java/sh/okx/rankup/requirements/requirement/PlaceholderRequirement.java b/src/main/java/sh/okx/rankup/requirements/requirement/PlaceholderRequirement.java index 583833f..aee7455 100644 --- a/src/main/java/sh/okx/rankup/requirements/requirement/PlaceholderRequirement.java +++ b/src/main/java/sh/okx/rankup/requirements/requirement/PlaceholderRequirement.java @@ -10,8 +10,8 @@ public class PlaceholderRequirement extends ProgressiveRequirement { public static final double DELTA = 0.00001D; - public PlaceholderRequirement(RankupPlugin plugin) { - super(plugin, "placeholder"); + public PlaceholderRequirement(RankupPlugin plugin, String name) { + super(plugin, name); } public PlaceholderRequirement(PlaceholderRequirement clone) { diff --git a/src/main/java/sh/okx/rankup/requirements/requirement/PlayerKillsRequirement.java b/src/main/java/sh/okx/rankup/requirements/requirement/PlayerKillsRequirement.java index d53adf7..778df55 100644 --- a/src/main/java/sh/okx/rankup/requirements/requirement/PlayerKillsRequirement.java +++ b/src/main/java/sh/okx/rankup/requirements/requirement/PlayerKillsRequirement.java @@ -7,8 +7,8 @@ import sh.okx.rankup.requirements.Requirement; public class PlayerKillsRequirement extends ProgressiveRequirement { - public PlayerKillsRequirement(RankupPlugin plugin) { - super(plugin, "player-kills"); + public PlayerKillsRequirement(RankupPlugin plugin, String name) { + super(plugin, name); } protected PlayerKillsRequirement(Requirement clone) { diff --git a/src/main/java/sh/okx/rankup/requirements/requirement/PlaytimeMinutesRequirement.java b/src/main/java/sh/okx/rankup/requirements/requirement/PlaytimeMinutesRequirement.java index e802c20..1f74f11 100644 --- a/src/main/java/sh/okx/rankup/requirements/requirement/PlaytimeMinutesRequirement.java +++ b/src/main/java/sh/okx/rankup/requirements/requirement/PlaytimeMinutesRequirement.java @@ -10,8 +10,8 @@ public class PlaytimeMinutesRequirement extends ProgressiveRequirement { private static final int TICKS_PER_MINUTE = 20 * 60; private Statistic playOneTick; - public PlaytimeMinutesRequirement(RankupPlugin plugin) { - super(plugin, "playtime-minutes"); + public PlaytimeMinutesRequirement(RankupPlugin plugin, String name) { + super(plugin, name); try { playOneTick = Statistic.valueOf("PLAY_ONE_MINUTE"); } catch (IllegalArgumentException e) { diff --git a/src/main/java/sh/okx/rankup/requirements/requirement/TotalMobKillsRequirement.java b/src/main/java/sh/okx/rankup/requirements/requirement/TotalMobKillsRequirement.java index 1d634aa..26a29b9 100644 --- a/src/main/java/sh/okx/rankup/requirements/requirement/TotalMobKillsRequirement.java +++ b/src/main/java/sh/okx/rankup/requirements/requirement/TotalMobKillsRequirement.java @@ -7,8 +7,8 @@ import sh.okx.rankup.requirements.Requirement; public class TotalMobKillsRequirement extends ProgressiveRequirement { - public TotalMobKillsRequirement(RankupPlugin plugin) { - super(plugin, "total-mob-kills"); + public TotalMobKillsRequirement(RankupPlugin plugin, String name) { + super(plugin, name); } private TotalMobKillsRequirement(Requirement clone) { diff --git a/src/main/java/sh/okx/rankup/requirements/requirement/UseItemRequirement.java b/src/main/java/sh/okx/rankup/requirements/requirement/UseItemRequirement.java index 54d2c7c..187f039 100644 --- a/src/main/java/sh/okx/rankup/requirements/requirement/UseItemRequirement.java +++ b/src/main/java/sh/okx/rankup/requirements/requirement/UseItemRequirement.java @@ -8,8 +8,8 @@ import sh.okx.rankup.requirements.Requirement; public class UseItemRequirement extends ProgressiveRequirement { - public UseItemRequirement(RankupPlugin plugin) { - super(plugin, "use-item", true); + public UseItemRequirement(RankupPlugin plugin, String name) { + super(plugin, name, true); } protected UseItemRequirement(UseItemRequirement clone) { diff --git a/src/main/java/sh/okx/rankup/requirements/requirement/WorldRequirement.java b/src/main/java/sh/okx/rankup/requirements/requirement/WorldRequirement.java index 8414347..be74d66 100644 --- a/src/main/java/sh/okx/rankup/requirements/requirement/WorldRequirement.java +++ b/src/main/java/sh/okx/rankup/requirements/requirement/WorldRequirement.java @@ -5,8 +5,8 @@ import sh.okx.rankup.requirements.Requirement; public class WorldRequirement extends Requirement { - public WorldRequirement(RankupPlugin plugin) { - super(plugin, "world"); + public WorldRequirement(RankupPlugin plugin, String name) { + super(plugin, name); } protected WorldRequirement(Requirement clone) { diff --git a/src/main/java/sh/okx/rankup/requirements/requirement/advancedachievements/AdvancedAchievementsAchievementRequirement.java b/src/main/java/sh/okx/rankup/requirements/requirement/advancedachievements/AdvancedAchievementsAchievementRequirement.java index 5e7f8bd..5ce97b7 100644 --- a/src/main/java/sh/okx/rankup/requirements/requirement/advancedachievements/AdvancedAchievementsAchievementRequirement.java +++ b/src/main/java/sh/okx/rankup/requirements/requirement/advancedachievements/AdvancedAchievementsAchievementRequirement.java @@ -7,8 +7,8 @@ import sh.okx.rankup.requirements.Requirement; public class AdvancedAchievementsAchievementRequirement extends Requirement { - public AdvancedAchievementsAchievementRequirement(RankupPlugin plugin) { - super(plugin, "advancedachievements-achievement"); + public AdvancedAchievementsAchievementRequirement(RankupPlugin plugin, String name) { + super(plugin, name); } protected AdvancedAchievementsAchievementRequirement(Requirement clone) { diff --git a/src/main/java/sh/okx/rankup/requirements/requirement/advancedachievements/AdvancedAchievementsTotalRequirement.java b/src/main/java/sh/okx/rankup/requirements/requirement/advancedachievements/AdvancedAchievementsTotalRequirement.java index efbc2ea..5f3beb0 100644 --- a/src/main/java/sh/okx/rankup/requirements/requirement/advancedachievements/AdvancedAchievementsTotalRequirement.java +++ b/src/main/java/sh/okx/rankup/requirements/requirement/advancedachievements/AdvancedAchievementsTotalRequirement.java @@ -7,8 +7,8 @@ import sh.okx.rankup.requirements.ProgressiveRequirement; public class AdvancedAchievementsTotalRequirement extends ProgressiveRequirement { - public AdvancedAchievementsTotalRequirement(RankupPlugin plugin) { - super(plugin, "advancedachievements-total"); + public AdvancedAchievementsTotalRequirement(RankupPlugin plugin, String name) { + super(plugin, name); } private AdvancedAchievementsTotalRequirement(Requirement clone) { diff --git a/src/main/java/sh/okx/rankup/requirements/requirement/mcmmo/McMMOPowerLevelRequirement.java b/src/main/java/sh/okx/rankup/requirements/requirement/mcmmo/McMMOPowerLevelRequirement.java index ba752e0..b601668 100644 --- a/src/main/java/sh/okx/rankup/requirements/requirement/mcmmo/McMMOPowerLevelRequirement.java +++ b/src/main/java/sh/okx/rankup/requirements/requirement/mcmmo/McMMOPowerLevelRequirement.java @@ -7,8 +7,8 @@ import sh.okx.rankup.requirements.ProgressiveRequirement; public class McMMOPowerLevelRequirement extends ProgressiveRequirement { - public McMMOPowerLevelRequirement(RankupPlugin plugin) { - super(plugin, "mcmmo-power-level"); + public McMMOPowerLevelRequirement(RankupPlugin plugin, String name) { + super(plugin, name); } protected McMMOPowerLevelRequirement(Requirement clone) { diff --git a/src/main/java/sh/okx/rankup/requirements/requirement/mcmmo/McMMOSkillRequirement.java b/src/main/java/sh/okx/rankup/requirements/requirement/mcmmo/McMMOSkillRequirement.java index fc30d63..9f7224a 100644 --- a/src/main/java/sh/okx/rankup/requirements/requirement/mcmmo/McMMOSkillRequirement.java +++ b/src/main/java/sh/okx/rankup/requirements/requirement/mcmmo/McMMOSkillRequirement.java @@ -6,8 +6,8 @@ import sh.okx.rankup.requirements.ProgressiveRequirement; public class McMMOSkillRequirement extends ProgressiveRequirement { - public McMMOSkillRequirement(RankupPlugin plugin) { - super(plugin, "mcmmo", true); + public McMMOSkillRequirement(RankupPlugin plugin, String name) { + super(plugin, name, true); } protected McMMOSkillRequirement(McMMOSkillRequirement clone) { diff --git a/src/main/java/sh/okx/rankup/requirements/requirement/superbvote/SuperbVoteVotesRequirement.java b/src/main/java/sh/okx/rankup/requirements/requirement/superbvote/SuperbVoteVotesRequirement.java index 47d7253..8d777e9 100644 --- a/src/main/java/sh/okx/rankup/requirements/requirement/superbvote/SuperbVoteVotesRequirement.java +++ b/src/main/java/sh/okx/rankup/requirements/requirement/superbvote/SuperbVoteVotesRequirement.java @@ -7,8 +7,8 @@ import sh.okx.rankup.requirements.Requirement; public class SuperbVoteVotesRequirement extends ProgressiveRequirement { - public SuperbVoteVotesRequirement(RankupPlugin plugin) { - super(plugin, "superbvote-votes"); + public SuperbVoteVotesRequirement(RankupPlugin plugin, String name) { + super(plugin, name); } private SuperbVoteVotesRequirement(Requirement clone) { diff --git a/src/main/java/sh/okx/rankup/requirements/requirement/towny/TownyKingNumberResidentsRequirement.java b/src/main/java/sh/okx/rankup/requirements/requirement/towny/TownyKingNumberResidentsRequirement.java index d6a04c1..f80a9cb 100644 --- a/src/main/java/sh/okx/rankup/requirements/requirement/towny/TownyKingNumberResidentsRequirement.java +++ b/src/main/java/sh/okx/rankup/requirements/requirement/towny/TownyKingNumberResidentsRequirement.java @@ -6,8 +6,8 @@ import sh.okx.rankup.requirements.Requirement; public class TownyKingNumberResidentsRequirement extends ProgressiveRequirement { - public TownyKingNumberResidentsRequirement(RankupPlugin plugin) { - super(plugin, "towny-king-residents"); + public TownyKingNumberResidentsRequirement(RankupPlugin plugin, String name) { + super(plugin, name); } protected TownyKingNumberResidentsRequirement(Requirement clone) { diff --git a/src/main/java/sh/okx/rankup/requirements/requirement/towny/TownyKingNumberTownsRequirement.java b/src/main/java/sh/okx/rankup/requirements/requirement/towny/TownyKingNumberTownsRequirement.java index e31c173..d4290f0 100644 --- a/src/main/java/sh/okx/rankup/requirements/requirement/towny/TownyKingNumberTownsRequirement.java +++ b/src/main/java/sh/okx/rankup/requirements/requirement/towny/TownyKingNumberTownsRequirement.java @@ -6,8 +6,8 @@ import sh.okx.rankup.requirements.Requirement; public class TownyKingNumberTownsRequirement extends ProgressiveRequirement { - public TownyKingNumberTownsRequirement(RankupPlugin plugin) { - super(plugin, "towny-king-towns"); + public TownyKingNumberTownsRequirement(RankupPlugin plugin, String name) { + super(plugin, name); } protected TownyKingNumberTownsRequirement(TownyKingNumberTownsRequirement clone) { diff --git a/src/main/java/sh/okx/rankup/requirements/requirement/towny/TownyKingRequirement.java b/src/main/java/sh/okx/rankup/requirements/requirement/towny/TownyKingRequirement.java index 4161433..b0977d5 100644 --- a/src/main/java/sh/okx/rankup/requirements/requirement/towny/TownyKingRequirement.java +++ b/src/main/java/sh/okx/rankup/requirements/requirement/towny/TownyKingRequirement.java @@ -5,8 +5,8 @@ import sh.okx.rankup.requirements.Requirement; public class TownyKingRequirement extends Requirement { - public TownyKingRequirement(RankupPlugin plugin) { - super(plugin, "towny-king"); + public TownyKingRequirement(RankupPlugin plugin, String name) { + super(plugin, name); } protected TownyKingRequirement(Requirement clone) { diff --git a/src/main/java/sh/okx/rankup/requirements/requirement/towny/TownyMayorNumberResidentsRequirement.java b/src/main/java/sh/okx/rankup/requirements/requirement/towny/TownyMayorNumberResidentsRequirement.java index dcf25de..0683dc0 100644 --- a/src/main/java/sh/okx/rankup/requirements/requirement/towny/TownyMayorNumberResidentsRequirement.java +++ b/src/main/java/sh/okx/rankup/requirements/requirement/towny/TownyMayorNumberResidentsRequirement.java @@ -6,8 +6,8 @@ import sh.okx.rankup.requirements.Requirement; public class TownyMayorNumberResidentsRequirement extends ProgressiveRequirement { - public TownyMayorNumberResidentsRequirement(RankupPlugin plugin) { - super(plugin, "towny-mayor-residents"); + public TownyMayorNumberResidentsRequirement(RankupPlugin plugin, String name) { + super(plugin, name); } protected TownyMayorNumberResidentsRequirement(Requirement clone) { diff --git a/src/main/java/sh/okx/rankup/requirements/requirement/towny/TownyMayorRequirement.java b/src/main/java/sh/okx/rankup/requirements/requirement/towny/TownyMayorRequirement.java index 722ee95..f3cf71a 100644 --- a/src/main/java/sh/okx/rankup/requirements/requirement/towny/TownyMayorRequirement.java +++ b/src/main/java/sh/okx/rankup/requirements/requirement/towny/TownyMayorRequirement.java @@ -5,8 +5,8 @@ import sh.okx.rankup.requirements.Requirement; public class TownyMayorRequirement extends Requirement { - public TownyMayorRequirement(RankupPlugin plugin) { - super(plugin, "towny-mayor"); + public TownyMayorRequirement(RankupPlugin plugin, String name) { + super(plugin, name); } protected TownyMayorRequirement(Requirement clone) { diff --git a/src/main/java/sh/okx/rankup/requirements/requirement/towny/TownyResidentRequirement.java b/src/main/java/sh/okx/rankup/requirements/requirement/towny/TownyResidentRequirement.java index f470a5c..1efa240 100644 --- a/src/main/java/sh/okx/rankup/requirements/requirement/towny/TownyResidentRequirement.java +++ b/src/main/java/sh/okx/rankup/requirements/requirement/towny/TownyResidentRequirement.java @@ -5,8 +5,8 @@ import sh.okx.rankup.requirements.Requirement; public class TownyResidentRequirement extends Requirement { - public TownyResidentRequirement(RankupPlugin plugin) { - super(plugin, "towny-resident"); + public TownyResidentRequirement(RankupPlugin plugin, String name) { + super(plugin, name); } protected TownyResidentRequirement(Requirement clone) { diff --git a/src/main/java/sh/okx/rankup/requirements/requirement/votingplugin/VotingPluginVotesRequirement.java b/src/main/java/sh/okx/rankup/requirements/requirement/votingplugin/VotingPluginVotesRequirement.java index be691f0..32b3fd9 100644 --- a/src/main/java/sh/okx/rankup/requirements/requirement/votingplugin/VotingPluginVotesRequirement.java +++ b/src/main/java/sh/okx/rankup/requirements/requirement/votingplugin/VotingPluginVotesRequirement.java @@ -8,8 +8,8 @@ import sh.okx.rankup.requirements.Requirement; public class VotingPluginVotesRequirement extends ProgressiveRequirement { - public VotingPluginVotesRequirement(RankupPlugin plugin) { - super(plugin, "votingplugin-votes"); + public VotingPluginVotesRequirement(RankupPlugin plugin, String name) { + super(plugin, name); } protected VotingPluginVotesRequirement(Requirement clone) { From d800cc903d1cea3e57daa81462fd464b35edba09 Mon Sep 17 00:00:00 2001 From: NicBOMB <19939315+NicBOMB@users.noreply.github.com> Date: Mon, 7 Feb 2022 17:02:33 -0800 Subject: [PATCH 08/27] trivialize enabling locales in the future thinking like an array programmer the Getter also enables other plugins/spigot to ask if Rankup supports a specific locale --- src/main/java/sh/okx/rankup/RankupPlugin.java | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/main/java/sh/okx/rankup/RankupPlugin.java b/src/main/java/sh/okx/rankup/RankupPlugin.java index 9d0d579..e9bacb7 100644 --- a/src/main/java/sh/okx/rankup/RankupPlugin.java +++ b/src/main/java/sh/okx/rankup/RankupPlugin.java @@ -115,6 +115,8 @@ public class RankupPlugin extends JavaPlugin { @Getter private RankupHelper helper; protected AutoRankup autoRankup = new AutoRankup(this); + @Getter + private String[] locales = {"en","pt_br","ru","zh_cn","fr","it","es","nl"}; private String errorMessage; private PermissionManager permissionManager = new VaultPermissionManager(this); private EconomyProvider economyProvider = new VaultEconomyProvider(); @@ -295,7 +297,7 @@ private void closeInventories() { private void loadConfigs(boolean init) { saveLocales(); - String locale = config.getString("locale", "en"); + String locale = config.getString("locale", locales[0]); File localeFile = new File(new File(getDataFolder(), "locale"), locale + ".yml"); messages = YamlConfiguration.loadConfiguration(localeFile); @@ -334,14 +336,7 @@ public void refreshRanks() { } private void saveLocales() { - saveLocale("en"); - saveLocale("pt_br"); - saveLocale("ru"); - saveLocale("zh_cn"); - saveLocale("fr"); - saveLocale("it"); - saveLocale("es"); - saveLocale("nl"); + for (String locale : locales){saveLocale(locale);} } private void saveLocale(String locale) { From 04a47fc301c8d16101c374adb04a75cb738583f9 Mon Sep 17 00:00:00 2001 From: NicBOMB <19939315+NicBOMB@users.noreply.github.com> Date: Wed, 26 Oct 2022 15:55:22 -0700 Subject: [PATCH 09/27] printed name fix and cleaner code spacing --- src/main/java/sh/okx/rankup/RankupPlugin.java | 12 +++++++++--- .../rankup/messages/pebble/RequirementContext.java | 2 +- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/main/java/sh/okx/rankup/RankupPlugin.java b/src/main/java/sh/okx/rankup/RankupPlugin.java index e9bacb7..1e9554e 100644 --- a/src/main/java/sh/okx/rankup/RankupPlugin.java +++ b/src/main/java/sh/okx/rankup/RankupPlugin.java @@ -180,7 +180,13 @@ public void onEnable() { getCommand("rankup3").setExecutor(new InfoCommand(this, notifier)); getServer().getPluginManager().registerEvents(new GuiListener(this), this); getServer().getPluginManager().registerEvents( - new JoinUpdateNotifier(notifier, () -> getConfig().getBoolean("notify-update"), "rankup.notify"), this); + new JoinUpdateNotifier( + notifier, + () -> getConfig().getBoolean("notify-update"), + "rankup.notify" + ), + this + ); placeholders = new Placeholders(this); placeholders.register(); @@ -262,12 +268,12 @@ public boolean error(CommandSender sender) { sender.sendMessage( ChatColor.RED + "Could not load Rankup, check console for more information."); } else { - getLogger().severe("Failed to load Rankup"); + getLogger().severe("Rankup failed to load!"); } for (String line : errorMessage.split("\n")) { getLogger().severe(line); } - getLogger().severe("More information can be found in the console log at startup"); + getLogger().severe("More information can be found in the console log at startup."); return true; } diff --git a/src/main/java/sh/okx/rankup/messages/pebble/RequirementContext.java b/src/main/java/sh/okx/rankup/messages/pebble/RequirementContext.java index db7160b..525f979 100644 --- a/src/main/java/sh/okx/rankup/messages/pebble/RequirementContext.java +++ b/src/main/java/sh/okx/rankup/messages/pebble/RequirementContext.java @@ -46,6 +46,6 @@ public double getPercent() { } public String toString() { - return "Requirement[" + requirement.getFullName() + "]"; + return "Requirements[" + requirement.getFullName() + "]"; } } From 7462dc167af56c85716abe81db4eab3cfe094cad Mon Sep 17 00:00:00 2001 From: NicBOMB <19939315+NicBOMB@users.noreply.github.com> Date: Tue, 8 Feb 2022 14:21:27 -0800 Subject: [PATCH 10/27] test configs separately from loading --- src/main/java/sh/okx/rankup/RankupPlugin.java | 32 ++++++++++--------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/src/main/java/sh/okx/rankup/RankupPlugin.java b/src/main/java/sh/okx/rankup/RankupPlugin.java index 1e9554e..f1b4959 100644 --- a/src/main/java/sh/okx/rankup/RankupPlugin.java +++ b/src/main/java/sh/okx/rankup/RankupPlugin.java @@ -216,9 +216,9 @@ public void reload(boolean init) { } setupEconomy(); - closeInventories(); - loadConfigs(init); + loadConfigs(); + testConfigs(init); long time = (long) (config.getDouble("autorankup-interval") * 60 * 20); if (time > 0) { @@ -300,13 +300,15 @@ private void closeInventories() { } } - private void loadConfigs(boolean init) { + private void loadConfigs() { saveLocales(); String locale = config.getString("locale", locales[0]); File localeFile = new File(new File(getDataFolder(), "locale"), locale + ".yml"); messages = YamlConfiguration.loadConfiguration(localeFile); + } + private void testConfigs(boolean init){ if (init) { Bukkit.getScheduler().runTask(this, () -> { refreshRanks(); @@ -317,6 +319,18 @@ private void loadConfigs(boolean init) { } } + private void saveLocales() { + for (String locale : locales){saveLocale(locale);} + } + + private void saveLocale(String locale) { + String name = "locale/" + locale + ".yml"; + File file = new File(getDataFolder(), name); + if (!file.exists()) { + saveResource(name, false); + } + } + public void refreshRanks() { try { registerRequirements(); @@ -341,18 +355,6 @@ public void refreshRanks() { } } - private void saveLocales() { - for (String locale : locales){saveLocale(locale);} - } - - private void saveLocale(String locale) { - String name = "locale/" + locale + ".yml"; - File file = new File(getDataFolder(), name); - if (!file.exists()) { - saveResource(name, false); - } - } - private List loadRankupConfig(String name) { File ymlFile = new File(getDataFolder(), name + ".yml"); File tomlFile = new File(getDataFolder(), name + ".toml"); From 1daa1a2a59057bd68bc5daa29d7a5e7595f8d2c0 Mon Sep 17 00:00:00 2001 From: NicBOMB <19939315+NicBOMB@users.noreply.github.com> Date: Wed, 9 Feb 2022 12:01:09 -0800 Subject: [PATCH 11/27] remove unnecessary loop and add an else case to stop once adv is found No reason to add more visual complexity when we should simply ask for `getValueString()` instead of `getValuesString()`. --- .../requirement/AdvancementRequirement.java | 27 +++++++++---------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/src/main/java/sh/okx/rankup/requirements/requirement/AdvancementRequirement.java b/src/main/java/sh/okx/rankup/requirements/requirement/AdvancementRequirement.java index 52128e1..397d8e8 100644 --- a/src/main/java/sh/okx/rankup/requirements/requirement/AdvancementRequirement.java +++ b/src/main/java/sh/okx/rankup/requirements/requirement/AdvancementRequirement.java @@ -25,22 +25,19 @@ protected AdvancementRequirement(Requirement clone) { @Override public boolean check(Player player) { - for (String string : getValuesString()) { - Iterator advancementIterator = Bukkit.advancementIterator(); - while (advancementIterator.hasNext()) { - Advancement adv = advancementIterator.next(); - String key = adv.getKey().getKey(); - Pattern pattern = Pattern.compile(string.replace("*", ".*").replace("-", "")); - if (pattern.matcher(key).find()) { - boolean positive = !string.startsWith("-"); - - AdvancementProgress progress = player.getAdvancementProgress(adv); - if (progress.isDone() == positive) { - return true; - } + String string = getValueString(); + Iterator advancementIterator = Bukkit.advancementIterator(); + while (advancementIterator.hasNext()) { + Advancement adv = advancementIterator.next(); + Pattern pattern = Pattern.compile(string.replace("*", ".*").replace("-", "")); + if (pattern.matcher(adv.getKey().getKey()).find()) { + boolean positive = !string.startsWith("-"); + AdvancementProgress progress = player.getAdvancementProgress(adv); + if (progress.isDone() == positive) { + return true; + } else { + return false; } - - } } return false; From 3b03645656c147ff5e9a50c86eda42bdce1aba19 Mon Sep 17 00:00:00 2001 From: NicBOMB <19939315+NicBOMB@users.noreply.github.com> Date: Wed, 26 Oct 2022 16:01:04 -0700 Subject: [PATCH 12/27] override getSub for advancement, advancedachievements, and placeholder --- .../requirements/requirement/AdvancementRequirement.java | 5 +++++ .../requirements/requirement/PlaceholderRequirement.java | 7 ++++++- .../AdvancedAchievementsAchievementRequirement.java | 5 +++++ 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/main/java/sh/okx/rankup/requirements/requirement/AdvancementRequirement.java b/src/main/java/sh/okx/rankup/requirements/requirement/AdvancementRequirement.java index 397d8e8..f7f6bc5 100644 --- a/src/main/java/sh/okx/rankup/requirements/requirement/AdvancementRequirement.java +++ b/src/main/java/sh/okx/rankup/requirements/requirement/AdvancementRequirement.java @@ -52,4 +52,9 @@ public Requirement clone() { public double getValueDouble() { return 1; } + + @Override + public String getSub(){ + return getValueString(); + } } diff --git a/src/main/java/sh/okx/rankup/requirements/requirement/PlaceholderRequirement.java b/src/main/java/sh/okx/rankup/requirements/requirement/PlaceholderRequirement.java index aee7455..42728ae 100644 --- a/src/main/java/sh/okx/rankup/requirements/requirement/PlaceholderRequirement.java +++ b/src/main/java/sh/okx/rankup/requirements/requirement/PlaceholderRequirement.java @@ -78,8 +78,13 @@ private String[] getParts(Player player) { @Override public String getFullName() { + return name + "#" + getSub(); + } + + @Override + public String getSub() { String[] parts = getValueString().split(" "); - return name + "#" + parts[0].replace("%", ""); + return parts[0].replace("%", ""); } @Override diff --git a/src/main/java/sh/okx/rankup/requirements/requirement/advancedachievements/AdvancedAchievementsAchievementRequirement.java b/src/main/java/sh/okx/rankup/requirements/requirement/advancedachievements/AdvancedAchievementsAchievementRequirement.java index 5ce97b7..134b709 100644 --- a/src/main/java/sh/okx/rankup/requirements/requirement/advancedachievements/AdvancedAchievementsAchievementRequirement.java +++ b/src/main/java/sh/okx/rankup/requirements/requirement/advancedachievements/AdvancedAchievementsAchievementRequirement.java @@ -26,6 +26,11 @@ public String getFullName() { return super.getFullName() + "#" + getValueString(); } + @Override + public String getSub() { + return getValueString(); + } + @Override public double getTotal(Player player) { return 1; From 43a98006ae55260e153a747d28d4f5595f6b3cf1 Mon Sep 17 00:00:00 2001 From: NicBOMB <19939315+NicBOMB@users.noreply.github.com> Date: Tue, 15 Feb 2022 15:50:05 -0800 Subject: [PATCH 13/27] Merge remote-tracking branch 'origin/master' into new-rankups.yml --- build.gradle | 8 +++-- .../messages/pebble/PebbleMessageBuilder.java | 2 +- .../rankup/messages/pebble/RankContext.java | 8 +++++ .../messages/pebble/RequirementContext.java | 3 +- .../okx/rankup/text/TextProcessorBuilder.java | 5 +-- .../text/pebble/PebbleTextProcessor.java | 7 ++-- .../java/sh/okx/rankup/pebble/PebbleTest.java | 32 ++++++++++++++++++- 7 files changed, 53 insertions(+), 12 deletions(-) diff --git a/build.gradle b/build.gradle index 4dde130..d31f4b0 100644 --- a/build.gradle +++ b/build.gradle @@ -24,7 +24,7 @@ java { repositories { mavenCentral() maven { - url 'https://hub.spigotmc.org/nexus/content/groups/public/' + url 'https://hub.spigotmc.org/nexus/content/repositories/snapshots/' } maven { url 'https://repo.extendedclip.com/content/repositories/placeholderapi/' @@ -57,12 +57,14 @@ dependencies { compileOnly ('me.clip:placeholderapi:2.10.9') { exclude group: 'org.bstats' } - compileOnly 'com.github.pyvesb:advanced-achievements:6.7.2' + compileOnly 'com.github.pyvesb:advanced-achievements:6.7.2' compileOnly 'com.github.astei:Superbvote:700fca43659b438cb9bb36c218a7646d2f2ef315' compileOnly('com.github.mcMMO-Dev:mcMMO:601297') { exclude group: 'com.sk89q.worldguard' } - compileOnly 'com.github.BenCodez:VotingPlugin:6.8.1' + compileOnly ('com.github.BenCodez:VotingPlugin:6.8.1') { + transitive = false + } compileOnly 'com.github.LlmDl:Towny:25fc18a' testImplementation 'com.github.LlmDl:Towny:25fc18a' diff --git a/src/main/java/sh/okx/rankup/messages/pebble/PebbleMessageBuilder.java b/src/main/java/sh/okx/rankup/messages/pebble/PebbleMessageBuilder.java index 0110c42..857367a 100644 --- a/src/main/java/sh/okx/rankup/messages/pebble/PebbleMessageBuilder.java +++ b/src/main/java/sh/okx/rankup/messages/pebble/PebbleMessageBuilder.java @@ -111,7 +111,7 @@ private TextProcessor processor(Player player) { return new TextProcessorBuilder() .legacy(context, plugin.getPlaceholders()) .papi(player) - .pebble(context, plugin.getPlaceholders()) + .pebble(plugin.getLogger(), context, plugin.getPlaceholders()) .papi(player) .colour() .create(); diff --git a/src/main/java/sh/okx/rankup/messages/pebble/RankContext.java b/src/main/java/sh/okx/rankup/messages/pebble/RankContext.java index 8589647..d5499a2 100644 --- a/src/main/java/sh/okx/rankup/messages/pebble/RankContext.java +++ b/src/main/java/sh/okx/rankup/messages/pebble/RankContext.java @@ -43,6 +43,14 @@ public RequirementContext getReq(String requirement) { return getRequirement(requirement); } + public boolean getHas(String requirement) { + return rank.getRequirement(player, requirement) != null; + } + + public boolean getHas(String requirement, String sub) { + return rank.getRequirement(player, requirement + "#" + sub) != null; + } + public RequirementContext getReq(String requirement, String sub) { return getRequirement(requirement, sub); } diff --git a/src/main/java/sh/okx/rankup/messages/pebble/RequirementContext.java b/src/main/java/sh/okx/rankup/messages/pebble/RequirementContext.java index 525f979..18c2760 100644 --- a/src/main/java/sh/okx/rankup/messages/pebble/RequirementContext.java +++ b/src/main/java/sh/okx/rankup/messages/pebble/RequirementContext.java @@ -38,7 +38,8 @@ public String getSub(){ } public double getQuotient() { - return (getTotal() > 0 ? (getProgress() / getTotal()) : 1); + double total = getTotal(); + return total == 0 ? 1 : getProgress() / total; } public double getPercent() { diff --git a/src/main/java/sh/okx/rankup/text/TextProcessorBuilder.java b/src/main/java/sh/okx/rankup/text/TextProcessorBuilder.java index 1baa96d..be8e5fc 100644 --- a/src/main/java/sh/okx/rankup/text/TextProcessorBuilder.java +++ b/src/main/java/sh/okx/rankup/text/TextProcessorBuilder.java @@ -3,6 +3,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.logging.Logger; import org.bukkit.entity.Player; import org.jetbrains.annotations.Nullable; import sh.okx.rankup.placeholders.Placeholders; @@ -17,8 +18,8 @@ public TextProcessorBuilder colour() { return this; } - public TextProcessorBuilder pebble(Map context, Placeholders options) { - processors.add(new PebbleTextProcessor(context, options)); + public TextProcessorBuilder pebble(Logger logger, Map context, Placeholders options) { + processors.add(new PebbleTextProcessor(logger, context, options)); return this; } diff --git a/src/main/java/sh/okx/rankup/text/pebble/PebbleTextProcessor.java b/src/main/java/sh/okx/rankup/text/pebble/PebbleTextProcessor.java index 2a2878c..adb88ad 100644 --- a/src/main/java/sh/okx/rankup/text/pebble/PebbleTextProcessor.java +++ b/src/main/java/sh/okx/rankup/text/pebble/PebbleTextProcessor.java @@ -11,18 +11,18 @@ import java.util.HashMap; import java.util.Map; import java.util.logging.Logger; -import org.bukkit.plugin.java.JavaPlugin; -import sh.okx.rankup.RankupPlugin; import sh.okx.rankup.messages.pebble.InvalidRequirementException; import sh.okx.rankup.placeholders.Placeholders; import sh.okx.rankup.text.TextProcessor; public class PebbleTextProcessor implements TextProcessor { + private final Logger logger; private final Map context; private final Placeholders options; - public PebbleTextProcessor(Map context, Placeholders options) { + public PebbleTextProcessor(Logger logger, Map context, Placeholders options) { + this.logger = logger; this.context = context; this.options = options; } @@ -60,7 +60,6 @@ public Map getFilters() { if (ex.getCause() instanceof InvocationTargetException) { if (ex.getCause().getCause() instanceof InvalidRequirementException) { InvalidRequirementException cause = (InvalidRequirementException) ex.getCause().getCause(); - Logger logger = JavaPlugin.getPlugin(RankupPlugin.class).getLogger(); logger.severe("Unknown requirement \"" + cause.getRequirement() + "\" on rank \"" + cause.getRank().getRank() + "\" in message:"); for (String line : string.split("\n")) { logger.severe(line); diff --git a/src/test/java/sh/okx/rankup/pebble/PebbleTest.java b/src/test/java/sh/okx/rankup/pebble/PebbleTest.java index e0d3fde..469410f 100644 --- a/src/test/java/sh/okx/rankup/pebble/PebbleTest.java +++ b/src/test/java/sh/okx/rankup/pebble/PebbleTest.java @@ -24,7 +24,7 @@ public void testIndex() { list.add("L2"); list.add("L3"); ctx.put("list", list); - PebbleTextProcessor processor = new PebbleTextProcessor(ctx, null); + PebbleTextProcessor processor = new PebbleTextProcessor(null, ctx, null); assertEquals("L2", processor.process("{{ list[one] }}")); } @@ -42,4 +42,34 @@ public void testIterable() { player.assertSaid("true"); } + + @Test + public void testRequirementAbsent() { + PlayerMock player = server.addPlayer(); + + plugin.getPermissions().addGroup(player.getUniqueId(), "B"); + RankElement rankElement = plugin.getRankups().getByPlayer(player); + + plugin.newMessageBuilder("{{ rank.has('xp-level') ? rank.req('xp-level').total | simple : 'none' }}") + .replacePlayer(player) + .replaceOldRank(rankElement.getRank()) + .send(player); + + player.assertSaid("none"); + } + + @Test + public void testRequirementPresent() { + PlayerMock player = server.addPlayer(); + + plugin.getPermissions().addGroup(player.getUniqueId(), "C"); + RankElement rankElement = plugin.getRankups().getByPlayer(player); + + plugin.newMessageBuilder("{{ rank.has('xp-level') ? rank.req('xp-level').total | simple : 'none' }}") + .replacePlayer(player) + .replaceOldRank(rankElement.getRank()) + .send(player); + + player.assertSaid("2"); + } } From b1bb10ad3d743a440a496633a70c91b2047b86c2 Mon Sep 17 00:00:00 2001 From: NicBOMB <19939315+NicBOMB@users.noreply.github.com> Date: Sat, 16 Apr 2022 21:07:16 -0700 Subject: [PATCH 14/27] Compilation Instructions for Windows and Linux credit to @Voltmage for being a guinea pig --- README.md | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ee0cced..73bf4ba 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,37 @@ # Rankup3 +## Compiling +### Prerequisites +Compilation requires the jdk8 (java development kit). +#### Windows +The [Eclipse Foundation](https://www.eclipse.org/org/) releases [the required build tools here](https://adoptium.net/temurin/releases). +It's recommended to select an installer download for simplicity and/or compiling other plugins. Don't select a zip archive. +You can easily clone this repo using the [github desktop app](https://desktop.github.com/). +1. Copy the link provided in the above green `Code` button. +2. In the github desktop app, Select `Clone a repository from the Internet` or `Clone repository...`, then select URL, and paste into the url line. +3. Open the Rankup3 directory you downloaded. The default location is in your user's `\Documents\GitHub\Rankup3` folder. +4. Open a command prompt in the directory and run [`dir`](https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/dir) to catalog the directory. +5. When you see the `gradlew` file listed, you can now try the [build command](#building). -## Installation +#### Linux +Add `jdk8` or `jdk8-openjdk` from your package manager. +Add `git` (required) and/or `gh` (optional, for github integration) from your package manager. +After installing packages, clone the repository by running `git clone` on the url provided above in the Code button, or the provided `gh` cloning command, from your shell. +Then try the [build command](#building). -Compilation requires the jre8 (java runtime environment) and jdk8 (java development kit). Rankup can be compiled with the `gradlew build` command. The compiled plugin jar will be in the `/build/libs` directory. A gradle wrapper is included, so installation of Gradle is not necessary. +### Building +In the Rankup3 directory, a jar can be compiled with the provided `gradlew` wrapper: +- Command Prompt: `gradlew build` +- Unix/Powershell/Other: `./gradlew build` + +#### Known Build Errors +##### `Unsupported class file major version #` +This error indicates you are building with the wrong version of the JDK. +Solution: +- Windows: Correct your `Environment Variables` for `JAVA_HOME` to point to the installation location of the JDK 8. `Environment Variables` is program searchable in the start menu that will prompt you with a list of filepaths to your jvm installations in the `JAVA_HOME` variable. The JDK 8's filepath should be moved to the top of the list before [building](#building). +- Linux: Your package manager may natively support installing multiple JDK versions simultaneously. Consult your package manger's docs for information about managing your jvm's. Here are some examples: [`sudo update-alternatives --config java`](https://help.ubuntu.com/community/Java#Choosing_the_default_Java_to_use) or [`sudo archlinux-java set java-8-openjdk`](https://wiki.archlinux.org/title/Java#Switching_between_JVM). The JDK 8 should be selected before [building](#building). +### Jars +The compiled plugin jar will be in the `/build/libs` directory. +All branches are using the same jar name. Rename your build artifacts. ## Translation From 709cddf51179a59bca7a87e3c8c469f9718c29c8 Mon Sep 17 00:00:00 2001 From: NicBOMB <19939315+NicBOMB@users.noreply.github.com> Date: Mon, 13 Jun 2022 08:05:20 -0700 Subject: [PATCH 15/27] Merge pull request #74 from okx-code/master bump version --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index d31f4b0..d1d5ed1 100644 --- a/build.gradle +++ b/build.gradle @@ -14,7 +14,7 @@ pitest { } group 'sh.okx' -version '3.13.2' +version '3.13.3' java { sourceCompatibility = JavaVersion.VERSION_1_8 From 804fd306f0eef022550064a70400ee4784fa0c33 Mon Sep 17 00:00:00 2001 From: flurbudurbur Date: Mon, 13 Jun 2022 18:11:55 +0200 Subject: [PATCH 16/27] A more enjoyable `rankups.yml`. --- src/main/resources/rankups.yml | 87 ++++++++++++++----- .../java/sh/okx/rankup/RankupBasicsTest.java | 2 +- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/main/resources/rankups.yml b/src/main/resources/rankups.yml index 1eb94fb..e9008c6 100644 --- a/src/main/resources/rankups.yml +++ b/src/main/resources/rankups.yml @@ -5,40 +5,81 @@ # Join the discord server for live support: https://discord.gg/maB4382 (buyers only) # -# this name doesn't matter -Aexample: - # the name of the group - # players have to be in this rank to rankup +# This name must be unique +# but can be anything you like. +ExampleA: + + # The name of the group players + # have to be in order to rank up. + # + # Prioritize displaynames! rank: 'A' - # the name of the rank a player can rankup to + + # The name of the group players + # will rank up to next. + # + # Prioritize displaynames! next: 'B' - # List of requirements to go to the next rank - # This example will charge 1000 money to rankup from A to B. - # https://okx.sh/rankup/List-of-Requirements.html - # custom requirements can also be added by other plugins. + + # List of requirements to go to the next rank. + # This example will charge 1000 money to rank up from A to B. + + # Learn more https://okx.sh/rankup/List-of-Requirements.html requirements: - 'money 1000' - # the console will run these commands when a player ranks up - # nb: groups are automatically changed with vault - #commands: - # this will run when a player ranks up from A to B. - #- 'say {{player}} well done for ranking up from {{rank.rank}} to {{next.rank}}!' -Bexample: + +ExampleB: rank: 'B' next: 'C' + + # This message will be sent + # in the server chat. + success-public: "&a{{player}} &ehas ranked up to: &d{{next.rank}}" requirements: - 'money 2500' -Cexample: + +ExampleC: rank: 'C' next: 'D' requirements: - 'money 5000' - 'xp-level 2' - # you can have a custom messages for each rank - # the paths of these messages are the same as in the messages for your locale + + # You can have a custom messages for each rank! + # the paths of these messages are the same as in the messages for your locale. rankup: - requirements-not-met: '&cYou need 5000 money and 2 levels of XP to rankup to D.' - list: - complete: "&7{{rank.rank}} &8\xbb &7{{next.rank}} &e(5000 money, 2 XP levels)" - current: "&c{{rank.rank}} &e\xbb &c{{next.rank}} &e(5000 money, 2 XP levels)" - incomplete: "&r{{rank.rank}} &e\xbb &r{{next.rank}} &e(5000 money, 2 XP levels)" \ No newline at end of file + + # The following message contains a placeholder. + + # Quick breakdown: + # - rank.req = specifies the rank. + # - money = specifies the requirement. + # - total = specifies what you want from the placeholder. + # - simple = formats the placeholder. + # Learn more: https://okx.sh/rankup/Text-Templating.html + + requirements-not-met: "&cYou need {{ rank.req('money').total | simple }} money and {{ rank.req('xp-level').total | simple }} levels of XP to rank up to {{next.rank}}." + +ExampleD: + rank: 'D' + next: 'E' + requirements: + - 'money 10000' + - 'xp-level 5' + rankup: + + # Small intro to multi-line syntax + # -> Notice the | after requirements-not-met. + # It means it's a multi-line message. + # + # -> The actual message is on the following + # line indented by 2 spaces. + # + # -> Notice that the message doesn't + # double quotes ("") like the others. + + requirements-not-met: | + &cYou need: + &c- {{ rank.req('money').total | simple }} money + &c- {{ rank.req('xp-level').total | simple }} XP levels + &cTo rank up to {{next.rank}}! \ No newline at end of file diff --git a/src/test/java/sh/okx/rankup/RankupBasicsTest.java b/src/test/java/sh/okx/rankup/RankupBasicsTest.java index 78ed6d8..1296143 100644 --- a/src/test/java/sh/okx/rankup/RankupBasicsTest.java +++ b/src/test/java/sh/okx/rankup/RankupBasicsTest.java @@ -41,7 +41,7 @@ public void testNotInLadder() { public void testLastRank() { PlayerMock player = server.addPlayer(); - groupProvider.addGroup(player.getUniqueId(), "D"); + groupProvider.addGroup(player.getUniqueId(), "E"); plugin.getHelper().rankup(player); From 29848d3dd602c448c79b52a7567841b9ec10c9d3 Mon Sep 17 00:00:00 2001 From: flurbudurbur Date: Mon, 13 Jun 2022 18:12:59 +0200 Subject: [PATCH 17/27] Merge remote-tracking branch 'origin/new-rankups.yml' into new-rankups.yml --- gradle/wrapper/gradle-wrapper.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 69a9715..aa991fc 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists From 8650e80fe28b72ba13ecdd869a3c3ff1a95abbfa Mon Sep 17 00:00:00 2001 From: flurbudurbur Date: Mon, 13 Jun 2022 21:36:58 +0200 Subject: [PATCH 18/27] A more enjoyable `en.yml`. --- src/main/resources/locale/en.yml | 82 ++++++++++++++++++-------------- 1 file changed, 46 insertions(+), 36 deletions(-) diff --git a/src/main/resources/locale/en.yml b/src/main/resources/locale/en.yml index b7b781b..777e62a 100644 --- a/src/main/resources/locale/en.yml +++ b/src/main/resources/locale/en.yml @@ -1,38 +1,49 @@ -# the messages in this section can be customised for each rankup in rankups.yml. +# The messages in this section can be customised for each rankup in rankups.yml. rankup: requirements-not-met: "&cYou need {{rank.requirement('money').total | money}} money to rankup." - no-rankup: "&eYou are at the highest rank." - # set to an empty string, ie: success-public: "" + + # Set to an empty string, ie: success-public: "" # to hide that message. success-public: "&a{{player}} &ehas ranked up to: &d{{next.rank}}" success-private: "&aYou have ranked up to: &d{{next.rank}}" + + must-prestige: "&cYou must prestige to /rankup further!" + no-rankup: "&eYou are at the highest rank." + # used for the text confirmation confirmation: |- &eAre you sure you want to rankup to &a{{next.rank}}&e? &eType &c/rankup &eagain to confirm. - must-prestige: "&cYou must prestige to /rankup further!" gui: rows: 1 title: "Rankup to {{next.rank}}" rankup: - material: EMERALD_BLOCK - # index can be separated by spaces to show in multiple groups - # for example: 0-3 9-12 18-21 - # you can also just use a single number instead of a range. + + # ===[ IMPORTANT PRE-FLATTENING INFO ]=== + # If you are using a 1.8-1.12 and you want to change this + # you can use MATERIAL:data, for example STAINED_GLASS_PANE:8 + # this works for both the rankup, cancel and fill blocks as well. + + material: GREEN_STAINED_GLASS_PANE + + # Index can be separated by spaces to show in multiple groups. + # -> For example: 0-3 9-12 18-21 + # But the "rows" must have enough space. + # You can also just use a single number instead of a range. + index: 0-3 - name: '&a&lConfirm' - # lore is optional - lore: '&6Rankup to &b{{next.rank}}' + name: "&a&lConfirm" + + # Lore is optional + lore: "&6Rankup to &b{{next.rank}}" cancel: - material: REDSTONE_BLOCK + material: RED_STAINED_GLASS_PANE index: 5-8 - name: '&c&lCancel' + name: "&c&lCancel" + lore: "&4Stay at &c{{rank.rank}}" fill: name: ' ' - # if you are using a 1.8-1.12 and you want to change this - # you can use MATERIAL:data, for example STAINED_GLASS_PANE:8 - # this works for both the rankup and cancel blocks as well material: BLACK_STAINED_GLASS_PANE ranksgui: @@ -42,39 +53,34 @@ rankup: width: 7 complete: material: GREEN_STAINED_GLASS_PANE - name: "&aRank &7{{next.rank}} &a(completed)" + name: "&aRank &7{{next.rank}}" + # Lore is optional. + lore: "&aCompleted" current: material: ORANGE_STAINED_GLASS_PANE name: "&dRankup to &7{{next.rank}}" + lore: "&cClick me to /rankup!" incomplete: material: RED_STAINED_GLASS_PANE - name: "&cRank &7{{next.rank}} &c(requires rankup)" + name: "&cRank &7{{next.rank}}" + lore: "&cRequires rankup" fill: material: BLACK_STAINED_GLASS_PANE name: ' ' - - # you can (and probably should) you override these in rankups.yml - # to show the specific requirements for each rank. - # however if you are just using money or don't need to change the message per rank, you can use any combination of: - # {{rank.requirement('money').total | simple}} {MONEY_NEEDED} {PERCENT_DONE } {PERCENT_LEFT } {AMOUNT } {AMOUNT_NEEDED } - # {{rank.requirement('money').total | simple}} and {MONEY_NEEDED} are different from {AMOUNT money} and {AMOUNT_NEEDED money} in that they use a different format. - # here is an example of showing the requirements for just money: - #list: - # complete: "&7{{rank.rank}} &8\xbb &7{{next.rank}} &efor &7${{rank.requirement('money').total | simple}}" - # current: "&c{{rank.rank}} &e\xbb &c{{next.rank}} &efor &a${{rank.requirement('money').total | simple}} &e{PERCENT_DONE money}%" - # incomplete: "&r{{rank.rank}} &e\xbb &r{{next.rank}} &efor &a${{rank.requirement('money').total | simple}}" list: complete: "&7{{rank.rank}} &8\xbb &7{{next.rank}}" current: "&c{{rank.rank}} &e\xbb &c{{next.rank}}" incomplete: "&r{{rank.rank}} &e\xbb &r{{next.rank}}" - # an empty string disables the header/footer + # An empty string disables the header/footer. header: "" footer: "" - # sent when a player tries to rankup when they are on cooldown + + # Sent when a player tries to rankup when they are on cooldown cooldown: singular: "&cYou must wait {{seconds_left}} more second to rankup again." plural: "&cYou must wait {{seconds_left}} more seconds to rankup again." + # prestige messages can also be customised prestige: requirements-not-met: "&cYou need {{rank.requirement('money').total | money}} money to prestige." @@ -89,20 +95,23 @@ prestige: gui: title: "Prestige to {{next.rank}}" + + # ===[ IMPORTANT PRE-FLATTENING INFO ]=== + # If you are using a 1.8-1.12 and you want to change this + # you can use MATERIAL:data, for example STAINED_GLASS_PANE:8 + # this works for both the rankup, cancel and fill blocks as well. + rankup: - material: GOLD_BLOCK + material: BLUE_STAINED_GLASS_PANE index: 0-3 name: '&a&lConfirm' lore: '&6Prestige to &b{{next.rank}}' cancel: - material: REDSTONE_BLOCK + material: RED_STAINED_GLASS_PANE index: 5-8 name: '&c&lCancel' fill: name: ' ' - # if you are using a 1.8-1.12 and you want to change this - # you can use MATERIAL:data, for example STAINED_GLASS_PANE:8 - # this works for both the rankup and cancel blocks as well material: BLACK_STAINED_GLASS_PANE list: @@ -115,5 +124,6 @@ prestige: singular: "&cYou must wait {{seconds_left}} second to prestige again." plural: "&cYou must wait {{seconds_left}} more seconds to prestige again." +# Other strings not-high-enough: "&cYou cannot prestige at your rank!" not-in-ladder: "&cSorry, but we could not find any rankups for the group(s) you are in. Use /ranks to list the rankups." \ No newline at end of file From 9241c45a0e9f14bbec328232456579cb2a38d661 Mon Sep 17 00:00:00 2001 From: flurbudurbur Date: Wed, 29 Jun 2022 20:50:36 +0200 Subject: [PATCH 19/27] Verbose YAML files --- src/main/resources/config.yml | 107 +++++++++++++++++-------------- src/main/resources/locale/en.yml | 13 ++-- src/main/resources/locale/es.yml | 2 +- 3 files changed, 69 insertions(+), 53 deletions(-) diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 4787fbe..faee630 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -1,83 +1,94 @@ -# this is used for letting you know that you need to update/change your config file +# This is used for letting you know that you need to update/change your config file. +# Do not change this. version: 10 -# the locale to use for messages -# all messages can be customised but this allows you to -# choose messages that are already translated -# locales can be found in the locale/ folder +# The locale to use for messages. +# All messages can be customised but this allows you to. +# Choose messages that are already translated. +# Locales can be found in the locale/ folder. locale: en -# interval (in minutes) to check to autorankup players -# ranking up manually will always be enabled -# set to 0 to disable +# Interval (in minutes) to check to autorankup players. +# Ranking up manually will always be enabled. +# Set to 0 to disable. + +# Players will need the rankup.auto permission to make use of this! +# Permission is given by default. autorankup-interval: 0 -# whether /ranks and /prestiges should be enabled (true) or disabled (false) +# Whether /ranks and /prestiges should be enabled (true) or disabled (false) # /rankup3 reload will not do anything if this is changed, -# you will have to restart your server. +# You will have to restart your server. ranks: true -# you can alternatively negate the permission rankup.prestiges -# this will also make the command not autocomplete in 1.13 + +# You can alternatively negate the permission rankup.prestiges. +# This will also make the command not autocomplete in 1.13+. prestiges: true -# whether to enable the /ranks GUI. -# will override the /ranks command +# Whether to enable the /ranks GUI. +# Will override the /ranks command. ranks-gui: false -# whether or not /prestige and /prestiges should be enabled. -# when a player reaches the top rank, they can do /prestige to return to the first rank, +# Whether /prestige and /prestiges should be enabled. +# When a player reaches the top rank, they can /prestige to return to the first rank, # but you will be able to grant them an additional "prestige" group or additional items. # -# if you do not want this command to autocomplete, make sure +# If you do not want this command to autocomplete, make sure # you negate the permission rankup.prestige with your permissions plugin. -# if enabled, a prestiges.yml file will be generated with some example prestiges -# You must restart your server when you change this for it to work! +# If enabled, a prestiges.yml file will be generated with some example prestiges +# you must restart your server when you change this for it to work! prestige: false -# if true, players with the permission rankup.notify will receive notifications when they join +# If true, players with the permission rankup.notify will receive notifications when they join # to update if the server is on an older version of Rankup. notify-update: true -# if rankups and prestiges should be by permissions -# if false, players will be checked for if they have a group of the same name as in rankups.yml, -# and automatically added and taken away from those groups. -# if true, players will be checked for the permission rankup.rank.RANK, where RANK -# is the rankup in rankups.yml. -# true also enables MANUAL MODE: Nothing will automatically change on rankup. -# You must use 'commands:' to change a player's group or permission when true. https://github.com/okx-code/Rankup3/wiki/How-to-rankups.yml-and-prestiges.yml#1-commands +# +------------------------------------------------------+ +# | Don't change this unless you know what you're doing! | +# +------------------------------------------------------+ +# Changing this setting to "true" can lead to your rankups.yml no longer working. +# +# Learn more about this: +# https://okx.sh/rankup/Advanced-Configuration/Permission-Rankup.html permission-rankup: false -# how people should confirm ranking up -# options are: gui, text or none +# How people should confirm ranking up +# Options are: gui, text or none confirmation-type: 'gui' -# how long, in seconds, people have to wait between a successful /rankup or /prestige +# How long in seconds people have to wait between a successful /rankup or /prestige # set to 0 to disable. cooldown: 1 -# if enabled, players can run /maxrankup to rankup as many times as possible, +# If enabled, players can run /maxrankup to rankup as many times as possible, # before they fail the requirements for the next rank. -# the permission rankup.maxrankup is used for this command, but it is given by default. -# note that /maxrankup, if enabled, has no confirmation. +# The permission rankup.maxrankup is used for this command, but it is given by default. +# Note that /maxrankup, if enabled, has no confirmation. max-rankup: # You must restart your server if you enable or disable /maxrankup! enabled: false - # whether to send a message for each rankup a player does - # if set to true, the chat may be spammed for each rankup a player goes through with /maxrankup - # if set to false, only the last rankup will be shown (if a player starts on rank A, then does - # /maxrankup and ranks up to B and then C, it will just say "player has ranked up to C") - individual-messages: true -# options when using the text rankup confirmation + # Whether to send a message for each rank a player ranks up for. + # -> If true, the chat may be spammed for each rankup a player goes through with /maxrankup + # Example: a player starts on rank A, but can rankup to rank D, the chat will say: + # "player has ranked up to B" + # "player has ranked up to C" + # "player has ranked up to D" + # -> If false, only the last rankup will be shown + # Example: a player starts on rank A, but can rankup to rank D, the chat will say: + # "player has ranked up to D". + individual-messages: false + +# Options when using the text rankup confirmation text: - # the time in seconds for a player to - # confirm by typing /rankup again + # The time in seconds for a player to + # confirm by typing /rankup again. timeout: 10 -# placeholders: +# Placeholders: # https://okx.sh/rankup/Placeholders.html placeholders: - # format for money. for more information, see + # Format for money. for more information, see # https://docs.oracle.com/javase/8/docs/api/java/text/DecimalFormat.html money-format: "#,##0.##" percent-format: "0.##" @@ -96,12 +107,12 @@ placeholders: incomplete: "Incomplete" last-rank-display-name: "last rank" -# what to shorten money by. +# What to shorten money by. # ie 1000 -> 1k -# set to an empty list to disable -# for each entry here, it counts as increasing by a factor of 1,000 -# the first represents thousands (1,000) then millions (1,000,000) then billions (1,000,000,000) etc. -# this is used in the "| shortmoney" filter +# Set to an empty list to disable +# For each entry here, it counts as increasing by a factor of 1,000 +# The first represents thousands (1,000) then millions (1,000,000) then billions (1,000,000,000) etc. +# This is used in the "| shortmoney" filter shorten: - 'K' - 'M' diff --git a/src/main/resources/locale/en.yml b/src/main/resources/locale/en.yml index 777e62a..3b0c9db 100644 --- a/src/main/resources/locale/en.yml +++ b/src/main/resources/locale/en.yml @@ -10,11 +10,12 @@ rankup: must-prestige: "&cYou must prestige to /rankup further!" no-rankup: "&eYou are at the highest rank." - # used for the text confirmation + # Only applies if confirmation-type: 'text' in config.yml. confirmation: |- &eAre you sure you want to rankup to &a{{next.rank}}&e? &eType &c/rankup &eagain to confirm. + # Only applies if confirmation-type: 'gui' in config.yml (Default). gui: rows: 1 title: "Rankup to {{next.rank}}" @@ -23,7 +24,7 @@ rankup: # ===[ IMPORTANT PRE-FLATTENING INFO ]=== # If you are using a 1.8-1.12 and you want to change this # you can use MATERIAL:data, for example STAINED_GLASS_PANE:8 - # this works for both the rankup, cancel and fill blocks as well. + # This works for both the rankup, cancel and fill blocks as well. material: GREEN_STAINED_GLASS_PANE @@ -46,6 +47,7 @@ rankup: name: ' ' material: BLACK_STAINED_GLASS_PANE + # Only applies if ranks-gui: true in config.yml. ranksgui: title: "Ranks" rows: 3 @@ -68,6 +70,7 @@ rankup: material: BLACK_STAINED_GLASS_PANE name: ' ' + # Only applies if ranks-gui: false in config.yml (Default). list: complete: "&7{{rank.rank}} &8\xbb &7{{next.rank}}" current: "&c{{rank.rank}} &e\xbb &c{{next.rank}}" @@ -81,7 +84,9 @@ rankup: singular: "&cYou must wait {{seconds_left}} more second to rankup again." plural: "&cYou must wait {{seconds_left}} more seconds to rankup again." -# prestige messages can also be customised +# Prestige messages can also be customised. +# They can be customized on a per-prestige basis in the prestiges.yml. +# If you do not see a prestiges.yml, enable it in the config.yml. prestige: requirements-not-met: "&cYou need {{rank.requirement('money').total | money}} money to prestige." no-prestige: "&eYou are at the highest prestige." @@ -99,7 +104,7 @@ prestige: # ===[ IMPORTANT PRE-FLATTENING INFO ]=== # If you are using a 1.8-1.12 and you want to change this # you can use MATERIAL:data, for example STAINED_GLASS_PANE:8 - # this works for both the rankup, cancel and fill blocks as well. + # This works for both the rankup, cancel and fill blocks as well. rankup: material: BLUE_STAINED_GLASS_PANE diff --git a/src/main/resources/locale/es.yml b/src/main/resources/locale/es.yml index 0ab1afd..cfdab27 100644 --- a/src/main/resources/locale/es.yml +++ b/src/main/resources/locale/es.yml @@ -1,4 +1,4 @@ - money# the messages in this section can be customised for each rankup in rankups.yml. +# the messages in this section can be customised for each rankup in rankups.yml. rankup: requirements-not-met: "&cNo satisfaces los requisitos para seguir al siguiente rango." no-rankup: "&eEstás en el último rango." From 360e48d0aefec7660e2b9af8673efea33cce13f5 Mon Sep 17 00:00:00 2001 From: NicBOMB <19939315+NicBOMB@users.noreply.github.com> Date: Sun, 10 Jul 2022 16:48:32 -0700 Subject: [PATCH 20/27] fix out of bounds error throwing for ranksgui --- src/main/java/sh/okx/rankup/ranksgui/RanksGui.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/main/java/sh/okx/rankup/ranksgui/RanksGui.java b/src/main/java/sh/okx/rankup/ranksgui/RanksGui.java index c792865..e713187 100644 --- a/src/main/java/sh/okx/rankup/ranksgui/RanksGui.java +++ b/src/main/java/sh/okx/rankup/ranksgui/RanksGui.java @@ -63,12 +63,11 @@ public void open() { RankElement rankElement0 = rankElement; ItemStack item = get((section, path0) -> Gui.getItem(plugin, section.getConfigurationSection(path0), player, rankElement0), path, rankPath, basePath, null); - - inventory.setItem(index++, item); - if (index > rows * 9) { - throw new IllegalArgumentException("Ranks GUI is too small for the number of ranks. Increase the number of rows on the ranks GUI."); + if (index > rows * width - offset) { + throw new IllegalArgumentException("Ranks GUI is set to " + (rows * width - offset) + ", which is too small for " + index + " ranks. Set a larger valid ranks GUI."); } - if (index == rowIndex) { + inventory.setItem(index, item); + if (++index == rowIndex) { rowIndex += 9; index += 9 - width; } From 6a1010022cc87e908787b2d0f7350a4708a4a8bd Mon Sep 17 00:00:00 2001 From: okx-code Date: Wed, 18 Jan 2023 21:38:11 +0000 Subject: [PATCH 21/27] add /rankup noconfirm --- .../sh/okx/rankup/commands/RankupCommand.java | 27 ++++ .../rankup/messages/NullMessageBuilder.java | 2 +- src/main/resources/config.yml | 7 +- src/main/resources/plugin.yml | 4 + .../sh/okx/rankup/RankupCommandsTest.java | 5 - .../DisabledNoConfirmCommandTest.java | 27 ++++ .../commands/EnabledNoConfirmCommandTest.java | 23 ++++ src/test/resources/noconfirm/config.yml | 116 ++++++++++++++++++ 8 files changed, 204 insertions(+), 7 deletions(-) delete mode 100644 src/test/java/sh/okx/rankup/RankupCommandsTest.java create mode 100644 src/test/java/sh/okx/rankup/commands/DisabledNoConfirmCommandTest.java create mode 100644 src/test/java/sh/okx/rankup/commands/EnabledNoConfirmCommandTest.java create mode 100644 src/test/resources/noconfirm/config.yml diff --git a/src/main/java/sh/okx/rankup/commands/RankupCommand.java b/src/main/java/sh/okx/rankup/commands/RankupCommand.java index b24fd68..e446015 100644 --- a/src/main/java/sh/okx/rankup/commands/RankupCommand.java +++ b/src/main/java/sh/okx/rankup/commands/RankupCommand.java @@ -1,8 +1,10 @@ package sh.okx.rankup.commands; +import java.util.Arrays; import java.util.Map; import java.util.WeakHashMap; import lombok.RequiredArgsConstructor; +import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.command.Command; import org.bukkit.command.CommandExecutor; @@ -29,6 +31,11 @@ public boolean onCommand(CommandSender sender, Command command, String label, St return true; } + if (plugin.getConfig().getBoolean("enable-noconfirm", true) && args.length > 0 && args[0].equalsIgnoreCase("noconfirm")) { + handleNoConfirm(sender, label, Arrays.copyOfRange(args, 1, args.length)); + return true; + } + // check if player if (!(sender instanceof Player)) { return false; @@ -39,6 +46,7 @@ public boolean onCommand(CommandSender sender, Command command, String label, St if (!plugin.getHelper().checkRankup(player)) { return true; } + RankElement rankElement = rankups.getByPlayer(player); FileConfiguration config = plugin.getConfig(); @@ -79,4 +87,23 @@ public boolean onCommand(CommandSender sender, Command command, String label, St } return true; } + + private void handleNoConfirm(CommandSender sender, String label, String[] args) { + if (sender.hasPermission("rankup.noconfirm.other") && args.length > 0) { + Player player = Bukkit.getPlayer(args[0]); + if (player == null) { + sender.sendMessage(ChatColor.RED + "Player not found: " + args[0]); + } else { + plugin.getHelper().rankup(player); + sender.sendMessage(ChatColor.GREEN + "Triggered no-confirmation rankup for " + player.getName()); + } + } else { + if (!(sender instanceof Player)) { + sender.sendMessage("/" + label + " noconfirm "); + return; + } + + plugin.getHelper().rankup((Player) sender); + } + } } diff --git a/src/main/java/sh/okx/rankup/messages/NullMessageBuilder.java b/src/main/java/sh/okx/rankup/messages/NullMessageBuilder.java index dbea283..f1263e2 100644 --- a/src/main/java/sh/okx/rankup/messages/NullMessageBuilder.java +++ b/src/main/java/sh/okx/rankup/messages/NullMessageBuilder.java @@ -30,7 +30,7 @@ public MessageBuilder replaceOldRank(Rank rank) { @Override public MessageBuilder replaceSeconds(long seconds, long secondsLeft) { - return null; + return this; } @Override diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 52e5467..780b54c 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -24,7 +24,7 @@ prestiges: true # will override the /ranks command ranks-gui: false -# whether or not /prestige and /prestiges should be enabled. +# whether /prestige and /prestiges should be enabled. # when a player reaches the top rank, they can do /prestige to return to the first rank, # but you will be able to grant them an additional "prestige" group or additional items. # @@ -46,6 +46,11 @@ notify-update: true # use commands to change a player's group or permission. permission-rankup: false +# if players can use /rankup noconfirm to bypass any confirmation +# the permission rankup.noconfirm is used for this command, but it is true by default +# the console can always do /rankup noconfirm +enable-noconfirm: true + # how people should confirm ranking up # options are: gui, text or none confirmation-type: 'gui' diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 5b95e1b..81e3f92 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -36,6 +36,7 @@ permissions: rankup.prestiges: true rankup.auto: true rankup.maxrankup: true + rankup.noconfirm: true rankup.admin: children: # if a player can see if the plugin needs updating when they run /pru @@ -47,6 +48,7 @@ permissions: # if a player receives notifications to update rankup when they log in. rankup.notify: true rankup.playtime: true + rankup.noconfirm.other: true default: op rankup.rankup: default: true @@ -60,6 +62,8 @@ permissions: default: true rankup.maxrankup: default: true + rankup.noconfirm: + default: true rankup.playtime: description: Use all /rankup3 playtime subcommands for anyone. children: diff --git a/src/test/java/sh/okx/rankup/RankupCommandsTest.java b/src/test/java/sh/okx/rankup/RankupCommandsTest.java deleted file mode 100644 index db09752..0000000 --- a/src/test/java/sh/okx/rankup/RankupCommandsTest.java +++ /dev/null @@ -1,5 +0,0 @@ -package sh.okx.rankup; - -public class RankupCommandsTest extends RankupTest { - -} diff --git a/src/test/java/sh/okx/rankup/commands/DisabledNoConfirmCommandTest.java b/src/test/java/sh/okx/rankup/commands/DisabledNoConfirmCommandTest.java new file mode 100644 index 0000000..d5f6e42 --- /dev/null +++ b/src/test/java/sh/okx/rankup/commands/DisabledNoConfirmCommandTest.java @@ -0,0 +1,27 @@ +package sh.okx.rankup.commands; + +import static org.junit.jupiter.api.Assertions.*; + +import be.seeseemelk.mockbukkit.entity.PlayerMock; +import org.junit.jupiter.api.Test; +import sh.okx.rankup.RankupTest; + +public class DisabledNoConfirmCommandTest extends RankupTest { + + public DisabledNoConfirmCommandTest() { + super("noconfirm"); + } + + @Test + public void testNoConfirmDisabled() { + PlayerMock player = server.addPlayer(); + player.addAttachment(plugin, "rankup.rankup", true); + player.addAttachment(plugin, "rankup.noconfirm", true); + + plugin.getPermissions().addGroup(player.getUniqueId(), "A"); + plugin.getEconomy().setPlayer(player, 10000); + + plugin.getCommand("rankup").execute(player, "rankup", new String[] {"noconfirm"}); + assertFalse(plugin.getPermissions().inGroup(player.getUniqueId(), "B")); + } +} diff --git a/src/test/java/sh/okx/rankup/commands/EnabledNoConfirmCommandTest.java b/src/test/java/sh/okx/rankup/commands/EnabledNoConfirmCommandTest.java new file mode 100644 index 0000000..f52bc9c --- /dev/null +++ b/src/test/java/sh/okx/rankup/commands/EnabledNoConfirmCommandTest.java @@ -0,0 +1,23 @@ +package sh.okx.rankup.commands; + +import static org.junit.jupiter.api.Assertions.*; + +import be.seeseemelk.mockbukkit.entity.PlayerMock; +import org.junit.jupiter.api.Test; +import sh.okx.rankup.RankupTest; + +public class EnabledNoConfirmCommandTest extends RankupTest { + + @Test + public void testNoConfirmEnabled() { + PlayerMock player = server.addPlayer(); + player.addAttachment(plugin, "rankup.rankup", true); + player.addAttachment(plugin, "rankup.noconfirm", true); + + plugin.getPermissions().addGroup(player.getUniqueId(), "A"); + plugin.getEconomy().setPlayer(player, 10000); + + plugin.getCommand("rankup").execute(player, "rankup", new String[] {"noconfirm"}); + assertTrue(plugin.getPermissions().inGroup(player.getUniqueId(), "B")); + } +} diff --git a/src/test/resources/noconfirm/config.yml b/src/test/resources/noconfirm/config.yml new file mode 100644 index 0000000..8bc4244 --- /dev/null +++ b/src/test/resources/noconfirm/config.yml @@ -0,0 +1,116 @@ +# this is used for letting you know that you need to update/change your config file +version: 10 + +# the locale to use for messages +# all messages can be customised but this allows you to +# choose messages that are already translated +# locales can be found in the locale/ folder +locale: en + +# interval (in minutes) to check to autorankup players +# ranking up manually will always be enabled +# set to 0 to disable +autorankup-interval: 0 + +# whether /ranks and /prestiges should be enabled (true) or disabled (false) +# /rankup3 reload will not do anything if this is changed, +# you will have to restart your server. +ranks: true +# you can alternatively negate the permission rankup.prestiges +# this will also make the command not autocomplete in 1.13 +prestiges: true + +# whether to enable the /ranks GUI. +# will override the /ranks command +ranks-gui: false + +# whether /prestige and /prestiges should be enabled. +# when a player reaches the top rank, they can do /prestige to return to the first rank, +# but you will be able to grant them an additional "prestige" group or additional items. +# +# if you do not want this command to autocomplete, make sure +# you negate the permission rankup.prestige with your permissions plugin. +# if enabled, a prestiges.yml file will be generated with some example prestiges +# You must restart your server when you change this for it to work! +prestige: false + +# if true, players with the permission rankup.notify will receive notifications when they join +# to update if the server is on an older version of Rankup. +notify-update: true + +# if rankups and prestiges should be by permissions +# if false, players will be checked for if they have a group of the same name as in rankups.yml, +# and automatically added and taken away from those groups. +# if true, players will be checked for the permission rankup.rank.RANK, where RANK +# is the rankup in rankups.yml. Nothing will automatically happen on rankup, so you must +# use commands to change a player's group or permission. +permission-rankup: false + +# if players can use /rankup noconfirm to bypass any confirmation +# the permission rankup.noconfirm is used for this command, but it is true by default +# the console can always do /rankup noconfirm +enable-noconfirm: false + +# how people should confirm ranking up +# options are: gui, text or none +confirmation-type: 'gui' + +# how long, in seconds, people have to wait between a successful /rankup or /prestige +# set to 0 to disable. +cooldown: 1 + +# if enabled, players can run /maxrankup to rankup as many times as possible, +# before they fail the requirements for the next rank. +# the permission rankup.maxrankup is used for this command, but it is given by default. +# note that /maxrankup, if enabled, has no confirmation. +max-rankup: + # You must restart your server if you enable or disable /maxrankup! + enabled: false + # whether to send a message for each rankup a player does + # if set to true, the chat may be spammed for each rankup a player goes through with /maxrankup + # if set to false, only the last rankup will be shown (if a player starts on rank A, then does + # /maxrankup and ranks up to B and then C, it will just say "player has ranked up to C") + individual-messages: true + +# options when using the text rankup confirmation +text: + # the time in seconds for a player to + # confirm by typing /rankup again + timeout: 10 + +# placeholders: +# https://okx.sh/rankup/Placeholders.html +placeholders: + # format for money. for more information, see + # https://docs.oracle.com/javase/8/docs/api/java/text/DecimalFormat.html + money-format: "#,##0.##" + percent-format: "0.##" + # the format used for requirements + simple-format: "#.##" + # used for current_rank and next_rank placeholders when a player is not in anything in rankups.yml + not-in-ladder: "None" + # used in the current_prestige placeholders when a player hasn't prestiged yet + no-prestige: "None" + # used in the next_rank and next_prestige placeholders when a player is at the highest rank or prestige + highest-rank: "None" + # used in the %rankup_status_[rank]% placeholders + status: + complete: "Complete" + current: "Current" + incomplete: "Incomplete" + last-rank-display-name: "last rank" + +# what to shorten money by. +# ie 1000 -> 1k +# set to an empty list to disable +# for each entry here, it counts as increasing by a factor of 1,000 +# the first represents thousands (1,000) then millions (1,000,000) then billions (1,000,000,000) etc. +# this is used in the "| shortmoney" filter +shorten: +- 'K' +- 'M' +- 'B' +- 'T' +- 'Q' +- 'Qu' +- 'S' \ No newline at end of file From 8e9112e526e7f103d1aa9633e2cc2aee7ebf3064 Mon Sep 17 00:00:00 2001 From: okx-code Date: Thu, 19 Jan 2023 00:10:49 +0000 Subject: [PATCH 22/27] add luckperms context --- build.gradle | 4 +- .../rankup/hook/LuckPermsGroupProvider.java | 64 +++++++++++++++++++ .../rankup/hook/VaultPermissionManager.java | 10 +++ src/main/resources/config.yml | 9 +++ src/main/resources/plugin.yml | 2 +- 5 files changed, 87 insertions(+), 2 deletions(-) create mode 100644 src/main/java/sh/okx/rankup/hook/LuckPermsGroupProvider.java diff --git a/build.gradle b/build.gradle index 915c329..299bbc1 100644 --- a/build.gradle +++ b/build.gradle @@ -46,7 +46,7 @@ dependencies { implementation group: 'org.slf4j', name: 'slf4j-api', version: '1.7.30' compileOnly 'org.jetbrains:annotations:22.0.0' - compileOnly 'org.spigotmc:spigot-api:1.18-R0.1-SNAPSHOT' + compileOnly 'org.spigotmc:spigot-api:1.19.3-R0.1-SNAPSHOT' compileOnly('com.github.Realizedd:TokenManager:3.2.4') { transitive = false } @@ -66,6 +66,8 @@ dependencies { transitive = false } + compileOnly 'net.luckperms:api:5.4' + compileOnly 'com.github.LlmDl:Towny:25fc18a' testImplementation 'com.github.LlmDl:Towny:25fc18a' diff --git a/src/main/java/sh/okx/rankup/hook/LuckPermsGroupProvider.java b/src/main/java/sh/okx/rankup/hook/LuckPermsGroupProvider.java new file mode 100644 index 0000000..c79964b --- /dev/null +++ b/src/main/java/sh/okx/rankup/hook/LuckPermsGroupProvider.java @@ -0,0 +1,64 @@ +package sh.okx.rankup.hook; + +import java.util.UUID; +import net.luckperms.api.LuckPerms; +import net.luckperms.api.context.ContextSet; +import net.luckperms.api.context.ImmutableContextSet; +import net.luckperms.api.model.group.Group; +import net.luckperms.api.model.user.User; +import net.luckperms.api.node.types.InheritanceNode; + +public class LuckPermsGroupProvider implements GroupProvider { + + private final LuckPerms luckPerms; + private final ContextSet contextSet; + + public LuckPermsGroupProvider(LuckPerms luckPerms, ContextSet contextSet) { + this.luckPerms = luckPerms; + this.contextSet = contextSet; + } + + public static LuckPermsGroupProvider createFromString(LuckPerms luckPerms, String context) { + try { + ImmutableContextSet.Builder builder = ImmutableContextSet.builder(); + for (String contextPair : context.split(" ")) { + String[] keyValue = contextPair.split("=", 2); + if (keyValue.length == 2) { + builder.add(keyValue[0], keyValue[1]); + } + } + + return new LuckPermsGroupProvider(luckPerms, builder.build()); + } catch (NullPointerException | IllegalArgumentException ex) { + throw new IllegalArgumentException("Context is invalid: " + context, ex); + } + } + + + @Override + public boolean inGroup(UUID uuid, String group) { + User user = luckPerms.getUserManager().getUser(uuid); + for (Group lpGroup : user.getInheritedGroups(user.getQueryOptions().toBuilder().context(contextSet).build())) { + if (lpGroup.getName().equals(group)) { + return true; + } + } + return false; + } + + @Override + public void addGroup(UUID uuid, String group) { + User user = luckPerms.getUserManager().getUser(uuid); + user.data().add(InheritanceNode.builder(group).context(contextSet).build()); + + luckPerms.getUserManager().saveUser(user); + } + + @Override + public void removeGroup(UUID uuid, String group) { + User user = luckPerms.getUserManager().getUser(uuid); + user.data().remove(InheritanceNode.builder(group).context(contextSet).build()); + + luckPerms.getUserManager().saveUser(user); + } +} diff --git a/src/main/java/sh/okx/rankup/hook/VaultPermissionManager.java b/src/main/java/sh/okx/rankup/hook/VaultPermissionManager.java index 68d50c4..eaeaa27 100644 --- a/src/main/java/sh/okx/rankup/hook/VaultPermissionManager.java +++ b/src/main/java/sh/okx/rankup/hook/VaultPermissionManager.java @@ -1,6 +1,8 @@ package sh.okx.rankup.hook; +import net.luckperms.api.LuckPerms; import net.milkbowl.vault.permission.Permission; +import org.bukkit.Bukkit; import org.bukkit.plugin.RegisteredServiceProvider; import sh.okx.rankup.RankupPlugin; @@ -26,6 +28,14 @@ private GroupProvider getVaultPermissionProvider() { if (!provider.hasGroupSupport()) { return null; } + String lpContext = plugin.getConfig().getString("luckperms-context"); + if (lpContext != null && !lpContext.isEmpty()) { + RegisteredServiceProvider lpProvider = Bukkit.getServicesManager().getRegistration(LuckPerms.class); + if (lpProvider != null) { + return LuckPermsGroupProvider.createFromString(lpProvider.getProvider(), lpContext); + } + } + return new VaultGroupProvider(provider); } diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 780b54c..a3783bc 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -46,6 +46,15 @@ notify-update: true # use commands to change a player's group or permission. permission-rankup: false +# if not empty, these are the contexts to use when modifying groups if LuckPerms is enabled +# if empty, this will be based on your LuckPerms config.yml 'use-vault-server' and 'vault-server' +# this option will only if permission-rankup is disabled +# +# luckperms-context: 'server=global' # to make all rankups global +# luckperms-context: 'server=survival' # to make all rankups specific to survival +# luckperms-context: 'server=survival world=world_nether' # to make all rankups specific to the nether world in survival +luckperms-context: '' + # if players can use /rankup noconfirm to bypass any confirmation # the permission rankup.noconfirm is used for this command, but it is true by default # the console can always do /rankup noconfirm diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 81e3f92..d16c456 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -3,7 +3,7 @@ version: "${version}" main: sh.okx.rankup.RankupPlugin author: Okx depend: [Vault] -softdepend: [PlaceholderAPI, mcMMO, AdvancedAchievements, Towny, SuperbVote, VotingPlugin] +softdepend: [PlaceholderAPI, mcMMO, AdvancedAchievements, Towny, SuperbVote, VotingPlugin, LuckPerms] api-version: 1.13 commands: From abb2a3a5cdbbfa2313ceebbcc1a15beeb795be7b Mon Sep 17 00:00:00 2001 From: okx-code Date: Thu, 19 Jan 2023 00:21:20 +0000 Subject: [PATCH 23/27] bump version --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 299bbc1..7728548 100644 --- a/build.gradle +++ b/build.gradle @@ -14,7 +14,7 @@ pitest { } group 'sh.okx' -version '3.13.3' +version '3.14' java { sourceCompatibility = JavaVersion.VERSION_1_8 From 2a88bb597765a4b895128938c15da8f40385c29b Mon Sep 17 00:00:00 2001 From: okx-code Date: Thu, 19 Jan 2023 00:26:24 +0000 Subject: [PATCH 24/27] delete github actions for now --- .github/workflows/pitest.yml | 28 ---------------------------- .github/workflows/test.yml | 28 ---------------------------- 2 files changed, 56 deletions(-) delete mode 100644 .github/workflows/pitest.yml delete mode 100644 .github/workflows/test.yml diff --git a/.github/workflows/pitest.yml b/.github/workflows/pitest.yml deleted file mode 100644 index 45fee65..0000000 --- a/.github/workflows/pitest.yml +++ /dev/null @@ -1,28 +0,0 @@ -name: PIT test -on: - push: - paths: - - 'src/**' - -jobs: - test: - if: github.ref == 'refs/heads/master' - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - name: Setup JDK - uses: actions/setup-java@v2 - with: - java-version: 11 - distribution: 'adopt' - - name: Gradle Test - run: ./gradlew pitest --no-daemon - - name: Upload Tests - uses: easingthemes/ssh-deploy@v2 - env: - SSH_PRIVATE_KEY: ${{ secrets.SERVER_SSH_KEY }} - ARGS: "-rvzt --delete" - SOURCE: "build/reports/pitest/" - REMOTE_HOST: ${{ secrets.REMOTE_HOST }} - REMOTE_USER: ${{ secrets.REMOTE_USER }} - TARGET: ${{ secrets.PITEST_TARGET }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml deleted file mode 100644 index b05ace7..0000000 --- a/.github/workflows/test.yml +++ /dev/null @@ -1,28 +0,0 @@ -name: Test -on: - push: - paths: - - 'src/**' - -jobs: - pitest: - if: github.ref == 'refs/heads/master' - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - name: Setup JDK - uses: actions/setup-java@v2 - with: - java-version: 11 - distribution: 'adopt' - - name: Gradle Test - run: ./gradlew test --no-daemon - - name: Upload Tests - uses: easingthemes/ssh-deploy@v2 - env: - SSH_PRIVATE_KEY: ${{ secrets.SERVER_SSH_KEY }} - ARGS: "-rvzt --delete" - SOURCE: "build/reports/tests/test/" - REMOTE_HOST: ${{ secrets.REMOTE_HOST }} - REMOTE_USER: ${{ secrets.REMOTE_USER }} - TARGET: ${{ secrets.REMOTE_TARGET }} From 8bf502922d9646c0479e937c04dc0bb304530eac Mon Sep 17 00:00:00 2001 From: okx-code Date: Wed, 28 Jun 2023 19:40:18 +0100 Subject: [PATCH 25/27] 3.14.1 --- build.gradle | 6 +-- src/main/java/sh/okx/rankup/RankupPlugin.java | 48 +++++-------------- .../java/sh/okx/rankup/ranks/RankList.java | 7 +-- .../okx/rankup/ranksgui/RanksGuiCommand.java | 3 ++ ...tingPluginPointsDeductibleRequirement.java | 3 +- .../VotingPluginPointsRequirement.java | 3 +- .../votingplugin/VotingPluginUtil.java | 33 +++++++++++++ .../VotingPluginVotesRequirement.java | 3 +- 8 files changed, 55 insertions(+), 51 deletions(-) create mode 100644 src/main/java/sh/okx/rankup/requirements/requirement/votingplugin/VotingPluginUtil.java diff --git a/build.gradle b/build.gradle index 7728548..35f7559 100644 --- a/build.gradle +++ b/build.gradle @@ -14,7 +14,7 @@ pitest { } group 'sh.okx' -version '3.14' +version '3.14.1' java { sourceCompatibility = JavaVersion.VERSION_1_8 @@ -46,7 +46,7 @@ dependencies { implementation group: 'org.slf4j', name: 'slf4j-api', version: '1.7.30' compileOnly 'org.jetbrains:annotations:22.0.0' - compileOnly 'org.spigotmc:spigot-api:1.19.3-R0.1-SNAPSHOT' + compileOnly 'org.spigotmc:spigot-api:1.20.1-R0.1-SNAPSHOT' compileOnly('com.github.Realizedd:TokenManager:3.2.4') { transitive = false } @@ -62,7 +62,7 @@ dependencies { compileOnly('com.github.mcMMO-Dev:mcMMO:601297') { exclude group: 'com.sk89q.worldguard' } - compileOnly ('com.bencodez:votingplugin:6.9.5') { + compileOnly ('com.bencodez:votingplugin:6.13.1') { transitive = false } diff --git a/src/main/java/sh/okx/rankup/RankupPlugin.java b/src/main/java/sh/okx/rankup/RankupPlugin.java index aa25570..83dcada 100644 --- a/src/main/java/sh/okx/rankup/RankupPlugin.java +++ b/src/main/java/sh/okx/rankup/RankupPlugin.java @@ -1,12 +1,6 @@ package sh.okx.rankup; import com.electronwill.nightconfig.toml.TomlFormat; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileReader; -import java.util.HashMap; -import java.util.List; -import java.util.Map; import lombok.Getter; import org.bukkit.Bukkit; import org.bukkit.ChatColor; @@ -21,12 +15,7 @@ import org.bukkit.plugin.PluginManager; import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.plugin.java.JavaPluginLoader; -import sh.okx.rankup.commands.InfoCommand; -import sh.okx.rankup.commands.MaxRankupCommand; -import sh.okx.rankup.commands.PrestigeCommand; -import sh.okx.rankup.commands.PrestigesCommand; -import sh.okx.rankup.commands.RanksCommand; -import sh.okx.rankup.commands.RankupCommand; +import sh.okx.rankup.commands.*; import sh.okx.rankup.economy.Economy; import sh.okx.rankup.economy.EconomyProvider; import sh.okx.rankup.economy.VaultEconomyProvider; @@ -49,24 +38,7 @@ import sh.okx.rankup.ranksgui.RanksGuiListener; import sh.okx.rankup.requirements.Requirement; import sh.okx.rankup.requirements.RequirementRegistry; -import sh.okx.rankup.requirements.requirement.AdvancementRequirement; -import sh.okx.rankup.requirements.requirement.BlockBreakRequirement; -import sh.okx.rankup.requirements.requirement.CraftItemRequirement; -import sh.okx.rankup.requirements.requirement.GroupRequirement; -import sh.okx.rankup.requirements.requirement.ItemDeductibleRequirement; -import sh.okx.rankup.requirements.requirement.ItemRequirement; -import sh.okx.rankup.requirements.requirement.MobKillsRequirement; -import sh.okx.rankup.requirements.requirement.MoneyDeductibleRequirement; -import sh.okx.rankup.requirements.requirement.MoneyRequirement; -import sh.okx.rankup.requirements.requirement.PermissionRequirement; -import sh.okx.rankup.requirements.requirement.PlaceholderRequirement; -import sh.okx.rankup.requirements.requirement.PlayerKillsRequirement; -import sh.okx.rankup.requirements.requirement.PlaytimeMinutesRequirement; -import sh.okx.rankup.requirements.requirement.TotalMobKillsRequirement; -import sh.okx.rankup.requirements.requirement.UseItemRequirement; -import sh.okx.rankup.requirements.requirement.WorldRequirement; -import sh.okx.rankup.requirements.requirement.XpLevelDeductibleRequirement; -import sh.okx.rankup.requirements.requirement.XpLevelRequirement; +import sh.okx.rankup.requirements.requirement.*; import sh.okx.rankup.requirements.requirement.advancedachievements.AdvancedAchievementsAchievementRequirement; import sh.okx.rankup.requirements.requirement.advancedachievements.AdvancedAchievementsTotalRequirement; import sh.okx.rankup.requirements.requirement.mcmmo.McMMOPowerLevelRequirement; @@ -74,12 +46,7 @@ import sh.okx.rankup.requirements.requirement.superbvote.SuperbVoteVotesRequirement; import sh.okx.rankup.requirements.requirement.tokenmanager.TokensDeductibleRequirement; import sh.okx.rankup.requirements.requirement.tokenmanager.TokensRequirement; -import sh.okx.rankup.requirements.requirement.towny.TownyKingNumberResidentsRequirement; -import sh.okx.rankup.requirements.requirement.towny.TownyKingNumberTownsRequirement; -import sh.okx.rankup.requirements.requirement.towny.TownyKingRequirement; -import sh.okx.rankup.requirements.requirement.towny.TownyMayorNumberResidentsRequirement; -import sh.okx.rankup.requirements.requirement.towny.TownyMayorRequirement; -import sh.okx.rankup.requirements.requirement.towny.TownyResidentRequirement; +import sh.okx.rankup.requirements.requirement.towny.*; import sh.okx.rankup.requirements.requirement.votingplugin.VotingPluginPointsDeductibleRequirement; import sh.okx.rankup.requirements.requirement.votingplugin.VotingPluginPointsRequirement; import sh.okx.rankup.requirements.requirement.votingplugin.VotingPluginVotesRequirement; @@ -89,6 +56,13 @@ import sh.okx.rankup.util.UpdateNotifier; import sh.okx.rankup.util.VersionChecker; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + public class RankupPlugin extends JavaPlugin { public static final int CONFIG_VERSION = 10; @@ -327,7 +301,7 @@ public void refreshRanks() { - } catch (Exception e) { + } catch (RuntimeException e) { this.errorMessage = e.getClass().getName() + ": " + e.getMessage(); e.printStackTrace(); } diff --git a/src/main/java/sh/okx/rankup/ranks/RankList.java b/src/main/java/sh/okx/rankup/ranks/RankList.java index 24d6341..c9b7db7 100644 --- a/src/main/java/sh/okx/rankup/ranks/RankList.java +++ b/src/main/java/sh/okx/rankup/ranks/RankList.java @@ -1,14 +1,11 @@ package sh.okx.rankup.ranks; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Objects; import lombok.Getter; import org.bukkit.entity.Player; import sh.okx.rankup.RankupPlugin; +import java.util.*; + public abstract class RankList { protected RankupPlugin plugin; diff --git a/src/main/java/sh/okx/rankup/ranksgui/RanksGuiCommand.java b/src/main/java/sh/okx/rankup/ranksgui/RanksGuiCommand.java index e437efa..d01bafe 100644 --- a/src/main/java/sh/okx/rankup/ranksgui/RanksGuiCommand.java +++ b/src/main/java/sh/okx/rankup/ranksgui/RanksGuiCommand.java @@ -18,6 +18,9 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command if (!(sender instanceof Player)) { return false; } + if (plugin.error(sender)) { + return true; + } Player player = (Player) sender; listener.open(new RanksGui(plugin, player)); diff --git a/src/main/java/sh/okx/rankup/requirements/requirement/votingplugin/VotingPluginPointsDeductibleRequirement.java b/src/main/java/sh/okx/rankup/requirements/requirement/votingplugin/VotingPluginPointsDeductibleRequirement.java index 880020e..aa7da86 100644 --- a/src/main/java/sh/okx/rankup/requirements/requirement/votingplugin/VotingPluginPointsDeductibleRequirement.java +++ b/src/main/java/sh/okx/rankup/requirements/requirement/votingplugin/VotingPluginPointsDeductibleRequirement.java @@ -1,6 +1,5 @@ package sh.okx.rankup.requirements.requirement.votingplugin; -import com.bencodez.votingplugin.user.UserManager; import com.bencodez.votingplugin.user.VotingPluginUser; import org.bukkit.entity.Player; import sh.okx.rankup.RankupPlugin; @@ -19,7 +18,7 @@ protected VotingPluginPointsDeductibleRequirement(Requirement clone) { @Override public void apply(Player player, double multiplier) { - VotingPluginUser user = UserManager.getInstance().getVotingPluginUser(player); + VotingPluginUser user = VotingPluginUtil.getInstance().getUserManager().getVotingPluginUser(player); if(!user.removePoints(getValueInt())) { plugin.getLogger().warning("Unable to remove VotingPlugin points"); } diff --git a/src/main/java/sh/okx/rankup/requirements/requirement/votingplugin/VotingPluginPointsRequirement.java b/src/main/java/sh/okx/rankup/requirements/requirement/votingplugin/VotingPluginPointsRequirement.java index 67c1c52..3259c4e 100644 --- a/src/main/java/sh/okx/rankup/requirements/requirement/votingplugin/VotingPluginPointsRequirement.java +++ b/src/main/java/sh/okx/rankup/requirements/requirement/votingplugin/VotingPluginPointsRequirement.java @@ -1,6 +1,5 @@ package sh.okx.rankup.requirements.requirement.votingplugin; -import com.bencodez.votingplugin.user.UserManager; import org.bukkit.entity.Player; import sh.okx.rankup.RankupPlugin; import sh.okx.rankup.requirements.ProgressiveRequirement; @@ -18,7 +17,7 @@ protected VotingPluginPointsRequirement(Requirement clone) { @Override public double getProgress(Player player) { - return UserManager.getInstance().getVotingPluginUser(player).getPoints(); + return VotingPluginUtil.getInstance().getUserManager().getVotingPluginUser(player).getPoints(); } @Override diff --git a/src/main/java/sh/okx/rankup/requirements/requirement/votingplugin/VotingPluginUtil.java b/src/main/java/sh/okx/rankup/requirements/requirement/votingplugin/VotingPluginUtil.java new file mode 100644 index 0000000..0d44f5f --- /dev/null +++ b/src/main/java/sh/okx/rankup/requirements/requirement/votingplugin/VotingPluginUtil.java @@ -0,0 +1,33 @@ +package sh.okx.rankup.requirements.requirement.votingplugin; + +import com.bencodez.votingplugin.VotingPluginMain; +import com.bencodez.votingplugin.user.UserManager; + +import java.lang.reflect.InvocationTargetException; + +public class VotingPluginUtil { + private static VotingPluginUtil instance; + + private UserManager userManager; + + private VotingPluginUtil() { + try { + userManager = (UserManager) UserManager.class.getMethod("getInstance").invoke(null); + } catch (NoSuchMethodException e) { + userManager = VotingPluginMain.getPlugin().getVotingPluginUserManager(); + } catch (InvocationTargetException | IllegalAccessException e) { + throw new RuntimeException(e); + } + } + + public static VotingPluginUtil getInstance() { + if (instance == null) { + instance = new VotingPluginUtil(); + } + return instance; + } + + public UserManager getUserManager() { + return userManager; + } +} diff --git a/src/main/java/sh/okx/rankup/requirements/requirement/votingplugin/VotingPluginVotesRequirement.java b/src/main/java/sh/okx/rankup/requirements/requirement/votingplugin/VotingPluginVotesRequirement.java index be691f0..f09ad7d 100644 --- a/src/main/java/sh/okx/rankup/requirements/requirement/votingplugin/VotingPluginVotesRequirement.java +++ b/src/main/java/sh/okx/rankup/requirements/requirement/votingplugin/VotingPluginVotesRequirement.java @@ -1,7 +1,6 @@ package sh.okx.rankup.requirements.requirement.votingplugin; import com.bencodez.votingplugin.topvoter.TopVoter; -import com.bencodez.votingplugin.user.UserManager; import org.bukkit.entity.Player; import sh.okx.rankup.RankupPlugin; import sh.okx.rankup.requirements.ProgressiveRequirement; @@ -18,7 +17,7 @@ protected VotingPluginVotesRequirement(Requirement clone) { @Override public double getProgress(Player player) { - return UserManager.getInstance().getVotingPluginUser(player).getTotal(TopVoter.AllTime); + return VotingPluginUtil.getInstance().getUserManager().getVotingPluginUser(player).getTotal(TopVoter.AllTime); } @Override From abd4365f20f52a58231265d34c8553603d096d20 Mon Sep 17 00:00:00 2001 From: okx-code Date: Tue, 25 Jul 2023 22:07:28 +0100 Subject: [PATCH 26/27] fix papi placeholders when using world or placeholder requirements --- build.gradle | 2 +- .../rankup/placeholders/RankupExpansion.java | 6 ++-- .../sh/okx/rankup/WorldRequirementTest.java | 29 +++++++++++++++++++ src/test/resources/world/rankups.yml | 5 ++++ 4 files changed, 38 insertions(+), 4 deletions(-) create mode 100644 src/test/java/sh/okx/rankup/WorldRequirementTest.java create mode 100644 src/test/resources/world/rankups.yml diff --git a/build.gradle b/build.gradle index 35f7559..5f12a00 100644 --- a/build.gradle +++ b/build.gradle @@ -14,7 +14,7 @@ pitest { } group 'sh.okx' -version '3.14.1' +version '3.14.2' java { sourceCompatibility = JavaVersion.VERSION_1_8 diff --git a/src/main/java/sh/okx/rankup/placeholders/RankupExpansion.java b/src/main/java/sh/okx/rankup/placeholders/RankupExpansion.java index d2cfd03..9cb0c52 100644 --- a/src/main/java/sh/okx/rankup/placeholders/RankupExpansion.java +++ b/src/main/java/sh/okx/rankup/placeholders/RankupExpansion.java @@ -177,11 +177,11 @@ private String getPlaceholderRequirement(Player player, Rank rank, String requir case "left": return placeholders.getSimpleFormat().format(orElse(requirement, r -> r.getRemaining(player), 0)); case "done": - return placeholders.getSimpleFormat().format(orElse(requirement, r -> r.getValueDouble() - r.getRemaining(player), 0)); + return placeholders.getSimpleFormat().format(orElse(requirement, r -> r.getTotal(player) - r.getRemaining(player), 0)); case "percent_left": - return placeholders.getPercentFormat().format(orElse(requirement, r -> (r.getRemaining(player) / r.getValueDouble()) * 100, 0)); + return placeholders.getPercentFormat().format(orElse(requirement, r -> (r.getRemaining(player) / r.getTotal(player)) * 100, 0)); case "percent_done": - return placeholders.getPercentFormat().format(orElse(requirement, r -> (1 - (r.getRemaining(player) / r.getValueDouble())) * 100, 100)); + return placeholders.getPercentFormat().format(orElse(requirement, r -> (1 - (r.getRemaining(player) / r.getTotal(player))) * 100, 100)); default: return null; } diff --git a/src/test/java/sh/okx/rankup/WorldRequirementTest.java b/src/test/java/sh/okx/rankup/WorldRequirementTest.java new file mode 100644 index 0000000..41c5326 --- /dev/null +++ b/src/test/java/sh/okx/rankup/WorldRequirementTest.java @@ -0,0 +1,29 @@ +package sh.okx.rankup; + + +import be.seeseemelk.mockbukkit.entity.PlayerMock; +import org.bukkit.Location; +import org.junit.jupiter.api.Test; +import sh.okx.rankup.placeholders.RankupExpansion; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class WorldRequirementTest extends RankupTest { + + public WorldRequirementTest() { + super("world"); + } + + @Test + public void testStatusComplete() { + PlayerMock player = server.addPlayer(); + groupProvider.addGroup(player.getUniqueId(), "a"); + + RankupExpansion expansion = plugin.getPlaceholders().getExpansion(); + assertEquals("0", expansion.placeholder(player, "requirement_world_percent_done")); + + player.teleport(new Location(server.getWorld("the_nether"), 0, 0, 0)); + + assertEquals("100", expansion.placeholder(player, "requirement_world_percent_done")); + } +} \ No newline at end of file diff --git a/src/test/resources/world/rankups.yml b/src/test/resources/world/rankups.yml new file mode 100644 index 0000000..625f9dc --- /dev/null +++ b/src/test/resources/world/rankups.yml @@ -0,0 +1,5 @@ +a: + rank: 'a' + next: 'b' + requirements: + - 'world the_nether' \ No newline at end of file From d37b1ba1cbab5022f122b297be485eac00bfd603 Mon Sep 17 00:00:00 2001 From: NicBOMB <19939315+NicBOMB@users.noreply.github.com> Date: Tue, 22 Aug 2023 12:49:14 -0700 Subject: [PATCH 27/27] default to empty string instead of `req.value` --- .../java/sh/okx/rankup/messages/pebble/RequirementContext.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/sh/okx/rankup/messages/pebble/RequirementContext.java b/src/main/java/sh/okx/rankup/messages/pebble/RequirementContext.java index 6db809d..12fa90c 100644 --- a/src/main/java/sh/okx/rankup/messages/pebble/RequirementContext.java +++ b/src/main/java/sh/okx/rankup/messages/pebble/RequirementContext.java @@ -37,7 +37,7 @@ public String getName() { } public String getSub(){ - return requirement.hasSubRequirement() ? requirement.getSub() : requirement.getValue(); + return requirement.hasSubRequirement() ? requirement.getSub() : ""; } public String getValue(){