diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..716779f35 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,23 @@ +root = true + +[*] +charset = utf-8 +indent_style = space +indent_size = 4 +insert_final_newline = true +trim_trailing_whitespace = true + +[*.java] +end_of_line=lf + +[*.json] +indent_size = 2 + +[*.{yml,yaml}] +indent_size = 2 + +[*.md] +trim_trailing_whitespace = false + +[*.sh] +end_of_line = lf diff --git a/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE/bug_report.md similarity index 72% rename from ISSUE_TEMPLATE.md rename to .github/ISSUE_TEMPLATE/bug_report.md index 11f97984d..40ccc1285 100644 --- a/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -1,3 +1,12 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: bug +assignees: '' + +--- + _Please paste the output from `/usb version` below_ ``` paste it here please (replace this text) @@ -10,5 +19,3 @@ _What steps will reproduce the problem?_ _If you have any log-files, please paste them to [pastebin.com](http://pastebin.com)_ * server-log: link-to-pastebin - -(Not following the template will result in closing the issue.) diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 000000000..11fc491ef --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: enhancement +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/.github/deploy-release.sh b/.github/deploy-release.sh new file mode 100644 index 000000000..4fbbaae91 --- /dev/null +++ b/.github/deploy-release.sh @@ -0,0 +1,61 @@ +#!/usr/bin/env bash + +echo -e "Running release script...\n" +echo -e "Publishing javadocs and artifacts...\n" +cd $HOME + +rsync -r --quiet -e "ssh -p 7685 -o StrictHostKeyChecking=no" \ +$HOME/work/uSkyBlock/uSkyBlock/po-utils/target/mvn-repo/ \ +u36810p330294@uskyblock.ovh:domains/uskyblock.ovh/public_html/maven/uskyblock/ + +rsync -r --quiet -e "ssh -p 7685 -o StrictHostKeyChecking=no" \ +$HOME/work/uSkyBlock/uSkyBlock/target/mvn-repo/ \ +u36810p330294@uskyblock.ovh:domains/uskyblock.ovh/public_html/maven/uskyblock/ + +rsync -r --quiet -e "ssh -p 7685 -o StrictHostKeyChecking=no" \ +$HOME/work/uSkyBlock/uSkyBlock/uSkyBlock-API/target/mvn-repo/ \ +u36810p330294@uskyblock.ovh:domains/uskyblock.ovh/public_html/maven/uskyblock/ + +rsync -r --quiet -e "ssh -p 7685 -o StrictHostKeyChecking=no" \ +$HOME/work/uSkyBlock/uSkyBlock/uSkyBlock-APIv2/target/mvn-repo/ \ +u36810p330294@uskyblock.ovh:domains/uskyblock.ovh/public_html/maven/uskyblock/ + +rsync -r --quiet -e "ssh -p 7685 -o StrictHostKeyChecking=no" \ +$HOME/work/uSkyBlock/uSkyBlock/uSkyBlock-Core/target/mvn-repo/ \ +u36810p330294@uskyblock.ovh:domains/uskyblock.ovh/public_html/maven/uskyblock/ + +rsync -r --quiet -e "ssh -p 7685 -o StrictHostKeyChecking=no" \ +$HOME/work/uSkyBlock/uSkyBlock/uSkyBlock-FAWE/target/mvn-repo/ \ +u36810p330294@uskyblock.ovh:domains/uskyblock.ovh/public_html/maven/uskyblock/ + +rsync -r --quiet -e "ssh -p 7685 -o StrictHostKeyChecking=no" \ +$HOME/work/uSkyBlock/uSkyBlock/uSkyBlock-Plugin/target/mvn-repo/ \ +u36810p330294@uskyblock.ovh:domains/uskyblock.ovh/public_html/maven/uskyblock/ + +echo -e "Publishing javadocs...\n" + +rsync -r --delete --quiet -e "ssh -p 7685 -o StrictHostKeyChecking=no" \ +$HOME/work/uSkyBlock/uSkyBlock/po-utils/target/site/apidocs/ \ +u36810p330294@uskyblock.ovh:domains/uskyblock.ovh/public_html/javadocs/release/po-utils/ + +rsync -r --delete --quiet -e "ssh -p 7685 -o StrictHostKeyChecking=no" \ +$HOME/work/uSkyBlock/uSkyBlock/uSkyBlock-API/target/site/apidocs/ \ +u36810p330294@uskyblock.ovh:domains/uskyblock.ovh/public_html/javadocs/release/uSkyBlock-API/ + +rsync -r --delete --quiet -e "ssh -p 7685 -o StrictHostKeyChecking=no" \ +$HOME/work/uSkyBlock/uSkyBlock/uSkyBlock-APIv2/target/site/apidocs/ \ +u36810p330294@uskyblock.ovh:domains/uskyblock.ovh/public_html/javadocs/release/uSkyBlock-APIv2/ + +rsync -r --delete --quiet -e "ssh -p 7685 -o StrictHostKeyChecking=no" \ +$HOME/work/uSkyBlock/uSkyBlock/uSkyBlock-Core/target/site/apidocs/ \ +u36810p330294@uskyblock.ovh:domains/uskyblock.ovh/public_html/javadocs/release/uSkyBlock-Core/ + +echo -e "Publishing final plugin release...\n" + +rsync -r --quiet -e "ssh -p 7685 -o StrictHostKeyChecking=no" \ +$HOME/work/uSkyBlock/uSkyBlock/uSkyBlock-Plugin/target/uSkyBlock-*.jar \ +u36810p330294@uskyblock.ovh:domains/uskyblock.ovh/public_html/downloads/release/uSkyBlock/ + +rsync -r --quiet --no-R --no-implied-dirs -e "ssh -p 7685 -o StrictHostKeyChecking=no" \ +$HOME/work/uSkyBlock/uSkyBlock/uSkyBlock-Plugin/target/classes/version.json \ +u36810p330294@uskyblock.ovh:domains/uskyblock.ovh/public_html/versions/release.json diff --git a/.github/deploy-staging.sh b/.github/deploy-staging.sh new file mode 100644 index 000000000..87ccfd640 --- /dev/null +++ b/.github/deploy-staging.sh @@ -0,0 +1,61 @@ +#!/usr/bin/env bash + +echo -e "Running staging script...\n" +echo -e "Publishing javadocs and artifacts...\n" +cd $HOME + +rsync -r --quiet -e "ssh -p 7685 -o StrictHostKeyChecking=no" \ +$HOME/work/uSkyBlock/uSkyBlock/po-utils/target/mvn-repo/ \ +u36810p330294@uskyblock.ovh:domains/uskyblock.ovh/public_html/maven/uskyblock/ + +rsync -r --quiet -e "ssh -p 7685 -o StrictHostKeyChecking=no" \ +$HOME/work/uSkyBlock/uSkyBlock/target/mvn-repo/ \ +u36810p330294@uskyblock.ovh:domains/uskyblock.ovh/public_html/maven/uskyblock/ + +rsync -r --quiet -e "ssh -p 7685 -o StrictHostKeyChecking=no" \ +$HOME/work/uSkyBlock/uSkyBlock/uSkyBlock-API/target/mvn-repo/ \ +u36810p330294@uskyblock.ovh:domains/uskyblock.ovh/public_html/maven/uskyblock/ + +rsync -r --quiet -e "ssh -p 7685 -o StrictHostKeyChecking=no" \ +$HOME/work/uSkyBlock/uSkyBlock/uSkyBlock-APIv2/target/mvn-repo/ \ +u36810p330294@uskyblock.ovh:domains/uskyblock.ovh/public_html/maven/uskyblock/ + +rsync -r --quiet -e "ssh -p 7685 -o StrictHostKeyChecking=no" \ +$HOME/work/uSkyBlock/uSkyBlock/uSkyBlock-Core/target/mvn-repo/ \ +u36810p330294@uskyblock.ovh:domains/uskyblock.ovh/public_html/maven/uskyblock/ + +rsync -r --quiet -e "ssh -p 7685 -o StrictHostKeyChecking=no" \ +$HOME/work/uSkyBlock/uSkyBlock/uSkyBlock-FAWE/target/mvn-repo/ \ +u36810p330294@uskyblock.ovh:domains/uskyblock.ovh/public_html/maven/uskyblock/ + +rsync -r --quiet -e "ssh -p 7685 -o StrictHostKeyChecking=no" \ +$HOME/work/uSkyBlock/uSkyBlock/uSkyBlock-Plugin/target/mvn-repo/ \ +u36810p330294@uskyblock.ovh:domains/uskyblock.ovh/public_html/maven/uskyblock/ + +echo -e "Publishing javadocs...\n" + +rsync -r --delete --quiet -e "ssh -p 7685 -o StrictHostKeyChecking=no" \ +$HOME/work/uSkyBlock/uSkyBlock/po-utils/target/site/apidocs/ \ +u36810p330294@uskyblock.ovh:domains/uskyblock.ovh/public_html/javadocs/master/po-utils/ + +rsync -r --delete --quiet -e "ssh -p 7685 -o StrictHostKeyChecking=no" \ +$HOME/work/uSkyBlock/uSkyBlock/uSkyBlock-API/target/site/apidocs/ \ +u36810p330294@uskyblock.ovh:domains/uskyblock.ovh/public_html/javadocs/master/uSkyBlock-API/ + +rsync -r --delete --quiet -e "ssh -p 7685 -o StrictHostKeyChecking=no" \ +$HOME/work/uSkyBlock/uSkyBlock/uSkyBlock-APIv2/target/site/apidocs/ \ +u36810p330294@uskyblock.ovh:domains/uskyblock.ovh/public_html/javadocs/master/uSkyBlock-APIv2/ + +rsync -r --delete --quiet -e "ssh -p 7685 -o StrictHostKeyChecking=no" \ +$HOME/work/uSkyBlock/uSkyBlock/uSkyBlock-Core/target/site/apidocs/ \ +u36810p330294@uskyblock.ovh:domains/uskyblock.ovh/public_html/javadocs/master/uSkyBlock-Core/ + +echo -e "Publishing final plugin release...\n" + +rsync -r --quiet -e "ssh -p 7685 -o StrictHostKeyChecking=no" \ +$HOME/work/uSkyBlock/uSkyBlock/uSkyBlock-Plugin/target/uSkyBlock-*.jar \ +u36810p330294@uskyblock.ovh:domains/uskyblock.ovh/public_html/downloads/master/uSkyBlock/ + +rsync -r --quiet --no-R --no-implied-dirs -e "ssh -p 7685 -o StrictHostKeyChecking=no" \ +$HOME/work/uSkyBlock/uSkyBlock/uSkyBlock-Plugin/target/classes/version.json \ +u36810p330294@uskyblock.ovh:domains/uskyblock.ovh/public_html/versions/staging.json diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 000000000..0d30a3531 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,58 @@ +name: Maven build + +on: + push: + branches: [ master ] + tags: [ 'v*' ] + pull_request: + branches: [ master ] + +jobs: + build_and_test: + if: github.repository_owner == 'uskyblock' + runs-on: ubuntu-24.04 + steps: + - name: Checkout repository + uses: actions/checkout@v3 + - name: Checkout submodules + run: | + sed -i 's/git@github.com:/https:\/\/github.com\//' .gitmodules + git submodule update --init --recursive + - name: Generate additional language files + run: | + cd uSkyBlock-Core/src/main/po && perl en2pirate.pl && cd - + cd uSkyBlock-Core/src/main/po && perl en2kitteh.pl && cd - + - name: JDK 21 + uses: actions/setup-java@v4 + with: + java-version: '21' + distribution: 'temurin' + - name: Install gettext + run: sudo apt-get install -y gettext + - name: Build with Maven + run: gradle build + + # Install our SSH key: + - name: Install SSH key + uses: shimataro/ssh-key-action@v2 + if: ${{ github.event_name == 'push' }} + with: + key: ${{ secrets.SSH_PRIVATE_KEY }} + known_hosts: ${{ secrets.SSH_KNOWN_HOST }} + + # Mark our scripts runnable: + - name: Mark deploy scripts runnable + if: ${{ github.event_name == 'push' }} + run: | + chmod +x "${GITHUB_WORKSPACE}/.github/deploy-staging.sh" + chmod +x "${GITHUB_WORKSPACE}/.github/deploy-release.sh" + + # Deploy from master branch (staging release): + - name: Run deploy script for staging release + if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }} + run: "${GITHUB_WORKSPACE}/.github/deploy-staging.sh" + + # Deploy from tag create (plugin release): + - name: Run deploy script for plugin release + if: ${{ github.event_name == 'push' && startsWith( github.ref, 'refs/tags/') }} + run: "${GITHUB_WORKSPACE}/.github/deploy-release.sh" diff --git a/.gitignore b/.gitignore index 355e4d4d2..98e1d962f 100644 --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,6 @@ .settings .classpath bin -/dependency-reduced-pom.xml # Package Files # *.jar *.war @@ -14,5 +13,9 @@ bin **/*.iml **/*~ .idea -**/target -**/*.mo \ No newline at end of file +**/*.mo + +*.DS_Store + +dependency-reduced-pom.xml +deploy_rsa \ No newline at end of file diff --git a/.gitmodules b/.gitmodules index 65af88c30..108b7e38b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "bukkit-utils"] path = bukkit-utils - url = git@github.com:rlf/bukkit-utils.git + url = git@github.com:uskyblock/bukkit-utils.git diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 9c751d96c..000000000 --- a/.travis.yml +++ /dev/null @@ -1,51 +0,0 @@ -language: java -jdk: -- openjdk8 -sudo: false -cache: - directories: - - "$HOME/.m2" -env: - matrix: - - MC=1.15 -git: - submodules: false -notifications: - email: false -before_install: -- sed -i 's/git@github.com:/https:\/\/github.com\//' .gitmodules -- git submodule update --init --recursive -- cd uSkyBlock-Core/src/main/po && perl en2pirate.pl && cd - -- cd uSkyBlock-Core/src/main/po && perl en2kitteh.pl && cd - -install: -- mvn -nsu -Dtravis.buildNumber=${TRAVIS_BUILD_NUMBER} -Pi18n,${MC} clean deploy -before_deploy: - - echo '|1|DBFhltHRAVmrfmMZPLbN7FwnS5E=|79CP65+tOIeVNeNNC2R680mpV9o= ecdsa-sha2-nistp256 - AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBK/krqsEUrVoDrYIc7I7nsiSCF0M7Xxr379IqZ2pLUPlxF2Or/MkTTokXzRsyspJazL1W1UrBDmKXHfO6+tyMMw=' - >> $HOME/.ssh/known_hosts - - openssl aes-256-cbc -K $encrypted_77431b0955a8_key -iv $encrypted_77431b0955a8_iv - -in deploy_rsa.enc -out deploy_rsa -d - - eval "$(ssh-agent -s)" - - chmod 600 deploy_rsa - - ssh-add deploy_rsa - - chmod +x scripts/deploy-staging.sh - - chmod +x scripts/deploy-release.sh -deploy: - - provider: script - skip_cleanup: true - script: bash scripts/deploy-staging.sh - on: - branch: "master" - - provider: script - skip_cleanup: true - script: bash scripts/deploy-release.sh - on: - tags: true -after_success: - - wget https://raw.githubusercontent.com/DiscordHooks/travis-ci-discord-webhook/master/send.sh - - chmod +x send.sh - - ./send.sh success $WEBHOOK_URL -after_failure: - - wget https://raw.githubusercontent.com/DiscordHooks/travis-ci-discord-webhook/master/send.sh - - chmod +x send.sh - - ./send.sh failure $WEBHOOK_URL diff --git a/README.md b/README.md index fe0c1e795..af9930c47 100644 --- a/README.md +++ b/README.md @@ -8,20 +8,44 @@ We are on [Spigot](https://www.spigotmc.org/resources/uskyblock-revived.66795/). This version depends on the following plugins: -* Bukkit/Spigot 1.15.1-R0.1-SNAPSHOT +* Spigot/Paper 1.19-R0.1-SNAPSHOT * Vault 1.7.x -* WorldEdit 7.1.0-SNAPSHOT -* WorldGuard 7.0.2-SNAPSHOT - -## Releases -[![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/rlf/uSkyBlock.svg)](http://isitmaintained.com/project/rlf/uSkyBlock "Average time to resolve an issue") [![Percentage of issues still open](http://isitmaintained.com/badge/open/rlf/uSkyBlock.svg)](http://isitmaintained.com/project/rlf/uSkyBlock "Percentage of issues still open") +* WorldEdit 7.2.13 +* WorldGuard 7.0.8-SNAPSHOT +## Releases https://www.spigotmc.org/resources/uskyblock-revived.66795/history Pre-releases will end in -SNAPSHOT, and is considered **unsafe** for production servers. Releases have a clean version number, has been tested, and should be safe for production servers. +# New Maven group/artifactId +Starting with version 3.0.0-SNAPSHOT, we've changed our Maven groupId's for all submodules except uSkyBlock-API. +If you're using uSkyBlock-Core or po-utils as dependency in your project, update your +dependencies to: + +```xml + + ovh.uskyblock + uSkyBlock-Core + 3.0.0 + +``` + +We're moving new API features towards APIv2, which is available as: + +```xml + + ovh.uskyblock + uSkyBlock-APIv2 + 3.0.0 + +``` + +Feel free to use any of the new APIv2 functions on servers running uSkyBlock 3.0.0+. The old API-methods will +be deprecated and removed in the upcoming plugin releases. + ### Bukkit/Spigot 1.7.9/10 Releases We provide pre-compiled versions (no support) [here](http://rlf.github.io/uSkyBlock): diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 000000000..d75cf5c7c --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,62 @@ +/* + * This file was generated by the Gradle 'init' task. + */ + +plugins { + `maven-publish` + kotlin("jvm") version "2.1.10" +} + +subprojects { + apply(plugin = "kotlin") + dependencies { + api(rootProject) + api(kotlin("script-runtime")) + kotlinScriptDef(rootProject) + } +} + +allprojects { + repositories { + gradlePluginPortal() + mavenLocal() + mavenCentral() + maven("https://hub.spigotmc.org/nexus/content/repositories/public") + maven("https://papermc.io/repo/repository/maven-public/") + maven("https://maven.enginehub.org/repo/") + maven("https://oss.sonatype.org/content/repositories/snapshots/") + maven("https://repo.codemc.org/repository/maven-public") + maven("https://repo.mvdw-software.com/content/groups/public/") + maven("https://www.uskyblock.ovh/maven/dependencies/") + maven("https://www.uskyblock.ovh/maven/uskyblock/") + maven("https://repo.maven.apache.org/maven2/") + maven("https://hub.spigotmc.org/nexus/content/repositories/snapshots/") + + maven("https://repo.onarandombox.com/content/groups/public") + maven("https://hub.spigotmc.org/nexus/content/groups/public/") + maven("https://jitpack.io") + maven("https://repo.minebench.de/") + maven("https://repo.maven.apache.org/maven2/") + } + + java.sourceCompatibility = JavaVersion.VERSION_21 + java.targetCompatibility = JavaVersion.VERSION_21 + kotlin.jvmToolchain(21) + + tasks.withType().configureEach { + enabled = false + } +} + +group = "ovh.uskyblock" +version = "3.2.0-SNAPSHOT" + +publishing { + publications.create("maven") { + from(components["java"]) + } +} + +tasks.withType() { + options.encoding = "UTF-8" +} diff --git a/bukkit-utils b/bukkit-utils deleted file mode 160000 index f4b494106..000000000 --- a/bukkit-utils +++ /dev/null @@ -1 +0,0 @@ -Subproject commit f4b494106b4c748c1f60404a65a62c03165dd162 diff --git a/bukkit-utils/.editorconfig b/bukkit-utils/.editorconfig new file mode 100644 index 000000000..716779f35 --- /dev/null +++ b/bukkit-utils/.editorconfig @@ -0,0 +1,23 @@ +root = true + +[*] +charset = utf-8 +indent_style = space +indent_size = 4 +insert_final_newline = true +trim_trailing_whitespace = true + +[*.java] +end_of_line=lf + +[*.json] +indent_size = 2 + +[*.{yml,yaml}] +indent_size = 2 + +[*.md] +trim_trailing_whitespace = false + +[*.sh] +end_of_line = lf diff --git a/bukkit-utils/.github/workflows/build.yml b/bukkit-utils/.github/workflows/build.yml new file mode 100644 index 000000000..e131b318c --- /dev/null +++ b/bukkit-utils/.github/workflows/build.yml @@ -0,0 +1,42 @@ +name: Maven build + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + build_and_test: + if: github.repository_owner == 'uskyblock' + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v2 + - name: JDK 16 + uses: actions/setup-java@v1 + with: + java-version: '16' + distribution: 'adopt' + - name: Build with Maven + run: gradle build + + # Deploy steps when pushed to master + - name: Install SSH key + if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }} + uses: shimataro/ssh-key-action@v2 + with: + key: ${{ secrets.SSH_PRIVATE_KEY }} + known_hosts: ${{ secrets.SSH_KNOWN_HOST }} + - name: Rsync deploy mvn repo + if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }} + run: | + rsync -r --quiet -e "ssh -p 2222 -o StrictHostKeyChecking=no" \ + target/mvn-repo/ \ + travis@travis.internetpolice.eu:WWW-USB/maven/uskyblock/ + - name: Rsync deploy javadocs + if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }} + run: | + rsync -r --quiet -e "ssh -p 2222 -o StrictHostKeyChecking=no" \ + target/site/apidocs \ + travis@travis.internetpolice.eu:WWW-USB/javadocs/dependencies/bukkit-utils/ diff --git a/bukkit-utils/.gitignore b/bukkit-utils/.gitignore new file mode 100644 index 000000000..520582144 --- /dev/null +++ b/bukkit-utils/.gitignore @@ -0,0 +1,4 @@ +/.idea +**/*.iml +/target +deploy_rsa diff --git a/bukkit-utils/README.md b/bukkit-utils/README.md new file mode 100644 index 000000000..b142362fa --- /dev/null +++ b/bukkit-utils/README.md @@ -0,0 +1,48 @@ +# bukkit-utils + +This module holds general Bukkit Utilities enabling easier Bukkit Plugin creation. + +## Utilities + +* [FileUtil](src/main/java/dk/lockfuglsang/minecraft/file/README.md) - UTF-8, Locale and merging config files from jar +* [YmlConfiguration](src/main/java/dk/lockfuglsang/minecraft/yml/README.md) - Support for comments in yml-files +* [Commands](src/main/java/dk/lockfuglsang/minecraft/command/README.md) - Framework for easy command-creation + +# License + +This module is copyrighted by the authors, and licensed for re-use as Apache License 2.0. + +# Usage + +Put this in your `pom.xml`: + +``` + + + uSkyBlock-mvn-repo + https://raw.github.com/rlf/mvn-repo/master + + + + + dk.lockfuglsang.minecraft + bukkit-utils + 1.22 + + +``` + +# Version History + +## 1.22 - Bukkit 1.13 compatible + +## 1.21 - Bukkit 1.12 compatible + +## v1.1 + +* FileUtil, I18nUtil + +## v1.0 +Initial release, extracted from the source-code used in uSkyBlock + +* YmlConfiguration, Commands \ No newline at end of file diff --git a/bukkit-utils/build.gradle.kts b/bukkit-utils/build.gradle.kts new file mode 100644 index 000000000..f8a731090 --- /dev/null +++ b/bukkit-utils/build.gradle.kts @@ -0,0 +1,28 @@ +/* + * This file was generated by the Gradle 'init' task. + */ + +dependencies { + implementation(project(":po-utils")) + implementation("org.spigotmc:spigot-api:1.20.6-R0.1-SNAPSHOT") + testImplementation("org.hamcrest:hamcrest-core:1.3") + testImplementation("org.hamcrest:hamcrest-library:1.3") + testImplementation("junit:junit:4.13.2") + testImplementation("org.mockito:mockito-core:5.14.2") + testImplementation("com.google.code.gson:gson:2.8.7") +} + +description = "bukkit-utils" + +val testsJar by tasks.registering(Jar::class) { + archiveClassifier.set("tests") + from(sourceSets["test"].output) +} + +configurations { + create("testsJar") { + isCanBeConsumed = true + isCanBeResolved = false + outgoing.artifact(testsJar) + } +} diff --git a/bukkit-utils/src/main/java/dk/lockfuglsang/minecraft/animation/Animation.java b/bukkit-utils/src/main/java/dk/lockfuglsang/minecraft/animation/Animation.java new file mode 100644 index 000000000..42b643ee9 --- /dev/null +++ b/bukkit-utils/src/main/java/dk/lockfuglsang/minecraft/animation/Animation.java @@ -0,0 +1,14 @@ +package dk.lockfuglsang.minecraft.animation; + +import org.bukkit.entity.Player; + +/** + * Common interface for animations + */ +public interface Animation { + boolean show(); + + boolean hide(); + + Player getPlayer(); +} diff --git a/bukkit-utils/src/main/java/dk/lockfuglsang/minecraft/animation/AnimationHandler.java b/bukkit-utils/src/main/java/dk/lockfuglsang/minecraft/animation/AnimationHandler.java new file mode 100644 index 000000000..47f13220c --- /dev/null +++ b/bukkit-utils/src/main/java/dk/lockfuglsang/minecraft/animation/AnimationHandler.java @@ -0,0 +1,116 @@ +package dk.lockfuglsang.minecraft.animation; + +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.plugin.Plugin; +import org.bukkit.scheduler.BukkitRunnable; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Handles particles and per-player block-animations + */ +public class AnimationHandler { + private final Map> animations = new ConcurrentHashMap<>(); + private final Map animationTasks = new ConcurrentHashMap<>(); + private final Plugin plugin; + + private int animTick; + + public AnimationHandler(Plugin plugin) { + this.plugin = plugin; + animTick = plugin.getConfig().getInt("animations.tick", 20); + } + + public void setAnimTick(int animTick) { + this.animTick = animTick; + plugin.getConfig().set("animations.tick", animTick); + } + + public synchronized void addAnimation(Animation animation) { + if (!animations.containsKey(animation.getPlayer().getUniqueId())) { + animations.put(animation.getPlayer().getUniqueId(), new HashSet()); + } + animations.get(animation.getPlayer().getUniqueId()).add(animation); + start(); + } + + public synchronized boolean removeAnimations(Player player) { + Set animSet = animations.remove(player.getUniqueId()); + if (animSet == null) { + return false; + } + for (Animation animation : animSet) { + animation.hide(); + } + return true; + } + + public synchronized void start() { + for (UUID uuid : animations.keySet()) { + AnimationTask animationTask = animationTasks.get(uuid); + if (animationTask == null && animations.get(uuid) != null && !animations.get(uuid).isEmpty()) { + animationTask = new AnimationTask(uuid); + animationTask.runTaskTimerAsynchronously(plugin, 0, animTick); + animationTasks.put(uuid, animationTask); + } + } + } + + public synchronized void stop() { + for (UUID uuid : animations.keySet()) { + AnimationTask animationTask = animationTasks.get(uuid); + if (animationTask != null) { + animationTask.cancel(); + animationTasks.remove(uuid); + } + } + if (plugin.isEnabled()) { + Bukkit.getScheduler().runTaskAsynchronously(plugin, new Runnable() { + @Override + public void run() { + Collection> anims = new ArrayList<>(animations.values()); + for (Set animSet : anims) { + for (Animation animation : animSet) { + animation.hide(); + } + } + } + }); + } + } + + private class AnimationTask extends BukkitRunnable { + private final UUID uniqueId; + public AnimationTask(UUID uniqueId) { + this.uniqueId = uniqueId; + } + + @Override + public void run() { + // Copy - to avoid ConcurrentModificationException + Set animSet = animations.get(uniqueId); + Set animCopy = (animSet != null) ? new HashSet<>(animSet) : Collections.emptySet(); + for (Animation animation : animCopy) { + if (!animation.show()) { + UUID uuid = animation.getPlayer().getUniqueId(); + animations.get(uuid).remove(animation); + if (animations.get(uuid).isEmpty()) { + animations.remove(uuid); + } + } + } + if (animations.get(uniqueId) == null) { + cancel(); + animationTasks.remove(uniqueId); + } + } + } +} diff --git a/bukkit-utils/src/main/java/dk/lockfuglsang/minecraft/animation/BlockAnimation.java b/bukkit-utils/src/main/java/dk/lockfuglsang/minecraft/animation/BlockAnimation.java new file mode 100644 index 000000000..1f27df819 --- /dev/null +++ b/bukkit-utils/src/main/java/dk/lockfuglsang/minecraft/animation/BlockAnimation.java @@ -0,0 +1,54 @@ +package dk.lockfuglsang.minecraft.animation; + +import org.bukkit.Location; +import org.bukkit.block.data.BlockData; +import org.bukkit.entity.Player; + +import java.util.List; + +/** + * Sends (bogus) block-info to the player + */ +public class BlockAnimation implements Animation { + private final Player player; + private final List points; + private final BlockData blockData; + private volatile boolean shown; + + public BlockAnimation(Player player, List points, BlockData blockData) { + this.player = player; + this.points = points; + this.blockData = blockData; + shown = false; + } + + @Override + public boolean show() { + if (shown) { + return true; + } + if (!player.isOnline()) { + return false; + } + for (Location loc : points) { + player.sendBlockChange(loc, blockData); + } + shown = true; + return true; + } + + @Override + public boolean hide() { + if (shown) { + shown = false; + player.sendBlockChanges(points.stream().map(loc -> loc.getBlock().getState()).toList()); + return true; + } + return false; + } + + @Override + public Player getPlayer() { + return player; + } +} diff --git a/bukkit-utils/src/main/java/dk/lockfuglsang/minecraft/command/AbstractCommand.java b/bukkit-utils/src/main/java/dk/lockfuglsang/minecraft/command/AbstractCommand.java new file mode 100644 index 000000000..a86d973ea --- /dev/null +++ b/bukkit-utils/src/main/java/dk/lockfuglsang/minecraft/command/AbstractCommand.java @@ -0,0 +1,121 @@ +package dk.lockfuglsang.minecraft.command; + +import dk.lockfuglsang.minecraft.po.I18nUtil; +import org.bukkit.command.CommandSender; +import org.bukkit.command.TabCompleter; +import org.bukkit.entity.Player; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.UUID; + +/** + * Convenience implementation of the Command + */ +public abstract class AbstractCommand implements Command { + private final String[] aliases; + private final String permission; + private final String description; + private final String usage; + private final String[] params; + private CompositeCommand parent; + private final Map featurePerms = new HashMap<>(); + private final Set permissionOverride; + + public AbstractCommand(String name, String permission, String params, String description, String usage, UUID... permissionOverride) { + this.aliases = name.split("\\|"); + this.permission = permission; + this.description = I18nUtil.tr(description); + this.usage = I18nUtil.tr(usage); + this.params = params != null && !params.trim().isEmpty() ? params.split(" ") : new String[0]; + this.permissionOverride = new HashSet<>(Arrays.asList(permissionOverride)); + } + + public AbstractCommand(String name, String permission, String params, String description) { + this(name, permission, params, description, null); + } + + public AbstractCommand(String name, String permission, String description) { + this(name, permission, null, description, null); + } + + public AbstractCommand(String name, String description) { + this(name, null, null, description, null); + } + + @Override + public String getName() { + return aliases[0]; + } + + public String[] getAliases() { + return aliases; + } + + @Override + public String getPermission() { + return permission; + } + + @Override + public String getDescription() { + return description; + } + + @Override + public String getUsage() { + return usage; + } + + @Override + public String[] getParams() { + return params; + } + + @Override + public TabCompleter getTabCompleter() { + return null; + } + + @Override + public CompositeCommand getParent() { + return parent; + } + + @Override + public void setParent(CompositeCommand parent) { + this.parent = parent; + } + + @Override + public void accept(CommandVisitor visitor) { + if (visitor != null) { + visitor.visit(this); + } + } + + public void addFeaturePermission(String perm, String description) { + featurePerms.put(perm, description); + } + + @Override + public Map getFeaturePermissions() { + return Collections.unmodifiableMap(featurePerms); + } + + public boolean hasPermissionOverride(CommandSender sender) { + if (sender instanceof Player) { + return permissionOverride.contains(((Player) sender).getUniqueId()) || (parent != null && parent.hasPermissionOverride(sender)); + } + return false; + } + + @Override + public boolean hasPermission(CommandSender sender, String permission) { + return hasPermissionOverride(sender) || sender.hasPermission(permission); + } +} diff --git a/bukkit-utils/src/main/java/dk/lockfuglsang/minecraft/command/BaseCommandExecutor.java b/bukkit-utils/src/main/java/dk/lockfuglsang/minecraft/command/BaseCommandExecutor.java new file mode 100644 index 000000000..26fc68308 --- /dev/null +++ b/bukkit-utils/src/main/java/dk/lockfuglsang/minecraft/command/BaseCommandExecutor.java @@ -0,0 +1,46 @@ +package dk.lockfuglsang.minecraft.command; + +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; + +import java.util.HashMap; +import java.util.UUID; + +import static dk.lockfuglsang.minecraft.po.I18nUtil.tr; + +/** + * Command delegator. + */ +public class BaseCommandExecutor extends CompositeCommand implements CommandExecutor { + + public BaseCommandExecutor(String name, String permission, String description) { + super(name, permission, description); + } + + public BaseCommandExecutor(String name, String permission, String params, String description) { + super(name, permission, params, description); + } + public BaseCommandExecutor(String name, String permission, String params, String description, UUID... permissionOverrides) { + super(name, permission, params, description, permissionOverrides); + } + + @Override + public boolean onCommand(CommandSender sender, Command command, String alias, String[] args) { + if (!CommandManager.isRequirementsMet(sender, this, args)) { + return true; + } + dk.lockfuglsang.minecraft.command.Command cmd = this; + if (!hasAccess(cmd, sender)) { + if (cmd != null) { + sender.sendMessage(tr("\u00a7eYou do not have access (\u00a74{0}\u00a7e)", cmd.getPermission())); + } else { + sender.sendMessage(tr("\u00a7eInvalid command: {0}", alias)); + } + showUsage(sender, 1); + } else { + return execute(sender, alias, new HashMap(), args); + } + return true; + } +} diff --git a/bukkit-utils/src/main/java/dk/lockfuglsang/minecraft/command/Command.java b/bukkit-utils/src/main/java/dk/lockfuglsang/minecraft/command/Command.java new file mode 100644 index 000000000..58c21b2a2 --- /dev/null +++ b/bukkit-utils/src/main/java/dk/lockfuglsang/minecraft/command/Command.java @@ -0,0 +1,89 @@ +package dk.lockfuglsang.minecraft.command; + +import org.bukkit.command.CommandSender; +import org.bukkit.command.TabCompleter; + +import java.util.Map; + +/** + * An abstraction for supporting nesting of commands. + * This is a light-weight version of the BukkitCommand abstraction. + */ +public interface Command { + /** + * Returns the name of the sub-command. + */ + String getName(); + + /** + * The permission of the command. Can be null. + */ + String getPermission(); + + /** + * A short description of the sub-command. + * Used when listing the commands with others. + */ + String getDescription(); + + /** + * A more verbatim description of the command. + * Used when /command help is executed. + */ + String getUsage(); + + /** + * The list of parameters accepted by the command. + * Can be empty, not null. + */ + String[] getParams(); + + /** + * Returns aliases for the command. + * Can be empty, cannot be null. + */ + String[] getAliases(); + + /** + * Executes the command. + */ + boolean execute(CommandSender sender, String alias, Map data, String... args); + + /** + * Optional TabCompleter to override the default ones. + * Can be null + */ + TabCompleter getTabCompleter(); + + /** + * Returns the parent command (if one such is available). + * May return null. + */ + CompositeCommand getParent(); + + /** + * Assigns a parent command. + * @param parent + */ + void setParent(CompositeCommand parent); + + /** + * Visitor pattern. + * @param visitor A visitor for this node. + */ + void accept(CommandVisitor visitor); + + /** + * Returns a map of feature-toggling permissions supporte by this command. + * @return A map of permission-node as key, and description as value. + */ + Map getFeaturePermissions(); + + /** + * Allows for other than permission checking + * @param sender The commandSender requesting permission + * @param permission The permission + * @return true if the sender can execute this command + */ + boolean hasPermission(CommandSender sender, String permission); +} diff --git a/bukkit-utils/src/main/java/dk/lockfuglsang/minecraft/command/CommandComparator.java b/bukkit-utils/src/main/java/dk/lockfuglsang/minecraft/command/CommandComparator.java new file mode 100644 index 000000000..e015dd07b --- /dev/null +++ b/bukkit-utils/src/main/java/dk/lockfuglsang/minecraft/command/CommandComparator.java @@ -0,0 +1,13 @@ +package dk.lockfuglsang.minecraft.command; + +import java.util.Comparator; + +/** + * Comparator for sorting sub-commands + */ +public class CommandComparator implements Comparator { + @Override + public int compare(Command o1, Command o2) { + return o1.getName().compareTo(o2.getName()); + } +} diff --git a/bukkit-utils/src/main/java/dk/lockfuglsang/minecraft/command/CommandManager.java b/bukkit-utils/src/main/java/dk/lockfuglsang/minecraft/command/CommandManager.java new file mode 100644 index 000000000..6d345ef8c --- /dev/null +++ b/bukkit-utils/src/main/java/dk/lockfuglsang/minecraft/command/CommandManager.java @@ -0,0 +1,31 @@ +package dk.lockfuglsang.minecraft.command; + +import org.bukkit.command.CommandSender; + +/** + * Static singleton manager for controlling common requirements. + */ +public enum CommandManager {; + private static RequirementChecker checker; + + public static void registerRequirements(RequirementChecker reqs) { + checker = reqs; + } + + public static boolean isRequirementsMet(CommandSender sender, Command command, String... args) { + if (checker != null) { + return checker.isRequirementsMet(sender, command, args); + } + return true; + } + + public interface RequirementChecker { + /** + * Checks whether the requirements for the command has been met. + * @param sender A sender to send detailed feedback to. + * @param args + * @return true iff the command can proceed. + */ + boolean isRequirementsMet(CommandSender sender, Command command, String... args); + } +} diff --git a/bukkit-utils/src/main/java/dk/lockfuglsang/minecraft/command/CommandVisitor.java b/bukkit-utils/src/main/java/dk/lockfuglsang/minecraft/command/CommandVisitor.java new file mode 100644 index 000000000..e0dc42910 --- /dev/null +++ b/bukkit-utils/src/main/java/dk/lockfuglsang/minecraft/command/CommandVisitor.java @@ -0,0 +1,8 @@ +package dk.lockfuglsang.minecraft.command; + +/** + * Simple visitor for the USBCommands + */ +public interface CommandVisitor { + void visit(Command cmd); +} diff --git a/bukkit-utils/src/main/java/dk/lockfuglsang/minecraft/command/CompositeCommand.java b/bukkit-utils/src/main/java/dk/lockfuglsang/minecraft/command/CompositeCommand.java new file mode 100644 index 000000000..0c18f174c --- /dev/null +++ b/bukkit-utils/src/main/java/dk/lockfuglsang/minecraft/command/CompositeCommand.java @@ -0,0 +1,365 @@ +package dk.lockfuglsang.minecraft.command; + +import dk.lockfuglsang.minecraft.command.completion.AbstractTabCompleter; +import org.bukkit.command.CommandSender; +import org.bukkit.command.TabCompleter; +import org.bukkit.entity.Player; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.stream.Collectors; + +import static dk.lockfuglsang.minecraft.po.I18nUtil.tr; + +/** + * Command with nested commandMap inside. + */ +public class CompositeCommand extends AbstractTabCompleter implements Command, TabCompleter { + + public static final String HELP_PATTERN = "(?iu)help|\\?"; + private static final int MAX_PER_PAGE = 10; + + private final String name; + private final String[] aliases; + private final String permission; + private final String description; + private final String[] params; + private CompositeCommand parent; + private final Map commandMap; + private final Map aliasMap; + private final Map tabMap; + private final Map featurePerms; + private final Set permissionOverride; + + public CompositeCommand(String name, String permission, String description) { + this(name, permission, null, description); + } + + public CompositeCommand(String name, String permission, String params, String description, UUID... permissionOverride) { + this.aliases = name.split("\\|"); + this.name = aliases[0]; + this.permission = permission; + this.description = description; + this.params = params != null ? params.split(" ") : new String[0]; + commandMap = new HashMap<>(); + aliasMap = new HashMap<>(); + tabMap = new HashMap<>(); + featurePerms = new HashMap<>(); + this.permissionOverride = new HashSet<>(Arrays.asList(permissionOverride)); + } + + public CompositeCommand add(Command... cmds) { + for (Command cmd : cmds) { + commandMap.put(cmd.getName(), cmd); + for (String alias : cmd.getAliases()) { + aliasMap.put(alias, cmd); + } + cmd.setParent(this); + } + return this; + } + + public CompositeCommand addTab(String arg, TabCompleter tab) { + tabMap.put(arg, tab); + return this; + } + + @Override + public String getName() { + return name; + } + + @Override + public String[] getAliases() { + return aliases; + } + + @Override + public String getPermission() { + return permission != null && !permission.isEmpty() ? permission : null; + } + + @Override + public String getDescription() { + return description; + } + + @Override + public String getUsage() { + return null; + } + + @Override + public String[] getParams() { + return params; + } + + @Override + public boolean execute(CommandSender sender, String alias, Map data, String... args) { + if (!CommandManager.isRequirementsMet(sender, this, args)) { + return true; + } + if (args.length == 0 || (args.length == 1 && args[0].matches(HELP_PATTERN))) { + showUsage(sender, 1); + } else if (args.length > 1 && args[0].matches(HELP_PATTERN)) { + showUsage(sender, args[1]); + } else if (args.length > params.length && hasAccess(this, sender)) { + String cmdName = args[params.length].toLowerCase(); + Command cmd = aliasMap.get(cmdName); + String[] subArgs = new String[args.length - 1 - params.length]; + System.arraycopy(args, 1 + params.length, subArgs, 0, subArgs.length); + int ix = 0; + for (String p : params) { + data.put(p, args[ix++]); + } + if (!hasAccess(cmd, sender)) { + if (cmd != null) { + sender.sendMessage(tr("\u00a7eYou do not have access (\u00a74{0}\u00a7e)", cmd.getPermission())); + } else { + sender.sendMessage(tr("\u00a7eInvalid command: {0}", cmdName)); + } + showUsage(sender, args[0]); + } else if (!cmd.execute(sender, cmdName, data, subArgs)) { + showUsage(sender, args[0]); + } + } else { + showUsage(sender, 1); + } + return true; + } + + public void showUsage(CommandSender sender, int page) { + String msg = tr("\u00a77Usage: {0}", getShortDescription(sender, this)); + if (!hasAccess(this, sender)) { + sender.sendMessage(msg.split("\n")); + return; + } + if (getUsage() != null && !getUsage().isEmpty()) { + msg += "\u00a77" + getUsage(); + } + List cmds = new ArrayList<>(commandMap.keySet()); + Collections.sort(cmds); + cmds = cmds.stream().filter(f -> hasAccess(commandMap.get(f), sender)).collect(Collectors.toList()); + int realPage = 0; + int maxPage = 0; + if (cmds.size() > MAX_PER_PAGE) { + msg = msg.substring(0, msg.length() - 1); // Remove \n + maxPage = (int) Math.round(Math.ceil(cmds.size() * 1f / MAX_PER_PAGE)); + realPage = Math.max(1, Math.min(maxPage, page)); + msg += " \u00a77[" + realPage + "/" + maxPage + "]\n"; + cmds = cmds.subList((realPage - 1) * MAX_PER_PAGE, Math.min(realPage * MAX_PER_PAGE, cmds.size())); + } + for (String key : cmds) { + Command cmd = commandMap.get(key); + if (hasAccess(cmd, sender)) { + msg += " " + getShortDescription(sender, cmd); + } + } + if (realPage > 0 && maxPage > realPage) { + msg += tr("\u00a77Use \u00a73/{0} ? {1}\u00a77 to display next page\n", getName(), (realPage + 1)); + } else if (realPage > 0 && maxPage == realPage) { + msg += tr("\u00a77Use \u00a73/{0} ? {1} \u00a77 to display previous page\n", getName(), (realPage - 1)); + } + sender.sendMessage(msg.split("\n")); + } + + protected void showUsage(CommandSender sender, String arg) { + String cmdName = arg.toLowerCase(); + Command cmd = cmdName.isEmpty() ? this : aliasMap.get(cmdName); + if (cmd != null && hasAccess(cmd, sender)) { + String msg = tr("\u00a77Usage: {0}", name) + " \u00a7e"; + msg += getShortDescription(sender, cmd); + if (cmd.getUsage() != null && !cmd.getUsage().isEmpty()) { + msg += "\u00a77" + cmd.getUsage(); + } + sender.sendMessage(msg.split("\n")); + } else if (cmdName.matches("[0-9]+")) { + showUsage(sender, Integer.parseInt(cmdName)); + } else { + List cmds = filter(new ArrayList<>(aliasMap.keySet()), cmdName); + if (cmds.isEmpty()) { + showUsage(sender, 1); + } else { + String msg = tr("\u00a77Usage: {0}", getShortDescription(sender, this)); + if (hasAccess(this, sender)) { + for (String key : cmds) { + Command scmd = commandMap.get(key); + if (scmd != null && hasAccess(scmd, sender)) { + msg += " " + getShortDescription(sender, scmd); + } + } + } + sender.sendMessage(msg.split("\n")); + } + } + } + + private String getShortDescription(CommandSender sender, Command cmd) { + String msg = "\u00a73" + cmd.getName(); + String[] aliases = cmd.getAliases(); + if (aliases.length > 1) { + msg += "\u00a77"; + for (int i = 1; i < aliases.length; i++) { + msg += " | " + aliases[i]; + } + } + msg += "\u00a7a"; + msg += getParamsAsString(cmd); + if (cmd instanceof CompositeCommand) { + msg += " [command|help]"; + } + msg += "\u00a77 - \u00a7e"; + msg += cmd.getDescription(); + if (sender.isOp() && cmd.getPermission() != null) { + msg += " \u00a7c(" + cmd.getPermission() + ")"; + } + msg += "\n"; + return msg; + } + + public static String getParamsAsString(Command cmd) { + String msg = ""; + for (String param : cmd.getParams()) { + if (param.startsWith("?")) { + msg += " [" + param.substring(1) + "]"; + } else { + msg += " <" + param + ">"; + } + } + return msg; + } + + public boolean hasAccess(Command cmd, CommandSender sender) { + return cmd != null && (cmd.getPermission() == null || cmd.hasPermission(sender, cmd.getPermission())); + } + + @Override + protected List getTabList(CommandSender commandSender, String term) { + ArrayList strings = new ArrayList<>(); + if (!hasAccess(this, commandSender)) { + return strings; + } + for (Command cmd : commandMap.values()) { + if (hasAccess(cmd, commandSender)) { + strings.addAll(Arrays.asList(cmd.getAliases())); + } + } + strings.add("help"); + return strings; + } + + protected TabCompleter getTabCompleter(Command cmd, int argNum) { + argNum = argNum >= 0 ? argNum : 0; + if (cmd.getParams().length > argNum) { + String paramName = cmd.getParams()[argNum]; + if (paramName != null && paramName.startsWith("?")) { + paramName = paramName.substring(1); + } + if (tabMap.containsKey(paramName)) { + return tabMap.get(paramName); + } else if (getParent() != null) { + TabCompleter tab = getParent().getTabCompleter(cmd, argNum); + if (tab != null) { + return tab; + } + } + } + if (cmd.getTabCompleter() != null) { + return cmd.getTabCompleter(); + } + return null; + } + + @Override + public List onTabComplete(CommandSender sender, org.bukkit.command.Command command, String alias, String[] args) { + if (args.length <= params.length && args.length > 0) { + TabCompleter tab = getTabCompleter(this, args.length - 1); + if (tab != null && tab != this) { + return tab.onTabComplete(sender, command, alias, args); + } else if (tab == this) { + return getTabList(sender, args[args.length - 1]); + } + } else if (args.length > params.length + 1) { // Sub-commands + String cmdName = args[params.length].toLowerCase(); + Command cmd = aliasMap.get(cmdName); + if (cmd != null && (args.length - params.length) > 1) { // Go deeper + String[] subArgs = new String[args.length - 1 - params.length]; + System.arraycopy(args, 1 + params.length, subArgs, 0, subArgs.length); + TabCompleter tab = getTabCompleter(cmd, subArgs.length - 1); + if (tab != null) { + return tab.onTabComplete(sender, command, alias, subArgs); + } + } else { + return super.onTabComplete(sender, command, alias, args); + } + } else { + return super.onTabComplete(sender, command, alias, args); + } + return Collections.emptyList(); + } + + @Override + public TabCompleter getTabCompleter() { + return this; + } + + @Override + public CompositeCommand getParent() { + return parent; + } + + @Override + public void setParent(CompositeCommand parent) { + this.parent = parent; + } + + public List getChildren() { + ArrayList list = new ArrayList<>(commandMap.values()); + Collections.sort(list, new CommandComparator()); + return Collections.unmodifiableList(list); + } + + @Override + public void accept(CommandVisitor visitor) { + if (visitor != null) { + visitor.visit(this); + for (Command child : getChildren()) { + child.accept(visitor); + } + } + } + + public void addFeaturePermission(String perm, String description) { + featurePerms.put(perm, description); + } + + @Override + public Map getFeaturePermissions() { + return Collections.unmodifiableMap(featurePerms); + } + + private boolean hasPermissionOverride(UUID uuid) { + return permissionOverride.contains(uuid) || (getParent() != null && getParent().hasPermissionOverride(uuid)); + } + + public boolean hasPermissionOverride(CommandSender sender) { + if (sender instanceof Player) { + return hasPermissionOverride(((Player) sender).getUniqueId()); + } + return false; + } + + public boolean hasPermission(CommandSender sender, String permission) { + if (sender instanceof Player) { + return hasPermissionOverride(((Player) sender).getUniqueId()) || sender.hasPermission(permission); + } + return sender.hasPermission(permission); + } +} diff --git a/bukkit-utils/src/main/java/dk/lockfuglsang/minecraft/command/DocumentCommand.java b/bukkit-utils/src/main/java/dk/lockfuglsang/minecraft/command/DocumentCommand.java new file mode 100644 index 000000000..f1b95699e --- /dev/null +++ b/bukkit-utils/src/main/java/dk/lockfuglsang/minecraft/command/DocumentCommand.java @@ -0,0 +1,80 @@ +package dk.lockfuglsang.minecraft.command; + +import dk.lockfuglsang.minecraft.command.completion.AbstractTabCompleter; +import org.bukkit.command.CommandSender; +import org.bukkit.command.PluginCommand; +import org.bukkit.command.TabCompleter; +import org.bukkit.plugin.java.JavaPlugin; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import static dk.lockfuglsang.minecraft.po.I18nUtil.marktr; +import static dk.lockfuglsang.minecraft.po.I18nUtil.tr; + +/** + * Command that traverses all the commands in a plugin, and generates documentation for them. + */ +public class DocumentCommand extends AbstractCommand { + public static final List FORMATS = Arrays.asList("text", "yml"); + private final JavaPlugin plugin; + private TabCompleter tabCompleter; + + public DocumentCommand(JavaPlugin plugin, String name, String permission) { + super(name, permission, "?format ?arg", marktr("saves documentation of the commands to a file")); + this.plugin = plugin; + tabCompleter = new AbstractTabCompleter() { + @Override + protected List getTabList(CommandSender commandSender, String term) { + return FORMATS; + } + }; + } + + @Override + public boolean execute(CommandSender sender, String alias, Map data, String... args) { + if (args.length == 0 || args[0].equalsIgnoreCase("text")) { + int lineWidth = args.length > 1 && args[1].matches("\\d+") ? Integer.parseInt(args[1], 10) : 130; + return writeToFile(sender, new PlainTextCommandVisitor(lineWidth), getName() + ".txt"); + } else if (args[0].equalsIgnoreCase("yml")) { + int groupDepth = args.length > 1 && args[1].matches("\\d+") ? Integer.parseInt(args[1], 10) : 2; + return writeToFile(sender, new PluginYamlCommandVisitor(groupDepth), getName() + ".yml"); + } + return false; + } + + private boolean writeToFile(CommandSender sender, DocumentWriter visitor, String filename) { + List commands = new ArrayList<>(plugin.getDescription().getCommands().keySet()); + Collections.sort(commands); + for (String cmd : commands) { + PluginCommand pluginCommand = plugin.getCommand(cmd); + if (pluginCommand.getExecutor() instanceof Command) { + ((Command) pluginCommand.getExecutor()).accept(visitor); + } + } + File docFile = new File(plugin.getDataFolder(), filename); + try (FileOutputStream fos = new FileOutputStream(docFile); + PrintStream ps = new PrintStream(fos, true, StandardCharsets.UTF_8)) + { + visitor.writeTo(ps); + sender.sendMessage(tr("Wrote documentation to {0}", docFile)); + return true; + } catch (IOException e) { + sender.sendMessage(tr("\u00a74Error writing documentation: {0}", e.getMessage())); + } + return false; + } + + @Override + public TabCompleter getTabCompleter() { + return tabCompleter; + } +} diff --git a/bukkit-utils/src/main/java/dk/lockfuglsang/minecraft/command/DocumentWriter.java b/bukkit-utils/src/main/java/dk/lockfuglsang/minecraft/command/DocumentWriter.java new file mode 100644 index 000000000..e26cf2d5d --- /dev/null +++ b/bukkit-utils/src/main/java/dk/lockfuglsang/minecraft/command/DocumentWriter.java @@ -0,0 +1,10 @@ +package dk.lockfuglsang.minecraft.command; + +import java.io.PrintStream; + +/** + * Common interface for the DocumentCommand-visitors + */ +public interface DocumentWriter extends CommandVisitor { + void writeTo(PrintStream ps); +} diff --git a/bukkit-utils/src/main/java/dk/lockfuglsang/minecraft/command/PlainTextCommandVisitor.java b/bukkit-utils/src/main/java/dk/lockfuglsang/minecraft/command/PlainTextCommandVisitor.java new file mode 100644 index 000000000..16b6b48d2 --- /dev/null +++ b/bukkit-utils/src/main/java/dk/lockfuglsang/minecraft/command/PlainTextCommandVisitor.java @@ -0,0 +1,66 @@ +package dk.lockfuglsang.minecraft.command; + +import dk.lockfuglsang.minecraft.util.FormatUtil; + +import java.io.PrintStream; +import java.util.List; + +import static dk.lockfuglsang.minecraft.po.I18nUtil.tr; + +/** + * Simple visitor for generating plain-text documentation of an Command-hierarchy. + */ +public class PlainTextCommandVisitor extends RowCommandVisitor implements DocumentWriter { + private final int linewidth; + + public PlainTextCommandVisitor(int linewidth) { + this.linewidth = linewidth; + } + + public void writeTo(PrintStream out) { + int[] colWidths = new int[3]; + for (Row row : getRows()) { + if (row != null) { + if (row.getCommand().length() > colWidths[0]) { + colWidths[0] = row.getCommand().length(); + } + if (row.getPermission().length() > colWidths[1]) { + colWidths[1] = row.getPermission().length(); + } + if (row.getDescription().length() > colWidths[2]) { + colWidths[2] = row.getDescription().length(); + } + } + } + colWidths[0]++; // make room for the '/' + if (colWidths[0] + colWidths[1] + colWidths[2] > linewidth && colWidths[0] + colWidths[1] < linewidth) { + // truncate description column + colWidths[2] = linewidth - colWidths[0] - colWidths[1]; + } + String rowFormat = ""; + String separator = ""; + for (int i = 0; i < colWidths.length; i++) { + if (i != 0) { + rowFormat += " | "; + separator += "-+-"; + } + rowFormat += "%-" + colWidths[i] + "s"; + separator += String.format("%" + colWidths[i] + "s", "").replaceAll(" ", "-"); + } + out.println(String.format(rowFormat, tr("Command"), tr("Permission"), tr("Description"))); + for (Row row : getRows()) { + if (row == null) { + out.println(separator); + } else { + String cmd = row.getCommand().isEmpty() ? "" : "/" + row.getCommand(); + String description = row.getDescription(); + List strings = FormatUtil.wordWrapStrict(description, colWidths[2]); + out.println(String.format(rowFormat, cmd, row.getPermission(), strings.size() > 0 ? strings.get(0) : "")); + for (int i = 1; i < strings.size(); i++) { + out.println(String.format(rowFormat, "", "", strings.get(i))); + } + } + } + } + +} diff --git a/bukkit-utils/src/main/java/dk/lockfuglsang/minecraft/command/PluginYamlCommandVisitor.java b/bukkit-utils/src/main/java/dk/lockfuglsang/minecraft/command/PluginYamlCommandVisitor.java new file mode 100644 index 000000000..2b34d29d6 --- /dev/null +++ b/bukkit-utils/src/main/java/dk/lockfuglsang/minecraft/command/PluginYamlCommandVisitor.java @@ -0,0 +1,266 @@ +package dk.lockfuglsang.minecraft.command; + +import org.bukkit.command.CommandExecutor; + +import java.io.PrintStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +/** + * Generates a yml-file following the syntax define in http://wiki.bukkit.org/Plugin_YAML + * @since 1.8 + */ +public class PluginYamlCommandVisitor implements DocumentWriter { + private final int groupDepth; + + List topLevel = new ArrayList<>(); + PermissionNode rootNode; + + PluginYamlCommandVisitor() { + this(2); + } + + PluginYamlCommandVisitor(int groupDepth) { + this.groupDepth = groupDepth; + rootNode = new PermissionNode(""); + } + + public void writeTo(PrintStream out) { + out.println("commands:"); + for (Command cmd : topLevel) { + out.println(" " + cmd.getName() + ":"); + out.println(" description: '" + cmd.getDescription().replaceAll("'", "''") + "'"); + if (cmd.getAliases().length > 1) { + String[] copy = new String[cmd.getAliases().length-1]; + System.arraycopy(cmd.getAliases(), 1, copy, 0, copy.length); + out.println(" aliases: " + Arrays.toString(copy)); + } + if (cmd.getPermission() != null && !cmd.getPermission().isEmpty()) { + out.println(" permission: " + cmd.getPermission()); + } + } + out.println("permissions:"); + out.println(" #"); + out.println(" # Permission Groups"); + out.println(" # ================="); + for (PermissionNode node : rootNode.getRoots()) { + if (!node.permission.isEmpty()) { + if (node.permission.split("\\.").length > groupDepth) { + continue; // Skip + } + out.println(" " + node.permission + ".*:"); + out.println(" children:"); + for (String p : node.getChildPermissions()) { + out.println(" " + p + ": true"); + } + out.println(); + } + } + out.println(" #"); + out.println(" # Permission Descriptions"); + out.println(" # ======================="); + for (PermissionNode node : rootNode.getLeafs()) { + if (!node.permission.isEmpty()) { + out.println(" " + node.permission + ":"); + String desc = node.getDescription(); + if (desc != null) { + out.println(" description: " + desc); + } + out.println(); + } + } + } + + private static String getCmdDescription(Command cmd) { + return "/" + getCommandPath(cmd) + " - " + cmd.getDescription(); + } + + private static String getCommandPath(Command cmd) { + String path = cmd.getParent() != null ? getCommandPath(cmd.getParent()) + " " : ""; + return path + cmd.getName() + CompositeCommand.getParamsAsString(cmd); + } + + @Override + public void visit(Command cmd) { + if (cmd instanceof CommandExecutor) { + topLevel.add(cmd); + } + String permission = getPermission(cmd); + rootNode.add(permission, cmd); + for (Map.Entry entry : cmd.getFeaturePermissions().entrySet()) { + rootNode.add(entry.getKey(), entry.getValue()); + } + } + + private String getPermission(Command cmd) { + if (cmd.getPermission() == null && cmd.getParent() != null) { + return getPermission(cmd.getParent()); + } else if (cmd.getPermission() != null) { + return cmd.getPermission(); + } + return null; + } + + static class PermissionNode implements Comparable { + String permission; + String description; // Used for feature perms + List children = new ArrayList<>(); + List commands = new ArrayList<>(); + + PermissionNode(String permission) { + this.permission = permission; + } + + PermissionNode(String permission, Command cmd) { + this.permission = permission; + commands.add(cmd); + } + + void add(String perm, Command cmd) { + if (permission.equalsIgnoreCase(perm) || perm == null) { + commands.add(cmd); + } else if (perm.startsWith(permission)) { + for (PermissionNode child : children) { + if (isSub(child, perm)) { + child.add(perm, cmd); + return; + } + } + String[] parts = !permission.isEmpty() + ? perm.substring(permission.length() + 1).split("\\.") + : perm.split("\\."); + String parentPerm = !permission.isEmpty() + ? permission + "." + parts[0] + : parts[0]; + PermissionNode n = new PermissionNode(parentPerm); + children.add(n); + for (int i = 1; i < parts.length; i++) { + parentPerm += "." + parts[i]; + PermissionNode newNode = new PermissionNode(parentPerm); + n.children.add(newNode); + n = newNode; + } + n.commands.add(cmd); + } + } + + private boolean isSub(PermissionNode child, String perm) { + return perm.startsWith(child.permission) + && (child.permission.equalsIgnoreCase(perm) || (perm.length() > child.permission.length() + && perm.charAt(child.permission.length()) == '.') + ); + } + + void add(String perm, String description) { + if (permission.equalsIgnoreCase(perm) || perm == null) { + if (this.description == null) { + this.description = description; + } + } else if (perm.startsWith(permission)) { + for (PermissionNode child : children) { + if (isSub(child, perm)) { + child.add(perm, description); + return; + } + } + String[] parts = !permission.isEmpty() + ? perm.substring(permission.length() + 1).split("\\.") + : perm.split("\\."); + String parentPerm = !permission.isEmpty() + ? permission + "." + parts[0] + : parts[0]; + PermissionNode n = new PermissionNode(parentPerm); + children.add(n); + for (int i = 1; i < parts.length; i++) { + parentPerm += "." + parts[i]; + PermissionNode newNode = new PermissionNode(parentPerm); + n.children.add(newNode); + n = newNode; + } + n.description = description; + } + } + + List getChildPermissions() { + List perms = new ArrayList<>(); + for (PermissionNode child : children) { + perms.add(child.permission); + perms.addAll(child.getChildPermissions()); + } + Collections.sort(perms); + return perms; + } + + void addRoots(List roots) { + if (!children.isEmpty()) { + roots.add(this); + for (PermissionNode node : children) { + node.addRoots(roots); + } + } + } + + List getRoots() { + List roots = new ArrayList<>(); + addRoots(roots); + Collections.sort(roots); + return roots; + } + + void addLeafs(List leafs) { + if (children.isEmpty() || !commands.isEmpty()) { + leafs.add(this); + } + for (PermissionNode node : children) { + node.addLeafs(leafs); + } + } + + List getLeafs() { + List leafs = new ArrayList<>(); + addLeafs(leafs); + Collections.sort(leafs); + return leafs; + } + + String getDescription() { + if (description != null) { + return "'" + description + "'"; + } + if (commands.size() == 1) { + return "'Grants access to " + getCmdDescription(commands.get(0)) + "'"; + } else if (commands.size() > 1) { + String desc = "|" + System.lineSeparator(); + desc += " Grants access to " + getCmdDescription(commands.get(0)); + for (int i = 1; i < commands.size(); i++) { + desc += System.lineSeparator(); + desc += " " + getCmdDescription(commands.get(i)); + } + return desc; + } + return null; + } + + public PermissionNode find(String perm) { + if (permission.equals(perm)) { + return this; + } else if (perm.startsWith(permission)) { + for (PermissionNode n : children) { + PermissionNode r = n.find(perm); + if (r != null) { + return r; + } + } + } + return null; + } + + @Override + public int compareTo(PermissionNode o) { + return permission.compareTo(o.permission); + } + } +} diff --git a/bukkit-utils/src/main/java/dk/lockfuglsang/minecraft/command/README.md b/bukkit-utils/src/main/java/dk/lockfuglsang/minecraft/command/README.md new file mode 100644 index 000000000..10badd268 --- /dev/null +++ b/bukkit-utils/src/main/java/dk/lockfuglsang/minecraft/command/README.md @@ -0,0 +1,73 @@ +# Commands + +This package contains a lot of convenience classes to easily create commands for use with Bukkit. + +It has an in-build help system, with pagination, and permission-control. + +# Usage + +## Getting Started + +To create a composite entry-level command, simply inherit from the `AbstractCommandExecutor` in your main plugin. + +```java +class MyPlugin extends JavaPlugin { + @Override + public void onEnable() { + mycmd = new AbstractCommandExecutor("mycmd", "myplugin.perm.mycmd", "main myplugin command"); + mycmd.add(new AbstractCommand("hello|h", "myplugin.perm.hello", "say hello to the player") { + @Override + public boolean execute(CommandSender sender, String alias, Map data, String... args) { + sender.sendMessage(new String[]{ + "Hello! and welcome " + sender.getName(), + "I was called with : " + alias, + "I had " + args.length + " arguments: " + Arrays.asList(args) + }); + return true; + } + }); + getCommand("mycmd", mycmd); + } +} +``` + +The above code, will register 2 commands for the plugin. +One is nested within the other. + +Help is automatically generated, so invoking `/mycmd` will show a list of possible options. +Invoking `/mycmd h your momma` will output: + +``` +Hello! and welcome CONSOLE +I was called with : h +I had 2 arguments: [your, momma] +``` + +## Tab-Completion + +All CompositeCommands have automatic tab-completion of their sub-commands. + +If you want to have tab-completion of arguments, you can easily roll your own: + +```java +monsterTab = new AbstractTabCompleter() { + @Override + protected List getTabList(CommandSender commandSender, String term) { + return Arrays.asList("animal", "monster", "villager"); + } +} +``` +The above tab-completer will do filtering automatically. + +Tab-completers can be re-used, if they handle a named argument: + +```java + public class MyCmd extends CompositeCommand { + public MyCmd() { + super("mycmd", "perm.mycmd", "main command"); + add(new AbstractCommand("list", "perm.list", "?monster", "lists a subset") { ... }); + add(new AbstractCommand("spawn", "perm.spawn", "monster", "spawn a monster") { ... }); + addTab("monster", monsterTab); + } + } +``` \ No newline at end of file diff --git a/bukkit-utils/src/main/java/dk/lockfuglsang/minecraft/command/RowCommandVisitor.java b/bukkit-utils/src/main/java/dk/lockfuglsang/minecraft/command/RowCommandVisitor.java new file mode 100644 index 000000000..3d50a933b --- /dev/null +++ b/bukkit-utils/src/main/java/dk/lockfuglsang/minecraft/command/RowCommandVisitor.java @@ -0,0 +1,90 @@ +package dk.lockfuglsang.minecraft.command; + +import org.bukkit.command.CommandExecutor; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import static dk.lockfuglsang.minecraft.po.I18nUtil.tr; + +/** + * A visitor that simply gathers the complete command-hierarchy into a list of rows. + * @since 1.8 + */ +class RowCommandVisitor implements CommandVisitor { + private final List rows = new ArrayList<>(); + + public List getRows() { + return rows; + } + + @Override + public void visit(Command cmd) { + if (cmd instanceof CommandExecutor) { + rows.add(null); // separator + } + String commandPath = getCommandPath(cmd); + String alias = getAliases(cmd); + if (!alias.isEmpty()) { + String shortCmd = getShortestCmd(cmd); + int ix = commandPath.lastIndexOf(" " + shortCmd) + 1; + commandPath = commandPath.substring(0, ix) + cmd.getName() + alias + commandPath.substring(ix+shortCmd.length()); + } + rows.add(new Row(commandPath, cmd.getDescription(), cmd.getPermission())); + List extraPerms = new ArrayList<>(cmd.getFeaturePermissions().keySet()); + Collections.sort(extraPerms); + for (String p : extraPerms) { + rows.add(new Row(null, cmd.getFeaturePermissions().get(p), p)); + } + } + + private String getCommandPath(Command cmd) { + String path = cmd.getParent() != null ? getCommandPath(cmd.getParent()) + " " : ""; + return path + getShortestCmd(cmd) + CompositeCommand.getParamsAsString(cmd); + } + + private String getShortestCmd(Command cmd) { + String cmdName = cmd.getName(); + for (String alias : cmd.getAliases()) { + if (alias.length() < cmdName.length()) { + cmdName = alias; + } + } + return cmdName; + } + + private String getAliases(Command cmd) { + String aliases = ""; + for (int i = 1; i < cmd.getAliases().length; i++) { + aliases += "|" + cmd.getAliases()[i]; + } + return aliases; + } + + static class Row { + private final String command; + private final String description; + private final String permission; + + Row(String command, String description, String permission) { + this.command = command; + this.description = description; + this.permission = permission; + } + + public String getCommand() { + return command != null ? command : ""; + } + + public String getDescription() { + return description != null ? description : ""; + } + + public String getPermission() { + return permission != null ? permission : ""; + } + + } +} diff --git a/bukkit-utils/src/main/java/dk/lockfuglsang/minecraft/command/completion/AbstractTabCompleter.java b/bukkit-utils/src/main/java/dk/lockfuglsang/minecraft/command/completion/AbstractTabCompleter.java new file mode 100644 index 000000000..dd0a87e2b --- /dev/null +++ b/bukkit-utils/src/main/java/dk/lockfuglsang/minecraft/command/completion/AbstractTabCompleter.java @@ -0,0 +1,44 @@ +package dk.lockfuglsang.minecraft.command.completion; + +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.command.TabCompleter; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +/** + * Common ancestor of the TabCompleters. + * Uses the Template Pattern for sub-classes. + */ +public abstract class AbstractTabCompleter implements TabCompleter { + + abstract protected List getTabList(CommandSender commandSender, String term); + + @Override + public List onTabComplete(CommandSender commandSender, Command command, String alias, String[] args) { + String term = args.length > 0 ? args[args.length-1] : ""; + return filter(getTabList(commandSender, term), term); + } + + public static List filter(List list, String prefix) { + Set filtered = new LinkedHashSet<>(); + if (list != null) { + Collections.sort(list); + String lowerPrefix = prefix.toLowerCase(); + for (String test : list) { + if (test.toLowerCase().startsWith(lowerPrefix)) { + filtered.add(test); + } + } + } + if (filtered.size() > 20) { + return new ArrayList<>(filtered).subList(0, 20); + } + return new ArrayList<>(filtered); + } +} diff --git a/bukkit-utils/src/main/java/dk/lockfuglsang/minecraft/file/FileUtil.java b/bukkit-utils/src/main/java/dk/lockfuglsang/minecraft/file/FileUtil.java new file mode 100644 index 000000000..25591fc5c --- /dev/null +++ b/bukkit-utils/src/main/java/dk/lockfuglsang/minecraft/file/FileUtil.java @@ -0,0 +1,281 @@ +package dk.lockfuglsang.minecraft.file; + +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.InvalidConfigurationException; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; + +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.logging.Level; +import java.util.logging.Logger; + +import static java.util.Objects.requireNonNull; + +/** + * Common file-utilities. + */ +public enum FileUtil { + ; + private static final Logger log = Logger.getLogger(FileUtil.class.getName()); + private static final Collection alwaysOverwrite = new ArrayList<>(); + private static final Collection neverOverwrite = new ArrayList<>(); + private static final Map configFiles = new ConcurrentHashMap<>(); + private static Locale locale = Locale.getDefault(); + private static File dataFolder; + + public static void setAlwaysOverwrite(String... configs) { + for (String s : configs) { + if (!alwaysOverwrite.contains(s)) { + alwaysOverwrite.add(s); + } + } + } + + public static void readConfig(FileConfiguration config, File file) { + if (file == null) { + log.log(Level.INFO, "No config file found, it will be created"); + return; + } + File configFile = file; + File localeFile = new File(configFile.getParentFile(), getLocaleName(file.getName())); + if (localeFile.exists() && localeFile.canRead()) { + configFile = localeFile; + } + if (!configFile.exists()) { + log.log(Level.INFO, "No " + configFile + " found, it will be created"); + return; + } + try (Reader rdr = new InputStreamReader(new FileInputStream(configFile), StandardCharsets.UTF_8)) { + config.load(rdr); + } catch (InvalidConfigurationException e) { + log.log(Level.SEVERE, "Unable to read config file " + configFile, e); + if (configFile.exists()) { + try { + Files.copy(Paths.get(configFile.toURI()), Paths.get(configFile.getParent(), configFile.getName() + ".err"), StandardCopyOption.REPLACE_EXISTING); + } catch (IOException e1) { + // Ignore - we tried... + } + } + } catch (IOException e) { + log.log(Level.SEVERE, "Unable to read config file " + configFile, e); + } + } + + public static void readConfig(FileConfiguration config, InputStream inputStream) { + if (inputStream == null) { + return; + } + try (Reader rdr = new InputStreamReader(inputStream, StandardCharsets.UTF_8)) { + config.load(rdr); + } catch (InvalidConfigurationException | IOException e) { + log.log(Level.SEVERE, "Unable to read configuration", e); + } + } + + public static String getBasename(String file) { + String[] lastPart = file.split("([/\\\\])"); + file = lastPart[lastPart.length - 1]; + if (file != null && file.lastIndexOf('.') != -1) { + return file.substring(0, file.lastIndexOf('.')); + } + return file; + } + + public static String getExtension(String fileName) { + if (fileName != null && !fileName.isEmpty()) { + return fileName.substring(getBasename(fileName).length() + 1); + } + return ""; + } + + private static File getDataFolder() { + return dataFolder != null ? dataFolder : new File("."); + } + + /** + * System-encoding agnostic config-reader + * Reads and returns the configuration found in: + *
+     *   a) the datafolder
+     *
+     *     a.1) if a config named "config_en.yml" exists - that is read.
+     *
+     *     a.2) otherwise "config.yml" is read (created if need be).
+     *
+     *   b) if the version differs from the same resource on the classpath
+     *
+     *      b.1) all nodes in the jar-file-version is merged* into the local-file
+     *
+     *      b.2) unless the configName is in the allwaysOverwrite - then the jar-version wins
+     *
+     * *merged: using data-conversion of special nodes.
+     * 
+ */ + public static FileConfiguration getYmlConfiguration(String configName) { + // Caching, for your convenience! (and a bigger memory print!) + + if (!configFiles.containsKey(configName)) { + FileConfiguration config = new YamlConfiguration(); + try { + // read from datafolder! + File configFile = getConfigFile(configName); + YamlConfiguration configJar = new YamlConfiguration(); + readConfig(config, configFile); + readConfig(configJar, getResource(configName)); + if (!configFile.exists() || config.getInt("version", 0) < configJar.getInt("version", 0)) { + if (configFile.exists()) { + if (neverOverwrite.contains(configName)) { + configFiles.put(configName, config); + return config; + } + File backupFolder = new File(getDataFolder(), "backup"); + backupFolder.mkdirs(); + String bakFile = String.format("%1$s-%2$tY%2$tm%2$td-%2$tH%2$tM.yml", getBasename(configName), new Date()); + log.log(Level.INFO, "Moving existing config " + configName + " to backup/" + bakFile); + Files.move(Paths.get(configFile.toURI()), + Paths.get(new File(backupFolder, bakFile).toURI()), + StandardCopyOption.REPLACE_EXISTING); + if (alwaysOverwrite.contains(configName)) { + FileUtil.copy(getResource(configName), configFile); + config = configJar; + } else { + config = mergeConfig(configJar, config); + config.save(configFile); + config.load(configFile); + } + } else { + config = mergeConfig(configJar, config); + config.save(configFile); + config.load(configFile); + } + } + } catch (Exception e) { + log.log(Level.SEVERE, "Unable to handle config-file " + configName, e); + } + configFiles.put(configName, config); + } + return configFiles.get(configName); + } + + private static InputStream getResource(String configName) { + String resourceName = getLocaleName(configName); + ClassLoader loader = FileUtil.class.getClassLoader(); + InputStream resourceAsStream = loader.getResourceAsStream(resourceName); + if (resourceAsStream != null) { + return resourceAsStream; + } + return loader.getResourceAsStream(configName); + } + + private static String getLocaleName(String fileName) { + String baseName = getBasename(fileName); + return baseName + "_" + locale + fileName.substring(baseName.length()); + } + + public static File getConfigFile(String configName) { + File file = new File(getDataFolder(), getLocaleName(configName)); + if (file.exists()) { + return file; + } + return new File(getDataFolder(), configName); + } + + public static void copy(InputStream stream, File file) throws IOException { + if (stream == null || file == null) { + throw new IOException("Invalid resource for " + file); + } + Files.copy(stream, Paths.get(file.toURI()), StandardCopyOption.REPLACE_EXISTING); + } + + /** + * Merges the important keys from src to destination. + * + * @param src The source (containing the new values). + * @param dest The destination (containing old-values). + */ + private static FileConfiguration mergeConfig(FileConfiguration src, FileConfiguration dest) { + int existing = dest.getInt("version"); + int version = src.getInt("version", existing); + dest.setDefaults(src); + dest.options().copyDefaults(true); + dest.set("version", version); + removeExcludes(dest); + moveNodes(src, dest); + replaceDefaults(src, dest); + return dest; + } + + /** + * Removes nodes from dest.defaults, that are specifically excluded in the config + */ + private static void removeExcludes(FileConfiguration dest) { + List keys = dest.getStringList("merge-ignore"); + for (String key : keys) { + requireNonNull(dest.getDefaults()).set(key, null); + } + } + + private static void replaceDefaults(FileConfiguration src, FileConfiguration dest) { + ConfigurationSection forceSection = src.getConfigurationSection("force-replace"); + if (forceSection != null) { + for (String key : forceSection.getKeys(true)) { + Object def = forceSection.get(key, null); + Object value = dest.get(key, def); + Object newDef = src.get(key, null); + if (def != null && def.equals(value)) { + dest.set(key, newDef); + } + } + } + dest.set("force-replace", null); + requireNonNull(dest.getDefaults()).set("force-replace", null); + } + + private static void moveNodes(FileConfiguration src, FileConfiguration dest) { + ConfigurationSection moveSection = src.getConfigurationSection("move-nodes"); + if (moveSection != null) { + List keys = new ArrayList<>(moveSection.getKeys(true)); + Collections.reverse(keys); // Depth first + for (String key : keys) { + if (moveSection.isString(key)) { + String srcPath = key; + String tgtPath = moveSection.getString(key, key); + Object value = dest.get(srcPath); + if (value != null) { + dest.set(tgtPath, value); + dest.set(srcPath, null); + } + } else if (moveSection.isConfigurationSection(key)) { + // Check to see if dest section should be nuked... + if (dest.isConfigurationSection(key) && dest.getConfigurationSection(key).getKeys(false).isEmpty()) { + dest.set(key, null); + } + } + } + } + dest.set("move-nodes", null); + requireNonNull(dest.getDefaults()).set("move-nodes", null); + } + + public static void setDataFolder(File dataFolder) { + FileUtil.dataFolder = dataFolder; + configFiles.clear(); + } + + public static void setLocale(Locale loc) { + locale = loc != null ? loc : locale; + } + + public static void reload() { + for (Map.Entry e : configFiles.entrySet()) { + File configFile = new File(getDataFolder(), e.getKey()); + readConfig(e.getValue(), configFile); + } + } +} diff --git a/bukkit-utils/src/main/java/dk/lockfuglsang/minecraft/file/README.md b/bukkit-utils/src/main/java/dk/lockfuglsang/minecraft/file/README.md new file mode 100644 index 000000000..85a8ef49d --- /dev/null +++ b/bukkit-utils/src/main/java/dk/lockfuglsang/minecraft/file/README.md @@ -0,0 +1,47 @@ +# File Utilities + +The `FileUtil` class simplifies resource reading for Bukkit plugins. + +## Features + +The primary usage of the FileUtil, is to read yml-files from the plugin-datafolder. + +The utility supports the following features: + + * Reading files in UTF-8, regardless of system-encoding. + * Support merging yml files from classpath (jar) with plugin files. + * Support for `version`ing of individual yml files. + * Support for localization of configuration files. + * Support for data-conversion on merges. + * Support caching of configuration objects (only read from disk once). + +## Usage + +```java + @Override + void onEnable() { + FileUtil.setDataFolder(getDataFolder()); // init plugin-data-folder + FileUtil.setLocale(new Locale("en")); // set locale used in locating translated resources + + YmlConfiguration config = FileUtil.getYmlConfiguration("config.yml"); + if (config.getBoolean("feature.enabled", true)) { + // do feature stuff + } + } +``` + +The `config` in the above example is created the following way: + * Read `config.yml` from the data-folder + * if a `config_en.yml` exists - read it + * else if a `config.yml` exist - read it + * Read `config.yml` from the jar-file (classpath) + * if a 'config_en.yml` exists - read it + * else if a `config.yml` exsits - read it + * Compare `version` read from both configs + * if `config.yml` is listed in `allwaysOverwrite` - just use the jar-file + * else if jar-version > file-version or file-version does not exist + * merge nodes from jar into config - preserving node-comments + * ignore nodes listed in `merge-ignore` from the existing config-file + * move nodes listed in `move-nodes` from the jar-file + * replace nodes listed in `replace-nodes` from the jar-file, if they have default values + * set `version` to the jar-file version diff --git a/bukkit-utils/src/main/java/dk/lockfuglsang/minecraft/util/BlockRequirement.java b/bukkit-utils/src/main/java/dk/lockfuglsang/minecraft/util/BlockRequirement.java new file mode 100644 index 000000000..0d243a8dd --- /dev/null +++ b/bukkit-utils/src/main/java/dk/lockfuglsang/minecraft/util/BlockRequirement.java @@ -0,0 +1,9 @@ +package dk.lockfuglsang.minecraft.util; + +import org.bukkit.block.data.BlockData; + +// This class should ideally be located in package us.talabrek.ultimateskyblock.challenge. However, it is not possible +// to move there as it is required by ItemStackUtil in this module. This is a limitation of the current design. +// The parsing logic should eventually be moved to the uSkyBlock-Core module. +public record BlockRequirement(BlockData type, int amount) { +} diff --git a/bukkit-utils/src/main/java/dk/lockfuglsang/minecraft/util/FormatUtil.java b/bukkit-utils/src/main/java/dk/lockfuglsang/minecraft/util/FormatUtil.java new file mode 100644 index 000000000..a980bccb8 --- /dev/null +++ b/bukkit-utils/src/main/java/dk/lockfuglsang/minecraft/util/FormatUtil.java @@ -0,0 +1,171 @@ +package dk.lockfuglsang.minecraft.util; + +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Format utility + */ +public enum FormatUtil {; + private static final Pattern FORMATTING = Pattern.compile("^.*(?(\u00a7[0-9a-fklmor])+).*"); + public static String stripFormatting(String format) { + if (format == null || format.trim().isEmpty()) { + return ""; + } + return format.replaceAll("(\u00a7|&)[0-9a-fklmor]", ""); + } + + public static String normalize(String format) { + if (format == null || format.trim().isEmpty()) { + return ""; + } + return format.replaceAll("(\u00a7|&)([0-9a-fklmor])", "\u00a7$2"); + } + + public static List wordWrap(String s, int lineSize) { + return wordWrap(s, lineSize, lineSize); + } + + /** + * Wraps the string rather lazyly around the linesize (over). + * + * I.e. + *
+     *   this is a line of words
+     * 
+ * Will break into the following with linesize of 11: + *
+     *   this is a line
+     *   of words
+     * 
+ * Note that the first line is longer than 11 (14). + */ + public static List wordWrap(String s, int firstSegment, int lineSize) { + String format = getFormat(s); + if (format == null || !s.startsWith(format)) { + format = ""; + } + List words = new ArrayList<>(); + int numChars = firstSegment; + int ix = 0; + int jx = 0; + while (ix < s.length()) { + ix = s.indexOf(' ', ix+1); + if (ix != -1) { + String subString = s.substring(jx, ix).trim(); + String f = getFormat(subString); + int chars = stripFormatting(subString).length() + 1; // remember the space + if (chars >= numChars) { + if (f != null) { + format = f; + } + if (!subString.isEmpty()) { + words.add(withFormat(format, subString)); + numChars = lineSize; + jx = ix + 1; + } + } + } else { + break; + } + } + words.add(withFormat(format, s.substring(jx).trim())); + return words; + } + + public static List wordWrapStrict(String s, int lineLength) { + List lines = new ArrayList<>(); + String format = getFormat(s); + if (format == null || !s.startsWith(format)) { + format = ""; + } + String[] words = s.split(" "); + String line = ""; + for (String word: words) { + String test = stripFormatting(line + " " + word).trim(); + if (test.length() <= lineLength) { + // add word + line += (line.isEmpty() ? "" : " ") + word; + } else if (line.isEmpty() || stripFormatting(word).length() > lineLength) { + // add word truncated + String f = getFormat(word); + String strip = stripFormatting(word); + do { + int len = Math.min(strip.length(), lineLength-line.length()-1); + lines.add(withFormat(format, line + (line.isEmpty() ? "" : " ") + strip.substring(0, len))); + strip = strip.substring(len); + if (f != null) { + format = f; + } + } while (strip.length() > lineLength); + line = strip; + } else { + // add line, then start a new + lines.add(withFormat(format, line)); + String f = getFormat(line); + if (f != null) { + format = f; + } + line = word; + } + } + if (!line.isEmpty()) { + lines.add(withFormat(format, line)); + } + return lines; + } + + private static String withFormat(String format, String subString) { + String sf = null; + if (!subString.startsWith("\u00a7")) { + sf = format + subString; + } else { + sf = subString; + } + return sf; + } + + private static String getFormat(String s) { + Matcher m = FORMATTING.matcher(s); + String format = null; + if (m.matches() && m.group("format") != null) { + format = m.group("format"); + } + return format; + } + + public static List prefix(List list, String prefix) { + List prefixed = new ArrayList<>(list.size()); + for (String s : list) { + prefixed.add(prefix + s); + } + return prefixed; + } + + public static String camelcase(String name) { + if (name == null || name.isEmpty()) { + return ""; + } + StringBuilder sb = new StringBuilder(); + for (String part : name.split("[ _]")) { + sb.append(Character.toUpperCase(part.charAt(0))); + sb.append(part.substring(1).toLowerCase()); + } + return sb.toString(); + } + + /** + * Escapes formatting by "denormalizing" back to using & instead of §. + * @param formatString A formatstring (formerly normalized). + * @return A non-format string using & instead of §. + * @since 1.10 + */ + public static String escape(String formatString) { + String escaped = normalize(formatString); + escaped = escaped + .replaceAll("\u00a7", "&"); + return escaped; + } +} diff --git a/bukkit-utils/src/main/java/dk/lockfuglsang/minecraft/util/ItemRequirement.java b/bukkit-utils/src/main/java/dk/lockfuglsang/minecraft/util/ItemRequirement.java new file mode 100644 index 000000000..bce668c73 --- /dev/null +++ b/bukkit-utils/src/main/java/dk/lockfuglsang/minecraft/util/ItemRequirement.java @@ -0,0 +1,69 @@ +package dk.lockfuglsang.minecraft.util; + +import org.bukkit.inventory.ItemStack; + +// This class should ideally be located in package us.talabrek.ultimateskyblock.challenge. However, it is not possible +// to move there as it is required by ItemStackUtil in this module. This is a limitation of the current design. +// The parsing logic should eventually be moved to the uSkyBlock-Core module. +public record ItemRequirement(ItemStack type, int amount, Operator operator, double increment) { + + public Integer amountForRepetitions(int repetitions) { + return (int) Math.floor(operator().apply(amount(), increment(), repetitions)); + } + + public enum Operator { + NONE("", 0.0) { + @Override + public double apply(double value, double increment, int repetitions) { + return value; + } + }, + ADD("+", 0.0) { + @Override + public double apply(double value, double increment, int repetitions) { + return value + increment * repetitions; + } + }, + SUBTRACT("-", 0.0) { + @Override + public double apply(double value, double increment, int repetitions) { + return value - increment * repetitions; + } + }, + MULTIPLY("*", 1.0) { + @Override + public double apply(double value, double increment, int repetitions) { + return value * Math.pow(increment, repetitions); + } + }, + DIVIDE("/", 1.0) { + @Override + public double apply(double value, double increment, int repetitions) { + return value / Math.pow(increment, repetitions); + } + }; + + private final String symbol; + private final double neutralElement; + + Operator(String symbol, double neutralElement) { + this.symbol = symbol; + this.neutralElement = neutralElement; + } + + public double getNeutralElement() { + return neutralElement; + } + + public abstract double apply(double value, double increment, int repetitions); + + public static Operator fromSymbol(String symbol) { + for (Operator operator : values()) { + if (operator.symbol.equals(symbol)) { + return operator; + } + } + throw new IllegalArgumentException("Unknown operator: " + symbol); + } + } +} diff --git a/bukkit-utils/src/main/java/dk/lockfuglsang/minecraft/util/ItemStackUtil.java b/bukkit-utils/src/main/java/dk/lockfuglsang/minecraft/util/ItemStackUtil.java new file mode 100644 index 000000000..62c564467 --- /dev/null +++ b/bukkit-utils/src/main/java/dk/lockfuglsang/minecraft/util/ItemStackUtil.java @@ -0,0 +1,328 @@ +package dk.lockfuglsang.minecraft.util; + +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.block.data.BlockData; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.inventory.ItemFlag; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import static dk.lockfuglsang.minecraft.po.I18nUtil.tr; + +/** + * Conversion to ItemStack from strings. + */ +public enum ItemStackUtil { + ; + private static final Pattern ITEM_AMOUNT_PROBABILITY_PATTERN = Pattern.compile( + "(\\{p=(?0\\.\\d+)})?(?(minecraft:)?[0-9A-Za-z_]+(\\[.*])?):(?\\d+)" + ); + private static final Pattern ITEM_TYPE_PATTERN = Pattern.compile( + "(?(minecraft:)?[0-9A-Za-z_]+(\\[.*])?)" + ); + private static final Pattern ITEM_REQUIREMENT_PATTERN = Pattern.compile( + "(?(minecraft:)?[0-9A-Za-z_]+(\\[.*])?):(?\\d+)(;(?[-+*^/])(?\\d+(.\\d+)?))?" + ); + private static final Pattern BLOCK_REQUIREMENT_PATTERN = Pattern.compile( + "(?(minecraft:)?[0-9A-Za-z_]+(\\[.*])?):(?\\d+)" + ); + + @NotNull + public static BlockRequirement createBlockRequirement(@NotNull String specification) { + Matcher matcher = BLOCK_REQUIREMENT_PATTERN.matcher(specification); + if (matcher.matches()) { + BlockData itemStack = getBlockType(matcher); + int amount = Integer.parseInt(matcher.group("amount")); + return new BlockRequirement(itemStack, amount); + } else { + throw new IllegalArgumentException("Invalid item requirement: '" + specification + "'"); + } + } + + @NotNull + private static BlockData getBlockType(@NotNull Matcher matcher) { + String type = matcher.group("type"); + return Bukkit.createBlockData(type.toLowerCase(Locale.ROOT)); + } + + @NotNull + public static ItemRequirement createItemRequirement(@NotNull String specification) { + Matcher matcher = ITEM_REQUIREMENT_PATTERN.matcher(specification); + if (matcher.matches()) { + ItemStack itemStack = getItemType(matcher); + int amount = Integer.parseInt(matcher.group("amount")); + ItemRequirement.Operator operator = matcher.group("op") != null ? + ItemRequirement.Operator.fromSymbol(matcher.group("op")) : ItemRequirement.Operator.NONE; + double increment = matcher.group("inc") != null ? + Double.parseDouble(matcher.group("inc")) : operator.getNeutralElement(); + return new ItemRequirement(itemStack, amount, operator, increment); + } else { + throw new IllegalArgumentException("Invalid item requirement: '" + specification + "'"); + } + } + + @NotNull + public static List createItemsWithProbability(@NotNull List items) { + List itemsWithProbability = new ArrayList<>(); + for (String reward : items) { + Matcher matcher = ITEM_AMOUNT_PROBABILITY_PATTERN.matcher(reward); + if (matcher.matches()) { + double probability = matcher.group("prob") != null ? + Double.parseDouble(matcher.group("prob")) : 1.0; + ItemStack itemStack = getItemType(matcher); + int amount = Integer.parseInt(matcher.group("amount")); + itemStack.setAmount(amount); + itemsWithProbability.add(new ItemProbability(probability, itemStack)); + } else { + throw new IllegalArgumentException("Unknown item: '" + reward + "' in '" + items + "'"); + } + } + return itemsWithProbability; + } + + @NotNull + private static ItemStack getItemType(@NotNull Matcher matcher) { + String type = matcher.group("type"); + return Bukkit.getItemFactory().createItemStack(type.toLowerCase(Locale.ROOT)); + } + + // used for parsing challenge rewards and starter chest items + @NotNull + public static List createItemList(@NotNull List items) { + List itemList = new ArrayList<>(); + for (String reward : items) { + if (reward != null && !reward.isEmpty()) { + itemList.add(createItemStackAmount(reward)); + } + } + return itemList; + } + + @NotNull + private static ItemStack createItemStackAmount(@NotNull String reward) { + Matcher matcher = ITEM_AMOUNT_PROBABILITY_PATTERN.matcher(reward); + if (matcher.matches()) { + ItemStack itemStack = getItemType(matcher); + int amount = Integer.parseInt(matcher.group("amount")); + itemStack.setAmount(amount); + return itemStack; + } else { + throw new IllegalArgumentException("Unknown item: '" + reward + "'"); + } + } + + public static ItemStack[] createItemArray(List items) { + return items != null ? items.toArray(new ItemStack[0]) : new ItemStack[0]; + } + + public static ItemStack createItemStack(String displayItem) { + return createItemStack(displayItem, null, null); + } + + public static ItemStack createItemStack(@NotNull String displayItem, @Nullable String name, @Nullable String description) { + Matcher matcher = ITEM_TYPE_PATTERN.matcher(displayItem); + if (!matcher.matches()) { + throw new IllegalArgumentException("Invalid item " + displayItem + " supplied!"); + } + ItemStack itemStack = getItemType(matcher); + ItemMeta meta = itemStack.getItemMeta(); + if (meta != null) { + if (name != null) { + meta.setDisplayName(FormatUtil.normalize(name)); + } + List lore = new ArrayList<>(); + if (description != null) { + lore.addAll(FormatUtil.wordWrap(FormatUtil.normalize(description), 30, 30)); + } + meta.setLore(lore); + itemStack.setItemMeta(meta); + } + return itemStack; + } + + public static @NotNull List clone(@NotNull List items) { + return items.stream().map(ItemStack::clone).toList(); + } + + public static Builder builder(ItemStack stack) { + return new Builder(stack); + } + + public static String asString(ItemStack item) { + var itemType = item.getType().getKey().toString(); + var itemMeta = item.getItemMeta(); + if (itemMeta != null) { + var componentString = itemMeta.getAsComponentString(); + if (!componentString.isEmpty() && !componentString.equals("[]")) { + itemType += componentString; + } + } + return itemType + ":" + item.getAmount(); + } + + public static String asShortString(List items) { + List shorts = new ArrayList<>(); + for (ItemStack item : items) { + shorts.add(asShortString(item)); + } + return "[" + String.join(", ", shorts) + "]"; + } + + public static String asShortString(ItemStack item) { + if (item == null) { + return ""; + } + return item.getAmount() > 1 + ? tr("\u00a7f{0}x \u00a77{1}", item.getAmount(), getItemName(item)) + : tr("\u00a77{0}", getItemName(item)); + } + + @NotNull + public static ItemStack asDisplayItem(@NotNull ItemStack item) { + ItemStack copy = new ItemStack(item); + ItemMeta itemMeta = copy.getItemMeta(); + if (itemMeta != null) { + itemMeta.addItemFlags(ItemFlag.values()); + } + copy.setItemMeta(itemMeta); + return copy; + } + + @Contract("null -> null; !null -> !null") + @Nullable + public static String getItemName(ItemStack stack) { + if (stack == null) { + return null; + } + var itemMeta = stack.getItemMeta(); + if (itemMeta != null && itemMeta.hasDisplayName() && !itemMeta.getDisplayName().trim().isEmpty()) { + return stack.getItemMeta().getDisplayName(); + } + return tr(FormatUtil.camelcase(stack.getType().name()).replaceAll("([A-Z])", " $1").trim()); + } + + @NotNull + public static String getBlockName(@NotNull BlockData block) { + return tr(FormatUtil.camelcase(block.getMaterial().name()).replaceAll("([A-Z])", " $1").trim()); + } + + @Contract(pure = true) + @NotNull + public static ItemStack[] asValidItemStacksWithAmount(@NotNull Map typesWithAmount) { + return typesWithAmount.entrySet().stream().flatMap(entry -> { + ItemStack requiredType = entry.getKey(); + int requiredAmount = entry.getValue(); + List result = new ArrayList<>(); + int remaining = requiredAmount; + while (remaining > 0) { + ItemStack item = requiredType.clone(); + item.setAmount(Math.min(remaining, item.getMaxStackSize())); + remaining -= item.getAmount(); + result.add(item); + } + return result.stream(); + }).toArray(ItemStack[]::new); + } + + /** + * Builder for ItemStack + */ + public static class Builder { + private final ItemStack itemStack; + + public Builder(ItemStack itemStack) { + this.itemStack = itemStack != null ? itemStack.clone() : new ItemStack(Material.AIR); + } + + public Builder type(Material mat) { + itemStack.setType(mat); + return this; + } + + public Builder amount(int amount) { + itemStack.setAmount(amount); + return this; + } + + public Builder displayName(String name) { + ItemMeta itemMeta = itemStack.getItemMeta(); + itemMeta.setDisplayName(name); + itemStack.setItemMeta(itemMeta); + return this; + } + + public Builder enchant(Enchantment enchantment, int level) { + ItemMeta itemMeta = itemStack.getItemMeta(); + itemMeta.addEnchant(enchantment, level, false); + itemStack.setItemMeta(itemMeta); + return this; + } + + public Builder select(boolean b) { + return b ? select() : deselect(); + } + + public Builder select() { + return enchant(Enchantment.PROTECTION, 1).add(ItemFlag.HIDE_ENCHANTS); + } + + public Builder deselect() { + return remove(Enchantment.PROTECTION).remove(ItemFlag.HIDE_ENCHANTS); + } + + public Builder add(ItemFlag... flags) { + ItemMeta meta = itemStack.getItemMeta(); + meta.addItemFlags(flags); + itemStack.setItemMeta(meta); + return this; + } + + public Builder remove(ItemFlag... flags) { + ItemMeta meta = itemStack.getItemMeta(); + meta.removeItemFlags(flags); + itemStack.setItemMeta(meta); + return this; + } + + private Builder remove(Enchantment enchantment) { + ItemMeta itemMeta = itemStack.getItemMeta(); + itemMeta.removeEnchant(enchantment); + itemStack.setItemMeta(itemMeta); + return this; + } + + public Builder lore(String lore) { + return lore(Collections.singletonList(FormatUtil.normalize(lore))); + } + + public Builder lore(List lore) { + ItemMeta itemMeta = itemStack.getItemMeta(); + if (itemMeta != null) { + if (itemMeta.getLore() == null) { + itemMeta.setLore(lore); + } else { + List oldLore = itemMeta.getLore(); + oldLore.addAll(lore); + itemMeta.setLore(oldLore); + } + itemStack.setItemMeta(itemMeta); + } + return this; + } + + public ItemStack build() { + return itemStack; + } + } + + public record ItemProbability(double probability, ItemStack item) { + } +} diff --git a/bukkit-utils/src/main/java/dk/lockfuglsang/minecraft/util/LocationUtil.java b/bukkit-utils/src/main/java/dk/lockfuglsang/minecraft/util/LocationUtil.java new file mode 100644 index 000000000..4a0ada285 --- /dev/null +++ b/bukkit-utils/src/main/java/dk/lockfuglsang/minecraft/util/LocationUtil.java @@ -0,0 +1,74 @@ +package dk.lockfuglsang.minecraft.util; + +import org.bukkit.Bukkit; +import org.bukkit.Location; + +import java.util.Locale; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Static convenience class for location methods. + */ +public enum LocationUtil { + ; + private static final Pattern LOCATION_PATTERN = Pattern.compile("((?[^:/]+)[:/])?(?[\\-0-9\\.]+),(?[\\-0-9\\.]+),(?[\\-0-9\\.]+)(:(?[\\-0-9\\.]+):(?[\\-0-9\\.]+))?"); + + public static String asString(Location loc) { + if (loc == null) { + return null; + } + String s = ""; + if (loc.getWorld() != null && loc.getWorld().getName() != null) { + s += loc.getWorld().getName() + ":"; + } + s += String.format(Locale.ENGLISH, "%.2f,%.2f,%.2f", loc.getX(), loc.getY(), loc.getZ()); + if (loc.getYaw() != 0f || loc.getPitch() != 0f) { + s += String.format(Locale.ENGLISH, ":%.2f:%.2f", loc.getYaw(), loc.getPitch()); + } + return s; + } + + /** + * Convenience method for when a location is needed as a yml key. + */ + public static String asKey(Location loc) { + return asString(loc).replaceAll(":", "/").replaceAll("\\.", "_"); + } + + public static Location fromString(String locString) { + if (locString == null || locString.isEmpty()) { + return null; + } + Matcher m = LOCATION_PATTERN.matcher(locString.replaceAll("_", "\\.")); + if (m.matches()) { + return new Location(Bukkit.getWorld(m.group("world")), + Double.parseDouble(m.group("x")), + Double.parseDouble(m.group("y")), + Double.parseDouble(m.group("z")), + m.group("yaw") != null ? Float.parseFloat(m.group("yaw")) : 0, + m.group("pitch") != null ? Float.parseFloat(m.group("pitch")) : 0 + ); + } + return null; + } + + public static Location centerOnBlock(Location loc) { + if (loc == null) { + return null; + } + return new Location(loc.getWorld(), + loc.getBlockX() + 0.5, loc.getBlockY() + 0.1, loc.getBlockZ() + 0.5, + loc.getYaw(), loc.getPitch()); + } + + public static Location centerInBlock(Location loc) { + if (loc == null) { + return null; + } + return new Location(loc.getWorld(), + loc.getBlockX() + 0.5, loc.getBlockY() + 0.5, loc.getBlockZ() + 0.5, + loc.getYaw(), loc.getPitch()); + } + +} diff --git a/bukkit-utils/src/main/java/dk/lockfuglsang/minecraft/util/TimeUtil.java b/bukkit-utils/src/main/java/dk/lockfuglsang/minecraft/util/TimeUtil.java new file mode 100644 index 000000000..30afe24ad --- /dev/null +++ b/bukkit-utils/src/main/java/dk/lockfuglsang/minecraft/util/TimeUtil.java @@ -0,0 +1,71 @@ +package dk.lockfuglsang.minecraft.util; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.time.Duration; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import static dk.lockfuglsang.minecraft.po.I18nUtil.tr; + +public enum TimeUtil { + ; + private static final Pattern TIME_PATTERN = Pattern.compile("((?[0-9]+)d)?\\s*((?[0-9]+)h)?\\s*((?[0-9]+)m)?\\s*((?[0-9]+)s)?\\s*((?[0-9]+)ms)?"); + + public static @Nullable Duration stringAsDuration(@NotNull String specification) { + Matcher matcher = TIME_PATTERN.matcher(specification); + if (matcher.matches()) { + Duration result = Duration.ZERO; + if (matcher.group("d") != null) { + result = result.plusDays(Long.parseLong(matcher.group("d"))); + } + if (matcher.group("h") != null) { + result = result.plusHours(Long.parseLong(matcher.group("h"))); + } + if (matcher.group("m") != null) { + result = result.plusMinutes(Long.parseLong(matcher.group("m"))); + } + if (matcher.group("s") != null) { + result = result.plusSeconds(Long.parseLong(matcher.group("s"))); + } + if (matcher.group("ms") != null) { + result = result.plusMillis(Long.parseLong(matcher.group("ms"))); + } + return result; + } + return null; + } + + public static @NotNull String durationAsString(@NotNull Duration duration) { + String result = ""; + if (duration.toDaysPart() > 0) { + result += " " + duration.toDaysPart() + tr("d"); + } + if (duration.toHoursPart() > 0) { + result += " " + duration.toHoursPart() + tr("h"); + } + if (duration.toMinutesPart() > 0) { + result += " " + duration.toMinutesPart() + tr("m"); + } + if (duration.toSecondsPart() > 0 || result.isEmpty()) { + result += " " + duration.toSecondsPart() + tr("s"); + } + return result.trim(); + } + + public static @NotNull String durationAsShort(@NotNull Duration duration) { + long m = duration.toMinutes(); + long s = duration.toSecondsPart(); + long ms = duration.toMillisPart(); + return String.format(tr("{0,number,0}:{1,number,00}.{2,number,000}", m, s, ms)); + } + + public static long durationAsTicks(@NotNull Duration duration) { + return duration.toMillis() / 50; + } + + public static @NotNull Duration ticksAsDuration(long ticks) { + return Duration.ofMillis(ticks * 50); + } +} diff --git a/bukkit-utils/src/main/java/dk/lockfuglsang/minecraft/util/Timer.java b/bukkit-utils/src/main/java/dk/lockfuglsang/minecraft/util/Timer.java new file mode 100644 index 000000000..f271fec34 --- /dev/null +++ b/bukkit-utils/src/main/java/dk/lockfuglsang/minecraft/util/Timer.java @@ -0,0 +1,31 @@ +package dk.lockfuglsang.minecraft.util; + +import org.jetbrains.annotations.NotNull; + +import java.time.Duration; +import java.time.Instant; + +public class Timer { + + private final Instant start; + + public Timer(@NotNull Instant start) { + this.start = start; + } + + public Instant getStart() { + return start; + } + + public Duration elapsed() { + return Duration.between(start, Instant.now()); + } + + public String elapsedAsString() { + return TimeUtil.durationAsString(elapsed()); + } + + public static Timer start() { + return new Timer(Instant.now()); + } +} diff --git a/bukkit-utils/src/main/java/dk/lockfuglsang/minecraft/util/VersionUtil.java b/bukkit-utils/src/main/java/dk/lockfuglsang/minecraft/util/VersionUtil.java new file mode 100644 index 000000000..e04f35d93 --- /dev/null +++ b/bukkit-utils/src/main/java/dk/lockfuglsang/minecraft/util/VersionUtil.java @@ -0,0 +1,75 @@ +package dk.lockfuglsang.minecraft.util; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Utility methods for versions + * @since v1.15 + */ +public enum VersionUtil {; + private static final Pattern VERSION_PATTERN = Pattern.compile("v?(?[0-9]+)[\\._](?[0-9]+)(?:[\\._](?[0-9]+))?(?.*)"); + public static Version getVersion(String versionString) { + Matcher m = VERSION_PATTERN.matcher(versionString); + if (m.matches()) { + int major = Integer.parseInt(m.group("major")); + int minor = m.group("minor") != null ? Integer.parseInt(m.group("minor")) : 0; + int micro = m.group("micro") != null ? Integer.parseInt(m.group("micro")) : 0; + return new Version(major, minor, micro, m.group("sub")); + } + return new Version(0,0,0, null); + } + + public static class Version { + private int major; + private int minor; + private int micro; + private String sub; + + public Version(int major, int minor, int micro, String sub) { + this.major = major; + this.minor = minor; + this.micro = micro; + this.sub = sub; + } + + public int getMajor() { + return major; + } + + public int getMinor() { + return minor; + } + + public int getMicro() { + return micro; + } + + public String getSub() { + return sub; + } + + public boolean isGTE(String version) { + Version other = getVersion(version); + return major > other.major || + major >= other.major && minor > other.minor || + major >= other.major && minor >= other.minor && micro >= other.micro; + } + + public boolean isLT(String version) { + Version other = getVersion(version); + return major < other.major || + major <= other.major && minor < other.minor || + major <= other.major && minor <= other.minor && micro < other.micro; + } + + @Override + public String toString() { + return "v" + major + "." + minor + "." + micro + (sub != null ? "-" + sub : ""); + } + + public String toReleaseString() { + return "v" + major + "_" + minor; + } + } +} diff --git a/bukkit-utils/src/test/java/dk/lockfuglsang/minecraft/command/BaseCommandExecutorTest.java b/bukkit-utils/src/test/java/dk/lockfuglsang/minecraft/command/BaseCommandExecutorTest.java new file mode 100644 index 000000000..fe211a7d5 --- /dev/null +++ b/bukkit-utils/src/test/java/dk/lockfuglsang/minecraft/command/BaseCommandExecutorTest.java @@ -0,0 +1,84 @@ +package dk.lockfuglsang.minecraft.command; + +import dk.lockfuglsang.minecraft.po.I18nUtil; +import org.bukkit.command.CommandSender; +import org.junit.BeforeClass; +import org.junit.Test; +import org.mockito.ArgumentMatchers; +import org.mockito.Mockito; +import org.mockito.stubbing.Answer; + +import java.io.File; +import java.util.Arrays; +import java.util.Locale; +import java.util.Map; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.when; + +public class BaseCommandExecutorTest { + StringBuffer messages = new StringBuffer(); + private static BaseCommandExecutor mycmd; + + @BeforeClass + public static void setUp() { + I18nUtil.initialize(new File("."), Locale.ENGLISH); + mycmd = new BaseCommandExecutor("mycmd", "myplugin.perm.mycmd", "main myplugin command"); + mycmd.add(new AbstractCommand("hello|h", "myplugin.perm.hello", "say hello to the player") { + @Override + public boolean execute(CommandSender sender, String alias, Map data, String... args) { + sender.sendMessage("Hello! and welcome " + sender.getName(), + "I was called with : " + alias, + "I had " + args.length + " arguments: " + Arrays.asList(args)); + return true; + } + }); + } + + @Test + public void testNoPermissions() { + CommandSender sender = createCommandSender(); + mycmd.onCommand(sender, null, "mycmd", new String[]{"mycmd", "h", "your", "momma"}); + assertThat(getMessages(), is("§eYou do not have access (§4myplugin.perm.mycmd§e)\n" + + "§7Usage: §3mycmd§a [command|help]§7 - §emain myplugin command")); + } + + private String getMessages() { + return messages.toString().trim(); + } + + @Test + public void testBasic() { + CommandSender sender = createCommandSender(); + addPerm(sender, "myplugin.perm.mycmd"); + addPerm(sender, "myplugin.perm.hello"); + mycmd.onCommand(sender, null, "mycmd", new String[]{"h", "your", "momma"}); + assertThat(getMessages(), is("Hello! and welcome null\nI was called with : h\nI had 2 arguments: [your, momma]")); + } + + private void addPerm(CommandSender sender, String s) { + when(sender.hasPermission(ArgumentMatchers.isA(String.class))).thenReturn(true); + } + + private CommandSender createCommandSender() { + CommandSender mock = Mockito.mock(CommandSender.class); + Answer answer = invocationOnMock -> { + for (Object o : invocationOnMock.getArguments()) { + if (o != null && o.getClass().isArray()) { + for (Object o2 : (Object[]) o) { + messages.append(o2).append("\n"); + } + } else { + messages.append(o).append("\n"); + } + } + return null; + }; + doAnswer(answer).when(mock).sendMessage(anyString()); + doAnswer(answer).when(mock).sendMessage(ArgumentMatchers.any(String[].class)); + return mock; + } +} diff --git a/bukkit-utils/src/test/java/dk/lockfuglsang/minecraft/command/CompositeCommandTest.java b/bukkit-utils/src/test/java/dk/lockfuglsang/minecraft/command/CompositeCommandTest.java new file mode 100644 index 000000000..d58da4262 --- /dev/null +++ b/bukkit-utils/src/test/java/dk/lockfuglsang/minecraft/command/CompositeCommandTest.java @@ -0,0 +1,182 @@ +package dk.lockfuglsang.minecraft.command; + +import dk.lockfuglsang.minecraft.po.I18nUtil; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.hamcrest.Matchers; +import org.junit.BeforeClass; +import org.junit.Test; +import org.mockito.stubbing.Answer; + +import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.UUID; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.*; + +public class CompositeCommandTest { + private static final UUID ownerUUID = UUID.randomUUID(); + private static final UUID adminUUID = UUID.randomUUID(); + private static final UUID modUUID = UUID.randomUUID(); + private static BaseCommandExecutor executor; + + @BeforeClass + public static void setupAll() { + I18nUtil.initialize(new File("."), Locale.ENGLISH); + executor = new BaseCommandExecutor("plugin", "plugin", null, "does stuff", ownerUUID); + CompositeCommand sut = new CompositeCommand("admin", "admin.admin.superadmin", null, "super important admin command", adminUUID) { + @Override + public boolean execute(CommandSender sender, String alias, Map data, String... args) { + sender.sendMessage("executed admin"); + return super.execute(sender, alias, data, args); + } + }; + sut.add(new AbstractCommand("sub", "perm.sub", "some sub-command") { + @Override + public boolean execute(CommandSender sender, String alias, Map data, String... args) { + sender.sendMessage("from sub"); + return false; + } + }); + sut.add(new AbstractCommand("sub2", "perm.sub2", "some other sub-command", "yay", null, modUUID) { + @Override + public boolean execute(CommandSender sender, String alias, Map data, String... args) { + sender.sendMessage("from sub2"); + return false; + } + }); + executor.add(sut); + } + + @Test + public void NoPermOnBase() { + // Arrange + Player player = mock(Player.class); + when(player.hasPermission(anyString())).thenReturn(false); + + // Act + executor.onCommand(player, null, "alias", new String[]{"admin", "sub"}); + + verify(player).sendMessage("§eYou do not have access (§4plugin§e)"); + } + + @Test + public void PermOnBase() { + // Arrange + Player player = mock(Player.class); + when(player.hasPermission(anyString())).thenAnswer((Answer) invocationOnMock -> + invocationOnMock.getArguments()[0] == "plugin"); + + // Act + executor.onCommand(player, null, "alias", new String[]{"admin", "sub"}); + + verify(player).sendMessage("§eYou do not have access (§4admin.admin.superadmin§e)"); + } + + @Test + public void PermOnAdmin() { + // Arrange + Player player = mock(Player.class); + final List messages = recordMessages(player); + when(player.hasPermission(anyString())).thenAnswer((Answer) invocationOnMock -> + invocationOnMock.getArguments()[0] == "plugin" + || invocationOnMock.getArguments()[0] == "admin.admin.superadmin" + ); + + // Act + executor.onCommand(player, null, "alias", new String[]{"admin", "sub"}); + + verify(player).sendMessage("executed admin"); + assertThat(messages, Matchers.contains("executed admin", "§eYou do not have access (§4perm.sub§e)")); + } + + @Test + public void AllPerms() { + // Arrange + Player player = mock(Player.class); + when(player.hasPermission(anyString())).thenReturn(true); + final List messages = recordMessages(player); + + // Act + executor.onCommand(player, null, "alias", new String[]{"admin", "sub"}); + + verify(player).sendMessage("executed admin"); + assertThat(messages, Matchers.contains("executed admin", "from sub")); + } + + @Test + public void NoPerm_PermissionOverride() { + // Arrange + Player player = mock(Player.class); + when(player.getUniqueId()).thenReturn(ownerUUID); + final List messages = recordMessages(player); + + // Act + executor.onCommand(player, null, "alias", new String[]{"admin", "sub"}); + + assertThat(messages, Matchers.contains("executed admin", "from sub")); + } + + @Test + public void NoPerm_PermissionSubOverride() { + // Arrange + Player player = mock(Player.class); + when(player.getUniqueId()).thenReturn(adminUUID); + final List messages = recordMessages(player); + + // Act + executor.onCommand(player, null, "alias", new String[]{"admin", "sub"}); + + assertThat(messages, Matchers.contains("§eYou do not have access (§4plugin§e)")); + } + + @Test + public void BasePerm_PermissionCompositeOverride() { + // Arrange + Player player = mock(Player.class); + when(player.getUniqueId()).thenReturn(adminUUID); + when(player.hasPermission(anyString())).thenAnswer((Answer) invocationOnMock -> + invocationOnMock.getArguments()[0] == "plugin" + ); + final List messages = recordMessages(player); + + // Act + executor.onCommand(player, null, "alias", new String[]{"admin", "sub"}); + + assertThat(messages, Matchers.contains("executed admin", "from sub")); + } + + @Test + public void BasePerm_PermissionAbstractCommandOverride() { + // Arrange + Player player = mock(Player.class); + when(player.getUniqueId()).thenReturn(modUUID); + when(player.hasPermission(anyString())).thenAnswer((Answer) invocationOnMock -> + invocationOnMock.getArguments()[0] == "plugin" + || invocationOnMock.getArguments()[0] == "admin.admin.superadmin" + ); + final List messages = recordMessages(player); + + // Act + executor.onCommand(player, null, "alias", new String[]{"admin", "sub2"}); + + assertThat(messages, Matchers.contains("executed admin", "from sub2")); + } + + private List recordMessages(Player player) { + final List messages = new ArrayList<>(); + Answer voidAnswer = i -> { + messages.add(String.join(" ", Arrays.asList(i.getArguments()).toArray(new String[0]))); + return null; + }; + doAnswer(voidAnswer).when(player).sendMessage(anyString()); + return messages; + } + +} diff --git a/bukkit-utils/src/test/java/dk/lockfuglsang/minecraft/command/PluginYamlCommandVisitorTest.java b/bukkit-utils/src/test/java/dk/lockfuglsang/minecraft/command/PluginYamlCommandVisitorTest.java new file mode 100644 index 000000000..2da17d608 --- /dev/null +++ b/bukkit-utils/src/test/java/dk/lockfuglsang/minecraft/command/PluginYamlCommandVisitorTest.java @@ -0,0 +1,55 @@ +package dk.lockfuglsang.minecraft.command; + +import org.junit.Test; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.nio.file.Files; +import java.nio.file.Paths; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; + +public class PluginYamlCommandVisitorTest { + @Test + public void writeToSimple() throws Exception { + PluginYamlCommandVisitor visitor = new PluginYamlCommandVisitor(); + BaseCommandExecutor cmd = new BaseCommandExecutor("cmd|c", "plugin.cmd", "player", "some description"); + cmd.add(new CompositeCommand("sub|s", "plugin.sub", "some sub description")); + cmd.add(new CompositeCommand("other", "plugin.cmd.other", "some other command")); + BaseCommandExecutor cmd2 = new BaseCommandExecutor("adm|a", "plugin.adm", "hey jude!"); + cmd2.add(new CompositeCommand("subs|ss", "plugin.sub", "some other sub")); + cmd2.add(new CompositeCommand("t2", "plugin.cmdtest", "?optional mandatory", "test")); + String expected = String.join(System.lineSeparator(), Files.readAllLines( + Paths.get(getClass().getClassLoader().getResource("yml/pluginyml_simple.yml").toURI()))); + + cmd.accept(visitor); + cmd2.accept(visitor); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + PrintStream out = new PrintStream(baos); + visitor.writeTo(out); + assertThat(baos.toString(), is(expected)); + } + + @Test + public void writeToSimpleFeatureMap() throws Exception { + PluginYamlCommandVisitor visitor = new PluginYamlCommandVisitor(); + BaseCommandExecutor cmd = new BaseCommandExecutor("cmd|c", "plugin.cmd", "some description"); + CompositeCommand sub = new CompositeCommand("sub|s", "plugin.sub", "some sub description"); + cmd.add(sub); + sub.addFeaturePermission("plugin.feature.a", "enables A"); + sub.addFeaturePermission("plugin.feature.b", "enables B"); + sub.addFeaturePermission("plugin.featuresub", "standalone feature"); + cmd.add(new CompositeCommand("other", "plugin.cmd.other", "some other command")); + String expected = String.join(System.lineSeparator(), Files.readAllLines( + Paths.get(getClass().getClassLoader().getResource("yml/pluginyml_featuremap.yml").toURI()))); + + cmd.accept(visitor); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + PrintStream out = new PrintStream(baos); + visitor.writeTo(out); + assertThat(baos.toString(), is(expected)); + } +} diff --git a/bukkit-utils/src/test/java/dk/lockfuglsang/minecraft/file/FileUtilTest.java b/bukkit-utils/src/test/java/dk/lockfuglsang/minecraft/file/FileUtilTest.java new file mode 100644 index 000000000..a65c357f4 --- /dev/null +++ b/bukkit-utils/src/test/java/dk/lockfuglsang/minecraft/file/FileUtilTest.java @@ -0,0 +1,23 @@ +package dk.lockfuglsang.minecraft.file; + +import org.junit.Test; + +import static org.hamcrest.core.Is.is; +import static org.hamcrest.MatcherAssert.assertThat; + +/** + * JUnit tests for FileUtil + */ +public class FileUtilTest { + @Test + public void testGetExtension() { + assertThat(FileUtil.getExtension("basename.ext"), is("ext")); + assertThat(FileUtil.getExtension("my file.with.dot.yml"), is("yml")); + } + + @Test + public void testBaseName() { + assertThat(FileUtil.getBasename("dir/something/filename.txt"), is("filename")); + assertThat(FileUtil.getBasename("dir\\something\\filename.txt"), is("filename")); + } +} diff --git a/bukkit-utils/src/test/java/dk/lockfuglsang/minecraft/nbt/NBTUtilTest.java b/bukkit-utils/src/test/java/dk/lockfuglsang/minecraft/nbt/NBTUtilTest.java new file mode 100644 index 000000000..911df5c52 --- /dev/null +++ b/bukkit-utils/src/test/java/dk/lockfuglsang/minecraft/nbt/NBTUtilTest.java @@ -0,0 +1,39 @@ +package dk.lockfuglsang.minecraft.nbt; + +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; +import org.hamcrest.CoreMatchers; +import org.junit.Test; + +import java.io.StringReader; +import java.util.List; +import java.util.Map; + +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; + +/** + * Tests the NBTUtil + */ +public class NBTUtilTest { + + /** + * Tests that the JSONMap will return the proper syntax. + */ + @Test + public void testJSONMap() { + String jsonString = "{\"Potion\":\"minecraft:empty\",\"CustomPotionEffects\":[{\"Id\":1},{\"Id\":2}]}"; + Gson gson = new Gson(); + + Map map = gson.fromJson(new StringReader(jsonString), new TypeToken>(){}.getType()); + assertThat(map.get("Potion"), CoreMatchers.is("minecraft:empty")); + assertThat(map.get("CustomPotionEffects"), instanceOf(List.class)); + assertThat(((List)map.get("CustomPotionEffects")).getFirst(), instanceOf(Map.class)); + } + + @Test + public void testGetGraftBukkitVersion() { + assertThat("net.minecraft.server.v1_10_R1.NBTTagString".split("\\.")[3], is("v1_10_R1")); + } +} diff --git a/bukkit-utils/src/test/java/dk/lockfuglsang/minecraft/util/BukkitServerMock.java b/bukkit-utils/src/test/java/dk/lockfuglsang/minecraft/util/BukkitServerMock.java new file mode 100644 index 000000000..97e639fac --- /dev/null +++ b/bukkit-utils/src/test/java/dk/lockfuglsang/minecraft/util/BukkitServerMock.java @@ -0,0 +1,124 @@ +package dk.lockfuglsang.minecraft.util; + +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.Server; +import org.bukkit.UnsafeValues; +import org.bukkit.inventory.ItemFactory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.Damageable; +import org.bukkit.inventory.meta.ItemMeta; +import org.mockito.stubbing.Answer; + +import java.lang.reflect.Field; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.TreeMap; + +import static org.mockito.Mockito.*; + +public class BukkitServerMock { + public static boolean useMetaData; + /** + * Stubbing data, allows for advanced stubbing behaviour for item-meta + */ + protected static Map> itemMetaMap = new HashMap<>(); + + public static Server setupServerMock() throws NoSuchFieldException, IllegalAccessException { + Field server = Bukkit.class.getDeclaredField("server"); + server.setAccessible(true); + Server serverMock = createServerMock(); + server.set(null, serverMock); + server.setAccessible(false); + return serverMock; + } + + @SuppressWarnings("deprecation") + public static Server createServerMock() { + Server serverMock = mock(Server.class); + ItemFactory itemFactoryMock = mock(ItemFactory.class); + + when(itemFactoryMock.isApplicable(any(ItemMeta.class), any(Material.class))) + .thenReturn(true); + + when(itemFactoryMock.equals(any(), any())) + .thenAnswer((Answer) invocationOnMock -> { + // Better equals for mocks? + return Objects.equals("" + invocationOnMock.getArguments()[0], + "" + invocationOnMock.getArguments()[1]); + }); + + when(itemFactoryMock.getItemMeta(any(Material.class))) + .thenAnswer((Answer) invocationOnMock -> createItemMetaStub()); + + when(itemFactoryMock.asMetaFor(any(ItemMeta.class), any(Material.class))) + .thenAnswer((Answer) invocationOnMock -> invocationOnMock.getArguments()[0] != null + ? (ItemMeta) invocationOnMock.getArguments()[0] + : null); + + when(itemFactoryMock.createItemStack(any())) + .thenAnswer((Answer) invocationOnMock -> { + String specification = (String) invocationOnMock.getArguments()[0]; + var componentSplitIndex = specification.indexOf('['); + String typeSpecification; + String components; + if (componentSplitIndex > 0) { + typeSpecification = specification.substring(0, componentSplitIndex); + components = specification.substring(componentSplitIndex); + } else { + typeSpecification = specification; + components = ""; + } + var type = Material.matchMaterial(typeSpecification); + var itemStack = new ItemStack(type); + var mockMeta = mock(ItemMeta.class, withSettings().extraInterfaces(Damageable.class)); + when(((Damageable)mockMeta).getDamage()).thenReturn(0); + when(mockMeta.toString()).thenReturn(components); + when(mockMeta.getAsComponentString()).thenReturn(components.isEmpty() ? "[]" : components); + when(itemStack.getItemMeta()).thenReturn(mockMeta); + return itemStack; + }); + + when(serverMock.getItemFactory()).thenReturn(itemFactoryMock); + + UnsafeValues unsafeMock = mock(UnsafeValues.class); + when(unsafeMock.fromLegacy(any(Material.class))).thenAnswer(a -> a.getArguments()[0]); + when(serverMock.getUnsafe()).thenReturn(unsafeMock); + return serverMock; + } + + @SuppressWarnings("unchecked") + public static ItemMeta createItemMetaStub() { + if (!useMetaData) { + return null; + } + ItemMeta meta = mock(ItemMeta.class, withSettings().extraInterfaces(Damageable.class)); + when(((Damageable)meta).getDamage()).thenReturn(0); + // Note: This is a HACKY way of stubbing, using mock and toString() + final Map metaData = new TreeMap<>(); + itemMetaMap.put(meta, metaData); + doAnswer((Answer) invocationOnMock -> { + String displayName = "" + invocationOnMock.getArguments()[0]; + if (displayName.isEmpty()) { + metaData.remove("displayName"); + } else { + metaData.put("displayName", displayName); + } + return null; + }).when(meta).setDisplayName(any(String.class)); + doAnswer((Answer) invocationOnMock -> { + List lore = (List) invocationOnMock.getArguments()[0]; + if (lore != null && !lore.isEmpty()) { + metaData.put("lore", "" + lore); + } else { + metaData.remove("lore"); + } + return null; + }).when(meta).setLore(any(List.class)); + when(meta.toString()).thenAnswer((Answer) invocationOnMock -> "" + metaData); + when(meta.clone()).thenReturn(meta); // Don't clone it - we need to verify it + return meta; + } +} diff --git a/bukkit-utils/src/test/java/dk/lockfuglsang/minecraft/util/FormatUtilTest.java b/bukkit-utils/src/test/java/dk/lockfuglsang/minecraft/util/FormatUtilTest.java new file mode 100644 index 000000000..b2d505593 --- /dev/null +++ b/bukkit-utils/src/test/java/dk/lockfuglsang/minecraft/util/FormatUtilTest.java @@ -0,0 +1,67 @@ +package dk.lockfuglsang.minecraft.util; + +import org.junit.Test; + +import java.util.Arrays; + +import static dk.lockfuglsang.minecraft.util.FormatUtil.*; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; + +public class FormatUtilTest { + + @Test + public void testNormalize() throws Exception { + String input = "pre&00&11&22&33&44&55&66&77&88&99&aa&bb&cc&dd&ee&ff&ggpost"; + String expected = "pre§00§11§22§33§44§55§66§77§88§99§aa§bb§cc§dd§ee§ff&ggpost"; + assertThat(normalize(input), is(expected)); + } + + @Test + public void testWordWrap() throws Exception { + assertThat(wordWrap("asdadfasd asfasdfasd", 9, 12), + is(Arrays.asList( + "asdadfasd", "asfasdfasd"))); + assertThat(wordWrap("§1Hello §2World §3How are you", 4, 10), + is(Arrays.asList("§1Hello", "§2World §3How", "§3are you"))); + + assertThat(wordWrap("§1§2§3§4§5Hello §aWorld §1§2§3§4what happens", 10, 10), is(Arrays.asList( + "§1§2§3§4§5Hello §aWorld", "§1§2§3§4what happens" + ))); + + assertThat(wordWrap("this is a short story of a long sentence withaverylongword in the end", 14, 14), + is(Arrays.asList( + "this is a short", + "story of a long", + "sentence withaverylongword", + "in the end"))); + } + + @Test + public void testWordWrapStrict() { + assertThat(wordWrapStrict("this is a short story of a long sentence withaverylongword in the end", 14), + is(Arrays.asList( + "this is a", + "short story of", + "a long", + "sentence witha", + "verylongword", + "in the end"))); + + assertThat(wordWrapStrict("§1Hello §2World §3How are you", 10), + is(Arrays.asList("§1Hello", "§2World §3How", "§3are you"))); + } + + @Test + public void testCapitalize() throws Exception { + assertThat(camelcase("SAND_CASTLE"), is("SandCastle")); + assertThat(camelcase("SHEEP"), is("Sheep")); + assertThat(camelcase("ender chest"), is("EnderChest")); + } + + @Test + public void testEscape() throws Exception { + String text = "\u00a7eHello World\r\n\u00a7aThis is a \u00a7kmagic\r\n\u00a7lhej"; + assertThat(escape(text), is("&eHello World\r\n&aThis is a &kmagic\r\n&lhej")); + } +} diff --git a/bukkit-utils/src/test/java/dk/lockfuglsang/minecraft/util/ItemStackMatcher.java b/bukkit-utils/src/test/java/dk/lockfuglsang/minecraft/util/ItemStackMatcher.java new file mode 100644 index 000000000..972705eac --- /dev/null +++ b/bukkit-utils/src/test/java/dk/lockfuglsang/minecraft/util/ItemStackMatcher.java @@ -0,0 +1,53 @@ +package dk.lockfuglsang.minecraft.util; + +import org.bukkit.inventory.ItemStack; +import org.hamcrest.Description; +import org.hamcrest.Factory; +import org.hamcrest.Matcher; +import org.hamcrest.TypeSafeDiagnosingMatcher; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import static org.hamcrest.collection.IsIterableContainingInOrder.contains; + +public class ItemStackMatcher extends TypeSafeDiagnosingMatcher { + + private final String expected; + + public ItemStackMatcher(ItemStack expected) { + this.expected = ItemStackUtil.asString(expected); + } + + @Override + protected boolean matchesSafely(ItemStack itemStack, Description description) { + String other = ItemStackUtil.asString(itemStack); + description.appendText(" was ").appendValue(other); + return expected.equals(other); + } + + @Override + public void describeTo(Description description) { + description.appendText(expected); + } + + @Factory + public static ItemStackMatcher itemStack(ItemStack expected) { + return new ItemStackMatcher(expected); + } + + @Factory + public static Matcher> itemStacks(Collection items) { + return itemStacks(items.toArray(new ItemStack[0])); + } + + @Factory + public static Matcher> itemStacks(ItemStack... items) { + List> matchers = new ArrayList<>(); + for (ItemStack item : items) { + matchers.add(itemStack(item)); + } + return contains(matchers); + } +} diff --git a/bukkit-utils/src/test/java/dk/lockfuglsang/minecraft/util/ItemStackUtilTest.java b/bukkit-utils/src/test/java/dk/lockfuglsang/minecraft/util/ItemStackUtilTest.java new file mode 100644 index 000000000..dcba69b76 --- /dev/null +++ b/bukkit-utils/src/test/java/dk/lockfuglsang/minecraft/util/ItemStackUtilTest.java @@ -0,0 +1,170 @@ +package dk.lockfuglsang.minecraft.util; + +import org.bukkit.Material; +import org.bukkit.inventory.ItemStack; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; + +import static dk.lockfuglsang.minecraft.util.ItemStackMatcher.itemStack; +import static dk.lockfuglsang.minecraft.util.ItemStackMatcher.itemStacks; +import static org.hamcrest.CoreMatchers.*; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.fail; + +public class ItemStackUtilTest extends BukkitServerMock { + + @BeforeClass + public static void setUpClass() throws Exception { + setupServerMock(); + } + + @Before + public void setUp() { + useMetaData = false; + itemMetaMap.clear(); + } + + @Test + public void createBasicItemRequirement() { + ItemRequirement actual = ItemStackUtil.createItemRequirement("stone:1"); + ItemRequirement expected = new ItemRequirement(new ItemStack(Material.STONE), 1, ItemRequirement.Operator.NONE, 0.0); + assertThat(actual, is(expected)); + } + + @Test + public void createItemRequirementWithDifferentIncrements() { + var specifications = List.of("stone:1", "stone:12;+1", "stone:1;-10", "stone:100;*0.1", "stone:1;/12.1"); + var expected = List.of( + new ItemRequirement(new ItemStack(Material.STONE), 1, ItemRequirement.Operator.NONE, 0.0), + new ItemRequirement(new ItemStack(Material.STONE), 12, ItemRequirement.Operator.ADD, 1.0), + new ItemRequirement(new ItemStack(Material.STONE), 1, ItemRequirement.Operator.SUBTRACT, 10.0), + new ItemRequirement(new ItemStack(Material.STONE), 100, ItemRequirement.Operator.MULTIPLY, 0.1), + new ItemRequirement(new ItemStack(Material.STONE), 1, ItemRequirement.Operator.DIVIDE, 12.1) + ); + for (int i = 0; i < specifications.size(); i++) { + ItemRequirement actual = ItemStackUtil.createItemRequirement(specifications.get(i)); + assertThat(actual, is(expected.get(i))); + } + } + + @Test + public void createComplexItemRequirement() { + ItemRequirement actual = ItemStackUtil.createItemRequirement("minecraft:white_banner[complex={item:[]}]:1923;/0.001"); + ItemRequirement expected = new ItemRequirement(new ItemStack(Material.WHITE_BANNER), 1923, ItemRequirement.Operator.DIVIDE, 0.001); + assertThat(actual, is(expected)); + assertThat(actual.type().getItemMeta().toString(), is("[complex={item:[]}]")); + } + + @Test + public void createItemsWithProbability1() { + List actual = ItemStackUtil.createItemsWithProbability(List.of("{p=0.9}LAVA_BUCKET:1")); + List expected = List.of(new ItemStackUtil.ItemProbability(0.9, new ItemStack(Material.LAVA_BUCKET, 1))); + assertThat(actual, notNullValue()); + assertThat(actual, is(expected)); + } + + @Test + public void createItemsWithProbabilityN() { + List actual = ItemStackUtil.createItemsWithProbability(List.of("{p=0.9}LAVA_BUCKET:1", "{p=0.2}STONE:3", "{p=0.3}NETHER_BRICK_FENCE:2")); + List expected = List.of(new ItemStackUtil.ItemProbability(0.9, new ItemStack(Material.LAVA_BUCKET, 1)), new ItemStackUtil.ItemProbability(0.2, new ItemStack(Material.STONE, 3)), new ItemStackUtil.ItemProbability(0.3, new ItemStack(Material.NETHER_BRICK_FENCE, 2))); + assertThat(actual, notNullValue()); + assertThat(actual, is(expected)); + } + + @Test + public void createItemsWithProbabilityWithComponents() { + useMetaData = true; + List actual = ItemStackUtil.createItemsWithProbability(List.of("{p=0.9}lava_bucket[some: value]:1", "{p=0.2}minecraft:stone[quoted: \"value\"]:3", "{p=0.3}NETHER_BRICK_FENCE[meta:{nested:{data:[{},{}]}}]:2")); + + assertThat(actual.get(0).item().getType(), is(Material.LAVA_BUCKET)); + assertThat(actual.get(0).item().getItemMeta().toString(), is("[some: value]")); + assertThat(actual.get(0).probability(), is(0.9)); + + assertThat(actual.get(1).item().getType(), is(Material.STONE)); + assertThat(actual.get(1).item().getItemMeta().toString(), is("[quoted: \"value\"]")); + assertThat(actual.get(1).probability(), is(0.2)); + + assertThat(actual.get(2).item().getType(), is(Material.NETHER_BRICK_FENCE)); + assertThat(actual.get(2).item().getItemMeta().toString(), is("[meta:{nested:{data:[{},{}]}}]")); + assertThat(actual.get(2).probability(), is(0.3)); + } + + @Test + public void createItemListInvalid() { + try { + ItemStackUtil.createItemList(List.of("DART")); + fail("Expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + assertThat(e.getMessage(), is("Unknown item: 'DART'")); + } + } + + @Test + public void createItemList() { + List actual = ItemStackUtil.createItemList(List.of("LAVA_BUCKET:1", "STONE:3", "NETHER_BRICK_FENCE:2")); + List expected = List.of(new ItemStack(Material.LAVA_BUCKET, 1), new ItemStack(Material.STONE, 3), new ItemStack(Material.NETHER_BRICK_FENCE, 2)); + assertThat(actual, itemStacks(expected)); + } + + @Test + public void createItemListStringAndListWithComponents() { + List actual = ItemStackUtil.createItemList(List.of("diamond_sword[damage:200]:2", "white_banner[minecraft:banner_patterns=[{color: \"green\", pattern: \"minecraft:creeper\"}]]:256")); + + assertThat(actual.get(0).getType(), is(Material.DIAMOND_SWORD)); + assertThat(actual.get(1).getType(), is(Material.WHITE_BANNER)); + + assertThat(actual.get(0).getItemMeta().toString(), is("[damage:200]")); + assertThat(actual.get(1).getItemMeta().toString(), is("[minecraft:banner_patterns=[{color: \"green\", pattern: \"minecraft:creeper\"}]]")); + + assertThat(actual.get(0).getAmount(), is(2)); + assertThat(actual.get(1).getAmount(), is(256)); + } + + @Test + public void createItemArrayNull() { + ItemStack[] actual = ItemStackUtil.createItemArray(null); + assertThat(actual, is(new ItemStack[0])); + } + + @Test + public void createItemArrayEmpty() { + ItemStack[] actual = ItemStackUtil.createItemArray(List.of()); + assertThat(actual, is(new ItemStack[0])); + } + + @Test + public void createItemArray() { + List expected = List.of(new ItemStack(Material.LAVA_BUCKET, 1), new ItemStack(Material.STONE, 3), new ItemStack(Material.NETHER_BRICK_FENCE, 2), new ItemStack(Material.JUNGLE_WOOD, 256) // Jungle Wood Planks + ); + ItemStack[] actual = ItemStackUtil.createItemArray(expected); + assertThat(actual, is(expected.toArray())); + } + + @Test + public void createItemStackName() { + ItemStack actual = ItemStackUtil.createItemStack("DIRT"); + ItemStack expected = new ItemStack(Material.DIRT, 1); + assertThat(actual, itemStack(expected)); + + actual = ItemStackUtil.createItemStack("DIORITE"); + expected = new ItemStack(Material.DIORITE, 1); + assertThat(actual, itemStack(expected)); + } + + @Test + public void testClone() { + List orig = new ArrayList<>(List.of(new ItemStack(Material.LAVA, 1), new ItemStack(Material.DIRT, 2), new ItemStack(Material.STONE, 3))); + List clone = ItemStackUtil.clone(orig); + assertThat(clone, itemStacks(orig)); + orig.get(0).setAmount(10); + orig.get(1).setAmount(20); + orig.remove(2); + assertThat(clone, not(orig)); + assertThat(clone.size(), is(3)); + assertThat(clone.get(1).getAmount(), is(2)); + } +} diff --git a/bukkit-utils/src/test/java/dk/lockfuglsang/minecraft/util/LocationUtilTest.java b/bukkit-utils/src/test/java/dk/lockfuglsang/minecraft/util/LocationUtilTest.java new file mode 100644 index 000000000..397528f89 --- /dev/null +++ b/bukkit-utils/src/test/java/dk/lockfuglsang/minecraft/util/LocationUtilTest.java @@ -0,0 +1,63 @@ +package dk.lockfuglsang.minecraft.util; + +import org.bukkit.Location; +import org.bukkit.Server; +import org.bukkit.World; +import org.junit.BeforeClass; +import org.junit.Test; + +import static org.hamcrest.Matchers.is; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class LocationUtilTest { + @BeforeClass + public static void SetupOnce() throws Exception { + Server server = BukkitServerMock.setupServerMock(); + when(server.getWorld(anyString())).thenAnswer((a) -> { + World mock = mock(World.class); + when(mock.getName()).thenReturn(a.getArguments()[0].toString()); + return mock; + }); + } + + @Test + public void asString() { + World world = mock(World.class); + when(world.getName()).thenReturn("world"); + Location loc = new Location(world, 1.12, -34.12, 2.0); + + assertThat(LocationUtil.asString(loc), is("world:1.12,-34.12,2.00")); + } + + @Test + public void asKey() { + World world = mock(World.class); + when(world.getName()).thenReturn("world"); + Location loc = new Location(world, 1.12, -34.12, 2.0); + + assertThat(LocationUtil.asKey(loc), is("world/1_12,-34_12,2_00")); + } + + @Test + public void fromString_asString() { + World world = mock(World.class); + when(world.getName()).thenReturn("world"); + Location loc = new Location(world, 1.12, -34.12, 2.0); + + String str = "world:1.12,-34.12,2.00"; + assertThat(LocationUtil.asString(LocationUtil.fromString(str)), is(LocationUtil.asString(loc))); + } + + @Test + public void fromString_asKey() { + World world = mock(World.class); + when(world.getName()).thenReturn("world"); + Location loc = new Location(world, 1.12, -34.12, 2.0); + + String key = "world/1_12,-34_12,2_00"; + assertThat(LocationUtil.asString(LocationUtil.fromString(key)), is(LocationUtil.asString(loc))); + } +} diff --git a/bukkit-utils/src/test/java/dk/lockfuglsang/minecraft/yml/YmlConfigurationTest.java b/bukkit-utils/src/test/java/dk/lockfuglsang/minecraft/yml/YmlConfigurationTest.java new file mode 100644 index 000000000..4db603ec3 --- /dev/null +++ b/bukkit-utils/src/test/java/dk/lockfuglsang/minecraft/yml/YmlConfigurationTest.java @@ -0,0 +1,32 @@ +package dk.lockfuglsang.minecraft.yml; + +import org.bukkit.configuration.file.YamlConfiguration; +import org.hamcrest.Matchers; +import org.junit.Test; + +import java.io.File; +import java.util.List; + +import static org.hamcrest.MatcherAssert.assertThat; + +public class YmlConfigurationTest { + @Test + public void testGetStringList_List() throws Exception { + File simpleYml = new File(getClass().getClassLoader().getResource("yml/simple.yml").toURI()); + YamlConfiguration config = new YamlConfiguration(); + config.load(simpleYml); + + List actual = config.getStringList("root.child node.some-section.another-list"); + assertThat(actual, Matchers.contains("what now", "do you know?")); + } + + @Test + public void testGetStringList_OneLineList() throws Exception { + File simpleYml = new File(getClass().getClassLoader().getResource("yml/simple.yml").toURI()); + YamlConfiguration config = new YamlConfiguration(); + config.load(simpleYml); + + List actual = config.getStringList("root.child node.some-section.section-list"); + assertThat(actual, Matchers.contains("Hej", "Mor")); + } +} diff --git a/bukkit-utils/src/test/resources/yml/.gitattributes b/bukkit-utils/src/test/resources/yml/.gitattributes new file mode 100644 index 000000000..b595ec7f3 --- /dev/null +++ b/bukkit-utils/src/test/resources/yml/.gitattributes @@ -0,0 +1 @@ +*.yml text eol=crlf \ No newline at end of file diff --git a/bukkit-utils/src/test/resources/yml/pluginyml_featuremap.yml b/bukkit-utils/src/test/resources/yml/pluginyml_featuremap.yml new file mode 100644 index 000000000..7f278c483 --- /dev/null +++ b/bukkit-utils/src/test/resources/yml/pluginyml_featuremap.yml @@ -0,0 +1,50 @@ +commands: + cmd: + description: 'some description' + aliases: [c] + permission: plugin.cmd +permissions: + # + # Permission Groups + # ================= + plugin.*: + children: + plugin.cmd: true + plugin.cmd.other: true + plugin.feature: true + plugin.feature.a: true + plugin.feature.b: true + plugin.featuresub: true + plugin.sub: true + + plugin.cmd.*: + children: + plugin.cmd.other: true + + plugin.feature.*: + children: + plugin.feature.a: true + plugin.feature.b: true + + # + # Permission Descriptions + # ======================= + plugin.cmd: + description: 'Grants access to /cmd - some description' + + plugin.cmd.other: + description: 'Grants access to /cmd other - some other command' + + plugin.feature.a: + description: 'enables A' + + plugin.feature.b: + description: 'enables B' + + plugin.featuresub: + description: 'standalone feature' + + plugin.sub: + description: 'Grants access to /cmd sub - some sub description' + + diff --git a/bukkit-utils/src/test/resources/yml/pluginyml_simple.yml b/bukkit-utils/src/test/resources/yml/pluginyml_simple.yml new file mode 100644 index 000000000..cf19c067b --- /dev/null +++ b/bukkit-utils/src/test/resources/yml/pluginyml_simple.yml @@ -0,0 +1,46 @@ +commands: + cmd: + description: 'some description' + aliases: [c] + permission: plugin.cmd + adm: + description: 'hey jude!' + aliases: [a] + permission: plugin.adm +permissions: + # + # Permission Groups + # ================= + plugin.*: + children: + plugin.adm: true + plugin.cmd: true + plugin.cmd.other: true + plugin.cmdtest: true + plugin.sub: true + + plugin.cmd.*: + children: + plugin.cmd.other: true + + # + # Permission Descriptions + # ======================= + plugin.adm: + description: 'Grants access to /adm - hey jude!' + + plugin.cmd: + description: 'Grants access to /cmd - some description' + + plugin.cmd.other: + description: 'Grants access to /cmd other - some other command' + + plugin.cmdtest: + description: 'Grants access to /adm t2 [optional] - test' + + plugin.sub: + description: | + Grants access to /cmd sub - some sub description + /adm subs - some other sub + + diff --git a/bukkit-utils/src/test/resources/yml/simple.yml b/bukkit-utils/src/test/resources/yml/simple.yml new file mode 100644 index 000000000..4eabc61f0 --- /dev/null +++ b/bukkit-utils/src/test/resources/yml/simple.yml @@ -0,0 +1,25 @@ +# +# This is a simple Yml file +# +# with multiple comments +root: + child-value: 10 + + # child nodes + child node: + some-double: 1.34 # a number + some-section: + section-list: [Hej, Mor] + another-list: + - what now + - do you know? + space-list: '13:1 34:3' + # this is a key and a value + key: 'value' + another node: + enabled: true + # a comment + a.section.deeper: + b: # b comment + c: false # should be false +"an.annoying:key this.is:for.sure": hello world \ No newline at end of file diff --git a/bukkit-utils/src/test/resources/yml/simple_expected.yml b/bukkit-utils/src/test/resources/yml/simple_expected.yml new file mode 100644 index 000000000..cd0d1735f --- /dev/null +++ b/bukkit-utils/src/test/resources/yml/simple_expected.yml @@ -0,0 +1,37 @@ +# +# This is a simple Yml file +# +# with multiple comments +root: + child-value: 10 + + # child nodes + child node: + some-double: 1.34 # a number + some-section: + section-list: + - Hej + - Mor + another-list: + - what now + - do you know? + space-list: 13:1 34:3 + # this is a key and a value + key: value + abe: |- + lincoln + was a wonderful + president + another node: + enabled: true + a: + section: + # a comment + deeper: + b: # b comment + c: false # should be false +an: + annoying:key this: + is:for: + sure: hello world + diff --git a/changelog.md b/changelog.md deleted file mode 100644 index 105d0400a..000000000 --- a/changelog.md +++ /dev/null @@ -1,25 +0,0 @@ -## Change Log for v2.7.6..v2.7.6-201809101626 - - -## Translations -``` -Gettext Statistics -Locale | code | % | Trans | Untr | Fuzzy | Translator --------------------- | ---------- | ---- | -------- | -------- | -------- | ---------------- -Chinese (China) | zh_CN | 31% | 230 | 524 | 0 | Miku_Snow -Czech | cs | 80% | 604 | 150 | 0 | martyHR -Danish | da | 94% | 706 | 48 | 0 | R4zorax -Dutch | nl | 91% | 683 | 71 | 0 | clrxbl -English (UK) | en_GB | 92% | 696 | 58 | 0 | dutchy1001 -French | fr | 82% | 616 | 138 | 0 | Wura -German | de | 87% | 656 | 98 | 0 | I9hdkill I9hdkill@spigotmc.org -Italian | it | 97% | 732 | 22 | 0 | Leomixer17 -Korean | ko | 90% | 676 | 78 | 0 | swan201 -Portuguese (Brazil) | pt_BR | 62% | 466 | 288 | 0 | _Rodrigo -Russian | ru | 62% | 465 | 289 | 0 | Error203 -Spanish | es | 62% | 469 | 285 | 0 | Delu -Swedish | sv | 87% | 653 | 101 | 0 | maol3 -Vietnamese (Vietnam) | vi_VN | 76% | 574 | 180 | 0 | -xx (LOL) | xx_LOL | 90% | 680 | 74 | 0 | Woolwind -xx (PIRATE) | xx_PIRATE | 90% | 680 | 74 | 0 | R4zorax -``` diff --git a/changelog.sh b/changelog.sh deleted file mode 100644 index 3ffa76390..000000000 --- a/changelog.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/bash -newversion=$1 -lastversion=$2 -if [[ -z $lastversion ]];then - tag=$( git describe --tags --abbrev=0 ) - lastversion="${tag%-*}" -fi -if [[ -z $newversion ]];then - newversion="HEAD" -fi -echo -e "## Change Log for ${lastversion}..${newversion}\n" -git log --oneline ${lastversion}..HEAD | grep "#" | awk '{printf(" * %s\n",$0)}' -echo -e "\n## Translations" -echo '```' -cat uSkyBlock-Core/target/classes/gettext-report.txt -echo '```' \ No newline at end of file diff --git a/crowdin.yml b/crowdin.yml new file mode 100644 index 000000000..22256eadf --- /dev/null +++ b/crowdin.yml @@ -0,0 +1,16 @@ +commit_message: "[ci skip]" +files: + - source: /uSkyBlock-Core/src/main/po/keys.pot + translation: /uSkyBlock-Core/src/main/po/%locale_with_underscore%.po + languages_mapping: + locale_with_underscore: + cs: cs + da: da + de: de + es-ES: es + fr: fr + it: it + ko: ko + nl: nl + ru: ru + sv: sv diff --git a/deploy_rsa.enc b/deploy_rsa.enc deleted file mode 100644 index d33709662..000000000 Binary files a/deploy_rsa.enc and /dev/null differ diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 000000000..d9c76c6b7 --- /dev/null +++ b/gradle.properties @@ -0,0 +1,7 @@ +systemProp.http.proxyHost=127.0.0.1 +systemProp.http.proxyPort=7890 + +systemProp.https.proxyHost=127.0.0.1 +systemProp.https.proxyPort=7890 + +org.gradle.java.installations.paths=D:/Java/jdk-21.0.3 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000..52e7c5f0f --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Sat Apr 26 12:58:38 CST 2025 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100644 index 000000000..a69d9cb6c --- /dev/null +++ b/gradlew @@ -0,0 +1,240 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# 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 POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +APP_NAME="Gradle" +APP_BASE_NAME=${0##*/} + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +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" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 000000000..f127cfd49 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,91 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@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 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, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +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" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +: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 %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/po-utils/build.gradle.kts b/po-utils/build.gradle.kts new file mode 100644 index 000000000..e179685ee --- /dev/null +++ b/po-utils/build.gradle.kts @@ -0,0 +1,18 @@ +/* + * This file was generated by the Gradle 'init' task. + */ + + +dependencies { + implementation("org.jetbrains:annotations:23.0.0") + testImplementation("junit:junit:4.13.2") + testImplementation("org.junit.vintage:junit-vintage-engine:5.9.0") + testImplementation("org.hamcrest:hamcrest:2.2") + testImplementation("org.hamcrest:hamcrest-library:2.2") +} + +description = "po-utils" + +java { + withJavadocJar() +} diff --git a/po-utils/pom.xml b/po-utils/pom.xml deleted file mode 100644 index 067655151..000000000 --- a/po-utils/pom.xml +++ /dev/null @@ -1,102 +0,0 @@ - - - - uSkyBlock - uSkyBlock - 2.8.8-SNAPSHOT - - 4.0.0 - jar - po-utils - - - false - - - - - internal.repo - Temporary Staging Repository - file://${project.build.directory}/mvn-repo - - - - - - org.jetbrains - annotations - 17.0.0 - - - - junit - junit - ${junit.version} - test - - - org.junit.vintage - junit-vintage-engine - ${junit-vintage-engine.version} - test - - - org.hamcrest - hamcrest - ${hamcrest.version} - test - - - org.hamcrest - hamcrest-library - ${hamcrest.version} - test - - - - - - - maven-compiler-plugin - - 1.8 - 1.8 - utf-8 - - - - org.apache.maven.plugins - maven-jar-plugin - 3.1.2 - - - org.apache.maven.plugins - maven-javadoc-plugin - 3.1.0 - - public - false - none - - - - - javadoc - - deploy - - - - - org.apache.maven.plugins - maven-surefire-plugin - - - org.apache.maven.plugins - maven-failsafe-plugin - - - - \ No newline at end of file diff --git a/po-utils/src/main/java/dk/lockfuglsang/minecraft/po/I18nUtil.java b/po-utils/src/main/java/dk/lockfuglsang/minecraft/po/I18nUtil.java index 2263853c9..ce9f45487 100644 --- a/po-utils/src/main/java/dk/lockfuglsang/minecraft/po/I18nUtil.java +++ b/po-utils/src/main/java/dk/lockfuglsang/minecraft/po/I18nUtil.java @@ -10,10 +10,10 @@ import java.io.InputStream; import java.nio.charset.StandardCharsets; import java.text.MessageFormat; -import java.util.ArrayList; -import java.util.List; import java.util.Locale; +import java.util.Optional; import java.util.Properties; +import java.util.logging.Level; import java.util.logging.Logger; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; @@ -24,9 +24,13 @@ public enum I18nUtil { ; private static final Logger log = Logger.getLogger(I18nUtil.class.getName()); - private static I18n i18n; - private static Locale locale; - private static File dataFolder = new File("."); + + // Dedicated lock for synchronizing modifications + private static final Object LOCK = new Object(); + + private static volatile I18n i18n; + private static volatile Locale locale; + private static volatile File dataFolder; /** * Translates the given {@link String} to the configured language. Returns the given String if no translation is @@ -56,7 +60,7 @@ public static String tr(@Nullable String s, @Nullable Object... args) { * @param key String to mark. * @return Input String. */ - @Contract("null -> null") + @Contract(value = "null -> null", pure = true) public static String marktr(@Nullable String key) { return key; } @@ -82,14 +86,30 @@ public static String pre(@Nullable String s, @Nullable Object... args) { * @return I18n instance for the configured locale. */ public static I18n getI18n() { - if (i18n == null) { + I18n result = i18n; + if (result == null) { + throw new IllegalStateException("I18nUtil not initialized!"); + } + return result; + } + + /** + * Initializes the I18nUtil. This method is called whenever the plugin loads or reloads. + * Thread safety is ensured by synchronizing modifications of shared fields. + * + * @param folder The plugin's data folder. + * @param loc The desired Locale. If null, Locale.ENGLISH is used. + */ + public static void initialize(@NotNull File folder, @Nullable Locale loc) { + synchronized (LOCK) { + dataFolder = folder; + locale = loc; i18n = new I18n(getLocale()); } - return i18n; } /** - * Returns the configured {@link Locale} or the default if unset. + * Returns the configured {@link Locale} or the default (Locale.ENGLISH) if unset. * @return Configured Locale. */ @NotNull @@ -99,28 +119,24 @@ public static Locale getLocale() { /** * Sets the {@link Locale}. Resets to the default locale if NULL is given. - * @param locale Locale to set. + * The I18n cache is cleared so that translations are reloaded. + * + * @param loc Locale to set. */ - public static void setLocale(@Nullable Locale locale) { - I18nUtil.locale = locale; - clearCache(); - } - - /** - * Sets the datafolder that is used to look for .po files. - * @param folder Location of the datafolder. - */ - public static void setDataFolder(@NotNull File folder) { - dataFolder = folder; - clearCache(); + public static void setLocale(@Nullable Locale loc) { + synchronized (LOCK) { + locale = loc; + clearCache(); + } } /** - * Clears the I18n cache, forces a reload of the .po files the next time that {@link I18nUtil#getLocale()} is - * accessed. + * Clears the I18n cache, forcing a reload of the .po files the next time that {@link I18nUtil#getLocale()} is accessed. */ public static void clearCache() { - i18n = null; + synchronized (LOCK) { + i18n = new I18n(getLocale()); + } } /** @@ -128,16 +144,16 @@ public static void clearCache() { * @param lang Language code.. * @return Locale based on the given string. */ - @Contract("null -> null") + @Contract(value = "null -> null", pure = true) public static Locale getLocale(@Nullable String lang) { if (lang != null) { - String[] parts = lang.split("[_\\-]"); + String[] parts = lang.split("[-_]"); if (parts.length >= 3) { - return new Locale(parts[0], parts[1], parts[2]); + return Locale.of(parts[0], parts[1], parts[2]); } else if (parts.length == 2) { - return new Locale(parts[0], parts[1]); + return Locale.of(parts[0], parts[1]); } else { - return new Locale(parts[0]); + return Locale.of(parts[0]); } } return null; @@ -148,31 +164,42 @@ public static Locale getLocale(@Nullable String lang) { */ public static class I18n { private final Locale locale; - private List props; + private final Properties translations = new Properties(); I18n(Locale locale) { this.locale = locale; - props = new ArrayList<>(); - addPropsFromPluginFolder(); - addPropsFromJar(); - addPropsFromZipInJar(); + + // Order of these calls is important here, because it specifies the priority of the files (git rlf/1233). + addPropertiesFromZipInJar().ifPresent(properties -> { + translations.putAll(properties); + log.log(Level.INFO, "Added {0} translations from ZIP inside JAR.", properties.size()); + }); + addPropertiesFromJar().ifPresent(properties -> { + translations.putAll(properties); + log.log(Level.INFO, "Added {0} translations from the JAR.", properties.size()); + }); + addPropertiesFromPluginFolder().ifPresent(properties -> { + translations.putAll(properties); + log.log(Level.INFO, "Added {0} translations from the plugin directory.", properties.size()); + }); + + log.log(Level.INFO, "Loaded {0} translations.", translations.size()); } - private void addPropsFromJar() { + private Optional addPropertiesFromJar() { try (InputStream in = getClass().getClassLoader().getResourceAsStream("po/" + locale + ".po")) { if (in == null) { - return; - } - Properties i18nProps = POParser.asProperties(in); - if (i18nProps != null && !i18nProps.isEmpty()) { - props.add(i18nProps); + return Optional.empty(); } + Properties properties = POParser.asProperties(in); + return Optional.ofNullable(properties); } catch (IOException e) { log.info("Unable to read translations from po/" + locale + ".po: " + e); } + return Optional.empty(); } - private void addPropsFromZipInJar() { + private Optional addPropertiesFromZipInJar() { // We zip the .po files, since they are currently half the footprint of the jar. try ( InputStream in = getClass().getClassLoader().getResourceAsStream("i18n.zip"); @@ -182,40 +209,36 @@ private void addPropsFromZipInJar() { do { nextEntry = zin != null ? zin.getNextEntry() : null; if (nextEntry != null && nextEntry.getName().equalsIgnoreCase(locale + ".po")) { - Properties i18nProps = POParser.asProperties(zin); - if (i18nProps != null && !i18nProps.isEmpty()) { - props.add(i18nProps); - } + Properties properties = POParser.asProperties(zin); + return Optional.ofNullable(properties); } } while (nextEntry != null); } catch (IOException e) { log.info("Unable to load translations from i18n.zip!" + locale + ".po: " + e); } + return Optional.empty(); } - private void addPropsFromPluginFolder() { + private Optional addPropertiesFromPluginFolder() { File poFile = new File(dataFolder, "i18n" + File.separator + locale + ".po"); if (poFile.exists()) { try (InputStream in = new FileInputStream(poFile)) { - Properties i18nProps = POParser.asProperties(in); - if (i18nProps != null && !i18nProps.isEmpty()) { - props.add(i18nProps); - } + Properties properties = POParser.asProperties(in); + return Optional.ofNullable(properties); } catch (IOException e) { log.info("Unable to load translations from i18n" + File.separator + locale + ".po: " + e); } } + return Optional.empty(); } public String tr(String key, Object... args) { if (key == null || key.trim().isEmpty()) { return ""; } - for (Properties prop : props) { - String propKey = prop.getProperty(key); - if (propKey != null && prop.containsKey(key) && !propKey.trim().isEmpty()) { - return format(propKey, args); - } + String propKey = translations.getProperty(key); + if (propKey != null && !propKey.trim().isEmpty()) { + return format(propKey, args); } return format(key, args); } diff --git a/po-utils/src/test/java/dk/lockfuglsang/minecraft/po/I18nUtilTest.java b/po-utils/src/test/java/dk/lockfuglsang/minecraft/po/I18nUtilTest.java index 8bb4316c9..e0953aef9 100644 --- a/po-utils/src/test/java/dk/lockfuglsang/minecraft/po/I18nUtilTest.java +++ b/po-utils/src/test/java/dk/lockfuglsang/minecraft/po/I18nUtilTest.java @@ -7,14 +7,15 @@ import java.net.URL; import java.util.Locale; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.Is.is; -import static org.junit.Assert.*; +import static org.junit.Assert.assertNull; public class I18nUtilTest { @Before - public void setUp() throws Exception { + public void setUp() { URL dataFolderUrl = getClass().getClassLoader().getResource(""); - I18nUtil.setDataFolder(new File(dataFolderUrl.getFile())); + I18nUtil.initialize(new File(dataFolderUrl.getFile()), Locale.ENGLISH); } @Test @@ -101,7 +102,7 @@ public void testPre_withNonFormattedString() { assertThat(I18nUtil.pre(TEST_STRING), is(TEST_STRING)); } - + @Test public void testPre_withFormattedString() { String TEST_STRING = "\u00a7bThis is a test for {0} regarding {1}."; diff --git a/po-utils/src/test/java/dk/lockfuglsang/minecraft/po/POParserTest.java b/po-utils/src/test/java/dk/lockfuglsang/minecraft/po/POParserTest.java index f3edda683..0f7c522f9 100644 --- a/po-utils/src/test/java/dk/lockfuglsang/minecraft/po/POParserTest.java +++ b/po-utils/src/test/java/dk/lockfuglsang/minecraft/po/POParserTest.java @@ -7,7 +7,7 @@ import java.util.Properties; import static org.hamcrest.CoreMatchers.*; -import static org.junit.Assert.assertThat; +import static org.hamcrest.MatcherAssert.assertThat; public class POParserTest { @@ -74,5 +74,4 @@ private void verifyProps(String name) throws IOException { assertThat(properties.getProperty(key), is(value)); } } - -} \ No newline at end of file +} diff --git a/pom.xml b/pom.xml deleted file mode 100644 index 5407f7007..000000000 --- a/pom.xml +++ /dev/null @@ -1,216 +0,0 @@ - - 4.0.0 - uSkyBlock - uSkyBlock - pom - 2.8.8-SNAPSHOT - Ultimate SkyBlock - - - ${project.version} - 1.23-SNAPSHOT - 1.2 - 1.6 - 1.7 - - UTF-8 - ${project.artifactId} - invalid - dev - msgfmt - msgmerge - - 1.8 - 1.8 - - - true - - - 2.1 - 4.12 - 5.5.1 - 3.0.0 - 2.0.2 - - - - po-utils - uSkyBlock-API - uSkyBlock-Core - uSkyBlock-Plugin - uSkyBlock-FAWE - - - - scm:git:git://github.com/rlf/uSkyBlock.git - scm:git:git://github.com/rlf/uSkyBlock.git - https://github.com/rlf/uSkyBlock.git - - - - ${finalName} - - - - org.apache.maven.plugins - maven-compiler-plugin - 3.8.1 - - 1.8 - 1.8 - - - - org.apache.maven.plugins - maven-jar-plugin - 3.1.2 - - - org.apache.maven.plugins - maven-javadoc-plugin - 3.1.0 - - - org.apache.maven.plugins - maven-surefire-plugin - 2.22.2 - - - org.apache.maven.plugins - maven-failsafe-plugin - 2.22.2 - - - - - - - spigotmc.org - https://hub.spigotmc.org/nexus/content/repositories/public - - - papermc - https://papermc.io/repo/repository/maven-public/ - - - vault-repo - http://nexus.hc.to/content/repositories/pub_releases - - - sk89q-repo - http://maven.sk89q.com/repo/ - - - onarandombox - http://repo.onarandombox.com/nexus/content/groups/public - - - CodeMC - https://repo.codemc.org/repository/maven-public - - - mvdw-software - https://repo.mvdw-software.com/content/groups/public/ - - - jitpack.io - https://jitpack.io - - - uskyblock-dependencies - https://www.uskyblock.ovh/maven/dependencies/ - - - uskyblock-maven - https://www.uskyblock.ovh/maven/uskyblock/ - - - - - uskyblock-dependencies - https://www.uskyblock.ovh/maven/dependencies/ - - - - - 1.15 - - true - - - - com.sk89q.worldedit - worldedit-bukkit - 7.0.1 - provided - - - com.sk89q.worldguard - worldguard-bukkit - 7.0.1 - provided - - - com.sk89q - worldedit - - - - - - - - - - dk.lockfuglsang.minecraft - bukkit-utils - ${bukkit-utils.version} - - - dk.lockfuglsang.minecraft - bukkit-utils - ${bukkit-utils.version} - test - tests - - - dk.lockfuglsang.minecraft - po-utils - ${po-utils.version} - - - net.milkbowl.vault - VaultAPI - true - ${vault.version} - - - com.github.rlf - uSkyBlock-API - ${api.version} - - - uSkyBlock - uSkyBlock-Core - ${project.version} - - - uSkyBlock - uSkyBlock-FAWE - ${project.version} - - - uSkyBlock - po-utils - ${project.version} - - - me.clip.deluxechat - DeluxeChatPlaceholderAPI - ${deluxechat.version} - - - - \ No newline at end of file diff --git a/prerelease.sh b/prerelease.sh deleted file mode 100644 index 67b11897f..000000000 --- a/prerelease.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash -tag=$( git describe --tags --abbrev=0 ) -lastversion="${tag%-*}" -tstamp=$( date +%Y%m%d%H%M ) -newversion="$lastversion-$tstamp" -./changelog.sh $newversion $lastversion > changelog.md -git commit -a -m "Changelog for $tag" -git push -git tag $newversion -git push --tags diff --git a/release.sh b/release.sh deleted file mode 100644 index 741225d4c..000000000 --- a/release.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/bash -if [[ -z $1 ]]; then - echo "Usage: $0 " - exit -1 -fi -newversion=$1 -tag=$( git describe --tags --abbrev=0 ) -tstamp=$( date +%Y%m%d%H%M ) -lastversion="${tag%-*}" -./changelog.sh $newversion $lastversion > changelog.md -git commit -a -m "Changelog for $tag" -git push -git tag $newversion -git push --tags diff --git a/scripts/deploy-release.sh b/scripts/deploy-release.sh deleted file mode 100644 index 712261bb5..000000000 --- a/scripts/deploy-release.sh +++ /dev/null @@ -1,40 +0,0 @@ -#!/usr/bin/env bash - -if [[ "$TRAVIS_PULL_REQUEST" == "false" ]]; then - - echo -e "Running release script...\n" - echo -e "Publishing javadocs and artifacts...\n" - cd $HOME - - rsync -r --quiet $HOME/build/uskyblock/uSkyBlock/po-utils/target/mvn-repo/ \ - dool1@shell.xs4all.nl:WWW/maven/uskyblock/ - - rsync -r --quiet $HOME/build/uskyblock/uSkyBlock/uSkyBlock-API/target/mvn-repo/ \ - dool1@shell.xs4all.nl:WWW/maven/uskyblock/ - - rsync -r --quiet $HOME/build/uskyblock/uSkyBlock/uSkyBlock-Core/target/mvn-repo/ \ - dool1@shell.xs4all.nl:WWW/maven/uskyblock/ - - rsync -r --quiet $HOME/build/uskyblock/uSkyBlock/uSkyBlock-FAWE/target/mvn-repo/ \ - dool1@shell.xs4all.nl:WWW/maven/uskyblock/ - - echo -e "Publishing javadocs...\n" - - rsync -r --delete --quiet $HOME/build/uskyblock/uSkyBlock/po-utils/target/site/apidocs/ \ - dool1@shell.xs4all.nl:WWW/javadocs/release/po-utils/ - - rsync -r --delete --quiet $HOME/build/uskyblock/uSkyBlock/uSkyBlock-API/target/site/apidocs/ \ - dool1@shell.xs4all.nl:WWW/javadocs/release/uSkyBlock-API/ - - rsync -r --delete --quiet $HOME/build/uskyblock/uSkyBlock/uSkyBlock-Core/target/site/apidocs/ \ - dool1@shell.xs4all.nl:WWW/javadocs/release/uSkyBlock-Core/ - - rsync -r --delete --quiet $HOME/build/uskyblock/uSkyBlock/uSkyBlock-FAWE/target/site/apidocs/ \ - dool1@shell.xs4all.nl:WWW/javadocs/release/uSkyBlock-FAWE/ - - echo -e "Publishing final plugin release...\n" - - rsync -r --quiet $HOME/build/uskyblock/uSkyBlock/uSkyBlock-Plugin/target/uSkyBlock-*.jar \ - dool1@shell.xs4all.nl:WWW/downloads/release/uSkyBlock/ - -fi diff --git a/scripts/deploy-staging.sh b/scripts/deploy-staging.sh deleted file mode 100644 index 150e817ec..000000000 --- a/scripts/deploy-staging.sh +++ /dev/null @@ -1,40 +0,0 @@ -#!/usr/bin/env bash - -if [[ "$TRAVIS_PULL_REQUEST" == "false" ]]; then - - echo -e "Running staging script...\n" - echo -e "Publishing javadocs and artifacts...\n" - cd $HOME - - rsync -r --quiet $HOME/build/uskyblock/uSkyBlock/po-utils/target/mvn-repo/ \ - dool1@shell.xs4all.nl:WWW/maven/uskyblock/ - - rsync -r --quiet $HOME/build/uskyblock/uSkyBlock/uSkyBlock-API/target/mvn-repo/ \ - dool1@shell.xs4all.nl:WWW/maven/uskyblock/ - - rsync -r --quiet $HOME/build/uskyblock/uSkyBlock/uSkyBlock-Core/target/mvn-repo/ \ - dool1@shell.xs4all.nl:WWW/maven/uskyblock/ - - rsync -r --quiet $HOME/build/uskyblock/uSkyBlock/uSkyBlock-FAWE/target/mvn-repo/ \ - dool1@shell.xs4all.nl:WWW/maven/uskyblock/ - - echo -e "Publishing javadocs...\n" - - rsync -r --delete --quiet $HOME/build/uskyblock/uSkyBlock/po-utils/target/site/apidocs/ \ - dool1@shell.xs4all.nl:WWW/javadocs/master/po-utils/ - - rsync -r --delete --quiet $HOME/build/uskyblock/uSkyBlock/uSkyBlock-API/target/site/apidocs/ \ - dool1@shell.xs4all.nl:WWW/javadocs/master/uSkyBlock-API/ - - rsync -r --delete --quiet $HOME/build/uskyblock/uSkyBlock/uSkyBlock-Core/target/site/apidocs/ \ - dool1@shell.xs4all.nl:WWW/javadocs/master/uSkyBlock-Core/ - - rsync -r --delete --quiet $HOME/build/uskyblock/uSkyBlock/uSkyBlock-FAWE/target/site/apidocs/ \ - dool1@shell.xs4all.nl:WWW/javadocs/master/uSkyBlock-FAWE/ - - echo -e "Publishing final plugin release...\n" - - rsync -r --quiet $HOME/build/uskyblock/uSkyBlock/uSkyBlock-Plugin/target/uSkyBlock-*.jar \ - dool1@shell.xs4all.nl:WWW/downloads/master/uSkyBlock/ - -fi diff --git a/settings.gradle.kts b/settings.gradle.kts new file mode 100644 index 000000000..e250c6b30 --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1,13 @@ +/* + * This file was generated by the Gradle 'init' task. + */ + +rootProject.name = "uSkyBlock" +include(":bukkit-utils") +include(":po-utils") +include(":uSkyBlock-APIv2") +include(":uSkyBlock-FAWE") +include(":uSkyBlock-Core") +include(":bukkit-utils") +include(":uSkyBlock-Plugin") +include(":uSkyBlock-API") diff --git a/uSkyBlock-API/build.gradle.kts b/uSkyBlock-API/build.gradle.kts new file mode 100644 index 000000000..5f60352d9 --- /dev/null +++ b/uSkyBlock-API/build.gradle.kts @@ -0,0 +1,13 @@ +/* + * This file was generated by the Gradle 'init' task. + */ + + +dependencies { + implementation("org.jetbrains:annotations:23.0.0") + compileOnly("org.spigotmc:spigot-api:1.20.6-R0.1-SNAPSHOT") +} + +group = "com.github.rlf" +description = "uSkyBlock-API" + diff --git a/uSkyBlock-API/pom.xml b/uSkyBlock-API/pom.xml deleted file mode 100644 index c38608171..000000000 --- a/uSkyBlock-API/pom.xml +++ /dev/null @@ -1,89 +0,0 @@ - - - 4.0.0 - - com.github.rlf - uSkyBlock-API - 2.8.8-SNAPSHOT - - UTF-8 - dev - github - false - - - - internal.repo - Temporary Staging Repository - file://${project.build.directory}/mvn-repo - - - - - spigotmc.org - https://hub.spigotmc.org/nexus/content/repositories/public - - - - - org.jetbrains - annotations - 17.0.0 - - - - - - org.apache.maven.plugins - maven-compiler-plugin - 3.8.1 - - 1.8 - 1.8 - - - - org.apache.maven.plugins - maven-jar-plugin - 3.1.2 - - - org.apache.maven.plugins - maven-javadoc-plugin - 3.1.0 - - public - false - none - - - - - javadoc - - deploy - - - - - - - - 1.15 - - true - - - - org.bukkit - bukkit - 1.15-R0.1-SNAPSHOT - true - compile - - - - - \ No newline at end of file diff --git a/uSkyBlock-API/src/main/java/us/talabrek/ultimateskyblock/api/ChallengeCompletion.java b/uSkyBlock-API/src/main/java/us/talabrek/ultimateskyblock/api/ChallengeCompletion.java index 9d988c1c2..8139172c8 100644 --- a/uSkyBlock-API/src/main/java/us/talabrek/ultimateskyblock/api/ChallengeCompletion.java +++ b/uSkyBlock-API/src/main/java/us/talabrek/ultimateskyblock/api/ChallengeCompletion.java @@ -1,12 +1,20 @@ package us.talabrek.ultimateskyblock.api; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.time.Duration; +import java.time.Instant; + /** * Represents a challenge-completion. + * * @since 2.7.0 */ public interface ChallengeCompletion { /** * The name of the challenge. + * * @return name of the challenge. * @since 2.7.0 */ @@ -14,13 +22,28 @@ public interface ChallengeCompletion { /** * The timestamp at which the cooldown runs out. + * * @return The timestamp at which the cooldown runs out. * @since 2.7.0 + * @deprecated Use {@link #cooldownUntil()} instead. */ - long getCooldownUntil(); + @Deprecated(since = "3.2.0") + default long getCooldownUntil() { + Instant cooldownUntil = cooldownUntil(); + return cooldownUntil == null ? 0 : cooldownUntil.toEpochMilli(); + } + + /** + * The timestamp at which the cooldown runs out. + * + * @return The timestamp at which the cooldown runs out, or null if there is no cooldown. + * @since 3.2.0 + */ + @Nullable Instant cooldownUntil(); /** * Whether or not the challenge is currently on cooldown. + * * @return Whether or not the challenge is currently on cooldown. * @since 2.7.0 */ @@ -28,13 +51,27 @@ public interface ChallengeCompletion { /** * How many milliseconds of the cooldown is left + * * @return How many milliseconds of the cooldown is left * @since 2.7.0 + * @deprecated Use {@link #getCooldown()} instead. + */ + @Deprecated(since = "3.2.0") + default long getCooldownInMillis() { + return getCooldown().toMillis(); + } + + /** + * How much of the cooldown is left + * + * @return The duration of the cooldown that is left + * @since 3.2.0 */ - long getCooldownInMillis(); + @NotNull Duration getCooldown(); /** * Total number of times the challenge has been completed + * * @return Total number of times the challenge has been completed * @since 2.7.0 */ @@ -42,6 +79,7 @@ public interface ChallengeCompletion { /** * Number of times the challenge has been completed within this cooldown. + * * @return Number of times the challenge has been completed within this cooldown. * @since 2.7.0 */ diff --git a/uSkyBlock-API/src/main/java/us/talabrek/ultimateskyblock/api/IslandInfo.java b/uSkyBlock-API/src/main/java/us/talabrek/ultimateskyblock/api/IslandInfo.java index 37cf90b94..3ea824561 100644 --- a/uSkyBlock-API/src/main/java/us/talabrek/ultimateskyblock/api/IslandInfo.java +++ b/uSkyBlock-API/src/main/java/us/talabrek/ultimateskyblock/api/IslandInfo.java @@ -3,11 +3,13 @@ import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.OfflinePlayer; +import org.bukkit.block.Biome; import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Set; @@ -75,9 +77,28 @@ public interface IslandInfo { /** * The name of the biome. + * + * @deprecated Unsafe String value, use {@link #getIslandBiome()} or {@link #getBiomeName()} instead. + * @return The name of the biome. + */ + @Deprecated(since="3.1.0") + default String getBiome() { + return getIslandBiome().name().toUpperCase(Locale.ROOT); + } + + /** + * The biome of the island. + * + * @return The iceland's biome. + */ + Biome getIslandBiome(); + + /** + * The name of the biome. + * * @return The name of the biome. */ - String getBiome(); + String getBiomeName(); /** * The current party-size of the island. @@ -100,6 +121,7 @@ public interface IslandInfo { * @return True if the player has been banned from this island. * @deprecated Use {@link IslandInfo#isBanned(OfflinePlayer)} */ + @Deprecated(since = "2.7.10") boolean isBanned(Player player); /** @@ -164,6 +186,7 @@ public interface IslandInfo { * @return List of players trusted on this island. * @deprecated Use #getTrusteeUUIDs instead */ + @Deprecated(since = "2.7") List getTrustees(); /** @@ -287,4 +310,7 @@ public interface IslandInfo { */ double getScoreOffset(); + public int getHopperLimit(); + public void setHopperLimit(int limit); + } diff --git a/uSkyBlock-API/src/main/java/us/talabrek/ultimateskyblock/api/UpdateChecker.java b/uSkyBlock-API/src/main/java/us/talabrek/ultimateskyblock/api/UpdateChecker.java new file mode 100644 index 000000000..35a27242b --- /dev/null +++ b/uSkyBlock-API/src/main/java/us/talabrek/ultimateskyblock/api/UpdateChecker.java @@ -0,0 +1,50 @@ +package us.talabrek.ultimateskyblock.api; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.net.URI; +import java.util.concurrent.CompletableFuture; + +public interface UpdateChecker { + URI URL_RELEASE = URI.create("https://www.uskyblock.ovh/versions/release.json"); + URI URL_STAGING = URI.create("https://www.uskyblock.ovh/versions/staging.json"); + + /** + * Compares the current version and latest version (if available) to see if there is a new version available. + * @return True if new version is available, false otherwise (no new version available or version info unavailable). + */ + boolean isUpdateAvailable(); + + /** + * Gets the latest release of uSkyBlock. Returns NULL if the version info hasn't been fetched yet or is unavailable. + * @return Latest release of uSkyBlock, NULL when unavailable. + */ + @Nullable String getLatestVersion(); + + /** + * Gets the current version of uSkyBlock running on the server. + * @return Current version of uSkyBlock. + */ + @NotNull String getCurrentVersion(); + + /** + * Fetches the latest version info from the uSkyBlock website. Returns a {@link CompletableFuture }, + * completes the HTTP request async. The CompletableFuture will contain NULL when version info cannot be obtained. + * @param uri URI to use for the HTTP request, official links are + * {@link UpdateChecker#URL_RELEASE} and {@link UpdateChecker#URL_STAGING}. + * @return CompletableFuture with the latest version info. + */ + CompletableFuture fetchLatestVersion(URI uri); + + /** + * Compares two version numbers. Returns a negative integer, zero, or a positive integer as this + * object is less than, equal to, or greater than the specified object. + * @see Comparable#compareTo(Object). + * @param currentVersion Current version number (may contain -SNAPSHOT). + * @param newVersion New version number (may contain -SNAPSHOT). + * @return Negative integer, zero, or a positive integer as this object is less than, + * equal to, or greater than the specified object. + */ + boolean isNewerVersion(String currentVersion, String newVersion); +} diff --git a/uSkyBlock-API/src/main/java/us/talabrek/ultimateskyblock/api/model/BlockScore.java b/uSkyBlock-API/src/main/java/us/talabrek/ultimateskyblock/api/model/BlockScore.java index 4d5456f4d..6214a3408 100644 --- a/uSkyBlock-API/src/main/java/us/talabrek/ultimateskyblock/api/model/BlockScore.java +++ b/uSkyBlock-API/src/main/java/us/talabrek/ultimateskyblock/api/model/BlockScore.java @@ -1,6 +1,8 @@ package us.talabrek.ultimateskyblock.api.model; import org.bukkit.ChatColor; +import org.bukkit.Material; +import org.bukkit.block.data.BlockData; import org.bukkit.inventory.ItemStack; /** @@ -14,8 +16,25 @@ public interface BlockScore { * * @return The type of block. * @since v2.1.2 + * @deprecated Converting a BlockData to an ItemStack is not supported in Minecraft. Use #getBlockData() instead. */ - ItemStack getBlock(); + @Deprecated(since = "v3.1.0") + default ItemStack getBlock() { + Material material = getBlockData().getMaterial(); + if (material.isItem()) { + return new ItemStack(material); + } else { + throw new UnsupportedOperationException("BlockData is not an item. Use getBlockData() instead."); + } + } + + /** + * The type of block. + * + * @return The type of block. + * @since v3.1.0 + */ + BlockData getBlockData(); /** * The number of blocks of this type found on the island. diff --git a/uSkyBlock-APIv2/build.gradle.kts b/uSkyBlock-APIv2/build.gradle.kts new file mode 100644 index 000000000..6ca5ac442 --- /dev/null +++ b/uSkyBlock-APIv2/build.gradle.kts @@ -0,0 +1,12 @@ +/* + * This file was generated by the Gradle 'init' task. + */ + + +dependencies { + implementation("org.jetbrains:annotations:23.0.0") + compileOnly("org.spigotmc:spigot-api:1.20.6-R0.1-SNAPSHOT") +} + +description = "uSkyBlock-APIv2" + diff --git a/uSkyBlock-APIv2/src/main/java/us/talabrek/ultimateskyblock/api/UltimateSkyblock.java b/uSkyBlock-APIv2/src/main/java/us/talabrek/ultimateskyblock/api/UltimateSkyblock.java new file mode 100644 index 000000000..27f6593e5 --- /dev/null +++ b/uSkyBlock-APIv2/src/main/java/us/talabrek/ultimateskyblock/api/UltimateSkyblock.java @@ -0,0 +1,12 @@ +package us.talabrek.ultimateskyblock.api; + +import org.jetbrains.annotations.NotNull; +import us.talabrek.ultimateskyblock.api.plugin.PluginInfo; + +public interface UltimateSkyblock { + /** + * Gets the {@link PluginInfo}, providing general information about this Ultimate Skyblock instance. + * @return General plugin information. + */ + @NotNull PluginInfo getPluginInfo(); +} diff --git a/uSkyBlock-APIv2/src/main/java/us/talabrek/ultimateskyblock/api/UltimateSkyblockProvider.java b/uSkyBlock-APIv2/src/main/java/us/talabrek/ultimateskyblock/api/UltimateSkyblockProvider.java new file mode 100644 index 000000000..dbcc6573e --- /dev/null +++ b/uSkyBlock-APIv2/src/main/java/us/talabrek/ultimateskyblock/api/UltimateSkyblockProvider.java @@ -0,0 +1,46 @@ +package us.talabrek.ultimateskyblock.api; + +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; + +public final class UltimateSkyblockProvider { + private static UltimateSkyblock instance = null; + + /** + * Gets the API instance of {@link UltimateSkyblock}, will throw {@link IllegalStateException} when the + * API isn't loaded yet. + * + * Convenience method, using Bukkit's {@link org.bukkit.plugin.ServicesManager} is the preferred way to get + * an API instance. + * @return UltimateSkyblock API instance. + * @throws IllegalStateException when the API isn't loaded yet. + */ + public static @NotNull UltimateSkyblock getInstance() { + if (instance == null) { + throw new IllegalStateException("UltimateSkyblock isn't loaded yet!"); + } + return instance; + } + + /** + * Internal method - Registers the uSkyBlock plugin instance with the API provider. + * @param instance uSkyBlock plugin instance + */ + @ApiStatus.Internal + public static void registerPlugin(UltimateSkyblock instance) { + UltimateSkyblockProvider.instance = instance; + } + + /** + * Internal method - Deregisters the uSkyBlock plugin instance with the API provider. + */ + @ApiStatus.Internal + public static void deregisterPlugin() { + UltimateSkyblockProvider.instance = null; + } + + /** + * No instance of this class should exist. + */ + private UltimateSkyblockProvider() {} +} diff --git a/uSkyBlock-APIv2/src/main/java/us/talabrek/ultimateskyblock/api/plugin/PluginInfo.java b/uSkyBlock-APIv2/src/main/java/us/talabrek/ultimateskyblock/api/plugin/PluginInfo.java new file mode 100644 index 000000000..fe8fc4d97 --- /dev/null +++ b/uSkyBlock-APIv2/src/main/java/us/talabrek/ultimateskyblock/api/plugin/PluginInfo.java @@ -0,0 +1,21 @@ +package us.talabrek.ultimateskyblock.api.plugin; + +import org.jetbrains.annotations.NotNull; + +/** + * Provides general information about the Ultimate Skyblock plugin instance running. + */ +public interface PluginInfo { + /** + * Gets the plugin version running on the server. + * @return Plugin version running. + */ + @NotNull String getPluginVersion(); + + /** + * Gets the {@link UpdateChecker}, which provides various information about the current and latest + * Ultimate Skyblock releases. + * @return Update checker for Ultimate Skyblock. + */ + @NotNull UpdateChecker getUpdateChecker(); +} diff --git a/uSkyBlock-APIv2/src/main/java/us/talabrek/ultimateskyblock/api/plugin/UpdateChecker.java b/uSkyBlock-APIv2/src/main/java/us/talabrek/ultimateskyblock/api/plugin/UpdateChecker.java new file mode 100644 index 000000000..39d874389 --- /dev/null +++ b/uSkyBlock-APIv2/src/main/java/us/talabrek/ultimateskyblock/api/plugin/UpdateChecker.java @@ -0,0 +1,50 @@ +package us.talabrek.ultimateskyblock.api.plugin; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.net.URI; +import java.util.concurrent.CompletableFuture; + +public interface UpdateChecker { + URI URL_RELEASE = URI.create("https://www.uskyblock.ovh/versions/release.json"); + URI URL_STAGING = URI.create("https://www.uskyblock.ovh/versions/staging.json"); + + /** + * Compares the current version and latest version (if available) to see if there is a new version available. + * @return True if new version is available, false otherwise (no new version available or version info unavailable). + */ + boolean isUpdateAvailable(); + + /** + * Gets the latest release of uSkyBlock. Returns NULL if the version info hasn't been fetched yet or is unavailable. + * @return Latest release of uSkyBlock, NULL when unavailable. + */ + @Nullable String getLatestVersion(); + + /** + * Gets the current version of uSkyBlock running on the server. + * @return Current version of uSkyBlock. + */ + @NotNull String getCurrentVersion(); + + /** + * Fetches the latest version info from the uSkyBlock website. Returns a {@link CompletableFuture }, + * completes the HTTP request async. The CompletableFuture will contain NULL when version info cannot be obtained. + * @param uri URI to use for the HTTP request, official links are + * {@link UpdateChecker#URL_RELEASE} and {@link UpdateChecker#URL_STAGING}. + * @return CompletableFuture with the latest version info. + */ + CompletableFuture fetchLatestVersion(URI uri); + + /** + * Compares two version numbers. Returns a negative integer, zero, or a positive integer as this + * object is less than, equal to, or greater than the specified object. + * @see Comparable#compareTo(Object). + * @param currentVersion Current version number (may contain -SNAPSHOT). + * @param newVersion New version number (may contain -SNAPSHOT). + * @return Negative integer, zero, or a positive integer as this object is less than, + * equal to, or greater than the specified object. + */ + boolean isNewerVersion(String currentVersion, String newVersion); +} diff --git a/uSkyBlock-Core/build.gradle.kts b/uSkyBlock-Core/build.gradle.kts new file mode 100644 index 000000000..9f0d1b15e --- /dev/null +++ b/uSkyBlock-Core/build.gradle.kts @@ -0,0 +1,41 @@ +/* + * This file was generated by the Gradle 'init' task. + */ + +dependencies { + implementation(project(":bukkit-utils")) + implementation(project(":po-utils")) + implementation(project(":uSkyBlock-API")) + implementation(project(":uSkyBlock-APIv2")) + implementation("io.papermc:paperlib:1.0.8") + implementation("org.bstats:bstats-bukkit:3.0.1") + implementation("com.google.inject:guice:7.0.0") + implementation("org.jetbrains:annotations:23.0.0") + testImplementation(project(":bukkit-utils", "testsJar")) + testImplementation("org.hamcrest:hamcrest:2.2") + testImplementation("org.hamcrest:hamcrest-library:2.2") + testImplementation("junit:junit:4.13.2") + testImplementation("org.junit.vintage:junit-vintage-engine:5.9.0") + testImplementation("org.mockito:mockito-core:5.14.2") + testImplementation("io.papermc.paper:paper-api:1.21.4-R0.1-SNAPSHOT") + testImplementation("com.sk89q.worldedit:worldedit-bukkit:7.2.19") + compileOnly("net.milkbowl.vault:VaultUnlockedAPI:2.10") + compileOnly("io.papermc.paper:paper-api:1.21.4-R0.1-SNAPSHOT") + compileOnly("com.onarandombox.multiversecore:Multiverse-Core:4.3.1") + compileOnly("com.onarandombox.multiverseinventories:Multiverse-Inventories:4.2.3") + compileOnly("com.sk89q.worldedit:worldedit-bukkit:7.2.19") + compileOnly("com.sk89q.worldguard:worldguard-bukkit:7.0.9") + compileOnly("com.google.guava:guava:33.1.0-jre") + compileOnly("com.google.code.gson:gson:2.10.1") + compileOnly("be.maximvdw:MVdWPlaceholderAPI:3.0.1-SNAPSHOT") { + exclude("*", "*") + } + compileOnly("net.kyori:adventure-api:4.16.0") + compileOnly("net.kyori:adventure-platform-bukkit:4.3.2") + compileOnly("org.apache.commons:commons-lang3:3.14.0") + compileOnly("org.apache.commons:commons-text:1.12.0") + compileOnly("org.apache.httpcomponents:httpclient:4.5.14") + compileOnly("org.apache.maven:maven-artifact:3.8.6") +} + +description = "uSkyBlock-Core" diff --git a/uSkyBlock-Core/pom.xml b/uSkyBlock-Core/pom.xml deleted file mode 100644 index 2f8b5a304..000000000 --- a/uSkyBlock-Core/pom.xml +++ /dev/null @@ -1,497 +0,0 @@ - - - - uSkyBlock - uSkyBlock - 2.8.8-SNAPSHOT - - 4.0.0 - jar - uSkyBlock-Core - - - false - - - - - internal.repo - Temporary Staging Repository - file://${project.build.directory}/mvn-repo - - - - - - - org.apache.maven.plugins - maven-compiler-plugin - - 1.8 - 1.8 - utf-8 - - - - com.google.code.maven-replacer-plugin - replacer - 1.5.3 - - - cleanup-po-files - process-resources - - replace - - - ${basedir}/src/main/po - *.po, *.pot - - - #: .*\n - - - - "POT-Creation-Date:.*\n - - - - - - - - - org.codehaus.mojo - buildnumber-maven-plugin - 1.4 - - - generate-resources - - create - - - - - 7 - false - false - DEV - - - - org.apache.maven.plugins - maven-shade-plugin - 3.1.0 - - - package - - shade - - - false - true - - - org.bstats:* - io.papermc:paperlib - net.wesjd:anvilgui - dk.lockfuglsang.minecraft:bukkit-utils - dk.lockfuglsang.minecraft:po-utils - com.googlecode.json-simple:json-simple - - - - - org.bstats - us.talabrek.ultimateskyblock.metrics - - - dk.lockfuglsang.minecraft - us.talabrek.ultimateskyblock.utils - - - io.papermc.lib - us.talabrek.ultimateskyblock.paperlib - - - net.wesjd.anvilgui - us.talabrek.ultimateskyblock.anvilgui - - - - - - - - maven-clean-plugin - 2.6.1 - - - - src/main/po - - *~ - - - *.po - *.pot - - - - - - - org.apache.maven.plugins - maven-antrun-plugin - 1.8 - - - prepare-package - run - - - - - - - - - - org.apache.maven.plugins - maven-javadoc-plugin - - private - false - none - - - - - javadoc - - deploy - - - - - org.apache.maven.plugins - maven-surefire-plugin - - - org.apache.maven.plugins - maven-failsafe-plugin - - - - - . - true - src/main/resources - - *.yml - README.md - **/*.properties - - - - . - false - src/main/resources - - schematics/* - structures/* - - - - . - ${basedir} - - README.md - LICENSE.txt - - - - - - - - 1.15 - - true - - - - org.bukkit - bukkit - 1.15-R0.1-SNAPSHOT - true - compile - - - - - i18n - - false - - - - - com.github.rlf - gettext-maven-plugin - 1.2.10 - - - find-bukkit-utils-msgids - generate-sources - - gettext - - - ${project.parent.basedir}/bukkit-utils/src/main/java - - - - find-msgids - generate-sources - - gettext - - - - -j - - - - - update-po-files - generate-resources - - merge - - - ${msgmergeCmd} - - -N - - - - - clear-fuzzy - generate-resources - - attrib - - - - --clear-fuzzy - --empty - --no-obsolete - - - xx_PIRATE.po - xx_lol_US.po - - - - - report-po-completion - process-resources - - report - - - ${msgfmtCmd} - - - - - ${project.build.directory}/classes - 2 - ${project.basedir}/src/main/po - us.talabrek.ultimateskyblock.i18n.Messages - properties - - --no-location - - - - - - - - - - - dk.lockfuglsang.minecraft - bukkit-utils - - - dk.lockfuglsang.minecraft - bukkit-utils - test - tests - - - uSkyBlock - po-utils - - - com.github.rlf - uSkyBlock-API - - - net.milkbowl.vault - VaultAPI - true - - - - - io.papermc - paperlib - 1.0.2 - compile - - - - com.github.WesJD - AnvilGUI - 4b7a469d7e - compile - - - - com.onarandombox.multiversecore - Multiverse-Core - 2.5 - compile - true - - - * - * - - - - - com.onarandombox.multiverseinventories - Multiverse-Inventories - 2.5.0-SNAPSHOT - compile - true - - - * - * - - - - - - org.bstats - bstats-bukkit - 1.4 - compile - - - - com.google.guava - guava - 21.0 - provided - - - - com.connorlinfoot.actionbarapi - ActionBarAPI - 1.5.4 - provided - - - be.maximvdw - MVdWPlaceholderAPI - 3.0.1-SNAPSHOT - provided - - - - * - * - - - - - me.clip.deluxechat - DeluxeChatPlaceholderAPI - - - - org.jetbrains - annotations - 17.0.0 - - - commons-lang - commons-lang - 2.6 - provided - - - - com.googlecode.json-simple - json-simple - 1.1.1 - - - - org.hamcrest - hamcrest - ${hamcrest.version} - test - - - org.hamcrest - hamcrest-library - ${hamcrest.version} - test - - - junit - junit - ${junit.version} - test - - - org.junit.vintage - junit-vintage-engine - ${junit-vintage-engine.version} - test - - - org.mockito - mockito-core - ${mockito.version} - test - - - org.powermock - powermock-module-junit4 - ${powermock.version} - test - - - org.powermock - powermock-api-mockito2 - ${powermock.version} - test - - - diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/MetricsManager.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/MetricsManager.java new file mode 100644 index 000000000..47dd75ea0 --- /dev/null +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/MetricsManager.java @@ -0,0 +1,43 @@ +package us.talabrek.ultimateskyblock; + +import com.google.inject.Inject; +import com.google.inject.Singleton; +import org.bstats.bukkit.Metrics; +import org.bstats.charts.SimplePie; + +import java.util.logging.Level; + +@Singleton +public class MetricsManager { + // Pushing to two accounts at the moment to track both legacy and current installations. + private static final int BSTATS_MUSPAH_ID = 7525; + private static final int BSTATS_RLF_ID = 2801; + + private final uSkyBlock plugin; + + @Inject + public MetricsManager(uSkyBlock plugin) { + this.plugin = plugin; + } + + public void setup() { + try { + setupMetrics(BSTATS_MUSPAH_ID); + setupMetrics(BSTATS_RLF_ID); + } catch (Exception ex) { + plugin.getLogger().log(Level.WARNING, "Failed to setup bStats metrics:", ex); + } + } + + private void setupMetrics(int pluginId) { + Metrics bStats = new Metrics(plugin, pluginId); + bStats.addCustomChart(new SimplePie("language", + () -> plugin.getConfig().getString("language", "en"))); + bStats.addCustomChart(new SimplePie("radius_and_distance", + () -> String.format("(%d,%d)", Settings.island_radius, Settings.island_distance))); + + // Temp. chart to measure storage usage for (legacy) uuid.PlayerDB. + bStats.addCustomChart(new SimplePie("playerdb_type", + () -> plugin.getConfig().getString("options.advanced.playerdb.storage", "yml"))); + } +} diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/PluginConfig.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/PluginConfig.java new file mode 100644 index 000000000..65e322301 --- /dev/null +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/PluginConfig.java @@ -0,0 +1,19 @@ +package us.talabrek.ultimateskyblock; + +import com.google.inject.Inject; +import dk.lockfuglsang.minecraft.file.FileUtil; +import org.bukkit.configuration.file.FileConfiguration; +import org.jetbrains.annotations.NotNull; + +public class PluginConfig { + + @Inject + public PluginConfig() { + Settings.loadPluginConfig(getYamlConfig()); + } + + @NotNull + public FileConfiguration getYamlConfig() { + return FileUtil.getYmlConfiguration("config.yml"); + } +} diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/Settings.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/Settings.java index c4dfb4d71..0a94be517 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/Settings.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/Settings.java @@ -1,14 +1,19 @@ package us.talabrek.ultimateskyblock; import dk.lockfuglsang.minecraft.po.I18nUtil; +import dk.lockfuglsang.minecraft.util.ItemStackUtil; +import org.bukkit.Registry; +import org.bukkit.block.Biome; import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.inventory.ItemStack; import us.talabrek.ultimateskyblock.handler.WorldEditHandler; -import dk.lockfuglsang.minecraft.util.ItemStackUtil; +import java.time.Duration; import java.util.HashSet; +import java.util.List; import java.util.Locale; import java.util.Set; +import java.util.logging.Level; import java.util.logging.Logger; public class Settings { @@ -21,19 +26,22 @@ public class Settings { public static boolean island_removeCreaturesByTeleport; public static int island_protectionRange; public static int island_radius; - public static ItemStack[] island_chestItems; + private static List island_chestItems; public static boolean island_addExtraItems; public static String[] island_extraPermissions; public static boolean island_allowIslandLock; public static boolean island_useIslandLevel; public static boolean island_useTopTen; public static int general_cooldownInfo; - public static int general_cooldownRestart; - public static int general_biomeChange; + public static Duration general_cooldownRestart; + public static Duration general_biomeChange; + public static Biome general_defaultBiome; + public static Biome general_defaultNetherBiome; public static boolean extras_sendToSpawn; + public static boolean extras_respawnAtIsland; public static boolean extras_obsidianToLava; public static String island_schematicName; - public static long island_topTenTimeout; + public static Duration island_topTenTimeout; public static boolean island_allowPvP; public static Locale locale = Locale.getDefault(); public static boolean nether_enabled; @@ -76,20 +84,23 @@ public static boolean loadPluginConfig(FileConfiguration config) { general_cooldownInfo = 60; } try { - general_biomeChange = config.getInt("options.general.biomeChange"); - if (general_biomeChange < 0) { - general_biomeChange = 0; + general_biomeChange = Duration.ofSeconds(config.getInt("options.general.biomeChange")); + if (general_biomeChange.isNegative()) { + general_biomeChange = Duration.ZERO; } } catch (Exception e) { - general_biomeChange = 3600; + general_biomeChange = Duration.ofHours(1); } + general_defaultBiome = loadBiome(config, "options.general.defaultBiome", Biome.OCEAN); + general_defaultNetherBiome = loadBiome(config, "options.general.defaultNetherBiome", Biome.NETHER_WASTES); + try { - general_cooldownRestart = config.getInt("options.general.cooldownRestart"); - if (general_cooldownRestart < 0) { - general_cooldownRestart = 0; + general_cooldownRestart = Duration.ofSeconds(config.getInt("options.general.cooldownRestart")); + if (general_cooldownRestart.isNegative()) { + general_cooldownRestart = Duration.ZERO; } } catch (Exception e) { - general_cooldownRestart = 60; + general_cooldownRestart = Duration.ofHours(1); } try { island_height = config.getInt("options.island.height"); @@ -108,9 +119,7 @@ public static boolean loadPluginConfig(FileConfiguration config) { changed = true; } general_spawnSize = config.getInt("options.general.spawnSize", 50); - island_chestItems = ItemStackUtil.createItemArray(ItemStackUtil.createItemList( - config.getStringList("options.island.chestItems") - )); + island_chestItems = ItemStackUtil.createItemList(config.getStringList("options.island.chestItems")); island_schematicName = config.getString("options.island.schematicName"); if (island_schematicName == null || "yourschematicname".equals(island_schematicName) || "uSkyBlockDefault".equals(island_schematicName)) { @@ -127,17 +136,17 @@ public static boolean loadPluginConfig(FileConfiguration config) { island_useIslandLevel = config.getBoolean("options.island.useIslandLevel"); island_extraPermissions = permissionList.toArray(new String[0]); extras_sendToSpawn = config.getBoolean("options.extras.sendToSpawn"); + extras_respawnAtIsland = config.getBoolean("options.extras.respawnAtIsland"); island_useTopTen = config.getBoolean("options.island.useTopTen"); general_worldName = config.getString("options.general.worldName", "skyworld"); island_removeCreaturesByTeleport = config.getBoolean("options.island.removeCreaturesByTeleport"); island_allowIslandLock = config.getBoolean("options.island.allowIslandLock"); - island_topTenTimeout = config.getInt("options.island.topTenTimeout", 7); // Every 7 minutes + island_topTenTimeout = Duration.ofMinutes(config.getLong("options.island.topTenTimeout", 7)); island_allowPvP = config.getString("options.island.allowPvP", "deny").equalsIgnoreCase("allow") || config.getString("options.island.allowPvP", "false").equalsIgnoreCase("true"); Locale loc = I18nUtil.getLocale(config.getString("language", null)); if (loc != null) { locale = loc; - I18nUtil.setLocale(locale); } nether_enabled = config.getBoolean("nether.enabled", false); if (nether_enabled && !WorldEditHandler.isOuterPossible()) { @@ -146,8 +155,26 @@ public static boolean loadPluginConfig(FileConfiguration config) { changed = true; } nether_lava_level = config.getInt("nether.lava_level", config.getInt("nether.lava-level", 32)); - nether_height = config.getInt("nether.height", island_height/2); + nether_height = config.getInt("nether.height", island_height / 2); return changed; } + @SuppressWarnings("removal") + private static Biome loadBiome(FileConfiguration config, String path, Biome defaultBiome) { + try { + String biomeKey = config.getString(path, defaultBiome.getKey().getKey()); + Biome parsedBiome = Registry.BIOME.match(biomeKey); + if (parsedBiome == null) { + log.log(Level.WARNING, "Invalid Biome in '{0}': {1}", new Object[]{path, biomeKey}); + parsedBiome = defaultBiome; + } + return parsedBiome; + } catch (Exception e) { + return defaultBiome; + } + } + + public static List getIslandChestItems() { + return ItemStackUtil.clone(island_chestItems); + } } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/SkyUpdateChecker.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/SkyUpdateChecker.java new file mode 100644 index 000000000..d7fc3af39 --- /dev/null +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/SkyUpdateChecker.java @@ -0,0 +1,126 @@ +package us.talabrek.ultimateskyblock; + +import com.google.gson.Gson; +import com.google.gson.JsonObject; +import com.google.inject.Inject; +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.util.EntityUtils; +import org.apache.maven.artifact.versioning.ComparableVersion; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import us.talabrek.ultimateskyblock.api.plugin.UpdateChecker; + +import java.net.URI; +import java.util.concurrent.CompletableFuture; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class SkyUpdateChecker implements UpdateChecker { + + private final Logger logger; + private final PluginConfig config; + private final uSkyBlock plugin; + + private String latestVersion; + + private final Gson gson = new Gson(); + + @Inject + public SkyUpdateChecker( + @NotNull uSkyBlock plugin, + @NotNull PluginConfig config, + @NotNull Logger logger + ) { + this.plugin = plugin; + this.config = config; + this.logger = logger; + } + + /** + * Triggers an update of the latest version info from the uSkyBlock website, and will log an INFO message if + * an update is available. + */ + public void checkForUpdates() { + URI uri = URL_RELEASE; + + if (config.getYamlConfig().getString("plugin-updates.branch", "RELEASE").equalsIgnoreCase("STAGING")) { + uri = URL_STAGING; + } + + fetchLatestVersion(uri).thenAccept(version -> { + latestVersion = version; + if (latestVersion == null) { + logger.info("Failed to check for new uSkyBlock versions."); + } + + if (isUpdateAvailable()) { + logger.info("There is a new version of uSkyBlock available: " + getLatestVersion()); + logger.info("Visit https://www.uskyblock.ovh/get to download."); + } + }); + } + + public boolean isUpdateAvailable() { + if (latestVersion != null) { + return isNewerVersion(getCurrentVersion(), getLatestVersion()); + } + return false; + } + + public @Nullable String getLatestVersion() { + return latestVersion; + } + + public @NotNull String getCurrentVersion() { + return plugin.getDescription().getVersion(); + } + + public CompletableFuture fetchLatestVersion(URI uri) { + CompletableFuture future = new CompletableFuture<>(); + future.completeAsync(() -> { + String userAgent = "uSkyBlock-Plugin/v" + getCurrentVersion() + " (www.uskyblock.ovh)"; + try (var httpclient = HttpClients.custom().setUserAgent(userAgent).build()) { + + int CONNECTION_TIMEOUT_MS = 10 * 1000; // Timeout in millis. + RequestConfig requestConfig = RequestConfig.custom() + .setConnectionRequestTimeout(CONNECTION_TIMEOUT_MS) + .setConnectTimeout(CONNECTION_TIMEOUT_MS) + .setSocketTimeout(CONNECTION_TIMEOUT_MS) + .build(); + + HttpGet request = new HttpGet(uri); + request.setConfig(requestConfig); + HttpResponse response = httpclient.execute(request); + HttpEntity entity = response.getEntity(); + + int status = response.getStatusLine().getStatusCode(); + if (status < 200 || status >= 300) { + return null; + } + + if (entity != null) { + JsonObject obj = gson.fromJson(EntityUtils.toString(entity), JsonObject.class); + if (obj.has("version")) { + return obj.get("version").getAsString(); + } + } + } catch (Exception ex) { + logger.log(Level.SEVERE, "Exception while trying to fetch latest plugin version.", ex); + } + + return null; + }); + + return future; + } + + public boolean isNewerVersion(String currentVersion, String newVersion) { + ComparableVersion current = new ComparableVersion(currentVersion); + ComparableVersion target = new ComparableVersion(newVersion); + return target.compareTo(current) > 0; + } +} diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/api/event/EventLogic.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/api/event/EventLogic.java index 503bc38a7..293de7100 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/api/event/EventLogic.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/api/event/EventLogic.java @@ -1,27 +1,31 @@ package us.talabrek.ultimateskyblock.api.event; +import com.google.inject.Inject; +import org.bukkit.Bukkit; +import org.jetbrains.annotations.NotNull; import us.talabrek.ultimateskyblock.island.IslandInfo; import us.talabrek.ultimateskyblock.player.PlayerInfo; -import us.talabrek.ultimateskyblock.uSkyBlock; +import us.talabrek.ultimateskyblock.util.Scheduler; public class EventLogic { - private final uSkyBlock plugin; + private final Scheduler scheduler; - public EventLogic(uSkyBlock plugin) { - this.plugin = plugin; + @Inject + public EventLogic(@NotNull Scheduler scheduler) { + this.scheduler = scheduler; } /** * Fires a new async {@link IslandLeaderChangedEvent}. * - * @param islandInfo {@link IslandInfo} for the island in the scope of this event. + * @param islandInfo {@link IslandInfo} for the island in the scope of this event. * @param originalLeaderInfo {@link PlayerInfo} for the original island leader. - * @param newLeaderInfo {@link PlayerInfo} for the new island leader. + * @param newLeaderInfo {@link PlayerInfo} for the new island leader. */ public void fireIslandLeaderChangedEvent(us.talabrek.ultimateskyblock.api.IslandInfo islandInfo, us.talabrek.ultimateskyblock.api.PlayerInfo originalLeaderInfo, us.talabrek.ultimateskyblock.api.PlayerInfo newLeaderInfo) { - plugin.async(() -> plugin.getServer().getPluginManager().callEvent(new IslandLeaderChangedEvent(islandInfo, originalLeaderInfo, newLeaderInfo))); + scheduler.async(() -> Bukkit.getPluginManager().callEvent(new IslandLeaderChangedEvent(islandInfo, originalLeaderInfo, newLeaderInfo))); } /** @@ -31,17 +35,17 @@ public void fireIslandLeaderChangedEvent(us.talabrek.ultimateskyblock.api.Island * @param playerInfo {@link PlayerInfo} for the joined member. */ public void fireMemberJoinedEvent(us.talabrek.ultimateskyblock.island.IslandInfo islandInfo, us.talabrek.ultimateskyblock.player.PlayerInfo playerInfo) { - plugin.async(() -> plugin.getServer().getPluginManager().callEvent(new MemberJoinedEvent(islandInfo, playerInfo))); + scheduler.async(() -> Bukkit.getPluginManager().callEvent(new MemberJoinedEvent(islandInfo, playerInfo))); } /** * Fires a new async {@link MemberLeftEvent}. * * @param islandInfo {@link IslandInfo} for the island that the member left. - * @param member {@link PlayerInfo} for the left member. + * @param member {@link PlayerInfo} for the left member. */ public void fireMemberLeftEvent(IslandInfo islandInfo, PlayerInfo member) { - plugin.async(() -> plugin.getServer().getPluginManager().callEvent(new MemberLeftEvent(islandInfo, member))); + scheduler.async(() -> Bukkit.getPluginManager().callEvent(new MemberLeftEvent(islandInfo, member))); } public void shutdown() { diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/api/impl/UltimateSkyblockApi.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/api/impl/UltimateSkyblockApi.java new file mode 100644 index 000000000..49f6b33e5 --- /dev/null +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/api/impl/UltimateSkyblockApi.java @@ -0,0 +1,32 @@ +package us.talabrek.ultimateskyblock.api.impl; + +import org.jetbrains.annotations.NotNull; +import us.talabrek.ultimateskyblock.api.UltimateSkyblock; +import us.talabrek.ultimateskyblock.api.plugin.PluginInfo; +import us.talabrek.ultimateskyblock.api.plugin.UpdateChecker; +import us.talabrek.ultimateskyblock.uSkyBlock; + +public class UltimateSkyblockApi implements UltimateSkyblock, PluginInfo { + private final uSkyBlock plugin; + + public UltimateSkyblockApi(uSkyBlock plugin) { + this.plugin = plugin; + } + + @Override + public @NotNull PluginInfo getPluginInfo() { + return this; + } + + /* PluginInfo impl */ + + @Override + public @NotNull String getPluginVersion() { + return plugin.getUpdateChecker().getCurrentVersion(); + } + + @Override + public @NotNull UpdateChecker getUpdateChecker() { + return plugin.getUpdateChecker(); + } +} diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/async/IncrementalRunnable.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/async/IncrementalRunnable.java index bda4eab2d..625fc13b4 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/async/IncrementalRunnable.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/async/IncrementalRunnable.java @@ -1,8 +1,18 @@ package us.talabrek.ultimateskyblock.async; -import org.bukkit.scheduler.BukkitRunnable; -import us.talabrek.ultimateskyblock.uSkyBlock; import dk.lockfuglsang.minecraft.util.TimeUtil; +import dk.lockfuglsang.minecraft.util.Timer; +import org.bukkit.scheduler.BukkitRunnable; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import us.talabrek.ultimateskyblock.PluginConfig; +import us.talabrek.ultimateskyblock.util.Scheduler; + +import java.time.Duration; +import java.time.Instant; +import java.util.concurrent.atomic.AtomicInteger; + +import static java.lang.Math.max; /** * Convenience template class for executing heavy tasks on the main thread. @@ -23,130 +33,120 @@ * */ public abstract class IncrementalRunnable extends BukkitRunnable { - private final uSkyBlock plugin; - private Runnable onCompletion; - /** - * The maximum number of consecutive ms to execute a task. - */ - private final int maxMs; - private final int maxConsecutive; - private final int yieldDelay; + private final Scheduler scheduler; + private Runnable onCompletion; /** - * The time of creation + * The maximum number of consecutive ms to execute a task. */ - private double tStart = 0; + private final Duration maxIterationTime; + private final long maxConsecutiveRuns; + private final Duration yieldDelay; + private Timer lifetimeTimer = null; /** * The time of completion. */ - private double tCompleted = 0; + private Instant completed = null; /** - * Millis used in processing. + * Time used in processing. */ - private double tUsed = 0; + private Duration processingTimeUsed = Duration.ZERO; /** * The time of the current incremental run. */ - private double tRunning = 0; + private Timer iterationTimer = null; private volatile boolean isCancelled = false; - private int consecutiveRuns = 0; + private final AtomicInteger consecutiveRuns = new AtomicInteger(0); /** * Number of iterations in total (calls to tick()) */ - private volatile int iterations = 0; + private final AtomicInteger iterations = new AtomicInteger(0); /** * Number of server-ticks consumed. */ - private volatile int ticks = 0; + private final AtomicInteger ticksConsumed = new AtomicInteger(0); - public IncrementalRunnable(uSkyBlock plugin) { - this(plugin, null, - plugin.getConfig().getInt("async.maxMs", 15), - plugin.getConfig().getInt("async.maxConsecutiveTicks", 20), - plugin.getConfig().getInt("async.yieldDelay", 2) - ); + public IncrementalRunnable(@NotNull Scheduler scheduler, @NotNull PluginConfig config) { + this(scheduler, config, null); } - public IncrementalRunnable(uSkyBlock plugin, Runnable onCompletion) { - this(plugin, onCompletion, - plugin.getConfig().getInt("async.maxMs", 15), - plugin.getConfig().getInt("async.maxConsecutiveTicks", 20), - plugin.getConfig().getInt("async.yieldDelay", 2) + public IncrementalRunnable(@NotNull Scheduler scheduler, @NotNull PluginConfig config, @Nullable Runnable onCompletion) { + this(scheduler, onCompletion, + Duration.ofMillis(config.getYamlConfig().getInt("async.maxMs", 15)), + config.getYamlConfig().getLong("async.maxConsecutiveTicks", 20), + TimeUtil.ticksAsDuration(config.getYamlConfig().getLong("async.yieldDelay", 2)) ); } - public IncrementalRunnable(uSkyBlock plugin, Runnable onCompletion, int maxMs, int maxConsecutive, int yieldDelay) { - this.plugin = plugin; + public IncrementalRunnable(@NotNull Scheduler scheduler, @Nullable Runnable onCompletion, @NotNull Duration maxIterationTime, long maxConsecutiveRuns, @NotNull Duration yieldDelay) { + this.scheduler = scheduler; this.onCompletion = onCompletion; - this.maxMs = maxMs; - this.maxConsecutive = maxConsecutive; + this.maxIterationTime = maxIterationTime; + this.maxConsecutiveRuns = maxConsecutiveRuns; this.yieldDelay = yieldDelay; } - protected boolean hasTime() { - return millisActive() < maxMs && !isCancelled; - } - /** - * Used by sub-classes to see how much time they have left. + * Used by subclasses to see how much time they have left. + * * @return The number of ms the current #execute() has been running. */ - protected long millisActive() { - return Math.round(t() - tRunning); + protected @NotNull Duration millisActive() { + return iterationTimer != null ? iterationTimer.elapsed() : Duration.ZERO; } - protected double millisLeft() { - return maxMs - millisActive(); + protected @NotNull Duration millisLeft() { + return maxIterationTime.minus(millisActive()); } public boolean stillTime() { - double millisPerTick = getTimeUsed()/(iterations != 0 ? iterations : 1); - return millisPerTick < millisLeft(); - } - - public uSkyBlock getPlugin() { - return plugin; + Duration millisPerTick = getProcessingTimeUsed().dividedBy(max(iterations.get(), 1)); + return millisLeft().minus(millisPerTick).isPositive(); } protected boolean tick() { - iterations++; + iterations.incrementAndGet(); return stillTime(); } /** * Executes a potentially heavy task + * * @return true if done, false otherwise. */ protected abstract boolean execute(); /** * Returns the number of ms the task has been active. + * * @return the number of ms the task has been active. */ - public long getTimeElapsed() { - if (tCompleted != 0d) { - return Math.round(tCompleted - tStart); + public @NotNull Duration getTimeElapsed() { + if (completed != null) { + assert lifetimeTimer != null; + return Duration.between(lifetimeTimer.getStart(), completed); } - if (tStart == 0) { - return -1; + if (lifetimeTimer == null) { + return Duration.ZERO; } - return Math.round(t() - tStart); + return lifetimeTimer.elapsed(); } /** * Returns the number of ms the task has been actively executing. + * * @return the number of ms the task has been actively executing. */ - public double getTimeUsed() { - return tUsed + (tRunning != 0 ? t()-tRunning : 0); + public @NotNull Duration getProcessingTimeUsed() { + return processingTimeUsed.plus(millisActive()); } public void cancel() { @@ -155,45 +155,41 @@ public void cancel() { @Override public final void run() { - tRunning = t(); - if (tStart == 0d) { - tStart = tRunning; + iterationTimer = Timer.start(); + if (lifetimeTimer == null) { + lifetimeTimer = Timer.start(); JobManager.addJob(this); } + int consecutiveRuns = this.consecutiveRuns.incrementAndGet(); try { - consecutiveRuns++; if (!execute() && !isCancelled) { - plugin.sync(this, TimeUtil.ticksAsMillis(consecutiveRuns < maxConsecutive ? 0 : yieldDelay)); + scheduler.sync(this, consecutiveRuns < maxConsecutiveRuns ? Duration.ZERO : yieldDelay); } else { if (onCompletion != null && !isCancelled) { - plugin.sync(onCompletion); + scheduler.sync(onCompletion); } complete(); } } finally { - tUsed += (t() - tRunning); - tRunning = 0; - if (consecutiveRuns > maxConsecutive) { - consecutiveRuns = 0; + processingTimeUsed = processingTimeUsed.plus(iterationTimer.elapsed()); + iterationTimer = null; + if (consecutiveRuns > maxConsecutiveRuns) { + this.consecutiveRuns.set(0); } - ticks++; + ticksConsumed.incrementAndGet(); } } - private static double t() { - return System.nanoTime()/1000000d; - } - protected void setOnCompletion(Runnable onCompletion) { this.onCompletion = onCompletion; } private void complete() { - tCompleted = t(); + completed = Instant.now(); JobManager.completeJob(this); } - public int getTicks() { - return ticks; + public int getTicksConsumed() { + return ticksConsumed.get(); } } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/async/JobManager.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/async/JobManager.java index c3c6cbc14..04a3308ca 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/async/JobManager.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/async/JobManager.java @@ -1,15 +1,19 @@ package us.talabrek.ultimateskyblock.async; +import java.time.Duration; import java.util.Collections; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; +import static java.lang.Math.max; + /** * Responsible for holding ongoing jobs, and recording status. */ -public enum JobManager {; - private static final ConcurrentMap jobStats = new ConcurrentHashMap<>(); +public enum JobManager { + ; + private static final ConcurrentMap jobStats = new ConcurrentHashMap<>(); public static void addJob(IncrementalRunnable runnable) { String jobName = runnable.getClass().getSimpleName(); @@ -27,7 +31,7 @@ public static void completeJob(IncrementalRunnable runnable) { jobStats.get(jobName).complete(runnable); } - public static Map getStats() { + public static Map getStats() { return Collections.unmodifiableMap(jobStats); } @@ -35,8 +39,8 @@ public static class Stats { private int jobs; private int jobsRunning; private long ticks; - private double timeActive; - private double timeElapsed; + private Duration timeActive; + private Duration timeElapsed; public Stats() { } @@ -48,9 +52,17 @@ public synchronized void add(IncrementalRunnable runnable) { public synchronized void complete(IncrementalRunnable runnable) { jobsRunning--; - ticks += runnable.getTicks(); - timeActive += runnable.getTimeUsed(); - timeElapsed += runnable.getTimeElapsed(); + ticks += runnable.getTicksConsumed(); + if (timeActive == null) { + timeActive = runnable.getProcessingTimeUsed(); + } else { + timeActive = timeActive.plus(runnable.getProcessingTimeUsed()); + } + if (timeElapsed == null) { + timeElapsed = runnable.getProcessingTimeUsed(); + } else { + timeElapsed = timeElapsed.plus(runnable.getTimeElapsed()); + } } public int getJobs() { @@ -61,24 +73,24 @@ public long getTicks() { return ticks; } - public double getTimeActive() { + public Duration getTimeActive() { return timeActive; } - public double getTimeElapsed() { + public Duration getTimeElapsed() { return timeElapsed; } - public double getAvgMsActivePerTick() { - return timeActive / (ticks > 0 ? ticks : 1); + public Duration getAvgRunningTimePerTick() { + return timeActive.dividedBy(max(1, ticks)); } - public double getAvgMsActivePerJob() { - return timeActive / (jobs > 0 ? jobs : 1); + public Duration getAvgRunningTimePerJob() { + return timeActive.dividedBy(max(1, jobs)); } - public double getAvgMsElapsedPerJob() { - return timeElapsed *1d / (jobs > 0 ? jobs : 1); + public Duration getAvgTimeElapsedPerJob() { + return timeElapsed.dividedBy(max(1, jobs)); } public int getRunningJobs() { diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/biome/BiomeConfig.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/biome/BiomeConfig.java new file mode 100644 index 000000000..0b79774ea --- /dev/null +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/biome/BiomeConfig.java @@ -0,0 +1,102 @@ +package us.talabrek.ultimateskyblock.biome; + +import com.google.inject.Inject; +import com.google.inject.Singleton; +import dk.lockfuglsang.minecraft.file.FileUtil; +import dk.lockfuglsang.minecraft.util.ItemStackUtil; +import org.bukkit.Material; +import org.bukkit.Registry; +import org.bukkit.block.Biome; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Logger; + +import static java.util.Objects.requireNonNull; + +@Singleton +public class BiomeConfig { + + private final Logger logger; + private final List configuredBiomeEntries; + private final List configuredBiomeKeys; + + @Inject + public BiomeConfig(Logger logger) { + this.logger = logger; + this.configuredBiomeEntries = loadBiomes(); + this.configuredBiomeKeys = configuredBiomeEntries.stream() + .map(entry -> entry.biome().getKey().getKey()) + .toList(); + } + + private @NotNull List loadBiomes() { + FileConfiguration biomeConfig = FileUtil.getYmlConfiguration("biomes.yml"); + ConfigurationSection biomeSection = biomeConfig.getConfigurationSection("biomes"); + if (biomeSection == null) { + throw new IllegalStateException("You biomes.yml is corrupted, missing 'biomes' section."); + } + List result = new ArrayList<>(); + for (String biomeKey : biomeSection.getKeys(false)) { + ConfigurationSection section = requireNonNull(biomeSection.getConfigurationSection(biomeKey)); + BiomeEntry biomeEntry = readBiomeEntry(biomeKey, section); + if (biomeEntry != null) { + result.add(biomeEntry); + } + } + return result; + } + + public @NotNull List getConfiguredBiomeEntries() { + return configuredBiomeEntries; + } + + public @NotNull List getAvailableBiomes(@NotNull Player player) { + return configuredBiomeEntries.stream() + .filter(entry -> player.hasPermission("usb.biome." + entry.biome().getKey().getKey())) + .toList(); + } + + public @NotNull List getConfiguredBiomeKeys() { + return configuredBiomeKeys; + } + + @SuppressWarnings("removal") + private @Nullable BiomeEntry readBiomeEntry(@NotNull String biomeKey, @NotNull ConfigurationSection section) { + String itemSpecification = section.getString("displayItem"); + String name = section.getString("name"); + String description = section.getString("description"); + + Biome biome = Registry.BIOME.match(biomeKey); + if (biome == null) { + logger.warning("Unknown biome key: " + biomeKey); + return null; + } + + ItemStack displayItem; + try { + displayItem = ItemStackUtil.createItemStack(itemSpecification); + } catch (IllegalArgumentException e) { + logger.warning("Invalid item specification for biome " + biomeKey + ": " + itemSpecification); + displayItem = new ItemStack(Material.GRASS_BLOCK); + } + + if (name == null) { + logger.warning("Missing name for biome " + biomeKey); + name = biomeKey; + } + + if (description == null) { + logger.warning("Missing description for biome " + biomeKey); + description = ""; + } + + return new BiomeEntry(displayItem, biome, name, description); + } +} diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/biome/BiomeEntry.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/biome/BiomeEntry.java new file mode 100644 index 000000000..9f7587a08 --- /dev/null +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/biome/BiomeEntry.java @@ -0,0 +1,22 @@ +package us.talabrek.ultimateskyblock.biome; + +import org.bukkit.block.Biome; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; + +public record BiomeEntry( + @NotNull ItemStack displayItem, + @NotNull Biome biome, + @NotNull String name, + @NotNull String description +) { + + public BiomeEntry { + displayItem = displayItem.clone(); + } + + @Override + public @NotNull ItemStack displayItem() { + return displayItem.clone(); + } +} diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/biome/BiomeGui.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/biome/BiomeGui.java new file mode 100644 index 000000000..0bcac18aa --- /dev/null +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/biome/BiomeGui.java @@ -0,0 +1,182 @@ +package us.talabrek.ultimateskyblock.biome; + +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.block.Biome; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.jetbrains.annotations.NotNull; +import us.talabrek.ultimateskyblock.gui.InventoryButton; +import us.talabrek.ultimateskyblock.gui.InventoryGui; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; + +import static dk.lockfuglsang.minecraft.po.I18nUtil.tr; +import static java.util.Objects.requireNonNull; + +public class BiomeGui extends InventoryGui { + + private static final int MAX_INVENTORY_SIZE = 54; + private static final int MIN_INVENTORY_SIZE = 18; + private static final int ROW_SIZE = 9; + private static final List RADIUS_OPTIONS = + List.of("10", "chunk", "20", "30", "40", "50", "60", "70", "80", "90", "100", "all"); + + private final List biomes; + private final Biome currentBiome; + private final int inventorySize; + private String radius = "all"; + + public BiomeGui(@NotNull List biomes, @NotNull Biome currentBiome) { + super(createInventory(computeInventorySize(biomes.size()))); + this.biomes = requireNonNull(biomes); + this.currentBiome = requireNonNull(currentBiome);; + this.inventorySize = computeInventorySize(biomes.size()); + } + + private static int computeInventorySize(int biomesSize) { + int inventorySize = ((int) Math.ceil((double) biomesSize / (double) ROW_SIZE)) * ROW_SIZE; + inventorySize += ROW_SIZE; // Add one row for radius controls + inventorySize = Math.min(inventorySize, MAX_INVENTORY_SIZE); + inventorySize = Math.max(inventorySize, MIN_INVENTORY_SIZE); + return inventorySize; + } + + private static Inventory createInventory(int size) { + return Bukkit.createInventory(null, size, "\u00a79" + tr("Island Biome")); + } + + @Override + public void decorate(@NotNull Player player) { + decorateBiomes(); + decorateRadiusControls(); + decorateBackButton(); + super.decorate(player); + } + + private void decorateBiomes() { + int displayedBiomes = Math.min(biomes.size(), inventorySize - ROW_SIZE); + for (int i = 0; i < displayedBiomes; i++) { + BiomeEntry biomeEntry = biomes.get(i); + this.addButton(i, createBiomeButton(biomeEntry, currentBiome)); + } + } + + private InventoryButton createBiomeButton(BiomeEntry biomeEntry, Biome currentBiome) { + ItemStack displayItem = biomeEntry.displayItem().clone(); + ItemMeta itemMeta = Objects.requireNonNull(displayItem.getItemMeta()); + itemMeta.setDisplayName("\u00a7a" + tr("Biome: {0}", biomeEntry.name())); + List lore = new ArrayList<>( + Arrays.stream(biomeEntry.description().split("\\R")) + .filter(line -> !line.isBlank()) + .map(line -> "\u00a7f" + line) + .toList() + ); + if (biomeEntry.biome().equals(currentBiome)) { + lore.add(tr("\u00a72\u00a7lThis is your current biome.")); + itemMeta.addEnchant(Enchantment.LOYALTY, 1, true); + } else { + lore.add(tr("\u00a7e\u00a7lClick to change to this biome.")); + } + itemMeta.setLore(lore); + displayItem.setItemMeta(itemMeta); + + return new InventoryButton() + .creator((player) -> displayItem) + .consumer((player, event) -> { + player.performCommand("island biome " + biomeEntry.biome().getKey().getKey() + " " + radius); + player.closeInventory(); + }); + } + + private void decorateRadiusControls() { + this.addButton(inventorySize - 6, creteMinusButton()); + this.addButton(inventorySize - 5, createRadiusDisplay()); + this.addButton(inventorySize - 4, createPlusButton()); + } + + private InventoryButton creteMinusButton() { + return new InventoryButton() + .creator((player) -> { + ItemStack displayItem = new ItemStack(Material.RED_CARPET); + ItemMeta itemMeta = requireNonNull(requireNonNull(displayItem.getItemMeta())); + itemMeta.setDisplayName(tr("\u00a7c-")); + List lore = List.of( + tr("Decrease radius of biome change"), + tr("Current radius: {0}", formatRadius(radius)) + ); + itemMeta.setLore(lore); + displayItem.setItemMeta(itemMeta); + return displayItem; + }) + .consumer((player, event) -> { + int ix = RADIUS_OPTIONS.indexOf(radius); + if (ix > 0) { + radius = RADIUS_OPTIONS.get(ix - 1); + } + decorate(player); + }); + } + + private InventoryButton createPlusButton() { + return new InventoryButton() + .creator((player) -> { + ItemStack displayItem = new ItemStack(Material.GREEN_CARPET); + ItemMeta itemMeta = requireNonNull(requireNonNull(displayItem.getItemMeta())); + itemMeta.setDisplayName(tr("\u00a72+")); + List lore = List.of( + tr("Increase radius of biome change"), + tr("Current radius: {0}", formatRadius(radius)) + ); + itemMeta.setLore(lore); + displayItem.setItemMeta(itemMeta); + return displayItem; + }) + .consumer((player, event) -> { + int ix = RADIUS_OPTIONS.indexOf(radius); + if (ix < RADIUS_OPTIONS.size() - 1) { + radius = RADIUS_OPTIONS.get(ix + 1); + } + decorate(player); + }); + } + + private InventoryButton createRadiusDisplay() { + return new InventoryButton() + .creator((player) -> { + ItemStack displayItem = new ItemStack(Material.GRASS_BLOCK); + ItemMeta itemMeta = requireNonNull(requireNonNull(displayItem.getItemMeta())); + itemMeta.setDisplayName(tr("Current radius: {0}", formatRadius(radius))); + displayItem.setItemMeta(itemMeta); + return displayItem; + }); + } + + private static String formatRadius(String radius) { + return switch (radius) { + case "chunk" -> tr("\u00a72chunk"); + case "all" -> tr("\u00a7call"); + default -> tr("\u00a7e{0}", radius); + }; + } + + private void decorateBackButton() { + InventoryButton backButton = new InventoryButton() + .creator((player) -> { + ItemStack displayItem = new ItemStack(Material.OAK_SIGN); + ItemMeta itemMeta = requireNonNull(displayItem.getItemMeta()); + itemMeta.setDisplayName(tr("\u00a7cBack to Main Menu")); + itemMeta.setLore(List.of(tr("\u00a7eClick here to return to the main island screen."))); + displayItem.setItemMeta(itemMeta); + return displayItem; + }) + .consumer((player, event) -> player.performCommand("island")); + this.addButton(inventorySize - 9, backButton); + } +} diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/biome/Biomes.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/biome/Biomes.java new file mode 100644 index 000000000..c54bfdfe2 --- /dev/null +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/biome/Biomes.java @@ -0,0 +1,23 @@ +package us.talabrek.ultimateskyblock.biome; + +import com.google.inject.Inject; +import org.bukkit.entity.Player; +import us.talabrek.ultimateskyblock.gui.GuiManager; +import us.talabrek.ultimateskyblock.island.IslandInfo; + +public class Biomes { + + private final GuiManager guiManager; + private final BiomeConfig biomeConfig; + + @Inject + public Biomes(GuiManager guiManager, BiomeConfig biomeConfig) { + this.guiManager = guiManager; + this.biomeConfig = biomeConfig; + } + + public void openBiomeGui(Player player, IslandInfo islandInfo) { + BiomeGui biomeGui = new BiomeGui(biomeConfig.getAvailableBiomes(player), islandInfo.getIslandBiome()); + guiManager.openGui(biomeGui, player); + } +} diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/block/BlockCollection.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/block/BlockCollection.java index 68a7e3d6e..e3dfce4ef 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/block/BlockCollection.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/block/BlockCollection.java @@ -1,47 +1,37 @@ package us.talabrek.ultimateskyblock.block; +import dk.lockfuglsang.minecraft.util.BlockRequirement; +import dk.lockfuglsang.minecraft.util.ItemStackUtil; import org.bukkit.Material; import org.bukkit.block.Block; -import org.bukkit.inventory.ItemStack; -import us.talabrek.ultimateskyblock.handler.VaultHandler; -import java.util.Collection; -import java.util.Collections; import java.util.HashMap; +import java.util.List; import java.util.Map; import static dk.lockfuglsang.minecraft.po.I18nUtil.tr; public class BlockCollection { - Map> blockCount; + Map blockCount; public BlockCollection() { this.blockCount = new HashMap<>(); } - public synchronized void add(Block block) { - Map countMap = blockCount.getOrDefault(block.getType(), new HashMap<>()); - int currentValue = countMap.getOrDefault(block.getData(), 0); - countMap.put(block.getData(), currentValue + 1); - blockCount.put(block.getType(), countMap); + public void add(Block block) { + int currentValue = blockCount.getOrDefault(block.getType(), 0); + blockCount.put(block.getType(), currentValue + 1); } /** * Returns null if all the items are in the BlockCollection, a String describing the missing items if it's not - * @param itemStacks - * @return */ - public synchronized String diff(Collection itemStacks) { + public String diff(List requirements) { StringBuilder sb = new StringBuilder(); - for (ItemStack item : itemStacks) { - int diff = 0; - if (item.getDurability() != 0) { - diff = item.getAmount() - count(item.getType(), (byte) (item.getDurability() & 0xff)); - } else { - diff = item.getAmount() - count(item.getType()); - } + for (BlockRequirement requirement : requirements) { + int diff = requirement.amount() - count(requirement.type().getMaterial()); if (diff > 0) { - sb.append(tr(" \u00a7f{0}x \u00a77{1}", diff, VaultHandler.getItemName(item))); + sb.append(tr(" \u00a7f{0}x \u00a77{1}", diff, ItemStackUtil.getBlockName(requirement.type()))); } } if (sb.toString().trim().isEmpty()) { @@ -51,12 +41,6 @@ public synchronized String diff(Collection itemStacks) { } private int count(Material type) { - Map countMap = blockCount.getOrDefault(type, Collections.emptyMap()); - return countMap.values().stream().mapToInt(i -> i).sum(); - } - - public int count(Material type, byte data) { - Map countMap = blockCount.getOrDefault(type, Collections.emptyMap()); - return countMap.getOrDefault(data, 0); + return blockCount.getOrDefault(type, 0); } } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/bootstrap/Commands.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/bootstrap/Commands.java new file mode 100644 index 000000000..d969bebe7 --- /dev/null +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/bootstrap/Commands.java @@ -0,0 +1,46 @@ +package us.talabrek.ultimateskyblock.bootstrap; + +import com.google.inject.Inject; +import com.google.inject.Singleton; +import org.bukkit.plugin.java.JavaPlugin; +import org.jetbrains.annotations.NotNull; +import us.talabrek.ultimateskyblock.chat.IslandTalkCommand; +import us.talabrek.ultimateskyblock.chat.PartyTalkCommand; +import us.talabrek.ultimateskyblock.command.AdminCommand; +import us.talabrek.ultimateskyblock.command.ChallengeCommand; +import us.talabrek.ultimateskyblock.command.IslandCommand; + +import static java.util.Objects.requireNonNull; + +@Singleton +public class Commands { + + private final IslandCommand islandCommand; + private final ChallengeCommand challengeCommand; + private final AdminCommand adminCommand; + private final IslandTalkCommand islandTalkCommand; + private final PartyTalkCommand partyTalkCommand; + + @Inject + public Commands( + @NotNull IslandCommand islandCommand, + @NotNull ChallengeCommand challengeCommand, + @NotNull AdminCommand adminCommand, + @NotNull IslandTalkCommand islandTalkCommand, + @NotNull PartyTalkCommand partyTalkCommand + ) { + this.islandCommand = islandCommand; + this.challengeCommand = challengeCommand; + this.adminCommand = adminCommand; + this.islandTalkCommand = islandTalkCommand; + this.partyTalkCommand = partyTalkCommand; + } + + public void registerCommands(JavaPlugin plugin) { + requireNonNull(plugin.getCommand("island")).setExecutor(islandCommand); + requireNonNull(plugin.getCommand("challenges")).setExecutor(challengeCommand); + requireNonNull(plugin.getCommand("usb")).setExecutor(adminCommand); + requireNonNull(plugin.getCommand("islandtalk")).setExecutor(islandTalkCommand); + requireNonNull(plugin.getCommand("partytalk")).setExecutor(partyTalkCommand); + } +} diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/bootstrap/Listeners.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/bootstrap/Listeners.java new file mode 100644 index 000000000..dbd89dd89 --- /dev/null +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/bootstrap/Listeners.java @@ -0,0 +1,129 @@ +package us.talabrek.ultimateskyblock.bootstrap; + +import com.google.inject.Inject; +import com.google.inject.Singleton; +import org.bukkit.event.HandlerList; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.PluginManager; +import org.jetbrains.annotations.NotNull; +import us.talabrek.ultimateskyblock.PluginConfig; +import us.talabrek.ultimateskyblock.Settings; +import us.talabrek.ultimateskyblock.chat.ChatEvents; +import us.talabrek.ultimateskyblock.event.ExploitEvents; +import us.talabrek.ultimateskyblock.event.GriefEvents; +import us.talabrek.ultimateskyblock.event.InternalEvents; +import us.talabrek.ultimateskyblock.event.ItemDropEvents; +import us.talabrek.ultimateskyblock.event.MenuEvents; +import us.talabrek.ultimateskyblock.event.NetherTerraFormEvents; +import us.talabrek.ultimateskyblock.event.PlayerEvents; +import us.talabrek.ultimateskyblock.event.SpawnEvents; +import us.talabrek.ultimateskyblock.event.ToolMenuEvents; +import us.talabrek.ultimateskyblock.event.WitherTagEvents; +import us.talabrek.ultimateskyblock.event.WorldGuardEvents; +import us.talabrek.ultimateskyblock.gui.GuiListener; +import us.talabrek.ultimateskyblock.signs.SignEvents; +import us.talabrek.ultimateskyblock.command.InviteHandler; +import us.talabrek.ultimateskyblock.uuid.PlayerDB; + +@Singleton +public class Listeners { + + private final PluginConfig config; + + private final GuiListener guiListener; + private final InternalEvents internalEvents; + private final PlayerEvents playerEvents; + private final MenuEvents menuEvents; + private final ExploitEvents exploitEvents; + private final WitherTagEvents witherTagEvents; + private final GriefEvents griefEvents; + private final ItemDropEvents itemDropEvents; + private final SpawnEvents spawnEvents; + private final WorldGuardEvents worldGuardEvents; + private final NetherTerraFormEvents netherTerraFormEvents; + private final ToolMenuEvents toolMenuEvents; + private final SignEvents signEvents; + private final ChatEvents chatEvents; + private final InviteHandler inviteHandler; + private final PlayerDB playerDB; + + @Inject + public Listeners( + @NotNull PluginConfig config, + @NotNull GuiListener guiListener, + @NotNull InternalEvents internalEvents, + @NotNull PlayerEvents playerEvents, + @NotNull MenuEvents menuEvents, + @NotNull ExploitEvents exploitEvents, + @NotNull WitherTagEvents witherTagEvents, + @NotNull GriefEvents griefEvents, + @NotNull ItemDropEvents itemDropEvents, + @NotNull SpawnEvents spawnEvents, + @NotNull WorldGuardEvents worldGuardEvents, + @NotNull NetherTerraFormEvents netherTerraFormEvents, + @NotNull ToolMenuEvents toolMenuEvents, + @NotNull SignEvents signEvents, + @NotNull ChatEvents chatEvents, + @NotNull InviteHandler inviteHandler, + @NotNull PlayerDB playerDB + ) { + this.config = config; + this.guiListener = guiListener; + this.internalEvents = internalEvents; + this.playerEvents = playerEvents; + this.menuEvents = menuEvents; + this.exploitEvents = exploitEvents; + this.witherTagEvents = witherTagEvents; + this.griefEvents = griefEvents; + this.itemDropEvents = itemDropEvents; + this.spawnEvents = spawnEvents; + this.worldGuardEvents = worldGuardEvents; + this.netherTerraFormEvents = netherTerraFormEvents; + this.toolMenuEvents = toolMenuEvents; + this.signEvents = signEvents; + this.chatEvents = chatEvents; + this.inviteHandler = inviteHandler; + this.playerDB = playerDB; + } + + public void registerListeners(Plugin plugin) { + PluginManager manager = plugin.getServer().getPluginManager(); + + manager.registerEvents(internalEvents, plugin); + manager.registerEvents(playerEvents, plugin); + manager.registerEvents(menuEvents, plugin); + manager.registerEvents(guiListener, plugin); + manager.registerEvents(exploitEvents, plugin); + manager.registerEvents(witherTagEvents, plugin); + manager.registerEvents(chatEvents, plugin); + manager.registerEvents(inviteHandler, plugin); + manager.registerEvents(playerDB, plugin); + + // TODO minoneer 06.02.2025: Move this logic. Either into the appropriate listener, or into submodules if we don't want all features active (e.g., the nether) + if (config.getYamlConfig().getBoolean("options.protection.enabled", true)) { + manager.registerEvents(griefEvents, plugin); + if (config.getYamlConfig().getBoolean("options.protection.item-drops", true)) { + manager.registerEvents(itemDropEvents, plugin); + } + } + if (config.getYamlConfig().getBoolean("options.island.spawn-limits.enabled", true)) { + manager.registerEvents(spawnEvents, plugin); + } + if (config.getYamlConfig().getBoolean("options.protection.visitors.block-banned-entry", true)) { + manager.registerEvents(worldGuardEvents, plugin); + } + if (Settings.nether_enabled) { + manager.registerEvents(netherTerraFormEvents, plugin); + } + if (config.getYamlConfig().getBoolean("tool-menu.enabled", true)) { + manager.registerEvents(toolMenuEvents, plugin); + } + if (config.getYamlConfig().getBoolean("signs.enabled", true)) { + manager.registerEvents(signEvents, plugin); + } + } + + public void unregisterListeners(Plugin plugin) { + HandlerList.unregisterAll(plugin); + } +} diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/bootstrap/PluginDataDir.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/bootstrap/PluginDataDir.java new file mode 100644 index 000000000..1f8d5ba85 --- /dev/null +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/bootstrap/PluginDataDir.java @@ -0,0 +1,17 @@ +package us.talabrek.ultimateskyblock.bootstrap; + +import com.google.inject.BindingAnnotation; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +@BindingAnnotation +@Target({FIELD, PARAMETER, METHOD}) +@Retention(RUNTIME) +public @interface PluginDataDir { +} diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/bootstrap/Services.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/bootstrap/Services.java new file mode 100644 index 000000000..fe4fbf2a6 --- /dev/null +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/bootstrap/Services.java @@ -0,0 +1,88 @@ +package us.talabrek.ultimateskyblock.bootstrap; + +import com.google.inject.Inject; +import com.google.inject.Singleton; +import dk.lockfuglsang.minecraft.animation.AnimationHandler; +import org.jetbrains.annotations.NotNull; +import us.talabrek.ultimateskyblock.MetricsManager; +import us.talabrek.ultimateskyblock.api.event.EventLogic; +import us.talabrek.ultimateskyblock.challenge.ChallengeLogic; +import us.talabrek.ultimateskyblock.command.admin.DebugCommand; +import us.talabrek.ultimateskyblock.handler.AsyncWorldEditHandler; +import us.talabrek.ultimateskyblock.handler.WorldGuardHandler; +import us.talabrek.ultimateskyblock.handler.placeholder.PlaceholderModule; +import us.talabrek.ultimateskyblock.hook.HookManager; +import us.talabrek.ultimateskyblock.island.IslandLogic; +import us.talabrek.ultimateskyblock.island.level.AutoIslandLevelRefresh; +import us.talabrek.ultimateskyblock.player.PlayerLogic; +import us.talabrek.ultimateskyblock.uSkyBlock; +import us.talabrek.ultimateskyblock.uuid.PlayerDB; + +@Singleton +public class Services { + + private final AnimationHandler animationHandler; + private final ChallengeLogic challengeLogic; + private final EventLogic eventLogic; + private final PlayerLogic playerLogic; + private final IslandLogic islandLogic; + private final PlayerDB playerDB; + private final MetricsManager metricsManager; + private final HookManager hookManager; + private final AutoIslandLevelRefresh autoIslandLevelRefresh; + private final PlaceholderModule placeholderModule; + + @Inject + public Services( + @NotNull AnimationHandler animationHandler, + @NotNull ChallengeLogic challengeLogic, + @NotNull EventLogic eventLogic, + @NotNull PlayerLogic playerLogic, + @NotNull IslandLogic islandLogic, + @NotNull PlayerDB playerDB, + @NotNull MetricsManager metricsManager, + @NotNull HookManager hookManager, + @NotNull AutoIslandLevelRefresh autoIslandLevelRefresh, + @NotNull PlaceholderModule placeholderModule + ) { + this.animationHandler = animationHandler; + this.challengeLogic = challengeLogic; + this.eventLogic = eventLogic; + this.playerLogic = playerLogic; + this.islandLogic = islandLogic; + this.playerDB = playerDB; + this.metricsManager = metricsManager; + this.hookManager = hookManager; + this.autoIslandLevelRefresh = autoIslandLevelRefresh; + this.placeholderModule = placeholderModule; + } + + public void startup(uSkyBlock plugin) { + metricsManager.setup(); + autoIslandLevelRefresh.startup(); + placeholderModule.startup(plugin); + } + + public void delayedEnable(uSkyBlock plugin) { + hookManager.setupHooks(); + + // TODO: make these non-static objects + AsyncWorldEditHandler.onEnable(plugin); + WorldGuardHandler.setupGlobal(plugin.getWorldManager().getWorld()); + if (plugin.getWorldManager().getNetherWorld() != null) { + WorldGuardHandler.setupGlobal(plugin.getWorldManager().getNetherWorld()); + } + } + + public void shutdown(uSkyBlock plugin) { + autoIslandLevelRefresh.shutdown(); + animationHandler.stop(); + challengeLogic.shutdown(); + eventLogic.shutdown(); + playerLogic.shutdown(); + islandLogic.shutdown(); + playerDB.shutdown(); + AsyncWorldEditHandler.onDisable(plugin); + DebugCommand.disableLogging(null); + } +} diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/bootstrap/SkyblockApp.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/bootstrap/SkyblockApp.java new file mode 100644 index 000000000..198c13be9 --- /dev/null +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/bootstrap/SkyblockApp.java @@ -0,0 +1,41 @@ +package us.talabrek.ultimateskyblock.bootstrap; + +import com.google.inject.Inject; +import com.google.inject.Singleton; +import org.bukkit.Bukkit; +import org.jetbrains.annotations.NotNull; +import us.talabrek.ultimateskyblock.uSkyBlock; + +@Singleton +public class SkyblockApp { + + private final Services services; + private final Commands commands; + private final Listeners listeners; + + @Inject + public SkyblockApp(@NotNull Services services, @NotNull Commands commands, @NotNull Listeners listeners) { + this.services = services; + this.commands = commands; + this.listeners = listeners; + } + + + public void startup(uSkyBlock plugin) { + services.startup(plugin); + } + + public void delayedEnable(uSkyBlock plugin) { + services.delayedEnable(plugin); + + // do these really have to be delayed? + commands.registerCommands(plugin); + listeners.registerListeners(plugin); + } + + public void shutdown(uSkyBlock plugin) { + Bukkit.getScheduler().cancelTasks(plugin); + listeners.unregisterListeners(plugin); + services.shutdown(plugin); + } +} diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/bootstrap/SkyblockModule.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/bootstrap/SkyblockModule.java new file mode 100644 index 000000000..0160f6cc6 --- /dev/null +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/bootstrap/SkyblockModule.java @@ -0,0 +1,75 @@ +package us.talabrek.ultimateskyblock.bootstrap; + +import com.google.inject.AbstractModule; +import com.google.inject.Provides; +import com.google.inject.Singleton; +import dk.lockfuglsang.minecraft.animation.AnimationHandler; +import dk.lockfuglsang.minecraft.command.DocumentCommand; +import org.bukkit.plugin.Plugin; +import org.jetbrains.annotations.NotNull; +import us.talabrek.ultimateskyblock.PluginConfig; +import us.talabrek.ultimateskyblock.SkyUpdateChecker; +import us.talabrek.ultimateskyblock.api.plugin.UpdateChecker; +import us.talabrek.ultimateskyblock.handler.placeholder.MVdWPlaceholderAPI; +import us.talabrek.ultimateskyblock.handler.placeholder.MvdwPlacehoderProvider; +import us.talabrek.ultimateskyblock.handler.placeholder.PlaceholderAPI; +import us.talabrek.ultimateskyblock.handler.placeholder.PlaceholderReplacerImpl; +import us.talabrek.ultimateskyblock.island.level.ChunkSnapshotLevelLogic; +import us.talabrek.ultimateskyblock.island.level.LevelLogic; +import us.talabrek.ultimateskyblock.uSkyBlock; +import us.talabrek.ultimateskyblock.util.Scheduler; +import us.talabrek.ultimateskyblock.uuid.BukkitPlayerDB; +import us.talabrek.ultimateskyblock.uuid.FilePlayerDB; +import us.talabrek.ultimateskyblock.uuid.MemoryPlayerDB; +import us.talabrek.ultimateskyblock.uuid.PlayerDB; + +import java.nio.file.Path; +import java.time.Clock; +import java.util.logging.Logger; + +public class SkyblockModule extends AbstractModule { + + private final uSkyBlock plugin; + + public SkyblockModule(uSkyBlock plugin) { + this.plugin = plugin; + } + + @Override + protected void configure() { + // TODO: this should not be injected, but it is here fore legacy reasons. Move all functionality out of the plugin class and into the appropriate classes. + bind(uSkyBlock.class).toInstance(plugin); + bind(Plugin.class).toInstance(plugin); + bind(Path.class).annotatedWith(PluginDataDir.class).toInstance(plugin.getDataFolder().toPath()); + bind(LevelLogic.class).to(ChunkSnapshotLevelLogic.class); + bind(UpdateChecker.class).to(SkyUpdateChecker.class); + bind(Clock.class).toInstance(Clock.systemUTC()); + bind(PlaceholderAPI.PlaceholderReplacer.class).to(PlaceholderReplacerImpl.class); + bind(MVdWPlaceholderAPI.class).toProvider(MvdwPlacehoderProvider.class); + } + + @Provides + @Singleton + public static + @NotNull PlayerDB providePlayerDB(PluginConfig config, uSkyBlock plugin, Scheduler scheduler, Logger logger) { + String playerDbStorage = config.getYamlConfig().getString("options.advanced.playerdb.storage", "yml"); + if (playerDbStorage.equalsIgnoreCase("yml")) { + return new FilePlayerDB(plugin, scheduler, logger); + } else if (playerDbStorage.equalsIgnoreCase("memory")) { + return new MemoryPlayerDB(config); + } else { + return new BukkitPlayerDB(); + } + } + + @Provides + @Singleton + public static @NotNull AnimationHandler provideAnimationHandler(Plugin plugin) { + return new AnimationHandler(plugin); + } + + @Provides + public static @NotNull DocumentCommand provideDocumentCommand(uSkyBlock plugin) { + return new DocumentCommand(plugin, "doc", "usb.admin.doc"); + } +} diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/challenge/Challenge.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/challenge/Challenge.java index 03cf2fc21..490c63d6d 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/challenge/Challenge.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/challenge/Challenge.java @@ -1,29 +1,30 @@ package us.talabrek.ultimateskyblock.challenge; -import dk.lockfuglsang.minecraft.nbt.NBTUtil; +import dk.lockfuglsang.minecraft.util.BlockRequirement; +import dk.lockfuglsang.minecraft.util.FormatUtil; +import dk.lockfuglsang.minecraft.util.ItemRequirement; +import dk.lockfuglsang.minecraft.util.ItemStackUtil; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.ItemMeta; -import us.talabrek.ultimateskyblock.handler.VaultHandler; +import org.jetbrains.annotations.NotNull; import us.talabrek.ultimateskyblock.player.PlayerInfo; import us.talabrek.ultimateskyblock.uSkyBlock; -import dk.lockfuglsang.minecraft.util.FormatUtil; -import dk.lockfuglsang.minecraft.util.ItemStackUtil; +import java.time.Duration; import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.logging.Level; -import java.util.regex.Matcher; -import java.util.regex.Pattern; +import java.util.Map; +import java.util.stream.Collectors; import static dk.lockfuglsang.minecraft.po.I18nUtil.tr; -import static dk.lockfuglsang.minecraft.util.FormatUtil.*; +import static dk.lockfuglsang.minecraft.util.FormatUtil.prefix; +import static dk.lockfuglsang.minecraft.util.FormatUtil.wordWrap; /** * The data-object for a challenge */ public class Challenge { - public static final Pattern REQ_PATTERN = Pattern.compile("(?(?[0-9A-Z_]+)(:(?[0-9]+))?(?\\{.*\\})?):(?[0-9]+)(;(?[+\\-*\\^])(?[0-9]+))?"); public static final int MAX_DETAILS = 11; public static final int MAX_LINE = 30; @@ -31,9 +32,9 @@ public enum Type { PLAYER, ISLAND, ISLAND_LEVEL; static Type from(String s) { - if (s == null || s.trim().isEmpty() || s.trim().toLowerCase().equals("onplayer")) { + if (s == null || s.trim().isEmpty() || s.trim().equalsIgnoreCase("onplayer")) { return PLAYER; - } else if (s != null && s.equalsIgnoreCase("islandlevel")) { + } else if (s.equalsIgnoreCase("islandlevel")) { return ISLAND_LEVEL; } return ISLAND; @@ -44,12 +45,13 @@ static Type from(String s) { private final String description; private final String displayName; private final Type type; - private final List requiredItems; + private final List requiredItems; + private final List requiredBlocks; private final List requiredEntities; private final List requiredChallenges; - private double requiredLevel; + private final double requiredLevel; private final Rank rank; - private final int resetInHours; + private final Duration resetDuration; private final ItemStack displayItem; private final String tool; private final ItemStack lockedItem; @@ -60,19 +62,21 @@ static Type from(String s) { private final Reward repeatReward; private final int repeatLimit; - public Challenge(String name, String displayName, String description, Type type, List requiredItems, - List requiredEntities, List requiredChallenges, double requiredLevel, Rank rank, int resetInHours, - ItemStack displayItem, String tool, ItemStack lockedItem, int offset, boolean takeItems, - int radius, Reward reward, Reward repeatReward, int repeatLimit) { + public Challenge(String name, String displayName, String description, Type type, List requiredItems, + @NotNull List requiredBlocks, List requiredEntities, + List requiredChallenges, double requiredLevel, Rank rank, + Duration resetDuration, ItemStack displayItem, String tool, ItemStack lockedItem, int offset, + boolean takeItems, int radius, Reward reward, Reward repeatReward, int repeatLimit) { this.name = name; this.displayName = displayName; this.type = type; this.requiredItems = requiredItems; + this.requiredBlocks = requiredBlocks; this.requiredEntities = requiredEntities; this.requiredChallenges = requiredChallenges; this.requiredLevel = requiredLevel; this.rank = rank; - this.resetInHours = resetInHours; + this.resetDuration = resetDuration; this.displayItem = displayItem; this.tool = tool; this.lockedItem = lockedItem; @@ -113,29 +117,17 @@ public double getRequiredLevel() { return requiredLevel; } - public List getRequiredItems(int timesCompleted) { - List items = new ArrayList<>(); - for (String item : requiredItems) { - if (item == null || item.trim().isEmpty()) { - continue; // Just skip it - } - Matcher m = REQ_PATTERN.matcher(item); - if (m.matches()) { - int amount = Integer.parseInt(m.group("amount"), 10); - char op = m.group("op") != null ? m.group("op").charAt(0) : 0; - int inc = m.group("inc") != null ? Integer.parseInt(m.group("inc"), 10) : 0; - amount = ChallengeLogic.calcAmount(amount, op, inc, timesCompleted); - ItemStack mat = ItemStackUtil.createItemStack(m.group("itemstack")); - ItemMeta meta = mat.getItemMeta(); - mat.setItemMeta(meta); - mat = NBTUtil.addNBTTag(mat, m.group("meta")); - mat.setAmount(amount); - items.add(mat); - } else if (!item.matches("[0-9]+") && type != Type.ISLAND_LEVEL) { - uSkyBlock.getInstance().getLogger().log(Level.INFO, "Malformed challenge " + name + ", item: " + item + " is not a valid required item"); - } - } - return items; + @NotNull + public Map getRequiredItems(int timesCompleted) { + return this.requiredItems.stream().collect(Collectors.toUnmodifiableMap( + item -> item.type().clone(), + item -> item.amountForRepetitions(timesCompleted) + )); + } + + @NotNull + public List getRequiredBlocks() { + return requiredBlocks; } public List getRequiredEntities() { @@ -150,65 +142,72 @@ public Rank getRank() { return rank; } - public int getResetInHours() { - return resetInHours; + public Duration getResetDuration() { + return resetDuration; } public ItemStack getDisplayItem(ChallengeCompletion completion, boolean withCurrency) { int timesCompleted = completion.getTimesCompletedInCooldown(); ItemStack currentChallengeItem = getDisplayItem(); ItemMeta meta = currentChallengeItem.getItemMeta(); - List lores = new ArrayList<>(); - lores.addAll(prefix(wordWrap(getDescription(), MAX_LINE), "\u00a77")); + List lores = new ArrayList<>(prefix(wordWrap(getDescription(), MAX_LINE), "\u00a77")); Reward reward = getReward(); if (completion.getTimesCompleted() > 0 && isRepeatable()) { currentChallengeItem.setAmount(completion.getTimesCompleted() < currentChallengeItem.getMaxStackSize() ? completion.getTimesCompleted() : currentChallengeItem.getMaxStackSize()); if (completion.isOnCooldown()) { - long cooldown = completion.getCooldownInMillis(); + Duration cooldown = completion.getCooldown(); if (timesCompleted < getRepeatLimit() || getRepeatLimit() <= 0) { if (getRepeatLimit() > 0) { lores.add(tr("\u00a74You can complete this {0} more time(s).", getRepeatLimit() - timesCompleted)); } - if (cooldown >= ChallengeLogic.MS_DAY) { - final int days = (int) (cooldown / ChallengeLogic.MS_DAY); - lores.add(tr("\u00a74Requirements will reset in {0} days.", days)); - } else if (cooldown >= ChallengeLogic.MS_HOUR) { - final int hours = (int) (cooldown / ChallengeLogic.MS_HOUR); - lores.add(tr("\u00a74Requirements will reset in {0} hours.", hours)); - } else if (cooldown >= 0) { - final int minutes = Math.round(cooldown / ChallengeLogic.MS_MIN); - lores.add(tr("\u00a74Requirements will reset in {0} minutes.", minutes)); + if (cooldown.toDays() > 0) { + lores.add(tr("\u00a74Requirements will reset in {0} days.", cooldown.toDays())); + } else if (cooldown.toHours() > 0) { + lores.add(tr("\u00a74Requirements will reset in {0} hours.", cooldown.toHours())); + } else { + lores.add(tr("\u00a74Requirements will reset in {0} minutes.", cooldown.toMinutes())); } } else { lores.add(tr("\u00a74This challenge is currently unavailable.")); - if (cooldown >= ChallengeLogic.MS_DAY) { - final int days = (int) (cooldown / ChallengeLogic.MS_DAY); - lores.add(tr("\u00a74You can complete this again in {0} days.", days)); - } else if (cooldown >= ChallengeLogic.MS_HOUR) { - final int hours = (int) (cooldown / ChallengeLogic.MS_HOUR); - lores.add(tr("\u00a74You can complete this again in {0} hours.", hours)); - } else if (cooldown >= 0) { - final int minutes = Math.round(cooldown / ChallengeLogic.MS_MIN); - lores.add(tr("\u00a74You can complete this again in {0} minutes.", minutes)); + if (cooldown.toDays() > 0) { + lores.add(tr("\u00a74You can complete this again in {0} days.", cooldown.toDays())); + } else if (cooldown.toHours() > 0) { + lores.add(tr("\u00a74You can complete this again in {0} hours.", cooldown.toHours())); + } else { + lores.add(tr("\u00a74You can complete this again in {0} minutes.", cooldown.toMinutes())); } } } reward = getRepeatReward(); } - List reqItems = getRequiredItems(timesCompleted); - if ((reqItems != null && !reqItems.isEmpty()) || (requiredEntities != null && !requiredEntities.isEmpty())) { + Map requiredItemsForChallenge = getRequiredItems(timesCompleted); + if (!requiredItemsForChallenge.isEmpty() || !requiredBlocks.isEmpty() + || (requiredEntities != null && !requiredEntities.isEmpty())) { lores.add(tr("\u00a7eThis challenge requires:")); } List details = new ArrayList<>(); - if (reqItems != null && !reqItems.isEmpty()) { - for (ItemStack item : reqItems) { + if (!requiredItemsForChallenge.isEmpty()) { + for (Map.Entry requiredItem : requiredItemsForChallenge.entrySet()) { + if (wrappedDetails(details).size() >= MAX_DETAILS) { + details.add(tr("\u00a77and more...")); + break; + } + int requiredAmount = requiredItem.getValue(); + ItemStack requiredType = requiredItem.getKey(); + details.add(requiredAmount > 1 + ? tr("\u00a7f{0}x \u00a77{1}", requiredAmount, ItemStackUtil.getItemName(requiredType)) + : tr("\u00a77{0}", ItemStackUtil.getItemName(requiredType))); + } + } + if (!requiredBlocks.isEmpty() && wrappedDetails(details).size() < MAX_DETAILS) { + for (BlockRequirement blockRequirement : requiredBlocks) { if (wrappedDetails(details).size() >= MAX_DETAILS) { details.add(tr("\u00a77and more...")); break; } - details.add(item.getAmount() > 1 - ? tr("\u00a7f{0}x \u00a77{1}", item.getAmount(), VaultHandler.getItemName(item)) - : tr("\u00a77{0}", VaultHandler.getItemName(item))); + details.add(blockRequirement.amount() > 1 + ? tr("\u00a7f{0}x \u00a77{1}", blockRequirement.amount(), ItemStackUtil.getBlockName(blockRequirement.type())) + : tr("\u00a77{0}", ItemStackUtil.getBlockName(blockRequirement.type()))); } } if (requiredEntities != null && !requiredEntities.isEmpty() && wrappedDetails(details).size() < MAX_DETAILS) { @@ -218,8 +217,8 @@ public ItemStack getDisplayItem(ChallengeCompletion completion, boolean withCurr break; } details.add(entityMatch.getCount() > 1 - ? tr("\u00a7f{0}x \u00a77{1}", entityMatch.getCount(), entityMatch.getDisplayName()) - : tr("\u00a77{0}", entityMatch.getDisplayName())); + ? tr("\u00a7f{0}x \u00a77{1}", entityMatch.getCount(), entityMatch.getDisplayName()) + : tr("\u00a77{0}", entityMatch.getDisplayName())); } } lores.addAll(wrappedDetails(details)); @@ -231,10 +230,8 @@ public ItemStack getDisplayItem(ChallengeCompletion completion, boolean withCurr lores.add(tr("\u00a7eMust be within {0} meters.", getRadius())); } List lines = wordWrap("\u00a7a" + reward.getRewardText(), 20, MAX_LINE); - lores.add(tr("\u00a76Item Reward: \u00a7a") + lines.get(0)); - for (String line : lines.subList(1, lines.size())) { - lores.add(line); - } + lores.add(tr("\u00a76Item Reward: \u00a7a") + lines.getFirst()); + lores.addAll(lines.subList(1, lines.size())); if (withCurrency) { lores.add(tr("\u00a76Currency Reward: \u00a7a{0}", reward.getCurrencyReward())); } @@ -251,7 +248,7 @@ public int getOffset() { } private List wrappedDetails(List details) { - return wordWrap(join(details, ", "), MAX_LINE); + return wordWrap(String.join(", ", details), MAX_LINE); } public ItemStack getDisplayItem() { @@ -293,16 +290,16 @@ public List getMissingRequirements(PlayerInfo playerInfo) { @Override public String toString() { return "Challenge{" + - "name='" + name + '\'' + - ", type=" + type + - ", requiredItems='" + requiredItems + '\'' + - ", rank='" + rank + '\'' + - ", resetInHours=" + resetInHours + - ", displayItem=" + displayItem + - ", takeItems=" + takeItems + - ", reward=" + reward + - ", repeatReward=" + repeatReward + - ", repeatLimit=" + repeatLimit + - '}'; + "name='" + name + '\'' + + ", type=" + type + + ", requiredItems='" + requiredItems + '\'' + + ", rank='" + rank + '\'' + + ", resetDuration=" + resetDuration + + ", displayItem=" + displayItem + + ", takeItems=" + takeItems + + ", reward=" + reward + + ", repeatReward=" + repeatReward + + ", repeatLimit=" + repeatLimit + + '}'; } } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/challenge/ChallengeCompletion.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/challenge/ChallengeCompletion.java index bde4b07fa..33c479b95 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/challenge/ChallengeCompletion.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/challenge/ChallengeCompletion.java @@ -1,19 +1,18 @@ package us.talabrek.ultimateskyblock.challenge; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.time.Duration; +import java.time.Instant; + public class ChallengeCompletion implements us.talabrek.ultimateskyblock.api.ChallengeCompletion { private String name; - private long cooldownUntil; + private Instant cooldownUntil; private int timesCompleted; private int timesCompletedInCooldown; - public ChallengeCompletion(final String name) { - super(); - this.name = name; - this.cooldownUntil = 0L; - this.timesCompleted = 0; - } - - public ChallengeCompletion(final String name, final long cooldownUntil, final int timesCompleted, final int timesCompletedInCooldown) { + public ChallengeCompletion(final String name, @Nullable Instant cooldownUntil, final int timesCompleted, final int timesCompletedInCooldown) { super(); this.name = name; this.cooldownUntil = cooldownUntil; @@ -26,22 +25,23 @@ public String getName() { return this.name; } - public long getCooldownUntil() { + @Override + public @Nullable Instant cooldownUntil() { return this.cooldownUntil; } @Override public boolean isOnCooldown() { - return cooldownUntil < 0 || cooldownUntil > System.currentTimeMillis(); + return getCooldown().isPositive(); } @Override - public long getCooldownInMillis() { - if (cooldownUntil < 0) { - return -1; + public @NotNull Duration getCooldown() { + if (cooldownUntil == null) { + return Duration.ZERO; } - long now = System.currentTimeMillis(); - return cooldownUntil > now ? cooldownUntil - now : 0; + Duration remainingCooldown = Duration.between(Instant.now(), cooldownUntil); + return remainingCooldown.isNegative() ? Duration.ZERO : remainingCooldown; } @Override @@ -53,8 +53,8 @@ public int getTimesCompletedInCooldown() { return isOnCooldown() ? this.timesCompletedInCooldown : timesCompleted > 0 ? 1 : 0; } - public void setCooldownUntil(final long newCompleted) { - this.cooldownUntil = newCompleted; + public void setCooldownUntil(@Nullable Instant newCooldown) { + this.cooldownUntil = newCooldown; this.timesCompletedInCooldown = 0; } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/challenge/ChallengeCompletionLogic.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/challenge/ChallengeCompletionLogic.java index e09316eb5..d82904e71 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/challenge/ChallengeCompletionLogic.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/challenge/ChallengeCompletionLogic.java @@ -4,17 +4,19 @@ import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; import com.google.common.cache.RemovalListener; -import com.google.common.cache.RemovalNotification; import dk.lockfuglsang.minecraft.file.FileUtil; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.YamlConfiguration; +import org.jetbrains.annotations.NotNull; import us.talabrek.ultimateskyblock.island.IslandInfo; import us.talabrek.ultimateskyblock.player.PlayerInfo; import us.talabrek.ultimateskyblock.uSkyBlock; import java.io.File; import java.io.IOException; +import java.time.Duration; +import java.time.Instant; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutionException; @@ -34,20 +36,15 @@ public ChallengeCompletionLogic(uSkyBlock plugin, FileConfiguration config) { this.plugin = plugin; storeOnIsland = config.getString("challengeSharing", "island").equalsIgnoreCase("island"); completionCache = CacheBuilder - .from(plugin.getConfig().getString("options.advanced.completionCache", "maximumSize=200,expireAfterWrite=15m,expireAfterAccess=10m")) - .removalListener(new RemovalListener>() { - @Override - public void onRemoval(RemovalNotification> removal) { - saveToFile(removal.getKey(), removal.getValue()); - } - }) - .build(new CacheLoader>() { - @Override - public Map load(String id) throws Exception { - return loadFromFile(id); - } + .from(plugin.getConfig().getString("options.advanced.completionCache", "maximumSize=200,expireAfterWrite=15m,expireAfterAccess=10m")) + .removalListener((RemovalListener>) removal -> saveToFile(removal.getKey(), removal.getValue())) + .build(new CacheLoader<>() { + @Override + public @NotNull Map load(@NotNull String id) { + return loadFromFile(id); } - ); + } + ); storageFolder = new File(plugin.getDataFolder(), "completion"); if (!storageFolder.exists() || !storageFolder.isDirectory()) { storageFolder.mkdirs(); @@ -70,7 +67,9 @@ private void saveToConfiguration(FileConfiguration configuration, Map loadFromConfiguration(ConfigurationSect plugin.getChallengeLogic().populateChallenges(challengeMap); if (root != null) { for (String challengeName : challengeMap.keySet()) { + long firstCompleted = root.getLong(challengeName + ".firstCompleted", 0); + Instant firstCompletedDuration = firstCompleted > 0 ? Instant.ofEpochMilli(firstCompleted) : null; challengeMap.put(challengeName, new ChallengeCompletion( - challengeName, - root.getLong(challengeName + ".firstCompleted", 0), - root.getInt(challengeName + ".timesCompleted", 0), - root.getInt(challengeName + ".timesCompletedSinceTimer", 0) + challengeName, + firstCompletedDuration, + root.getInt(challengeName + ".timesCompleted", 0), + root.getInt(challengeName + ".timesCompletedSinceTimer", 0) )); } } @@ -135,10 +136,10 @@ public Map getChallenges(PlayerInfo playerInfo) { } catch (ExecutionException e) { plugin.getLogger().log(Level.WARNING, "Error fetching challenge-completion for id " + id); } - if ((challengeMap == null || challengeMap.isEmpty())) { + if (challengeMap.isEmpty()) { // Fetch from the player-yml file challengeMap = loadFromConfiguration(playerInfo.getConfig().getConfigurationSection("player.challenges")); - if (challengeMap != null && !challengeMap.isEmpty()) { + if (!challengeMap.isEmpty()) { completionCache.put(id, challengeMap); } // Wipe it @@ -157,12 +158,12 @@ public void completeChallenge(PlayerInfo playerInfo, String challengeName) { if (challenges.containsKey(challengeName)) { ChallengeCompletion completion = challenges.get(challengeName); if (!completion.isOnCooldown()) { - long resetInMillis = uSkyBlock.getInstance().getChallengeLogic().getResetInMillis(challengeName); - if (resetInMillis >= 0) { - long now = System.currentTimeMillis(); - completion.setCooldownUntil(now + resetInMillis); + Duration resetDuration = plugin.getChallengeLogic().getResetDuration(challengeName); + if (resetDuration.isPositive()) { + Instant now = Instant.now(); + completion.setCooldownUntil(now.plus(resetDuration)); } else { - completion.setCooldownUntil(resetInMillis); + completion.setCooldownUntil(null); } } completion.addTimesCompleted(); @@ -173,7 +174,7 @@ public void resetChallenge(PlayerInfo playerInfo, String challenge) { Map challenges = getChallenges(playerInfo); if (challenges.containsKey(challenge)) { challenges.get(challenge).setTimesCompleted(0); - challenges.get(challenge).setCooldownUntil(0L); + challenges.get(challenge).setCooldownUntil(null); } } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/challenge/ChallengeDefaults.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/challenge/ChallengeDefaults.java index 0518670be..345200889 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/challenge/ChallengeDefaults.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/challenge/ChallengeDefaults.java @@ -1,10 +1,10 @@ package us.talabrek.ultimateskyblock.challenge; -/** - */ +import java.time.Duration; + public class ChallengeDefaults { - public final int resetInHours; - public final String displayItem = "160:5"; + public final Duration resetDuration; + public final String displayItem = "LIME_STAINED_GLASS_PANE"; public final boolean requiresPreviousRank; public final String repeatableColor; public final String finishedColor; @@ -16,10 +16,10 @@ public class ChallengeDefaults { public final boolean showLockedChallengeName; public final int repeatLimit; - ChallengeDefaults(int resetInHours, boolean requiresPreviousRank, String repeatableColor, String finishedColor, + ChallengeDefaults(Duration resetDuration, boolean requiresPreviousRank, String repeatableColor, String finishedColor, String challengeColor, int rankLeeway, boolean enableEconomyPlugin, boolean broadcastCompletion, int radius, boolean showLockedChallengeName, int repeatLimit) { - this.resetInHours = resetInHours; + this.resetDuration = resetDuration; this.requiresPreviousRank = requiresPreviousRank; this.repeatableColor = repeatableColor; this.finishedColor = finishedColor; diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/challenge/ChallengeFactory.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/challenge/ChallengeFactory.java index ba7e2172e..8d34a5bf3 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/challenge/ChallengeFactory.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/challenge/ChallengeFactory.java @@ -1,11 +1,15 @@ package us.talabrek.ultimateskyblock.challenge; +import dk.lockfuglsang.minecraft.util.BlockRequirement; +import dk.lockfuglsang.minecraft.util.ItemRequirement; +import dk.lockfuglsang.minecraft.util.ItemStackUtil; +import org.bukkit.Registry; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.entity.EntityType; import org.bukkit.inventory.ItemStack; -import dk.lockfuglsang.minecraft.util.ItemStackUtil; import us.talabrek.ultimateskyblock.util.MetaUtil; +import java.time.Duration; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; @@ -32,16 +36,17 @@ public static void setLogger(Logger log) { public static ChallengeDefaults createDefaults(ConfigurationSection section) { return new ChallengeDefaults( - section.getInt("defaultResetInHours", 144), - section.getBoolean("requiresPreviousRank", true), - normalize(section.getString("repeatableColor", "&a")), - normalize(section.getString("finishedColor", "&2")), - normalize(section.getString("challengeColor", "&e")), - section.getInt("rankLeeway", 1), - section.getBoolean("enableEconomyPlugin", true), - section.getBoolean("broadcastCompletion", true), - section.getInt("radius", 10), section.getBoolean("showLockedChallengeName", true), - section.getInt("repeatLimit", 0)); + Duration.ofHours(section.getLong("defaultResetInHours", 144L)), + section.getBoolean("requiresPreviousRank", true), + normalize(section.getString("repeatableColor", "&a")), + normalize(section.getString("finishedColor", "&2")), + normalize(section.getString("challengeColor", "&e")), + section.getInt("rankLeeway", 1), + section.getBoolean("enableEconomyPlugin", true), + section.getBoolean("broadcastCompletion", true), + section.getInt("radius", 10), + section.getBoolean("showLockedChallengeName", true), + section.getInt("repeatLimit", 0)); } public static Challenge createChallenge(Rank rank, ConfigurationSection section, ChallengeDefaults defaults) { @@ -51,13 +56,16 @@ public static Challenge createChallenge(Rank rank, ConfigurationSection section, } String displayName = section.getString("name", name); Challenge.Type type = Challenge.Type.from(section.getString("type", "onPlayer")); - List requiredItems = section.isList("requiredItems") ? section.getStringList("requiredItems") : Arrays.asList(section.getString("requiredItems", "").split(" ")); + List requiredItems = section.getStringList("requiredItems").stream() + .map(ItemStackUtil::createItemRequirement).toList(); + List requiredBlocks = section.getStringList("requiredBlocks").stream() + .map(ItemStackUtil::createBlockRequirement).toList(); List requiredEntities = createEntities(section.getStringList("requiredEntities")); - int resetInHours = section.getInt("resetInHours", rank.getResetInHours()); + Duration resetDuration = Duration.ofHours(section.getLong("resetInHours", rank.getResetDuration().toHours())); String description = section.getString("description"); ItemStack displayItem = createItemStack( - section.getString("displayItem", defaults.displayItem), - normalize(displayName), description); + section.getString("displayItem", defaults.displayItem), + normalize(displayName), description); ItemStack lockedItem = section.isString("lockedDisplayItem") ? createItemStack(section.getString("lockedDisplayItem", "BARRIER"), displayName, description) : null; boolean takeItems = section.getBoolean("takeItems", true); int radius = section.getInt("radius", 10); @@ -70,11 +78,12 @@ public static Challenge createChallenge(Rank rank, ConfigurationSection section, int offset = section.getInt("offset", 0); int repeatLimit = section.getInt("repeatLimit", 0); return new Challenge(name, displayName, description, type, - requiredItems, requiredEntities, requiredChallenges, section.getDouble("requiredLevel", 0d), rank, - resetInHours, displayItem, section.getString("tool", null), lockedItem, offset, takeItems, - radius, reward, repeatReward, repeatLimit); + requiredItems, requiredBlocks, requiredEntities, requiredChallenges, section.getDouble("requiredLevel", 0d), + rank, resetDuration, displayItem, section.getString("tool", null), lockedItem, offset, takeItems, + radius, reward, repeatReward, repeatLimit); } + @SuppressWarnings("removal") private static List createEntities(List requiredEntities) { List entities = new ArrayList<>(); for (String entityString : requiredEntities) { @@ -84,9 +93,9 @@ private static List createEntities(List requiredEntities) { String meta = m.group("meta"); String countStr = m.group("count"); int count = countStr != null ? Integer.parseInt(countStr, 10) : 1; - EntityType entityType = EntityType.fromName(type); - Map map = meta != null ? MetaUtil.createMap(meta.substring(1)) : new HashMap(); // skip the leading ':' - if (entityType != null && map != null) { + EntityType entityType = Registry.ENTITY_TYPE.match(type); + Map map = meta != null ? MetaUtil.createMap(meta.substring(1)) : new HashMap<>(); // skip the leading ':' + if (entityType != null) { entities.add(new EntityMatch(entityType, map, count)); } else { throw new IllegalArgumentException("Malformed requiredEntities: " + entityString); @@ -109,12 +118,12 @@ private static Reward createReward(ConfigurationSection section) { items.addAll(Arrays.asList(section.getString("items").split(" "))); } return new Reward( - section.getString("text", "\u00a74Unknown"), - ItemStackUtil.createItemsWithProbabilty(items), - section.getString("permission"), - section.getInt("currency", 0), - section.getInt("xp", 0), - section.getStringList("commands")); + section.getString("text", "\u00a74Unknown"), + ItemStackUtil.createItemsWithProbability(items), + section.getString("permission"), + section.getInt("currency", 0), + section.getInt("xp", 0), + section.getStringList("commands")); } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/challenge/ChallengeLogic.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/challenge/ChallengeLogic.java index 7ed34661c..1617393e0 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/challenge/ChallengeLogic.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/challenge/ChallengeLogic.java @@ -1,6 +1,9 @@ package us.talabrek.ultimateskyblock.challenge; -import dk.lockfuglsang.minecraft.nbt.NBTUtil; +import com.google.inject.Inject; +import com.google.inject.Singleton; +import dk.lockfuglsang.minecraft.file.FileUtil; +import dk.lockfuglsang.minecraft.util.BlockRequirement; import dk.lockfuglsang.minecraft.util.FormatUtil; import dk.lockfuglsang.minecraft.util.ItemStackUtil; import org.bukkit.Bukkit; @@ -15,20 +18,25 @@ import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemFlag; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.ItemMeta; +import org.jetbrains.annotations.NotNull; import us.talabrek.ultimateskyblock.api.event.MemberJoinedEvent; import us.talabrek.ultimateskyblock.block.BlockCollection; -import us.talabrek.ultimateskyblock.handler.VaultHandler; +import us.talabrek.ultimateskyblock.hook.HookManager; import us.talabrek.ultimateskyblock.island.IslandInfo; import us.talabrek.ultimateskyblock.player.Perk; +import us.talabrek.ultimateskyblock.player.PerkLogic; import us.talabrek.ultimateskyblock.player.PlayerInfo; import us.talabrek.ultimateskyblock.uSkyBlock; +import java.time.Duration; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.EnumMap; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; @@ -38,6 +46,7 @@ import java.util.Set; import java.util.UUID; import java.util.logging.Level; +import java.util.logging.Logger; import java.util.regex.Matcher; import static dk.lockfuglsang.minecraft.po.I18nUtil.tr; @@ -46,25 +55,36 @@ /** * The home of challenge business logic. */ +@Singleton public class ChallengeLogic implements Listener { - public static final long MS_MIN = 60 * 1000; - public static final long MS_HOUR = 60 * MS_MIN; - public static final long MS_DAY = 24 * MS_HOUR; public static final int COLS_PER_ROW = 9; public static final int ROWS_OF_RANKS = 5; public static final int CHALLENGE_PAGESIZE = ROWS_OF_RANKS * COLS_PER_ROW; + private final Logger logger; private final FileConfiguration config; private final uSkyBlock plugin; + private final PerkLogic perkLogic; + private final HookManager hookManager; + private final Map ranks; public final ChallengeDefaults defaults; public final ChallengeCompletionLogic completionLogic; private final ItemStack lockedItem; - private final Map lockedItemMap = new HashMap<>(); - - public ChallengeLogic(FileConfiguration config, uSkyBlock plugin) { - this.config = config; + private final Map lockedItemMap = new EnumMap<>(Challenge.Type.class); + + @Inject + public ChallengeLogic( + @NotNull Logger logger, + @NotNull uSkyBlock plugin, + @NotNull PerkLogic perkLogic, + @NotNull HookManager hookManager + ) { + this.logger = logger; + this.perkLogic = perkLogic; + this.hookManager = hookManager; + this.config = FileUtil.getYmlConfiguration("challenges.yml"); this.plugin = plugin; this.defaults = ChallengeFactory.createDefaults(config.getRoot()); ranks = ChallengeFactory.createRankMap(config.getConfigurationSection("ranks"), defaults); @@ -126,7 +146,7 @@ public List getAllChallengeNames() { } public List getChallengesForRank(String rank) { - return ranks.containsKey(rank) ? ranks.get(rank).getChallenges() : Collections.emptyList(); + return ranks.containsKey(rank) ? ranks.get(rank).getChallenges() : Collections.emptyList(); } public void completeChallenge(final Player player, String challengeName) { @@ -188,21 +208,6 @@ public Challenge getChallenge(String challengeName) { return null; } - public static int calcAmount(int amount, char op, int inc, int timesCompleted) { - switch (op) { - case '+': - return amount + inc * timesCompleted; - case '-': - return amount - inc * timesCompleted; // Why? - case '*': - case '^': - return amount * (int) Math.pow(inc, timesCompleted); // Oh, my god! Just do the time m8! - case '/': - return amount / (int) Math.pow(inc, timesCompleted); // Yay! Free stuff!!! - } - return amount; - } - public boolean tryComplete(final Player player, final String challenge, final String type) { if (type.equalsIgnoreCase("onPlayer")) { return tryCompleteOnPlayer(player, challenge); @@ -214,15 +219,22 @@ public boolean tryComplete(final Player player, final String challenge, final St return false; } + /** + * Try to complete a {@link Challenge} for the given {@link Player} where a minimal island level is a requirement. + * + * @param player Player to complete the challenge for. + * @param challenge Challenge to complete. + * @return True if the challenge was completed succesfully, false otherwise. + */ private boolean tryCompleteIslandLevel(Player player, Challenge challenge) { if (plugin.getIslandInfo(player).getLevel() >= challenge.getRequiredLevel()) { - giveReward(player, challenge.getName()); + giveReward(player, challenge); return true; } return false; } - private boolean islandContains(Player player, List itemStacks, int radius) { + private boolean islandContains(Player player, List itemStacks, int radius) { final Location l = player.getLocation(); final int px = l.getBlockX(); final int py = l.getBlockY(); @@ -245,12 +257,20 @@ private boolean islandContains(Player player, List itemStacks, int ra return true; } + /** + * Try to complete a {@link Challenge} on the island of the given {@link Player} where items or entities are + * required to be present on the island. + * + * @param player Player to complete the challenge for. + * @param challengeName Challenge to complete. + * @return True if the challenge was completed succesfully, false otherwise. + */ private boolean tryCompleteOnIsland(Player player, String challengeName) { Challenge challenge = getChallenge(challengeName); - List requiredItems = challenge.getRequiredItems(0); + List requiredBlocks = challenge.getRequiredBlocks(); int radius = challenge.getRadius(); - if (islandContains(player, requiredItems, radius) && hasEntitiesNear(player, challenge.getRequiredEntities(), radius)) { - giveReward(player, challengeName); + if (islandContains(player, requiredBlocks, radius) && hasEntitiesNear(player, challenge.getRequiredEntities(), radius)) { + giveReward(player, challenge); return true; } return false; @@ -258,7 +278,7 @@ private boolean tryCompleteOnIsland(Player player, String challengeName) { private boolean hasEntitiesNear(Player player, List requiredEntities, int radius) { Map countMap = new LinkedHashMap<>(); - Map> matchMap = new HashMap<>(); + Map> matchMap = new EnumMap<>(EntityType.class); for (EntityMatch match : requiredEntities) { countMap.put(match, match.getCount()); Set set = matchMap.get(match.getType()); @@ -289,14 +309,15 @@ private boolean hasEntitiesNear(Player player, List requiredEntitie StringBuilder sb = new StringBuilder(); for (Map.Entry entry : countMap.entrySet()) { sb.append("\u00a7e - "); - sb.append(" \u00a74" + entry.getValue() + " \u00a7ex"); - sb.append(" \u00a7b" + entry.getKey().getDisplayName() + "\n"); + sb.append(" \u00a74").append(entry.getValue()).append(" \u00a7ex"); + sb.append(" \u00a7b").append(entry.getKey().getDisplayName()).append("\n"); } player.sendMessage(tr("\u00a7eStill the following entities short:\n{0}", sb.toString()).split("\n")); } return countMap.isEmpty(); } + // TODO: This logic is duplicated in SignLogic.tryComplete. Refactor to a common method. private boolean tryCompleteOnPlayer(Player player, String challengeName) { Challenge challenge = getChallenge(challengeName); PlayerInfo playerInfo = plugin.getPlayerInfo(player); @@ -304,17 +325,23 @@ private boolean tryCompleteOnPlayer(Player player, String challengeName) { if (challenge != null && completion != null) { StringBuilder sb = new StringBuilder(); boolean hasAll = true; - List requiredItems = challenge.getRequiredItems(completion.getTimesCompletedInCooldown()); - for (ItemStack required : requiredItems) { - String name = VaultHandler.getItemName(required); - if (!player.getInventory().containsAtLeast(required, required.getAmount())) { - sb.append(tr(" \u00a74{0} \u00a7b{1}", (required.getAmount() - getCountOf(player.getInventory(), required)), name)); + Map requiredItems = challenge.getRequiredItems(completion.getTimesCompletedInCooldown()); + for (Map.Entry required : requiredItems.entrySet()) { + ItemStack requiredType = required.getKey(); + int requiredAmount = required.getValue(); + String name = ItemStackUtil.getItemName(requiredType); + if (!player.getInventory().containsAtLeast(requiredType, requiredAmount)) { + sb.append(tr(" \u00a74{0} \u00a7b{1}", (requiredAmount - getCountOf(player.getInventory(), requiredType)), name)); hasAll = false; } } if (hasAll) { if (challenge.isTakeItems()) { - player.getInventory().removeItem(requiredItems.toArray(new ItemStack[requiredItems.size()])); + ItemStack[] itemsToRemove = ItemStackUtil.asValidItemStacksWithAmount(requiredItems); + var leftovers = player.getInventory().removeItem(itemsToRemove); + if (!leftovers.isEmpty()) { + throw new IllegalStateException("Player " + player.getName() + " had items left over after completing challenge " + challengeName + ": " + leftovers); + } } giveReward(player, challenge); return true; @@ -326,17 +353,9 @@ private boolean tryCompleteOnPlayer(Player player, String challengeName) { } public int getCountOf(Inventory inventory, ItemStack required) { - int count = 0; - for (ItemStack invItem : inventory.all(required.getType()).values()) { - if (invItem.getDurability() == required.getDurability() && NBTUtil.getNBTTag(invItem).equals(NBTUtil.getNBTTag(required))) { - count += invItem.getAmount(); - } - } - return count; - } - - public boolean giveReward(final Player player, final String challengeName) { - return giveReward(player, getChallenge(challengeName)); + return Arrays.stream(inventory.getContents()) + .filter(item -> item != null && item.isSimilar(required)) + .mapToInt(ItemStack::getAmount).sum(); } private boolean giveReward(Player player, Challenge challenge) { @@ -350,12 +369,6 @@ private boolean giveReward(Player player, Challenge challenge) { } else { reward = challenge.getRepeatReward(); } - float rewBonus = 1; - if (defaults.enableEconomyPlugin && VaultHandler.hasEcon()) { - Perk perk = plugin.getPerkLogic().getPerk(player); - rewBonus += perk.getRewBonus(); - VaultHandler.depositPlayer(player, reward.getCurrencyReward() * rewBonus); - } player.giveExp(reward.getXpReward()); boolean wasBroadcast = false; if (defaults.broadcastCompletion && isFirstCompletion) { @@ -364,8 +377,19 @@ private boolean giveReward(Player player, Challenge challenge) { } player.sendMessage(tr("\u00a7eItem reward(s): \u00a7f{0}", reward.getRewardText())); player.sendMessage(tr("\u00a7eExp reward: \u00a7f{0,number,#.#}", reward.getXpReward())); - if (defaults.enableEconomyPlugin && VaultHandler.hasEcon()) { - player.sendMessage(tr("\u00a7eCurrency reward: \u00a7f{0,number,###.##} {1} \u00a7a ({2,number,##.##})%", reward.getCurrencyReward() * rewBonus, VaultHandler.getEcon().currencyNamePlural(), (rewBonus - 1.0) * 100.0)); + if (defaults.enableEconomyPlugin) { + double rewBonus = 1; + Perk perk = perkLogic.getPerk(player); + rewBonus +=perk.getRewBonus(); + double currencyReward = reward.getCurrencyReward() * rewBonus; + double percentage = (rewBonus - 1.0) * 100.0; + + hookManager.getEconomyHook().ifPresent((hook) -> { + hook.depositPlayer(player, currencyReward); + + player.sendMessage(tr("\u00a7eCurrency reward: \u00a7f{0,number,###.##} {1} \u00a7a ({2,number,##.##})%", + currencyReward, hook.getCurrenyName(), percentage)); + }); } if (reward.getPermissionReward() != null) { List perms = Arrays.asList(reward.getPermissionReward().trim().split(" ")); @@ -398,12 +422,12 @@ private boolean giveReward(Player player, Challenge challenge) { command = command.replaceAll("\\{challengeName\\}", Matcher.quoteReplacement(challenge.getDisplayName())); plugin.execCommand(player, command, true); } - playerInfo.completeChallenge(challengeName, wasBroadcast); + playerInfo.completeChallenge(challenge, wasBroadcast); return true; } - public long getResetInMillis(String challenge) { - return getChallenge(challenge).getResetInHours() * MS_HOUR; + public Duration getResetDuration(String challenge) { + return getChallenge(challenge).getResetDuration(); } public ItemStack getItemStack(PlayerInfo playerInfo, String challengeName) { @@ -418,7 +442,8 @@ public ItemStack getItemStack(PlayerInfo playerInfo, String challengeName) { lores.add(tr("\u00a74\u00a7lYou can't repeat this challenge.")); } if (completion.getTimesCompleted() > 0) { - meta.addEnchant(Enchantment.LOYALTY, 0, true); + meta.addEnchant(Enchantment.LOYALTY, 1, true); + meta.addItemFlags(ItemFlag.HIDE_ENCHANTS); } meta.setLore(lores); currentChallengeItem.setItemMeta(meta); @@ -430,7 +455,7 @@ public void populateChallenges(Map challengeMap) { for (Challenge challenge : rank.getChallenges()) { String key = challenge.getName().toLowerCase(); if (!challengeMap.containsKey(key)) { - challengeMap.put(key, new ChallengeCompletion(key, 0L, 0, 0)); + challengeMap.put(key, new ChallengeCompletion(key, null, 0, 0)); } } } @@ -456,24 +481,37 @@ public void populateChallengeRank(Inventory menu, PlayerInfo pi, int page, boole private List getRanksForPage(int page, List ranksOnPage) { int rowsToSkip = (page - 1) * ROWS_OF_RANKS; - for (Iterator it = ranksOnPage.iterator(); it.hasNext(); ) { - Rank rank = it.next(); - int rowsInRank = getRows(rank); - if (rowsToSkip <= 0 || ((rowsToSkip - rowsInRank) < 0)) { + List allRanks = new ArrayList<>(ranksOnPage); + + int i = 1; + for (Iterator it = ranksOnPage.iterator(); it.hasNext(); i++) { + it.next(); + int rowsInRanks = calculateRows(allRanks.subList(0, i)); + if (rowsToSkip <= 0 || ((rowsToSkip - rowsInRanks) < 0)) { return ranksOnPage; } - rowsToSkip -= rowsInRank; it.remove(); } return ranksOnPage; } private int calculateRows(List ranksOnPage) { - int row = 0; + int totalRows = 0; + int previousRowsOnPage = 0; + int currentRows; + for (Rank rank : ranksOnPage) { - row += getRows(rank); + currentRows = getRows(rank); + totalRows += currentRows; + + if (previousRowsOnPage < 5 && (currentRows + previousRowsOnPage) > 5) { + totalRows = totalRows + (5 - previousRowsOnPage); + previousRowsOnPage = currentRows; + } else { + previousRowsOnPage = previousRowsOnPage + currentRows; + } } - return row; + return totalRows; } private int getRows(Rank rank) { @@ -524,10 +562,8 @@ public int populateChallengeRank(Inventory menu, final Rank rank, int location, } if (locked != null) { currentChallengeItem.setType(locked.getType()); - currentChallengeItem.setDurability(locked.getDurability()); } else if (lockedItem != null) { currentChallengeItem.setType(lockedItem.getType()); - currentChallengeItem.setDurability(lockedItem.getDurability()); } } else { lores = currentChallengeItem.getItemMeta().getLore(); @@ -544,7 +580,7 @@ public int populateChallengeRank(Inventory menu, final Rank rank, int location, } menu.setItem(location, currentChallengeItem); } catch (Exception e) { - plugin.getLogger().log(Level.SEVERE, "Invalid challenge " + challenge, e); + logger.log(Level.SEVERE, "Invalid challenge " + challenge, e); } } return location; @@ -615,10 +651,9 @@ public boolean isIslandSharing() { @EventHandler public void onMemberJoinedEvent(MemberJoinedEvent e) { - if (!completionLogic.isIslandSharing() || !(e.getPlayerInfo() instanceof PlayerInfo)) { + if (!completionLogic.isIslandSharing() || !(e.getPlayerInfo() instanceof PlayerInfo playerInfo)) { return; } - PlayerInfo playerInfo = (PlayerInfo) e.getPlayerInfo(); Map completions = completionLogic.getIslandChallenges(e.getIslandInfo().getName()); List permissions = new ArrayList<>(); for (Map.Entry entry : completions.entrySet()) { diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/challenge/EntityMatch.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/challenge/EntityMatch.java index f2eada7d0..d86c19651 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/challenge/EntityMatch.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/challenge/EntityMatch.java @@ -1,8 +1,14 @@ package us.talabrek.ultimateskyblock.challenge; +import com.google.gson.Gson; +import org.apache.commons.text.WordUtils; +import org.bukkit.DyeColor; import org.bukkit.entity.Entity; import org.bukkit.entity.EntityType; -import org.json.simple.JSONObject; +import org.bukkit.material.Colorable; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import us.talabrek.ultimateskyblock.uSkyBlock; import us.talabrek.ultimateskyblock.util.EntityUtil; import java.lang.reflect.Field; @@ -15,35 +21,17 @@ * Data object holding values for matching an entity against a challenge. */ public class EntityMatch { - /** - * @see @link http://minecraft.gamepedia.com/Data_values#Wool.2C_Stained_Clay.2C_Stained_Glass_and_Carpet - */ - private static final byte[] DV_2_COLOR_MAP = { - 0xf, // 0 -> white - 0x6, // 1 -> orange (gold) - 0xd, // 2 -> magenta (light purple) - 0x9, // 3 -> light blue - 0xe, // 4 -> yellow - 0xa, // 5 -> lime - 0xc, // 6 -> pink (red) - 0x8, // 7 -> gray (dark gray) - 0x7, // 8 -> light gray (gray) - 0xb, // 9 -> cyan (aqua) - 0x5, // 10 -> purple (dark purple) - 0x1, // 11 -> blue (dark blue) - 0x6, // 12 -> brown (no brown! we re-use gold) - 0x2, // 13 -> green (dark green) - 0x4, // 14 -> red (dark red) - 0x0 // 15 -> black - }; + private static final Gson gson = new Gson(); + private final uSkyBlock server = uSkyBlock.getInstance(); + private static final String[] COLOR_KEYS = {"Color", "color"}; private final EntityType type; - private final Map meta; + private final Map meta; private final int count; public EntityMatch(EntityType type, Map meta, int count) { this.type = type; - this.meta = meta != null ? meta : new HashMap(); + this.meta = meta != null ? meta : new HashMap<>(); this.count = count; } @@ -55,21 +43,34 @@ public int getCount() { return count; } - public boolean matches(Entity entity) { - if (entity != null && entity.getType() == type) { - for (String key : meta.keySet()) { - if (!matchFieldGetter(entity, key, meta.get(key))) { - return false; + /** + * Try to match the given {@link Entity} to this EntityMatch configuration. + * @param entity Entity to match. + * @return True if matches, false otherwise. + */ + public boolean matches(@Nullable Entity entity) { + if (entity == null || entity.getType() != type) { + return false; + } + + for (String key : meta.keySet()) { + if (key.equalsIgnoreCase("color")) { + if (entity instanceof Colorable) { + return ((Colorable) entity).getColor() == getColor(meta.get(key)); } + return true; + } + + if (!matchFieldGetter(entity, key, meta.get(key))) { + return false; } - return true; } - return false; + return true; } private boolean matchFieldGetter(Entity entity, String key, Object value) { try { - Method method = entity.getClass().getMethod("get" + key, null); + Method method = entity.getClass().getMethod("get" + key); Object entityValue = method.invoke(entity); return matchValues(entityValue, value); } catch (InvocationTargetException | IllegalAccessException e) { @@ -83,7 +84,7 @@ private boolean matchFieldGetter(Entity entity, String key, Object value) { private boolean matchField(Entity entity, String key, Object value) { try { Field field = entity.getClass().getDeclaredField(key); - boolean wasAccessible = field.isAccessible(); + boolean wasAccessible = field.canAccess(this); if (!wasAccessible) { field.setAccessible(true); } @@ -101,50 +102,102 @@ private boolean matchField(Entity entity, String key, Object value) { private boolean matchValues(Object entityValue, Object value) { if (value instanceof Number && entityValue instanceof Enum) { - return ((Number) value).intValue() == ((Enum) entityValue).ordinal(); + return ((Number) value).intValue() == ((Enum) entityValue).ordinal(); } else if (value instanceof String && entityValue instanceof Enum) { - return ((String) value).equalsIgnoreCase(((Enum) entityValue).name()); + return ((String) value).equalsIgnoreCase(((Enum) entityValue).name()); } return ("" + entityValue).equalsIgnoreCase("" + value); } @Override public String toString() { - return type.name() + (meta.isEmpty() ? "" : ":" + JSONObject.toJSONString(meta)); + return type.name() + (meta.isEmpty() ? "" : ":" + gson.toJson(meta)); } public String getDisplayName() { StringBuilder sb = new StringBuilder(); - String name = EntityUtil.getEntityDisplayName(type); - String color = getColorCode(meta); - sb.append(color); - sb.append(name); - Map extra = new HashMap<>(meta); + Map extra = new HashMap<>(meta); for (String key : COLOR_KEYS) { - extra.remove(key); + if (meta.containsKey(key)) { + String color = WordUtils.capitalizeFully(getColor(meta.get(key)).toString().replace("_", " ")); + sb.append(color).append(" "); + extra.remove(key); + } } + + String name = EntityUtil.getEntityDisplayName(type); + sb.append(name); + if (!extra.isEmpty()) { - sb.append(":").append(JSONObject.toJSONString(extra)); + sb.append(":").append(gson.toJson(extra)); } return sb.toString(); } - private String getColorCode(Map meta) { - // TODO: 30/01/2016 - R4zorax: Support more entities? - for (String key : COLOR_KEYS) { - if (meta.containsKey(key)) { - try { - int colorcode = Integer.parseInt("" + meta.get(key)); - return dataValueToFormattingCode(colorcode); - } catch (NumberFormatException e) { - // ignore - } - } + /** + * Converts the given String to the corresponding {@link DyeColor}. + * Defaults to {@link DyeColor#WHITE} on invalid or NULL input. + * @param input DyeColor enum value. + * @return Corresponding DyeColor, defaults to WHITE on invalid or NULL input. + */ + private @NotNull DyeColor getColor(@Nullable String input) { + try { + return DyeColor.valueOf(input); + } catch (IllegalArgumentException ex) { + server.getLogger().warning("Invalid DyeColor value: " + input); + server.getLogger().warning("See https://hub.spigotmc.org/javadocs/spigot/org/bukkit/DyeColor.html#enum-constant-summary for valid colors."); + } + + return DyeColor.WHITE; + } + + /** + * Converts the given legacy integer value to the corresponsing {@link DyeColor}. + * Defaults to {@link DyeColor#WHITE} on invalid or NULL input. + * @param input Legacy DyeColor integer value. + * @return Corresponding DyeColor, defaults to WHITE on invalid or NULL input. + * @deprecated To be used for legacy challenge configs only, use {@link EntityMatch#getColor(String)}. + */ + @Deprecated(since = "2.11") + private @NotNull DyeColor getColor(@Nullable Number input) { + if (input == null) { + return DyeColor.WHITE; } - return ""; + + return switch (input.intValue()) { + case 0 -> DyeColor.WHITE; + case 1 -> DyeColor.ORANGE; + case 2 -> DyeColor.MAGENTA; + case 3 -> DyeColor.LIGHT_BLUE; + case 4 -> DyeColor.YELLOW; + case 5 -> DyeColor.LIME; + case 6 -> DyeColor.PINK; + case 7 -> DyeColor.GRAY; + case 8 -> DyeColor.LIGHT_GRAY; + case 9 -> DyeColor.CYAN; + case 10 -> DyeColor.PURPLE; + case 11 -> DyeColor.BLUE; + case 12 -> DyeColor.BROWN; + case 13 -> DyeColor.GREEN; + case 14 -> DyeColor.RED; + case 15 -> DyeColor.BLACK; + default -> DyeColor.WHITE; + }; } - private String dataValueToFormattingCode(int colorcode) { - return "\u00a7" + Integer.toHexString(DV_2_COLOR_MAP[colorcode & 0xf]); + /** + * Convenience method to translate a meta String or Number to the corresponding {@link DyeColor}. + * See {@link EntityMatch#getColor(String)} for more info. + * @param input Meta String or Number. + * @return Corresponding DyeColor, defaults to WHITE. + */ + private @NotNull DyeColor getColor(@Nullable Object input) { + if (input instanceof String) { + return getColor((String) input); + } else if (input instanceof Number) { + return getColor((Number) input); + } + + return DyeColor.WHITE; } } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/challenge/Rank.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/challenge/Rank.java index b11e2e011..154e85547 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/challenge/Rank.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/challenge/Rank.java @@ -1,12 +1,13 @@ package us.talabrek.ultimateskyblock.challenge; +import dk.lockfuglsang.minecraft.util.FormatUtil; +import dk.lockfuglsang.minecraft.util.ItemStackUtil; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.inventory.ItemStack; import us.talabrek.ultimateskyblock.player.PlayerInfo; -import dk.lockfuglsang.minecraft.util.FormatUtil; -import dk.lockfuglsang.minecraft.util.ItemStackUtil; import us.talabrek.ultimateskyblock.uSkyBlock; +import java.time.Duration; import java.util.ArrayList; import java.util.List; @@ -110,7 +111,7 @@ private int getLeeway(PlayerInfo playerInfo) { return leeway; } - public int getResetInHours() { - return config.getInt("resetInHours", defaults.resetInHours); + public Duration getResetDuration() { + return Duration.ofHours(config.getLong("resetInHours", defaults.resetDuration.toHours())); } } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/challenge/Reward.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/challenge/Reward.java index 62f7280db..8ba5bfe2f 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/challenge/Reward.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/challenge/Reward.java @@ -1,7 +1,7 @@ package us.talabrek.ultimateskyblock.challenge; -import org.bukkit.inventory.ItemStack; import dk.lockfuglsang.minecraft.util.ItemStackUtil; +import org.bukkit.inventory.ItemStack; import java.util.ArrayList; import java.util.List; @@ -11,7 +11,7 @@ * A reward record */ public class Reward { - private static final Random RND = new Random(System.currentTimeMillis()); + private static final Random RND = new Random(); private final List itemReward; private final String permissionReward; @@ -32,8 +32,8 @@ public Reward(String rewardText, List itemReward, public List getItemReward() { List copy = new ArrayList<>(); for (ItemStackUtil.ItemProbability e : itemReward) { - if (RND.nextDouble() < e.getProbability()) { - copy.add(e.getItem().clone()); + if (RND.nextDouble() < e.probability()) { + copy.add(e.item().clone()); } } return copy; @@ -62,13 +62,13 @@ public List getCommands() { @Override public String toString() { return "Reward{" + - "itemReward=" + itemReward + - ", permissionReward='" + permissionReward + '\'' + - ", currencyReward=" + currencyReward + - ", xpReward=" + xpReward + - ", rewardText='" + rewardText + '\'' + - ", commands=" + commands + - '}'; + "itemReward=" + itemReward + + ", permissionReward='" + permissionReward + '\'' + + ", currencyReward=" + currencyReward + + ", xpReward=" + xpReward + + ", rewardText='" + rewardText + '\'' + + ", commands=" + commands + + '}'; } } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/chat/ChatEvents.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/chat/ChatEvents.java index ea5d129fd..a659cbde1 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/chat/ChatEvents.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/chat/ChatEvents.java @@ -1,10 +1,13 @@ package us.talabrek.ultimateskyblock.chat; +import com.google.inject.Inject; +import com.google.inject.Singleton; import org.bukkit.Server; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; import org.bukkit.event.player.AsyncPlayerChatEvent; +import org.jetbrains.annotations.NotNull; import us.talabrek.ultimateskyblock.api.event.IslandChatEvent; import us.talabrek.ultimateskyblock.uSkyBlock; @@ -12,11 +15,13 @@ * @see us.talabrek.ultimateskyblock.api.event.IslandChatEvent * @see IslandChatCommand */ +@Singleton public class ChatEvents implements Listener { private final ChatLogic logic; private final uSkyBlock plugin; - public ChatEvents(ChatLogic logic, uSkyBlock plugin) { + @Inject + public ChatEvents(@NotNull ChatLogic logic, @NotNull uSkyBlock plugin) { this.logic = logic; this.plugin = plugin; } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/chat/ChatLogic.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/chat/ChatLogic.java index 790d8f9ee..08af01c35 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/chat/ChatLogic.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/chat/ChatLogic.java @@ -1,65 +1,103 @@ package us.talabrek.ultimateskyblock.chat; +import com.google.inject.Inject; +import com.google.inject.Singleton; import dk.lockfuglsang.minecraft.po.I18nUtil; +import dk.lockfuglsang.minecraft.util.FormatUtil; import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import us.talabrek.ultimateskyblock.PluginConfig; import us.talabrek.ultimateskyblock.api.IslandInfo; -import us.talabrek.ultimateskyblock.api.event.IslandChatEvent; import us.talabrek.ultimateskyblock.handler.WorldGuardHandler; import us.talabrek.ultimateskyblock.handler.placeholder.PlaceholderHandler; import us.talabrek.ultimateskyblock.uSkyBlock; -import dk.lockfuglsang.minecraft.util.FormatUtil; +import us.talabrek.ultimateskyblock.world.WorldManager; import java.util.Arrays; import java.util.Collections; +import java.util.EnumMap; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.UUID; import java.util.regex.Matcher; +import static us.talabrek.ultimateskyblock.api.event.IslandChatEvent.Type; + /** * The primary logic of uSkyBlocks chat-handling */ +@Singleton public class ChatLogic { - private final List ALONE = Arrays.asList( - I18nUtil.tr("But you are ALLLLLLL ALOOOOONE!"), - I18nUtil.tr("But you are Yelling in the wind!"), - I18nUtil.tr("But your fantasy friends are gone!"), - I18nUtil.tr("But you are Talking to your self!") + private static final List ALONE_MESSAGES = Arrays.asList( + I18nUtil.tr("But you are ALLLLLLL ALOOOOONE!"), + I18nUtil.tr("But you are Yelling in the wind!"), + I18nUtil.tr("But your fantasy friends are gone!"), + I18nUtil.tr("But you are Talking to your self!") ); private final uSkyBlock plugin; - private final Map formats = new HashMap<>(); - private final Map toggled = new HashMap<>(); + private final WorldManager worldManager; + private final PlaceholderHandler placeholderHandler; + private final Map formats = new EnumMap<>(Type.class); + private final Map toggled = new HashMap<>(); - public ChatLogic(uSkyBlock plugin) { + @Inject + public ChatLogic( + @NotNull uSkyBlock plugin, + @NotNull PluginConfig config, + @NotNull WorldManager worldManager, + @NotNull PlaceholderHandler placeholderHandler + ) { this.plugin = plugin; - formats.put(IslandChatEvent.Type.PARTY, plugin.getConfig().getString("options.party.chat-format", "&9PARTY &r{DISPLAYNAME} &f>&d {MESSAGE}")); - formats.put(IslandChatEvent.Type.ISLAND, plugin.getConfig().getString("options.island.chat-format", "&9SKY &r{DISPLAYNAME} &f>&b {MESSAGE}")); + this.worldManager = worldManager; + this.placeholderHandler = placeholderHandler; + formats.put(Type.PARTY, + config.getYamlConfig().getString("options.party.chat-format", "&9PARTY &r{DISPLAYNAME} &f>&d {MESSAGE}")); + formats.put(Type.ISLAND, + config.getYamlConfig().getString("options.island.chat-format", "&9SKY &r{DISPLAYNAME} &f>&b {MESSAGE}")); } - public List getRecipients(Player player, IslandChatEvent.Type chatType) { - if (chatType == IslandChatEvent.Type.PARTY) { - IslandInfo islandInfo = plugin.getIslandInfo(player); - return islandInfo != null ? islandInfo.getOnlineMembers() : Collections.singletonList(player); - } else if (chatType == IslandChatEvent.Type.ISLAND) { - if (plugin.getWorldManager().isSkyWorld(player.getWorld())) { - return WorldGuardHandler.getPlayersInRegion(plugin.getWorldManager().getWorld(), - WorldGuardHandler.getIslandRegionAt(player.getLocation())); + /** + * Gets a {@link List} containing {@link Player}'s with all the recipients that should receive the given message + * {@link Type} from the sending {@link Player}. Returns an empty list when there are no recipients. + * + * @param sender Player sending the message. + * @param chatType Message type that the player is sending. + * @return List of all recipients, or an empty list if there are none. + */ + public @NotNull List getRecipients(Player sender, Type chatType) { + if (chatType == Type.PARTY) { + IslandInfo islandInfo = plugin.getIslandInfo(sender); + return islandInfo != null ? islandInfo.getOnlineMembers() : Collections.singletonList(sender); + } else if (chatType == Type.ISLAND) { + if (worldManager.isSkyWorld(sender.getWorld())) { + return WorldGuardHandler.getPlayersInRegion(worldManager.getWorld(), + WorldGuardHandler.getIslandRegionAt(sender.getLocation())); } return Collections.emptyList(); } return Collections.emptyList(); } - public void sendMessage(Player player, IslandChatEvent.Type type, String message) { + /** + * Sends the given message to all online partymembers or island visitors on the given {@link Player}'s island, + * depending on the given {@link Type}. + * + * @param sender Player sending the message. + * @param type Message type to send. + * @param message Message to send. + */ + public void sendMessage(Player sender, Type type, String message) { String format = getFormat(type); format = FormatUtil.normalize(format); - format = format.replaceAll("\\{DISPLAYNAME\\}", Matcher.quoteReplacement(player.getDisplayName())); - String msg = format.replaceAll("\\{MESSAGE\\}", Matcher.quoteReplacement(message)); - msg = PlaceholderHandler.replacePlaceholders(player, msg); - List onlineMembers = getRecipients(player, type); + format = format.replaceAll("\\{DISPLAYNAME}", Matcher.quoteReplacement(sender.getDisplayName())); + String msg = format.replaceAll("\\{MESSAGE}", Matcher.quoteReplacement(message)); + msg = placeholderHandler.replacePlaceholders(sender, msg); + List onlineMembers = getRecipients(sender, type); if (onlineMembers.size() <= 1) { - player.sendMessage(I18nUtil.tr("\u00a7cSorry! {0}", "\u00a79" + ALONE.get(((int) Math.round(Math.random() * ALONE.size())) % ALONE.size()))); + sender.sendMessage(I18nUtil.tr("\u00a7cSorry! {0}", "\u00a79" + + ALONE_MESSAGES.get(((int) Math.round(Math.random() * ALONE_MESSAGES.size())) % ALONE_MESSAGES.size()))); } else { for (Player member : onlineMembers) { member.sendMessage(msg); @@ -67,18 +105,25 @@ public void sendMessage(Player player, IslandChatEvent.Type type, String message } } - public String getFormat(IslandChatEvent.Type type) { + /** + * Gets the message format for the given {@link Type}. + * + * @param type Island chat type to lookup. + * @return Message format. + */ + public @NotNull String getFormat(Type type) { return formats.get(type); } /** - * Toggles the chat-type on or off, returns true if it was toggled on. - * @param player - * @param type - * @return + * Toggle the {@link Type} on or off for the given {@link Player}, returns true if it is toggled on. + * + * @param player Player to toggle the chat type for. + * @param type Chat type to toggle. + * @return True if it is toggled on, false otherwise. */ - public synchronized boolean toggle(Player player, IslandChatEvent.Type type) { - IslandChatEvent.Type oldType = toggled.get(player.getUniqueId()); + public synchronized boolean toggle(Player player, Type type) { + Type oldType = toggled.get(player.getUniqueId()); if (oldType == type) { toggled.remove(player.getUniqueId()); return false; @@ -89,11 +134,12 @@ public synchronized boolean toggle(Player player, IslandChatEvent.Type type) { } /** - * Returns the current toggle, or null if none exists. - * @param player - * @return + * Gets the current {@link Type} toggle for the given {@link Player}, or null if none exists. + * + * @param player Player to lookup. + * @return The current Type toggle, or null if none exists. */ - public synchronized IslandChatEvent.Type getToggle(Player player) { + public synchronized @Nullable Type getToggle(Player player) { return toggled.get(player.getUniqueId()); } } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/chat/IslandChatCommand.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/chat/IslandChatCommand.java index 26398b42b..5d7adf308 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/chat/IslandChatCommand.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/chat/IslandChatCommand.java @@ -53,7 +53,7 @@ public boolean execute(CommandSender commandSender, String alias, Map inviteMap = new HashMap<>(); private final Map> waitingInvites = new HashMap<>(); private final uSkyBlock plugin; + private final Scheduler scheduler; - public InviteHandler(uSkyBlock plugin) { + @Inject + public InviteHandler(@NotNull uSkyBlock plugin, @NotNull Scheduler scheduler) { this.plugin = plugin; + this.scheduler = scheduler; } private synchronized void invite(Player player, final IslandInfo island, Player otherPlayer) { @@ -51,46 +58,37 @@ private synchronized void invite(Player player, final IslandInfo island, Player } if (oPi.getHasIsland()) { us.talabrek.ultimateskyblock.api.IslandInfo oIsland = plugin.getIslandInfo(oPi); - if (oIsland.isParty() && oIsland.isLeader(otherPlayer)) { - player.sendMessage(tr("\u00a74That player is already leader on another island.")); - otherPlayer.sendMessage(tr("\u00a7e{0}\u00a7e tried to invite you, but you are already in a party.", player.getDisplayName())); + if (oIsland.isParty() && !oIsland.isLeader(otherPlayer)) { + player.sendMessage(tr("§4That player is already member on another island. ")); + otherPlayer.sendMessage(tr("§e{0}§e tried to invite you, but you are already in a party." + + "To leave your current party, use: /island leave.", player.getDisplayName())); return; } } final UUID uniqueId = otherPlayer.getUniqueId(); invites.put(uniqueId, otherPlayer.getName()); - final Invite invite = new Invite(island.getName(), uniqueId, player.getDisplayName()); + player.sendMessage(tr("\u00a7aInvite sent to {0}", otherPlayer.getDisplayName())); + otherPlayer.sendMessage(tr("{0}\u00a7e has invited you to join their island!", player.getDisplayName()), + tr("\u00a7f/island [accept/reject]\u00a7e to accept or reject the invite."), + tr("\u00a74WARNING: You will lose your current island if you accept!")); + Duration timeout = Duration.ofSeconds(plugin.getConfig().getInt("options.party.invite-timeout", 30)); + BukkitTask timeoutTask = scheduler.async(() -> uninvite(island, uniqueId), timeout); + final Invite invite = new Invite(island.getName(), player.getDisplayName(), timeoutTask); inviteMap.put(uniqueId, invite); waitingInvites.put(island.getName(), invites); - player.sendMessage(tr("\u00a7aInvite sent to {0}", otherPlayer.getDisplayName())); - otherPlayer.sendMessage(new String[]{ - tr("{0}\u00a7e has invited you to join their island!", player.getDisplayName()), - tr("\u00a7f/island [accept/reject]\u00a7e to accept or reject the invite."), - tr("\u00a74WARNING: You will lose your current island if you accept!") - }); - long timeout = TimeUtil.secondsAsMillis(plugin.getConfig().getInt("options.party.invite-timeout", 30)); - BukkitTask timeoutTask = plugin.async(new Runnable() { - @Override - public void run() { - uninvite(island, uniqueId); - } - }, timeout); - invite.setTimeoutTask(timeoutTask); island.sendMessageToIslandGroup(true, I18nUtil.marktr("{0}\u00a7d invited {1}"), player.getDisplayName(), otherPlayer.getDisplayName()); } private synchronized boolean reject(Player player) { Invite invite = inviteMap.remove(player.getUniqueId()); if (invite != null) { - if (invite.getTimeoutTask() != null) { - invite.getTimeoutTask().cancel(); - } - IslandInfo island = plugin.getIslandInfo(invite.getIslandName()); + invite.timeoutTask().cancel(); + IslandInfo island = plugin.getIslandInfo(invite.islandName()); if (island != null) { island.sendMessageToIslandGroup(true, marktr("{0}\u00a7e has rejected the invitation."), player.getDisplayName()); } - if (waitingInvites.containsKey(invite.getIslandName())) { - waitingInvites.get(invite.getIslandName()).remove(player.getUniqueId()); + if (waitingInvites.containsKey(invite.islandName())) { + waitingInvites.get(invite.islandName()).remove(player.getUniqueId()); } return true; } @@ -106,28 +104,23 @@ private synchronized boolean accept(final Player player) { } Invite invite = inviteMap.remove(uuid); if (invite != null) { - if (invite.getTimeoutTask() != null) { - invite.getTimeoutTask().cancel(); - } + invite.timeoutTask().cancel(); PlayerInfo pi = plugin.getPlayerInfo(player); - final IslandInfo island = plugin.getIslandInfo(invite.getIslandName()); + final IslandInfo island = plugin.getIslandInfo(invite.islandName()); boolean deleteOldIsland = false; if (pi.getHasIsland() && pi.getIslandLocation() != null) { String islandName = WorldGuardHandler.getIslandNameAt(pi.getIslandLocation()); deleteOldIsland = !island.getName().equals(islandName); } - Map uuids = waitingInvites.get(invite.getIslandName()); + Map uuids = waitingInvites.get(invite.islandName()); if (uuids != null) { uuids.remove(uuid); } - Runnable joinIsland = new Runnable() { - @Override - public void run() { - player.sendMessage(tr("\u00a7aYou have joined an island! Use /island party to see the other members.")); - addPlayerToParty(player, island); - plugin.getTeleportLogic().homeTeleport(player, true); - plugin.clearPlayerInventory(player); - } + Runnable joinIsland = () -> { + player.sendMessage(tr("\u00a7aYou have joined an island! Use /island party to see the other members.")); + addPlayerToParty(player, island); + plugin.getTeleportLogic().homeTeleport(player, true); + plugin.clearPlayerInventory(player); }; if (deleteOldIsland) { plugin.deletePlayerIsland(player.getName(), joinIsland); @@ -173,10 +166,10 @@ private synchronized boolean uninvite(IslandInfo islandInfo, UUID uuid) { if (invites != null && invites.contains(uuid)) { Invite invite = inviteMap.remove(uuid); invites.remove(uuid); - if (invite != null && invite.getTimeoutTask() != null) { - invite.getTimeoutTask().cancel(); + if (invite != null) { + invite.timeoutTask().cancel(); } - islandInfo.sendMessageToIslandGroup(true, marktr("\u00a7eInvitation for {0}\u00a7e has timedout or been cancelled."), invite.getDisplayName()); + islandInfo.sendMessageToIslandGroup(true, marktr("\u00a7eInvitation for {0}\u00a7e has timedout or been cancelled."), invite.displayName()); Player player = Bukkit.getPlayer(uuid); if (player != null && player.isOnline()) { player.sendMessage(tr("\u00a7eInvitation for {0}''s island has timedout or been cancelled.", islandInfo.getLeader())); @@ -215,43 +208,6 @@ public void onRejectEvent(RejectEvent e) { } } - @SuppressWarnings("UnusedDeclaration") - private static class Invite { - private final long time; - private final String islandName; - private final UUID uniqueId; - private final String displayName; - private BukkitTask timeoutTask; - - public Invite(String islandName, UUID uniqueId, String displayName) { - this.islandName = islandName; - this.uniqueId = uniqueId; - this.displayName = displayName; - time = System.currentTimeMillis(); - } - - public long getTime() { - return time; - } - - public String getIslandName() { - return islandName; - } - - public UUID getUniqueId() { - return uniqueId; - } - - public String getDisplayName() { - return displayName; - } - - public BukkitTask getTimeoutTask() { - return timeoutTask; - } - - public void setTimeoutTask(BukkitTask timeoutTask) { - this.timeoutTask = timeoutTask; - } + private record Invite(String islandName, String displayName, BukkitTask timeoutTask) { } } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/IslandCommand.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/IslandCommand.java index 0159e6bca..f15f4a027 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/IslandCommand.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/IslandCommand.java @@ -1,9 +1,12 @@ package us.talabrek.ultimateskyblock.command; +import com.google.inject.Inject; +import com.google.inject.Singleton; import dk.lockfuglsang.minecraft.command.BaseCommandExecutor; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; import us.talabrek.ultimateskyblock.Settings; import us.talabrek.ultimateskyblock.command.completion.AllPlayerTabCompleter; import us.talabrek.ultimateskyblock.command.completion.BiomeTabCompleter; @@ -11,31 +14,7 @@ import us.talabrek.ultimateskyblock.command.completion.OnlinePlayerTabCompleter; import us.talabrek.ultimateskyblock.command.completion.PermissionTabCompleter; import us.talabrek.ultimateskyblock.command.completion.SchematicTabCompleter; -import us.talabrek.ultimateskyblock.command.island.AcceptRejectCommand; -import us.talabrek.ultimateskyblock.command.island.AutoCommand; -import us.talabrek.ultimateskyblock.command.island.BanCommand; -import us.talabrek.ultimateskyblock.command.island.BiomeCommand; -import us.talabrek.ultimateskyblock.command.island.CreateCommand; -import us.talabrek.ultimateskyblock.command.island.HomeCommand; -import us.talabrek.ultimateskyblock.command.island.InfoCommand; -import us.talabrek.ultimateskyblock.command.island.InviteCommand; -import us.talabrek.ultimateskyblock.command.island.KickCommand; -import us.talabrek.ultimateskyblock.command.island.LeaveCommand; -import us.talabrek.ultimateskyblock.command.island.LevelCommand; -import us.talabrek.ultimateskyblock.command.island.LockUnlockCommand; -import us.talabrek.ultimateskyblock.command.island.LogCommand; -import us.talabrek.ultimateskyblock.command.island.MakeLeaderCommand; -import us.talabrek.ultimateskyblock.command.island.MobLimitCommand; -import us.talabrek.ultimateskyblock.command.island.PartyCommand; -import us.talabrek.ultimateskyblock.command.island.PermCommand; -import us.talabrek.ultimateskyblock.command.island.RestartCommand; -import us.talabrek.ultimateskyblock.command.island.SetHomeCommand; -import us.talabrek.ultimateskyblock.command.island.SetWarpCommand; -import us.talabrek.ultimateskyblock.command.island.SpawnCommand; -import us.talabrek.ultimateskyblock.command.island.ToggleWarp; -import us.talabrek.ultimateskyblock.command.island.TopCommand; -import us.talabrek.ultimateskyblock.command.island.TrustCommand; -import us.talabrek.ultimateskyblock.command.island.WarpCommand; +import us.talabrek.ultimateskyblock.command.island.*; import us.talabrek.ultimateskyblock.menu.SkyBlockMenu; import us.talabrek.ultimateskyblock.uSkyBlock; @@ -45,60 +24,100 @@ /** * The main /island command */ +@Singleton public class IslandCommand extends BaseCommandExecutor { private final uSkyBlock plugin; private final SkyBlockMenu menu; - public IslandCommand(uSkyBlock plugin, SkyBlockMenu menu) { + @Inject + public IslandCommand( + @NotNull uSkyBlock plugin, + @NotNull SkyBlockMenu menu, + + @NotNull OnlinePlayerTabCompleter onlinePlayerTabCompleter, + @NotNull AllPlayerTabCompleter allPlayerTabCompleter, + @NotNull BiomeTabCompleter biomeTabCompleter, + @NotNull MemberTabCompleter memberTabCompleter, + @NotNull SchematicTabCompleter schematicTabCompleter, + @NotNull PermissionTabCompleter permissionTabCompleter, + + @NotNull HomeCommand homeCommand, + @NotNull LevelCommand levelCommand, + @NotNull InfoCommand infoCommand, + @NotNull InviteCommand inviteCommand, + @NotNull AcceptRejectCommand acceptRejectCommand, + @NotNull LeaveCommand leaveCommand, + @NotNull KickCommand kickCommand, + @NotNull PartyCommand partyCommand, + @NotNull MakeLeaderCommand makeLeaderCommand, + @NotNull SpawnCommand spawnCommand, + @NotNull TrustCommand trustCommand, + @NotNull MobLimitCommand mobLimitCommand, + @NotNull AutoCommand autoCommand, + @NotNull PermCommand permCommand, + @NotNull RestartCommand restartCommand, + @NotNull LogCommand logCommand, + @NotNull CreateCommand createCommand, + @NotNull SetHomeCommand setHomeCommand, + @NotNull SetWarpCommand setWarpCommand, + @NotNull WarpCommand warpCommand, + @NotNull ToggleWarp toggleWarpCommand, + @NotNull BanCommand banCommand, + @NotNull LockUnlockCommand lockUnlockCommand, + @NotNull TopCommand topCommand, + @NotNull BiomeCommand biomeCommand, + @NotNull GreetingCommand greetingCommand, + @NotNull FarewellCommand farewellCommand, + @NotNull BuyHopperLimitCommand buyHopperLimitCommand + ) { super("island|is", "usb.island.create", marktr("general island command")); this.plugin = plugin; this.menu = menu; + addFeaturePermission("usb.mod.bypasscooldowns", tr("allows user to bypass cooldowns")); addFeaturePermission("usb.mod.bypassprotection", tr("allows user to bypass visitor-protections")); addFeaturePermission("usb.mod.bypassteleport", tr("allows user to bypass teleport-delay")); addFeaturePermission("usb.island.signs.use", tr("allows user to use [usb] signs")); addFeaturePermission("usb.island.signs.place", tr("allows user to place [usb] signs")); - InviteHandler inviteHandler = new InviteHandler(plugin); - plugin.getServer().getPluginManager().registerEvents(inviteHandler, plugin); - OnlinePlayerTabCompleter onlinePlayerTabCompleter = new OnlinePlayerTabCompleter(); - AllPlayerTabCompleter playerTabCompleter = new AllPlayerTabCompleter(onlinePlayerTabCompleter); - addTab("island", playerTabCompleter); - addTab("player", playerTabCompleter); + addTab("island", allPlayerTabCompleter); + addTab("player", allPlayerTabCompleter); addTab("oplayer", onlinePlayerTabCompleter); - addTab("biome", new BiomeTabCompleter()); - addTab("member", new MemberTabCompleter(plugin)); - addTab("schematic", new SchematicTabCompleter(plugin)); - addTab("perm", new PermissionTabCompleter(plugin)); - add(new RestartCommand(plugin)); - add(new LogCommand(plugin, menu)); - CreateCommand createCommand = new CreateCommand(plugin); + addTab("biome", biomeTabCompleter); + addTab("member", memberTabCompleter); + addTab("schematic", schematicTabCompleter); + addTab("perm", permissionTabCompleter); + + add(restartCommand); + add(logCommand); add(createCommand); - add(new SetHomeCommand(plugin)); - HomeCommand homeCommand = new HomeCommand(plugin); + add(setHomeCommand); add(homeCommand); - add(new SetWarpCommand(plugin)); - add(new WarpCommand(plugin)); - add(new ToggleWarp(plugin)); - add(new BanCommand(plugin)); - add(new LockUnlockCommand(plugin)); + add(setWarpCommand); + add(warpCommand); + add(toggleWarpCommand); + add(banCommand); + add(lockUnlockCommand); if (Settings.island_useTopTen) { - add(new TopCommand(plugin)); + add(topCommand); } - add(new BiomeCommand(plugin, menu)); - add(new LevelCommand(plugin)); - add(new InfoCommand(plugin)); - add(new InviteCommand(plugin, inviteHandler)); - add(new AcceptRejectCommand(plugin)); - add(new LeaveCommand(plugin)); - add(new KickCommand(plugin)); - add(new PartyCommand(plugin, menu, inviteHandler)); - add(new MakeLeaderCommand(plugin)); - add(new SpawnCommand(plugin)); - add(new TrustCommand(plugin)); - add(new MobLimitCommand(plugin)); - add(new AutoCommand(plugin, createCommand, homeCommand)); - add(new PermCommand(plugin)); + add(biomeCommand); + add(levelCommand); + add(infoCommand); + add(inviteCommand); + add(acceptRejectCommand); + add(leaveCommand); + add(kickCommand); + add(partyCommand); + add(makeLeaderCommand); + add(spawnCommand); + add(trustCommand); + add(mobLimitCommand); + add(autoCommand); + add(permCommand); + add(greetingCommand); + add(farewellCommand); + add(buyHopperLimitCommand); } @Override @@ -106,8 +125,7 @@ public boolean onCommand(CommandSender sender, Command command, String alias, St if (!plugin.isRequirementsMet(sender, this, args)) { return true; } - if (sender instanceof Player) { - Player player = (Player) sender; + if (sender instanceof Player player) { if (args.length == 0) { player.openInventory(menu.displayIslandGUI(player)); return true; diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/AdminChallengeCommand.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/AdminChallengeCommand.java index 234973e12..7617c5caa 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/AdminChallengeCommand.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/AdminChallengeCommand.java @@ -1,11 +1,13 @@ package us.talabrek.ultimateskyblock.command.admin; +import com.google.inject.Inject; import dk.lockfuglsang.minecraft.command.AbstractCommand; import dk.lockfuglsang.minecraft.command.CompositeCommand; import dk.lockfuglsang.minecraft.po.I18nUtil; import dk.lockfuglsang.minecraft.util.FormatUtil; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; import us.talabrek.ultimateskyblock.challenge.Challenge; import us.talabrek.ultimateskyblock.challenge.ChallengeCompletion; import us.talabrek.ultimateskyblock.player.PlayerInfo; @@ -24,7 +26,8 @@ public class AdminChallengeCommand extends CompositeCommand { private final uSkyBlock plugin; - public AdminChallengeCommand(final uSkyBlock plugin) { + @Inject + public AdminChallengeCommand(@NotNull uSkyBlock plugin) { super("challenge|ch", "usb.mod.challenges", "player", marktr("Manage challenges for a player")); this.plugin = plugin; add(new ChallengeCommand("complete", null, "completes the challenge for the player") { @@ -91,11 +94,12 @@ public boolean execute(CommandSender commandSender, String alias, Map 0) { sender.sendMessage(I18nUtil.tr("\u00a74Challenge {0} has already been completed", challengeName)); } else { - playerInfo.completeChallenge(challengeName, true); + playerInfo.completeChallenge(challenge, true); playerInfo.save(); sender.sendMessage(I18nUtil.tr("\u00a7eChallenge {0} has been completed for {1}", challengeName, playerInfo.getPlayerName())); } @@ -113,7 +117,7 @@ public boolean execute(CommandSender sender, String alias, Map d return super.execute(sender, alias, data, args); } - private abstract class ChallengeCommand extends AbstractCommand { + private abstract static class ChallengeCommand extends AbstractCommand { public ChallengeCommand(String name, String permission, String description) { super(name, permission, "challenge", description); } @@ -147,7 +151,7 @@ public RankCommand(String name, String permission, String description) { public boolean execute(CommandSender sender, String alias, Map data, String... args) { PlayerInfo playerInfo = (PlayerInfo) data.get("playerInfo"); if (playerInfo != null && args.length > 0) { - String rankName = FormatUtil.join(Arrays.asList(args), " "); + String rankName = String.join(" ", args); List challenges = plugin.getChallengeLogic().getChallengesForRank(rankName); if (challenges == null || challenges.isEmpty()) { sender.sendMessage(I18nUtil.tr("\u00a74No rank named {0} was found!", rankName)); diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/AdminIslandCommand.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/AdminIslandCommand.java index 9dca4346a..a5870dc4d 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/AdminIslandCommand.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/AdminIslandCommand.java @@ -1,11 +1,13 @@ package us.talabrek.ultimateskyblock.command.admin; +import com.google.inject.Inject; import dk.lockfuglsang.minecraft.command.AbstractCommand; import dk.lockfuglsang.minecraft.command.CompositeCommand; import org.bukkit.Bukkit; import org.bukkit.block.Biome; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; import us.talabrek.ultimateskyblock.handler.ConfirmHandler; import us.talabrek.ultimateskyblock.handler.WorldGuardHandler; import us.talabrek.ultimateskyblock.island.IslandInfo; @@ -23,7 +25,8 @@ public class AdminIslandCommand extends CompositeCommand { private final uSkyBlock plugin; - public AdminIslandCommand(final uSkyBlock plugin, final ConfirmHandler confirmHandler) { + @Inject + public AdminIslandCommand(@NotNull uSkyBlock plugin, @NotNull ConfirmHandler confirmHandler) { super("island|is", "usb.admin.island", marktr("manage islands")); this.plugin = plugin; add(new AbstractIslandInfoCommand("protect", "usb.admin.protect", marktr("protects the island")) { @@ -72,7 +75,6 @@ protected void doExecute(CommandSender sender, PlayerInfo playerInfo, IslandInfo public boolean execute(CommandSender sender, String alias, Map data, String... args) { IslandInfo islandInfo = null; if (args.length == 2) { - //noinspection deprecation islandInfo = plugin.getIslandInfo(Bukkit.getPlayer(args[1])); } else if (args.length == 1 && sender instanceof Player) { String islandName = WorldGuardHandler.getIslandNameAt(((Player) sender).getLocation()); @@ -109,7 +111,11 @@ public boolean execute(CommandSender sender, String alias, Map d sender.sendMessage(tr("\u00a74That player has no island.")); return false; } - setBiome(sender, playerInfo, plugin.getIslandInfo(playerInfo), args[1]); + Biome biome = plugin.getBiome(args[0]); + if (biome == null) { + return false; + } + setBiome(sender, playerInfo, plugin.getIslandInfo(playerInfo), biome); return true; } else if (args.length == 1 && sender instanceof Player) { Biome biome = plugin.getBiome(args[0]); @@ -122,7 +128,7 @@ public boolean execute(CommandSender sender, String alias, Map d sender.sendMessage(tr("\u00a74No valid island at your location")); return false; } - setBiome(sender, islandInfo, biome.name()); + setBiome(sender, islandInfo, biome); return true; } return false; @@ -133,8 +139,7 @@ public boolean execute(CommandSender sender, String alias, Map d public boolean execute(CommandSender sender, String alias, Map data, String... args) { String cmd = "/usb island purge"; IslandInfo islandInfo = null; - if (args.length == 0 && sender instanceof Player) { - Player player = (Player) sender; + if (args.length == 0 && sender instanceof Player player) { String islandName = WorldGuardHandler.getIslandNameAt(player.getLocation()); islandInfo = plugin.getIslandInfo(islandName); } else if (args.length == 1) { @@ -187,29 +192,21 @@ private void removePlayerFromIsland(CommandSender sender, PlayerInfo playerInfo, playerInfo.save(); } - private void setBiome(CommandSender sender, IslandInfo islandInfo, String biome) { - if (uSkyBlock.getInstance().setBiome(islandInfo.getIslandLocation(), biome)) { - islandInfo.setBiome(biome); - sender.sendMessage(tr("\u00a7eChanged biome of {0}s island to {1}.", islandInfo.getLeader(), biome)); - } else { - islandInfo.setBiome("OCEAN"); - sender.sendMessage(tr("\u00a7eChanged biome of {0}s island to OCEAN.", islandInfo.getLeader())); - } + private void setBiome(CommandSender sender, IslandInfo islandInfo, Biome biome) { + uSkyBlock.getInstance().setBiome(islandInfo.getIslandLocation(), biome); + islandInfo.setBiome(biome); + sender.sendMessage(tr("\u00a7eChanged biome of {0}s island to {1}.", islandInfo.getLeader(), biome)); sender.sendMessage(tr("\u00a7aYou may need to go to spawn, or relog, to see the changes.")); } - private void setBiome(CommandSender sender, PlayerInfo playerInfo, IslandInfo islandInfo, String biome) { + private void setBiome(CommandSender sender, PlayerInfo playerInfo, IslandInfo islandInfo, Biome biome) { if (playerInfo == null || !playerInfo.getHasIsland()) { sender.sendMessage(tr("\u00a74That player has no island.")); return; } - if (uSkyBlock.getInstance().setBiome(playerInfo.getIslandLocation(), biome)) { - islandInfo.setBiome(biome); - sender.sendMessage(tr("\u00a7e{0} has had their biome changed to {1}.", playerInfo.getPlayerName(), biome)); - } else { - islandInfo.setBiome("OCEAN"); - sender.sendMessage(tr("\u00a7e{0} has had their biome changed to OCEAN.", playerInfo.getPlayerName())); - } + uSkyBlock.getInstance().setBiome(playerInfo.getIslandLocation(), biome); + islandInfo.setBiome(biome); + sender.sendMessage(tr("\u00a7e{0} has had their biome changed to {1}.", playerInfo.getPlayerName(), biome)); sender.sendMessage(tr("\u00a7aYou may need to go to spawn, or relog, to see the changes.")); } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/ChunkCommand.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/ChunkCommand.java index 0212522f3..f5e58b101 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/ChunkCommand.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/ChunkCommand.java @@ -1,11 +1,14 @@ package us.talabrek.ultimateskyblock.command.admin; +import com.google.inject.Inject; import dk.lockfuglsang.minecraft.command.AbstractCommand; import dk.lockfuglsang.minecraft.command.CompositeCommand; import org.bukkit.Chunk; import org.bukkit.World; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import us.talabrek.ultimateskyblock.uSkyBlock; import java.util.Map; @@ -13,16 +16,16 @@ import static dk.lockfuglsang.minecraft.po.I18nUtil.tr; public class ChunkCommand extends CompositeCommand { - public ChunkCommand() { + + @Inject + public ChunkCommand(@NotNull uSkyBlock plugin) { super("chunk", "usb.admin.chunk", marktr("various chunk commands")); + add(new RequireChunkCommand("regen", marktr("regenerate current chunk")) { @Override void doChunkCommand(Player player, Chunk chunk) { - if (chunk.getWorld().regenerateChunk(chunk.getX(), chunk.getZ())) { - player.sendMessage(tr("successfully regenerated chunk at {0},{1}", chunk.getX(), chunk.getZ())); - } else { - player.sendMessage(tr("\u00a74FAILED!\u00a7e could not regenerate chunk at {0},{1}", chunk.getX(), chunk.getZ())); - } + plugin.getWorldManager().getChunkRegenerator(chunk.getWorld()).regenerateChunk(chunk); + player.sendMessage(tr("successfully regenerated chunk at {0},{1}", chunk.getX(), chunk.getZ())); } }); add(new RequireChunkCommand("unload", marktr("unload current chunk")) { @@ -44,7 +47,7 @@ void doChunkCommand(Player player, Chunk chunk) { }); } - public abstract class RequireChunkCommand extends AbstractCommand { + public abstract static class RequireChunkCommand extends AbstractCommand { public RequireChunkCommand(String name, String description) { super(name, null, "?x ?z ?r", description); } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/ConfigCommand.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/ConfigCommand.java deleted file mode 100644 index a4133cb9b..000000000 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/ConfigCommand.java +++ /dev/null @@ -1,93 +0,0 @@ -package us.talabrek.ultimateskyblock.command.admin; - -import dk.lockfuglsang.minecraft.command.AbstractCommand; -import dk.lockfuglsang.minecraft.command.CompositeCommand; -import dk.lockfuglsang.minecraft.command.completion.AbstractTabCompleter; -import dk.lockfuglsang.minecraft.file.FileUtil; -import dk.lockfuglsang.minecraft.yml.YmlConfiguration; -import org.bukkit.command.CommandSender; -import org.bukkit.command.TabCompleter; -import org.bukkit.entity.Player; -import us.talabrek.ultimateskyblock.uSkyBlock; - -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - -import static dk.lockfuglsang.minecraft.po.I18nUtil.marktr; -import static dk.lockfuglsang.minecraft.po.I18nUtil.tr; - -/** - * Command for showing the config-gui. - */ -public class ConfigCommand extends CompositeCommand { - private final uSkyBlock plugin; - public static final List CONFIGS = Arrays.asList("config", "levelConfig", "challenges", "signs"); - - public ConfigCommand(uSkyBlock plugin) { - super("config|c", "usb.admin.config", "?config", marktr("open GUI for config")); - add(new AbstractCommand("search", marktr("searches config for a specific key")) { - @Override - public boolean execute(CommandSender commandSender, String alias, Map map, String... args) { - if (args.length > 0) { - YmlConfiguration config = FileUtil.getYmlConfiguration(map.get("config") + ".yml"); - String searchTerms = String.join(" ", args); - List results = config.getKeys(true).stream() - .filter(f -> f.contains(searchTerms)) - .map(m -> tr("\u00a79{0}\u00a78: \u00a7e{1}", m.replace(searchTerms, tr("\u00a7c{0}\u00a79", searchTerms)), ConfigCommand.toString(config, m))) - .collect(Collectors.toList()); - results.add(0, tr("Found the following matching {0}:", searchTerms)); - commandSender.sendMessage(results.toArray(new String[0])); - return true; - } - return false; - } - }); - this.plugin = plugin; - } - - private static String toString(YmlConfiguration config, String key) { - if (config.isConfigurationSection(key)) { - return tr("\u00a7a
"); - } else if (config.isDouble(key)) { - return tr("\u00a73{0,number,#.##}", config.getDouble(key)); - } else if (config.isBoolean(key)) { - return tr("\u00a72{0}", config.getBoolean(key)); - } else if (config.isList(key)) { - return "\n - " + String.join("\n - ", config.getStringList(key)); - } - return config.get(key).toString(); - } - - @Override - public TabCompleter getTabCompleter() { - return new AbstractTabCompleter() { - @Override - protected List getTabList(CommandSender commandSender, String term) { - return CONFIGS; - } - }; - } - - @Override - public boolean execute(CommandSender sender, String alias, Map data, String... args) { - if (args.length > 0 && CONFIGS.contains(args[0])) { - data.put("config", args[0]); - } - if (sender instanceof Player && args.length <= 1) { - String configName = "config"; - if (args.length > 0) { - if (CONFIGS.contains(args[0])) { - configName = args[0]; - } else { - sender.sendMessage(tr("\u00a7eInvalid configuration name")); - return false; - } - } - plugin.getConfigMenu().showMenu((Player) sender, configName + ".yml", 1); - return true; - } - return super.execute(sender, alias, data, args); - } -} diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/CooldownCommand.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/CooldownCommand.java index 3f92c8bf7..e5b64efbb 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/CooldownCommand.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/CooldownCommand.java @@ -1,15 +1,19 @@ package us.talabrek.ultimateskyblock.command.admin; +import com.google.inject.Inject; import dk.lockfuglsang.minecraft.command.AbstractCommand; import dk.lockfuglsang.minecraft.command.CompositeCommand; import dk.lockfuglsang.minecraft.command.completion.AbstractTabCompleter; +import dk.lockfuglsang.minecraft.util.TimeUtil; import org.bukkit.Bukkit; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; import us.talabrek.ultimateskyblock.Settings; import us.talabrek.ultimateskyblock.uSkyBlock; -import dk.lockfuglsang.minecraft.util.TimeUtil; +import java.time.Duration; +import java.time.Instant; import java.util.Arrays; import java.util.List; import java.util.Map; @@ -21,7 +25,9 @@ * Manages player cooldowns */ public class CooldownCommand extends CompositeCommand { - public CooldownCommand(final uSkyBlock plugin) { + + @Inject + public CooldownCommand(@NotNull uSkyBlock plugin) { super("cooldown|cd", "usb.admin.cooldown", marktr("Controls player-cooldowns")); add(new AbstractCommand("clear|c", null, "player command", marktr("clears the cooldown on a command (* = all)")) { @Override @@ -29,17 +35,16 @@ public boolean execute(CommandSender sender, String alias, Map d if (args.length < 2) { return false; } - //noinspection deprecation - Player p = Bukkit.getPlayer(args[0]); - if (p == null || !p.isOnline()) { + Player player = Bukkit.getPlayer(args[0]); + if (player == null || !player.isOnline()) { sender.sendMessage(tr("\u00a7eThe player is not currently online")); return false; } if ("restart|biome".contains(args[1])) { - if (plugin.getCooldownHandler().clearCooldown(p, args[1])) { - sender.sendMessage(tr("Cleared cooldown on {0} for {1}", args[1], p.getDisplayName())); + if (plugin.getCooldownHandler().clearCooldown(player, args[1])) { + sender.sendMessage(tr("Cleared cooldown on {0} for {1}", args[1], player.getDisplayName())); } else { - sender.sendMessage(tr("No active cooldown on {0} for {1} detected!", args[1], p.getDisplayName())); + sender.sendMessage(tr("No active cooldown on {0} for {1} detected!", args[1], player.getDisplayName())); } return true; } else { @@ -54,16 +59,15 @@ public boolean execute(CommandSender sender, String alias, Map d if (args.length < 2) { return false; } - //noinspection deprecation - Player p = Bukkit.getPlayer(args[0]); - if (p == null || !p.isOnline()) { + Player player = Bukkit.getPlayer(args[0]); + if (player == null || !player.isOnline()) { sender.sendMessage(tr("\u00a7eThe player is not currently online")); return false; } if ("restart|biome".contains(args[1])) { - int cooldown = getCooldown(args[1]); - plugin.getCooldownHandler().resetCooldown(p, args[1], cooldown); - sender.sendMessage(tr("\u00a7eReset cooldown on {0} for {1}\u00a7e to {2} seconds", args[1], p.getDisplayName(), cooldown)); + Duration cooldown = getCooldown(args[1]); + plugin.getCooldownHandler().resetCooldown(player, args[1], cooldown); + sender.sendMessage(tr("\u00a7eReset cooldown on {0} for {1}\u00a7e to {2} seconds", args[1], player.getDisplayName(), cooldown)); return true; } else { sender.sendMessage(tr("Invalid command supplied, only restart and biome supported!")); @@ -77,19 +81,20 @@ public boolean execute(CommandSender sender, String alias, Map d if (args.length < 1) { return false; } - //noinspection deprecation - Player p = Bukkit.getPlayer(args[0]); - if (p == null || !p.isOnline()) { + Player player = Bukkit.getPlayer(args[0]); + if (player == null || !player.isOnline()) { sender.sendMessage(tr("\u00a7eThe player is not currently online")); return false; } - Map map = plugin.getCooldownHandler().getCooldowns(p.getUniqueId()); + Map map = plugin.getCooldownHandler().getCooldowns(player.getUniqueId()); StringBuilder sb = new StringBuilder(); if (map != null && !map.isEmpty()) { - long now = System.currentTimeMillis(); - sb.append(tr("\u00a7eCmd Cooldown") + "\n"); - for (String cmd : map.keySet()) { - sb.append(tr("\u00a7a{0} \u00a7c{1}", cmd, TimeUtil.millisAsString(map.get(cmd) - now)) + "\n"); + Instant now = Instant.now(); + sb.append(tr("\u00a7eCmd Cooldown")).append("\n"); + for (var entry : map.entrySet()) { + String cmd = entry.getKey(); + Duration remainingCooldown = Duration.between(now, entry.getValue()); + sb.append(tr("\u00a7a{0} \u00a7c{1}", cmd, TimeUtil.durationAsString(remainingCooldown))).append("\n"); } } else { sb.append(tr("\u00a7eNo active cooldowns for \u00a79{0}\u00a7e found.", data.get("playerName"))); @@ -106,12 +111,11 @@ protected List getTabList(CommandSender commandSender, String term) { }); } - private int getCooldown(String cmd) { - switch (cmd) { - case "restart": return Settings.general_cooldownRestart; - case "biome": return Settings.general_biomeChange; - } - return 0; + private Duration getCooldown(String cmd) { + return switch (cmd) { + case "restart" -> Settings.general_cooldownRestart; + case "biome" -> Settings.general_biomeChange; + default -> Duration.ZERO; + }; } - } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/DebugCommand.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/DebugCommand.java index 80ec135dd..5bd0dae67 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/DebugCommand.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/DebugCommand.java @@ -1,11 +1,14 @@ package us.talabrek.ultimateskyblock.command.admin; +import com.google.inject.Inject; import dk.lockfuglsang.minecraft.command.AbstractCommand; import dk.lockfuglsang.minecraft.command.CompositeCommand; import dk.lockfuglsang.minecraft.po.I18nUtil; import org.bukkit.command.CommandSender; +import org.jetbrains.annotations.NotNull; import us.talabrek.ultimateskyblock.uSkyBlock; import dk.lockfuglsang.minecraft.util.FormatUtil; +import us.talabrek.ultimateskyblock.util.PluginInfo; import java.io.File; import java.io.IOException; @@ -26,11 +29,17 @@ * Debug control. */ public class DebugCommand extends CompositeCommand { + + private final PluginInfo pluginInfo; + public static final Logger log = Logger.getLogger("us.talabrek.ultimateskyblock"); private static Handler logHandler = null; - public DebugCommand(final uSkyBlock plugin) { + @Inject + public DebugCommand(@NotNull uSkyBlock plugin, @NotNull PluginInfo pluginInfo) { super("debug", "usb.admin.debug", marktr("control debugging")); + this.pluginInfo = pluginInfo; + add(new AbstractCommand("setlevel", null, "level", marktr("set debug-level")) { @Override public boolean execute(CommandSender sender, String alias, Map data, String... args) { @@ -72,7 +81,7 @@ public boolean execute(CommandSender sender, String alias, Map d } } - public static void setLogLevel(CommandSender sender, String arg) { + public void setLogLevel(CommandSender sender, String arg) { try { Level level = Level.parse(arg.toUpperCase()); log.setLevel(level); @@ -96,7 +105,7 @@ public static void disableLogging(CommandSender sender) { logHandler = null; } - public static void enableLogging(CommandSender sender, uSkyBlock plugin) { + public void enableLogging(CommandSender sender, uSkyBlock plugin) { if (logHandler != null) { log.removeHandler(logHandler); plugin.getLogger().removeHandler(logHandler); @@ -104,13 +113,13 @@ public static void enableLogging(CommandSender sender, uSkyBlock plugin) { File logFolder = new File(plugin.getDataFolder(), "logs"); logFolder.mkdirs(); try { - String logFile = logFolder.toString() + File.separator + "uskyblock.%u.log"; + String logFile = logFolder + File.separator + "uskyblock.%u.log"; logHandler = new FileHandler(logFile, true); logHandler.setFormatter(new SingleLineFormatter()); log.addHandler(logHandler); plugin.getLogger().addHandler(logHandler); Level level = log.getLevel() != null ? log.getLevel() : Level.FINER; - log.log(level, FormatUtil.stripFormatting(plugin.getVersionInfo(true))); + log.log(level, FormatUtil.stripFormatting(pluginInfo.getVersionInfo(true))); sender.sendMessage("\u00a7eLogging to " + logFile); } catch (IOException e) { log.log(Level.WARNING, "Unable to enable logging", e); diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/FlatlandFixCommand.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/FlatlandFixCommand.java index 17724fd14..845ce4689 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/FlatlandFixCommand.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/FlatlandFixCommand.java @@ -1,15 +1,18 @@ package us.talabrek.ultimateskyblock.command.admin; +import com.google.inject.Inject; import dk.lockfuglsang.minecraft.command.AbstractCommand; import dk.lockfuglsang.minecraft.po.I18nUtil; import org.bukkit.Location; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; import us.talabrek.ultimateskyblock.handler.WorldGuardHandler; import us.talabrek.ultimateskyblock.player.PlayerInfo; import us.talabrek.ultimateskyblock.uSkyBlock; import us.talabrek.ultimateskyblock.util.LocationUtil; +import java.time.Duration; import java.util.Map; import static dk.lockfuglsang.minecraft.po.I18nUtil.marktr; @@ -21,7 +24,8 @@ public class FlatlandFixCommand extends AbstractCommand { private final uSkyBlock plugin; - public FlatlandFixCommand(uSkyBlock plugin) { + @Inject + public FlatlandFixCommand(@NotNull uSkyBlock plugin) { super("fix-flatland", "usb.admin.remove", "?player", marktr("tries to fix the the area of flatland.")); this.plugin = plugin; } @@ -49,7 +53,7 @@ public boolean execute(CommandSender sender, String alias, Map d private boolean tryFlatlandFix(CommandSender sender, Location islandLocation) { // TODO: 29/12/2014 - R4zorax: Load chunks first? - if (!plugin.getIslandLogic().clearFlatland(sender, islandLocation, 0)) { + if (!plugin.getIslandLogic().clearFlatland(sender, islandLocation, Duration.ZERO)) { sender.sendMessage(tr("\u00a74No flatland detected at {0}''s island!", LocationUtil.asString(islandLocation))); } return true; diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/FlushCommand.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/FlushCommand.java index a22b801b3..74e2a35af 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/FlushCommand.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/FlushCommand.java @@ -1,7 +1,9 @@ package us.talabrek.ultimateskyblock.command.admin; +import com.google.inject.Inject; import dk.lockfuglsang.minecraft.command.AbstractCommand; import org.bukkit.command.CommandSender; +import org.jetbrains.annotations.NotNull; import us.talabrek.ultimateskyblock.uSkyBlock; import java.util.Map; @@ -12,7 +14,8 @@ public class FlushCommand extends AbstractCommand { private final uSkyBlock plugin; - public FlushCommand(uSkyBlock plugin) { + @Inject + public FlushCommand(@NotNull uSkyBlock plugin) { super("flush", "usb.admin.cache", marktr("flushes all caches to files")); this.plugin = plugin; } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/GenTopTenCommand.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/GenTopTenCommand.java index 41cd60da9..c4a6d3808 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/GenTopTenCommand.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/GenTopTenCommand.java @@ -1,7 +1,9 @@ package us.talabrek.ultimateskyblock.command.admin; +import com.google.inject.Inject; import dk.lockfuglsang.minecraft.command.AbstractCommand; import org.bukkit.command.CommandSender; +import org.jetbrains.annotations.NotNull; import us.talabrek.ultimateskyblock.uSkyBlock; import java.util.Map; @@ -15,7 +17,8 @@ public class GenTopTenCommand extends AbstractCommand { private final uSkyBlock plugin; - public GenTopTenCommand(uSkyBlock plugin) { + @Inject + public GenTopTenCommand(@NotNull uSkyBlock plugin) { super("topten", "usb.mod.topten", marktr("manually update the top 10 list")); this.plugin = plugin; } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/GotoIslandCommand.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/GotoIslandCommand.java index 181956aa8..003d7428c 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/GotoIslandCommand.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/GotoIslandCommand.java @@ -1,8 +1,10 @@ package us.talabrek.ultimateskyblock.command.admin; +import com.google.inject.Inject; import dk.lockfuglsang.minecraft.po.I18nUtil; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; import us.talabrek.ultimateskyblock.player.PlayerInfo; import us.talabrek.ultimateskyblock.uSkyBlock; @@ -15,7 +17,8 @@ public class GotoIslandCommand extends AbstractPlayerInfoCommand { private final uSkyBlock plugin; - public GotoIslandCommand(uSkyBlock plugin) { + @Inject + public GotoIslandCommand(@NotNull uSkyBlock plugin) { super("goto", "usb.mod.goto", marktr("teleport to another players island")); this.plugin = plugin; } @@ -24,6 +27,7 @@ public GotoIslandCommand(uSkyBlock plugin) { protected void doExecute(final CommandSender sender, final PlayerInfo playerInfo) { if (!(sender instanceof Player)) { sender.sendMessage(I18nUtil.tr("\u00a74Only supported for players")); + return; } final Player player = (Player) sender; if (!playerInfo.getHasIsland()) { @@ -38,4 +42,4 @@ protected void doExecute(final CommandSender sender, final PlayerInfo playerInfo sender.sendMessage(I18nUtil.tr("\u00a74That player does not have an island!")); } } -} \ No newline at end of file +} diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/ImportCommand.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/ImportCommand.java index d7aa61dc5..4e0bfe1e2 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/ImportCommand.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/ImportCommand.java @@ -1,5 +1,6 @@ package us.talabrek.ultimateskyblock.command.admin; +import com.google.inject.Inject; import dk.lockfuglsang.minecraft.command.AbstractCommand; import org.bukkit.command.CommandSender; import org.bukkit.command.TabCompleter; @@ -17,6 +18,7 @@ public class ImportCommand extends AbstractCommand { private final ImportTabCompleter completer; + @Inject public ImportCommand() { super("import", "usb.admin.import", "format", marktr("imports players and islands from other formats")); completer = new ImportTabCompleter(); diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/ItemInfoCommand.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/ItemInfoCommand.java new file mode 100644 index 000000000..9063c990a --- /dev/null +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/ItemInfoCommand.java @@ -0,0 +1,45 @@ +package us.talabrek.ultimateskyblock.command.admin; + +import com.google.inject.Inject; +import dk.lockfuglsang.minecraft.command.AbstractCommand; +import dk.lockfuglsang.minecraft.command.CompositeCommand; +import dk.lockfuglsang.minecraft.util.ItemStackUtil; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +import java.util.Map; + +import static dk.lockfuglsang.minecraft.po.I18nUtil.marktr; +import static dk.lockfuglsang.minecraft.po.I18nUtil.tr; + +/** + * Command for querying items reg. NBT stuff + */ +public class ItemInfoCommand extends CompositeCommand { + + @Inject + public ItemInfoCommand() { + super("iteminfo", "usb.admin.iteminfo", marktr("advanced info about items")); + add(new AbstractCommand("info|i", marktr("shows the component format for the currently held item")) { + @Override + public boolean execute(CommandSender sender, String alias, Map data, String... args) { + if (sender instanceof Player player) { + ItemStack itemStack = player.getInventory().getItemInMainHand(); + if (!itemStack.getType().isItem()) { + player.sendMessage(tr("\u00a7cNo item in hand!")); + return true; + } + String[] msgs = new String[]{ + tr("\u00a7eInfo for \u00a79{0}", ItemStackUtil.asString(itemStack)), + tr("\u00a77 - name: \u00a79{0}", ItemStackUtil.getItemName(itemStack)) + }; + player.sendMessage(msgs); + return true; + } + sender.sendMessage(tr("\u00a7eCan only be executed as a player")); + return false; + } + }); + } +} diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/JobsCommand.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/JobsCommand.java index f51b6fdf0..635d3b3fc 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/JobsCommand.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/JobsCommand.java @@ -1,11 +1,11 @@ package us.talabrek.ultimateskyblock.command.admin; +import com.google.inject.Inject; import dk.lockfuglsang.minecraft.command.AbstractCommand; import dk.lockfuglsang.minecraft.command.CompositeCommand; +import dk.lockfuglsang.minecraft.util.TimeUtil; import org.bukkit.command.CommandSender; import us.talabrek.ultimateskyblock.async.JobManager; -import us.talabrek.ultimateskyblock.uSkyBlock; -import dk.lockfuglsang.minecraft.util.TimeUtil; import java.util.ArrayList; import java.util.Collections; @@ -19,52 +19,36 @@ * Command for reporting and controlling async jobs. */ public class JobsCommand extends CompositeCommand { - private final uSkyBlock plugin; - public JobsCommand(uSkyBlock plugin) { + @Inject + public JobsCommand() { super("jobs|j", "usb.admin.jobs", marktr("controls async jobs")); - this.plugin = plugin; - /* - add(new AbstractCommand("list|l", "usb.admin.jobs.list", "list all jobs") { - @Override - public boolean execute(CommandSender sender, String alias, Map data, String... args) { - return false; - } - }); - */ + add(new AbstractCommand("stats|s", "usb.admin.jobs.stats", "show statistics") { @Override public boolean execute(CommandSender sender, String alias, Map data, String... args) { StringBuilder sb = new StringBuilder(); - sb.append(tr("\u00a79Job Statistics") + "\n"); - sb.append(tr("\u00a77----------------") + "\n"); + sb.append(tr("\u00a79Job Statistics")).append("\n"); + sb.append(tr("\u00a77----------------")).append("\n"); Map stats = JobManager.getStats(); List jobs = new ArrayList<>(stats.keySet()); Collections.sort(jobs); sb.append(String.format("\u00a77%-6s %-8s %-8s %-8s %-8s %-8s %-20s\n", - tr("#"), tr("ms/job"), tr("ms/tick"), tr("ticks"), tr("act"), tr("time"), tr("name"))); + tr("#"), tr("ms/job"), tr("ms/tick"), tr("ticks"), tr("act"), tr("time"), tr("name"))); for (String jobName : jobs) { JobManager.Stats stat = stats.get(jobName); sb.append(String.format("\u00a77%6d %8s %8s %8d \u00a7c%8d \u00a77%8s \u00a79%-20s \n", stat.getJobs(), - TimeUtil.millisAsShort(Math.round(stat.getAvgMsActivePerJob())), - TimeUtil.millisAsShort(Math.round(stat.getAvgMsActivePerTick())), - stat.getTicks(), - stat.getRunningJobs(), - TimeUtil.millisAsShort(Math.round(stat.getAvgMsElapsedPerJob())), - tr(jobName) + TimeUtil.durationAsShort(stat.getAvgRunningTimePerJob()), + TimeUtil.durationAsShort(stat.getAvgRunningTimePerTick()), + stat.getTicks(), + stat.getRunningJobs(), + TimeUtil.durationAsShort(stat.getAvgTimeElapsedPerJob()), + tr(jobName) )); } sender.sendMessage(sb.toString().split("\n")); return true; } }); - /* - add(new AbstractCommand("report|r", "usb.admin.jobs.report", "dump report to file") { - @Override - public boolean execute(CommandSender sender, String alias, Map data, String... args) { - return false; - } - }); - */ } } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/LanguageCommand.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/LanguageCommand.java index cde30eae4..dbac57823 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/LanguageCommand.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/LanguageCommand.java @@ -1,8 +1,10 @@ package us.talabrek.ultimateskyblock.command.admin; +import com.google.inject.Inject; import dk.lockfuglsang.minecraft.command.AbstractCommand; import dk.lockfuglsang.minecraft.po.I18nUtil; import org.bukkit.command.CommandSender; +import org.jetbrains.annotations.NotNull; import us.talabrek.ultimateskyblock.Settings; import us.talabrek.ultimateskyblock.uSkyBlock; @@ -22,7 +24,8 @@ public class LanguageCommand extends AbstractCommand { private final uSkyBlock plugin; - public LanguageCommand(uSkyBlock plugin) { + @Inject + public LanguageCommand(@NotNull uSkyBlock plugin) { super("lang|l", "usb.admin.lang", "language", marktr("changes the language of the plugin, and reloads")); this.plugin = plugin; } @@ -32,7 +35,7 @@ public boolean execute(CommandSender sender, String alias, Map d if (args.length == 1) { Locale loc = I18nUtil.getLocale(args[0]); Settings.locale = loc; - I18nUtil.clearCache(); + I18nUtil.setLocale(loc); plugin.getConfig().set("language", args[0]); plugin.saveConfig(); plugin.reloadConfig(); @@ -53,7 +56,7 @@ public boolean execute(CommandSender sender, String alias, Map d if (line.startsWith("---")) { header = false; } else if (!header && line.contains("|")) { - String parts[] = line.split("\\|"); + String[] parts = line.split("\\|"); if (parts.length == 7) { sb.append(tr("\u00a7f{0} \u00a77{1} \u00a79 by {2} \u00a77{3}\n", parts[1].trim(), parts[0].trim(), parts[6].trim(), parts[2].trim())); } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/MakeLeaderCommand.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/MakeLeaderCommand.java index c038cdace..97e7ef618 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/MakeLeaderCommand.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/MakeLeaderCommand.java @@ -4,6 +4,7 @@ import dk.lockfuglsang.minecraft.po.I18nUtil; import org.bukkit.Location; import org.bukkit.command.CommandSender; +import org.jetbrains.annotations.NotNull; import us.talabrek.ultimateskyblock.handler.WorldGuardHandler; import us.talabrek.ultimateskyblock.island.IslandInfo; import us.talabrek.ultimateskyblock.player.PlayerInfo; @@ -19,7 +20,7 @@ public class MakeLeaderCommand extends AbstractCommand { private final uSkyBlock plugin; - public MakeLeaderCommand(uSkyBlock plugin) { + public MakeLeaderCommand(@NotNull uSkyBlock plugin) { super("makeleader|transfer", "usb.admin.makeleader", "leader oplayer", marktr("transfer leadership to another player")); this.plugin = plugin; } @@ -27,40 +28,35 @@ public MakeLeaderCommand(uSkyBlock plugin) { @Override public boolean execute(final CommandSender sender, String alias, Map data, final String... args) { if (args.length == 2) { - plugin.async(new Runnable() { - @Override - public void run() { - String islandPlayerName = args[0]; - String playerName = args[1]; - PlayerInfo currentLeader = plugin.getPlayerInfo(islandPlayerName); - PlayerInfo newLeader = plugin.getPlayerInfo(playerName); + String islandPlayerName = args[0]; + String playerName = args[1]; + PlayerInfo currentLeader = plugin.getPlayerInfo(islandPlayerName); + PlayerInfo newLeader = plugin.getPlayerInfo(playerName); - if (currentLeader == null || !currentLeader.getHasIsland()) { - sender.sendMessage(I18nUtil.tr("\u00a74Player {0} has no island to transfer!", islandPlayerName)); - return; - } - IslandInfo islandInfo = plugin.getIslandInfo(currentLeader); - if (islandInfo == null) { - sender.sendMessage(I18nUtil.tr("\u00a74Player {0} has no island to transfer!", islandPlayerName)); - return; - } - if (newLeader != null && newLeader.getHasIsland() && !newLeader.locationForParty().equals(islandInfo.getName())) { - sender.sendMessage(I18nUtil.tr("\u00a7ePlayer \u00a7d{0}\u00a7e already has an island.\u00a7eUse \u00a7d/usb island remove \u00a7e to remove him first.", playerName)); - return; - } - newLeader.setJoinParty(islandInfo.getIslandLocation()); - Location homeLocation = currentLeader.getHomeLocation(); - islandInfo.removeMember(currentLeader); // Remove leader - islandInfo.setupPartyLeader(newLeader.getPlayerName()); // Promote member - islandInfo.addMember(currentLeader); - newLeader.setHomeLocation(homeLocation); - currentLeader.save(); - newLeader.save(); - WorldGuardHandler.updateRegion(islandInfo); - plugin.getEventLogic().fireIslandLeaderChangedEvent(islandInfo, currentLeader, newLeader); - islandInfo.sendMessageToIslandGroup(true, marktr("\u00a7bLeadership transferred by {0}\u00a7b to {1}"), sender.getName(), playerName); - } - }); + if (currentLeader == null || !currentLeader.getHasIsland()) { + sender.sendMessage(I18nUtil.tr("\u00a74Player {0} has no island to transfer!", islandPlayerName)); + return true; + } + IslandInfo islandInfo = plugin.getIslandInfo(currentLeader); + if (islandInfo == null) { + sender.sendMessage(I18nUtil.tr("\u00a74Player {0} has no island to transfer!", islandPlayerName)); + return true; + } + if (newLeader != null && newLeader.getHasIsland() && !newLeader.locationForParty().equals(islandInfo.getName())) { + sender.sendMessage(I18nUtil.tr("\u00a7ePlayer \u00a7d{0}\u00a7e already has an island.\u00a7eUse \u00a7d/usb island remove \u00a7e to remove him first.", playerName)); + return true; + } + newLeader.setJoinParty(islandInfo.getIslandLocation()); + Location homeLocation = currentLeader.getHomeLocation(); + islandInfo.removeMember(currentLeader); // Remove leader + islandInfo.setupPartyLeader(newLeader.getPlayerName()); // Promote member + islandInfo.addMember(currentLeader); + newLeader.setHomeLocation(homeLocation); + currentLeader.save(); + newLeader.save(); + WorldGuardHandler.updateRegion(islandInfo); + plugin.getEventLogic().fireIslandLeaderChangedEvent(islandInfo, currentLeader, newLeader); + islandInfo.sendMessageToIslandGroup(true, marktr("\u00a7bLeadership transferred by {0}\u00a7b to {1}"), sender.getName(), playerName); return true; } return false; diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/NBTCommand.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/NBTCommand.java deleted file mode 100644 index 990947802..000000000 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/NBTCommand.java +++ /dev/null @@ -1,90 +0,0 @@ -package us.talabrek.ultimateskyblock.command.admin; - -import dk.lockfuglsang.minecraft.command.AbstractCommand; -import dk.lockfuglsang.minecraft.command.CompositeCommand; -import dk.lockfuglsang.minecraft.nbt.NBTUtil; -import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; -import org.bukkit.inventory.ItemStack; -import us.talabrek.ultimateskyblock.handler.VaultHandler; -import dk.lockfuglsang.minecraft.util.ItemStackUtil; - -import java.util.Map; - -import static dk.lockfuglsang.minecraft.po.I18nUtil.marktr; -import static dk.lockfuglsang.minecraft.po.I18nUtil.tr; - -/** - * Command for querying items reg. NBT stuff - */ -public class NBTCommand extends CompositeCommand { - public NBTCommand() { - super("nbt", "usb.admin.nbt", marktr("advanced info about NBT stuff")); - add(new AbstractCommand("info|i", marktr("shows the NBTTag for the currently held item")) { - @Override - public boolean execute(CommandSender sender, String alias, Map data, String... args) { - if (sender instanceof Player) { - Player player = (Player) sender; - ItemStack itemStack = player.getInventory().getItemInHand(); - if (itemStack != null) { - String[] msgs = new String[]{ - tr("\u00a7eInfo for \u00a79{0}", ItemStackUtil.asString(itemStack)), - tr("\u00a77 - name: \u00a79{0}", VaultHandler.getItemName(itemStack)), - tr("\u00a77 - nbttag: \u00a79{0}", NBTUtil.getNBTTag(itemStack)) - }; - player.sendMessage(msgs); - } else { - player.sendMessage(tr("\u00a7cNo item in hand!")); - } - return true; - } - sender.sendMessage(tr("\u00a7eCan only be executed as a player")); - return false; - } - }); - add(new AbstractCommand("set|s", null, "nbttag", marktr("sets the NBTTag on the currently held item")) { - @Override - public boolean execute(CommandSender sender, String alias, Map data, String... args) { - if (sender instanceof Player) { - if (args.length > 0) { - Player player = (Player) sender; - ItemStack itemStack = player.getInventory().getItemInHand(); - if (itemStack != null) { - String nbtTag = join(args); - itemStack = NBTUtil.setNBTTag(itemStack, nbtTag); - player.getInventory().setItemInHand(itemStack); - player.sendMessage(tr("\u00a7eSet \u00a79{0}\u00a7e to \u00a7c{1}", nbtTag, itemStack)); - } else { - player.sendMessage(tr("\u00a7cNo item in hand!")); - } - return true; - } - } - sender.sendMessage(tr("\u00a7eCan only be executed as a player")); - return false; - } - }); - add(new AbstractCommand("add|a", null, "nbttag", marktr("adds the NBTTag on the currently held item")) { - @Override - public boolean execute(CommandSender sender, String alias, Map data, String... args) { - if (sender instanceof Player) { - if (args.length > 0) { - Player player = (Player) sender; - ItemStack itemStack = player.getInventory().getItemInHand(); - if (itemStack != null) { - String nbtTag = join(args); - itemStack = NBTUtil.addNBTTag(itemStack, nbtTag); - player.getInventory().setItemInHand(itemStack); - player.sendMessage(tr("\u00a7eAdded \u00a79{0}\u00a7e to \u00a7c{1}", nbtTag, itemStack)); - } else { - player.sendMessage(tr("\u00a7cNo item in hand!")); - } - return true; - } - } - sender.sendMessage(tr("\u00a7eCan only be executed as a player")); - return false; - } - }); - } -} diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/OrphanCommand.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/OrphanCommand.java index 4c89306bf..685cc04c1 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/OrphanCommand.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/OrphanCommand.java @@ -1,9 +1,11 @@ package us.talabrek.ultimateskyblock.command.admin; +import com.google.inject.Inject; import dk.lockfuglsang.minecraft.command.AbstractCommand; import dk.lockfuglsang.minecraft.command.CompositeCommand; import dk.lockfuglsang.minecraft.po.I18nUtil; import org.bukkit.command.CommandSender; +import org.jetbrains.annotations.NotNull; import us.talabrek.ultimateskyblock.island.OrphanLogic; import us.talabrek.ultimateskyblock.uSkyBlock; @@ -16,7 +18,9 @@ * Handles Orphans. */ public class OrphanCommand extends CompositeCommand { - public OrphanCommand(final uSkyBlock plugin) { + + @Inject + public OrphanCommand(@NotNull uSkyBlock plugin) { super("orphan", "usb.admin.orphan", marktr("manage orphans")); add(new AbstractCommand("count", marktr("count orphans")) { @Override @@ -41,7 +45,7 @@ public boolean execute(CommandSender sender, String alias, Map d sender.sendMessage(I18nUtil.tr("\u00a7eNo orphans currently registered.")); } else { int pageSize = 50; - int pages = (int)Math.ceil(orphans.size() / pageSize); + int pages = (int) Math.ceil((double) orphans.size() / pageSize); int page = args.length > 0 && args[0].matches("[0-9]+") ? Integer.parseInt(args[0], 10) : 1; if (page < 1) page = 1; if (page > pages) page = pages; diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/PerkCommand.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/PerkCommand.java index bbfa7c271..101b8e88d 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/PerkCommand.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/PerkCommand.java @@ -1,9 +1,11 @@ package us.talabrek.ultimateskyblock.command.admin; +import com.google.inject.Inject; import dk.lockfuglsang.minecraft.command.AbstractCommand; import dk.lockfuglsang.minecraft.command.CompositeCommand; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; import us.talabrek.ultimateskyblock.player.Perk; import us.talabrek.ultimateskyblock.uSkyBlock; @@ -14,16 +16,18 @@ import static dk.lockfuglsang.minecraft.util.FormatUtil.stripFormatting; public class PerkCommand extends CompositeCommand { - public PerkCommand(final uSkyBlock plugin) { + + @Inject + public PerkCommand(@NotNull uSkyBlock plugin) { super("perk", "usb.admin.perk", marktr("shows perk-information")); add(new AbstractCommand("list", "lists all perks") { @Override public boolean execute(CommandSender sender, String alias, Map data, String... args) { StringBuilder sb = new StringBuilder(); for (Map.Entry entry : plugin.getPerkLogic().getPerkMap().entrySet()) { - sb.append("\u00a79" + entry.getKey() + ":\n"); + sb.append("\u00a79").append(entry.getKey()).append(":\n"); String value = (entry.getValue().toString().replaceAll("\n", "\n ")).trim(); - sb.append(" " + value + "\n"); + sb.append(" ").append(value).append("\n"); } sender.sendMessage(sb.toString().split("\n")); return true; @@ -37,9 +41,9 @@ public boolean execute(CommandSender sender, String alias, Map d if (player != null) { StringBuilder sb = new StringBuilder(); Perk perk = plugin.getPerkLogic().getPerk(player); - sb.append("\u00a79" + player.getName() + ":\n"); + sb.append("\u00a79").append(player.getName()).append(":\n"); String value = (perk.toString().replaceAll("\n", "\n ")).trim(); - sb.append(" " + value + "\n"); + sb.append(" ").append(value).append("\n"); sender.sendMessage(sb.toString().split("\n")); return true; } else { diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/PlayerInfoCommand.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/PlayerInfoCommand.java new file mode 100644 index 000000000..30ffe7562 --- /dev/null +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/PlayerInfoCommand.java @@ -0,0 +1,20 @@ +package us.talabrek.ultimateskyblock.command.admin; + +import com.google.inject.Inject; +import org.bukkit.command.CommandSender; +import us.talabrek.ultimateskyblock.player.PlayerInfo; + +import static dk.lockfuglsang.minecraft.po.I18nUtil.marktr; + +public class PlayerInfoCommand extends AbstractPlayerInfoCommand { + + @Inject + public PlayerInfoCommand() { + super("info", "usb.admin.info", marktr("show player-information")); + } + + @Override + protected void doExecute(CommandSender sender, PlayerInfo playerInfo) { + sender.sendMessage(playerInfo.toString()); + } +} diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/ProtectAllCommand.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/ProtectAllCommand.java index 78edcfd45..283b8c0b0 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/ProtectAllCommand.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/ProtectAllCommand.java @@ -1,11 +1,15 @@ package us.talabrek.ultimateskyblock.command.admin; +import com.google.inject.Inject; import dk.lockfuglsang.minecraft.command.AbstractCommand; import org.bukkit.command.CommandSender; +import org.jetbrains.annotations.NotNull; import us.talabrek.ultimateskyblock.command.admin.task.ProtectAllTask; +import us.talabrek.ultimateskyblock.island.IslandLogic; import us.talabrek.ultimateskyblock.uSkyBlock; import us.talabrek.ultimateskyblock.util.ProgressTracker; +import java.time.Duration; import java.util.Map; import static dk.lockfuglsang.minecraft.po.I18nUtil.marktr; @@ -16,11 +20,14 @@ */ public class ProtectAllCommand extends AbstractCommand { private final uSkyBlock plugin; + private final IslandLogic islandLogic; private ProtectAllTask task; - public ProtectAllCommand(uSkyBlock plugin) { + @Inject + public ProtectAllCommand(@NotNull uSkyBlock plugin, @NotNull IslandLogic islandLogic) { super("protectall", "usb.admin.protectall", marktr("protects all islands (time consuming)")); this.plugin = plugin; + this.islandLogic = islandLogic; } private boolean isProtectAllActive() { @@ -41,8 +48,9 @@ public boolean execute(CommandSender sender, String alias, Map d } } sender.sendMessage(tr("\u00a7eStarting a protect-all task. It will take a while.")); - ProgressTracker tracker = new ProgressTracker(sender, "\u00a77- Protect-All {0,number,##}% ({1}/{2}, failed:{3}, skipped:{4}) ~ {5}", 10, plugin.getConfig().getInt("async.long.feedbackEvery", 30000)); - task = new ProtectAllTask(plugin, sender, tracker); + Duration feedbackFrequency = Duration.ofMillis(plugin.getConfig().getLong("async.long.feedbackEvery", 30000)); + ProgressTracker tracker = new ProgressTracker(sender, "\u00a77- Protect-All {0,number,##}% ({1}/{2}, failed:{3}, skipped:{4}) ~ {5}", 10, feedbackFrequency); + task = new ProtectAllTask(plugin, sender, islandLogic.getIslandDirectory(), tracker); task.runTaskAsynchronously(plugin); return true; } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/PurgeCommand.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/PurgeCommand.java index 89edecec0..7c5a9619c 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/PurgeCommand.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/PurgeCommand.java @@ -1,13 +1,17 @@ package us.talabrek.ultimateskyblock.command.admin; +import com.google.inject.Inject; import dk.lockfuglsang.minecraft.command.AbstractCommand; +import dk.lockfuglsang.minecraft.util.TimeUtil; import org.bukkit.command.CommandSender; -import org.bukkit.scheduler.BukkitRunnable; +import org.jetbrains.annotations.NotNull; import us.talabrek.ultimateskyblock.command.admin.task.PurgeScanTask; import us.talabrek.ultimateskyblock.command.admin.task.PurgeTask; +import us.talabrek.ultimateskyblock.island.IslandLogic; import us.talabrek.ultimateskyblock.uSkyBlock; -import dk.lockfuglsang.minecraft.util.TimeUtil; +import us.talabrek.ultimateskyblock.util.Scheduler; +import java.time.Duration; import java.util.Map; import static dk.lockfuglsang.minecraft.po.I18nUtil.marktr; @@ -18,14 +22,18 @@ */ public class PurgeCommand extends AbstractCommand { private final uSkyBlock plugin; + private final IslandLogic islandLogic; + private final Scheduler scheduler; private PurgeScanTask scanTask; private PurgeTask purgeTask; - private String days = null; - public PurgeCommand(uSkyBlock plugin) { + @Inject + public PurgeCommand(@NotNull uSkyBlock plugin, @NotNull IslandLogic islandLogic, Scheduler scheduler) { super("purge", "usb.admin.purge", "time-in-days|stop|confirm ?level ?force", marktr("purges all abandoned islands")); this.plugin = plugin; + this.islandLogic = islandLogic; + this.scheduler = scheduler; } @Override @@ -38,7 +46,7 @@ public boolean execute(final CommandSender sender, String alias, Map 1 && args[1].matches("[0-9]+([.,][0-9]+)?")) { try { @@ -48,25 +56,22 @@ public boolean execute(final CommandSender sender, String alias, Map { + scanTask = new PurgeScanTask(plugin, islandLogic.getIslandDirectory().toFile(), time, purgeLevel, sender, () -> { if (force) { doPurge(sender); } else { - int timeout = plugin.getConfig().getInt("options.advanced.purgeTimeout", 600000); - sender.sendMessage(tr("\u00a74PURGE:\u00a7e Do \u00a79usb purge confirm\u00a7e within {0} to accept.", TimeUtil.millisAsString(timeout))); - new BukkitRunnable() { - @Override - public void run() { - if (scanTask.isActive()) { - sender.sendMessage("\u00a77purge timed out"); - scanTask.stop(); - } + Duration timeout = Duration.ofMillis(plugin.getConfig().getLong("options.advanced.purgeTimeout", 600000)); // TODO: this option does not have an entry in plugin.yml + sender.sendMessage(tr("\u00a74PURGE:\u00a7e Do \u00a79usb purge confirm\u00a7e within {0} to accept.", TimeUtil.durationAsString(timeout))); + scheduler.async(() -> { + if (scanTask.isActive()) { + sender.sendMessage("\u00a77purge timed out"); + scanTask.stop(); } - }.runTaskLaterAsynchronously(plugin, TimeUtil.millisAsTicks(timeout)); + }, timeout); } }); scanTask.runTaskAsynchronously(plugin); diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/RegionCommand.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/RegionCommand.java index e58664abc..924497de9 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/RegionCommand.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/RegionCommand.java @@ -1,5 +1,6 @@ package us.talabrek.ultimateskyblock.command.admin; +import com.google.inject.Inject; import com.sk89q.worldedit.math.BlockVector2; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.CuboidRegion; @@ -15,6 +16,7 @@ import org.bukkit.World; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; import us.talabrek.ultimateskyblock.handler.WorldEditHandler; import us.talabrek.ultimateskyblock.handler.WorldGuardHandler; import us.talabrek.ultimateskyblock.uSkyBlock; @@ -36,7 +38,8 @@ public class RegionCommand extends CompositeCommand { private int dash; private Material material; - public RegionCommand(uSkyBlock plugin, final AnimationHandler animationHandler) { + @Inject + public RegionCommand(@NotNull AnimationHandler animationHandler) { super("region|rg", "usb.admin.region", marktr("region manipulations")); this.animationHandler = animationHandler; add(new AbstractCommand("show", marktr("shows the borders of the current island")) { @@ -255,6 +258,6 @@ public void setMaterial(Material material) { } public synchronized void addAnimation(Player player, List points) { - animationHandler.addAnimation(new BlockAnimation(player, points, material, (byte) 0)); + animationHandler.addAnimation(new BlockAnimation(player, points, material.createBlockData())); } } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/RegisterIslandToPlayerCommand.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/RegisterIslandToPlayerCommand.java index 2c55a8716..54caf61b5 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/RegisterIslandToPlayerCommand.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/RegisterIslandToPlayerCommand.java @@ -1,5 +1,6 @@ package us.talabrek.ultimateskyblock.command.admin; +import com.google.inject.Inject; import dk.lockfuglsang.minecraft.command.AbstractCommand; import dk.lockfuglsang.minecraft.po.I18nUtil; import org.bukkit.command.CommandSender; @@ -14,19 +15,21 @@ * Registers an island to a player. */ public class RegisterIslandToPlayerCommand extends AbstractCommand { + + @Inject public RegisterIslandToPlayerCommand() { super("register", "usb.admin.register", "player", marktr("set a player''s island to your location")); } + @Override public boolean execute(final CommandSender sender, String alias, Map data, final String... args) { - if (!(sender instanceof Player)) { + if (!(sender instanceof Player player)) { return false; } if (args.length < 1) { return false; } String playerName = args[0]; - Player player = (Player) sender; if (uSkyBlock.getInstance().devSetPlayerIsland(player, player.getLocation(), playerName)) { sender.sendMessage(I18nUtil.tr("\u00a7aSet {0}''s island to the current island.", playerName)); } else { diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/ReloadCommand.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/ReloadCommand.java index 72855524e..057368474 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/ReloadCommand.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/ReloadCommand.java @@ -1,5 +1,6 @@ package us.talabrek.ultimateskyblock.command.admin; +import com.google.inject.Inject; import dk.lockfuglsang.minecraft.command.AbstractCommand; import dk.lockfuglsang.minecraft.po.I18nUtil; import org.bukkit.command.CommandSender; @@ -13,6 +14,8 @@ * Reloads the config-files for USB. */ public class ReloadCommand extends AbstractCommand { + + @Inject public ReloadCommand() { super("reload", "usb.admin.reload", marktr("reload configuration from file.")); } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/SetMaintenanceCommand.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/SetMaintenanceCommand.java index aec4da6ac..7f348d27d 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/SetMaintenanceCommand.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/SetMaintenanceCommand.java @@ -1,8 +1,10 @@ package us.talabrek.ultimateskyblock.command.admin; +import com.google.inject.Inject; import dk.lockfuglsang.minecraft.command.AbstractCommand; import org.bukkit.command.CommandSender; import org.bukkit.command.ConsoleCommandSender; +import org.jetbrains.annotations.NotNull; import us.talabrek.ultimateskyblock.uSkyBlock; import java.util.Map; @@ -16,7 +18,8 @@ public class SetMaintenanceCommand extends AbstractCommand { private final uSkyBlock plugin; - public SetMaintenanceCommand(uSkyBlock plugin) { + @Inject + public SetMaintenanceCommand(@NotNull uSkyBlock plugin) { super("maintenance", "usb.admin.maintenance", "true|false", marktr("toggles maintenance mode")); this.plugin = plugin; } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/VersionCommand.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/VersionCommand.java index 4fec092f9..e23e57b7f 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/VersionCommand.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/VersionCommand.java @@ -1,8 +1,10 @@ package us.talabrek.ultimateskyblock.command.admin; +import com.google.inject.Inject; import dk.lockfuglsang.minecraft.command.AbstractCommand; import org.bukkit.command.CommandSender; -import us.talabrek.ultimateskyblock.uSkyBlock; +import org.jetbrains.annotations.NotNull; +import us.talabrek.ultimateskyblock.util.PluginInfo; import java.util.Map; import java.util.UUID; @@ -13,17 +15,18 @@ * Displays detailed version information. */ public class VersionCommand extends AbstractCommand { - private final uSkyBlock plugin; - public VersionCommand(uSkyBlock plugin) { + private final PluginInfo pluginInfo; + + @Inject + public VersionCommand(@NotNull PluginInfo pluginInfo) { super("version|v", "usb.admin.version", null, marktr("displays version information"), null, UUID.fromString("97e8584c-438c-43cf-8b58-4e56c52398ed")); - this.plugin = plugin; + this.pluginInfo = pluginInfo; } @Override public boolean execute(CommandSender sender, String alias, Map data, String... args) { - sender.sendMessage(plugin.getVersionInfo(true).split("\n")); + sender.sendMessage(pluginInfo.getVersionInfo(true).split("\n")); return true; } - } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/WGCommand.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/WGCommand.java index 5eb18b7ed..795726e6e 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/WGCommand.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/WGCommand.java @@ -1,9 +1,11 @@ package us.talabrek.ultimateskyblock.command.admin; +import com.google.inject.Inject; import dk.lockfuglsang.minecraft.command.CompositeCommand; import dk.lockfuglsang.minecraft.po.I18nUtil; import org.bukkit.World; import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; import us.talabrek.ultimateskyblock.command.island.RequirePlayerCommand; import us.talabrek.ultimateskyblock.handler.WorldEditHandler; import us.talabrek.ultimateskyblock.handler.WorldGuardHandler; @@ -22,7 +24,8 @@ public class WGCommand extends CompositeCommand { private final uSkyBlock plugin; - public WGCommand(final uSkyBlock plugin) { + @Inject + public WGCommand(@NotNull final uSkyBlock plugin) { super("wg", "usb.admin.wg", marktr("various WorldGuard utilities")); this.plugin = plugin; add(new RequirePlayerCommand("refresh", null, marktr("refreshes the chunks around the player")) { diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/task/ProtectAllTask.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/task/ProtectAllTask.java index fcc57f9fb..e318a4189 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/task/ProtectAllTask.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/task/ProtectAllTask.java @@ -1,6 +1,7 @@ package us.talabrek.ultimateskyblock.command.admin.task; import dk.lockfuglsang.minecraft.file.FileUtil; +import dk.lockfuglsang.minecraft.util.TimeUtil; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import org.bukkit.scheduler.BukkitRunnable; @@ -10,8 +11,10 @@ import us.talabrek.ultimateskyblock.util.IslandUtil; import us.talabrek.ultimateskyblock.util.LogUtil; import us.talabrek.ultimateskyblock.util.ProgressTracker; -import dk.lockfuglsang.minecraft.util.TimeUtil; +import java.nio.file.Path; +import java.time.Duration; +import java.time.Instant; import java.util.logging.Level; import java.util.logging.Logger; @@ -25,13 +28,15 @@ public class ProtectAllTask extends BukkitRunnable { private final CommandSender sender; private final uSkyBlock plugin; private final ProgressTracker tracker; + private final Path islandDirectory; private volatile boolean active; - public ProtectAllTask(final uSkyBlock plugin, final CommandSender sender, ProgressTracker tracker) { + public ProtectAllTask(final uSkyBlock plugin, final CommandSender sender, Path islandDirectory, ProgressTracker tracker) { this.plugin = plugin; this.tracker = tracker; this.sender = sender; + this.islandDirectory = islandDirectory; } public boolean isActive() { @@ -48,9 +53,9 @@ public void run() { long failed = 0; long success = 0; long skipped = 0; - long tStart = System.currentTimeMillis(); + Instant tStart = Instant.now(); try { - String[] list = plugin.directoryIslands.list(IslandUtil.createIslandFilenameFilter()); + String[] list = islandDirectory.toFile().list(IslandUtil.createIslandFilenameFilter()); long total = list != null ? list.length : 0; if (list != null) { for (String fileName : list) { @@ -79,13 +84,13 @@ public void run() { active = false; } String message = tr("\u00a7eCompleted protect-all in {0}, {1} new regions were created!", getElapsed(tStart), success); - if (sender instanceof Player && ((Player)sender).isOnline()) { + if (sender instanceof Player && ((Player) sender).isOnline()) { sender.sendMessage(message); } LogUtil.log(Level.INFO, message); } - private String getElapsed(long tStart) { - return TimeUtil.millisAsString(System.currentTimeMillis() - tStart); + private String getElapsed(Instant tStart) { + return TimeUtil.durationAsString(Duration.between(tStart, Instant.now())); } } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/task/PurgeScanTask.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/task/PurgeScanTask.java index 3c79fc911..575dc92a6 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/task/PurgeScanTask.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/task/PurgeScanTask.java @@ -1,7 +1,7 @@ package us.talabrek.ultimateskyblock.command.admin.task; import dk.lockfuglsang.minecraft.file.FileUtil; -import dk.lockfuglsang.minecraft.util.TimeUtil; +import dk.lockfuglsang.minecraft.util.Timer; import org.bukkit.OfflinePlayer; import org.bukkit.command.CommandSender; import org.bukkit.scheduler.BukkitRunnable; @@ -12,6 +12,8 @@ import us.talabrek.ultimateskyblock.uuid.PlayerDB; import java.io.File; +import java.time.Duration; +import java.time.Instant; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -26,31 +28,32 @@ /** * Scans for all players on a list of islands. */ +// TODO: test this class!!! public class PurgeScanTask extends BukkitRunnable { private final List islandList; private final List purgeList; - private final long cutOff; + private final Instant cutOff; private final uSkyBlock plugin; private final CommandSender sender; private final Runnable callback; private final double purgeLevel; private final ProgressTracker tracker; - private final long tStart; + private final Timer timer; private final PlayerDB playerDB; private volatile boolean active; private boolean done; - public PurgeScanTask(uSkyBlock plugin, File islandDir, int time, double purgeLevel, CommandSender sender, Runnable callback) { + public PurgeScanTask(uSkyBlock plugin, File islandDir, Duration time, double purgeLevel, CommandSender sender, Runnable callback) { this.plugin = plugin; this.sender = sender; this.callback = callback; - this.cutOff = System.currentTimeMillis() - (time * 3600000L); + this.cutOff = Instant.now().minus(time); String[] islandList = islandDir.list(IslandUtil.createIslandFilenameFilter()); this.islandList = new ArrayList<>(Arrays.asList(islandList)); purgeList = new ArrayList<>(); this.purgeLevel = purgeLevel; - int feedbackEvery = plugin.getConfig().getInt("async.long.feedbackEvery", 30000); - tStart = System.currentTimeMillis(); + Duration feedbackEvery = Duration.ofMillis(plugin.getConfig().getLong("async.long.feedbackEvery", 30000)); + timer = Timer.start(); tracker = new ProgressTracker(sender, marktr("\u00a77- SCANNING: {0,number,##}% ({1}/{2} failed: {3}) ~ {4}"), 25, feedbackEvery); active = true; playerDB = plugin.getPlayerDB(); @@ -78,7 +81,7 @@ private void generatePurgeList() { failed++; } progress++; - tracker.progressUpdate(progress, total, failed, TimeUtil.millisAsString(System.currentTimeMillis()-tStart)); + tracker.progressUpdate(progress, total, failed, timer.elapsedAsString()); } } @@ -101,7 +104,7 @@ public List getPurgeList() { private boolean abandonedSince(Set members) { for (UUID member : members) { OfflinePlayer player = playerDB.getOfflinePlayer(member); - if (player == null || player.getLastPlayed() > cutOff) { + if (player == null || Instant.ofEpochMilli(player.getLastPlayed()).isAfter(cutOff)) { return false; } } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/task/PurgeTask.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/task/PurgeTask.java index 018bf7de2..556f7347d 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/task/PurgeTask.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/admin/task/PurgeTask.java @@ -1,12 +1,14 @@ package us.talabrek.ultimateskyblock.command.admin.task; import dk.lockfuglsang.minecraft.po.I18nUtil; +import dk.lockfuglsang.minecraft.util.TimeUtil; +import dk.lockfuglsang.minecraft.util.Timer; import org.bukkit.command.CommandSender; import org.bukkit.scheduler.BukkitRunnable; import us.talabrek.ultimateskyblock.uSkyBlock; import us.talabrek.ultimateskyblock.util.ProgressTracker; -import dk.lockfuglsang.minecraft.util.TimeUtil; +import java.time.Duration; import java.util.List; import java.util.logging.Level; @@ -20,34 +22,33 @@ public class PurgeTask extends BukkitRunnable { private final List purgeList; private final uSkyBlock plugin; private final CommandSender sender; - private final int feedbackEvery; private final ProgressTracker tracker; - private final long tStart; + private final Timer timer; private boolean active; public PurgeTask(uSkyBlock plugin, List purgeList, CommandSender sender) { this.plugin = plugin; this.sender = sender; this.purgeList = purgeList; - tStart = System.currentTimeMillis(); - feedbackEvery = plugin.getConfig().getInt("async.long.feedbackEvery", 30000); + this.timer = Timer.start(); + Duration feedbackEvery = Duration.ofMillis(plugin.getConfig().getInt("async.long.feedbackEvery", 30000)); tracker = new ProgressTracker(sender, marktr("- PURGING: {0,number,##}% ({1}/{2}), elapsed {3}, estimated completion ~{4}"), 25, feedbackEvery); active = true; } private void doPurge() { int total = purgeList.size(); - int cnt = 0; + int completed = 0; while (!purgeList.isEmpty()) { if (!active) { break; } - final String islandName = purgeList.remove(0); + String islandName = purgeList.removeFirst(); plugin.getIslandLogic().purge(islandName); - cnt++; - long elapsed = System.currentTimeMillis() - tStart; - long eta = (elapsed/cnt) * (total-cnt); - tracker.progressUpdate(cnt, total, TimeUtil.millisAsString(elapsed), TimeUtil.millisAsString(eta)); + completed++; + Duration elapsed = timer.elapsed(); + Duration eta = elapsed.dividedBy(completed).multipliedBy(total - completed); + tracker.progressUpdate(completed, total, TimeUtil.durationAsString(elapsed), TimeUtil.durationAsTicks(eta)); } plugin.getOrphanLogic().save(); } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/challenge/ChallengeCompleteCommand.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/challenge/ChallengeCompleteCommand.java index a9450e50e..2ee27a8b3 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/challenge/ChallengeCompleteCommand.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/challenge/ChallengeCompleteCommand.java @@ -1,9 +1,11 @@ package us.talabrek.ultimateskyblock.command.challenge; +import com.google.inject.Inject; import dk.lockfuglsang.minecraft.command.AbstractCommand; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; -import us.talabrek.ultimateskyblock.uSkyBlock; +import org.jetbrains.annotations.NotNull; +import us.talabrek.ultimateskyblock.challenge.ChallengeLogic; import java.util.Map; @@ -14,11 +16,12 @@ * Complete Challenge Command */ public class ChallengeCompleteCommand extends AbstractCommand { - private final uSkyBlock plugin; + private final ChallengeLogic challengeLogic; - public ChallengeCompleteCommand(uSkyBlock plugin) { + @Inject + public ChallengeCompleteCommand(@NotNull ChallengeLogic challengeLogic) { super("complete|c", "usb.island.challenges", "challenge", marktr("try to complete a challenge")); - this.plugin = plugin; + this.challengeLogic = challengeLogic; } @Override @@ -30,11 +33,8 @@ public boolean execute(CommandSender sender, String alias, Map d if (args == null || args.length == 0) { return false; } - String challengeName = ""; - for (String arg : args) { - challengeName += " " + arg; - } - plugin.getChallengeLogic().completeChallenge((Player) sender, challengeName.trim()); + String challengeName = String.join(" ", args); + challengeLogic.completeChallenge((Player) sender, challengeName); return true; } } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/challenge/ChallengeInfoCommand.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/challenge/ChallengeInfoCommand.java index fabd25f72..eff0dfca6 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/challenge/ChallengeInfoCommand.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/challenge/ChallengeInfoCommand.java @@ -1,15 +1,17 @@ package us.talabrek.ultimateskyblock.command.challenge; +import com.google.inject.Inject; import dk.lockfuglsang.minecraft.command.AbstractCommand; import org.bukkit.ChatColor; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; import us.talabrek.ultimateskyblock.challenge.Challenge; import us.talabrek.ultimateskyblock.challenge.ChallengeCompletion; import us.talabrek.ultimateskyblock.challenge.ChallengeLogic; import us.talabrek.ultimateskyblock.player.PlayerInfo; -import us.talabrek.ultimateskyblock.uSkyBlock; +import us.talabrek.ultimateskyblock.player.PlayerLogic; import java.util.Map; @@ -20,28 +22,29 @@ * Shows information about a challenge */ public class ChallengeInfoCommand extends AbstractCommand { - private final uSkyBlock plugin; - public ChallengeInfoCommand(uSkyBlock plugin) { + private final ChallengeLogic challengeLogic; + private final PlayerLogic playerLogic; + + @Inject + public ChallengeInfoCommand( + @NotNull ChallengeLogic challengeLogic, + @NotNull PlayerLogic playerLogic + ) { super("info|i", null, "challenge", marktr("show information about the challenge")); - this.plugin = plugin; + this.challengeLogic = challengeLogic; + this.playerLogic = playerLogic; } @Override public boolean execute(CommandSender sender, String alias, Map data, String... args) { - if (!(sender instanceof Player)) { + if (!(sender instanceof Player player)) { sender.sendMessage(tr("\u00a7cCommand only available for players.")); return false; } - String challengeName = ""; - for (String arg : args) { - challengeName += " " + arg; - } - challengeName = challengeName.trim(); - ChallengeLogic challengeLogic = plugin.getChallengeLogic(); - Player player = (Player) sender; + String challengeName = String.join(" ", args); Challenge challenge = challengeLogic.getChallenge(challengeName); - PlayerInfo playerInfo = plugin.getPlayerInfo(player); + PlayerInfo playerInfo = playerLogic.getPlayerInfo(player); if (challenge != null && challenge.getRank().isAvailable(playerInfo)) { player.sendMessage("\u00a7eChallenge Name: " + ChatColor.WHITE + challengeName.toLowerCase()); if (challengeLogic.getRanks().size() > 1) { diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/completion/AllPlayerTabCompleter.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/completion/AllPlayerTabCompleter.java index 3b6ecc5dc..996ea4c12 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/completion/AllPlayerTabCompleter.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/completion/AllPlayerTabCompleter.java @@ -1,5 +1,6 @@ package us.talabrek.ultimateskyblock.command.completion; +import com.google.inject.Inject; import dk.lockfuglsang.minecraft.command.completion.AbstractTabCompleter; import org.bukkit.command.CommandSender; import us.talabrek.ultimateskyblock.uSkyBlock; @@ -15,6 +16,7 @@ public class AllPlayerTabCompleter extends AbstractTabCompleter { private final OnlinePlayerTabCompleter online; + @Inject public AllPlayerTabCompleter(OnlinePlayerTabCompleter online) { this.online = online; } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/completion/AvailableChallengeTabCompleter.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/completion/AvailableChallengeTabCompleter.java index 1344eb957..5986b9a33 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/completion/AvailableChallengeTabCompleter.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/completion/AvailableChallengeTabCompleter.java @@ -1,10 +1,13 @@ package us.talabrek.ultimateskyblock.command.completion; +import com.google.inject.Inject; import dk.lockfuglsang.minecraft.command.completion.AbstractTabCompleter; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import us.talabrek.ultimateskyblock.challenge.ChallengeLogic; import us.talabrek.ultimateskyblock.player.PlayerInfo; -import us.talabrek.ultimateskyblock.uSkyBlock; +import us.talabrek.ultimateskyblock.player.PlayerLogic; import java.util.List; @@ -12,15 +15,27 @@ * TabCompleter for challenge-names */ public class AvailableChallengeTabCompleter extends AbstractTabCompleter { + + private final PlayerLogic playerLogic; + private final ChallengeLogic challengeLogic; + + @Inject + public AvailableChallengeTabCompleter( + @NotNull PlayerLogic playerLogic, + @NotNull ChallengeLogic challengeLogic + ) { + this.playerLogic = playerLogic; + this.challengeLogic = challengeLogic; + } + @Override protected List getTabList(CommandSender commandSender, String term) { - PlayerInfo pi = null; if (commandSender instanceof Player) { - pi = uSkyBlock.getInstance().getPlayerInfo((Player)commandSender); + PlayerInfo pi = playerLogic.getPlayerInfo((Player) commandSender); if (pi != null) { - uSkyBlock.getInstance().getChallengeLogic().getAvailableChallengeNames(pi); + return challengeLogic.getAvailableChallengeNames(pi); } } - return uSkyBlock.getInstance().getChallengeLogic().getAllChallengeNames(); + return challengeLogic.getAllChallengeNames(); } } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/completion/BiomeTabCompleter.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/completion/BiomeTabCompleter.java index f2c05fcf0..4e9678f17 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/completion/BiomeTabCompleter.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/completion/BiomeTabCompleter.java @@ -1,8 +1,10 @@ package us.talabrek.ultimateskyblock.command.completion; +import com.google.inject.Inject; import dk.lockfuglsang.minecraft.command.completion.AbstractTabCompleter; import org.bukkit.command.CommandSender; -import us.talabrek.ultimateskyblock.command.island.BiomeCommand; +import org.jetbrains.annotations.NotNull; +import us.talabrek.ultimateskyblock.biome.BiomeConfig; import java.util.ArrayList; import java.util.List; @@ -11,10 +13,16 @@ * TabCompleter for Biomes. */ public class BiomeTabCompleter extends AbstractTabCompleter { - private static final List BIOMES = new ArrayList<>(BiomeCommand.BIOMES.keySet()); + + private final BiomeConfig biomeConfig; + + @Inject + public BiomeTabCompleter(@NotNull BiomeConfig biomeConfig) { + this.biomeConfig = biomeConfig; + } @Override protected List getTabList(CommandSender commandSender, String term) { - return filter(BIOMES, term); + return filter(new ArrayList<>(biomeConfig.getConfiguredBiomeKeys()), term); } } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/completion/MemberTabCompleter.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/completion/MemberTabCompleter.java index a330c246e..351ea1759 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/completion/MemberTabCompleter.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/completion/MemberTabCompleter.java @@ -1,12 +1,17 @@ package us.talabrek.ultimateskyblock.command.completion; +import com.google.inject.Inject; import dk.lockfuglsang.minecraft.command.completion.AbstractTabCompleter; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; import org.bukkit.command.TabCompleter; import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import us.talabrek.ultimateskyblock.api.IslandInfo; +import us.talabrek.ultimateskyblock.island.IslandLogic; import us.talabrek.ultimateskyblock.player.PlayerInfo; -import us.talabrek.ultimateskyblock.uSkyBlock; +import us.talabrek.ultimateskyblock.player.PlayerLogic; import java.util.ArrayList; import java.util.List; @@ -15,20 +20,26 @@ * Only list members of your current party. */ public class MemberTabCompleter implements TabCompleter { - private final uSkyBlock plugin; + private final PlayerLogic playerLogic; + private final IslandLogic islandLogic; - public MemberTabCompleter(uSkyBlock plugin) { - this.plugin = plugin; + @Inject + public MemberTabCompleter( + @NotNull PlayerLogic playerLogic, + @NotNull IslandLogic islandLogic + ) { + this.playerLogic = playerLogic; + this.islandLogic = islandLogic; } @Override - public List onTabComplete(CommandSender sender, Command command, String alias, String[] args) { + public @Nullable List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, String[] args) { if (sender instanceof Player) { - PlayerInfo playerInfo = plugin.getPlayerInfo((Player) sender); + PlayerInfo playerInfo = playerLogic.getPlayerInfo((Player) sender); if (playerInfo != null && playerInfo.getHasIsland()) { - us.talabrek.ultimateskyblock.api.IslandInfo islandInfo = plugin.getIslandInfo(playerInfo); + IslandInfo islandInfo = islandLogic.getIslandInfo(playerInfo); if (islandInfo != null) { - String member = args.length > 0 ? args[args.length-1] : ""; + String member = args.length > 0 ? args[args.length - 1] : ""; return AbstractTabCompleter.filter(new ArrayList<>(islandInfo.getMembers()), member); } } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/completion/PermissionTabCompleter.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/completion/PermissionTabCompleter.java index 72245c391..89f8532ca 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/completion/PermissionTabCompleter.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/completion/PermissionTabCompleter.java @@ -1,5 +1,6 @@ package us.talabrek.ultimateskyblock.command.completion; +import com.google.inject.Inject; import dk.lockfuglsang.minecraft.command.completion.AbstractTabCompleter; import org.bukkit.command.CommandSender; import us.talabrek.ultimateskyblock.menu.PartyPermissionMenuItem; @@ -9,8 +10,9 @@ import java.util.List; public class PermissionTabCompleter extends AbstractTabCompleter { - private uSkyBlock plugin; + private final uSkyBlock plugin; + @Inject public PermissionTabCompleter(uSkyBlock plugin) { this.plugin = plugin; } @@ -23,5 +25,4 @@ protected List getTabList(CommandSender commandSender, String term) { } return list; } - } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/completion/RankTabCompleter.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/completion/RankTabCompleter.java index ead7505c2..51de52ec5 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/completion/RankTabCompleter.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/completion/RankTabCompleter.java @@ -1,9 +1,11 @@ package us.talabrek.ultimateskyblock.command.completion; +import com.google.inject.Inject; import dk.lockfuglsang.minecraft.command.completion.AbstractTabCompleter; import org.bukkit.command.CommandSender; +import org.jetbrains.annotations.NotNull; +import us.talabrek.ultimateskyblock.challenge.ChallengeLogic; import us.talabrek.ultimateskyblock.challenge.Rank; -import us.talabrek.ultimateskyblock.uSkyBlock; import java.util.ArrayList; import java.util.List; @@ -14,16 +16,17 @@ * Rank name tab completer */ public class RankTabCompleter extends AbstractTabCompleter { - private final uSkyBlock plugin; + private final ChallengeLogic challengeLogic; - public RankTabCompleter(uSkyBlock plugin) { - this.plugin = plugin; + @Inject + public RankTabCompleter(@NotNull ChallengeLogic challengeLogic) { + this.challengeLogic = challengeLogic; } @Override protected List getTabList(CommandSender commandSender, String term) { List rankNames = new ArrayList<>(); - for (Rank rank : plugin.getChallengeLogic().getRanks()) { + for (Rank rank : challengeLogic.getRanks()) { rankNames.add(stripFormatting(rank.getRankKey())); } return rankNames; diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/completion/SchematicTabCompleter.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/completion/SchematicTabCompleter.java index 204e0b47c..d97b9a9fd 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/completion/SchematicTabCompleter.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/completion/SchematicTabCompleter.java @@ -1,5 +1,6 @@ package us.talabrek.ultimateskyblock.command.completion; +import com.google.inject.Inject; import dk.lockfuglsang.minecraft.command.completion.AbstractTabCompleter; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; @@ -13,6 +14,7 @@ public class SchematicTabCompleter extends AbstractTabCompleter { private final uSkyBlock plugin; + @Inject public SchematicTabCompleter(uSkyBlock plugin) { this.plugin = plugin; } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/AcceptRejectCommand.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/AcceptRejectCommand.java index 653f4d18e..0534a9d9c 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/AcceptRejectCommand.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/AcceptRejectCommand.java @@ -1,6 +1,8 @@ package us.talabrek.ultimateskyblock.command.island; +import com.google.inject.Inject; import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; import us.talabrek.ultimateskyblock.api.event.AcceptEvent; import us.talabrek.ultimateskyblock.api.event.RejectEvent; import us.talabrek.ultimateskyblock.uSkyBlock; @@ -13,7 +15,8 @@ public class AcceptRejectCommand extends RequirePlayerCommand { private final uSkyBlock plugin; - public AcceptRejectCommand(uSkyBlock plugin) { + @Inject + public AcceptRejectCommand(@NotNull uSkyBlock plugin) { super("accept|reject", "usb.party.join", marktr("accept/reject an invitation.")); this.plugin = plugin; } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/AutoCommand.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/AutoCommand.java index deae88d82..e65f2e779 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/AutoCommand.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/AutoCommand.java @@ -1,8 +1,10 @@ package us.talabrek.ultimateskyblock.command.island; +import com.google.inject.Inject; import dk.lockfuglsang.minecraft.command.AbstractCommand; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; import us.talabrek.ultimateskyblock.player.PlayerInfo; import us.talabrek.ultimateskyblock.uSkyBlock; @@ -18,7 +20,8 @@ public class AutoCommand extends AbstractCommand { private final CreateCommand create; private final HomeCommand home; - public AutoCommand(uSkyBlock plugin, CreateCommand create, HomeCommand home) { + @Inject + public AutoCommand(@NotNull uSkyBlock plugin, @NotNull CreateCommand create, @NotNull HomeCommand home) { super("auto", "usb.island.create", marktr("teleports you to your island (or create one)")); this.plugin = plugin; this.create = create; diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/BanCommand.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/BanCommand.java index a303b8a48..676767eed 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/BanCommand.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/BanCommand.java @@ -1,8 +1,10 @@ package us.talabrek.ultimateskyblock.command.island; +import com.google.inject.Inject; import org.bukkit.Bukkit; import org.bukkit.OfflinePlayer; import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; import us.talabrek.ultimateskyblock.handler.WorldGuardHandler; import us.talabrek.ultimateskyblock.island.IslandInfo; import us.talabrek.ultimateskyblock.player.PlayerInfo; @@ -14,7 +16,9 @@ import static dk.lockfuglsang.minecraft.po.I18nUtil.tr; public class BanCommand extends RequireIslandCommand { - public BanCommand(uSkyBlock plugin) { + + @Inject + public BanCommand(@NotNull uSkyBlock plugin) { super(plugin, "ban|unban", "usb.island.ban", "player", marktr("ban/unban a player from your island.")); addFeaturePermission("usb.exempt.ban", tr("exempts user from being banned")); } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/BiomeCommand.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/BiomeCommand.java index 51537754e..4519f2d9e 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/BiomeCommand.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/BiomeCommand.java @@ -1,73 +1,56 @@ package us.talabrek.ultimateskyblock.command.island; +import com.google.inject.Inject; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldguard.protection.regions.ProtectedRegion; import org.bukkit.Chunk; import org.bukkit.Location; +import org.bukkit.Registry; import org.bukkit.block.Biome; import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; import us.talabrek.ultimateskyblock.Settings; +import us.talabrek.ultimateskyblock.biome.BiomeConfig; +import us.talabrek.ultimateskyblock.biome.Biomes; import us.talabrek.ultimateskyblock.handler.WorldGuardHandler; import us.talabrek.ultimateskyblock.island.IslandInfo; import us.talabrek.ultimateskyblock.island.task.SetBiomeTask; -import us.talabrek.ultimateskyblock.menu.SkyBlockMenu; import us.talabrek.ultimateskyblock.player.PlayerInfo; import us.talabrek.ultimateskyblock.uSkyBlock; -import java.util.HashMap; +import java.time.Duration; import java.util.Map; import static dk.lockfuglsang.minecraft.po.I18nUtil.marktr; import static dk.lockfuglsang.minecraft.po.I18nUtil.tr; -import static us.talabrek.ultimateskyblock.util.BiomeUtil.getBiome; public class BiomeCommand extends RequireIslandCommand { - public static final Map BIOMES = new HashMap() { - { - put("ocean", Biome.OCEAN); - put("jungle", Biome.JUNGLE); - put("hell", Biome.NETHER); - put("sky", Biome.THE_END); - put("mushroom", Biome.MUSHROOM_FIELDS); - put("swampland", Biome.SWAMP); - put("taiga", Biome.SNOWY_TAIGA); - put("desert", Biome.DESERT); - put("forest", Biome.FOREST); - put("plains", Biome.PLAINS); - put("extreme_hills", Biome.DARK_FOREST_HILLS); - put("deep_ocean", Biome.DEEP_OCEAN); - Biome b = getBiome("ICE_PLAINS"); - if (b != null) { - put("ice_plains", b); - } - b = getBiome("FLOWER_FOREST"); - if (b != null) { - put("flower_forest", b); - } - } - }; - private final SkyBlockMenu menu; + private final Biomes biomes; + private final BiomeConfig biomeConfig; - public BiomeCommand(uSkyBlock plugin, SkyBlockMenu menu) { + @Inject + public BiomeCommand(@NotNull uSkyBlock plugin, @NotNull Biomes biomes, @NotNull BiomeConfig biomeConfig) { super(plugin, "biome|b", null, "biome ?radius", marktr("change the biome of the island")); - this.menu = menu; + this.biomes = biomes; + this.biomeConfig = biomeConfig; addFeaturePermission("usb.exempt.cooldown.biome", tr("exempt player from biome-cooldown")); - for (String biome : BIOMES.keySet()) { + for (String biome : biomeConfig.getConfiguredBiomeKeys()) { addFeaturePermission("usb.biome." + biome, tr("Let the player change their islands biome to {0}", biome.toUpperCase())); } } @Override + @SuppressWarnings("removal") protected boolean doExecute(String alias, final Player player, PlayerInfo pi, final IslandInfo island, Map data, final String... args) { if (args.length == 0) { if (!island.hasPerm(player, "canChangeBiome")) { player.sendMessage(tr("\u00a7cYou do not have permission to change the biome of your current island.")); } else { - player.openInventory(menu.displayBiomeGUI(player)); // Weird, that we show the UI + biomes.openBiomeGui(player, island); } } if (args.length >= 1) { - final String biome = args[0].toLowerCase(); + final String biomeKey = args[0].toLowerCase(); if (!island.hasPerm(player, "canChangeBiome")) { player.sendMessage(tr("\u00a74You do not have permission to change the biome of this island!")); return true; @@ -78,64 +61,65 @@ protected boolean doExecute(String alias, final Player player, PlayerInfo pi, fi player.sendMessage(tr("\u00a7eYou must be on your island to change the biome!")); return true; } - if (!biomeExists(biome)) { - player.sendMessage(tr("\u00a7cYou have misspelled the biome name. Must be one of {0}", BIOMES.keySet())); + Biome biome = Registry.BIOME.match(biomeKey); + if (biome == null) { + player.sendMessage(tr("\u00a7cYou have misspelled the biome name. Must be one of {0}", biomeConfig.getConfiguredBiomeKeys())); return true; } - int cooldown = plugin.getCooldownHandler().getCooldown(player, "biome"); - if (cooldown > 0) { - player.sendMessage(tr("\u00a7eYou can change your biome again in {0,number,#} minutes.", cooldown / 60)); + Duration cooldown = plugin.getCooldownHandler().getCooldown(player, "biome"); + if (cooldown.isPositive()) { + player.sendMessage(tr("\u00a7eYou can change your biome again in {0,number,#} seconds.", cooldown.toSeconds())); return true; } - if (!player.hasPermission("usb.biome." + biome.toLowerCase())) { + if (!player.hasPermission("usb.biome." + biomeKey.toLowerCase())) { player.sendMessage(tr("\u00a7cYou do not have permission to change your biome to that type.")); return true; } BlockVector3 minP = region.getMinimumPoint(); BlockVector3 maxP = region.getMaximumPoint(); + if (Settings.island_distance > Settings.island_protectionRange) { + int buffer = (Settings.island_distance - Settings.island_protectionRange) / 2; + minP.subtract(buffer, 0, buffer); + maxP.add(buffer, 0, buffer); + } + boolean changeEntireIslandBiome; + if (args.length == 2 && args[1].matches("[0-9]+")) { int radius = Integer.parseInt(args[1], 10); - Location loc = location.clone().add(-radius, 0, -radius); - if (region.contains(loc.getBlockX(), loc.getBlockY(), loc.getBlockZ())) { - minP = BlockVector3.at(loc.getBlockX(), loc.getBlockY(), loc.getBlockZ()); - } - loc = location.clone().add(radius, 0, radius); - if (region.contains(loc.getBlockX(), loc.getBlockY(), loc.getBlockZ())) { - maxP = BlockVector3.at(loc.getBlockX(), loc.getBlockY(), loc.getBlockZ()); - } - player.sendMessage(tr("\u00a77The pixies are busy changing the biome near you to \u00a79{0}\u00a77, be patient.", biome)); + minP = BlockVector3.at(Math.max(location.getBlockX() - radius, minP.getBlockX()), + Math.max(location.getBlockY() - radius, minP.getBlockY()), + Math.max(location.getBlockZ() - radius, minP.getBlockZ())); + maxP = BlockVector3.at(Math.min(location.getBlockX() + radius, maxP.getBlockX()), + Math.min(location.getBlockY() + radius, maxP.getBlockY()), + Math.min(location.getBlockZ() + radius, maxP.getBlockZ())); + changeEntireIslandBiome = false; + player.sendMessage(tr("\u00a77The pixies are busy changing the biome near you to \u00a79{0}\u00a77, be patient.", biomeKey)); } else if (args.length == 2 && args[1].equalsIgnoreCase("chunk")) { Chunk chunk = location.clone().getChunk(); - minP = BlockVector3.at(chunk.getX() << 4, 0, chunk.getZ() << 4); + minP = BlockVector3.at(chunk.getX() << 4, location.getWorld().getMinHeight(), chunk.getZ() << 4); maxP = BlockVector3.at((chunk.getX() << 4) + 15, location.getWorld().getMaxHeight(), (chunk.getZ() << 4) + 15); - player.sendMessage(tr("\u00a77The pixies are busy changing the biome in your current chunk to \u00a79{0}\u00a77, be patient.", biome)); + changeEntireIslandBiome = false; + player.sendMessage(tr("\u00a77The pixies are busy changing the biome in your current chunk to \u00a79{0}\u00a77, be patient.", biomeKey)); } else if (args.length < 2 || args[1].equalsIgnoreCase("all")) { - player.sendMessage(tr("\u00a77The pixies are busy changing the biome of your island to \u00a79{0}\u00a77, be patient.", biome)); - } - Biome biomeEnum = BIOMES.get(biome); - if (biomeEnum == null) { - player.sendMessage(tr("\u00a7eInvalid biome {0} supplied!", biome)); + changeEntireIslandBiome = true; + player.sendMessage(tr("\u00a77The pixies are busy changing the biome of your island to \u00a79{0}\u00a77, be patient.", biomeKey)); + } else { + player.sendMessage(tr("\u00a7cInvalid arguments. Use /is biome [radius|chunk|all]")); return true; } - new SetBiomeTask(plugin, player.getWorld(), minP, maxP, biomeEnum, () -> { - if (args.length == 1) { + new SetBiomeTask(plugin, player.getWorld(), minP, maxP, biome, () -> { + String biomeName = biome.getKey().getKey(); + if (changeEntireIslandBiome) { island.setBiome(biome); - player.sendMessage(tr("\u00a7aYou have changed your island''s biome to {0}", biome.toUpperCase())); - island.sendMessageToIslandGroup(true, marktr("{0} changed the island biome to {1}"), player.getName(), biome.toUpperCase()); + player.sendMessage(tr("\u00a7aYou have changed your island''s biome to {0}", biomeName)); + island.sendMessageToIslandGroup(true, marktr("{0} changed the island biome to {1}"), player.getName(), biomeName); plugin.getCooldownHandler().resetCooldown(player, "biome", Settings.general_biomeChange); } else { - player.sendMessage(tr("\u00a7aYou have changed {0} blocks around you to the {1} biome", args[1], biome.toUpperCase())); - island.sendMessageToIslandGroup(true, marktr("{0} created an area with {1} biome"), player.getName(), biome.toUpperCase()); + player.sendMessage(tr("\u00a7aYou have changed {0} blocks around you to the {1} biome", args[1], biomeName)); + island.sendMessageToIslandGroup(true, marktr("{0} created an area with {1} biome"), player.getName(), biomeName); } }).runTask(plugin); } return true; } - - public static boolean biomeExists(String biomeName) { - if (biomeName == null) { - return false; - } - return BIOMES.containsKey(biomeName.toLowerCase()); - } } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/BuyHopperLimitCommand.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/BuyHopperLimitCommand.java new file mode 100644 index 000000000..e840ebeb8 --- /dev/null +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/BuyHopperLimitCommand.java @@ -0,0 +1,64 @@ +package us.talabrek.ultimateskyblock.command.island; + +import com.google.inject.Inject; +import org.bukkit.entity.Player; +import us.talabrek.ultimateskyblock.island.IslandInfo; +import us.talabrek.ultimateskyblock.player.PlayerInfo; +import us.talabrek.ultimateskyblock.uSkyBlock; + +import java.util.Map; + +import static dk.lockfuglsang.minecraft.po.I18nUtil.marktr; +import static dk.lockfuglsang.minecraft.po.I18nUtil.tr; + +public class BuyHopperLimitCommand extends RequireIslandCommand { + + @Inject + public BuyHopperLimitCommand(uSkyBlock plugin) { + super(plugin, "hopper", "usb.island.create", "?oper", marktr("Pay to add hopper limits.")); + } + + private int calcPrice(int curlimit) { + int price; + if (curlimit <= 40) price = 25 * curlimit; + else if (curlimit >= 90) price = 10000; + else { + price = (int) (1000 * Math.pow(1.05, curlimit - 40)); + if (price > 10000) price = 10000; + } + return price; + } + + @Override + protected boolean doExecute(String alias, Player player, PlayerInfo pi, IslandInfo island, Map data, String... args) { + us.talabrek.ultimateskyblock.api.IslandInfo islandInfo = uSkyBlock.getInstance().getIslandInfo(player); + int curlimit = islandInfo.getHopperLimit(); + if (args.length > 0 && args[0].equals("buy")) { + int price = calcPrice(curlimit); + uSkyBlock.getInstance().getHookManager().getEconomyHook().ifPresent((hook) -> { + boolean success; + String result = hook.withdrawPlayer(player, price); + success = result == null; + if (success) { + islandInfo.setHopperLimit(curlimit + 1); + player.sendMessage(tr("\u00a7a\u00a7lBuy Extra Hopper Success!")); + player.sendMessage(tr("\u00a77======================")); + } else { + player.sendMessage(tr("\u00a7c\u00a7lBuy Extra Hopper Failed!")); + if (hook.getBalance(player) < price) { + player.sendMessage(tr("\u00a7dYou do not have enough money!")); + player.sendMessage(tr("\u00a7dYou have {0}", hook.getBalance(player))); + } + player.sendMessage(tr("\u00a7c {0}", result)); + } + }); + } else { + player.sendMessage(tr("\u00a7b\u00a7lBuy Extra Hopper Limit")); + player.sendMessage(tr("\u00a77======================")); + } + player.sendMessage(tr("\u00a7bCurrent Extra Limit is {0}", islandInfo.getHopperLimit())); + player.sendMessage(tr("\u00a7bPrice to buy another hopper limit is {0}", calcPrice(islandInfo.getHopperLimit()))); + plugin.execCommand(player, "console:tellraw " + player.getName() + " [{\"text\":\"click to buy hopper\",\"color\":\"green\",\"clickEvent\":{\"action\":\"run_command\",\"value\":\"/is hopper buy\"}}]", false); + return true; + } +} diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/CreateCommand.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/CreateCommand.java index d9401a724..962e5a963 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/CreateCommand.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/CreateCommand.java @@ -1,11 +1,15 @@ package us.talabrek.ultimateskyblock.command.island; +import com.google.inject.Inject; import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; import us.talabrek.ultimateskyblock.Settings; +import us.talabrek.ultimateskyblock.api.IslandInfo; import us.talabrek.ultimateskyblock.api.event.CreateIslandEvent; import us.talabrek.ultimateskyblock.player.PlayerInfo; import us.talabrek.ultimateskyblock.uSkyBlock; +import java.time.Duration; import java.util.Map; import static dk.lockfuglsang.minecraft.po.I18nUtil.marktr; @@ -14,7 +18,8 @@ public class CreateCommand extends RequirePlayerCommand { private final uSkyBlock plugin; - public CreateCommand(uSkyBlock plugin) { + @Inject + public CreateCommand(@NotNull uSkyBlock plugin) { super("create|c", "usb.island.create", "?schematic", marktr("create an island")); this.plugin = plugin; addFeaturePermission("usb.exempt.cooldown.create", tr("exempt player from create-cooldown")); @@ -23,23 +28,23 @@ public CreateCommand(uSkyBlock plugin) { @Override protected boolean doExecute(String alias, Player player, Map data, String... args) { PlayerInfo pi = plugin.getPlayerInfo(player); - int cooldown = plugin.getCooldownHandler().getCooldown(player, "restart"); - if (!pi.getHasIsland() && cooldown == 0) { + Duration cooldown = plugin.getCooldownHandler().getCooldown(player, "restart"); + if (!pi.getHasIsland() && cooldown.isZero()) { String cSchem = args != null && args.length > 0 ? args[0] : Settings.island_schematicName; plugin.getServer().getPluginManager().callEvent(new CreateIslandEvent(player, cSchem)); } else if (pi.getHasIsland()) { - us.talabrek.ultimateskyblock.api.IslandInfo island = plugin.getIslandInfo(pi); + IslandInfo island = plugin.getIslandInfo(pi); if (island.isLeader(player)) { player.sendMessage(tr("\u00a74Island found!" + - "\u00a7e You already have an island. If you want a fresh island, type" + - "\u00a7b /is restart\u00a7e to get one")); + "\u00a7e You already have an island. If you want a fresh island, type" + + "\u00a7b /is restart\u00a7e to get one")); } else { player.sendMessage(tr("\u00a74Island found!" + - "\u00a7e You are already a member of an island. To start your own, first" + - "\u00a7b /is leave")); + "\u00a7e You are already a member of an island. To start your own, first" + + "\u00a7b /is leave")); } } else { - player.sendMessage(tr("\u00a7eYou can create a new island in {0,number,#} seconds.", cooldown)); + player.sendMessage(tr("\u00a7eYou can create a new island in {0,number,#} seconds.", cooldown.toSeconds())); } return true; } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/FarewellCommand.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/FarewellCommand.java new file mode 100644 index 000000000..202b99c41 --- /dev/null +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/FarewellCommand.java @@ -0,0 +1,34 @@ +package us.talabrek.ultimateskyblock.command.island; + +import com.google.inject.Inject; +import org.bukkit.entity.Player; +import us.talabrek.ultimateskyblock.island.IslandInfo; +import us.talabrek.ultimateskyblock.player.PlayerInfo; +import us.talabrek.ultimateskyblock.uSkyBlock; + +import java.util.Map; + +import static dk.lockfuglsang.minecraft.po.I18nUtil.marktr; +import static dk.lockfuglsang.minecraft.po.I18nUtil.tr; + +public class FarewellCommand extends RequireIslandCommand { + @Inject + public FarewellCommand(uSkyBlock plugin) { + super(plugin, "farewell", null, "?msg", marktr("Change farewell message of your island")); + } + + @Override + protected boolean doExecute(String alias, Player player, PlayerInfo pi, IslandInfo island, Map data, String... args) { + us.talabrek.ultimateskyblock.api.IslandInfo islandInfo = uSkyBlock.getInstance().getIslandInfo(player); + StringBuilder str= new StringBuilder(); + for (String arg:args){ + str.append(arg).append(" "); + } + String cmd = "console:rg flag -w skyworld " + islandInfo.getName() + "island farewell "+str; + uSkyBlock.getInstance().execCommand(player, cmd,false); + + str = new StringBuilder(str.toString().replaceAll("&(?=[0-9a-fA-FkKlLmMnNoOrR])", "\u00a7")); + player.sendMessage(tr("\u00a7bYour island farewell message has changed to: \u00a7r")+str); + return true; + } +} diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/GreetingCommand.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/GreetingCommand.java new file mode 100644 index 000000000..20541de08 --- /dev/null +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/GreetingCommand.java @@ -0,0 +1,37 @@ +package us.talabrek.ultimateskyblock.command.island; + +import com.google.inject.Inject; +import dk.lockfuglsang.minecraft.command.AbstractCommand; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import us.talabrek.ultimateskyblock.api.IslandInfo; +import us.talabrek.ultimateskyblock.player.PlayerInfo; +import us.talabrek.ultimateskyblock.uSkyBlock; + +import java.util.Map; + +import static dk.lockfuglsang.minecraft.po.I18nUtil.marktr; +import static dk.lockfuglsang.minecraft.po.I18nUtil.tr; + +public class GreetingCommand extends RequireIslandCommand { + @Inject + public GreetingCommand(@NotNull uSkyBlock plugin) { + super(plugin, "greeting", null, "?msg", marktr("Change greeting message of your island")); + } + + @Override + protected boolean doExecute(String alias, Player player, PlayerInfo pi, us.talabrek.ultimateskyblock.island.IslandInfo island, Map data, String... args) { + IslandInfo islandInfo = uSkyBlock.getInstance().getIslandInfo(player); + StringBuilder str= new StringBuilder(); + for (String arg:args){ + str.append(arg).append(" "); + } + String cmd = "console:rg flag -w skyworld " + islandInfo.getName() + "island greeting "+str; + uSkyBlock.getInstance().execCommand(player, cmd,false); + + str = new StringBuilder(str.toString().replaceAll("&(?=[0-9a-fA-FkKlLmMnNoOrR])", "\u00a7")); + player.sendMessage(tr("\u00a7bYour island greeting message has changed to: \u00a7r")+str); + return true; + } +} diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/HomeCommand.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/HomeCommand.java index cb318c741..205098a57 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/HomeCommand.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/HomeCommand.java @@ -1,7 +1,9 @@ package us.talabrek.ultimateskyblock.command.island; +import com.google.inject.Inject; import dk.lockfuglsang.minecraft.po.I18nUtil; import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; import us.talabrek.ultimateskyblock.island.IslandInfo; import us.talabrek.ultimateskyblock.player.PlayerInfo; import us.talabrek.ultimateskyblock.uSkyBlock; @@ -11,7 +13,9 @@ import static dk.lockfuglsang.minecraft.po.I18nUtil.marktr; public class HomeCommand extends RequireIslandCommand { - public HomeCommand(uSkyBlock plugin) { + + @Inject + public HomeCommand(@NotNull uSkyBlock plugin) { super(plugin, "home|h", "usb.island.home", marktr("teleport to the island home")); } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/InfoCommand.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/InfoCommand.java index cb76941ad..88b19bc48 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/InfoCommand.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/InfoCommand.java @@ -1,25 +1,33 @@ package us.talabrek.ultimateskyblock.command.island; +import com.google.inject.Inject; +import dk.lockfuglsang.minecraft.util.ItemStackUtil; import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; import us.talabrek.ultimateskyblock.Settings; import us.talabrek.ultimateskyblock.api.async.Callback; import us.talabrek.ultimateskyblock.api.model.BlockScore; -import us.talabrek.ultimateskyblock.handler.VaultHandler; +import us.talabrek.ultimateskyblock.api.model.IslandScore; import us.talabrek.ultimateskyblock.island.IslandInfo; import us.talabrek.ultimateskyblock.player.PatienceTester; import us.talabrek.ultimateskyblock.player.PlayerInfo; import us.talabrek.ultimateskyblock.uSkyBlock; -import us.talabrek.ultimateskyblock.util.LogUtil; import java.util.Map; import java.util.logging.Level; +import java.util.logging.Logger; import static dk.lockfuglsang.minecraft.po.I18nUtil.marktr; import static dk.lockfuglsang.minecraft.po.I18nUtil.tr; public class InfoCommand extends RequireIslandCommand { - public InfoCommand(uSkyBlock plugin) { + + private final Logger logger; + + @Inject + public InfoCommand(@NotNull uSkyBlock plugin, @NotNull Logger logger) { super(plugin, "info", "usb.island.info", "?island", marktr("check your or another''s island info")); + this.logger = logger; addFeaturePermission("usb.island.info.other", tr("allows user to see others island info")); } @@ -65,7 +73,7 @@ public boolean getIslandInfo(final Player player, final String islandPlayer, fin return false; } final PlayerInfo playerInfo = islandPlayer.equals(player.getName()) ? plugin.getPlayerInfo(player) : plugin.getPlayerInfo(islandPlayer); - final Callback showInfo = new Callback() { + final Callback showInfo = new Callback<>() { @Override public void run() { if (player.isOnline()) { @@ -82,8 +90,8 @@ public void run() { player.sendMessage(tr("Score Count Block")); for (BlockScore score : getState().getTop((currentPage - 1) * 10, 10)) { player.sendMessage(score.getState().getColor() + tr("{0,number,00.00} {1,number,#} {2}", - score.getScore(), score.getCount(), - VaultHandler.getItemName(score.getBlock()))); + score.getScore(), score.getCount(), + ItemStackUtil.getBlockName(score.getBlockData()))); } player.sendMessage(tr("\u00a7aIsland level is {0,number,###.##}", getState().getScore())); } @@ -91,16 +99,12 @@ public void run() { PatienceTester.stopRunning(player, "usb.island.info.active"); } }; - plugin.sync(() -> { - try { - PatienceTester.startRunning(player, "usb.island.info.active"); - plugin.calculateScoreAsync(player, playerInfo.locationForParty(), showInfo); - } catch (Exception e) { - LogUtil.log(Level.SEVERE, "Error while calculating Island Level", e); - } - }, 1L); + try { + PatienceTester.startRunning(player, "usb.island.info.active"); + plugin.calculateScoreAsync(player, playerInfo.locationForParty(), showInfo); + } catch (Exception e) { + logger.log(Level.SEVERE, "Error while calculating Island Level", e); + } return true; } - - -} \ No newline at end of file +} diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/InviteCommand.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/InviteCommand.java index cf5188ce2..6e1c3a645 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/InviteCommand.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/InviteCommand.java @@ -1,10 +1,11 @@ package us.talabrek.ultimateskyblock.command.island; +import com.google.inject.Inject; import dk.lockfuglsang.minecraft.po.I18nUtil; import org.bukkit.Bukkit; import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; import us.talabrek.ultimateskyblock.api.event.InviteEvent; -import us.talabrek.ultimateskyblock.command.InviteHandler; import us.talabrek.ultimateskyblock.island.IslandInfo; import us.talabrek.ultimateskyblock.player.PlayerInfo; import us.talabrek.ultimateskyblock.uSkyBlock; @@ -14,11 +15,10 @@ import static dk.lockfuglsang.minecraft.po.I18nUtil.marktr; public class InviteCommand extends RequireIslandCommand { - private final InviteHandler inviteHandler; - public InviteCommand(uSkyBlock plugin, InviteHandler inviteHandler) { + @Inject + public InviteCommand(@NotNull uSkyBlock plugin) { super(plugin, "invite", "usb.party.invite", "oplayer", marktr("invite a player to your island")); - this.inviteHandler = inviteHandler; } @Override @@ -40,7 +40,6 @@ protected boolean doExecute(String alias, Player player, PlayerInfo pi, IslandIn } } if (args.length == 1) { - //noinspection deprecation Player otherPlayer = Bukkit.getPlayer(args[0]); if (!island.hasPerm(player, "canInviteOthers")) { player.sendMessage(I18nUtil.tr("\u00a74You do not have permission to invite others to this island!")); diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/KickCommand.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/KickCommand.java index 8f11f146f..77486ffee 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/KickCommand.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/KickCommand.java @@ -1,8 +1,10 @@ package us.talabrek.ultimateskyblock.command.island; +import com.google.inject.Inject; import dk.lockfuglsang.minecraft.po.I18nUtil; import org.bukkit.Bukkit; import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; import us.talabrek.ultimateskyblock.island.IslandInfo; import us.talabrek.ultimateskyblock.player.PlayerInfo; import us.talabrek.ultimateskyblock.uSkyBlock; @@ -11,9 +13,10 @@ import static dk.lockfuglsang.minecraft.po.I18nUtil.marktr; -@SuppressWarnings("deprecation") public class KickCommand extends RequireIslandCommand { - public KickCommand(uSkyBlock plugin) { + + @Inject + public KickCommand(@NotNull uSkyBlock plugin) { super(plugin, "kick|remove", "usb.party.kick", "player", marktr("remove a member from your island.")); } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/LeaveCommand.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/LeaveCommand.java index 24de8e602..bd1c515eb 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/LeaveCommand.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/LeaveCommand.java @@ -1,7 +1,9 @@ package us.talabrek.ultimateskyblock.command.island; +import com.google.inject.Inject; import org.bukkit.Bukkit; import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; import us.talabrek.ultimateskyblock.island.IslandInfo; import us.talabrek.ultimateskyblock.player.PlayerInfo; import us.talabrek.ultimateskyblock.uSkyBlock; @@ -11,10 +13,10 @@ import static dk.lockfuglsang.minecraft.po.I18nUtil.marktr; import static dk.lockfuglsang.minecraft.po.I18nUtil.tr; -@SuppressWarnings("deprecation") public class LeaveCommand extends RequireIslandCommand { - public LeaveCommand(uSkyBlock plugin) { + @Inject + public LeaveCommand(@NotNull uSkyBlock plugin) { super(plugin, "leave", "usb.party.leave", marktr("leave your party")); } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/LevelCommand.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/LevelCommand.java index 55baa93b0..62b6cb407 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/LevelCommand.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/LevelCommand.java @@ -1,6 +1,8 @@ package us.talabrek.ultimateskyblock.command.island; +import com.google.inject.Inject; import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; import us.talabrek.ultimateskyblock.Settings; import us.talabrek.ultimateskyblock.api.IslandRank; import us.talabrek.ultimateskyblock.api.async.Callback; @@ -14,7 +16,9 @@ import static dk.lockfuglsang.minecraft.po.I18nUtil.tr; public class LevelCommand extends RequireIslandCommand { - public LevelCommand(uSkyBlock plugin) { + + @Inject + public LevelCommand(@NotNull uSkyBlock plugin) { super(plugin, "level", "usb.island.level", "?island", marktr("check your or anothers island level")); addFeaturePermission("usb.island.level.other", tr("allows user to query for others levels")); } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/LockUnlockCommand.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/LockUnlockCommand.java index c5a7604d3..634624b63 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/LockUnlockCommand.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/LockUnlockCommand.java @@ -1,6 +1,8 @@ package us.talabrek.ultimateskyblock.command.island; +import com.google.inject.Inject; import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; import us.talabrek.ultimateskyblock.Settings; import us.talabrek.ultimateskyblock.island.IslandInfo; import us.talabrek.ultimateskyblock.player.PlayerInfo; @@ -12,7 +14,9 @@ import static dk.lockfuglsang.minecraft.po.I18nUtil.tr; public class LockUnlockCommand extends RequireIslandCommand { - public LockUnlockCommand(uSkyBlock plugin) { + + @Inject + public LockUnlockCommand(@NotNull uSkyBlock plugin) { super(plugin, "lock|unlock", "usb.island.lock", marktr("lock your island to non-party members.")); } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/LogCommand.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/LogCommand.java index afeb53a92..6be8bacc0 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/LogCommand.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/LogCommand.java @@ -1,5 +1,6 @@ package us.talabrek.ultimateskyblock.command.island; +import com.google.inject.Inject; import org.bukkit.entity.Player; import us.talabrek.ultimateskyblock.island.IslandInfo; import us.talabrek.ultimateskyblock.menu.SkyBlockMenu; @@ -13,6 +14,7 @@ public class LogCommand extends RequireIslandCommand { private final SkyBlockMenu menu; + @Inject public LogCommand(uSkyBlock plugin, SkyBlockMenu menu) { super(plugin, "log|l", "usb.island.log", marktr("display log")); this.menu = menu; diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/MakeLeaderCommand.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/MakeLeaderCommand.java index 2e222d004..f1148a4e5 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/MakeLeaderCommand.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/MakeLeaderCommand.java @@ -1,6 +1,8 @@ package us.talabrek.ultimateskyblock.command.island; +import com.google.inject.Inject; import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; import us.talabrek.ultimateskyblock.handler.WorldGuardHandler; import us.talabrek.ultimateskyblock.island.IslandInfo; import us.talabrek.ultimateskyblock.player.PlayerInfo; @@ -12,7 +14,9 @@ import static dk.lockfuglsang.minecraft.po.I18nUtil.tr; public class MakeLeaderCommand extends RequireIslandCommand { - public MakeLeaderCommand(uSkyBlock plugin) { + + @Inject + public MakeLeaderCommand(@NotNull uSkyBlock plugin) { super(plugin, "makeleader|transfer", "usb.island.makeleader", "member", marktr("transfer leadership to another member")); } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/MobLimitCommand.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/MobLimitCommand.java index c1af011e7..ff77fb556 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/MobLimitCommand.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/MobLimitCommand.java @@ -1,6 +1,8 @@ package us.talabrek.ultimateskyblock.command.island; +import com.google.inject.Inject; import org.bukkit.command.CommandSender; +import org.jetbrains.annotations.NotNull; import us.talabrek.ultimateskyblock.command.admin.AbstractIslandInfoCommand; import us.talabrek.ultimateskyblock.island.IslandInfo; import us.talabrek.ultimateskyblock.player.PlayerInfo; @@ -11,7 +13,8 @@ public class MobLimitCommand extends AbstractIslandInfoCommand { private final uSkyBlock plugin; - public MobLimitCommand(uSkyBlock plugin) { + @Inject + public MobLimitCommand(@NotNull uSkyBlock plugin) { super("limits", "usb.island.limit", marktr("show the islands limits")); this.plugin = plugin; } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/PartyCommand.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/PartyCommand.java index ac3e7f899..e15824b23 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/PartyCommand.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/PartyCommand.java @@ -1,10 +1,12 @@ package us.talabrek.ultimateskyblock.command.island; +import com.google.inject.Inject; import dk.lockfuglsang.minecraft.command.AbstractCommand; import dk.lockfuglsang.minecraft.command.CompositeCommand; import dk.lockfuglsang.minecraft.po.I18nUtil; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; import us.talabrek.ultimateskyblock.command.InviteHandler; import us.talabrek.ultimateskyblock.island.IslandInfo; import us.talabrek.ultimateskyblock.menu.SkyBlockMenu; @@ -20,7 +22,8 @@ public class PartyCommand extends CompositeCommand { private final uSkyBlock plugin; private final SkyBlockMenu menu; - public PartyCommand(final uSkyBlock plugin, SkyBlockMenu menu, final InviteHandler inviteHandler) { + @Inject + public PartyCommand(@NotNull uSkyBlock plugin, @NotNull SkyBlockMenu menu, @NotNull InviteHandler inviteHandler) { super("party", null, marktr("show party information")); this.plugin = plugin; this.menu = menu; diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/PermCommand.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/PermCommand.java index 30a50f129..5e3228bb6 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/PermCommand.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/PermCommand.java @@ -1,6 +1,8 @@ package us.talabrek.ultimateskyblock.command.island; +import com.google.inject.Inject; import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; import us.talabrek.ultimateskyblock.island.IslandInfo; import us.talabrek.ultimateskyblock.menu.PartyPermissionMenuItem; import us.talabrek.ultimateskyblock.player.PlayerInfo; @@ -14,7 +16,9 @@ import static dk.lockfuglsang.minecraft.po.I18nUtil.tr; public class PermCommand extends RequireIslandCommand { - public PermCommand(uSkyBlock plugin) { + + @Inject + public PermCommand(@NotNull uSkyBlock plugin) { super(plugin, "perm", "usb.island.perm", "member ?perm", marktr("changes a members island-permissions")); } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/RestartCommand.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/RestartCommand.java index f038a88fd..a343a4d79 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/RestartCommand.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/RestartCommand.java @@ -1,19 +1,24 @@ package us.talabrek.ultimateskyblock.command.island; +import com.google.inject.Inject; import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; import us.talabrek.ultimateskyblock.Settings; import us.talabrek.ultimateskyblock.api.event.RestartIslandEvent; import us.talabrek.ultimateskyblock.island.IslandInfo; import us.talabrek.ultimateskyblock.player.PlayerInfo; import us.talabrek.ultimateskyblock.uSkyBlock; +import java.time.Duration; import java.util.Map; import static dk.lockfuglsang.minecraft.po.I18nUtil.marktr; import static dk.lockfuglsang.minecraft.po.I18nUtil.tr; public class RestartCommand extends RequireIslandCommand { - public RestartCommand(uSkyBlock plugin) { + + @Inject + public RestartCommand(@NotNull uSkyBlock plugin) { super(plugin, "restart|reset", "usb.island.restart", "?schematic", marktr("delete your island and start a new one.")); addFeaturePermission("usb.exempt.cooldown.restart", tr("exempt player from restart-cooldown")); } @@ -28,9 +33,9 @@ protected boolean doExecute(String alias, Player player, PlayerInfo pi, IslandIn } return true; } - int cooldown = plugin.getCooldownHandler().getCooldown(player, "restart"); - if (cooldown > 0) { - player.sendMessage(tr("\u00a7cYou can restart your island in {0} seconds.", cooldown)); + Duration cooldown = plugin.getCooldownHandler().getCooldown(player, "restart"); + if (cooldown.isPositive()) { + player.sendMessage(tr("\u00a7cYou can restart your island in {0} seconds.", cooldown.toSeconds())); return true; } else { if (pi.isIslandGenerating()) { diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/SetHomeCommand.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/SetHomeCommand.java index 5dddcf15a..f904a01e7 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/SetHomeCommand.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/SetHomeCommand.java @@ -1,21 +1,42 @@ package us.talabrek.ultimateskyblock.command.island; +import com.google.inject.Inject; import org.bukkit.entity.Player; import us.talabrek.ultimateskyblock.island.IslandInfo; import us.talabrek.ultimateskyblock.player.PlayerInfo; import us.talabrek.ultimateskyblock.uSkyBlock; +import us.talabrek.ultimateskyblock.util.LocationUtil; import java.util.Map; import static dk.lockfuglsang.minecraft.po.I18nUtil.marktr; +import static dk.lockfuglsang.minecraft.po.I18nUtil.tr; public class SetHomeCommand extends RequireIslandCommand { + + @Inject public SetHomeCommand(uSkyBlock plugin) { super(plugin, "sethome|tpset", "usb.island.sethome", marktr("set the island-home")); } @Override protected boolean doExecute(String alias, Player player, PlayerInfo pi, IslandInfo island, Map data, String... args) { - return plugin.homeSet(player); + if (!player.getWorld().getName().equalsIgnoreCase(plugin.getWorldManager().getWorld().getName())) { + player.sendMessage(tr("\u00a74You must be closer to your island to set your skyblock home!")); + return true; + } + if (!plugin.playerIsOnOwnIsland(player)) { + player.sendMessage(tr("\u00a74You must be closer to your island to set your skyblock home!")); + return true; + } + if (pi == null || !LocationUtil.isSafeLocation(player.getLocation())) { + player.sendMessage(tr("\u00a74Your current location is not a safe home-location.")); + return true; + } + + pi.setHomeLocation(player.getLocation()); + pi.save(); + player.sendMessage(tr("\u00a7aYour skyblock home has been set to your current location.")); + return true; } } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/SetWarpCommand.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/SetWarpCommand.java index f5f9f4eae..043b5edd4 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/SetWarpCommand.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/SetWarpCommand.java @@ -1,7 +1,9 @@ package us.talabrek.ultimateskyblock.command.island; +import com.google.inject.Inject; import dk.lockfuglsang.minecraft.po.I18nUtil; import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; import us.talabrek.ultimateskyblock.island.IslandInfo; import us.talabrek.ultimateskyblock.player.PlayerInfo; import us.talabrek.ultimateskyblock.uSkyBlock; @@ -11,7 +13,9 @@ import static dk.lockfuglsang.minecraft.po.I18nUtil.marktr; public class SetWarpCommand extends RequireIslandCommand { - public SetWarpCommand(uSkyBlock plugin) { + + @Inject + public SetWarpCommand(@NotNull uSkyBlock plugin) { super(plugin, "setwarp|warpset", "usb.island.setwarp", marktr("set your island''s warp location")); } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/SpawnCommand.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/SpawnCommand.java index 8f8156b9c..003b5af65 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/SpawnCommand.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/SpawnCommand.java @@ -1,6 +1,8 @@ package us.talabrek.ultimateskyblock.command.island; +import com.google.inject.Inject; import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; import us.talabrek.ultimateskyblock.uSkyBlock; import java.util.Map; @@ -13,7 +15,8 @@ public class SpawnCommand extends RequirePlayerCommand { private final uSkyBlock plugin; - public SpawnCommand(uSkyBlock plugin) { + @Inject + public SpawnCommand(@NotNull uSkyBlock plugin) { super("spawn", "usb.island.spawn", marktr("teleports you to the skyblock spawn")); this.plugin = plugin; } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/ToggleWarp.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/ToggleWarp.java index f36002c6d..8dc21e4fa 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/ToggleWarp.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/ToggleWarp.java @@ -1,5 +1,6 @@ package us.talabrek.ultimateskyblock.command.island; +import com.google.inject.Inject; import org.bukkit.entity.Player; import us.talabrek.ultimateskyblock.island.IslandInfo; import us.talabrek.ultimateskyblock.player.PlayerInfo; @@ -11,6 +12,8 @@ import static dk.lockfuglsang.minecraft.po.I18nUtil.tr; public class ToggleWarp extends RequireIslandCommand { + + @Inject public ToggleWarp(uSkyBlock plugin) { super(plugin, "togglewarp|tw", "usb.island.togglewarp", marktr("enable/disable warping to your island.")); } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/TopCommand.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/TopCommand.java index f4845569c..c2f2fbfd4 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/TopCommand.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/TopCommand.java @@ -1,7 +1,9 @@ package us.talabrek.ultimateskyblock.command.island; +import com.google.inject.Inject; import dk.lockfuglsang.minecraft.command.AbstractCommand; import org.bukkit.command.CommandSender; +import org.jetbrains.annotations.NotNull; import us.talabrek.ultimateskyblock.uSkyBlock; import java.util.Map; @@ -12,7 +14,8 @@ public class TopCommand extends AbstractCommand { private final uSkyBlock plugin; - public TopCommand(uSkyBlock plugin) { + @Inject + public TopCommand(@NotNull uSkyBlock plugin) { super("top", "usb.island.top", "?page", marktr("display the top10 of islands")); this.plugin = plugin; addFeaturePermission("usb.admin.topten", tr("enables user to all-ways generate top-ten (no caching)")); diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/TrustCommand.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/TrustCommand.java index 044062621..fd36e3a18 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/TrustCommand.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/TrustCommand.java @@ -1,8 +1,10 @@ package us.talabrek.ultimateskyblock.command.island; +import com.google.inject.Inject; import org.bukkit.Bukkit; import org.bukkit.OfflinePlayer; import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; import us.talabrek.ultimateskyblock.handler.WorldGuardHandler; import us.talabrek.ultimateskyblock.island.IslandInfo; import us.talabrek.ultimateskyblock.player.PlayerInfo; @@ -16,12 +18,20 @@ import static dk.lockfuglsang.minecraft.po.I18nUtil.tr; public class TrustCommand extends RequireIslandCommand { - public TrustCommand(uSkyBlock plugin) { + + @Inject + public TrustCommand(@NotNull uSkyBlock plugin) { super(plugin, "trust|untrust", "usb.island.trust", "?player", marktr("trust/untrust a player to help on your island.")); } @Override protected boolean doExecute(final String alias, final Player player, final PlayerInfo pi, final IslandInfo island, Map data, String... args) { + PlayerInfo playerInfo = plugin.getPlayerInfo(player); + boolean isFirstCompletion = playerInfo.checkChallenge("page1finished") == 0; + if(isFirstCompletion){ + player.sendMessage(tr("\u00a7c你现在没有权限信任他,请先完成第一页任务")); + return true; + } if (args.length == 0) { player.sendMessage(tr("\u00a7eThe following players are trusted on your island:")); player.sendMessage(tr("\u00a74{0}", island.getTrustees())); @@ -42,6 +52,12 @@ protected boolean doExecute(final String alias, final Player player, final Playe return true; } if (alias.equals("trust")) { + PlayerInfo pinfo = plugin.getPlayerInfo(offlinePlayer.getUniqueId()); + boolean pii = pinfo.checkChallenge("page1finished") == 0; + if(pii){ + player.sendMessage(tr("\u00a7c你现在没有权限信任他,请先完成第一页任务")); + return true; + } island.trustPlayer(offlinePlayer, player); if (offlinePlayer.isOnline()) { offlinePlayer.getPlayer().sendMessage(tr("\u00a7eYou are now trusted on \u00a74{0}''s \u00a7eisland.", pi.getDisplayName())); diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/WarpCommand.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/WarpCommand.java index ee4ad5823..d5c03fd4f 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/WarpCommand.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/command/island/WarpCommand.java @@ -1,6 +1,8 @@ package us.talabrek.ultimateskyblock.command.island; +import com.google.inject.Inject; import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; import us.talabrek.ultimateskyblock.island.IslandInfo; import us.talabrek.ultimateskyblock.player.PlayerInfo; import us.talabrek.ultimateskyblock.uSkyBlock; @@ -14,7 +16,8 @@ public class WarpCommand extends RequirePlayerCommand { private final uSkyBlock plugin; - public WarpCommand(uSkyBlock plugin) { + @Inject + public WarpCommand(@NotNull uSkyBlock plugin) { super("warp|w", "usb.island.warp", "island", marktr("warp to another player''s island")); this.plugin = plugin; } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/event/ExploitEvents.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/event/ExploitEvents.java index 667b08013..ffb745c1a 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/event/ExploitEvents.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/event/ExploitEvents.java @@ -1,5 +1,7 @@ package us.talabrek.ultimateskyblock.event; +import com.google.inject.Inject; +import com.google.inject.Singleton; import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; @@ -12,6 +14,7 @@ import org.bukkit.event.vehicle.VehicleDamageEvent; import org.bukkit.event.vehicle.VehicleDestroyEvent; import org.bukkit.event.vehicle.VehicleEnterEvent; +import org.jetbrains.annotations.NotNull; import us.talabrek.ultimateskyblock.uSkyBlock; import static dk.lockfuglsang.minecraft.po.I18nUtil.tr; @@ -19,6 +22,7 @@ /** * This class is handling the event listeners for stopping various exploits. */ +@Singleton public class ExploitEvents implements Listener { private final uSkyBlock plugin; @@ -28,7 +32,8 @@ public class ExploitEvents implements Listener { private final boolean breakVehicleEnabled; private final boolean anyVillagerTradingAllowed; - public ExploitEvents(uSkyBlock plugin) { + @Inject + public ExploitEvents(@NotNull uSkyBlock plugin) { this.plugin = plugin; FileConfiguration config = plugin.getConfig(); visitorVillagerTradingProtected = config.getBoolean("options.protection.visitors.villager-trading", true); diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/event/GriefEvents.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/event/GriefEvents.java index 01620c482..84b93d355 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/event/GriefEvents.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/event/GriefEvents.java @@ -1,19 +1,13 @@ package us.talabrek.ultimateskyblock.event; +import com.google.inject.Inject; +import com.google.inject.Singleton; import dk.lockfuglsang.minecraft.po.I18nUtil; import org.bukkit.Location; import org.bukkit.Material; +import org.bukkit.NamespacedKey; import org.bukkit.configuration.file.FileConfiguration; -import org.bukkit.entity.Animals; -import org.bukkit.entity.Creature; -import org.bukkit.entity.Creeper; -import org.bukkit.entity.Entity; -import org.bukkit.entity.Monster; -import org.bukkit.entity.Player; -import org.bukkit.entity.Projectile; -import org.bukkit.entity.TNTPrimed; -import org.bukkit.entity.Wither; -import org.bukkit.entity.WitherSkull; +import org.bukkit.entity.*; import org.bukkit.event.Cancellable; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; @@ -25,17 +19,19 @@ import org.bukkit.event.player.PlayerEggThrowEvent; import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.event.player.PlayerShearEntityEvent; +import org.bukkit.persistence.PersistentDataContainer; +import org.bukkit.persistence.PersistentDataType; import org.bukkit.projectiles.ProjectileSource; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import us.talabrek.ultimateskyblock.handler.WorldGuardHandler; import us.talabrek.ultimateskyblock.island.IslandInfo; import us.talabrek.ultimateskyblock.uSkyBlock; -import java.text.MessageFormat; -import java.text.ParseException; - /** * Handling of mob-related events. */ +@Singleton public class GriefEvents implements Listener { private final uSkyBlock plugin; @@ -47,7 +43,8 @@ public class GriefEvents implements Listener { private final boolean witherEnabled; private final boolean hatchingEnabled; - public GriefEvents(uSkyBlock plugin) { + @Inject + public GriefEvents(@NotNull uSkyBlock plugin) { this.plugin = plugin; FileConfiguration config = plugin.getConfig(); creeperEnabled = config.getBoolean("options.protection.creepers", true); @@ -68,11 +65,6 @@ public void onCreeperExplode(ExplosionPrimeEvent event) { && !isValidTarget(((Creeper)event.getEntity()).getTarget())) { event.setCancelled(true); - } else if (event.getEntity() instanceof TNTPrimed) { - TNTPrimed tntPrimed = (TNTPrimed) event.getEntity(); - if (tntPrimed.getSource() instanceof Player && !isValidTarget(tntPrimed.getSource())) { - event.setCancelled(true); - } } } @@ -128,7 +120,8 @@ public void onEntityDamage(EntityDamageByEntityEvent event) { private void cancelMobDamage(EntityDamageByEntityEvent event) { if (killAnimalsEnabled && event.getEntity() instanceof Animals) { event.setCancelled(true); - } else if (killMonstersEnabled && event.getEntity() instanceof Monster) { + } else if (killMonstersEnabled && (event.getEntity() instanceof Monster + || event.getEntity() instanceof Shulker)) { event.setCancelled(true); } } @@ -171,39 +164,34 @@ public void onWitherSkullExplosion(EntityDamageByEntityEvent e) { } } - private void handleWitherRampage(Cancellable e, Wither shooter, Location targetLocation) { - String islandName = getOwningIsland(shooter); + private void handleWitherRampage(Cancellable event, Wither shooter, Location targetLocation) { + String withersIsland = getOwningIsland(shooter); String targetIsland = WorldGuardHandler.getIslandNameAt(targetLocation); - if (targetIsland == null || !targetIsland.equals(islandName)) { - e.setCancelled(true); - checkWitherLeash(shooter, islandName); + if (targetIsland == null || !targetIsland.equals(withersIsland)) { + event.setCancelled(true); + checkWitherLeash(shooter, withersIsland); } } - private void checkWitherLeash(Wither shooter, String islandName) { + private void checkWitherLeash(@NotNull Wither shooter, @Nullable String withersIsland) { String currentIsland = WorldGuardHandler.getIslandNameAt(shooter.getLocation()); - if (currentIsland == null || !currentIsland.equals(islandName)) { + if (currentIsland == null || !currentIsland.equals(withersIsland)) { shooter.remove(); - IslandInfo islandInfo = plugin.getIslandInfo(islandName); + IslandInfo islandInfo = plugin.getIslandInfo(withersIsland); if (islandInfo != null) { islandInfo.sendMessageToOnlineMembers(I18nUtil.tr("\u00a7cWither Despawned!\u00a7e It wandered too far from your island.")); } } } - private String getOwningIsland(Wither wither) { - if (wither.hasMetadata("fromIsland")) { - return wither.getMetadata("fromIsland").get(0).asString(); + private @Nullable String getOwningIsland(@NotNull Wither wither) { + PersistentDataContainer container = wither.getPersistentDataContainer(); + NamespacedKey key = new NamespacedKey(plugin, WitherTagEvents.ENTITY_ORIGIN_METADATA); + if (container.has(key, PersistentDataType.STRING)) { + return container.get(key, PersistentDataType.STRING); + } else { + return null; } - try { - Object[] parse = new MessageFormat(I18nUtil.marktr("{0}''s Wither")).parse(wither.getCustomName()); - if (parse != null && parse.length == 1 && parse[0] instanceof String) { - return (String) parse[0]; - } - } catch (ParseException e) { - // Ignore - } - return null; } @EventHandler @@ -215,5 +203,4 @@ public void onEgg(PlayerEggThrowEvent e) { e.setHatching(false); } } - } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/event/InternalEvents.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/event/InternalEvents.java index 870402289..57638f3c5 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/event/InternalEvents.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/event/InternalEvents.java @@ -1,8 +1,11 @@ package us.talabrek.ultimateskyblock.event; +import com.google.inject.Inject; +import com.google.inject.Singleton; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; +import org.jetbrains.annotations.NotNull; import us.talabrek.ultimateskyblock.api.event.CreateIslandEvent; import us.talabrek.ultimateskyblock.api.event.IslandInfoEvent; import us.talabrek.ultimateskyblock.api.event.MemberJoinedEvent; @@ -17,11 +20,12 @@ /** * Main event-handler for internal uSkyBlock events */ -@SuppressWarnings("WeakerAccess") +@Singleton public class InternalEvents implements Listener { private final uSkyBlock plugin; - public InternalEvents(uSkyBlock plugin) { + @Inject + public InternalEvents(@NotNull uSkyBlock plugin) { this.plugin = plugin; } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/event/ItemDropEvents.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/event/ItemDropEvents.java index 4c6c4e8dd..47d4d1845 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/event/ItemDropEvents.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/event/ItemDropEvents.java @@ -1,5 +1,7 @@ package us.talabrek.ultimateskyblock.event; +import com.google.inject.Inject; +import com.google.inject.Singleton; import org.bukkit.entity.Item; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; @@ -11,6 +13,7 @@ import org.bukkit.event.player.PlayerDropItemEvent; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.ItemMeta; +import org.jetbrains.annotations.NotNull; import us.talabrek.ultimateskyblock.uSkyBlock; import java.util.ArrayList; @@ -21,11 +24,13 @@ /** * Handles the internal item-drop protection. */ +@Singleton public class ItemDropEvents implements Listener { private final uSkyBlock plugin; private final boolean visitorsCanDrop; - public ItemDropEvents(uSkyBlock plugin) { + @Inject + public ItemDropEvents(@NotNull uSkyBlock plugin) { this.plugin = plugin; visitorsCanDrop = plugin.getConfig().getBoolean("options.protection.visitors.item-drops", true); } @@ -42,6 +47,11 @@ public void onDropEvent(PlayerDropItemEvent event) { plugin.notifyPlayer(player, tr("\u00a7eVisitors can't drop items!")); return; } + if (plugin.playerIsInSpawn(player)) { + event.setCancelled(true); + plugin.notifyPlayer(player, tr("\u00a7eSpawn point can't drop items!")); + return; + } addDropInfo(player, event.getItemDrop().getItemStack()); } @@ -109,7 +119,7 @@ public void onPickupEvent(EntityPickupItemEvent event) { return; } Player player = (Player) event.getEntity(); - if (event.isCancelled() || !plugin.getWorldManager().isSkyWorld(player.getWorld())) { + if (event.isCancelled() || !plugin.getWorldManager().isSkyWorld(player.getWorld()) || !plugin.getWorldManager().isSkyNether(player.getWorld() )) { clearDropInfo(event.getItem()); return; } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/event/MenuEvents.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/event/MenuEvents.java index d3c42c781..be6648db9 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/event/MenuEvents.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/event/MenuEvents.java @@ -1,37 +1,28 @@ package us.talabrek.ultimateskyblock.event; +import com.google.inject.Inject; +import com.google.inject.Singleton; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; import org.bukkit.event.inventory.InventoryClickEvent; +import org.jetbrains.annotations.NotNull; +import us.talabrek.ultimateskyblock.menu.SkyBlockMenu; import us.talabrek.ultimateskyblock.player.UltimateHolder; -import us.talabrek.ultimateskyblock.uSkyBlock; -import static dk.lockfuglsang.minecraft.po.I18nUtil.tr; -import static dk.lockfuglsang.minecraft.util.FormatUtil.stripFormatting; - -/** - * Menu events. - */ +@Singleton public class MenuEvents implements Listener { - private final uSkyBlock plugin; + private final SkyBlockMenu mainMenu; - public MenuEvents(uSkyBlock plugin) { - this.plugin = plugin; + @Inject + public MenuEvents(@NotNull SkyBlockMenu mainMenu) { + this.mainMenu = mainMenu; } @EventHandler(priority = EventPriority.MONITOR) public void guiClick(final InventoryClickEvent event) { - if (!(event.getInventory().getHolder() instanceof UltimateHolder)) { - // Not our menu. - return; - } - - UltimateHolder holder = (UltimateHolder) event.getInventory().getHolder(); - if (holder.getMenuType() == UltimateHolder.MenuType.CONFIG) { - plugin.getConfigMenu().onClick(event); - } else { - plugin.getMenu().onClick(event); + if (event.getInventory().getHolder() instanceof UltimateHolder holder && holder.getMenuType() == UltimateHolder.MenuType.DEFAULT) { + mainMenu.onClick(event); } } } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/event/NetherTerraFormEvents.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/event/NetherTerraFormEvents.java index 539d90e37..7f945ca8d 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/event/NetherTerraFormEvents.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/event/NetherTerraFormEvents.java @@ -1,5 +1,7 @@ package us.talabrek.ultimateskyblock.event; +import com.google.inject.Inject; +import com.google.inject.Singleton; import com.sk89q.worldguard.protection.regions.ProtectedCuboidRegion; import org.bukkit.GameMode; import org.bukkit.Location; @@ -7,9 +9,11 @@ import org.bukkit.block.Block; import org.bukkit.block.BlockFace; import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.entity.EntityType; import org.bukkit.entity.Fireball; import org.bukkit.entity.PigZombie; import org.bukkit.entity.Player; +import org.bukkit.entity.WitherSkeleton; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; @@ -18,7 +22,7 @@ import org.bukkit.event.entity.EntityExplodeEvent; import org.bukkit.inventory.ItemStack; import org.bukkit.util.Vector; -import us.talabrek.ultimateskyblock.handler.EntitySpawner; +import org.jetbrains.annotations.NotNull; import us.talabrek.ultimateskyblock.handler.WorldGuardHandler; import us.talabrek.ultimateskyblock.uSkyBlock; import us.talabrek.ultimateskyblock.util.LocationUtil; @@ -35,11 +39,12 @@ /** * Responsible for forming the correct blocks in nether on block-breaks. */ +@Singleton public class NetherTerraFormEvents implements Listener { private final uSkyBlock plugin; - private final Map> terraFormMap = new HashMap<>(); + private final Map> terraFormMap = new HashMap<>(); private final Map toolWeights = new HashMap<>(); - private static final Random RND = new Random(System.currentTimeMillis()); + private static final Random RND = new Random(); private final double maxScan; private final double chanceWither; private final double chanceSkeleton; @@ -48,9 +53,9 @@ public class NetherTerraFormEvents implements Listener { private final boolean spawnEnabled; private final double minPitch; private final double maxPitch; - private final EntitySpawner entitySpawner; - public NetherTerraFormEvents(uSkyBlock plugin) { + @Inject + public NetherTerraFormEvents(@NotNull uSkyBlock plugin) { this.plugin = plugin; // TODO: 23/09/2015 - R4zorax: Allow this to be perk-based? terraformEnabled = plugin.getConfig().getBoolean("nether.terraform-enabled", true); @@ -60,7 +65,7 @@ public NetherTerraFormEvents(uSkyBlock plugin) { ConfigurationSection config = plugin.getConfig().getConfigurationSection("nether.terraform"); if (config != null) { for (String key : config.getKeys(false)) { - Material mat = Material.getMaterial(key); + Material mat = Material.matchMaterial(key); if (mat != null) { terraFormMap.put(mat, MaterialUtil.createProbabilityList(config.getStringList(key))); } @@ -83,7 +88,6 @@ public NetherTerraFormEvents(uSkyBlock plugin) { chanceWither = 0.4; chanceSkeleton = 0.1; } - entitySpawner = new EntitySpawner(); } @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) @@ -105,8 +109,7 @@ public void onBlockBreak(BlockBreakEvent event) { if (!terraFormMap.containsKey(block.getType())) { return; // Not a block we terra-form on. } - // TODO: 10/07/2016 - R4zorax: Handle dual-wielding (would break 1.8 compatibility) - ItemStack tool = event.getPlayer().getItemInHand(); + ItemStack tool = event.getPlayer().getInventory().getItemInMainHand(); if (event.getBlock().getDrops(tool).isEmpty()) { return; // Only terra-form when stuff is mined correctly } @@ -133,7 +136,7 @@ private Double getToolWeight(ItemStack tool) { } private void spawnBlock(Material type, Location location, Vector v, ProtectedCuboidRegion islandRegion) { - Location spawnLoc = null; + Location spawnLoc; if (MaterialUtil.isFallingMaterial(type)) { spawnLoc = findSolidSpawnLocation(location, v, islandRegion); } else { @@ -147,18 +150,18 @@ private void spawnBlock(Material type, Location location, Vector v, ProtectedCub private Location findAirSpawnLocation(Location location, Vector v, ProtectedCuboidRegion islandRegion) { // Searches in a cone for an air block Location lookAt = new Location(location.getWorld(), - Math.round(location.getX() + v.getX()), - Math.round(location.getY() + v.getY()), - Math.round(location.getZ() + v.getZ())); + Math.round(location.getX() + v.getX()), + Math.round(location.getY() + v.getY()), + Math.round(location.getZ() + v.getZ())); while (v.length() < maxScan) { for (Location loc : getLocationsInPlane(lookAt, v)) { if (loc.getBlock().getType() == Material.AIR && isAdjacentToSolid(loc) - && isInIslandRegion(islandRegion, loc)) { + && isInIslandRegion(islandRegion, loc)) { return loc; } } double n = v.length(); - v.normalize().multiply(n+1); + v.normalize().multiply(n + 1); } return null; } @@ -181,22 +184,22 @@ private Location findSolidSpawnLocation(Location location, Vector v, ProtectedCu while (v.length() < maxScan) { for (Location loc : getLocationsInPlane(location, v)) { if (loc.getBlock().getType() == Material.AIR - && loc.getBlock().getRelative(BlockFace.DOWN).getType().isSolid() - && isInIslandRegion(islandRegion, loc)) { + && loc.getBlock().getRelative(BlockFace.DOWN).getType().isSolid() + && isInIslandRegion(islandRegion, loc)) { return loc; } } double n = v.length(); - v.normalize().multiply(n+1); + v.normalize().multiply(n + 1); } return null; } private List getLocationsInPlane(Location location, Vector v) { Location lookAt = new Location(location.getWorld(), - Math.round(location.getX() + v.getX()), - Math.round(location.getY() + v.getY()), - Math.round(location.getZ() + v.getZ())); + Math.round(location.getX() + v.getX()), + Math.round(location.getY() + v.getY()), + Math.round(location.getZ() + v.getZ())); List locs = new ArrayList<>(); boolean xFixed = Math.abs(v.getX()) > Math.abs(v.getZ()); for (int r = 1; r <= v.length(); r++) { @@ -211,14 +214,14 @@ private List getLocationsInPlane(Location location, Vector v) { } } Collections.shuffle(locs); - locs = locs.subList(0, locs.size()/2); // Only try half + locs = locs.subList(0, locs.size() / 2); // Only try half return locs; } public List getYield(Material material, double toolWeight) { List copy = new ArrayList<>(); for (MaterialUtil.MaterialProbability e : terraFormMap.get(material)) { - if (RND.nextDouble() < e.getProbability()*toolWeight) { + if (RND.nextDouble() < e.getProbability() * toolWeight) { copy.add(e.getMaterial()); } } @@ -231,8 +234,7 @@ public void onGhastExplode(EntityExplodeEvent event) { return; // Bail out, not our problem } // TODO: 23/09/2015 - R4zorax: Perhaps enable this when island has a certain level? - if (event.getEntity() instanceof Fireball) { - Fireball fireball = (Fireball) event.getEntity(); + if (event.getEntity() instanceof Fireball fireball) { fireball.setIsIncendiary(false); fireball.setFireTicks(0); event.setCancelled(true); @@ -240,8 +242,7 @@ public void onGhastExplode(EntityExplodeEvent event) { } /** - * Comes AFTER the SpawnEvents{@link #onCreatureSpawn(CreatureSpawnEvent)} - so cancelled will have effect - * @param e + * Comes AFTER the {@link SpawnEvents#onCreatureSpawn(CreatureSpawnEvent)} - so cancelled will have effect */ @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) public void onCreatureSpawn(CreatureSpawnEvent e) { @@ -262,11 +263,13 @@ public void onCreatureSpawn(CreatureSpawnEvent e) { e.setCancelled(true); double p = RND.nextDouble(); if (p <= chanceWither && block.getRelative(BlockFace.UP, 3).getType() == Material.AIR) { - entitySpawner.spawnWitherSkeleton(e.getLocation()); - } else if (p <= chanceWither+chanceBlaze) { - entitySpawner.spawnBlaze(e.getLocation()); - } else if (p <= chanceWither+chanceBlaze+chanceSkeleton) { - entitySpawner.spawnSkeleton(e.getLocation()); + WitherSkeleton mob = (WitherSkeleton) e.getLocation().getWorld().spawnEntity( + e.getLocation(), EntityType.WITHER_SKELETON); + mob.getEquipment().setItemInMainHand(new ItemStack(Material.STONE_SWORD, 1)); + } else if (p <= chanceWither + chanceBlaze) { + e.getLocation().getWorld().spawnEntity(e.getLocation(), EntityType.BLAZE); + } else if (p <= chanceWither + chanceBlaze + chanceSkeleton) { + e.getLocation().getWorld().spawnEntity(e.getLocation(), EntityType.SKELETON); } else { e.setCancelled(false); // Spawn PigZombie } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/event/PlayerEvents.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/event/PlayerEvents.java index d4ca5fabc..78576d4e9 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/event/PlayerEvents.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/event/PlayerEvents.java @@ -1,10 +1,20 @@ package us.talabrek.ultimateskyblock.event; +import com.destroystokyo.paper.event.player.PlayerTeleportEndGatewayEvent; +import com.google.common.util.concurrent.RateLimiter; +import com.google.inject.Inject; +import com.google.inject.Singleton; +import dk.lockfuglsang.minecraft.util.ItemStackUtil; +import org.bukkit.ChatColor; import org.bukkit.Location; import org.bukkit.Material; +import org.bukkit.World; import org.bukkit.block.Block; +import org.bukkit.block.data.BlockData; +import org.bukkit.block.data.Levelled; import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.entity.EnderPearl; +import org.bukkit.entity.EntityType; import org.bukkit.entity.Player; import org.bukkit.entity.Projectile; import org.bukkit.event.EventHandler; @@ -12,55 +22,68 @@ import org.bukkit.event.Listener; import org.bukkit.event.block.Action; import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.block.BlockExplodeEvent; import org.bukkit.event.block.BlockPlaceEvent; -import org.bukkit.event.entity.EntityChangeBlockEvent; -import org.bukkit.event.entity.EntityDamageByEntityEvent; -import org.bukkit.event.entity.EntityDamageEvent; -import org.bukkit.event.entity.FoodLevelChangeEvent; -import org.bukkit.event.player.PlayerInteractEvent; -import org.bukkit.event.player.PlayerRespawnEvent; -import org.bukkit.event.player.PlayerTeleportEvent; +import org.bukkit.event.entity.*; +import org.bukkit.event.player.*; +import org.bukkit.event.world.PortalCreateEvent; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.PlayerInventory; import org.bukkit.projectiles.ProjectileSource; +import org.jetbrains.annotations.NotNull; import us.talabrek.ultimateskyblock.Settings; import us.talabrek.ultimateskyblock.api.async.Callback; import us.talabrek.ultimateskyblock.api.event.IslandInfoEvent; -import us.talabrek.ultimateskyblock.api.model.IslandScore; -import us.talabrek.ultimateskyblock.handler.VaultHandler; import us.talabrek.ultimateskyblock.handler.WorldGuardHandler; import us.talabrek.ultimateskyblock.island.BlockLimitLogic; import us.talabrek.ultimateskyblock.island.IslandInfo; import us.talabrek.ultimateskyblock.player.PatienceTester; -import us.talabrek.ultimateskyblock.player.Perk; import us.talabrek.ultimateskyblock.player.PlayerInfo; import us.talabrek.ultimateskyblock.uSkyBlock; +import us.talabrek.ultimateskyblock.util.LocationUtil; +import us.talabrek.ultimateskyblock.util.LogUtil; +import us.talabrek.ultimateskyblock.world.WorldManager; +import java.time.Duration; +import java.time.Instant; import java.util.Arrays; +import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Random; import java.util.Set; import java.util.UUID; -import java.util.WeakHashMap; +import java.util.logging.Level; import static dk.lockfuglsang.minecraft.po.I18nUtil.tr; +@Singleton public class PlayerEvents implements Listener { - private static final Set FIRE_TRAP = new HashSet<>( - Arrays.asList(EntityDamageEvent.DamageCause.LAVA, EntityDamageEvent.DamageCause.FIRE, EntityDamageEvent.DamageCause.FIRE_TICK)); + private static final Set FIRE_TRAP = new HashSet<>(Arrays.asList( + EntityDamageEvent.DamageCause.LAVA, + EntityDamageEvent.DamageCause.FIRE, + EntityDamageEvent.DamageCause.FIRE_TICK, + EntityDamageEvent.DamageCause.HOT_FLOOR)); private static final Random RANDOM = new Random(); - private static final int OBSIDIAN_SPAM = 10000; // Max once every 10 seconds. + private static final Duration OBSIDIAN_SPAM = Duration.ofSeconds(10); private final uSkyBlock plugin; private final boolean visitorFallProtected; private final boolean visitorFireProtected; private final boolean visitorMonsterProtected; private final boolean protectLava; - private final Map obsidianClick = new WeakHashMap<>(); + private final Map obsidianClick = new HashMap<>(); private final boolean blockLimitsEnabled; + private final Map leafSaplings = Map.of( + Material.OAK_LEAVES, Material.OAK_SAPLING, + Material.SPRUCE_LEAVES, Material.SPRUCE_SAPLING, + Material.BIRCH_LEAVES, Material.BIRCH_SAPLING, + Material.ACACIA_LEAVES, Material.ACACIA_SAPLING, + Material.JUNGLE_LEAVES, Material.JUNGLE_SAPLING, + Material.DARK_OAK_LEAVES, Material.DARK_OAK_SAPLING); - public PlayerEvents(uSkyBlock plugin) { + @Inject + public PlayerEvents(@NotNull uSkyBlock plugin) { this.plugin = plugin; FileConfiguration config = plugin.getConfig(); visitorFallProtected = config.getBoolean("options.protection.visitors.fall", true); @@ -70,52 +93,50 @@ public PlayerEvents(uSkyBlock plugin) { blockLimitsEnabled = config.getBoolean("options.island.block-limits.enabled", false); } - @EventHandler(priority = EventPriority.NORMAL) + @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) public void onPlayerFoodChange(final FoodLevelChangeEvent event) { - if (event.getEntity() instanceof Player && plugin.getWorldManager().isSkyWorld(event.getEntity().getWorld())) { - Player hungerman = (Player) event.getEntity(); - float randomNum = RANDOM.nextFloat(); - if (plugin.getWorldManager().isSkyWorld(hungerman.getWorld()) - && hungerman.getFoodLevel() > event.getFoodLevel() - && plugin.playerIsOnIsland(hungerman)) { - Perk perk = plugin.getPerkLogic().getPerk(hungerman); - if (randomNum <= perk.getHungerReduction()) { + if (event.getEntity() instanceof Player player) { + if (player.getFoodLevel() > event.getFoodLevel() && plugin.playerIsOnIsland(player)) { + if (RANDOM.nextFloat() <= plugin.getPerkLogic().getPerk(player).getHungerReduction()) { event.setCancelled(true); } } } } - @EventHandler(priority = EventPriority.NORMAL) + @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) public void onClickOnObsidian(final PlayerInteractEvent event) { - if (!plugin.getWorldManager().isSkyWorld(event.getPlayer().getWorld())) { - return; - } - long now = System.currentTimeMillis(); Player player = event.getPlayer(); - PlayerInventory inventory = player.getInventory(); Block block = event.getClickedBlock(); - Long lastClick = obsidianClick.get(player.getUniqueId()); - if (lastClick != null && (lastClick + OBSIDIAN_SPAM) >= now) { - plugin.notifyPlayer(player, tr("\u00a74You can only convert obsidian once every 10 seconds")); - return; - } - if (Settings.extras_obsidianToLava && plugin.playerIsOnIsland(player) - && plugin.getWorldManager().isSkyWorld(player.getWorld()) - && event.getAction() == Action.RIGHT_CLICK_BLOCK - && player.getItemInHand() != null - && player.getItemInHand().getType() == Material.BUCKET - && block != null - && block.getType() == Material.OBSIDIAN - && !testForObsidian(block)) { + if (plugin.playerIsOnIsland(player) + && Settings.extras_obsidianToLava + && event.hasBlock() + && event.hasItem() + && event.getAction() == Action.RIGHT_CLICK_BLOCK + && event.getMaterial() == Material.BUCKET + && block != null + && block.getType() == Material.OBSIDIAN + && !testForObsidian(block)) { + Instant now = Instant.now(); + Instant lastClick = obsidianClick.get(player.getUniqueId()); + if (lastClick != null && lastClick.plus(OBSIDIAN_SPAM).isAfter(now)) { + plugin.notifyPlayer(player, tr("\u00a74You can only convert obsidian once every 10 seconds")); + return; + } + PlayerInventory inventory = player.getInventory(); if (inventory.firstEmpty() != -1) { - obsidianClick.put(player.getUniqueId(), now); - player.sendMessage(tr("\u00a7eChanging your obsidian back into lava. Be careful!")); - inventory.removeItem(new ItemStack(Material.BUCKET, 1)); - inventory.addItem(new ItemStack(Material.LAVA_BUCKET, 1)); - player.updateInventory(); - block.setType(Material.AIR); - event.setCancelled(true); // Don't execute the click anymore (since that would re-place the lava). + HashMap leftover = inventory.removeItem(new ItemStack(Material.BUCKET)); + if (leftover.isEmpty()) { + obsidianClick.put(player.getUniqueId(), now); + player.sendMessage(tr("\u00a7eChanging your obsidian back into lava. Be careful!")); + leftover = inventory.addItem(new ItemStack(Material.LAVA_BUCKET)); + // Just in case, drop the item if their inventory somehow filled before we could add it + if (!leftover.isEmpty()) { + player.getWorld().dropItem(block.getLocation(), new ItemStack(Material.LAVA_BUCKET)); + } + block.setType(Material.AIR); + event.setCancelled(true); + } } else { player.sendMessage(tr("\u00a7eYour inventory must have another empty space!")); } @@ -139,130 +160,146 @@ public boolean testForObsidian(final Block block) { return false; } - @EventHandler + // Prevent re-placing lava that was picked up in the last tick + @EventHandler(ignoreCancelled = true) + public void onLavaPlace(final PlayerBucketEmptyEvent event) { + if (Settings.extras_obsidianToLava && event.getBucket() == Material.LAVA_BUCKET) { + Instant now = Instant.now(); + Instant lastClick = obsidianClick.get(event.getPlayer().getUniqueId()); + if (lastClick != null && lastClick.plus(Duration.ofMillis(50)).isAfter(now)) { + event.setCancelled(true); + } + } + } + + @EventHandler(ignoreCancelled = true) public void onLavaReplace(BlockPlaceEvent event) { if (!protectLava || !plugin.getWorldManager().isSkyWorld(event.getPlayer().getWorld())) { - return; // Skip + return; } - if (isLavaSource(event.getBlockReplacedState().getType(), event.getBlockReplacedState().getRawData())) { + if (isLavaSource(event.getBlockReplacedState().getBlockData())) { plugin.notifyPlayer(event.getPlayer(), tr("\u00a74It''s a bad idea to replace your lava!")); event.setCancelled(true); } } - private boolean isLavaSource(Material type, byte data) { - return (type == Material.LAVA) && data == 0; + private boolean isLavaSource(BlockData blockData) { + return (blockData.getMaterial() == Material.LAVA + && blockData instanceof Levelled level + && level.getLevel() == 0); } + // If an entity, such as an Enderman, attempts to replace a lava source block then cancel it and drop the item instead @EventHandler public void onLavaAbsorption(EntityChangeBlockEvent event) { - if (!plugin.getWorldManager().isSkyWorld(event.getBlock().getWorld())) { + Block block = event.getBlock(); + if (!protectLava || !plugin.getWorldManager().isSkyWorld(block.getWorld())) { return; } - if (isLavaSource(event.getBlock().getType(), event.getBlock().getData())) { + if (isLavaSource(block.getBlockData())) { if (event.getTo() != Material.LAVA) { event.setCancelled(true); - // TODO: R4zorax - 21-07-2018: missing datavalue (might convert stuff - exploit) - ItemStack item = new ItemStack(event.getTo(), 1); - Location above = event.getBlock().getLocation().add(0, 1, 0); - event.getBlock().getWorld().dropItemNaturally(above, item); + // Drop the item diagonally above to reduce the risk of the item falling into the lava + block.getWorld().dropItemNaturally(block.getLocation().add(1, 1, 1), new ItemStack(event.getTo())); } } } - // TODO 2018-11-09 Muspah: Move (parts) to new EntityDamageByEntityEvent-handler - @EventHandler(priority = EventPriority.HIGHEST) + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) public void onVisitorDamage(final EntityDamageEvent event) { - // Only protect things in the Skyworld. - if (!plugin.getWorldManager().isSkyWorld(event.getEntity().getWorld())) { - return; - } - - // Only protect visitors against damage if pvp is disabled: - if (Settings.island_allowPvP) { - return; - } - - // This protection only applies to players: - if (!(event.getEntity() instanceof Player)) { - return; - } - - // Don't protect players on their own islands: - if (plugin.playerIsOnIsland((Player) event.getEntity())) { - return; + if (event.getEntity() instanceof Player player + && plugin.getWorldManager().isSkyAssociatedWorld(player.getWorld()) + && !plugin.playerIsOnIsland(player)) { + if ((visitorFireProtected && FIRE_TRAP.contains(event.getCause())) + || (visitorFallProtected && (event.getCause() == EntityDamageEvent.DamageCause.FALL))) { + event.setDamage(-event.getDamage()); + event.setCancelled(true); + } } + } - if ((visitorFireProtected && FIRE_TRAP.contains(event.getCause())) - || (visitorFallProtected && (event.getCause() == EntityDamageEvent.DamageCause.FALL)) - || (visitorMonsterProtected && - (event.getCause() == EntityDamageEvent.DamageCause.ENTITY_ATTACK + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onVisitorDamageByEntity(final EntityDamageByEntityEvent event) { + if (event.getEntity() instanceof Player player + && plugin.getWorldManager().isSkyAssociatedWorld(player.getWorld()) + && !plugin.playerIsOnIsland(player) + && !(event.getDamager() instanceof Player && Settings.island_allowPvP)) { + if (visitorMonsterProtected && + (event.getCause() == EntityDamageEvent.DamageCause.ENTITY_ATTACK || event.getCause() == EntityDamageEvent.DamageCause.ENTITY_SWEEP_ATTACK || event.getCause() == EntityDamageEvent.DamageCause.ENTITY_EXPLOSION || event.getCause() == EntityDamageEvent.DamageCause.PROJECTILE || event.getCause() == EntityDamageEvent.DamageCause.MAGIC - || event.getCause() == EntityDamageEvent.DamageCause.POISON))) { - event.setDamage(-event.getDamage()); - event.setCancelled(true); + || event.getCause() == EntityDamageEvent.DamageCause.POISON)) { + event.setDamage(-event.getDamage()); + event.setCancelled(true); + } } } @EventHandler(priority = EventPriority.HIGHEST) public void onSpawnDamage(final EntityDamageEvent event) { - if (!plugin.getWorldManager().isSkyWorld(event.getEntity().getWorld())) { - return; - } - if (event.getEntity() instanceof Player && plugin.playerIsInSpawn((Player) event.getEntity()) && event.getCause() == EntityDamageEvent.DamageCause.VOID) { + if (event.getEntity() instanceof Player player && plugin.playerIsInSpawn(player) && event.getCause() == EntityDamageEvent.DamageCause.VOID) { event.setDamage(-event.getDamage()); event.setCancelled(true); - plugin.getTeleportLogic().spawnTeleport((Player) event.getEntity(), true); + player.setFallDistance(0); + plugin.getTeleportLogic().spawnTeleport(player, true); } } - @EventHandler + @EventHandler(ignoreCancelled = true) public void onMemberDamage(final EntityDamageByEntityEvent event) { - if (!plugin.getWorldManager().isSkyAssociatedWorld(event.getEntity().getWorld())) { - return; - } - if (!(event.getEntity() instanceof Player)) { - return; - } - Player p2 = (Player) event.getEntity(); - if (event.getDamager() instanceof Player) { - Player p1 = (Player) event.getDamager(); - cancelMemberDamage(p1, p2, event); - } else if (event.getDamager() instanceof Projectile - && !(event.getDamager() instanceof EnderPearl)) { - ProjectileSource shooter = ((Projectile) event.getDamager()).getShooter(); - if (shooter instanceof Player) { - Player p1 = (Player) shooter; - cancelMemberDamage(p1, p2, event); + if (event.getEntity() instanceof Player victim && plugin.getWorldManager().isSkyAssociatedWorld(victim.getWorld())) { + if (event.getDamager() instanceof Player attacker) { + cancelMemberDamage(attacker, victim, event); + } else if (event.getDamager() instanceof Projectile && !(event.getDamager() instanceof EnderPearl)) { + ProjectileSource shooter = ((Projectile) event.getDamager()).getShooter(); + if (shooter instanceof Player attacker) { + cancelMemberDamage(attacker, victim, event); + } } } } - private void cancelMemberDamage(Player p1, Player p2, EntityDamageByEntityEvent event) { - IslandInfo is1 = plugin.getIslandInfo(p1); - IslandInfo is2 = plugin.getIslandInfo(p2); + private void cancelMemberDamage(Player attacker, Player victim, EntityDamageByEntityEvent event) { + IslandInfo is1 = plugin.getIslandInfo(attacker); + IslandInfo is2 = plugin.getIslandInfo(victim); if (is1 != null && is2 != null && is1.getName().equals(is2.getName())) { - plugin.notifyPlayer(p1, tr("\u00a7eYou cannot hurt island-members.")); + plugin.notifyPlayer(attacker, tr("\u00a7eYou cannot hurt island-members.")); event.setCancelled(true); } } @EventHandler(priority = EventPriority.HIGHEST) public void onPlayerRespawn(PlayerRespawnEvent event) { - if (Settings.extras_sendToSpawn) { + WorldManager wm = plugin.getWorldManager(); + World pWorld = event.getPlayer().getWorld(); + if (!wm.isSkyAssociatedWorld(pWorld)) { return; } - if (plugin.getWorldManager().isSkyWorld(event.getPlayer().getWorld())) { + + if (Settings.extras_respawnAtIsland) { + PlayerInfo playerInfo = plugin.getPlayerInfo(event.getPlayer()); + if (playerInfo.getHasIsland()) { + Location homeLocation = LocationUtil.findNearestSafeLocation(playerInfo.getHomeLocation(), null); + if (homeLocation == null) { + homeLocation = LocationUtil.findNearestSafeLocation(playerInfo.getIslandLocation(), null); + } + // If homeLocation is somehow still null, we intentionally fallthrough + if (homeLocation != null) { + event.setRespawnLocation(homeLocation); + return; + } + } + } + if (!Settings.extras_sendToSpawn && wm.isSkyWorld(pWorld)) { event.setRespawnLocation(plugin.getWorldManager().getWorld().getSpawnLocation()); } } @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) public void onTeleport(PlayerTeleportEvent event) { - if (event.getTo() != null || !plugin.getWorldManager().isSkyWorld(event.getTo().getWorld())) { + if (event.getTo() == null || !plugin.getWorldManager().isSkyWorld(event.getTo().getWorld())) { return; } final Player player = event.getPlayer(); @@ -282,27 +319,54 @@ public void onTeleport(PlayerTeleportEvent event) { } } - @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) - public void onLeafBreak(BlockBreakEvent event) { - if (!plugin.getWorldManager().isSkyWorld(event.getPlayer().getWorld())) { - return; + private RateLimiter rateLimiter = RateLimiter.create(1); + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onPlayerPortal(EntityPortalEnterEvent event) { + if (event.getEntityType() == EntityType.PLAYER) { + Player player = (Player) event.getEntity(); + PlayerInfo playerInfo = plugin.getPlayerInfo(player); + boolean isFirstCompletion = playerInfo.checkChallenge("page1finished") == 0; + if (player.isOp()) return; + if (isFirstCompletion){ + event.setCancelled(true); + if (rateLimiter.tryAcquire()) { + player.sendMessage("\u00a7c地狱门已被禁用"); + } + } } - if (event.getBlock().getType() != Material.OAK_LEAVES || (event.getBlock().getData() & 0x3) != 0) { - return; + else { + IslandInfo islandInfo = plugin.getIslandInfo(event.getLocation()); + if (islandInfo != null) { + PlayerInfo playerInfo = plugin.getPlayerInfo(islandInfo.getLeader()); + boolean isFirstCompletion = playerInfo.checkChallenge("page1finished") == 0; + if (isFirstCompletion){ + event.setCancelled(true); + } + } } - // Ok, a player broke an OAK LEAF in the Skyworld - String islandName = WorldGuardHandler.getIslandNameAt(event.getPlayer().getLocation()); - IslandInfo islandInfo = plugin.getIslandInfo(islandName); - if (islandInfo != null && islandInfo.getLeafBreaks() == 0) { - // Add an oak-sapling - event.getBlock().getWorld().dropItemNaturally(event.getBlock().getLocation(), new ItemStack(Material.OAK_SAPLING, 1)); - islandInfo.setLeafBreaks(islandInfo.getLeafBreaks() + 1); + } + + /** + * This EventHandler handles {@link BlockBreakEvent} to detect if a player broke leaves in the skyworld, + * and will drop a sapling if so. This will prevent cases where the default generated tree on a new + * island drops no saplings. + * + * @param event BlockBreakEvent to handle. + */ + @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) + public void onLeafBreak(BlockBreakEvent event) { + if (plugin.playerIsOnIsland(event.getPlayer()) && leafSaplings.containsKey(event.getBlock().getType())) { + IslandInfo islandInfo = plugin.getIslandInfo(event.getBlock().getLocation()); + if (islandInfo != null && islandInfo.getLeafBreaks() == 0) { + event.getBlock().getWorld().dropItemNaturally(event.getBlock().getLocation(), new ItemStack(leafSaplings.get(event.getBlock().getType()))); + islandInfo.setLeafBreaks(1); + } } } - + @EventHandler(ignoreCancelled = true) - public void onBlockPlaceEvent(BlockPlaceEvent event) - { + public void onBlockPlaceEvent(BlockPlaceEvent event) { final Player player = event.getPlayer(); if (!blockLimitsEnabled || !plugin.getWorldManager().isSkyAssociatedWorld(player.getWorld())) { return; // Skip @@ -319,8 +383,8 @@ public void onBlockPlaceEvent(BlockPlaceEvent event) final String key = "usb.block-limits"; if (!PatienceTester.isRunning(player, key)) { PatienceTester.startRunning(player, key); - player.sendMessage(tr("\u00a74{0} is limited. \u00a7eScanning your island to see if you are allowed to place more, please be patient", VaultHandler.getItemName(new ItemStack(type)))); - plugin.fireAsyncEvent(new IslandInfoEvent(player, islandInfo.getIslandLocation(), new Callback() { + player.sendMessage(tr("\u00a74{0} is limited. \u00a7eScanning your island to see if you are allowed to place more, please be patient", ItemStackUtil.getItemName(new ItemStack(type)))); + plugin.fireAsyncEvent(new IslandInfoEvent(player, islandInfo.getIslandLocation(), new Callback<>() { @Override public void run() { player.sendMessage(tr("\u00a7e... Scanning complete, you can try again")); @@ -332,15 +396,18 @@ public void run() { } if (canPlace == BlockLimitLogic.CanPlace.NO) { event.setCancelled(true); - player.sendMessage(tr("\u00a74You''ve hit the {0} limit!\u00a7e You can''t have more of that type on your island!\u00a79 Max: {1,number}", VaultHandler.getItemName(new ItemStack(type)), plugin.getBlockLimitLogic().getLimit(type))); + if (type == Material.HOPPER) + player.sendMessage(tr("\u00a74You''ve hit the {0} limit!\u00a7e You can''t have more of that type on your island!\u00a79 Max: {1,number}", ItemStackUtil.getItemName(new ItemStack(type)), (plugin.getBlockLimitLogic().getLimit(type)+islandInfo.getHopperLimit()))); + else + player.sendMessage(tr("\u00a74You''ve hit the {0} limit!\u00a7e You can''t have more of that type on your island!\u00a79 Max: {1,number}", ItemStackUtil.getItemName(new ItemStack(type)), plugin.getBlockLimitLogic().getLimit(type))); return; } plugin.getBlockLimitLogic().incBlockCount(islandInfo.getIslandLocation(), type); } - + @EventHandler(ignoreCancelled = true) - public void onHopperDestroy(BlockBreakEvent event){ - if (!blockLimitsEnabled || !plugin.getWorldManager().isSkyAssociatedWorld(event.getPlayer().getWorld())) { + public void onBlockBreak(BlockBreakEvent event) { + if (!blockLimitsEnabled || !plugin.getWorldManager().isSkyAssociatedWorld(event.getBlock().getWorld())) { return; // Skip } IslandInfo islandInfo = plugin.getIslandInfo(event.getBlock().getLocation()); @@ -349,4 +416,28 @@ public void onHopperDestroy(BlockBreakEvent event){ } plugin.getBlockLimitLogic().decBlockCount(islandInfo.getIslandLocation(), event.getBlock().getType()); } + + @EventHandler(ignoreCancelled = true) + public void onBlockExplode(EntityExplodeEvent event) { + if (blockLimitsEnabled && plugin.getWorldManager().isSkyAssociatedWorld(event.getLocation().getWorld())) { + IslandInfo islandInfo = plugin.getIslandInfo(event.getLocation()); + if (islandInfo != null) { + for (Block block : event.blockList()) { + plugin.getBlockLimitLogic().decBlockCount(islandInfo.getIslandLocation(), block.getType()); + } + } + } + } + + @EventHandler(ignoreCancelled = true) + public void onBlockExplodeUnknown(BlockExplodeEvent event) { + if (blockLimitsEnabled && plugin.getWorldManager().isSkyAssociatedWorld(event.getBlock().getWorld())) { + IslandInfo islandInfo = plugin.getIslandInfo(event.getBlock().getLocation()); + if (islandInfo != null) { + for (Block block : event.blockList()) { + plugin.getBlockLimitLogic().decBlockCount(islandInfo.getIslandLocation(), block.getType()); + } + } + } + } } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/event/SpawnEvents.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/event/SpawnEvents.java index b7bbcd139..a4e1360c6 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/event/SpawnEvents.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/event/SpawnEvents.java @@ -1,16 +1,14 @@ package us.talabrek.ultimateskyblock.event; -import dk.lockfuglsang.minecraft.po.I18nUtil; +import com.google.inject.Inject; +import com.google.inject.Singleton; import org.bukkit.Location; import org.bukkit.Material; +import org.bukkit.NamespacedKey; +import org.bukkit.Registry; import org.bukkit.World; import org.bukkit.block.Biome; -import org.bukkit.entity.EntityType; -import org.bukkit.entity.Ghast; -import org.bukkit.entity.Phantom; -import org.bukkit.entity.Player; -import org.bukkit.entity.WaterMob; -import org.bukkit.entity.Wither; +import org.bukkit.entity.*; import org.bukkit.event.Cancellable; import org.bukkit.event.Event; import org.bukkit.event.EventHandler; @@ -19,41 +17,36 @@ import org.bukkit.event.entity.CreatureSpawnEvent; import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.inventory.ItemStack; -import org.bukkit.material.MonsterEggs; -import org.bukkit.material.SpawnEgg; -import org.bukkit.metadata.FixedMetadataValue; +import org.bukkit.inventory.meta.SpawnEggMeta; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import us.talabrek.ultimateskyblock.api.IslandInfo; import us.talabrek.ultimateskyblock.handler.WorldGuardHandler; +import us.talabrek.ultimateskyblock.player.PlayerInfo; import us.talabrek.ultimateskyblock.uSkyBlock; -import us.talabrek.ultimateskyblock.util.LocationUtil; +import us.talabrek.ultimateskyblock.util.LogUtil; -import java.util.Arrays; -import java.util.HashSet; -import java.util.List; -import java.util.Set; +import java.util.*; +import java.util.logging.Level; import static dk.lockfuglsang.minecraft.po.I18nUtil.tr; +import static us.talabrek.ultimateskyblock.util.LogUtil.log; /** * Responsible for controlling spawns on uSkyBlock islands. */ +@Singleton public class SpawnEvents implements Listener { - private static final Set RIGHT_CLICKS = new HashSet<>(Arrays.asList(Action.RIGHT_CLICK_AIR, Action.RIGHT_CLICK_BLOCK)); - private static final Set PLAYER_INITIATED = new HashSet<>(Arrays.asList( - CreatureSpawnEvent.SpawnReason.BREEDING, - CreatureSpawnEvent.SpawnReason.BUILD_IRONGOLEM, CreatureSpawnEvent.SpawnReason.BUILD_SNOWMAN, - CreatureSpawnEvent.SpawnReason.BUILD_WITHER - )); - private static final Set ADMIN_INITIATED = new HashSet<>(Arrays.asList( - CreatureSpawnEvent.SpawnReason.SPAWNER_EGG - )); + private static final Set RIGHT_CLICKS = Set.of(Action.RIGHT_CLICK_AIR, Action.RIGHT_CLICK_BLOCK); + private HashMap newIsland = new HashMap<>(); private final uSkyBlock plugin; private boolean phantomsInOverworld; private boolean phantomsInNether; - public SpawnEvents(uSkyBlock plugin) { + @Inject + public SpawnEvents(@NotNull uSkyBlock plugin) { this.plugin = plugin; phantomsInOverworld = plugin.getConfig().getBoolean("options.spawning.phantoms.overworld", true); phantomsInNether = plugin.getConfig().getBoolean("options.spawning.phantoms.nether", false); @@ -61,23 +54,23 @@ public SpawnEvents(uSkyBlock plugin) { @EventHandler public void onSpawnEggEvent(PlayerInteractEvent event) { - Player player = event != null ? event.getPlayer() : null; - if (player == null || event.isCancelled() || !plugin.getWorldManager().isSkyWorld(player.getWorld())) { + Player player = event.getPlayer(); + if (event.useItemInHand() == Event.Result.DENY || !plugin.getWorldManager().isSkyWorld(player.getWorld())) { return; // Bail out, we don't care } if (player.hasPermission("usb.mod.bypassprotection") || player.isOp()) { return; } ItemStack item = event.getItem(); - if (RIGHT_CLICKS.contains(event.getAction()) && item != null && isSpawnEgg(item)) { + if (RIGHT_CLICKS.contains(event.getAction()) && item != null && item.getItemMeta() instanceof SpawnEggMeta) { if (!plugin.playerIsOnIsland(player)) { event.setCancelled(true); plugin.notifyPlayer(player, tr("\u00a7eYou can only use spawn-eggs on your own island.")); return; } - SpawnEgg spawnEgg = (SpawnEgg) item.getData(); - checkLimits(event, spawnEgg.getSpawnedType(), player.getLocation()); - if (event.isCancelled()) { + + checkLimits(event, getSpawnEggType(item), player.getLocation()); + if (event.useItemInHand() == Event.Result.DENY) { plugin.notifyPlayer(player, tr("\u00a7cYou have reached your spawn-limit for your island.")); event.setUseItemInHand(Event.Result.DENY); event.setUseInteractedBlock(Event.Result.DENY); @@ -85,43 +78,151 @@ public void onSpawnEggEvent(PlayerInteractEvent event) { } } - private boolean isSpawnEgg(ItemStack item) { - return item.getType().name().endsWith("_SPAWN_EGG") && item.getData() instanceof MonsterEggs; + private static @Nullable EntityType getSpawnEggType(@NotNull ItemStack itemStack) { + if (itemStack.getItemMeta() instanceof SpawnEggMeta spawnEggMeta) { + EntitySnapshot spawnedEntity = spawnEggMeta.getSpawnedEntity(); + if (spawnedEntity != null) { + return spawnedEntity.getEntityType(); + } else { + String key = itemStack.getType().getKey().toString(); + String entityKey = key.replace("_spawn_egg", ""); + NamespacedKey namespacedKey = Objects.requireNonNull(NamespacedKey.fromString(entityKey)); + return Registry.ENTITY_TYPE.get(namespacedKey); + } + } else { + return null; + } } + private int fastpos(int pos){ + pos+=64; + return (pos<0)?((pos+1)/128):(pos/128); + } + + @EventHandler(ignoreCancelled = true) public void onCreatureSpawn(CreatureSpawnEvent event) { if (event == null || !plugin.getWorldManager().isSkyAssociatedWorld(event.getLocation().getWorld())) { return; // Bail out, we don't care } - if (!event.isCancelled() && ADMIN_INITIATED.contains(event.getSpawnReason())) { + if (event.getSpawnReason().equals(CreatureSpawnEvent.SpawnReason.SPAWNER_EGG)) { return; // Allow it, the above method would have blocked it if it should be blocked. } + if (event.getEntity() instanceof Phantom) { + String island = fastpos(event.getLocation().getBlockX()) + "," + fastpos(event.getLocation().getBlockZ()); + + if(newIsland.get(island) == null){ + IslandInfo is = plugin.getIslandInfo(event.getLocation()); + if(is != null){ + PlayerInfo pi = plugin.getPlayerInfo(is.getLeader()); + if (pi != null && pi.checkChallenge("page1finished")==0){ + // newbie protection + event.setCancelled(true); + pi = null; is = null; + newIsland.put(island, true); + log(Level.INFO, "Add inf Phantom Protection: "+island+" : protect"); + return; + } + pi = null; is = null; + newIsland.put(island, false); + log(Level.INFO, "Add inf Phantom Protection: "+island+" : Not-protect"); + } + }else{ + if (newIsland.get(island) == true){ + event.setCancelled(true); + return; + } + } + } + if (event.getEntity() instanceof WaterMob) { + Random r = new Random(); + if (r.nextInt(20) != 0){ + event.setCancelled(true); + return; + } + } + if (event.getEntity() instanceof ArmorStand){ + return; + } checkLimits(event, event.getEntity().getType(), event.getLocation()); if (event.getEntity() instanceof WaterMob) { Location loc = event.getLocation(); - if (isDeepOceanBiome(loc) && isPrismarineRoof(loc)) { - loc.getWorld().spawnEntity(loc, EntityType.GUARDIAN); + if(doPrismarineRoof(loc)) event.setCancelled(true); - } } - if (event.getSpawnReason() == CreatureSpawnEvent.SpawnReason.BUILD_WITHER && event.getEntity() instanceof Wither) { - IslandInfo islandInfo = plugin.getIslandInfo(event.getLocation()); - if (islandInfo != null && islandInfo.getLeader() != null) { - event.getEntity().setCustomName(I18nUtil.tr("{0}''s Wither", islandInfo.getLeader())); - event.getEntity().setMetadata("fromIsland", new FixedMetadataValue(plugin, islandInfo.getName())); + if (!event.isCancelled() && event.getEntity() instanceof Enderman) { + Location loc = event.getLocation(); + + if(isPurpurFloor(loc)) { + if (isEndBiome(loc)) { + Random r = new Random(); + if (r.nextInt(10) == 0) { + loc.getWorld().spawnEntity(loc, EntityType.SHULKER); + event.setCancelled(true); + } + } else { + loc.getWorld().spawnEntity(loc, EntityType.SHULKER); + event.setCancelled(true); + } } } } - private boolean isPrismarineRoof(Location loc) { + private boolean isPurpurFloor(Location loc){ + List purpurBlocks = Arrays.asList(Material.PURPUR_BLOCK, Material.PURPUR_PILLAR, Material.PURPUR_SLAB); + loc.setY(loc.getY()-1); + boolean ret=purpurBlocks.contains(loc.getBlock().getType()); + loc.setY(loc.getY()+1); + return ret; + } + + private boolean doPrismarineRoof(Location loc) { List prismarineBlocks = Arrays.asList(Material.PRISMARINE, Material.PRISMARINE_BRICKS, Material.DARK_PRISMARINE); - return prismarineBlocks.contains(LocationUtil.findRoofBlock(loc).getType()); + Location tloc = loc.clone(); + if(tloc.getBlockY()<47 || tloc.getBlockY()>64) + return false; + while(tloc.getBlockY()<=70){ + if (tloc.getBlock().getType() == Material.WATER){ + tloc.add(0,1,0); + }else{ + if(prismarineBlocks.contains(tloc.getBlock().getType())){ + Random r = new Random(); + if (r.nextInt(5) == 0){ + if(r.nextInt(1000) == 0){ + if(r.nextInt(1000)==0){ + Drowned drowned= (Drowned) loc.getWorld().spawnEntity(loc, EntityType.DROWNED); + drowned.getEquipment().setItemInMainHand(new ItemStack(Material.TRIDENT)); + LogUtil.log(Level.INFO, java.time.Clock.systemUTC().instant()+" "+plugin.getIslandInfo(loc).getLeader()+" RANDOM TRIDENT"); + } + else{ + Drowned drowned= (Drowned) loc.getWorld().spawnEntity(loc, EntityType.DROWNED); + if(drowned.getEquipment().getItemInMainHand().equals(new ItemStack(Material.TRIDENT))){ + LogUtil.log(Level.INFO,java.time.Clock.systemUTC().instant()+" "+plugin.getIslandInfo(loc).getLeader()+" DROWNED TRIDENT"); + } + else{ + LogUtil.log(Level.INFO,java.time.Clock.systemUTC().instant()+" "+plugin.getIslandInfo(loc).getLeader()+" DROWNED NO TRIDENT"); + } + } + } + else{ + loc.getWorld().spawnEntity(loc, EntityType.GUARDIAN); + } + } + return true; + }else if(tloc.getBlock().getType() == Material.SEA_LANTERN){ + Random r = new Random(); + if (r.nextInt(50) == 0) + loc.getWorld().spawnEntity(loc, EntityType.ELDER_GUARDIAN); + return true; + } + return false; + } + } + return false; } - private boolean isDeepOceanBiome(Location loc) { - List deepOceans = Arrays.asList(Biome.DEEP_OCEAN, Biome.DEEP_COLD_OCEAN, Biome.DEEP_FROZEN_OCEAN, Biome.DEEP_LUKEWARM_OCEAN, Biome.DEEP_WARM_OCEAN); - return deepOceans.contains(loc.getWorld().getBiome(loc.getBlockX(), loc.getBlockY(), loc.getBlockZ())); + private boolean isEndBiome(Location loc) { + return loc.getWorld().getBiome(loc.getBlockX(), loc.getBlockZ())==Biome.THE_END; } private void checkLimits(Cancellable event, EntityType entityType, Location location) { @@ -133,12 +234,7 @@ private void checkLimits(Cancellable event, EntityType entityType, Location loca event.setCancelled(true); // Only allow spawning on active islands... return; } - if (entityType.getEntityClass().isAssignableFrom(Ghast.class) && location.getWorld().getEnvironment() != World.Environment.NETHER) { - // Disallow ghasts for now... - event.setCancelled(true); - return; - } - us.talabrek.ultimateskyblock.api.IslandInfo islandInfo = plugin.getIslandInfo(islandName); + IslandInfo islandInfo = plugin.getIslandInfo(islandName); if (islandInfo == null) { // Disallow spawns on inactive islands event.setCancelled(true); diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/event/ToolMenuEvents.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/event/ToolMenuEvents.java index fd18b147e..5a0b8d958 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/event/ToolMenuEvents.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/event/ToolMenuEvents.java @@ -1,5 +1,7 @@ package us.talabrek.ultimateskyblock.event; +import com.google.inject.Inject; +import com.google.inject.Singleton; import dk.lockfuglsang.minecraft.util.ItemStackUtil; import org.bukkit.GameMode; import org.bukkit.Material; @@ -11,6 +13,7 @@ import org.bukkit.event.block.Action; import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; import us.talabrek.ultimateskyblock.challenge.Challenge; import us.talabrek.ultimateskyblock.uSkyBlock; @@ -20,16 +23,17 @@ /** * Events triggering the tool-menu */ +@Singleton public class ToolMenuEvents implements Listener { - public static final String COMPLETE_CHALLENGE_CMD = "challenges complete "; private final uSkyBlock plugin; private final ItemStack tool; - private final Map commandMap = new HashMap<>(); + private final Map commandMap = new HashMap<>(); - public ToolMenuEvents(uSkyBlock plugin) { + @Inject + public ToolMenuEvents(@NotNull uSkyBlock plugin) { this.plugin = plugin; - tool = ItemStackUtil.createItemStack(plugin.getConfig().getString("tool-menu.tool", "SAPLING")); + tool = ItemStackUtil.createItemStack(plugin.getConfig().getString("tool-menu.tool", Material.OAK_SAPLING.toString())); registerChallenges(); registerCommands(); } @@ -61,8 +65,8 @@ private void registerChallenges() { @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) public void onBlockHit(PlayerInteractEvent e) { - if (e == null || e.getClickedBlock() == null - || e.getAction() != Action.LEFT_CLICK_BLOCK || e.getPlayer().getGameMode() != GameMode.SURVIVAL) { + if (e.getClickedBlock() == null || e.getAction() != Action.LEFT_CLICK_BLOCK || + e.getPlayer().getGameMode() != GameMode.SURVIVAL) { return; } Player player = e.getPlayer(); @@ -72,16 +76,8 @@ public void onBlockHit(PlayerInteractEvent e) { // We are in a skyworld, a block has been hit, with the tool Material block = e.getClickedBlock().getType(); - short data = e.getClickedBlock().getData(); - String itemId = ItemStackUtil.asString(new ItemStack(block, 1, data)); - if (commandMap.containsKey(itemId)) { - doCmd(e, player, itemId); - } - if (!e.isCancelled()) { - itemId = ItemStackUtil.asString(new ItemStack(block, 1)); - if (commandMap.containsKey(itemId)) { - doCmd(e, player, itemId); - } + if (commandMap.containsKey(block.toString())) { + doCmd(e, player, block.toString()); } } @@ -99,7 +95,13 @@ private void doCmd(PlayerInteractEvent e, Player player, String itemId) { } } + /** + * Checks if the given {@link ItemStack} is the configured tool for the tool menu. + * + * @param item ItemStack to check. + * @return True if it is the configured tool, false otherwise. + */ private boolean isTool(ItemStack item) { - return item != null && tool != null && item.getType() == tool.getType() && item.getDurability() == tool.getDurability(); + return item != null && tool != null && item.getType() == tool.getType(); } } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/event/WitherTagEvents.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/event/WitherTagEvents.java new file mode 100644 index 000000000..caad720ad --- /dev/null +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/event/WitherTagEvents.java @@ -0,0 +1,40 @@ +package us.talabrek.ultimateskyblock.event; + +import com.google.inject.Inject; +import com.google.inject.Singleton; +import dk.lockfuglsang.minecraft.po.I18nUtil; +import org.bukkit.NamespacedKey; +import org.bukkit.entity.Wither; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.entity.CreatureSpawnEvent; +import org.bukkit.persistence.PersistentDataType; +import org.jetbrains.annotations.NotNull; +import us.talabrek.ultimateskyblock.api.IslandInfo; +import us.talabrek.ultimateskyblock.uSkyBlock; + +@Singleton +public class WitherTagEvents implements Listener { + + static final String ENTITY_ORIGIN_METADATA = "from-island"; + private final uSkyBlock plugin; + + @Inject + public WitherTagEvents(@NotNull uSkyBlock plugin) { + this.plugin = plugin; + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onCreatureSpawn(CreatureSpawnEvent event) { + if (event.getSpawnReason() == CreatureSpawnEvent.SpawnReason.BUILD_WITHER + && event.getEntity() instanceof Wither wither) { + IslandInfo islandInfo = plugin.getIslandInfo(event.getLocation()); + if (islandInfo != null && islandInfo.getLeader() != null) { + wither.setCustomName(I18nUtil.tr("{0}''s Wither", islandInfo.getLeader())); + NamespacedKey key = new NamespacedKey(plugin, ENTITY_ORIGIN_METADATA); + wither.getPersistentDataContainer().set(key, PersistentDataType.STRING, islandInfo.getName()); + } + } + } +} diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/event/WorldGuardEvents.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/event/WorldGuardEvents.java index 48fd54c54..120280c72 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/event/WorldGuardEvents.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/event/WorldGuardEvents.java @@ -1,11 +1,14 @@ package us.talabrek.ultimateskyblock.event; +import com.google.inject.Inject; +import com.google.inject.Singleton; import org.bukkit.Location; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerMoveEvent; import org.bukkit.util.Vector; +import org.jetbrains.annotations.NotNull; import us.talabrek.ultimateskyblock.handler.WorldGuardHandler; import us.talabrek.ultimateskyblock.island.IslandInfo; import us.talabrek.ultimateskyblock.uSkyBlock; @@ -15,10 +18,12 @@ /** * Replacement for the WG ENTRY/EXIT deny flags. */ +@Singleton public class WorldGuardEvents implements Listener { private final uSkyBlock plugin; - public WorldGuardEvents(uSkyBlock plugin) { + @Inject + public WorldGuardEvents(@NotNull uSkyBlock plugin) { this.plugin = plugin; } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/gui/GuiListener.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/gui/GuiListener.java new file mode 100644 index 000000000..34a1e6233 --- /dev/null +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/gui/GuiListener.java @@ -0,0 +1,37 @@ +package us.talabrek.ultimateskyblock.gui; + +import com.google.inject.Inject; +import com.google.inject.Singleton; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.inventory.InventoryCloseEvent; +import org.bukkit.event.inventory.InventoryOpenEvent; +import org.jetbrains.annotations.NotNull; + +import static java.util.Objects.requireNonNull; + +@Singleton +public class GuiListener implements Listener { + private final GuiManager guiManager; + + @Inject + public GuiListener(@NotNull GuiManager guiManager) { + this.guiManager = requireNonNull(guiManager); + } + + @EventHandler + public void onClick(@NotNull InventoryClickEvent event) { + this.guiManager.handleClick(event); + } + + @EventHandler + public void onOpen(@NotNull InventoryOpenEvent event) { + this.guiManager.handleOpen(event); + } + + @EventHandler + public void onClose(@NotNull InventoryCloseEvent event) { + this.guiManager.handleClose(event); + } +} diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/gui/GuiManager.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/gui/GuiManager.java new file mode 100644 index 000000000..900334a85 --- /dev/null +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/gui/GuiManager.java @@ -0,0 +1,70 @@ +package us.talabrek.ultimateskyblock.gui; + +import com.google.inject.Singleton; +import org.bukkit.entity.Player; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.inventory.InventoryCloseEvent; +import org.bukkit.event.inventory.InventoryOpenEvent; +import org.bukkit.inventory.Inventory; +import org.jetbrains.annotations.NotNull; + +import java.util.HashMap; +import java.util.Map; + +import static java.util.Objects.requireNonNull; + +/* + * New GUI system as of Feb 2025. + * This system is designed to be more flexible, easier to use and extend, and to separate concerns. + * All old GUIs from us.talabrek.ultimateskyblock.menu should be converted to this system. + */ +@Singleton +public class GuiManager { + + private final Map activeInventories = new HashMap<>(); + + public void openGui(@NotNull InventoryGui gui, @NotNull Player player) { + requireNonNull(gui); + requireNonNull(player); + this.registerHandledInventory(gui.getInventory(), gui); + player.openInventory(gui.getInventory()); + } + + public void registerHandledInventory(@NotNull Inventory inventory, @NotNull InventoryHandler handler) { + requireNonNull(inventory); + requireNonNull(handler); + this.activeInventories.put(inventory, handler); + } + + public void unregisterInventory(@NotNull Inventory inventory) { + requireNonNull(inventory); + this.activeInventories.remove(inventory); + } + + public void handleClick(@NotNull InventoryClickEvent event) { + requireNonNull(event); + InventoryHandler handler = this.activeInventories.get(event.getInventory()); + if (handler != null) { + event.setCancelled(true); + handler.onClick(event); + } + } + + public void handleOpen(@NotNull InventoryOpenEvent event) { + requireNonNull(event); + InventoryHandler handler = this.activeInventories.get(event.getInventory()); + if (handler != null) { + handler.onOpen(event); + } + } + + public void handleClose(@NotNull InventoryCloseEvent event) { + requireNonNull(event); + Inventory inventory = event.getInventory(); + InventoryHandler handler = this.activeInventories.get(inventory); + if (handler != null) { + handler.onClose(event); + this.unregisterInventory(inventory); + } + } +} diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/gui/InventoryButton.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/gui/InventoryButton.java new file mode 100644 index 000000000..ed9443f15 --- /dev/null +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/gui/InventoryButton.java @@ -0,0 +1,38 @@ +package us.talabrek.ultimateskyblock.gui; + +import org.bukkit.entity.Player; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; + +import java.util.function.BiConsumer; +import java.util.function.Function; + +import static java.util.Objects.requireNonNull; + +public class InventoryButton { + + private Function iconCreator; + private BiConsumer eventConsumer = (player, event) -> { + }; + + public @NotNull InventoryButton creator(@NotNull Function iconCreator) { + this.iconCreator = iconCreator; + return this; + } + + public @NotNull InventoryButton consumer(@NotNull BiConsumer eventConsumer) { + this.eventConsumer = eventConsumer; + return this; + } + + public @NotNull BiConsumer getEventConsumer() { + requireNonNull(this.eventConsumer, "EventConsumer is not set"); + return this.eventConsumer; + } + + public @NotNull Function getIconCreator() { + requireNonNull(this.iconCreator, "IconCreator is not set"); + return this.iconCreator; + } +} diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/gui/InventoryGui.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/gui/InventoryGui.java new file mode 100644 index 000000000..025817802 --- /dev/null +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/gui/InventoryGui.java @@ -0,0 +1,82 @@ +package us.talabrek.ultimateskyblock.gui; + +import dk.lockfuglsang.minecraft.util.ItemStackUtil; +import org.bukkit.entity.Player; +import org.bukkit.event.inventory.ClickType; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.inventory.InventoryCloseEvent; +import org.bukkit.event.inventory.InventoryOpenEvent; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +import static java.util.Objects.requireNonNull; + +public abstract class InventoryGui implements InventoryHandler { + + private static final Set REGULAR_CLICK_TYPES = Set.of( + ClickType.LEFT, + ClickType.RIGHT, + ClickType.SHIFT_LEFT, + ClickType.SHIFT_RIGHT, + ClickType.DOUBLE_CLICK, + ClickType.CREATIVE + ); + private final Inventory inventory; + private final Map buttonMap = new HashMap<>(); + + public InventoryGui(@NotNull Inventory inventory) { + this.inventory = requireNonNull(inventory); + } + + public @NotNull Inventory getInventory() { + return this.inventory; + } + + public void addButton(int slot, @NotNull InventoryButton button) { + requireNonNull(button); + this.buttonMap.put(slot, button); + } + + public void decorate(@NotNull Player player) { + requireNonNull(player); + this.buttonMap.forEach((slot, button) -> { + ItemStack icon = button.getIconCreator().apply(player); + icon = ItemStackUtil.asDisplayItem(icon); + this.inventory.setItem(slot, icon); + }); + } + + @Override + public void onClick(@NotNull InventoryClickEvent event) { + requireNonNull(event); + if (!Objects.equals(event.getClickedInventory(), this.inventory)) { + // Ignore clicks in other (lower) inventory our outside the inventory. + return; + } + if (!REGULAR_CLICK_TYPES.contains(event.getClick())) { + // Ignore non-regular click types. + return; + } + int slot = event.getSlot(); + InventoryButton button = this.buttonMap.get(slot); + if (button != null) { + button.getEventConsumer().accept((Player) event.getWhoClicked(), event); + } + } + + @Override + public void onOpen(@NotNull InventoryOpenEvent event) { + requireNonNull(event); + this.decorate((Player) event.getPlayer()); + } + + @Override + public void onClose(@NotNull InventoryCloseEvent event) { + } +} diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/gui/InventoryHandler.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/gui/InventoryHandler.java new file mode 100644 index 000000000..4ae2842c0 --- /dev/null +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/gui/InventoryHandler.java @@ -0,0 +1,16 @@ +package us.talabrek.ultimateskyblock.gui; + +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.inventory.InventoryCloseEvent; +import org.bukkit.event.inventory.InventoryOpenEvent; +import org.jetbrains.annotations.NotNull; + +public interface InventoryHandler { + + void onClick(@NotNull InventoryClickEvent event); + + void onOpen(@NotNull InventoryOpenEvent event); + + void onClose(@NotNull InventoryCloseEvent event); + +} diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/ActionBarHandler.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/ActionBarHandler.java deleted file mode 100644 index d524f079a..000000000 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/ActionBarHandler.java +++ /dev/null @@ -1,37 +0,0 @@ -package us.talabrek.ultimateskyblock.handler; - -import org.bukkit.Bukkit; -import org.bukkit.entity.Player; -import us.talabrek.ultimateskyblock.handler.actionbarapi.ActionBarAPIAdaptor; - -/** - * Static handler allowing for soft-depend. - */ -public enum ActionBarHandler {; - public static boolean isEnabled() { - return isActionBarAPI(); - } - - public static boolean isActionBarAPI() { - return Bukkit.getPluginManager().isPluginEnabled("ActionBarAPI"); - } - - public static void sendActionBar(Player player, String message) { - try { - if (isActionBarAPI()) { - ActionBarAPIAdaptor.sendActionBar(player, message); - } else { - sendFallback(player, message); - } - } catch (Exception e) { - // Suppress incompatibilities - this is just a "best-effort" approach. - sendFallback(player, message); - } - } - - private static void sendFallback(Player player, String message) { - if (player != null) { - player.sendMessage(message); - } - } -} diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/AsyncWorldEditHandler.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/AsyncWorldEditHandler.java index 2f56ed9c8..e35c0554b 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/AsyncWorldEditHandler.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/AsyncWorldEditHandler.java @@ -2,6 +2,7 @@ import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.regions.Region; +import com.sk89q.worldedit.util.SideEffectSet; import com.sk89q.worldedit.world.World; import dk.lockfuglsang.minecraft.util.VersionUtil; import org.bukkit.Bukkit; @@ -53,17 +54,17 @@ public static AWEAdaptor getAWEAdaptor() { if (!uSkyBlock.getInstance().getConfig().getBoolean("asyncworldedit.enabled", true)) { return NULL_ADAPTOR; } - Plugin fawe = getFAWE(); - Plugin awe = getAWE(); - String className = null; + //Plugin fawe = getFAWE(); + Plugin fawe = null; // Disabled b/c 1.18.1 releases + String className; if (fawe != null) { VersionUtil.Version version = VersionUtil.getVersion(fawe.getDescription().getVersion()); className = "us.talabrek.ultimateskyblock.handler.asyncworldedit.FAWEAdaptor"; try { - adaptor = (AWEAdaptor) Class.forName(className).newInstance(); - log(Level.INFO, "Hooked into FAWE " + version); - } catch (InstantiationException | IllegalAccessException | ClassNotFoundException | NoClassDefFoundError e) { - log(Level.WARNING, "Unable to locate FAWE adaptor for version " + version + ": " + e); + adaptor = (AWEAdaptor) Class.forName(className).getDeclaredConstructor().newInstance(); + log(Level.INFO, "Hooked into FAWE version " + version); + } catch (Exception ex) { + log(Level.WARNING, "Unable to locate FAWE adaptor for version " + version + ": " + ex); adaptor = NULL_ADAPTOR; } } else { @@ -81,13 +82,9 @@ public static Plugin getFAWE() { return Bukkit.getPluginManager().getPlugin("FastAsyncWorldEdit"); } - public static Plugin getAWE() { - return Bukkit.getPluginManager().getPlugin("AsyncWorldEdit"); - } - public static final AWEAdaptor NULL_ADAPTOR = new AWEAdaptor() { @Override - public void onEnable(Plugin plugin) { + public void onEnable(uSkyBlock plugin) { } @@ -102,7 +99,7 @@ public void loadIslandSchematic(File file, Location origin, PlayerPerk playerPer } @Override - public void onDisable(Plugin plugin) { + public void onDisable(uSkyBlock plugin) { } @@ -113,18 +110,13 @@ public EditSession createEditSession(World world, int maxBlocks) { @Override public void regenerate(final Region region, final Runnable onCompletion) { - uSkyBlock.getInstance().sync(new Runnable() { - @Override - public void run() { - try { - final EditSession editSession = WorldEditHandler.createEditSession(region.getWorld(), region.getArea() * 255); - editSession.setReorderMode(EditSession.ReorderMode.MULTI_STAGE); - editSession.setFastMode(true); - editSession.getWorld().regenerate(region, editSession); - editSession.flushSession(); - } finally { - onCompletion.run(); - } + uSkyBlock.getInstance().getScheduler().sync(() -> { + try (EditSession editSession = WorldEditHandler.createEditSession(region.getWorld(), (int) region.getVolume())) { + editSession.setReorderMode(EditSession.ReorderMode.MULTI_STAGE); + editSession.setSideEffectApplier(SideEffectSet.defaults()); + editSession.getWorld().regenerate(region, editSession); + } finally { + onCompletion.run(); } }); } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/ChunkComparator.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/ChunkComparator.java deleted file mode 100644 index aede1edb0..000000000 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/ChunkComparator.java +++ /dev/null @@ -1,25 +0,0 @@ -package us.talabrek.ultimateskyblock.handler; - -import com.sk89q.worldedit.math.BlockVector2; - -import java.util.Comparator; - -public class ChunkComparator implements Comparator { - private BlockVector2 origin; - - public ChunkComparator(BlockVector2 origin) { - this.origin = origin; - } - - @Override - public int compare(BlockVector2 o1, BlockVector2 o2) { - int cmp = (int) Math.round(origin.distanceSq(o1) - origin.distanceSq(o2)); - if (cmp == 0) { - cmp = o1.getBlockX() - o2.getBlockX(); - } - if (cmp == 0) { - cmp = o1.getBlockZ() - o2.getBlockZ(); - } - return cmp; - } -} diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/ConfirmHandler.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/ConfirmHandler.java index 55ed18d92..a2435b498 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/ConfirmHandler.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/ConfirmHandler.java @@ -1,10 +1,15 @@ package us.talabrek.ultimateskyblock.handler; +import com.google.inject.Inject; +import com.google.inject.Singleton; import dk.lockfuglsang.minecraft.po.I18nUtil; import org.bukkit.entity.Player; -import us.talabrek.ultimateskyblock.uSkyBlock; -import dk.lockfuglsang.minecraft.util.TimeUtil; +import org.jetbrains.annotations.NotNull; +import us.talabrek.ultimateskyblock.PluginConfig; +import us.talabrek.ultimateskyblock.util.Scheduler; +import java.time.Duration; +import java.time.Instant; import java.util.Map; import java.util.UUID; import java.util.WeakHashMap; @@ -12,68 +17,72 @@ /** * Handles confirms. */ +@Singleton public class ConfirmHandler { private final Map confirmMap = new WeakHashMap<>(); - private final uSkyBlock plugin; - private final int timeout; + private final Scheduler scheduler; + private final PluginConfig config; + private final Duration timeout; - public ConfirmHandler(uSkyBlock plugin, int timeout) { - this.plugin = plugin; - this.timeout = timeout; + @Inject + public ConfirmHandler( + @NotNull Scheduler scheduler, + @NotNull PluginConfig config + ) { + this.scheduler = scheduler; + this.config = config; + this.timeout = Duration.ofSeconds(config.getYamlConfig().getInt("options.advanced.confirmTimeout", 10)); } - public long millisLeft(final Player player, final String cmd) { + public @NotNull Duration durationLeft(@NotNull Player player, @NotNull String cmd) { UUID uuid = player.getUniqueId(); if (confirmMap.containsKey(uuid)) { ConfirmCommand command = confirmMap.get(uuid); if (command != null && command.isValid(cmd, timeout)) { - return TimeUtil.secondsAsMillis(timeout) - (System.currentTimeMillis() - command.tstamp); + return timeout.minus(Duration.between(command.timeStamp, Instant.now())); } } - return -1; + return Duration.ZERO; } - public boolean checkCommand(final Player player, final String cmd) { - if (!confirmationsActiveFor(cmd)) { + public boolean checkCommand(@NotNull Player player, @NotNull String command) { + if (!confirmationsActiveFor(command)) { return true; } UUID uuid = player.getUniqueId(); if (confirmMap.containsKey(uuid)) { ConfirmCommand confirmCommand = confirmMap.get(uuid); - if (confirmCommand != null && confirmCommand.isValid(cmd, timeout)) { + if (confirmCommand != null && confirmCommand.isValid(command, timeout)) { confirmMap.remove(uuid); return true; } } - confirmMap.put(uuid, new ConfirmCommand(cmd)); - plugin.async(new Runnable() { - @Override - public void run() { - ConfirmCommand confirmCommand = confirmMap.remove(player.getUniqueId()); - if (confirmCommand != null && player != null && player.isOnline()) { - player.sendMessage(I18nUtil.tr("\u00a79{0}\u00a77 timed out", cmd)); - } + confirmMap.put(uuid, new ConfirmCommand(command)); + scheduler.async(() -> { + ConfirmCommand confirmCommand = confirmMap.remove(uuid); + if (confirmCommand != null && player.isOnline()) { + player.sendMessage(I18nUtil.tr("\u00a79{0}\u00a77 timed out", command)); } - }, TimeUtil.secondsAsMillis(timeout)); - player.sendMessage(I18nUtil.tr("\u00a7eDoing \u00a79{0}\u00a7e is \u00a7cRISKY\u00a7e. Repeat the command within \u00a7a{1}\u00a7e seconds to accept!", cmd, timeout)); + }, timeout); + player.sendMessage(I18nUtil.tr("\u00a7eDoing \u00a79{0}\u00a7e is \u00a7cRISKY\u00a7e. Repeat the command within \u00a7a{1}\u00a7e seconds to accept!", command, timeout)); return false; } - private boolean confirmationsActiveFor(String cmd) { - return plugin.getConfig().getBoolean("confirmation." + cmd.replaceAll("[^a-z\\ ]", ""), true); + private boolean confirmationsActiveFor(@NotNull String command) { + return config.getYamlConfig().getBoolean("confirmation." + command.replaceAll("[^a-z\\ ]", ""), true); } private static class ConfirmCommand { - private final String cmd; - private final long tstamp; + private final String command; + private final Instant timeStamp; - private ConfirmCommand(String cmd) { - this.cmd = cmd; - this.tstamp = System.currentTimeMillis(); + private ConfirmCommand(String command) { + this.command = command; + this.timeStamp = Instant.now(); } - public boolean isValid(String cmd, long timeout) { - return this.cmd.equals(cmd) && tstamp >= (System.currentTimeMillis() - TimeUtil.secondsAsMillis(timeout)); + public boolean isValid(String command, Duration timeout) { + return this.command.equals(command) && timeStamp.plus(timeout).isAfter(Instant.now()); } } } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/CooldownHandler.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/CooldownHandler.java index 4184c17a1..1a1d7e439 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/CooldownHandler.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/CooldownHandler.java @@ -1,80 +1,66 @@ package us.talabrek.ultimateskyblock.handler; +import com.google.inject.Inject; +import com.google.inject.Singleton; import org.bukkit.entity.Player; -import us.talabrek.ultimateskyblock.uSkyBlock; -import dk.lockfuglsang.minecraft.util.TimeUtil; +import org.jetbrains.annotations.NotNull; +import us.talabrek.ultimateskyblock.util.Scheduler; -import java.util.Collections; +import java.time.Clock; +import java.time.Duration; +import java.time.Instant; +import java.util.HashMap; import java.util.Map; import java.util.UUID; -import java.util.WeakHashMap; -import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; /** * Responsible for handling various cooldowns on commands. */ +@Singleton public class CooldownHandler { - private final Map> cooldowns = new WeakHashMap<>(); - private final uSkyBlock plugin; - public CooldownHandler(uSkyBlock plugin) { - this.plugin = plugin; + private final Map cooldownExpires = new HashMap<>(); + private final Clock clock; + private final Scheduler scheduler; + + private record KeyPair(@NotNull UUID uuid, @NotNull String cmd) { + } + + @Inject + public CooldownHandler(@NotNull Clock clock, @NotNull Scheduler scheduler) { + this.clock = clock; + this.scheduler = scheduler; } - /** - * Returns the number of seconds left on the cooldown. - * 0 if it's not on cooldown anymore. - * @param player The player - * @param cmd The command to check - * @return - */ - public int getCooldown(org.bukkit.entity.Player player, String cmd) { + public Duration getCooldown(@NotNull Player player, @NotNull String cmd) { if (player.hasPermission("usb.mod.bypasscooldowns") || player.hasPermission("usb.exempt.cooldown." + cmd)) { - return 0; - } - Map map = cooldowns.get(player.getUniqueId()); - if (map != null) { - Long timeout = map.get(cmd); - long now = System.currentTimeMillis(); - return timeout != null && timeout > now ? TimeUtil.millisAsSeconds(timeout - now) : 0; + return Duration.ZERO; } - return 0; + var now = clock.instant(); + var end = cooldownExpires.getOrDefault(new KeyPair(player.getUniqueId(), cmd), now); + var remaining = Duration.between(now, end); + return remaining.isNegative() ? Duration.ZERO : remaining; } - public void resetCooldown(final Player player, final String cmd, int cooldownSecs) { - UUID uuid = player.getUniqueId(); - if (!cooldowns.containsKey(uuid)) { - Map cdMap = new ConcurrentHashMap<>(); - cooldowns.put(uuid, cdMap); + public void resetCooldown(@NotNull Player player, @NotNull String cmd, @NotNull Duration cooldown) { + var key = new KeyPair(player.getUniqueId(), cmd); + if (cooldown.isZero() || cooldown.isNegative()) { + cooldownExpires.remove(key); + } else { + cooldownExpires.put(key, clock.instant().plus(cooldown)); + scheduler.sync(() -> cooldownExpires.remove(key), cooldown.plusSeconds(1)); } - if (cooldownSecs == 0) { - cooldowns.get(uuid).remove(cmd); - return; - } - cooldowns.get(uuid).put(cmd, System.currentTimeMillis() + TimeUtil.secondsAsMillis(cooldownSecs)); - plugin.async(new Runnable() { - @Override - public void run() { - Map cmdMap = cooldowns.get(player.getUniqueId()); - if (cmdMap != null) { - cmdMap.remove(cmd); - } - } - }, TimeUtil.secondsAsMillis(cooldownSecs)); } - public boolean clearCooldown(Player player, String cmd) { - Map cmdMap = cooldowns.get(player.getUniqueId()); - if (cmdMap != null) { - return cmdMap.remove(cmd) != null; - } - return false; + public boolean clearCooldown(@NotNull Player player, @NotNull String cmd) { + var key = new KeyPair(player.getUniqueId(), cmd); + return cooldownExpires.remove(key) != null; } - public Map getCooldowns(UUID uuid) { - if (cooldowns.containsKey(uuid)) { - return cooldowns.get(uuid); - } - return Collections.emptyMap(); + public Map getCooldowns(@NotNull UUID uuid) { + return cooldownExpires.entrySet().stream() + .filter(e -> e.getKey().uuid().equals(uuid)) + .collect(Collectors.toMap(e -> e.getKey().cmd(), Map.Entry::getValue)); } } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/EntitySpawner.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/EntitySpawner.java deleted file mode 100644 index d99e857c2..000000000 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/EntitySpawner.java +++ /dev/null @@ -1,29 +0,0 @@ -package us.talabrek.ultimateskyblock.handler; - -import org.bukkit.Location; -import org.bukkit.Material; -import org.bukkit.entity.Blaze; -import org.bukkit.entity.EntityType; -import org.bukkit.entity.Skeleton; -import org.bukkit.entity.WitherSkeleton; -import org.bukkit.inventory.ItemStack; - -/** - * Abstraction for spawning entities (cross-version-support) - */ -@SuppressWarnings("UnusedReturnValue") -public class EntitySpawner { - public WitherSkeleton spawnWitherSkeleton(Location location) { - WitherSkeleton mob = (WitherSkeleton) location.getWorld().spawnEntity(location, EntityType.WITHER_SKELETON); - mob.getEquipment().setItemInMainHand(new ItemStack(Material.STONE_SWORD, 1)); - return mob; - } - - public Blaze spawnBlaze(Location location) { - return (Blaze) location.getWorld().spawnEntity(location, EntityType.BLAZE); - } - - public Skeleton spawnSkeleton(Location location) { - return (Skeleton) location.getWorld().spawnEntity(location, EntityType.SKELETON); - } -} diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/MultiverseCoreHandler.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/MultiverseCoreHandler.java deleted file mode 100644 index 0c20900a7..000000000 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/MultiverseCoreHandler.java +++ /dev/null @@ -1,88 +0,0 @@ -package us.talabrek.ultimateskyblock.handler; - -import com.onarandombox.MultiverseCore.MultiverseCore; -import com.onarandombox.MultiverseCore.api.MultiverseWorld; -import org.bukkit.Bukkit; -import org.bukkit.Location; -import org.bukkit.World; -import org.bukkit.WorldType; -import org.bukkit.plugin.Plugin; -import us.talabrek.ultimateskyblock.Settings; -import us.talabrek.ultimateskyblock.util.LocationUtil; - -import static us.talabrek.ultimateskyblock.util.LocationUtil.centerOnBlock; - -/** - * Wrapper for the MVCore plugin. - */ -public enum MultiverseCoreHandler {; - public static MultiverseCore getMultiverseCore() { - Plugin plugin = Bukkit.getPluginManager().getPlugin("Multiverse-Core"); - if (plugin instanceof MultiverseCore) { - return (MultiverseCore)plugin; - } - return null; - } - - public static boolean hasMultiverse() { - Plugin plugin = Bukkit.getPluginManager().getPlugin("Multiverse-Core"); - return plugin != null && plugin.isEnabled(); - } - - public static void importWorld(World skyWorld) { - MultiverseCore core = getMultiverseCore(); - if (core != null) { - Location worldSpawn = new Location(skyWorld, 0.5, Settings.island_height + 0.1, 0.5); - if (!core.getMVWorldManager().isMVWorld(skyWorld)) { - core.getMVWorldManager().addWorld(skyWorld.getName(), World.Environment.NORMAL, "0", WorldType.NORMAL, false, "uSkyBlock", false); - } - MultiverseWorld mvWorld = core.getMVWorldManager().getMVWorld(skyWorld); - mvWorld.setEnvironment(World.Environment.NORMAL); - mvWorld.setScaling(1); - mvWorld.setGenerator("uSkyBlock"); - if (Settings.general_spawnSize > 0) { - if (LocationUtil.isEmptyLocation(mvWorld.getSpawnLocation())) { - mvWorld.setAdjustSpawn(false); - Location spawn = centerOnBlock(worldSpawn); - mvWorld.setSpawnLocation(spawn); - skyWorld.setSpawnLocation(spawn.getBlockX(), spawn.getBlockY(), spawn.getBlockZ()); - } - } - if (!Settings.extras_sendToSpawn) { - mvWorld.setRespawnToWorld(mvWorld.getName()); - } - } else if (hasMultiverse()) { - if (!Bukkit.dispatchCommand(Bukkit.getConsoleSender(), "mv import " + skyWorld.getName() + " NORMAL -g uSkyBlock")) { - Bukkit.dispatchCommand(Bukkit.getConsoleSender(), "mv import " + skyWorld.getName() + " NORMAL uSkyBlock"); - } - } - } - - public static void importNetherWorld(World skyNetherWorld) { - MultiverseCore core = getMultiverseCore(); - if (core != null) { - Location worldSpawn = new Location(skyNetherWorld, 0.5, Settings.island_height/2.0 + 0.1, 0.5); - if (!core.getMVWorldManager().isMVWorld(skyNetherWorld)) { - core.getMVWorldManager().addWorld(skyNetherWorld.getName(), World.Environment.NETHER, "0", WorldType.NORMAL, false, "uSkyBlock", false); - } - MultiverseWorld mvWorld = core.getMVWorldManager().getMVWorld(skyNetherWorld); - mvWorld.setEnvironment(World.Environment.NETHER); - mvWorld.setScaling(1.0); - mvWorld.setGenerator("uSkyBlock"); - if (Settings.general_spawnSize > 0) { - if (LocationUtil.isEmptyLocation(mvWorld.getSpawnLocation())) { - mvWorld.setAdjustSpawn(false); - mvWorld.setSpawnLocation(centerOnBlock(worldSpawn)); - skyNetherWorld.setSpawnLocation(worldSpawn.getBlockX(), worldSpawn.getBlockY(), worldSpawn.getBlockZ()); - } - } - if (!Settings.extras_sendToSpawn) { - mvWorld.setRespawnToWorld(Settings.general_worldName); - } - } else if (hasMultiverse()) { - if (!Bukkit.dispatchCommand(Bukkit.getConsoleSender(), "mv import " + skyNetherWorld.getName() + " NETHER -g uSkyBlock")) { - Bukkit.dispatchCommand(Bukkit.getConsoleSender(), "mv import " + skyNetherWorld.getName() + " NETHER uSkyBlock"); - } - } - } -} diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/MultiverseInventoriesHandler.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/MultiverseInventoriesHandler.java deleted file mode 100644 index 405765457..000000000 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/MultiverseInventoriesHandler.java +++ /dev/null @@ -1,19 +0,0 @@ -package us.talabrek.ultimateskyblock.handler; - -import org.bukkit.Bukkit; -import org.bukkit.World; -import us.talabrek.ultimateskyblock.handler.multiverseinventories.MultiverseInventoriesAdaptor; - -/** - * Handler for accessing Multiverse-Inventories, if enabled. - */ -public enum MultiverseInventoriesHandler {; - public static void linkWorlds(World... worlds) { - if (isMVInv()) { - MultiverseInventoriesAdaptor.linkWorlds(worlds); - } - } - public static boolean isMVInv() { - return Bukkit.getPluginManager().isPluginEnabled("Multiverse-Inventories"); - } -} diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/VaultHandler.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/VaultHandler.java deleted file mode 100644 index 9cce3a6b2..000000000 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/VaultHandler.java +++ /dev/null @@ -1,68 +0,0 @@ -package us.talabrek.ultimateskyblock.handler; - -import dk.lockfuglsang.minecraft.util.ItemStackUtil; -import net.milkbowl.vault.economy.Economy; -import net.milkbowl.vault.permission.Permission; -import org.bukkit.entity.Player; -import org.bukkit.inventory.ItemStack; -import org.bukkit.plugin.RegisteredServiceProvider; -import us.talabrek.ultimateskyblock.uSkyBlock; - -public enum VaultHandler {; - private static Permission perms; - private static Economy econ; - - static { - perms = null; - econ = null; - } - - public static void addPermission(final Player player, final String perk) { - perms.playerAdd(player, perk); - } - - public static void removePermission(final Player player, final String perk) { - perms.playerRemove(player, perk); - } - - public static boolean hasPermission(final Player player, final String perk) { - return perms.playerHas(player, perk); - } - - public static boolean setupPermissions() { - final RegisteredServiceProvider rsp = (RegisteredServiceProvider) uSkyBlock.getInstance().getServer().getServicesManager().getRegistration((Class) Permission.class); - if (rsp.getProvider() != null) { - perms = rsp.getProvider(); - } - return perms != null; - } - - public static boolean setupEconomy() { - if (uSkyBlock.getInstance().getServer().getPluginManager().getPlugin("Vault") == null) { - return false; - } - final RegisteredServiceProvider rsp = (RegisteredServiceProvider) uSkyBlock.getInstance().getServer().getServicesManager().getRegistration((Class) Economy.class); - if (rsp == null) { - return false; - } - econ = rsp.getProvider(); - return econ != null; - } - - public static String getItemName(ItemStack stack) { - return ItemStackUtil.getItemName(stack); - } - - public static boolean hasEcon() { - return econ != null; - } - - public static void depositPlayer(Player player, double v) { - econ.depositPlayer(player, v); - } - - public static Economy getEcon() { - return econ; - } - -} diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/WorldEditHandler.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/WorldEditHandler.java index 048f4f114..eff9f34c8 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/WorldEditHandler.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/WorldEditHandler.java @@ -14,8 +14,10 @@ import com.sk89q.worldedit.regions.CuboidRegion; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.session.ClipboardHolder; +import com.sk89q.worldedit.util.SideEffectSet; import com.sk89q.worldguard.protection.regions.ProtectedRegion; -import org.apache.commons.lang.Validate; +import dk.lockfuglsang.minecraft.util.Timer; +import org.apache.commons.lang3.Validate; import org.bukkit.Chunk; import org.bukkit.Location; import org.bukkit.World; @@ -31,6 +33,7 @@ import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; +import java.time.Duration; import java.util.ArrayList; import java.util.HashSet; import java.util.List; @@ -48,24 +51,24 @@ public static void loadIslandSchematic(final File file, final Location origin, P } boolean noAir = false; BlockVector3 to = BlockVector3.at(origin.getBlockX(), origin.getBlockY(), origin.getBlockZ()); - EditSession editSession = WorldEdit.getInstance().getEditSessionFactory().getEditSession(new BukkitWorld(origin.getWorld()), -1); - editSession.setFastMode(true); - ProtectedRegion region = WorldGuardHandler.getIslandRegionAt(origin); - if (region != null) { - editSession.setMask(new RegionMask(getRegion(origin.getWorld(), region))); - } - try { + + try (EditSession editSession = WorldEdit.getInstance().newEditSessionBuilder() + .world(new BukkitWorld(origin.getWorld())).build()) { + editSession.setSideEffectApplier(SideEffectSet.none()); + ProtectedRegion region = WorldGuardHandler.getIslandRegionAt(origin); + if (region != null) { + editSession.setMask(new RegionMask(getRegion(origin.getWorld(), region))); + } ClipboardFormat clipboardFormat = ClipboardFormats.findByFile(file); try (InputStream in = new FileInputStream(file)) { Clipboard clipboard = clipboardFormat.getReader(in).read(); Operation operation = new ClipboardHolder(clipboard) - .createPaste(editSession) - .to(to) - .ignoreAirBlocks(noAir) - .build(); + .createPaste(editSession) + .to(to) + .ignoreAirBlocks(noAir) + .build(); Operations.completeBlindly(operation); } - editSession.flushSession(); } catch (IOException e) { log.log(Level.INFO, "Unable to paste schematic " + file, e); } @@ -169,11 +172,11 @@ public static Set getChunks(Region region) { * M(x) = X mod 16, i.e. Mc = C mod 16. * * Borders: - * O = A + 16 - Ma | A > 0 - * = A - Ma | A <= 0 + * {@code O = A + 16 - Ma | A > 0} + * {@code = A - Ma | A <= 0} * - * Q = C - Mc - 1 | C > 0 && Mc != 15 - * = C + Mc - 16 | C < 0 && Mc != -1 + * {@code Q = C - Mc - 1 | C > 0 && Mc != 15} + * {@code = C + Mc - 16 | C < 0 && Mc != -1} * */ public static Set getBorderRegions(Region region) { @@ -199,23 +202,23 @@ public static Set getBorderRegions(Region region) { // min < minChunk < maxChunk < max if (minModX != 0) { borders.add(new CuboidRegion(region.getWorld(), - BlockVector3.at(minX, minY, minZ), - BlockVector3.at(minChunkX - 1, maxY, maxZ))); + BlockVector3.at(minX, minY, minZ), + BlockVector3.at(minChunkX - 1, maxY, maxZ))); } if (maxModZ != 15 && maxModZ != -1) { borders.add(new CuboidRegion(region.getWorld(), - BlockVector3.at(minChunkX, minY, maxChunkZ + 1), - BlockVector3.at(maxChunkX, maxY, maxZ))); + BlockVector3.at(minChunkX, minY, maxChunkZ + 1), + BlockVector3.at(maxChunkX, maxY, maxZ))); } if (maxModX != 15 && maxModX != -1) { borders.add(new CuboidRegion(region.getWorld(), - BlockVector3.at(maxChunkX + 1, minY, minZ), - BlockVector3.at(maxX, maxY, maxZ))); + BlockVector3.at(maxChunkX + 1, minY, minZ), + BlockVector3.at(maxX, maxY, maxZ))); } if (minModZ != 0) { borders.add(new CuboidRegion(region.getWorld(), - BlockVector3.at(minChunkX, minY, minZ), - BlockVector3.at(maxChunkX, maxY, minChunkZ - 1))); + BlockVector3.at(minChunkX, minY, minZ), + BlockVector3.at(maxChunkX, maxY, minChunkZ - 1))); } return borders; } @@ -227,12 +230,12 @@ public static void clearIsland(@NotNull final World islandWorld, @NotNull final log.finer("Clearing island " + region); uSkyBlock plugin = uSkyBlock.getInstance(); - final long t = System.currentTimeMillis(); + final Timer timer = Timer.start(); final Region cube = getRegion(islandWorld, region); Runnable onCompletion = () -> { - long diff = System.currentTimeMillis() - t; + Duration elapsed = timer.elapsed(); LogUtil.log(Level.INFO, String.format("Cleared island on %s in %d.%03d seconds", - islandWorld.getName(), (diff / 1000), (diff % 1000))); + islandWorld.getName(), elapsed.toSeconds(), elapsed.toMillisPart())); if (afterDeletion != null) { afterDeletion.run(); } @@ -263,7 +266,7 @@ public static Region getRegion(World skyWorld, ProtectedRegion region) { public static boolean isOuterPossible() { return Settings.island_distance >= Settings.island_protectionRange && - ((Settings.island_distance % 32) == 0 || (Settings.island_distance - Settings.island_protectionRange) > 32); + ((Settings.island_distance % 32) == 0 || (Settings.island_distance - Settings.island_protectionRange) > 32); } public static void loadRegion(Location location) { @@ -295,6 +298,6 @@ public static void refreshRegion(Location location) { } public static EditSession createEditSession(com.sk89q.worldedit.world.World bukkitWorld, int maxBlocks) { - return WorldEdit.getInstance().getEditSessionFactory().getEditSession(bukkitWorld, maxBlocks); + return WorldEdit.getInstance().newEditSessionBuilder().world(bukkitWorld).maxBlocks(maxBlocks).build(); } } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/WorldGuardHandler.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/WorldGuardHandler.java index f1e6d64f1..c6fa5e3cd 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/WorldGuardHandler.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/WorldGuardHandler.java @@ -29,7 +29,6 @@ import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; -import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.UUID; @@ -75,7 +74,7 @@ public static boolean protectIsland(uSkyBlock plugin, CommandSender sender, Isla try { RegionManager regionManager = getRegionManager(plugin.getWorldManager().getWorld()); String regionName = islandConfig.getName() + "island"; - if (islandConfig != null && noOrOldRegion(regionManager, regionName, islandConfig)) { + if (noOrOldRegion(regionManager, regionName, islandConfig)) { updateRegion(islandConfig); islandConfig.setRegionVersion(getVersion()); return true; @@ -121,6 +120,7 @@ private static ProtectedCuboidRegion setRegionFlags(IslandInfo islandConfig) { return setRegionFlags(islandConfig, regionName); } + @SuppressWarnings("deprecation") // WorldGuard flags are deprecated to warn developers without replacement option private static ProtectedCuboidRegion setRegionFlags(IslandInfo islandConfig, String regionName) { Location islandLocation = islandConfig.getIslandLocation(); BlockVector3 minPoint = getProtectionVectorRight(islandLocation); @@ -214,11 +214,13 @@ public static void islandUnlock(final CommandSender sender, final String islandN } public static BlockVector3 getProtectionVectorLeft(final Location island) { - return BlockVector3.at(island.getX() + Settings.island_radius - 1, 255.0, island.getZ() + Settings.island_radius - 1); + World world = island.getWorld(); + return BlockVector3.at(island.getX() + Settings.island_radius - 1, world.getMaxHeight() - 1, island.getZ() + Settings.island_radius - 1); } public static BlockVector3 getProtectionVectorRight(final Location island) { - return BlockVector3.at(island.getX() - Settings.island_radius, 0.0, island.getZ() - Settings.island_radius); + World world = island.getWorld(); + return BlockVector3.at(island.getX() - Settings.island_radius, world.getMinHeight(), island.getZ() - Settings.island_radius); } public static String getIslandNameAt(Location location) { @@ -306,11 +308,7 @@ public static Set getIntersectingRegions(Location islandLocatio RegionManager regionManager = getRegionManager(islandLocation.getWorld()); ApplicableRegionSet applicableRegions = regionManager.getApplicableRegions(getIslandRegion(islandLocation)); Set regions = getRegions(applicableRegions); - for (Iterator iterator = regions.iterator(); iterator.hasNext(); ) { - if (iterator.next() instanceof GlobalProtectedRegion) { - iterator.remove(); - } - } + regions.removeIf(protectedRegion -> protectedRegion instanceof GlobalProtectedRegion); log.exiting(CN, "getIntersectingRegions"); return regions; } @@ -322,7 +320,8 @@ public static boolean isIslandIntersectingSpawn(Location islandLocation) { if (r == 0) { return false; } - ProtectedRegion spawn = new ProtectedCuboidRegion("spawn", BlockVector3.at(-r, 0, -r), BlockVector3.at(r, 255, r)); + World world = islandLocation.getWorld(); + ProtectedRegion spawn = new ProtectedCuboidRegion("spawn", BlockVector3.at(-r, world.getMinHeight(), -r), BlockVector3.at(r, world.getMaxHeight() - 1, r)); ProtectedCuboidRegion islandRegion = getIslandRegion(islandLocation); return !islandRegion.getIntersectingRegions(Collections.singletonList(spawn)).isEmpty(); } catch (Exception e) { diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/actionbarapi/ActionBarAPIAdaptor.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/actionbarapi/ActionBarAPIAdaptor.java deleted file mode 100644 index cabd286ff..000000000 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/actionbarapi/ActionBarAPIAdaptor.java +++ /dev/null @@ -1,13 +0,0 @@ -package us.talabrek.ultimateskyblock.handler.actionbarapi; - -import com.connorlinfoot.actionbarapi.ActionBarAPI; -import org.bukkit.entity.Player; - -/** - * Runtime adaptor. - */ -public enum ActionBarAPIAdaptor {; - public static void sendActionBar(Player player, String message) { - ActionBarAPI.sendActionBar(player, message); - } -} diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/asyncworldedit/AWEAdaptor.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/asyncworldedit/AWEAdaptor.java index f78a5d03e..959e57e1b 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/asyncworldedit/AWEAdaptor.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/asyncworldedit/AWEAdaptor.java @@ -6,7 +6,9 @@ import org.bukkit.Location; import org.bukkit.entity.Player; import org.bukkit.plugin.Plugin; +import org.jetbrains.annotations.Nullable; import us.talabrek.ultimateskyblock.player.PlayerPerk; +import us.talabrek.ultimateskyblock.uSkyBlock; import java.io.File; @@ -14,11 +16,11 @@ * Interface for various AWE version-adaptors. */ public interface AWEAdaptor { - void onEnable(Plugin plugin); + void onEnable(uSkyBlock plugin); - void onDisable(Plugin plugin); + void onDisable(uSkyBlock plugin); - void loadIslandSchematic(File file, Location origin, PlayerPerk playerPerk); + void loadIslandSchematic(File file, Location origin, @Nullable PlayerPerk playerPerk); void registerCompletion(Player player); EditSession createEditSession(World world, int maxBlocks); diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/asyncworldedit/PlayerJob.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/asyncworldedit/PlayerJob.java index 4c24f52c5..d40d8748e 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/asyncworldedit/PlayerJob.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/asyncworldedit/PlayerJob.java @@ -2,8 +2,10 @@ import dk.lockfuglsang.minecraft.po.I18nUtil; import org.bukkit.entity.Player; -import us.talabrek.ultimateskyblock.handler.ActionBarHandler; +import us.talabrek.ultimateskyblock.uSkyBlock; +import java.time.Duration; +import java.time.Instant; import java.util.logging.Logger; /** @@ -12,11 +14,12 @@ class PlayerJob { private static final Logger log = Logger.getLogger(PlayerJob.class.getName()); private final Player player; - private final long progressEveryMs; + private final Duration progressInterval; private final double progressEveryPct; - private long lastProgressMs; + private Instant lastProgress; private double percentage; private double lastProgressPct; + private final uSkyBlock plugin; /** * The number of blocks placed in previous jobs @@ -26,11 +29,13 @@ class PlayerJob { private int maxQueuedBlocks; private int startOffset; - PlayerJob(Player player, long progressEveryMs, double progressEveryPct) { + PlayerJob(Player player, Duration progressInterval, double progressEveryPct, uSkyBlock plugin) { this.player = player; - this.progressEveryMs = progressEveryMs; + this.plugin = plugin; + + this.progressInterval = progressInterval; this.progressEveryPct = progressEveryPct; - lastProgressMs = System.currentTimeMillis(); + lastProgress = Instant.now(); lastProgressPct = 0; placedBlocks = 0; maxQueuedBlocks = 0; @@ -55,19 +60,19 @@ public int progress(int blocksPlaced) { log.finer("waiting: " + this); return blocksPlaced; } - this.placedBlocks = Math.min(blocksPlaced-startOffset, (maxQueuedBlocks-offset)); + this.placedBlocks = Math.min(blocksPlaced - startOffset, (maxQueuedBlocks - offset)); this.percentage = Math.floor(Math.min((100d * getPlacedBlocks() / maxQueuedBlocks), 100)); showProgress(I18nUtil.tr("\u00a79Creating island...\u00a7e{0,number,###}%", percentage)); log.finer("progress: " + this); - return blocksPlaced-placedBlocks; + return blocksPlaced - placedBlocks; } private void showProgress(String message) { - long t = System.currentTimeMillis(); - if (t > (lastProgressMs + progressEveryMs) || percentage > (lastProgressPct + progressEveryPct)) { - ActionBarHandler.sendActionBar(player, message); - lastProgressMs = t; - lastProgressPct = Math.floor(percentage/ progressEveryPct) * progressEveryPct; + Instant now = Instant.now(); + if (now.isAfter(lastProgress.plus(progressInterval)) || percentage > (lastProgressPct + progressEveryPct)) { + plugin.getPlayerLogic().getNotificationManager().sendActionBar(player, message); + lastProgress = now; + lastProgressPct = Math.floor(percentage / progressEveryPct) * progressEveryPct; } } @@ -89,18 +94,18 @@ public int mark(int max, int startAt) { @Override public String toString() { return "PlayerJob{" + - "player=" + player + - ", startOffset=" + startOffset + - ", offset=" + offset + - ", placedBlocks=" + placedBlocks + - ", maxQueuedBlocks=" + maxQueuedBlocks + - ", percentage=" + percentage + - '}'; + "player=" + player + + ", startOffset=" + startOffset + + ", offset=" + offset + + ", placedBlocks=" + placedBlocks + + ", maxQueuedBlocks=" + maxQueuedBlocks + + ", percentage=" + percentage + + '}'; } @Override public boolean equals(Object o) { - return o instanceof PlayerJob && ((PlayerJob)o).getPlayer().getUniqueId().equals(player.getUniqueId()); + return o instanceof PlayerJob && ((PlayerJob) o).getPlayer().getUniqueId().equals(player.getUniqueId()); } @Override diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/multiverseinventories/MultiverseInventoriesAdaptor.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/multiverseinventories/MultiverseInventoriesAdaptor.java deleted file mode 100644 index 34bd8e3ed..000000000 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/multiverseinventories/MultiverseInventoriesAdaptor.java +++ /dev/null @@ -1,30 +0,0 @@ -package us.talabrek.ultimateskyblock.handler.multiverseinventories; - -import com.onarandombox.multiverseinventories.MultiverseInventories; -import com.onarandombox.multiverseinventories.WorldGroup; -import com.onarandombox.multiverseinventories.profile.WorldGroupManager; -import com.onarandombox.multiverseinventories.share.Sharables; -import org.bukkit.Bukkit; -import org.bukkit.World; - -/** - * Adaptor, so we don't need to rely on MVInv on runtime. - */ -public class MultiverseInventoriesAdaptor { - public static void linkWorlds(World... worlds) { - WorldGroupManager groupManager = getMVInv().getGroupManager(); - WorldGroup worldGroup = groupManager.getGroup("skyblock"); - if (worldGroup == null) { - worldGroup = groupManager.newEmptyGroup("skyblock"); - worldGroup.getShares().addAll(Sharables.ALL_DEFAULT); - } - for (World world : worlds) { - worldGroup.addWorld(world); - } - groupManager.updateGroup(worldGroup); - } - - public static MultiverseInventories getMVInv() { - return (MultiverseInventories) Bukkit.getPluginManager().getPlugin("Multiverse-Inventories"); - } -} diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/placeholder/ChatPlaceholder.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/placeholder/ChatPlaceholder.java deleted file mode 100644 index d28b6ea58..000000000 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/placeholder/ChatPlaceholder.java +++ /dev/null @@ -1,26 +0,0 @@ -package us.talabrek.ultimateskyblock.handler.placeholder; - -import org.bukkit.Bukkit; -import org.bukkit.entity.Player; -import org.bukkit.event.EventHandler; -import org.bukkit.event.Listener; -import org.bukkit.event.player.AsyncPlayerChatEvent; -import us.talabrek.ultimateskyblock.uSkyBlock; - -/** - * Our native chat-placeholder handler. - */ -public class ChatPlaceholder extends TextPlaceholder implements Listener { - @Override - public boolean registerPlaceholder(uSkyBlock plugin, PlaceholderReplacer replacer) { - Bukkit.getPluginManager().registerEvents(this, plugin); - return super.registerPlaceholder(plugin, replacer); - } - - @EventHandler - public void onChatEvent(AsyncPlayerChatEvent e) { - Player player = e.getPlayer(); - e.setFormat(PlaceholderHandler.replacePlaceholders(player, e.getFormat())); - e.setMessage(PlaceholderHandler.replacePlaceholders(player, e.getMessage())); - } -} diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/placeholder/ChatReplaceListener.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/placeholder/ChatReplaceListener.java new file mode 100644 index 000000000..dae1b8b62 --- /dev/null +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/placeholder/ChatReplaceListener.java @@ -0,0 +1,27 @@ +package us.talabrek.ultimateskyblock.handler.placeholder; + +import com.google.inject.Inject; +import com.google.inject.Singleton; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.AsyncPlayerChatEvent; +import org.jetbrains.annotations.NotNull; + +@Singleton +public class ChatReplaceListener implements Listener { + + private final PlaceholderHandler placeholderHandler; + + @Inject + public ChatReplaceListener(@NotNull PlaceholderHandler placeholderHandler) { + this.placeholderHandler = placeholderHandler; + } + + @EventHandler + public void onChatEvent(AsyncPlayerChatEvent event) { + Player player = event.getPlayer(); + event.setFormat(placeholderHandler.replacePlaceholders(player, event.getFormat())); + event.setMessage(placeholderHandler.replacePlaceholders(player, event.getMessage())); + } +} diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/placeholder/DeluxeChatPlaceholderAPI.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/placeholder/DeluxeChatPlaceholderAPI.java deleted file mode 100644 index 5d77bb786..000000000 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/placeholder/DeluxeChatPlaceholderAPI.java +++ /dev/null @@ -1,50 +0,0 @@ -package us.talabrek.ultimateskyblock.handler.placeholder; - -import me.clip.deluxechat.placeholders.DeluxePlaceholderHook; -import me.clip.deluxechat.placeholders.PlaceholderHandler; -import org.bukkit.Bukkit; -import org.bukkit.entity.Player; -import us.talabrek.ultimateskyblock.uSkyBlock; - -/** - * DeluxeChatPlaceholder integration - */ -public class DeluxeChatPlaceholderAPI implements PlaceholderAPI { - private uSkyBlock plugin; - private PlaceholderReplacer replacer; - - @Override - public boolean registerPlaceholder(uSkyBlock plugin, PlaceholderReplacer replacer) { - this.plugin = plugin; - this.replacer = replacer; - if (Bukkit.getPluginManager().isPluginEnabled("DeluxeChat")) { - Hook hook = new Hook(); - me.clip.deluxechat.placeholders.PlaceholderHandler.registerPlaceholderHook(plugin, hook); - return true; - } - return false; - } - - @Override - public void unregisterPlaceholder(uSkyBlock plugin, PlaceholderReplacer placeholderReplacer) { - if (Bukkit.getPluginManager().isPluginEnabled("DeluxeChat")) { - PlaceholderHandler.unregisterPlaceholderHook(plugin); - } - } - - @Override - public String replacePlaceholders(Player player, String message) { - return PlaceholderHandler.setPlaceholders(player, message); - } - - private class Hook extends DeluxePlaceholderHook { - @Override - public String onPlaceholderRequest(Player player, String placeholder) { - String usbPlaceholder = "usb_" + placeholder; - if (replacer.getPlaceholders().contains(usbPlaceholder)) { - return replacer.replace(player, player, usbPlaceholder); - } - return null; - } - } -} diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/placeholder/MVdWPlaceholderAPI.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/placeholder/MVdWPlaceholderAPI.java index 45844e65a..0ec4a81ad 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/placeholder/MVdWPlaceholderAPI.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/placeholder/MVdWPlaceholderAPI.java @@ -1,45 +1,38 @@ package us.talabrek.ultimateskyblock.handler.placeholder; -import org.bukkit.Bukkit; +import com.google.inject.Inject; +import com.google.inject.Singleton; import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; import us.talabrek.ultimateskyblock.uSkyBlock; /** * MVdWPlaceholder proxy */ +@Singleton public class MVdWPlaceholderAPI implements PlaceholderAPI { - public boolean isAvailable() { - // Might not be enabled yet... - return Bukkit.getPluginManager().getPlugin("MVdWPlaceholderAPI") != null; + + private final PlaceholderReplacer replacer; + + @Inject + MVdWPlaceholderAPI(@NotNull PlaceholderReplacer replacer) { + this.replacer = replacer; } - @Override - public boolean registerPlaceholder(uSkyBlock plugin, final PlaceholderReplacer replacer) { - if (isAvailable()) { - be.maximvdw.placeholderapi.PlaceholderReplacer proxy = e -> { - if (replacer.getPlaceholders().contains(e.getPlaceholder())) { - return replacer.replace(e.getOfflinePlayer(), e.getPlayer(), e.getPlaceholder()); - } - return null; - }; - for (String placeholder : replacer.getPlaceholders()) { - be.maximvdw.placeholderapi.PlaceholderAPI.registerPlaceholder(plugin, placeholder, proxy); + public void setup(uSkyBlock plugin) { + be.maximvdw.placeholderapi.PlaceholderReplacer proxy = e -> { + if (replacer.getPlaceholders().contains(e.getPlaceholder())) { + return replacer.replace(e.getOfflinePlayer(), e.getPlayer(), e.getPlaceholder()); } - return true; + return null; + }; + for (String placeholder : replacer.getPlaceholders()) { + be.maximvdw.placeholderapi.PlaceholderAPI.registerPlaceholder(plugin, placeholder, proxy); } - return false; - } - - @Override - public void unregisterPlaceholder(uSkyBlock plugin, PlaceholderReplacer placeholderReplacer) { - // Not implemented. } @Override public String replacePlaceholders(Player player, String message) { - if (isAvailable()) { - return be.maximvdw.placeholderapi.PlaceholderAPI.replacePlaceholders(player, message); - } - return message; + return be.maximvdw.placeholderapi.PlaceholderAPI.replacePlaceholders(player, message); } } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/placeholder/MvdwPlacehoderProvider.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/placeholder/MvdwPlacehoderProvider.java new file mode 100644 index 000000000..16764ec02 --- /dev/null +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/placeholder/MvdwPlacehoderProvider.java @@ -0,0 +1,19 @@ +package us.talabrek.ultimateskyblock.handler.placeholder; + +import com.google.inject.Inject; +import com.google.inject.Provider; + +public class MvdwPlacehoderProvider implements Provider { + + private final PlaceholderAPI.PlaceholderReplacer replacer; + + @Inject + public MvdwPlacehoderProvider(PlaceholderAPI.PlaceholderReplacer replacer) { + this.replacer = replacer; + } + + @Override + public MVdWPlaceholderAPI get() { + return new MVdWPlaceholderAPI(replacer); + } +} diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/placeholder/PlaceholderAPI.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/placeholder/PlaceholderAPI.java index 8796d74c2..8dab1cf70 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/placeholder/PlaceholderAPI.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/placeholder/PlaceholderAPI.java @@ -2,18 +2,19 @@ import org.bukkit.OfflinePlayer; import org.bukkit.entity.Player; -import us.talabrek.ultimateskyblock.uSkyBlock; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; -import java.util.Set; +import java.util.Collection; -/** - */ public interface PlaceholderAPI { + interface PlaceholderReplacer { - Set getPlaceholders(); - String replace(OfflinePlayer offlinePlayer, Player player, String placeholder); + + @NotNull Collection getPlaceholders(); + + @Nullable String replace(@Nullable OfflinePlayer offlinePlayer, @Nullable Player player, @Nullable String placeholder); } - String replacePlaceholders(Player player, String message); - boolean registerPlaceholder(uSkyBlock plugin, PlaceholderReplacer replacer); - void unregisterPlaceholder(uSkyBlock plugin, PlaceholderReplacer placeholderReplacer); + + @Nullable String replacePlaceholders(@Nullable Player player, @Nullable String message); } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/placeholder/PlaceholderHandler.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/placeholder/PlaceholderHandler.java index e9204b8a8..43fc765e6 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/placeholder/PlaceholderHandler.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/placeholder/PlaceholderHandler.java @@ -1,73 +1,31 @@ package us.talabrek.ultimateskyblock.handler.placeholder; -import dk.lockfuglsang.minecraft.file.FileUtil; +import com.google.inject.Singleton; import org.bukkit.entity.Player; -import us.talabrek.ultimateskyblock.uSkyBlock; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.Nullable; import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; +import java.util.Collection; +@Singleton public class PlaceholderHandler { - private static final String[] ADAPTORS = { - ChatPlaceholder.class.getName(), - ServerCommandPlaceholder.class.getName(), - "us.talabrek.ultimateskyblock.handler.placeholder.MVdWPlaceholderAPI", - "us.talabrek.ultimateskyblock.handler.placeholder.DeluxeChatPlaceholderAPI" - }; - private static PlaceholderAPI.PlaceholderReplacer replacer; - private static final List apis = new ArrayList<>(); + private final Collection apis = new ArrayList<>(); - public static void register(uSkyBlock plugin) { - PlaceholderAPI.PlaceholderReplacer placeholderReplacer = getReplacer(plugin); - for (String className : ADAPTORS) { - String baseName = FileUtil.getExtension(className); - if (plugin.getConfig().getBoolean("placeholder." + baseName.toLowerCase(), false)) { - try { - Class aClass = Class.forName(className); - Object o = aClass.newInstance(); - if (o instanceof PlaceholderAPI) { - PlaceholderAPI api = (PlaceholderAPI) o; - if (api.registerPlaceholder(plugin, placeholderReplacer)) { - plugin.getLogger().info("uSkyBlock hooked into " + baseName); - apis.add(api); - } else { - plugin.getLogger().info("uSkyBlock failed to hook into " + baseName); - } - } - } catch (Throwable e) { - // Ignore - plugin.getLogger().info("uSkyBlock failed to hook into " + baseName); - } - } - } - } - - public static void unregister(uSkyBlock plugin) { - PlaceholderAPI.PlaceholderReplacer placeholderReplacer = getReplacer(plugin); - for (Iterator it = apis.iterator(); it.hasNext();) { - it.next().unregisterPlaceholder(plugin, placeholderReplacer); - it.remove(); - } - replacer = null; + public void registerPlaceholders(PlaceholderAPI api) { + apis.add(api); } - public static String replacePlaceholders(Player player, String message) { + @Contract("_, null -> null") + public String replacePlaceholders(Player player, @Nullable String message) { if (message == null) { return null; } - String msg = message; + String replacedMessage = message; for (PlaceholderAPI api : apis) { - msg = api.replacePlaceholders(player, msg); - } - return msg; - } - - private static PlaceholderAPI.PlaceholderReplacer getReplacer(uSkyBlock plugin) { - if (replacer == null) { - replacer = new PlaceholderReplacerImpl(plugin); + replacedMessage = api.replacePlaceholders(player, replacedMessage); } - return replacer; + return replacedMessage; } } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/placeholder/PlaceholderModule.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/placeholder/PlaceholderModule.java new file mode 100644 index 000000000..44121866b --- /dev/null +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/placeholder/PlaceholderModule.java @@ -0,0 +1,52 @@ +package us.talabrek.ultimateskyblock.handler.placeholder; + +import com.google.inject.Inject; +import com.google.inject.Provider; +import org.bukkit.Bukkit; +import org.jetbrains.annotations.NotNull; +import us.talabrek.ultimateskyblock.PluginConfig; +import us.talabrek.ultimateskyblock.uSkyBlock; + +public class PlaceholderModule { + + private final PlaceholderHandler placeholderHandler; + private final PluginConfig config; + private final ChatReplaceListener chatReplaceListener; + private final ServerCommandReplaceListener serverCommandListener; + private final TextPlaceholder textPlaceholder; + private final Provider mvdwPlaceholderProvider; + + @Inject + public PlaceholderModule( + @NotNull PlaceholderHandler placeholderHandler, + @NotNull PluginConfig config, + @NotNull ChatReplaceListener chatReplaceListener, + @NotNull ServerCommandReplaceListener serverCommandListener, + @NotNull TextPlaceholder textPlaceholder, + @NotNull Provider mvdwPlaceholderProvider + ) { + this.placeholderHandler = placeholderHandler; + this.config = config; + this.chatReplaceListener = chatReplaceListener; + this.serverCommandListener = serverCommandListener; + this.textPlaceholder = textPlaceholder; + this.mvdwPlaceholderProvider = mvdwPlaceholderProvider; + } + + public void startup(uSkyBlock plugin) { + if (config.getYamlConfig().getBoolean("placeholder.chatplaceholder", false)) { + plugin.getServer().getPluginManager().registerEvents(chatReplaceListener, plugin); + } + if (config.getYamlConfig().getBoolean("placeholder.servercommandplaceholder", false)) { + plugin.getServer().getPluginManager().registerEvents(serverCommandListener, plugin); + } + + if (config.getYamlConfig().getBoolean("placeholder.mvdwplaceholderapi", false) + && Bukkit.getPluginManager().getPlugin("MVdWPlaceholderAPI") != null) { + MVdWPlaceholderAPI mvdwPlaceholder = mvdwPlaceholderProvider.get(); + mvdwPlaceholder.setup(plugin); + placeholderHandler.registerPlaceholders(mvdwPlaceholder); + } + placeholderHandler.registerPlaceholders(textPlaceholder); + } +} diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/placeholder/PlaceholderReplacerImpl.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/placeholder/PlaceholderReplacerImpl.java index 4670cf514..1a8e1993c 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/placeholder/PlaceholderReplacerImpl.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/placeholder/PlaceholderReplacerImpl.java @@ -3,17 +3,22 @@ import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; +import com.google.inject.Inject; +import com.google.inject.Singleton; import org.bukkit.OfflinePlayer; import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import us.talabrek.ultimateskyblock.api.IslandRank; import us.talabrek.ultimateskyblock.island.IslandInfo; +import us.talabrek.ultimateskyblock.island.IslandLogic; import us.talabrek.ultimateskyblock.island.LimitLogic; import us.talabrek.ultimateskyblock.player.PlayerInfo; +import us.talabrek.ultimateskyblock.player.PlayerLogic; import us.talabrek.ultimateskyblock.uSkyBlock; import us.talabrek.ultimateskyblock.util.LocationUtil; -import java.util.Arrays; -import java.util.HashSet; +import java.util.Collection; import java.util.Set; import java.util.UUID; import java.util.concurrent.ExecutionException; @@ -24,58 +29,72 @@ /** * The actual replacer for placeholders */ +@Singleton public class PlaceholderReplacerImpl implements PlaceholderAPI.PlaceholderReplacer { - private static final Set PLACEHOLDERS = new HashSet<>(Arrays.asList( - "usb_version", - "usb_island_level", - "usb_island_level_int", - "usb_island_rank", - "usb_island_leader", - "usb_island_golems_max", - "usb_island_monsters_max", - "usb_island_animals_max", - "usb_island_villagers_max", - "usb_island_partysize_max", - "usb_island_golems", - "usb_island_monsters", - "usb_island_animals", - "usb_island_villagers", - "usb_island_partysize", - "usb_island_biome", - "usb_island_bans", - "usb_island_members", - "usb_island_trustees", - "usb_island_location", - "usb_island_location_x", - "usb_island_location_y", - "usb_island_location_z", - "usb_island_schematic" - )); + private static final Collection PLACEHOLDERS = Set.of( + "usb_version", + "usb_island_level", + "usb_island_level_int", + "usb_island_rank", + "usb_island_leader", + "usb_island_golems_max", + "usb_island_monsters_max", + "usb_island_animals_max", + "usb_island_villagers_max", + "usb_island_partysize_max", + "usb_island_golems", + "usb_island_monsters", + "usb_island_animals", + "usb_island_villagers", + "usb_island_partysize", + "usb_island_biome", + "usb_island_bans", + "usb_island_members", + "usb_island_trustees", + "usb_island_location", + "usb_island_location_x", + "usb_island_location_y", + "usb_island_location_z", + "usb_island_schematic" + ); private final uSkyBlock plugin; + private final PlayerLogic playerLogic; + private final IslandLogic islandLogic; + private final LimitLogic limitLogic; private final LoadingCache cache; - public PlaceholderReplacerImpl(uSkyBlock plugin) { + @Inject + public PlaceholderReplacerImpl( + @NotNull uSkyBlock plugin, + @NotNull PlayerLogic playerLogic, + @NotNull IslandLogic islandLogic, + @NotNull LimitLogic limitLogic + ) { this.plugin = plugin; - cache = CacheBuilder - .from(plugin.getConfig().getString("options.advanced.placeholderCache", - "maximumSize=200,expireAfterWrite=20s")) - .build(new CacheLoader() { - @Override - public String load(CacheEntry cacheEntry) throws Exception { - try { - return lookup(cacheEntry); - } catch (RuntimeException e) { - throw new ExecutionException(e.getMessage(), e); - } + this.playerLogic = playerLogic; + this.islandLogic = islandLogic; + this.limitLogic = limitLogic; + + this.cache = CacheBuilder + .from(plugin.getConfig().getString("options.advanced.placeholderCache", + "maximumSize=200,expireAfterWrite=20s")) + .build(new CacheLoader<>() { + @Override + public @NotNull String load(@NotNull CacheEntry cacheEntry) throws Exception { + try { + return lookup(cacheEntry); + } catch (RuntimeException e) { + throw new ExecutionException(e.getMessage(), e); } - }); + } + }); } private String lookup(CacheEntry entry) { - String placeholder = entry.getPlaceholder(); + String placeholder = entry.placeholder(); if (placeholder.startsWith("usb_island")) { - PlayerInfo playerInfo = plugin.getPlayerLogic().getPlayerInfo(entry.getUuid()); - IslandInfo islandInfo = plugin.getIslandLogic().getIslandInfo(playerInfo); + PlayerInfo playerInfo = playerLogic.getPlayerInfo(entry.uuid()); + IslandInfo islandInfo = islandLogic.getIslandInfo(playerInfo); if (playerInfo == null || islandInfo == null) { return tr("N/A"); } @@ -87,43 +106,46 @@ private String lookup(CacheEntry entry) { } private String lookup(String placeholder) { - switch (placeholder) { - case "usb_version": return plugin.getDescription().getVersion(); + if (placeholder.equals("usb_version")) { + return plugin.getDescription().getVersion(); } throw new IllegalArgumentException("Unsupported placeholder " + placeholder); } private String lookup(IslandInfo islandInfo, String placeholder) { - switch (placeholder) { - case "usb_island_level": return pre("{0,number,##.#}", islandInfo.getLevel()); - case "usb_island_level_int": return pre("{0,number,#}", islandInfo.getLevel()); - case "usb_island_rank": return getRank(islandInfo); - case "usb_island_leader": return islandInfo.getLeader(); - case "usb_island_golems_max": return "" + islandInfo.getMaxGolems(); - case "usb_island_monsters_max": return "" + islandInfo.getMaxMonsters(); - case "usb_island_animals_max": return "" + islandInfo.getMaxAnimals(); - case "usb_island_villagers_max": return "" + islandInfo.getMaxVillagers(); - case "usb_island_partysize_max": return "" + islandInfo.getMaxPartySize(); - case "usb_island_golems": return "" + plugin.getLimitLogic().getCreatureCount(islandInfo).get(LimitLogic.CreatureType.GOLEM); - case "usb_island_monsters": return "" + plugin.getLimitLogic().getCreatureCount(islandInfo).get(LimitLogic.CreatureType.MONSTER); - case "usb_island_animals": return "" + plugin.getLimitLogic().getCreatureCount(islandInfo).get(LimitLogic.CreatureType.ANIMAL); - case "usb_island_villagers": return "" + plugin.getLimitLogic().getCreatureCount(islandInfo).get(LimitLogic.CreatureType.VILLAGER); - case "usb_island_partysize": return "" + islandInfo.getPartySize(); - case "usb_island_biome": return islandInfo.getBiome(); - case "usb_island_bans": return ""+islandInfo.getBans(); - case "usb_island_members": return ""+islandInfo.getMembers(); - case "usb_island_trustees": return ""+islandInfo.getTrustees(); - case "usb_island_location": return LocationUtil.asString(islandInfo.getIslandLocation()); - case "usb_island_location_x": return pre("{0,number,#}", islandInfo.getIslandLocation().getBlockX()); - case "usb_island_location_y": return pre("{0,number,#}", islandInfo.getIslandLocation().getBlockY()); - case "usb_island_location_z": return pre("{0,number,#}", islandInfo.getIslandLocation().getBlockZ()); - case "usb_island_schematic": return islandInfo.getSchematicName(); - } - throw new IllegalArgumentException("Unsupported placeholder " + placeholder); + return switch (placeholder) { + case "usb_island_level" -> pre("{0,number,##.#}", islandInfo.getLevel()); + case "usb_island_level_int" -> pre("{0,number,#}", islandInfo.getLevel()); + case "usb_island_rank" -> getRank(islandInfo); + case "usb_island_leader" -> islandInfo.getLeader(); + case "usb_island_golems_max" -> "" + islandInfo.getMaxGolems(); + case "usb_island_monsters_max" -> "" + islandInfo.getMaxMonsters(); + case "usb_island_animals_max" -> "" + islandInfo.getMaxAnimals(); + case "usb_island_villagers_max" -> "" + islandInfo.getMaxVillagers(); + case "usb_island_partysize_max" -> "" + islandInfo.getMaxPartySize(); + case "usb_island_golems" -> "" + limitLogic.getCreatureCount(islandInfo).get(LimitLogic.CreatureType.GOLEM); + case "usb_island_monsters" -> + "" + limitLogic.getCreatureCount(islandInfo).get(LimitLogic.CreatureType.MONSTER); + case "usb_island_animals" -> + "" + limitLogic.getCreatureCount(islandInfo).get(LimitLogic.CreatureType.ANIMAL); + case "usb_island_villagers" -> + "" + limitLogic.getCreatureCount(islandInfo).get(LimitLogic.CreatureType.VILLAGER); + case "usb_island_partysize" -> "" + islandInfo.getPartySize(); + case "usb_island_biome" -> islandInfo.getBiomeName(); + case "usb_island_bans" -> "" + islandInfo.getBans(); + case "usb_island_members" -> "" + islandInfo.getMembers(); + case "usb_island_trustees" -> "" + islandInfo.getTrustees(); + case "usb_island_location" -> LocationUtil.asString(islandInfo.getIslandLocation()); + case "usb_island_location_x" -> pre("{0,number,#}", islandInfo.getIslandLocation().getBlockX()); + case "usb_island_location_y" -> pre("{0,number,#}", islandInfo.getIslandLocation().getBlockY()); + case "usb_island_location_z" -> pre("{0,number,#}", islandInfo.getIslandLocation().getBlockZ()); + case "usb_island_schematic" -> islandInfo.getSchematicName(); + default -> throw new IllegalArgumentException("Unsupported placeholder " + placeholder); + }; } private String getRank(IslandInfo islandInfo) { - IslandRank rank = plugin.getIslandLogic().getRank(islandInfo.getName()); + IslandRank rank = islandLogic.getRank(islandInfo.getName()); if (rank != null) { return pre("{0,number,#}", rank.getRank()); } else { @@ -132,12 +154,12 @@ private String getRank(IslandInfo islandInfo) { } @Override - public Set getPlaceholders() { + public @NotNull Collection getPlaceholders() { return PLACEHOLDERS; } @Override - public String replace(OfflinePlayer offlinePlayer, Player player, String placeholder) { + public @Nullable String replace(@Nullable OfflinePlayer offlinePlayer, @Nullable Player player, @Nullable String placeholder) { if (placeholder == null || !placeholder.startsWith("usb_")) { return null; } @@ -156,41 +178,6 @@ public String replace(OfflinePlayer offlinePlayer, Player player, String placeho } } - private static class CacheEntry { - private final UUID uuid; - private final String placeholder; - - private CacheEntry(UUID uuid, String placeholder) { - this.uuid = uuid; - this.placeholder = placeholder; - } - - public UUID getUuid() { - return uuid; - } - - public String getPlaceholder() { - return placeholder; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - CacheEntry that = (CacheEntry) o; - - if (!placeholder.equals(that.placeholder)) return false; - if (!uuid.equals(that.uuid)) return false; - - return true; - } - - @Override - public int hashCode() { - int result = uuid.hashCode(); - result = 31 * result + placeholder.hashCode(); - return result; - } + private record CacheEntry(UUID uuid, String placeholder) { } } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/placeholder/ServerCommandPlaceholder.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/placeholder/ServerCommandPlaceholder.java deleted file mode 100644 index 5bd1a34d2..000000000 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/placeholder/ServerCommandPlaceholder.java +++ /dev/null @@ -1,28 +0,0 @@ -package us.talabrek.ultimateskyblock.handler.placeholder; - -import org.bukkit.Bukkit; -import org.bukkit.entity.Player; -import org.bukkit.event.EventHandler; -import org.bukkit.event.Listener; -import org.bukkit.event.server.ServerCommandEvent; -import us.talabrek.ultimateskyblock.uSkyBlock; - -/** - * Replaces placeholders in server-commands - */ -public class ServerCommandPlaceholder extends TextPlaceholder implements Listener { - @Override - public boolean registerPlaceholder(uSkyBlock plugin, PlaceholderReplacer replacer) { - Bukkit.getPluginManager().registerEvents(this, plugin); - return super.registerPlaceholder(plugin, replacer); - } - - @EventHandler - public void onCmd(ServerCommandEvent e) { - String cmd = e.getCommand(); - String replacement = PlaceholderHandler.replacePlaceholders(e.getSender() instanceof Player ? (Player) e.getSender() : null, cmd); - if (replacement != null && !cmd.equals(replacement)) { - e.setCommand(cmd); - } - } -} diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/placeholder/ServerCommandReplaceListener.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/placeholder/ServerCommandReplaceListener.java new file mode 100644 index 000000000..1e0efae02 --- /dev/null +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/placeholder/ServerCommandReplaceListener.java @@ -0,0 +1,29 @@ +package us.talabrek.ultimateskyblock.handler.placeholder; + +import com.google.inject.Inject; +import com.google.inject.Singleton; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.server.ServerCommandEvent; +import org.jetbrains.annotations.NotNull; + +@Singleton +public class ServerCommandReplaceListener implements Listener { + + private final PlaceholderHandler placeholderHandler; + + @Inject + public ServerCommandReplaceListener(@NotNull PlaceholderHandler placeholderHandler) { + this.placeholderHandler = placeholderHandler; + } + + @EventHandler + public void onCommand(ServerCommandEvent event) { + String command = event.getCommand(); + String replacement = placeholderHandler.replacePlaceholders(event.getSender() instanceof Player ? (Player) event.getSender() : null, command); + if (replacement != null && !command.equals(replacement)) { + event.setCommand(command); + } + } +} diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/placeholder/TextPlaceholder.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/placeholder/TextPlaceholder.java index ce41921de..d85554915 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/placeholder/TextPlaceholder.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/placeholder/TextPlaceholder.java @@ -1,7 +1,10 @@ package us.talabrek.ultimateskyblock.handler.placeholder; +import com.google.inject.Inject; +import com.google.inject.Singleton; import org.bukkit.entity.Player; -import us.talabrek.ultimateskyblock.uSkyBlock; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -9,23 +12,23 @@ /** * Common PlaceholderAPI for internal placeholders. */ +@Singleton public class TextPlaceholder implements PlaceholderAPI { + private static final Pattern PLACEHOLDER_PATTERN = Pattern.compile("\\{(?usb_[^}]*)\\}"); - private uSkyBlock plugin; - private PlaceholderReplacer replacer; + private final PlaceholderReplacer replacer; - @Override - public boolean registerPlaceholder(uSkyBlock plugin, PlaceholderReplacer replacer) { - this.plugin = plugin; + @Inject + public TextPlaceholder(@NotNull PlaceholderReplacer replacer) { this.replacer = replacer; - return true; } - public String replacePlaceholders(Player player, String message) { + @Override + public @Nullable String replacePlaceholders(@Nullable Player player, @Nullable String message) { return replacePlaceholdersInternal(player, message); } - private String replacePlaceholdersInternal(Player player, String message) { + private @Nullable String replacePlaceholdersInternal(@Nullable Player player, @Nullable String message) { if (message == null) { return null; } @@ -35,17 +38,17 @@ private String replacePlaceholdersInternal(Player player, String message) { int ix = 0; StringBuilder sb = new StringBuilder(); do { - sb.append(message.substring(ix, matcher.start())); + sb.append(message, ix, matcher.start()); String placeholderString = matcher.group("placeholder"); if (placeholderString != null && replacer.getPlaceholders().contains(placeholderString)) { String replacement = replacer.replace(null, player, placeholderString); if (replacement != null) { sb.append(replacement); } else { - sb.append(message.substring(matcher.start(), matcher.end())); + sb.append(message, matcher.start(), matcher.end()); } } else { - sb.append("{" + placeholderString + "}"); + sb.append("{").append(placeholderString).append("}"); } ix = matcher.end(); } while (matcher.find()); @@ -56,9 +59,4 @@ private String replacePlaceholdersInternal(Player player, String message) { } return result; } - - @Override - public void unregisterPlaceholder(uSkyBlock plugin, PlaceholderReplacer placeholderReplacer) { - // Not needed, since the plugin will unregister all handlers - } } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/task/WorldEditClear.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/task/WorldEditClear.java index fa5388acc..2fe1aa63d 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/task/WorldEditClear.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/task/WorldEditClear.java @@ -6,6 +6,7 @@ import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.CuboidRegion; import com.sk89q.worldedit.regions.Region; +import com.sk89q.worldedit.util.SideEffectSet; import com.sk89q.worldedit.world.block.BlockTypes; import org.bukkit.World; import us.talabrek.ultimateskyblock.async.IncrementalRunnable; @@ -29,7 +30,7 @@ public class WorldEditClear extends IncrementalRunnable { private final List regions; public WorldEditClear(uSkyBlock plugin, World world, Set borderRegions, Runnable onCompletion) { - super(plugin, onCompletion); + super(plugin.getScheduler(), plugin.getPluginConfig(), onCompletion); this.world = world; log.log(Level.FINE, "Planning regen of borders: " + borderRegions); regions = createRegions(borderRegions); @@ -39,14 +40,14 @@ public WorldEditClear(uSkyBlock plugin, World world, Set borderRegions, private List createRegions(Set borderRegions) { List list = new ArrayList<>(); for (Region region : borderRegions) { - if (region.getLength() > region.getWidth()) { + if (region.getLength() > region.getWidth()) { // Z-axis BlockVector3 min = region.getMinimumPoint(); BlockVector3 max = region.getMaximumPoint(); BlockVector3 pt = BlockVector3.at(max.getX(), max.getY(), max.getZ()); pt = pt.withZ(min.getBlockZ()); while (pt.getBlockZ() < max.getBlockZ()) { - int dz = Math.min(INCREMENT, Math.abs(max.getBlockZ()-pt.getBlockZ())); + int dz = Math.min(INCREMENT, Math.abs(max.getBlockZ() - pt.getBlockZ())); pt = pt.add(0, 0, dz); list.add(new CuboidRegion(min, pt)); min = min.withZ(pt.getZ()); @@ -58,7 +59,7 @@ private List createRegions(Set borderRegions) { BlockVector3 pt = BlockVector3.at(max.getX(), max.getY(), max.getZ()); pt = pt.withX(min.getBlockX()); while (pt.getBlockX() < max.getBlockX()) { - int dx = Math.min(INCREMENT, Math.abs(max.getBlockX()-pt.getBlockX())); + int dx = Math.min(INCREMENT, Math.abs(max.getBlockX() - pt.getBlockX())); pt = pt.add(dx, 0, 0); list.add(new CuboidRegion(min, pt)); min = min.withX(pt.getX()); @@ -72,16 +73,14 @@ private List createRegions(Set borderRegions) { protected boolean execute() { while (!regions.isEmpty()) { final Region region = regions.remove(0); - final EditSession editSession = WorldEditHandler.createEditSession( - new BukkitWorld(world), region.getArea() * 255); - editSession.setReorderMode(EditSession.ReorderMode.MULTI_STAGE); - editSession.setFastMode(true); - try { + try (EditSession editSession = WorldEditHandler.createEditSession( + new BukkitWorld(world), (int) region.getVolume())) { + editSession.setReorderMode(EditSession.ReorderMode.MULTI_STAGE); + editSession.setSideEffectApplier(SideEffectSet.defaults()); editSession.setBlocks(region, BlockTypes.AIR.getDefaultState()); } catch (MaxChangedBlocksException e) { log.log(Level.INFO, "Warning: we got MaxChangedBlocks from WE, please increase it!"); } - editSession.flushSession(); if (!tick()) { break; } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/task/WorldEditClearFlatlandTask.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/task/WorldEditClearFlatlandTask.java index a2459617a..93519e253 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/task/WorldEditClearFlatlandTask.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/handler/task/WorldEditClearFlatlandTask.java @@ -7,19 +7,25 @@ import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.CuboidRegion; import com.sk89q.worldedit.regions.Region; +import com.sk89q.worldedit.util.SideEffectSet; import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockTypes; import dk.lockfuglsang.minecraft.util.TimeUtil; import org.bukkit.command.CommandSender; +import org.jetbrains.annotations.NotNull; +import us.talabrek.ultimateskyblock.PluginConfig; import us.talabrek.ultimateskyblock.async.IncrementalRunnable; import us.talabrek.ultimateskyblock.handler.AsyncWorldEditHandler; import us.talabrek.ultimateskyblock.handler.WorldEditHandler; -import us.talabrek.ultimateskyblock.uSkyBlock; +import us.talabrek.ultimateskyblock.util.Scheduler; +import us.talabrek.ultimateskyblock.world.WorldManager; import java.util.Iterator; import java.util.Set; import java.util.logging.Level; +import java.util.logging.Logger; +import static java.util.Objects.requireNonNull; import static us.talabrek.ultimateskyblock.util.LogUtil.log; /** @@ -27,30 +33,38 @@ * Not as fast as WorldEditRegenTask, but more versatile. */ public class WorldEditClearFlatlandTask extends IncrementalRunnable { - private static final BlockState AIR = BlockTypes.AIR.getDefaultState(); + private static final BlockState AIR = requireNonNull(BlockTypes.AIR).getDefaultState(); + private final Logger logger; private final Set borderRegions; private final Set innerChunks; - private final uSkyBlock plugin; private final BukkitWorld bukkitWorld; private final int minY; private final int maxY; private final int maxBlocks; - public WorldEditClearFlatlandTask(final uSkyBlock plugin, final CommandSender commandSender, final Region region, final String format) { - super(plugin); + public WorldEditClearFlatlandTask( + @NotNull Scheduler scheduler, + @NotNull PluginConfig config, + @NotNull WorldManager worldManager, + @NotNull Logger logger, + @NotNull CommandSender commandSender, + @NotNull Region region, + @NotNull String format + ) { + super(scheduler, config); + this.logger = logger; setOnCompletion(() -> { - String duration = TimeUtil.millisAsString(WorldEditClearFlatlandTask.this.getTimeElapsed()); - log(Level.INFO, String.format("Region %s was cleared in %s", region.toString(), duration)); + String duration = TimeUtil.durationAsShort(WorldEditClearFlatlandTask.this.getTimeElapsed()); + log(Level.INFO, String.format("Region %s was cleared in %s", region, duration)); commandSender.sendMessage(String.format(format, duration)); }); - this.plugin = plugin; innerChunks = WorldEditHandler.getInnerChunks(region); borderRegions = WorldEditHandler.getBorderRegions(region); - bukkitWorld = new BukkitWorld(plugin.getWorldManager().getWorld()); + bukkitWorld = new BukkitWorld(worldManager.getWorld()); minY = Math.min(region.getMinimumPoint().getBlockY(), region.getMaximumPoint().getBlockY()); maxY = Math.max(region.getMinimumPoint().getBlockY(), region.getMaximumPoint().getBlockY()); - maxBlocks = 2*Math.max(region.getLength(), region.getWidth())*16*(maxY-minY); + maxBlocks = 2 * Math.max(region.getLength(), region.getWidth()) * 16 * (maxY - minY); } @Override @@ -58,32 +72,32 @@ public boolean execute() { Iterator inner = innerChunks.iterator(); Iterator border = borderRegions.iterator(); while (!isComplete()) { - EditSession editSession = AsyncWorldEditHandler.createEditSession(bukkitWorld, maxBlocks); - editSession.setFastMode(true); - editSession.setReorderMode(EditSession.ReorderMode.MULTI_STAGE); - if (inner.hasNext()) { - BlockVector2 chunk = inner.next(); - inner.remove(); - try { - int x = chunk.getX() << 4; - int z = chunk.getZ() << 4; - editSession.setBlocks(new CuboidRegion(bukkitWorld, - BlockVector3.at(x, minY, z), - BlockVector3.at(x + 15, maxY, z + 15)), + try (EditSession editSession = AsyncWorldEditHandler.createEditSession(bukkitWorld, maxBlocks)) { + editSession.setSideEffectApplier(SideEffectSet.defaults()); + editSession.setReorderMode(EditSession.ReorderMode.MULTI_STAGE); + if (inner.hasNext()) { + BlockVector2 chunk = inner.next(); + inner.remove(); + try { + int x = chunk.getX() << 4; + int z = chunk.getZ() << 4; + editSession.setBlocks(new CuboidRegion(bukkitWorld, + BlockVector3.at(x, minY, z), + BlockVector3.at(x + 15, maxY, z + 15)), AIR); - } catch (MaxChangedBlocksException e) { - plugin.getLogger().log(Level.WARNING, "Unable to clear flat-land", e); - } - } else if (border.hasNext()) { - Region borderRegion = border.next(); - border.remove(); - try { - editSession.setBlocks(borderRegion, AIR); - } catch (MaxChangedBlocksException e) { - plugin.getLogger().log(Level.WARNING, "Unable to clear flat-land", e); + } catch (MaxChangedBlocksException e) { + logger.log(Level.WARNING, "Unable to clear flat-land", e); + } + } else if (border.hasNext()) { + Region borderRegion = border.next(); + border.remove(); + try { + editSession.setBlocks(borderRegion, AIR); + } catch (MaxChangedBlocksException e) { + logger.log(Level.WARNING, "Unable to clear flat-land", e); + } } } - editSession.flushSession(); if (!tick()) { break; } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/hook/HookFailedException.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/hook/HookFailedException.java new file mode 100644 index 000000000..e8c30d3a6 --- /dev/null +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/hook/HookFailedException.java @@ -0,0 +1,7 @@ +package us.talabrek.ultimateskyblock.hook; + +public class HookFailedException extends RuntimeException { + public HookFailedException(String message) { + super(message); + } +} diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/hook/HookManager.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/hook/HookManager.java new file mode 100644 index 000000000..7c5cbec2a --- /dev/null +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/hook/HookManager.java @@ -0,0 +1,149 @@ +package us.talabrek.ultimateskyblock.hook; + +import com.google.inject.Inject; +import com.google.inject.Singleton; +import org.bukkit.Bukkit; +import org.jetbrains.annotations.NotNull; +import us.talabrek.ultimateskyblock.hook.economy.EconomyHook; +import us.talabrek.ultimateskyblock.hook.economy.VaultEconomy; +import us.talabrek.ultimateskyblock.hook.permissions.PermissionsHook; +import us.talabrek.ultimateskyblock.hook.permissions.VaultPermissions; +import us.talabrek.ultimateskyblock.hook.world.MultiverseHook; +import us.talabrek.ultimateskyblock.uSkyBlock; + +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; +import java.util.logging.Level; +import java.util.logging.Logger; + +@Singleton +public class HookManager { + private final uSkyBlock plugin; + private final Logger logger; + private final Map hooks = new ConcurrentHashMap<>(); + + @Inject + public HookManager(@NotNull uSkyBlock plugin, @NotNull Logger logger) { + this.plugin = plugin; + this.logger = logger; + } + + /** + * Returns an {@link Optional} containing the requested {@link PluginHook}, or null if the hook is not available. + * + * @param hook Name of the requested hook. + * @return Optional containing the requested PluginHook, or null if unavailable. + */ + public Optional getHook(String hook) { + return Optional.ofNullable(hooks.get(hook)); + } + + /** + * Short method for {@link #getHook(String)} to get the optional {@link EconomyHook}. + * + * @return optional of EconomyHook. + */ + public Optional getEconomyHook() { + return Optional.ofNullable((EconomyHook) getHook("Economy").orElse(null)); + } + + /** + * Short method for {@link #getHook(String)} to get the optional {@link MultiverseHook}. + * + * @return optional of MultiverseHook. + */ + public Optional getMultiverse() { + return Optional.ofNullable((MultiverseHook) getHook("Multiverse").orElse(null)); + } + + /** + * Short method for {@link #getHook(String)} to get the optional {@link PermissionsHook}. + * + * @return optional of PermissionsHook. + */ + public Optional getPermissionsHook() { + return Optional.ofNullable((PermissionsHook) getHook("Permissions").orElse(null)); + } + + /** + * Tries to enable the hook in the given {@link PluginHook}. Adds the plugin hook to the list of enabled hooks + * if successfull. Throws a {@link HookFailedException} otherwise. + * + * @param hook Hook to enable and register. + * @throws HookFailedException if hooking into the plugin failes. + */ + public void registerHook(PluginHook hook) throws HookFailedException { + hook.onHook(); + hooks.put(hook.getHookName(), hook); + } + + public void setupHooks() { + setupMultiverse(); + setupEconomyHook(); + setupPermissionsHook(); + } + + /** + * Checks and hooks if there are compatible Economy plugins available. + * + * @return True if a compatible Economy plugin has been found and hooking succeeded, false otherwise. + */ + public boolean setupEconomyHook() { + try { + if (plugin.getServer().getPluginManager().isPluginEnabled("Vault")) { + VaultEconomy vault = new VaultEconomy(plugin); + registerHook(vault); + logger.info("Hooked into Vault economy"); + return true; + } + } catch (HookFailedException ex) { + logger.log(Level.SEVERE, "Failed to hook into Vault economy.", ex); + } + + logger.info("Failed to find a compatible economy system. Economy rewards will be disabled."); + return false; + } + + /** + * Checks and hooks into Multiverse-Core. + * + * @return True if hooking succeeded, false otherwise. + */ + public boolean setupMultiverse() { + try { + if (plugin.getServer().getPluginManager().isPluginEnabled("Multiverse-Core")) { + MultiverseHook mvHook = new MultiverseHook(plugin); + registerHook(mvHook); + logger.info("Hooked into Multiverse-Core"); + return true; + } + } catch (HookFailedException ex) { + logger.log(Level.SEVERE, "Failed to hook into Multiverse-Core", ex); + } + + logger.warning("Failed to find Multiverse-Core. Multiworld support will be limited."); + return false; + } + + /** + * Checks and hooks if there are compatible Permissions plugins available. + * + * @return True if a compatible Permissions plugin has geen found and hooking succeeded, false otherwise. + */ + public boolean setupPermissionsHook() { + try { + if (plugin.getServer().getPluginManager().isPluginEnabled("Vault")) { + VaultPermissions vault = new VaultPermissions(plugin); + registerHook(vault); + logger.info("Hooked into Vault permissions."); + return true; + } + } catch (HookFailedException ex) { + logger.log(Level.SEVERE, "Failed to hook into Vault permissions plugin.", ex); + } + + logger.warning("Failed to find a compatible permissions system. Permission rewards will be disabled."); + return false; + } +} diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/hook/PluginHook.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/hook/PluginHook.java new file mode 100644 index 000000000..ac1fe308e --- /dev/null +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/hook/PluginHook.java @@ -0,0 +1,54 @@ +package us.talabrek.ultimateskyblock.hook; + +import org.jetbrains.annotations.NotNull; +import us.talabrek.ultimateskyblock.uSkyBlock; + +public abstract class PluginHook { + protected final uSkyBlock plugin; + private final String hookName; + private final String implementing; + + /** + * PluginHook constructor. + * @param plugin uSkyBlock instance + * @param hookName The name of this plugin hook. + * @param implementing The name of the actual plugin that this hook will implement. + */ + public PluginHook(@NotNull uSkyBlock plugin, @NotNull String hookName, @NotNull String implementing) { + this.plugin = plugin; + this.hookName = hookName; + this.implementing = implementing; + } + + /** + * Called when the hook is enabled. Throws {@link HookFailedException} if the hooking fails. + * @throws HookFailedException if hooking fails. + */ + public void onHook() throws HookFailedException { + + } + + /** + * Called when the hook is disabled. Throws {@link HookFailedException} if the unhooking fails. + * @throws HookFailedException if unhooking fails. + */ + public void onUnhook() throws HookFailedException { + + } + + /** + * Gets the configured name of this hook. + * @return Hook name + */ + public @NotNull String getHookName() { + return hookName; + } + + /** + * Gets the name of the plugin that this hook implements. + * @return Name of the plugin that this hook implements. + */ + public @NotNull String getImplementing() { + return implementing; + } +} diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/hook/economy/EconomyHook.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/hook/economy/EconomyHook.java new file mode 100644 index 000000000..9436d6472 --- /dev/null +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/hook/economy/EconomyHook.java @@ -0,0 +1,46 @@ +package us.talabrek.ultimateskyblock.hook.economy; + +import org.bukkit.OfflinePlayer; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import us.talabrek.ultimateskyblock.hook.PluginHook; +import us.talabrek.ultimateskyblock.uSkyBlock; + +public abstract class EconomyHook extends PluginHook { + public EconomyHook(@NotNull uSkyBlock plugin, @NotNull String implementing) { + super(plugin, "Economy", implementing); + } + + /** + * Gets balance of an {@link OfflinePlayer} + * @param player of the player + * @return Amount currently held in players account + */ + public abstract double getBalance(@NotNull OfflinePlayer player); + + /** + * Deposit an amount to an {@link OfflinePlayer} - DO NOT USE NEGATIVE AMOUNTS + * + * @param player to deposit to + * @param amount Amount to deposit + * @return Detailed response of transaction + */ + public abstract @Nullable String depositPlayer(@NotNull OfflinePlayer player, double amount); + + /** + * Withdraw an amount from an {@link OfflinePlayer} - DO NOT USE NEGATIVE AMOUNTS + * + * @param player to withdraw from + * @param amount Amount to withdraw + * @return Detailed response of transaction + */ + public abstract @Nullable String withdrawPlayer(@NotNull OfflinePlayer player, double amount); + + /** + * Returns the icon or name of the currency in plural form. Defaults to $. + * @return icon or of the currency + */ + public @NotNull String getCurrenyName() { + return "$"; + } +} diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/hook/economy/VaultEconomy.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/hook/economy/VaultEconomy.java new file mode 100644 index 000000000..73e9ba883 --- /dev/null +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/hook/economy/VaultEconomy.java @@ -0,0 +1,87 @@ +package us.talabrek.ultimateskyblock.hook.economy; + +import net.milkbowl.vault.economy.Economy; +import net.milkbowl.vault.economy.EconomyResponse; +import org.bukkit.OfflinePlayer; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.server.ServiceRegisterEvent; +import org.bukkit.event.server.ServiceUnregisterEvent; +import org.bukkit.plugin.RegisteredServiceProvider; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import us.talabrek.ultimateskyblock.uSkyBlock; + +import java.util.Optional; + +public class VaultEconomy extends EconomyHook implements Listener { + private Economy economy; + + public VaultEconomy(@NotNull uSkyBlock plugin) { + super(plugin, "Vault"); + setupEconomy().ifPresent(vaultPlugin -> this.economy = vaultPlugin); + plugin.getServer().getPluginManager().registerEvents(this, plugin); + } + + private Optional setupEconomy() { + RegisteredServiceProvider rsp = + plugin.getServer().getServicesManager().getRegistration(Economy.class); + if (rsp != null) { + economy = rsp.getProvider(); + plugin.getLogger().info("Using " + economy.getName() + " as economy provider."); + return Optional.of(economy); + } + return Optional.empty(); + } + + @Override + public @NotNull String getCurrenyName() { + if (economy != null) { + return economy.currencyNamePlural(); + } + return super.getCurrenyName(); + } + + @Override + public double getBalance(@NotNull OfflinePlayer player) { + if (economy != null) { + return economy.getBalance(player); + } + return 0; + } + + @Override + public @Nullable String depositPlayer(@NotNull OfflinePlayer player, double amount) { + if (economy != null) { + EconomyResponse response = economy.depositPlayer(player, amount); + if (response.transactionSuccess()) return null; else return response.errorMessage; + } + return "Economy is null"; + } + + @Override + public @Nullable String withdrawPlayer(@NotNull OfflinePlayer player, double amount) { + if (economy != null) { + EconomyResponse response = economy.withdrawPlayer(player, amount); + if (response.transactionSuccess()) return null; else return response.errorMessage; + } + return "Economy is null"; + } + + @EventHandler + public void onEconomyRegister(ServiceRegisterEvent event) { + if (event.getProvider().getProvider() instanceof Economy) { + setupEconomy().ifPresent(vaultPlugin -> this.economy = vaultPlugin); + plugin.getLogger().info("Economy registered"); + } + } + + @EventHandler + public void onEconomyUnregister(ServiceUnregisterEvent event) { + if (event.getProvider().getProvider() instanceof Economy) { + this.economy = null; + setupEconomy().ifPresent(vaultPlugin -> this.economy = vaultPlugin); + plugin.getLogger().info("Economy unregistered"); + } + } +} diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/hook/permissions/PermissionsHook.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/hook/permissions/PermissionsHook.java new file mode 100644 index 000000000..552b08da2 --- /dev/null +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/hook/permissions/PermissionsHook.java @@ -0,0 +1,38 @@ +package us.talabrek.ultimateskyblock.hook.permissions; + +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import us.talabrek.ultimateskyblock.hook.PluginHook; +import us.talabrek.ultimateskyblock.uSkyBlock; + +public abstract class PermissionsHook extends PluginHook { + public PermissionsHook(@NotNull uSkyBlock plugin, @NotNull String implementing) { + super(plugin, "Permissions", implementing); + } + + /** + * Add permission to a player ONLY for the world the player is currently on. This is a world-specific operation. + * @param player Player Object + * @param perk Permission node + * @return Success or Failure + */ + public abstract boolean addPermission(@NotNull Player player, @NotNull String perk); + + /** + * Remove permission from a player. This is a world-specific operation. + * @param player Player Object + * @param perk Permission node + * @return Success or Failure + */ + public abstract boolean removePermission(@NotNull Player player, @NotNull String perk); + + /** + * Gets the value of the specified permission, if set. If a permission override is not set on this object, + * the default value of the permission will be returned. + * @param perk Name of the permission + * @return Value of the permission + */ + public boolean hasPermission(@NotNull Player player, @NotNull String perk) { + return player.hasPermission(perk); + } +} diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/hook/permissions/VaultPermissions.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/hook/permissions/VaultPermissions.java new file mode 100644 index 000000000..156fcf1eb --- /dev/null +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/hook/permissions/VaultPermissions.java @@ -0,0 +1,60 @@ +package us.talabrek.ultimateskyblock.hook.permissions; + +import net.milkbowl.vault2.permission.Permission; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.server.ServiceRegisterEvent; +import org.bukkit.event.server.ServiceUnregisterEvent; +import org.bukkit.plugin.RegisteredServiceProvider; +import org.jetbrains.annotations.NotNull; +import us.talabrek.ultimateskyblock.uSkyBlock; + +import java.util.Optional; + +public class VaultPermissions extends PermissionsHook implements Listener { + private Permission permission; + + public VaultPermissions(@NotNull uSkyBlock plugin) { + super(plugin, "Vault"); + setupPermission().ifPresent(vaultPlugin -> this.permission = vaultPlugin); + plugin.getServer().getPluginManager().registerEvents(this, plugin); + } + + private Optional setupPermission() { + RegisteredServiceProvider rsp = + plugin.getServer().getServicesManager().getRegistration(Permission.class); + if (rsp != null) { + permission = rsp.getProvider(); + plugin.getLogger().info("Using " + permission.getName() + " as permission provider."); + return Optional.of(permission); + } + + return Optional.empty(); + } + + @Override + public boolean addPermission(@NotNull Player player, @NotNull String perk) { + return permission.playerAdd(player, perk); + } + + @Override + public boolean removePermission(@NotNull Player player, @NotNull String perk) { + return permission.playerRemove(player, perk); + } + + @EventHandler + public void onPermissionRegister(ServiceRegisterEvent event) { + if (event.getProvider().getProvider() instanceof Permission) { + setupPermission().ifPresent(vaultPlugin -> this.permission = vaultPlugin); + } + } + + @EventHandler + public void onPermissionUnregister(ServiceUnregisterEvent event) { + if (event.getProvider().getProvider() instanceof Permission) { + this.permission = null; + setupPermission().ifPresent(vaultPlugin -> this.permission = vaultPlugin); + } + } +} diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/hook/world/MultiverseHook.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/hook/world/MultiverseHook.java new file mode 100644 index 000000000..9881894b9 --- /dev/null +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/hook/world/MultiverseHook.java @@ -0,0 +1,139 @@ +package us.talabrek.ultimateskyblock.hook.world; + +import com.onarandombox.MultiverseCore.MultiverseCore; +import com.onarandombox.MultiverseCore.api.MultiverseWorld; +import com.onarandombox.multiverseinventories.MultiverseInventories; +import com.onarandombox.multiverseinventories.WorldGroup; +import com.onarandombox.multiverseinventories.profile.WorldGroupManager; +import com.onarandombox.multiverseinventories.share.Sharables; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.WorldType; +import org.bukkit.plugin.Plugin; +import org.jetbrains.annotations.NotNull; +import us.talabrek.ultimateskyblock.Settings; +import us.talabrek.ultimateskyblock.hook.PluginHook; +import us.talabrek.ultimateskyblock.uSkyBlock; +import us.talabrek.ultimateskyblock.util.LocationUtil; + +import java.util.Optional; + +public class MultiverseHook extends PluginHook { + private MultiverseCore mvCore; + private MultiverseInventories mvInventories; + + private static final String GENERATOR_NAME = "uSkyBlock"; + + public MultiverseHook(@NotNull uSkyBlock plugin) { + super(plugin, "Multiverse", "Multiverse"); + + if (plugin.getServer().getPluginManager().isPluginEnabled("Multiverse-Core")) { + setupCore().ifPresent(mvPlugin -> this.mvCore = mvPlugin); + } + if (plugin.getServer().getPluginManager().isPluginEnabled("Multiverse-Inventories")) { + setupInventories().ifPresent(mvPlugin -> this.mvInventories = mvPlugin); + } + } + + private Optional setupCore() { + Plugin mvPlugin = plugin.getServer().getPluginManager().getPlugin("Multiverse-Core"); + if (mvPlugin instanceof MultiverseCore) { + plugin.getLogger().info("Found Multiverse-Core."); + return Optional.of((MultiverseCore) mvPlugin); + } + + return Optional.empty(); + } + + private Optional setupInventories() { + Plugin mvPlugin = plugin.getServer().getPluginManager().getPlugin("Multiverse-Inventories"); + if (mvPlugin instanceof MultiverseInventories) { + plugin.getLogger().info("Found Multiverse-Inventories."); + return Optional.of((MultiverseInventories) mvPlugin); + } + + return Optional.empty(); + } + + /** + * Registers the given {@link World} to {@link MultiverseCore} as the skyblock overworld (skyworld). + * @param world World to register. + */ + public void registerOverworld(@NotNull World world) { + if (mvCore == null) { + return; + } + + if (!mvCore.getMVWorldManager().isMVWorld(world)) { + mvCore.getMVWorldManager().addWorld(world.getName(), World.Environment.NORMAL, + "0", WorldType.NORMAL, false, GENERATOR_NAME, false); + } + + MultiverseWorld mvWorld = mvCore.getMVWorldManager().getMVWorld(world); + mvWorld.setEnvironment(World.Environment.NORMAL); + mvWorld.setScaling(1.0); + mvWorld.setGenerator(GENERATOR_NAME); + + if (Settings.general_spawnSize > 0 && LocationUtil.isEmptyLocation(mvWorld.getSpawnLocation())) { + Location spawn = LocationUtil.centerOnBlock( + new Location(world, 0.5, Settings.island_height + 0.1, 0.5)); + mvWorld.setAdjustSpawn(false); + mvWorld.setSpawnLocation(spawn); + world.setSpawnLocation(spawn); + } + + if (!Settings.extras_sendToSpawn) { + mvWorld.setRespawnToWorld(mvWorld.getName()); + } + } + + /** + * Registers the given {@link World} to {@link MultiverseCore} as the skyblock nether world (skyworld_nether). + * @param world World to register. + */ + public void registerNetherworld(@NotNull World world) { + if (mvCore == null) { + return; + } + + if (!mvCore.getMVWorldManager().isMVWorld(world)) { + mvCore.getMVWorldManager().addWorld(world.getName(), World.Environment.NETHER, + "0", WorldType.NORMAL, false, GENERATOR_NAME, false); + } + + MultiverseWorld mvWorld = mvCore.getMVWorldManager().getMVWorld(world); + mvWorld.setEnvironment(World.Environment.NETHER); + mvWorld.setScaling(1.0); + mvWorld.setGenerator(GENERATOR_NAME); + if (Settings.general_spawnSize > 0 && LocationUtil.isEmptyLocation(mvWorld.getSpawnLocation())) { + Location spawn = LocationUtil.centerOnBlock( + new Location(world, 0.5, Settings.island_height/2.0 + 0.1, 0.5)); + mvWorld.setAdjustSpawn(false); + mvWorld.setSpawnLocation(spawn); + world.setSpawnLocation(spawn); + } + + if (!Settings.extras_sendToSpawn) { + mvWorld.setRespawnToWorld(plugin.getWorldManager().getWorld().getName()); + } + + linkNetherInventory(plugin.getWorldManager().getWorld(), world); + } + + private void linkNetherInventory(@NotNull World... worlds) { + if (mvCore == null || mvInventories == null) { + return; + } + + WorldGroupManager groupManager = mvInventories.getGroupManager(); + WorldGroup worldGroup = groupManager.getGroup("skyblock"); + if (worldGroup == null) { + worldGroup = groupManager.newEmptyGroup("skyblock"); + worldGroup.getShares().addAll(Sharables.ALL_DEFAULT); + } + for (World world : worlds) { + worldGroup.addWorld(world); + } + groupManager.updateGroup(worldGroup); + } +} diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/imports/BlockRequirementConverter.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/imports/BlockRequirementConverter.java new file mode 100644 index 000000000..470788168 --- /dev/null +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/imports/BlockRequirementConverter.java @@ -0,0 +1,97 @@ +package us.talabrek.ultimateskyblock.imports; + +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; + +import java.io.BufferedReader; +import java.io.File; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Objects; +import java.util.logging.Logger; + +import static us.talabrek.ultimateskyblock.util.FileUtil.generateTimestamp; + +public class BlockRequirementConverter { + + private static final int NEW_CHALLENGES_VERSION = 108; + private final Logger logger; + + public BlockRequirementConverter(Logger logger) { + this.logger = logger; + } + + public void checkAndDoImport(File directory) { + var challengesFile = new File(directory, "challenges.yml"); + if (challengesFile.exists() && YamlConfiguration.loadConfiguration(challengesFile).getInt("version") < NEW_CHALLENGES_VERSION) { + importFile(challengesFile); + } + } + + public void importFile(File file) { + Path configFile = file.toPath(); + try { + Files.copy(configFile, configFile.getParent().resolve(configFile.getFileName() + "_" + generateTimestamp() + ".old")); + + FileConfiguration config = new YamlConfiguration(); + config.load(file); + + if (file.getName().equals("challenges.yml")) { + convertChallenges(config); + } + + config.save(file); + } catch (Exception e) { + throw new RuntimeException("An error occurred while attempting to convert file " + file, e); + } + } + + private void convertChallenges(FileConfiguration config) throws Exception { + var oldVersion = config.getInt("version"); + if (oldVersion >= NEW_CHALLENGES_VERSION) { + logger.warning("Expecting challanges.yml version " + (NEW_CHALLENGES_VERSION - 1) + ", but found " + oldVersion + " instead. Skipping conversion."); + return; + } + logger.info("Converting challenges.yml to new block requirement format."); + + convertBlockRequirements(config); + } + + private void convertBlockRequirements(FileConfiguration config) { + var ranks = config.getConfigurationSection("ranks"); + if (ranks != null) { + for (var rank : ranks.getKeys(false)) { + var challenges = ranks.getConfigurationSection(rank + ".challenges"); + if (challenges != null) { + for (var challenge : challenges.getKeys(false)) { + var challengeSection = challenges.getConfigurationSection(challenge); + if (challengeSection != null + && challengeSection.getString("type", "").equals("onIsland") + && challengeSection.isSet("requiredItems") + ) { + var requiredItems = challengeSection.getStringList("requiredItems"); + var requiredBlocks = new ArrayList<>(requiredItems); + challengeSection.set("requiredBlocks", requiredBlocks); + challengeSection.set("requiredItems", null); + } + } + } + } + } + + // Add the new header with explanations and usage instructions + var defaultConfig = new YamlConfiguration(); + try (var reader = new BufferedReader(new InputStreamReader(Objects.requireNonNull( + getClass().getClassLoader().getResourceAsStream("challenges.yml")), StandardCharsets.UTF_8))) { + defaultConfig.load(reader); + } catch (Exception e) { + logger.warning("Failed to load default challenges.yml file - unable to update the config header."); + } + config.options().setHeader(defaultConfig.options().getHeader()); + + config.set("version", NEW_CHALLENGES_VERSION); + } +} diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/imports/ItemComponentConverter.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/imports/ItemComponentConverter.java new file mode 100644 index 000000000..18789afa5 --- /dev/null +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/imports/ItemComponentConverter.java @@ -0,0 +1,341 @@ +package us.talabrek.ultimateskyblock.imports; + +import org.bukkit.Material; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.BufferedReader; +import java.io.File; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.*; +import java.util.logging.Logger; +import java.util.regex.Pattern; + +public class ItemComponentConverter { + + private final Logger logger; + + public ItemComponentConverter(Logger logger) { + this.logger = logger; + } + + public void checkAndDoImport(File directory) { + var configFile = new File(directory, "config.yml"); + if (configFile.exists() && YamlConfiguration.loadConfiguration(configFile).getInt("version") <= 108) { + importFile(configFile); + } + var challengesFile = new File(directory, "challenges.yml"); + if (challengesFile.exists() && YamlConfiguration.loadConfiguration(challengesFile).getInt("version") <= 106) { + importFile(challengesFile); + } + } + + public void importFile(File file) { + Path configFile = file.toPath(); + try { + Files.copy(configFile, configFile.getParent().resolve(configFile.getFileName() + ".old")); + + FileConfiguration config = new YamlConfiguration(); + config.load(file); + + if (file.getName().equals("challenges.yml")) { + convertChallenges(config); + } else if (file.getName().equals("config.yml")) { + convertConfig(config); + } + + config.save(file); + } catch (Exception e) { + throw new RuntimeException("An error occurred while attempting to convert file " + file, e); + } + } + + private void convertConfig(FileConfiguration config) { + var oldVersion = config.getInt("version"); + if (oldVersion > 108) { + logger.warning("Expecting config.yml version 108, but found " + oldVersion + " instead. Skipping conversion."); + return; + } + + logger.info("Converting config.yml to new item component format for Minecraft 1.20.5 and later."); + + for (var path : config.getKeys(true)) { + if (path.endsWith("chestItems") + || (path.contains(".extraPermissions.") && config.isList(path)) + || path.endsWith("extraItems") + ) { + var oldSpecifications = config.getStringList(path); + var results = oldSpecifications.stream().map(spec -> convertItemReward(spec, path)).toList(); + var newSpecifications = results.stream().map(pair -> pair.item).toList(); + var newComments = results.stream().map(pair -> pair.comment).filter(Objects::nonNull).toList(); + if (!newSpecifications.isEmpty()) { + config.set(path, newSpecifications); + } + if (!newComments.isEmpty()) { + List comments = new ArrayList<>(config.getComments(path)); + comments.addAll(newComments); + config.setComments(path, comments); + } + } else if (path.endsWith("displayItem") || path.endsWith("tool")) { + var oldSpecification = config.getString(path); + var pair = convertDisplayItem(oldSpecification, path); + config.set(path, pair.item); + if (pair.comment != null) { + List comments = new ArrayList<>(config.getComments(path)); + comments.add(pair.comment); + config.setComments(path, comments); + } + } + } + + // fix old grass block in default config. We don't fix all of them, as GRASS has referred to both grass_block + // and short_grass in the past, and we cannot determine what the user meant. They should fix it manually. + List giantBonus = config.getStringList("options.island.extraPermissions.giantbonus"); + if (giantBonus.contains("grass:1")) { + var index = giantBonus.indexOf("grass:1"); + giantBonus.set(index, "grass_block:1"); + config.set("options.island.extraPermissions.giantbonus", giantBonus); + } + + config.set("version", 109); + } + + private void convertChallenges(FileConfiguration config) throws Exception { + var oldVersion = config.getInt("version"); + if (oldVersion > 106) { + logger.warning("Expecting challanges.yml version 106, but found " + oldVersion + " instead. Skipping conversion."); + return; + } + logger.info("Converting challenges.yml to new item component format for Minecraft 1.20.5 and later."); + + convertChallengeItems(config); + updateHeaderAndVersion(config); + } + + + private void convertChallengeItems(FileConfiguration config) { + for (var path : config.getKeys(true)) { + if (path.endsWith("displayItem") || path.endsWith("lockedDisplayItem")) { + var oldSpecification = config.getString(path); + var pair = convertDisplayItem(oldSpecification, path); + config.set(path, pair.item); + if (pair.comment != null) { + List comments = new ArrayList<>(config.getComments(path)); + comments.add(pair.comment); + config.setComments(path, comments); + } + } else if (path.endsWith(".items")) { + var oldSpecifications = config.getStringList(path); + var results = oldSpecifications.stream().map(spec -> convertItemReward(spec, path)).toList(); + var newSpecifications = results.stream().map(pair -> pair.item).toList(); + var newComments = results.stream().map(pair -> pair.comment).filter(Objects::nonNull).toList(); + config.set(path, newSpecifications); + if (!newComments.isEmpty()) { + List comments = new ArrayList<>(config.getComments(path)); + comments.addAll(newComments); + config.setComments(path, comments); + } + } else if (path.endsWith("requiredItems")) { + var oldSpecifications = config.getStringList(path); + var results = oldSpecifications.stream().map(spec -> convertItemRequirement(spec, path)).toList(); + var newSpecifications = results.stream().map(pair -> pair.item).toList(); + var newComments = results.stream().map(pair -> pair.comment).filter(Objects::nonNull).toList(); + config.set(path, newSpecifications); + if (!newComments.isEmpty()) { + List comments = new ArrayList<>(config.getComments(path)); + comments.addAll(newComments); + config.setComments(path, comments); + } + } + } + } + + private void updateHeaderAndVersion(FileConfiguration config) throws Exception { + config.set("version", 107); + // Add a comment to the version number with the latest changes. This replaces the old explanation, + // which is moved to the header where it is more convenient and stable. + config.setComments("version", Arrays.asList( + null, + "This file has been updated to version 107. Please check the changes made in this version.", + "Changes in this version:", + " - Items are now specified in the new component format.", + " - Refer to the config header for the new format.", + " - NBT tags are not automatically converted. They have been moved to the comments, please check them manually.", + " You can use the following converter to convert the old item specifications to the new format:", + " https://docs.papermc.io/misc/tools/item-command-converter", + "DO NOT CHANGE THE VERSION! You will break the conversion and unexpected things will happen!" + )); + + // Add the new header with explanations and usage instructions + var defaultConfig = new YamlConfiguration(); + try (var reader = new BufferedReader(new InputStreamReader(Objects.requireNonNull( + getClass().getClassLoader().getResourceAsStream("challenges.yml")), StandardCharsets.UTF_8))) { + defaultConfig.load(reader); + } + config.options().setHeader(defaultConfig.options().getHeader()); + } + + private static final Pattern DISPLAY_PATTERN = Pattern.compile("(?[0-9A-Z_]+)(:(?[0-9]+))?\\s*(?\\{.*})?"); + private static final Pattern REWARD_PATTERN = Pattern.compile("(\\{p=(?0\\.[0-9]+)})?(?[0-9A-Z_]+)(:(?[0-9]+))?:(?[0-9]+)\\s*(?\\{.*})?"); + public static final Pattern REQUIREMENT_PATTERN = Pattern.compile("(?(?[0-9A-Z_]+)(:(?[0-9]+))?(?\\{.*})?):(?[0-9]+)(;(?[-+*^])(?[0-9]+))?"); + + private SpecificationCommentPair convertDisplayItem(String oldSpecification, String path) { + var matcher = DISPLAY_PATTERN.matcher(oldSpecification); + if (!matcher.matches()) { + throw new IllegalArgumentException("Invalid display item specification at path " + path); + } + var itemType = fixMaterial(matcher.group("id")); + var type = Material.matchMaterial(itemType); + if (type != null) { + itemType = type.getKey().getKey(); + } else { + itemType = itemType.toLowerCase(Locale.ROOT); + logger.warning("Unknown material " + itemType + " in display item specification at " + path); + } + var sub = matcher.group("sub"); + if (sub != null) { + logger.warning("Old pre-1.13 item specification at " + path + " uses subtypes, which are no longer supported."); + } + var meta = matcher.group("meta"); + if (meta != null && meta.trim().isEmpty()) { + meta = null; + } + String comment = null; + if (meta != null) { + logger.warning("Some items contain NBT tags, which are not automatically converted. Please check the entry at " + path + " manually."); + comment = itemType + meta; + } + + var newSpecification = new StringBuilder(); + newSpecification.append(itemType); + if (meta != null) { + newSpecification.append("[]"); + } + + return new SpecificationCommentPair(newSpecification.toString(), comment); + } + + private SpecificationCommentPair convertItemReward(String oldSpecification, String path) { + var matcher = REWARD_PATTERN.matcher(oldSpecification); + if (!matcher.matches()) { + throw new IllegalArgumentException("Invalid reward item specification at path " + path); + } + var probability = matcher.group("prob"); + + var itemType = fixMaterial(matcher.group("id")); + var type = Material.matchMaterial(itemType); + if (type != null) { + itemType = type.getKey().getKey(); + } else { + itemType = itemType.toLowerCase(Locale.ROOT); + logger.warning("Unknown material " + itemType + " in reward item specification at " + path); + } + var sub = matcher.group("sub"); + if (sub != null) { + logger.warning("Old pre-1.13 item specification at " + path + " uses subtypes, which are no longer supported."); + } + var amount = matcher.group("amount"); + var meta = matcher.group("meta"); + if (meta != null && meta.trim().isEmpty()) { + meta = null; + } + String comment = null; + String convertedMeta = convertDefaultItemMeta(meta); + + if (meta != null && convertedMeta == null) { + logger.warning("Some items contain NBT tags, which are not automatically converted. Please check the entry at " + path + " manually."); + comment = itemType + meta; + } + + var newSpecification = new StringBuilder(); + if (probability != null) { + newSpecification.append("{p=").append(probability).append("}"); + } + newSpecification.append(itemType); + if (convertedMeta != null) { + newSpecification.append(convertedMeta); + } else if (meta != null) { + newSpecification.append("[]"); + } + + newSpecification.append(":").append(amount); + + return new SpecificationCommentPair(newSpecification.toString(), comment); + } + + private SpecificationCommentPair convertItemRequirement(String oldSpecification, String path) { + var matcher = REQUIREMENT_PATTERN.matcher(oldSpecification); + if (!matcher.matches()) { + throw new IllegalArgumentException("Invalid requirement item specification at path " + path + ": " + oldSpecification); + } + + var itemType = fixMaterial(matcher.group("type")); + var type = Material.matchMaterial(itemType); + if (type != null) { + itemType = type.getKey().getKey(); + } else { + itemType = itemType.toLowerCase(Locale.ROOT); + logger.warning("Unknown material " + itemType + " in reward item specification at " + path); + } + var sub = matcher.group("subtype"); + if (sub != null) { + logger.warning("Old pre-1.13 item specification at " + path + " uses subtypes, which are no longer supported."); + } + var amount = matcher.group("amount"); + var meta = matcher.group("meta"); + if (meta != null && meta.trim().isEmpty()) { + meta = null; + } + String comment = null; + if (meta != null) { + logger.warning("Some items contain NBT tags, which are not automatically converted. Please check the entry at " + path + " manually."); + comment = itemType + meta; + } + + var op = matcher.group("op"); + var inc = matcher.group("inc"); + + var newSpecification = new StringBuilder(); + newSpecification.append(itemType); + if (meta != null) { + newSpecification.append("[]"); + } + newSpecification.append(":").append(amount); + if (op != null) { + newSpecification.append(";").append(op).append(inc); + } + + return new SpecificationCommentPair(newSpecification.toString(), comment); + } + + private static final Map DEFAULT_ITEM_META_CONVERSION = Map.of( + "{Enchantments:[{id:\"minecraft:infinity\",lvl:1},{id:\"minecraft:unbreaking\",lvl:3}]}", "[enchantments={levels:{infinity:1,unbreaking:3}}]", + "{Enchantments:[{id:\"minecraft:infinity\",lvl:1},{id:\"minecraft:unbreaking\",lvl:3},{id:\"minecraft:power\",lvl:5}]}", "[enchantments={levels:{infinity:1,power:5,unbreaking:3}}]", + "{Enchantments:[{id:\"minecraft:sharpness\",lvl:5},{id:\"minecraft:unbreaking\",lvl:3},{id:\"minecraft:fire\",lvl:1}]}", "[enchantments={levels:{fire_aspect:1,sharpness:5,unbreaking:3}}]", + "{Enchantments:[{id:\"minecraft:feather_falling\",lvl:4},{id:\"minecraft:protection\",lvl:4},{id:\"minecraft:blast_protection\",lvl:4}]}", "[enchantments={levels:{blast_protection:4,feather_falling:4,protection:4}}]" + ); + + private String convertDefaultItemMeta(String oldMeta) { + if (oldMeta == null) { + return null; + } + return DEFAULT_ITEM_META_CONVERSION.get(oldMeta); + } + + private static final Map MATERIAL_UPDATES = Map.of( + "SIGN", "OAK_SIGN", + "SAPLING", "OAK_SAPLING" + ); + + private String fixMaterial(String material) { + return MATERIAL_UPDATES.getOrDefault(material, material); + } + + private record SpecificationCommentPair(@NotNull String item, @Nullable String comment) { + } +} diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/imports/USBImporterExecutor.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/imports/USBImporterExecutor.java index bb3e34e75..a68505711 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/imports/USBImporterExecutor.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/imports/USBImporterExecutor.java @@ -1,19 +1,18 @@ package us.talabrek.ultimateskyblock.imports; -import dk.lockfuglsang.minecraft.util.TimeUtil; +import com.google.inject.Inject; +import com.google.inject.Singleton; +import dk.lockfuglsang.minecraft.util.Timer; import org.bukkit.Bukkit; import org.bukkit.command.CommandSender; -import us.talabrek.ultimateskyblock.imports.challenges.ConfigPre113Importer; import us.talabrek.ultimateskyblock.imports.fixuuidleader.UUIDLeaderImporter; -import us.talabrek.ultimateskyblock.imports.name2uuid.Name2UUIDImporter; import us.talabrek.ultimateskyblock.imports.update.USBUpdateImporter; -import us.talabrek.ultimateskyblock.imports.wolfwork.WolfWorkUSBImporter; import us.talabrek.ultimateskyblock.uSkyBlock; import us.talabrek.ultimateskyblock.util.ProgressTracker; import java.io.File; +import java.time.Duration; import java.util.ArrayList; -import java.util.Iterator; import java.util.List; import java.util.ServiceLoader; import java.util.logging.Level; @@ -25,20 +24,22 @@ /** * Delegates and batches the import. */ +@Singleton public class USBImporterExecutor { private final uSkyBlock plugin; private final ProgressTracker progressTracker; private List importers; - private volatile long tStart; + private volatile Timer timer; private volatile int countSuccess; private volatile int countSkip; private volatile int countFailed; + @Inject public USBImporterExecutor(uSkyBlock plugin) { this.plugin = plugin; double progressEveryPct = plugin.getConfig().getDouble("importer.progressEveryPct", 10); - long progressEveryMs = plugin.getConfig().getLong("importer.progressEveryMs", 10000); - progressTracker = new ProgressTracker(Bukkit.getConsoleSender(), marktr("\u00a7eProgress: {0,number,##}% ({1}/{2} - success:{3}, failed:{4}, skipped:{5}) ~ {6}"), progressEveryPct, progressEveryMs); + Duration progressInterval = Duration.ofMillis(plugin.getConfig().getLong("importer.progressEveryMs", 10000)); + progressTracker = new ProgressTracker(Bukkit.getConsoleSender(), marktr("\u00a7eProgress: {0,number,##}% ({1}/{2} - success:{3}, failed:{4}, skipped:{5}) ~ {6}"), progressEveryPct, progressInterval); } public List getImporterNames() { @@ -52,14 +53,11 @@ public List getImporterNames() { private List getImporters() { if (importers == null) { importers = new ArrayList<>(); - importers.add(new WolfWorkUSBImporter()); importers.add(new UUIDLeaderImporter()); importers.add(new USBUpdateImporter()); - importers.add(new Name2UUIDImporter()); - importers.add(new ConfigPre113Importer()); - ServiceLoader serviceLoader = ServiceLoader.load(USBImporter.class, getClass().getClassLoader()); - for (Iterator it = serviceLoader.iterator(); it.hasNext(); ) { - importers.add(it.next()); + ServiceLoader serviceLoader = ServiceLoader.load(USBImporter.class, getClass().getClassLoader()); + for (USBImporter usbImporter : serviceLoader) { + importers.add(usbImporter); } } return importers; @@ -83,16 +81,11 @@ public void importUSB(final CommandSender sender, String name) { sender.sendMessage(tr("\u00a74No importer named \u00a7e{0}\u00a74 found", name)); return; } - Bukkit.getServer().getScheduler().runTaskAsynchronously(plugin, new Runnable() { - @Override - public void run() { - doImport(sender, importer); - } - }); + Bukkit.getServer().getScheduler().runTaskAsynchronously(plugin, () -> doImport(sender, importer)); } private void doImport(CommandSender sender, USBImporter importer) { - tStart = System.currentTimeMillis(); + this.timer = Timer.start(); importer.init(plugin); countSuccess = 0; countFailed = 0; @@ -108,8 +101,7 @@ private void doImport(CommandSender sender, USBImporter importer) { private void doImport(final CommandSender sender, final USBImporter importer, final File[] files) { try { - for (int i = 0; i < files.length; i++) { - File file = files[i]; + for (File file : files) { try { Boolean status = importer.importFile(file); if (status == null) { @@ -126,9 +118,7 @@ private void doImport(final CommandSender sender, final USBImporter importer, fi countFailed++; log(Level.WARNING, "Could not import file " + file, t); } - progressTracker.progressUpdate(countSuccess + countFailed + countSkip, files.length, - countSuccess, countFailed, countSkip, - TimeUtil.millisAsString(System.currentTimeMillis()-tStart)); + progressTracker.progressUpdate(countSuccess + countFailed + countSkip, files.length, countSuccess, countFailed, countSkip, timer.elapsedAsString()); } } finally { complete(sender, importer); @@ -137,8 +127,7 @@ private void doImport(final CommandSender sender, final USBImporter importer, fi private void complete(CommandSender sender, USBImporter importer) { importer.completed(countSuccess, countFailed, countSkip); - sender.sendMessage(tr("\u00a7eConverted {0}/{1} files in {2}", - countSuccess, (countSuccess + countFailed), TimeUtil.millisAsString(System.currentTimeMillis() - tStart))); + sender.sendMessage(tr("\u00a7eConverted {0}/{1} files in {2}", countSuccess, (countSuccess + countFailed), timer.elapsedAsString())); plugin.getConfig().set("importer." + importer.getName() + ".imported", true); plugin.saveConfig(); } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/imports/challenges/ConfigPre113Importer.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/imports/challenges/ConfigPre113Importer.java deleted file mode 100644 index 0c80e20f4..000000000 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/imports/challenges/ConfigPre113Importer.java +++ /dev/null @@ -1,197 +0,0 @@ -package us.talabrek.ultimateskyblock.imports.challenges; - -import dk.lockfuglsang.minecraft.util.ItemStackUtil; -import dk.lockfuglsang.minecraft.yml.YmlConfiguration; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.configuration.file.FileConfiguration; -import org.bukkit.inventory.ItemStack; -import us.talabrek.ultimateskyblock.imports.USBImporter; -import us.talabrek.ultimateskyblock.uSkyBlock; -import us.talabrek.ultimateskyblock.util.LogUtil; - -import java.io.File; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Set; -import java.util.logging.Level; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import java.util.stream.Collectors; - -import static dk.lockfuglsang.minecraft.file.FileUtil.readConfig; - -public class ConfigPre113Importer implements USBImporter { - private static final Pattern REQ_PATTERN = Pattern.compile("(?(?[0-9A-Z_]+)(:(?[0-9]+))?)(?\\{.*\\})?:(?[0-9]+)(;(?[+\\-*\\^])(?[0-9]+))?"); - private static final Pattern ITEM_AMOUNT_PATTERN = Pattern.compile("(\\{p=(?0\\.[0-9]+)\\})?(?(?[0-9A-Z_]+)(:(?[0-9]+))?):(?[0-9]+)\\s*(?\\{.*\\})?"); - private static final Pattern ITEM_PATTERN = Pattern.compile("(?(?[0-9A-Z_]+)(:(?[0-9]+))?)\\s*(?\\{.*\\})?"); - - private uSkyBlock plugin; - - @Override - public String getName() { - return "config-1-13"; - } - - @Override - public void init(uSkyBlock plugin) { - this.plugin = plugin; - plugin.setMaintenanceMode(true); - } - - @Override - public Boolean importFile(File file) { - FileConfiguration config = new YmlConfiguration(); - readConfig(config, file); - try { - config.save(new File(file.getParentFile(), file.getName() + ".org")); - } catch (IOException e) { - e.printStackTrace(); - } - if (file.getName().equals("challenges.yml")) { - convertChallenges(config); - } else if (file.getName().equals("config.yml")) { - config = plugin.getConfig(); - convertConfig(config); - } - - try { - config.save(file); - } catch (IOException e) { - e.printStackTrace(); - } - return true; - } - - private void convertConfig(FileConfiguration config) { - Set keys = config.getKeys(true); - // displayItems (and lockedDisplayItem) - for (String key : keys.stream().filter(f -> f.toLowerCase().endsWith("displayitem")).collect(Collectors.toList())) { - try { - config.set(key, convertItem(config.getString(key))); - } catch (Exception e) { - LogUtil.log(Level.WARNING, "Unable to convert config.yml:" + key + ":" + e.getMessage()); - } - } - // chestItems - for (String key : keys.stream().filter(f -> f.endsWith(".chestItems") || f.endsWith(".extraItems")).collect(Collectors.toList())) { - try { - List value = new ArrayList<>(); - value.addAll(convertItemAmountList(config.getStringList(key))); - config.set(key, value); - } catch (Exception e) { - LogUtil.log(Level.WARNING, "Unable to convert config.yml:" + key + ":" + e.getMessage()); - } - } - // extraPermissions - ConfigurationSection extraPerms = config.getConfigurationSection("options.island.extraPermissions"); - if (extraPerms != null) { - for (String key : extraPerms.getKeys(false)) { - try { - extraPerms.set(key, convertItemAmountList(extraPerms.getStringList(key))); - } catch (Exception e) { - LogUtil.log(Level.WARNING, "Unable to convert config.yml:" + key + ":" + e.getMessage()); - } - } - } - } - - private void convertChallenges(FileConfiguration config) { - // Convert all requiredItems - Set keys = config.getKeys(true); - for (String key : keys.stream().filter(f -> f.endsWith(".requiredItems")).collect(Collectors.toList())) { - try { - List value = new ArrayList<>(); - value.addAll(convertRequiredItemsList(config.getStringList(key))); - config.set(key, value); - } catch (Exception e) { - LogUtil.log(Level.WARNING, "Unable to convert challenges.yml:" + key + ":" + e.getMessage()); - } - } - // Rewards (.items) - for (String key : keys.stream().filter(f -> f.endsWith(".items")).collect(Collectors.toList())) { - try { - List value = new ArrayList<>(); - value.addAll(convertItemAmountList(config.getStringList(key))); - config.set(key, value); - } catch (Exception e) { - LogUtil.log(Level.WARNING, "Unable to convert challenges.yml:" + key + ":" + e.getMessage()); - } - } - // displayItems (and lockedDisplayItem) - for (String key : keys.stream().filter(f -> f.toLowerCase().endsWith("displayitem")).collect(Collectors.toList())) { - try { - config.set(key, convertItem(config.getString(key))); - } catch (Exception e) { - LogUtil.log(Level.WARNING, "Unable to convert challenges.yml:" + key + ":" + e.getMessage()); - } - } - } - - private Collection convertItemAmountList(List stringList) { - List values = new ArrayList<>(); - for (String value : stringList) { - values.add(convertItemAmount(value)); - } - return values.stream().filter(f -> f != null).collect(Collectors.toList()); - } - - private Collection convertRequiredItemsList(List stringList) { - List values = new ArrayList<>(); - for (String value : stringList) { - values.add(convertRequiredItems(value)); - } - return values.stream().filter(f -> f != null).collect(Collectors.toList()); - } - - private String convertRequiredItems(String value) { - Matcher m = REQ_PATTERN.matcher(value); - if (m.matches()) { - String itemstack = m.group("itemstack"); - ItemStack itemStack = ItemStackUtil.createItemStack(itemstack); - String newItemStack = itemStack.getType().name() + (itemStack.getDurability() > 0 ? ":" + itemStack.getDurability() : ""); - return replaceItemStack(m, value, newItemStack); - } - return null; - } - - private String convertItemAmount(String reward) { - Matcher m = ITEM_AMOUNT_PATTERN.matcher(reward); - if (!m.matches()) { - throw new IllegalArgumentException("Unknown item: '" + reward + "'"); - } - String itemstack = m.group("itemstack"); - ItemStack itemStack = ItemStackUtil.createItemStack(itemstack); - String newItemStack = itemStack.getType().name() + (itemStack.getDurability() > 0 ? ":" + itemStack.getDurability() : ""); - return replaceItemStack(m, reward, newItemStack); - } - - private String convertItem(String displayItem) { - Matcher m = ITEM_PATTERN.matcher(displayItem); - if (!m.matches()) { - throw new IllegalArgumentException("Unknown displayItem: '" + displayItem + "'"); - } - String itemstack = m.group("itemstack"); - ItemStack itemStack = ItemStackUtil.createItemStack(itemstack); - String newItemStack = itemStack.getType().name() + (itemStack.getDurability() > 0 ? ":" + itemStack.getDurability() : ""); - return replaceItemStack(m, displayItem, newItemStack); - } - - private String replaceItemStack(Matcher m, String original, String replacement) { - return original.substring(0, m.start("itemstack")) + replacement + original.substring(m.end("itemstack")); - } - - @Override - public File[] getFiles() { - return new File[]{ - new File(plugin.getDataFolder(), "challenges.yml"), - new File(plugin.getDataFolder(), "config.yml"), - }; - } - - @Override - public void completed(int success, int failed, int skipped) { - plugin.setMaintenanceMode(false); - } -} diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/imports/fixuuidleader/UUIDLeaderImporter.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/imports/fixuuidleader/UUIDLeaderImporter.java index 68e6831de..7ace8bec6 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/imports/fixuuidleader/UUIDLeaderImporter.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/imports/fixuuidleader/UUIDLeaderImporter.java @@ -8,12 +8,12 @@ import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; -import java.io.FilenameFilter; import java.io.IOException; -import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardCopyOption; +import java.util.Objects; import java.util.logging.Level; import java.util.logging.Logger; @@ -46,7 +46,7 @@ public Boolean importFile(File file) { boolean changed = false; String leaderName = null; try (FileWriter fw = new FileWriter(ymlPath.toFile()); BufferedWriter out = new BufferedWriter(fw)) { - for (String line : Files.readAllLines(file.toPath(), Charset.forName("UTF-8"))) { + for (String line : Files.readAllLines(file.toPath(), StandardCharsets.UTF_8)) { if (line.contains("leader:")) { leaderName = line.substring(line.indexOf("leader:") + 7).trim(); } @@ -70,12 +70,10 @@ public Boolean importFile(File file) { @Override public File[] getFiles() { - return plugin.directoryIslands.listFiles(new FilenameFilter() { - @Override - public boolean accept(File dir, String name) { - return name != null && name.endsWith(".err"); - } - }); + var result = plugin.getDataFolder().toPath() + .resolve("islands").toFile() + .listFiles((dir, name) -> name != null && name.endsWith(".err")); + return Objects.requireNonNullElseGet(result, () -> new File[0]); } @Override diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/imports/name2uuid/Name2UUIDImporter.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/imports/name2uuid/Name2UUIDImporter.java deleted file mode 100644 index d67a8329f..000000000 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/imports/name2uuid/Name2UUIDImporter.java +++ /dev/null @@ -1,335 +0,0 @@ -package us.talabrek.ultimateskyblock.imports.name2uuid; - -import dk.lockfuglsang.minecraft.file.FileUtil; -import org.bukkit.Bukkit; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.configuration.file.FileConfiguration; -import org.bukkit.configuration.file.YamlConfiguration; -import us.talabrek.ultimateskyblock.handler.WorldGuardHandler; -import us.talabrek.ultimateskyblock.imports.USBImporter; -import us.talabrek.ultimateskyblock.mojang.MojangAPI; -import us.talabrek.ultimateskyblock.mojang.NameUUIDConsumer; -import us.talabrek.ultimateskyblock.mojang.ProgressCallback; -import us.talabrek.ultimateskyblock.uSkyBlock; -import us.talabrek.ultimateskyblock.util.ProgressTracker; -import dk.lockfuglsang.minecraft.util.TimeUtil; -import us.talabrek.ultimateskyblock.util.UUIDUtil; -import us.talabrek.ultimateskyblock.uuid.PlayerDB; - -import java.io.File; -import java.io.FilenameFilter; -import java.io.IOException; -import java.text.MessageFormat; -import java.util.ArrayList; -import java.util.Date; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.UUID; -import java.util.logging.FileHandler; -import java.util.logging.Formatter; -import java.util.logging.Level; -import java.util.logging.LogRecord; -import java.util.logging.Logger; - -import static dk.lockfuglsang.minecraft.po.I18nUtil.marktr; -import static dk.lockfuglsang.minecraft.po.I18nUtil.tr; - -/** - * Converts from name-based files to UUID based. - */ -public class Name2UUIDImporter implements USBImporter { - - private Logger log; - - public static final FilenameFilter YML_FILES = new FilenameFilter() { - @Override - public boolean accept(File dir, String name) { - // Don't include UUID converted files - return name != null && name.endsWith(".yml") && !name.matches("[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}.yml"); - } - }; - - private uSkyBlock plugin; - private FileHandler handler; - private PlayerDB playerDB; - private File[] playerFiles; - private File playerErrorFolder; - private File islandErrorFolder; - private Set invalidNames = new HashSet<>(); - - @Override - public String getName() { - return "name2uuid"; - } - - @Override - public void init(uSkyBlock plugin) { - this.plugin = plugin; - playerDB = plugin.getPlayerDB(); - plugin.setMaintenanceMode(true); - log = plugin.getLogger(); - try { - if (handler == null) { - handler = new FileHandler(plugin.getDataFolder() + File.separator + "name2uuid-report.log", true); - handler.setFormatter(new SingleLineFormatter()); - } else { - log.removeHandler(handler); - } - log.addHandler(handler); - log.setUseParentHandlers(false); - } catch (IOException e) { - log.severe("Unable to create file-logging to a report.log file"); - } - log.info("==================================="); - log.info("=== Running name2uuid importer"); - log.info("==================================="); - } - - @Override - public Boolean importFile(File file) { - if (file.getParentFile().getName().equalsIgnoreCase("players")) { - return importPlayer(file); - } else if (file.getParentFile().getName().equalsIgnoreCase("islands")){ - return importIsland(file); - } else if (file.getName().equalsIgnoreCase("uuid2name.yml")) { - return importPlayerDB(); - } - return null; - } - - private Boolean importPlayerDB() { - final long tStart = System.currentTimeMillis(); - double progressEveryPct = plugin.getConfig().getDouble("importer.progressEveryPct", 10); - long progressEveryMs = plugin.getConfig().getLong("importer.progressEveryMs", 30000); - - List names = new ArrayList<>(); - for (File f : playerFiles) { - names.add(FileUtil.getBasename(f)); - } - final PlayerDB playerDB = plugin.getPlayerDB(); - Bukkit.getConsoleSender().sendMessage(tr("\u00a77PlayerDB: Filtering {0} players from uuid2name.yml", names.size())); - ProgressTracker dbtracker = new ProgressTracker(Bukkit.getConsoleSender(), - marktr("\u00a77 - {0,number,##}% ({1}/{2}) ~ {3}"), - progressEveryPct, progressEveryMs); - int cnt = 0; - int total = names.size(); - for (Iterator it = names.iterator(); it.hasNext(); ) { - String name = it.next(); - if (playerDB.getUUIDFromName(name, false) != null) { - cnt++; - it.remove(); - } - if (cnt % 20 == 0) { - dbtracker.progressUpdate(cnt, total, TimeUtil.millisAsString(System.currentTimeMillis()-tStart)); - } - } - Bukkit.getConsoleSender().sendMessage(tr("\u00a77PlayerDB: Filtered {0} names", cnt)); - - final ProgressTracker tracker = new ProgressTracker(Bukkit.getConsoleSender(), - marktr("\u00a77 - MojangAPI:{4}: {0,number,##}% ({1}/{2}, failed:{3} ~ {5,number,##}%), {6}"), - progressEveryPct, progressEveryMs); - MojangAPI mojangAPI = new MojangAPI(); - Bukkit.getConsoleSender().sendMessage(tr("\u00a77MojangAPI: Trying to fetch {0} players from Mojang", names.size())); - - // This call blocks - good thing we are in Async mode - mojangAPI.fetchUUIDs(names, new NameUUIDConsumer() { - @Override - public void success(Map names) { - for (Map.Entry entry : names.entrySet()) { - playerDB.updatePlayer(entry.getValue(), entry.getKey(), null); - } - } - - @Override - public void renamed(String oldName, String newName, UUID id) { - playerDB.updatePlayer(id, oldName, null); - playerDB.updatePlayer(id, newName, null); - } - - @Override - public void unknown(List unknownNames) { - invalidNames.addAll(unknownNames); - } - }, new ProgressCallback() { - @Override - public void progress(int progress, int failed, int total, String message) { - tracker.progressUpdate(progress, total, failed, message, - 100f*failed/(progress > 0 ? progress : 1), TimeUtil.millisAsString(System.currentTimeMillis()-tStart)); - } - - @Override - public void complete(boolean success) { - Bukkit.getConsoleSender().sendMessage(tr("\u00a77 - MojangAPI:\u00a7aCOMPLETED: {0}", success ? tr("SUCCESS") : tr("FAILED"))); - } - - @Override - public void error(String message) { - Bukkit.getConsoleSender().sendMessage(tr("\u00a77 - MojangAPI:\u00a7cERROR: {0}", message)); - } - }); - return true; - } - - private Boolean importPlayer(File file) { - log.info("Importing " + file); - String name = FileUtil.getBasename(file); - FileConfiguration config = new YamlConfiguration(); - FileUtil.readConfig(config, file); - UUID uniqueId; - if (invalidNames.contains(name)) { - uniqueId = UUIDUtil.fromString(config.getString("player.uuid", null)); - if (uniqueId != null) { - invalidNames.remove(name); - playerDB.updatePlayer(uniqueId, name, null); - } - } else { - uniqueId = playerDB.getUUIDFromName(name); - } - if (uniqueId == null) { - log.info("No UUID found for " + name); - file.renameTo(new File(playerErrorFolder, file.getName())); - return false; - } - File newConfig = new File(plugin.getDataFolder() + File.separator + "players", uniqueId.toString() + ".yml"); - if (file.renameTo(newConfig)) { - FileUtil.readConfig(config, newConfig); - config.set("player.name", name); - config.set("player.uuid", UUIDUtil.asString(uniqueId)); - try { - config.save(newConfig); - if (!newConfig.getName().equals(file.getName())) { - if (file.exists() && !file.delete()) { - file.deleteOnExit(); - } - } - return true; - } catch (IOException e) { - log.log(Level.SEVERE, "Failed!", e); - return false; - } - } else if (newConfig.exists()) { - log.info("Unable to move " + file + " to " + newConfig + " since it already exists!"); - file.renameTo(new File(newConfig.getParent(), newConfig.getName() + ".old")); - } - return false; - } - - private Boolean importIsland(File file) { - log.info("Importing " + file); - FileConfiguration config = new YamlConfiguration(); - FileUtil.readConfig(config, file); - if (config.getInt("version", 0) >= 3) { - log.info("- island already converted, version is " + config.getInt("version")); - return null; - } - if (config.contains("party.leader")) { - String leaderName = config.getString("party.leader", null); - if (invalidNames.contains(leaderName)) { - log.info("Island leader had no UUID, removing island " + file); - file.renameTo(new File(islandErrorFolder, file.getName())); - String islandName = FileUtil.getBasename(file); - plugin.getOrphanLogic().addOrphan(islandName); - WorldGuardHandler.removeIslandRegion(islandName); - return false; - } - config.set("party.leader-uuid", getUUIDString(leaderName)); - } - ConfigurationSection members = config.getConfigurationSection("party.members"); - if (members != null) { - for (String member : members.getKeys(false)) { - ConfigurationSection section = members.getConfigurationSection(member); - members.set(member, null); - String uuid = getUUIDString(member); - if (uuid != null) { - members.createSection(uuid, section.getValues(true)); - members.set(uuid + ".name", member); - } - } - } - List bans = config.getStringList("banned.list"); - List newBans = new ArrayList<>(); - for (String name : bans) { - String uuid = getUUIDString(name); - if (uuid != null) { - newBans.add(uuid); - } - } - config.set("banned.list", newBans); - List trusts = config.getStringList("trust.list"); - List newTrusts = new ArrayList<>(); - for (String name : trusts) { - String uuid = getUUIDString(name); - if (uuid != null) { - newTrusts.add(uuid); - } - } - config.set("trust.list", newTrusts); - config.set("version", 3); - try { - config.save(file); - return true; - } catch (IOException e) { - log.log(Level.SEVERE, "Failed to import " + file, e); - return false; - } - } - - @Override - public File[] getFiles() { - File playerFolder = new File(plugin.getDataFolder(), "players"); - playerErrorFolder = new File(playerFolder, "errors"); - if (!playerErrorFolder.exists()) { - playerErrorFolder.mkdirs(); - } - playerFiles = playerFolder.listFiles(YML_FILES); - File islandFolder = new File(plugin.getDataFolder(), "islands"); - islandErrorFolder = new File(islandFolder, "errors"); - if (!islandErrorFolder.exists()) { - islandErrorFolder.mkdirs(); - } - File[] islandFiles = islandFolder.listFiles(YML_FILES); - File[] files = new File[islandFiles.length + playerFiles.length + 1]; - files[0] = new File(plugin.getDataFolder(), "uuid2name.yml"); - System.arraycopy(playerFiles, 0, files, 1, playerFiles.length); - System.arraycopy(islandFiles, 0, files, playerFiles.length+1, islandFiles.length); - return files; - } - - @Override - public void completed(int success, int failed, int skipped) { - if (handler != null) { - handler.close(); - log.removeHandler(handler); - log.setUseParentHandlers(true); - } - plugin.setMaintenanceMode(false); - } - - private String getUUIDString(String name) { - return invalidNames.contains(name) ? null : UUIDUtil.asString(getUUID(name)); - } - - private UUID getUUID(String playerName) { - return playerDB.getUUIDFromName(playerName); - } - - public static class SingleLineFormatter extends Formatter { - @Override - public String format(LogRecord record) { - try { - return String.format("%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS.%1$tL %2$5s : %3$s\n", - new Date(record.getMillis()), - record.getLevel().getName(), - record.getMessage()); - } catch (IllegalArgumentException e) { - return String.format("%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS.%1$tL %2$s : %3$s\n", - new Date(record.getMillis()), - record.getLevel().getName(), - MessageFormat.format(record.getMessage(), record.getParameters())); - } - } - } -} diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/imports/update/USBUpdateImporter.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/imports/update/USBUpdateImporter.java index 9fa78d4a2..ccbbb370e 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/imports/update/USBUpdateImporter.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/imports/update/USBUpdateImporter.java @@ -6,6 +6,7 @@ import us.talabrek.ultimateskyblock.util.IslandUtil; import java.io.File; +import java.util.Objects; /** * An importer that simply loads all island-infos into memory - activating the built-in yml @@ -34,7 +35,10 @@ public Boolean importFile(File file) { @Override public File[] getFiles() { - return plugin.directoryIslands.listFiles(IslandUtil.createIslandFilenameFilter()); + var result = plugin.getDataFolder().toPath() + .resolve("islands").toFile() + .listFiles(IslandUtil.createIslandFilenameFilter()); + return Objects.requireNonNullElseGet(result, () -> new File[0]); } @Override diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/imports/wolfwork/PlayerInfo.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/imports/wolfwork/PlayerInfo.java deleted file mode 100644 index 9c5bbb9d3..000000000 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/imports/wolfwork/PlayerInfo.java +++ /dev/null @@ -1,361 +0,0 @@ -package us.talabrek.ultimateskyblock.imports.wolfwork; - -import org.bukkit.Bukkit; -import org.bukkit.Location; -import org.bukkit.World; -import org.bukkit.entity.Player; - -import java.io.Serializable; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; - -/** - * Mirror of the PlayerInfo in wolfwork/uSkyBlock - */ -public class PlayerInfo implements Serializable { - private static final long serialVersionUID = 1L; - private String playerName; - private boolean hasIsland; - private boolean hasParty; - private boolean warpActive; - private List members; - private List banned; - private String partyLeader; - private String partyIslandLocation; - private String islandLocation; - private String homeLocation; - private String warpLocation; - private String deathWorld; - private HashMap challengeList; - private float islandExp; - private int islandLevel; - - public PlayerInfo(final String playerName) { - this.playerName = playerName; - members = new ArrayList(); - banned = new ArrayList(); - hasIsland = false; - warpActive = false; - islandLocation = null; - homeLocation = null; - warpLocation = null; - deathWorld = null; - hasParty = false; - partyLeader = null; - partyIslandLocation = null; - islandExp = 0.0F; - challengeList = new HashMap(); - islandLevel = 0; - } - - public void startNewIsland(Location l) { - hasIsland = true; - setIslandLocation(l); - islandLevel = 0; - islandExp = 0.0F; - partyIslandLocation = null; - partyLeader = null; - hasParty = false; - homeLocation = null; - warpLocation = null; - warpActive = false; - members = new ArrayList(); - } - - public void removeFromIsland() { - hasIsland = false; - setIslandLocation(null); - islandLevel = 0; - islandExp = 0.0F; - partyIslandLocation = null; - partyLeader = null; - hasParty = false; - homeLocation = null; - warpLocation = null; - warpActive = false; - members = new ArrayList(); - } - - public void toggleWarpActive() { - if (!this.warpActive) - warpActive = true; - else - warpActive = false; - } - - public void warpOn() { - warpActive = true; - } - - public void warpOff() { - warpActive = true; - } - - public boolean isWarpActive() { - return warpActive; - } - - public void setWarpLocation(Location l) { - warpLocation = getStringLocation(l); - } - - public Location getWarpLocation() { - return getLocationString(warpLocation); - } - - public List getBanned() { - if (banned == null) - banned = new ArrayList(); - return banned; - } - - public void addBan(String player) { - getBanned().add(player); - } - - public void removeBan(String player) { - getBanned().remove(player); - } - - public boolean isBanned(String player) { - return getBanned().contains(player); - } - - public void addMember(final String member) { - members.add(member); - } - - public void clearChallenges() { - challengeList.clear(); - } - - public void buildChallengeList() { - // Disabled - } - - public boolean challengeExists(final String challenge) { - if (challengeList.containsKey(challenge.toLowerCase())) { - return true; - } - return false; - } - - public boolean checkChallenge(final String challenge) { - if (challengeList.containsKey(challenge.toLowerCase())) { - return challengeList.get(challenge.toLowerCase()).booleanValue(); - } - - return false; - } - - public void completeChallenge(final String challenge) { - if (challengeList.containsKey(challenge)) { - challengeList.remove(challenge); - challengeList.put(challenge, Boolean.valueOf(true)); - } - } - - public void displayChallengeList() { - // Does nothing - } - - public String getDeathWorld() { - return deathWorld; - } - - public boolean getHasIsland() { - return hasIsland; - } - - public boolean getHasParty() { - if (members == null) { - members = new ArrayList(); - } - return hasParty; - } - - public Location getHomeLocation() { - return getLocationString(homeLocation); - } - - public float getIslandExp() { - return islandExp; - } - - public int getIslandLevel() { - return islandLevel; - } - - public Location getIslandLocation() { - return getLocationString(islandLocation); - } - - private Location getLocationString(final String s) { - if (s == null || s.trim() == "") { - return null; - } - final String[] parts = s.split(":"); - if (parts.length == 4) { - final World w = Bukkit.getServer().getWorld(parts[0]); - final int x = Integer.parseInt(parts[1]); - final int y = Integer.parseInt(parts[2]); - final int z = Integer.parseInt(parts[3]); - return new Location(w, x, y, z); - } - return null; - } - - public List getMembers() { - return members; - } - - public Location getPartyIslandLocation() { - return getLocationString(partyIslandLocation); - } - - public String getPartyLeader() { - return partyLeader; - } - - public Player getPlayer() { - //noinspection deprecation - return Bukkit.getPlayerExact(playerName); - } - - public String getPlayerName() { - return playerName; - } - - private String getStringLocation(final Location l) { - if (l == null) { - return ""; - } - return l.getWorld().getName() + ":" + l.getBlockX() + ":" + l.getBlockY() + ":" + l.getBlockZ(); - } - - public void ListData() { - // Do nothing - } - - public void removeMember(final String member) { - members.remove(member); - } - - public void resetAllChallenges() { - challengeList = null; - buildChallengeList(); - } - - public void resetChallenge(final String challenge) { - if (challengeList.containsKey(challenge)) { - challengeList.remove(challenge); - challengeList.put(challenge, Boolean.valueOf(false)); - } - } - - public void setDeathWorld(final String dw) { - deathWorld = dw; - } - - public void setHasIsland(final boolean b) { - hasIsland = b; - } - - public void setHomeLocation(final Location l) { - homeLocation = getStringLocation(l); - } - - public void setIslandExp(final float i) { - islandExp = i; - } - - public void setIslandLevel(final int i) { - islandLevel = i; - } - - public void setIslandLocation(final Location l) { - islandLocation = getStringLocation(l); - } - - public void setJoinParty(final String leader, final Location l) { - hasParty = true; - partyLeader = leader; - partyIslandLocation = getStringLocation(l); - } - - public void setLeaveParty() { - hasParty = false; - partyLeader = null; - islandLevel = 0; - partyIslandLocation = null; - members = new ArrayList(); - } - - public void setMembers(final List newMembers) { - members = newMembers; - } - - public void setPartyIslandLocation(final Location l) { - partyIslandLocation = getStringLocation(l); - } - - public void setPartyLeader(final String leader) { - partyLeader = leader; - } - - public void setPlayerName(final String s) { - playerName = s; - } - - public Location getTeleportLocation() { - Location target = getHomeLocation(); - if (target == null) { - if (getIslandLocation() == null && getHasParty()) - target = getPartyIslandLocation(); - else if (getIslandLocation() != null) - target = getIslandLocation(); - } - - return target; - } - - public boolean teleportHome(Player player) { - // Do nothing - return false; - } - - public boolean teleportWarp(Player player) { - // Do nothing - return false; - } - - public void recalculateLevel(final Runnable callback) { - // Do nothing - } - - public void save() { - // Do nothing - } - - @Override - public String toString() { - return "PlayerInfo{" + - "playerName='" + playerName + '\'' + - ", hasIsland=" + hasIsland + - ", hasParty=" + hasParty + - ", warpActive=" + warpActive + - ", members=" + members + - ", banned=" + banned + - ", partyLeader='" + partyLeader + '\'' + - ", partyIslandLocation='" + partyIslandLocation + '\'' + - ", islandLocation='" + islandLocation + '\'' + - ", homeLocation='" + homeLocation + '\'' + - ", warpLocation='" + warpLocation + '\'' + - ", deathWorld='" + deathWorld + '\'' + - ", challengeList=" + challengeList + - ", islandExp=" + islandExp + - ", islandLevel=" + islandLevel + - '}'; - } -} diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/imports/wolfwork/SerializableLocation.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/imports/wolfwork/SerializableLocation.java deleted file mode 100644 index df576a49c..000000000 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/imports/wolfwork/SerializableLocation.java +++ /dev/null @@ -1,31 +0,0 @@ -package us.talabrek.ultimateskyblock.imports.wolfwork; - -import org.bukkit.Bukkit; -import org.bukkit.Location; -import org.bukkit.World; - -import java.io.Serializable; - -public class SerializableLocation implements Serializable { - private static final long serialVersionUID = 23L; - private final String world; - private final double x; - private final double y; - private final double z; - - public SerializableLocation(final Location loc) { - x = loc.getX(); - y = loc.getY(); - z = loc.getZ(); - world = loc.getWorld().getName(); - } - - public Location getLocation() { - final World w = Bukkit.getWorld(world); - if (w == null) { - return null; - } - final Location toRet = new Location(w, x, y, z); - return toRet; - } -} \ No newline at end of file diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/imports/wolfwork/WolfWorkObjectInputStream.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/imports/wolfwork/WolfWorkObjectInputStream.java deleted file mode 100644 index 2677dfaa8..000000000 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/imports/wolfwork/WolfWorkObjectInputStream.java +++ /dev/null @@ -1,22 +0,0 @@ -package us.talabrek.ultimateskyblock.imports.wolfwork; - -import java.io.IOException; -import java.io.InputStream; -import java.io.ObjectInputStream; -import java.io.ObjectStreamClass; - -public class WolfWorkObjectInputStream extends ObjectInputStream { - public WolfWorkObjectInputStream(InputStream in) throws IOException { - super(in); - } - - @Override - protected Class resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException { - if (desc.getName().equals("us.talabrek.ultimateskyblock.PlayerInfo")) { - return PlayerInfo.class; - } else if (desc.getName().equals("us.talabrek.ultimateskyblock.SerializableLocation")) { - return SerializableLocation.class; - } - return super.resolveClass(desc); - } -} diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/imports/wolfwork/WolfWorkUSBImporter.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/imports/wolfwork/WolfWorkUSBImporter.java deleted file mode 100644 index 62c6c0f7b..000000000 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/imports/wolfwork/WolfWorkUSBImporter.java +++ /dev/null @@ -1,167 +0,0 @@ -package us.talabrek.ultimateskyblock.imports.wolfwork; - -import us.talabrek.ultimateskyblock.challenge.ChallengeCompletion; -import us.talabrek.ultimateskyblock.handler.WorldGuardHandler; -import us.talabrek.ultimateskyblock.imports.USBImporter; -import us.talabrek.ultimateskyblock.island.IslandInfo; -import us.talabrek.ultimateskyblock.uSkyBlock; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FilenameFilter; -import java.io.IOException; -import java.io.ObjectInputStream; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Stack; -import java.util.logging.Level; - -import static us.talabrek.ultimateskyblock.util.LogUtil.log; - -/** - * An importer for the wolfwork branch. - */ -public class WolfWorkUSBImporter implements USBImporter { - - private uSkyBlock plugin; - - @Override - public String getName() { - return "wolfwork"; - } - - @Override - public void init(uSkyBlock plugin) { - this.plugin = plugin; - } - - @Override - public Boolean importFile(File file) { - try { - if (file != null && file.getName().equals("orphanedIslands.bin")) { - if (file.exists()) { - importOrphanedIslands(plugin, file); - return true; - } - return false; - } - PlayerInfo playerInfo = readPlayerInfo(file); - if (playerInfo == null) { - return false; - } - us.talabrek.ultimateskyblock.player.PlayerInfo pi = importPlayerInfo(plugin, playerInfo); - importIsland(plugin, playerInfo, pi); - plugin.getPlayerLogic().removeActivePlayer(pi); - if (!file.delete()) { - file.deleteOnExit(); - } - return true; - } catch (Exception e) { - log(Level.WARNING, "Unable to import " + file, e); - return false; - } - } - - private int importOrphanedIslands(uSkyBlock plugin, File orphanFile) { - try (ObjectInputStream in = new WolfWorkObjectInputStream(new FileInputStream(orphanFile))) { - Object stackObj = in.readObject(); - if (stackObj instanceof Stack) { - int countOrphan = 0; - Stack stack = (Stack) stackObj; - while (!stack.isEmpty()) { - SerializableLocation remove = stack.remove(0); - plugin.getOrphanLogic().addOrphan(remove.getLocation()); - countOrphan++; - } - if (!orphanFile.delete()) { - orphanFile.deleteOnExit(); - } - return countOrphan; - } - } catch (IOException | ClassNotFoundException e) { - log(Level.WARNING, "Unable to read the orphanedIslands.bin file", e); - } - return 0; - } - - private us.talabrek.ultimateskyblock.player.PlayerInfo importPlayerInfo(uSkyBlock plugin, PlayerInfo playerInfo) { - // Copy PlayerInfo - us.talabrek.ultimateskyblock.player.PlayerInfo pi = plugin.getPlayerInfo(playerInfo.getPlayerName()); - if (playerInfo.getIslandLocation() != null) { - pi.setIslandLocation(playerInfo.getIslandLocation()); - } else if (playerInfo.getPartyIslandLocation() != null) { - pi.setIslandLocation(playerInfo.getPartyIslandLocation()); - } - pi.setHomeLocation(playerInfo.getHomeLocation()); - // Challenges - long now = System.currentTimeMillis(); - for (us.talabrek.ultimateskyblock.api.ChallengeCompletion challengeApi : pi.getChallenges()) { - if (challengeApi instanceof ChallengeCompletion) { - ChallengeCompletion challenge = (ChallengeCompletion) challengeApi; - if (playerInfo.checkChallenge(challenge.getName())) { - challenge.setCooldownUntil(now); - challenge.setTimesCompleted(1); - } else { - challenge.setTimesCompleted(0); - } - } - } - pi.save(); - return pi; - } - - private void importIsland(uSkyBlock plugin, PlayerInfo playerInfo, us.talabrek.ultimateskyblock.player.PlayerInfo pi) { - // Copy IslandInfo - IslandInfo islandInfo = plugin.getIslandInfo(pi); - if (islandInfo != null) { - if (playerInfo.getPartyLeader() != null) { - islandInfo.setupPartyLeader(playerInfo.getPartyLeader()); - } else { - islandInfo.setupPartyLeader(playerInfo.getPlayerName()); - } - for (String member : playerInfo.getMembers()) { - islandInfo.setupPartyMember(uSkyBlock.getInstance().getPlayerInfo(member)); - } - islandInfo.setWarp(playerInfo.isWarpActive()); - islandInfo.setWarpLocation(playerInfo.getWarpLocation()); - for (String banned : playerInfo.getBanned()) { - islandInfo.banPlayer(plugin.getPlayerDB().getUUIDFromName(banned)); - } - // Not really that important - since it's most likely different! - islandInfo.setLevel(playerInfo.getIslandLevel()); - islandInfo.setBiome("OCEAN"); - islandInfo.save(); - WorldGuardHandler.updateRegion(islandInfo); - } - } - - private PlayerInfo readPlayerInfo(File playerFile) { - try (ObjectInputStream in = new WolfWorkObjectInputStream(new FileInputStream(playerFile))) { - Object o = in.readObject(); - if (o instanceof PlayerInfo) { - return (PlayerInfo) o; - } - } catch (ClassNotFoundException|IOException e) { - // Ignore... - } - return null; - } - - @Override - public File[] getFiles() { - ArrayList fileList = new ArrayList<>(); - fileList.add(new File(plugin.getDataFolder(), "orphanedIslands.bin")); - fileList.addAll(Arrays.asList(plugin.directoryPlayers.listFiles(new FilenameFilter() { - @Override - public boolean accept(File dir, String name) { - return name != null && !name.endsWith(".yml"); - } - }))); - return fileList.toArray(new File[fileList.size()]); - } - - @Override - public void completed(int success, int failed, int skipped) { - // Do nothing - } -} diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/BlockLimitLogic.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/BlockLimitLogic.java index cfb00d6e0..a3f4e41d6 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/BlockLimitLogic.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/BlockLimitLogic.java @@ -1,12 +1,14 @@ package us.talabrek.ultimateskyblock.island; +import com.google.inject.Inject; +import com.google.inject.Singleton; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.configuration.file.FileConfiguration; +import org.jetbrains.annotations.NotNull; +import us.talabrek.ultimateskyblock.PluginConfig; import us.talabrek.ultimateskyblock.api.model.BlockScore; import us.talabrek.ultimateskyblock.island.level.IslandScore; -import us.talabrek.ultimateskyblock.uSkyBlock; import java.util.Collections; import java.util.HashMap; @@ -15,32 +17,33 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Logger; +@Singleton public class BlockLimitLogic { - public enum CanPlace { YES, UNCERTAIN, NO}; + public enum CanPlace {YES, UNCERTAIN, NO} - private static final Logger log = Logger.getLogger(BlockLimitLogic.class.getName()); - private uSkyBlock plugin; - private Map blockLimits = new HashMap<>(); + private final Map blockLimits = new HashMap<>(); // TODO: R4zorax - 13-07-2018: Persist this somehow - and use a guavacache - private Map> blockCounts = new HashMap<>(); + private final Map> blockCounts = new HashMap<>(); private final boolean limitsEnabled; - public BlockLimitLogic(uSkyBlock plugin) { - this.plugin = plugin; - FileConfiguration config = plugin.getConfig(); - limitsEnabled = config.getBoolean("options.island.block-limits.enabled", false); + @Inject + public BlockLimitLogic( + @NotNull PluginConfig config, + @NotNull Logger logger + ) { + limitsEnabled = config.getYamlConfig().getBoolean("options.island.block-limits.enabled", false); if (limitsEnabled) { - ConfigurationSection section = config.getConfigurationSection("options.island.block-limits"); + ConfigurationSection section = config.getYamlConfig().getConfigurationSection("options.island.block-limits"); Set keys = section.getKeys(false); keys.remove("enabled"); for (String key : keys) { - Material material = Material.getMaterial(key.toUpperCase()); + Material material = Material.matchMaterial(key.toUpperCase()); int limit = section.getInt(key, -1); if (material != null && limit >= 0) { blockLimits.put(material, limit); } else { - log.warning("Unknown material " + key + " supplied for block-limit, or value not an integer"); + logger.warning("Unknown material " + key + " supplied for block-limit, or value not an integer"); } } } @@ -50,7 +53,7 @@ public int getLimit(Material type) { return blockLimits.getOrDefault(type, Integer.MAX_VALUE); } - public Map getLimits() { + public Map getLimits() { return Collections.unmodifiableMap(blockLimits); } @@ -62,10 +65,10 @@ public void updateBlockCount(Location islandLocation, IslandScore score) { blockCounts.put(islandLocation, countMap); } - private Map asBlockCount(IslandScore score) { + private Map asBlockCount(IslandScore score) { Map countMap = new ConcurrentHashMap<>(); for (BlockScore blockScore : score.getTop()) { - Material type = blockScore.getBlock().getType(); + Material type = blockScore.getBlockData().getMaterial(); if (blockLimits.containsKey(type)) { int initalValue = countMap.getOrDefault(type, 0); initalValue += blockScore.getCount(); @@ -93,6 +96,9 @@ public CanPlace canPlace(Material type, IslandInfo islandInfo) { } else if (count == -2) { return CanPlace.UNCERTAIN; } + if (type == Material.HOPPER){ + count -= islandInfo.getHopperLimit(); + } return count < blockLimits.getOrDefault(type, Integer.MAX_VALUE) ? CanPlace.YES : CanPlace.NO; } @@ -102,7 +108,7 @@ public void incBlockCount(Location islandLocation, Material type) { } Map islandCount = blockCounts.getOrDefault(islandLocation, new ConcurrentHashMap<>()); int blockCount = islandCount.getOrDefault(type, 0); - islandCount.put(type, blockCount+1); + islandCount.put(type, blockCount + 1); blockCounts.put(islandLocation, islandCount); } @@ -112,7 +118,7 @@ public void decBlockCount(Location islandLocation, Material type) { } Map islandCount = blockCounts.getOrDefault(islandLocation, new ConcurrentHashMap<>()); int blockCount = islandCount.getOrDefault(type, 0); - islandCount.put(type, blockCount-1); + islandCount.put(type, blockCount - 1); blockCounts.put(islandLocation, islandCount); } } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/IslandGenerator.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/IslandGenerator.java index 1ff90c783..2adb6503f 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/IslandGenerator.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/IslandGenerator.java @@ -1,5 +1,7 @@ package us.talabrek.ultimateskyblock.island; +import com.google.inject.Inject; +import com.google.inject.Singleton; import dk.lockfuglsang.minecraft.util.ItemStackUtil; import org.bukkit.Bukkit; import org.bukkit.Location; @@ -7,11 +9,13 @@ import org.bukkit.World; import org.bukkit.block.Block; import org.bukkit.block.Chest; -import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import us.talabrek.ultimateskyblock.PluginConfig; import us.talabrek.ultimateskyblock.Settings; +import us.talabrek.ultimateskyblock.bootstrap.PluginDataDir; import us.talabrek.ultimateskyblock.handler.AsyncWorldEditHandler; import us.talabrek.ultimateskyblock.player.Perk; import us.talabrek.ultimateskyblock.player.PlayerPerk; @@ -23,6 +27,7 @@ import java.io.IOException; import java.io.InputStream; import java.net.URL; +import java.nio.file.Path; import java.security.CodeSource; import java.util.ArrayList; import java.util.Arrays; @@ -36,31 +41,39 @@ /** * The factory for creating islands (actual blocks). */ +@Singleton public class IslandGenerator { - private static final Logger log = Logger.getLogger(IslandGenerator.class.getName()); + private final Logger logger; private final File[] schemFiles; private final File netherSchematic; private final File directorySchematics; - public IslandGenerator(@NotNull File dataFolder, @NotNull FileConfiguration config) { - directorySchematics = new File(dataFolder + File.separator + "schematics"); + @Inject + public IslandGenerator( + @NotNull Logger logger, + @NotNull @PluginDataDir Path dataFolder, + @NotNull PluginConfig config + ) { + this.logger = logger; + directorySchematics = dataFolder.resolve("schematics").toFile(); if (!directorySchematics.exists()) { //noinspection ResultOfMethodCallIgnored directorySchematics.mkdir(); } copySchematicsFromJar(); - netherSchematic = getSchematicFile(config.getString("nether.schematicName", "uSkyBlockNether")); + netherSchematic = getSchematicFile(config.getYamlConfig().getString("nether.schematicName", "uSkyBlockNether")); schemFiles = loadSchematics(config); if (schemFiles == null) { - log.info("[uSkyBlock] No schematic file loaded."); + logger.info("No schematic file loaded."); } else { - log.info("[uSkyBlock] " + schemFiles.length + " schematics loaded."); + logger.info(schemFiles.length + " schematics loaded."); } } /** * Gets a {@link List} of available schematic names. + * * @return List of available schematic names. */ public List getSchemeNames() { @@ -74,9 +87,10 @@ public List getSchemeNames() { /** * Generate an island at the given {@link Location}. + * * @param playerPerk PlayerPerk object for the island owner. - * @param next Location to generate an island. - * @param cSchem New island schematic. + * @param next Location to generate an island. + * @param cSchem New island schematic. * @return True if the island was generated, false otherwise. */ public boolean createIsland(@NotNull PlayerPerk playerPerk, @NotNull Location next, @Nullable String cSchem) { @@ -105,8 +119,9 @@ public boolean createIsland(@NotNull PlayerPerk playerPerk, @NotNull Location ne /** * Find the nearest chest at the given {@link Location} and fill the chest with the starter and {@link Perk} * based items. + * * @param location Location to search for a chest. - * @param perk Perk containing extra perk-based items to add. + * @param perk Perk containing extra perk-based items to add. * @return True if the chest is found and filled, false otherwise. */ public boolean findAndSetChest(@NotNull Location location, @NotNull Perk perk) { @@ -116,8 +131,9 @@ public boolean findAndSetChest(@NotNull Location location, @NotNull Perk perk) { /** * Fill the {@link Inventory} of the given chest {@link Location} with the starter and {@link Perk} based items. + * * @param chestLocation Location of the chest block. - * @param perk Perk containing extra perk-based items to add. + * @param perk Perk containing extra perk-based items to add. * @return True if the chest is found and filled, false otherwise. */ public boolean setChest(@Nullable Location chestLocation, @NotNull Perk perk) { @@ -129,7 +145,7 @@ public boolean setChest(@Nullable Location chestLocation, @NotNull Perk perk) { if (block.getType() == Material.CHEST) { final Chest chest = (Chest) block.getState(); final Inventory inventory = chest.getInventory(); - inventory.addItem(Settings.island_chestItems); + inventory.addItem(Settings.getIslandChestItems().toArray(new ItemStack[0])); if (Settings.island_addExtraItems) { inventory.addItem(ItemStackUtil.createItemArray(perk.getExtraItems())); } @@ -158,36 +174,36 @@ private void copySchematicsFromJar() { try (InputStream inputStream = uSkyBlock.class.getClassLoader().getResourceAsStream(entry.getName())) { FileUtil.copy(inputStream, f); } catch (IOException e) { - log.log(Level.WARNING, "Unable to load schematic " + entry.getName(), e); + logger.log(Level.WARNING, "Unable to load schematic " + entry.getName(), e); } } } zin.closeEntry(); } } catch (IOException e) { - log.log(Level.WARNING, "Unable to find schematics in plugin JAR", e); + logger.log(Level.WARNING, "Unable to find schematics in plugin JAR", e); } } - private File[] loadSchematics(@NotNull FileConfiguration config) { + private File[] loadSchematics(@NotNull PluginConfig config) { return directorySchematics.listFiles((dir, name) -> { String basename = FileUtil.getBasename(name); - boolean enabled = config.getBoolean("island-schemes." + basename + ".enabled", true); + boolean enabled = config.getYamlConfig().getBoolean("island-schemes." + basename + ".enabled", true); return enabled && - name != null && - (name.endsWith(".schematic") || name.endsWith(".schem")) && - !name.startsWith("uSkyBlock") && - !basename.toLowerCase().endsWith("nether"); + name != null && + (name.endsWith(".schematic") || name.endsWith(".schem")) && + !name.startsWith("uSkyBlock") && + !basename.toLowerCase().endsWith("nether"); }); } private File getSchematicFile(String cSchem) { List extensions = Arrays.asList("schematic", "schem"); return extensions.stream() - .map(f -> new File(directorySchematics, cSchem + "." + f)) - .filter(f -> f.exists() && f.canRead()) - .findFirst() - .orElse(null); + .map(f -> new File(directorySchematics, cSchem + "." + f)) + .filter(f -> f.exists() && f.canRead()) + .findFirst() + .orElse(null); } private boolean isSchematicFile(ZipEntry entry, String prefix) { diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/IslandInfo.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/IslandInfo.java index 5b1893a7b..729c13b28 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/IslandInfo.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/IslandInfo.java @@ -1,11 +1,13 @@ package us.talabrek.ultimateskyblock.island; import dk.lockfuglsang.minecraft.util.TimeUtil; -import org.apache.commons.lang.Validate; +import org.apache.commons.lang3.Validate; import org.bukkit.ChatColor; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.OfflinePlayer; +import org.bukkit.Registry; +import org.bukkit.block.Biome; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.YamlConfiguration; @@ -30,10 +32,12 @@ import java.io.File; import java.io.IOException; +import java.nio.file.Path; import java.text.DateFormat; import java.text.ParseException; +import java.time.Duration; +import java.time.Instant; import java.util.ArrayList; -import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashSet; @@ -57,7 +61,6 @@ public class IslandInfo implements us.talabrek.ultimateskyblock.api.IslandInfo { private static final Logger log = Logger.getLogger(IslandInfo.class.getName()); private static final Pattern OLD_LOG_PATTERN = Pattern.compile("\u00a7d\\[(?[^\\]]+)\\]\u00a77 (?.*)"); private static final int YML_VERSION = 3; - private static File directory = new File("."); private final uSkyBlock plugin; private File file; @@ -66,13 +69,13 @@ public class IslandInfo implements us.talabrek.ultimateskyblock.api.IslandInfo { private boolean dirty = false; private boolean toBeDeleted = false; - public IslandInfo(@NotNull String islandName, @NotNull uSkyBlock plugin) { + public IslandInfo(@NotNull String islandName, @NotNull uSkyBlock plugin, @NotNull Path islandDirectory) { Validate.notNull(islandName, "IslandName cannot be null"); Validate.notEmpty(islandName, "IslandName cannot be empty"); this.plugin = plugin; config = new YamlConfiguration(); - file = new File(directory, islandName + ".yml"); + file = islandDirectory.resolve(islandName + ".yml").toFile(); name = islandName; if (file.exists()) { readConfig(config, file); @@ -95,7 +98,7 @@ private void updateConfig() { int oldMaxSize = config.getInt("maxSize"); if (oldMaxSize > Settings.general_maxPartySize) { ConfigurationSection leaderSection = config.getConfigurationSection("party.members." + - UUIDUtil.asString(getLeaderUniqueId())); + UUIDUtil.asString(getLeaderUniqueId())); if (leaderSection != null) { leaderSection.set("maxPartySizePermission", oldMaxSize); } @@ -115,10 +118,6 @@ private void updateConfig() { save(); } - public static void setDirectory(@NotNull File dir) { - directory = dir; - } - public void resetIslandConfig(@NotNull final String leader) { Validate.notNull(leader, "Leader cannot be null"); Validate.notEmpty(leader, "Leader cannot be empty"); @@ -205,7 +204,7 @@ public void updatePermissionPerks(@NotNull final Player member, @NotNull Perk pe if (isLeader(member)) { String oldLeaderName = getLeader(); config.set("party.leader", member.getName()); - updateRegion |= !oldLeaderName.equals(member.getName()); + updateRegion = !oldLeaderName.equals(member.getName()); } ConfigurationSection section = config.getConfigurationSection("party.members." + member.getUniqueId()); boolean dirty = false; @@ -242,7 +241,7 @@ public void updatePermissionPerks(@NotNull final Player member, @NotNull Perk pe section.set("blockLimits ", null); if (!blockLimits.isEmpty()) { ConfigurationSection maxBlocks = section.createSection("blockLimits"); - for (Map.Entry limit : blockLimits.entrySet()) { + for (Map.Entry limit : blockLimits.entrySet()) { maxBlocks.set(limit.getKey().name(), limit.getValue()); } } @@ -285,31 +284,31 @@ public void saveToFile() { @Override public int getMaxPartySize() { return getMaxPartyIntValue("maxPartySizePermission", - plugin.getPerkLogic().getIslandPerk(getSchematicName()).getPerk().getMaxPartySize()); + plugin.getPerkLogic().getIslandPerk(getSchematicName()).getPerk().getMaxPartySize()); } @Override public int getMaxAnimals() { return getMaxPartyIntValue("maxAnimals", - plugin.getPerkLogic().getIslandPerk(getSchematicName()).getPerk().getAnimals()); + plugin.getPerkLogic().getIslandPerk(getSchematicName()).getPerk().getAnimals()); } @Override public int getMaxMonsters() { return getMaxPartyIntValue("maxMonsters", - plugin.getPerkLogic().getIslandPerk(getSchematicName()).getPerk().getMonsters()); + plugin.getPerkLogic().getIslandPerk(getSchematicName()).getPerk().getMonsters()); } @Override public int getMaxVillagers() { return getMaxPartyIntValue("maxVillagers", - plugin.getPerkLogic().getIslandPerk(getSchematicName()).getPerk().getVillagers()); + plugin.getPerkLogic().getIslandPerk(getSchematicName()).getPerk().getVillagers()); } @Override public int getMaxGolems() { return getMaxPartyIntValue("maxGolems", - plugin.getPerkLogic().getIslandPerk(getSchematicName()).getPerk().getGolems()); + plugin.getPerkLogic().getIslandPerk(getSchematicName()).getPerk().getGolems()); } @Override @@ -329,7 +328,7 @@ public Map getBlockLimits() { blockLimitMap.computeIfAbsent(type, (k) -> { int memberMax = blockLimits.getInt(material, 0); return blockLimitMap.compute(type, (key, oldValue) -> - oldValue != null && memberMax > 0 ? Math.max(memberMax, oldValue) : null); + oldValue != null && memberMax > 0 ? Math.max(memberMax, oldValue) : null); }); } } @@ -385,20 +384,30 @@ public boolean hasPerm(String playerName, String perm) { return hasPerm(plugin.getPlayerDB().getUUIDFromName(playerName), perm); } - private boolean hasPerm(UUID uuid, String perm) { + public boolean hasPerm(UUID uuid, String perm) { return uuid.equals(getLeaderUniqueId()) || config.getBoolean("party.members." + UUIDUtil.asString(uuid) + "." + perm); } @Override - public String getBiome() { - return config.getString("general.biome", "OCEAN").toUpperCase(); + @SuppressWarnings("removal") + public Biome getIslandBiome() { + String biomeKey = config.getString("general.biome", Settings.general_defaultBiome.getKey().getKey()); + Biome biome = Registry.BIOME.match(biomeKey); + if (biome != null) { + return biome; + } else { + return Settings.general_defaultNetherBiome; + } } - public void setBiome(@NotNull String biome) { - Validate.notNull(biome, "Biome cannot be null"); - Validate.notEmpty(biome, "Biome cannot be empty"); + @Override + public String getBiomeName() { + return getIslandBiome().getKey().getKey(); + } - config.set("general.biome", biome.toUpperCase()); + public void setBiome(@NotNull Biome biome) { + Validate.notNull(biome, "Biome cannot be null"); + config.set("general.biome", biome.getKey().getKey()); save(); } @@ -415,17 +424,26 @@ public void setWarpLocation(@Nullable Location loc) { save(); } + // TODO: deprecate and replace all string-based methods + // TODO: unify all methods to take/return a custom Profile type that is guaranteed to contain a UUID and a name public boolean togglePerm(@NotNull final String playername, @NotNull final String perm) { Validate.notNull(playername, "Playername cannot be null"); Validate.notEmpty(playername, "Playername cannot be empty"); + + UUID uuid = plugin.getPlayerDB().getUUIDFromName(playername); + return togglePerm(uuid, perm); + } + + public boolean togglePerm(@NotNull final UUID playerId, @NotNull final String perm) { + Validate.notNull(playerId, "Playername cannot be null"); Validate.notNull(perm, "Perm cannot be null"); Validate.notEmpty(perm, "Perm cannot be empty"); - String uuidString = UUIDUtil.asString(plugin.getPlayerDB().getUUIDFromName(playername)); + String uuidString = UUIDUtil.asString(playerId); ConfigurationSection memberSection = config.getConfigurationSection("party.members." + uuidString); try { if (memberSection == null) { - log.info("Perms for " + playername + " failed to toggle because player is not a part of that island!"); + log.info("Perms for " + playerId + " failed to toggle because player is not a part of that island!"); return false; } if (memberSection.getBoolean(perm, false)) { @@ -436,7 +454,7 @@ public boolean togglePerm(@NotNull final String playername, @NotNull final Strin save(); return true; } catch (NullPointerException e) { - log.info("Perms for " + playername + " failed to toggle because player is not a part of that island!"); + log.info("Perms for " + playerId + " failed to toggle because player is not a part of that island!"); return false; } } @@ -507,7 +525,7 @@ public void log(@NotNull String message, @Nullable Object[] args) { sb.append(";").append(arg); } } - log.add(0, sb.toString()); + log.addFirst(sb.toString()); int logSize = plugin.getConfig().getInt("options.island.log-size", 10); if (log.size() > logSize) { log = log.subList(0, logSize); @@ -524,18 +542,22 @@ public int getPartySize() { public boolean isLeader(@NotNull OfflinePlayer target) { Validate.notNull(target, "Target cannot be null"); - return target.getUniqueId().equals(getLeaderUniqueId()); + return isLeader(target.getUniqueId()); } @Override - public boolean isLeader(Player player) { - return player.getUniqueId().equals(getLeaderUniqueId()); + public boolean isLeader(@NotNull Player player) { + return isLeader(player.getUniqueId()); } public boolean isLeader(String playerName) { return getLeaderUniqueId().equals(plugin.getPlayerDB().getUUIDFromName(playerName)); } + public boolean isLeader(@NotNull UUID playerId) { + return getLeaderUniqueId().equals(playerId); + } + public boolean hasWarp() { return config.getBoolean("general.warpActive"); } @@ -556,6 +578,7 @@ public void setWarp(boolean active) { /** * Locks the island. Might get cancelled via the fired {@link IslandLockEvent}. + * * @param player {@link Player} initializing the lock. * @return True if the island was locked, false otherwise, e.g. when the event is cancelled. */ @@ -582,6 +605,7 @@ public boolean lock(@NotNull Player player) { /** * Unlocks the island. Might get cancelled via the fired {@link IslandUnlockEvent}. + * * @param player {@link Player} initializing the unlock. * @return True if the island was unlocked, false otherwise, e.g. when the event is cancelled. */ @@ -906,28 +930,28 @@ public List getLog() { log.addAll(config.getStringList("log")); } List convertedList = new ArrayList<>(); - long t = System.currentTimeMillis(); + Instant now = Instant.now(); for (String logEntry : log) { String[] split = logEntry.split(";"); if (split.length >= 2) { - long then = Long.parseLong(split[0]); + Instant then = Instant.ofEpochMilli(Long.parseLong(split[0])); String msg = split[1]; Object[] args = new Object[split.length - 2]; System.arraycopy(split, 2, args, 0, args.length); - convertedList.add(tr("\u00a79{1} \u00a77- {0}", TimeUtil.millisAsString(t - then), tr(msg, args))); + convertedList.add(tr("\u00a79{1} \u00a77- {0}", TimeUtil.durationAsString(Duration.between(then, now)), tr(msg, args))); } else { Matcher m = OLD_LOG_PATTERN.matcher(logEntry); if (m.matches()) { String date = m.group("date"); - Date parsedDate = null; + Instant parsedDate = null; try { - parsedDate = DateFormat.getDateInstance(3).parse(date); + parsedDate = DateFormat.getDateInstance(DateFormat.SHORT).parse(date).toInstant(); } catch (ParseException e) { // Ignore } String msg = m.group("msg"); if (parsedDate != null) { - convertedList.add(tr("\u00a79{1} \u00a77- {0}", TimeUtil.millisAsString(t - parsedDate.getTime()), msg)); + convertedList.add(tr("\u00a79{1} \u00a77- {0}", TimeUtil.durationAsString(Duration.between(parsedDate, now)), msg)); } else { convertedList.add(logEntry); } @@ -949,11 +973,11 @@ public boolean isParty() { public Location getWarpLocation() { if (hasWarp()) { return new Location(plugin.getWorldManager().getWorld(), - config.getInt("general.warpLocationX", 0), - config.getInt("general.warpLocationY", 0), - config.getInt("general.warpLocationZ", 0), - (float) config.getDouble("general.warpYaw", 0), - (float) config.getDouble("general.warpPitch", 0)); + config.getInt("general.warpLocationX", 0), + config.getInt("general.warpLocationY", 0), + config.getInt("general.warpLocationZ", 0), + (float) config.getDouble("general.warpYaw", 0), + (float) config.getDouble("general.warpPitch", 0)); } return null; } @@ -968,7 +992,7 @@ public String toString() { String str = "\u00a7bIsland Info:\n"; str += ChatColor.GRAY + " - level: " + ChatColor.DARK_AQUA + String.format("%5.2f", getLevel()) + "\n"; str += ChatColor.GRAY + " - location: " + ChatColor.DARK_AQUA + name + "\n"; - str += ChatColor.GRAY + " - biome: " + ChatColor.DARK_AQUA + getBiome() + "\n"; + str += ChatColor.GRAY + " - biome: " + ChatColor.DARK_AQUA + getBiomeName() + "\n"; str += ChatColor.GRAY + " - schematic: " + ChatColor.DARK_AQUA + getSchematicName() + "\n"; str += ChatColor.GRAY + " - warp: " + ChatColor.DARK_AQUA + hasWarp() + "\n"; if (hasWarp()) { @@ -1073,6 +1097,15 @@ public void setLeafBreaks(int breaks) { dirty = true; } + public int getHopperLimit() { + return config.getInt("blocks.hopperLimits", 0); + } + + public void setHopperLimit(int limit) { + config.set("blocks.hopperLimits", limit); + dirty = true; + } + @Override public String getSchematicName() { return config.getString("general.schematicName", Settings.island_schematicName); @@ -1114,6 +1147,7 @@ public void setHopperCount(int i) { /** * If you need to inject a custom {@link File} for e.g. unit tests, do it here. + * * @param file Custom File */ public void setFile(File file) { @@ -1122,6 +1156,7 @@ public void setFile(File file) { /** * If you need to inject a custom {@link FileConfiguration} for e.g. unit tests, do it here. + * * @param config Custom FileConfiguration */ public void setConfig(FileConfiguration config) { diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/IslandLocatorLogic.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/IslandLocatorLogic.java index af27b0c5e..a8637fb51 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/IslandLocatorLogic.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/IslandLocatorLogic.java @@ -1,16 +1,26 @@ package us.talabrek.ultimateskyblock.island; +import com.google.inject.Inject; +import com.google.inject.Singleton; import dk.lockfuglsang.minecraft.file.FileUtil; -import dk.lockfuglsang.minecraft.yml.YmlConfiguration; import org.bukkit.Location; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.entity.Player; import org.bukkit.util.Vector; +import org.jetbrains.annotations.NotNull; import us.talabrek.ultimateskyblock.Settings; +import us.talabrek.ultimateskyblock.bootstrap.PluginDataDir; import us.talabrek.ultimateskyblock.uSkyBlock; import us.talabrek.ultimateskyblock.util.LocationUtil; +import us.talabrek.ultimateskyblock.util.Scheduler; +import us.talabrek.ultimateskyblock.world.WorldManager; import java.io.File; import java.io.IOException; +import java.nio.file.Path; +import java.time.Duration; +import java.time.Instant; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Logger; @@ -20,19 +30,35 @@ /** * Responsible for keeping track of and locating island locations for new islands. */ +@Singleton public class IslandLocatorLogic { - private static final Logger log = Logger.getLogger(IslandLocatorLogic.class.getName()); + private final Logger logger; + private final WorldManager worldManager; + private final Scheduler scheduler; + private final OrphanLogic orphanLogic; private final uSkyBlock plugin; private final File configFile; - private final YmlConfiguration config; - private final Map reservations = new ConcurrentHashMap<>(); + private final FileConfiguration config; + private final Map reservations = new ConcurrentHashMap<>(); private Location lastIsland = null; - private long reservationTimeout; + private final Duration reservationTimeout; - public IslandLocatorLogic(final uSkyBlock plugin) { + @Inject + public IslandLocatorLogic( + @NotNull uSkyBlock plugin, + @NotNull @PluginDataDir Path pluginDir, + @NotNull Logger logger, + @NotNull WorldManager worldManager, + @NotNull Scheduler scheduler, + @NotNull OrphanLogic orphanLogic + ) { this.plugin = plugin; - this.configFile = new File(plugin.getDataFolder(), "lastIslandConfig.yml"); - this.config = new YmlConfiguration(); + this.configFile = pluginDir.resolve("lastIslandConfig.yml").toFile(); + this.logger = logger; + this.worldManager = worldManager; + this.scheduler = scheduler; + this.orphanLogic = orphanLogic; + this.config = new YamlConfiguration(); FileUtil.readConfig(config, configFile); // Backward compatibility if (!config.contains("options.general.lastIslandX") && plugin.getConfig().contains("options.general.lastIslandX")) { @@ -41,14 +67,14 @@ public IslandLocatorLogic(final uSkyBlock plugin) { plugin.getConfig().set("options.general.lastIslandX", null); plugin.getConfig().set("options.general.lastIslandZ", null); } - reservationTimeout = plugin.getConfig().getLong("options.island.reservationTimeout", 5 * 60000); + reservationTimeout = Duration.ofMillis(plugin.getConfig().getLong("options.island.reservationTimeout", 5 * 60000)); } private Location getLastIsland() { if (lastIsland == null) { - lastIsland = new Location(plugin.getWorldManager().getWorld(), - config.getInt("options.general.lastIslandX", 0), Settings.island_height, - config.getInt("options.general.lastIslandZ", 0)); + lastIsland = new Location(worldManager.getWorld(), + config.getInt("options.general.lastIslandX", 0), Settings.island_height, + config.getInt("options.general.lastIslandZ", 0)); } return LocationUtil.alignToDistance(lastIsland, Settings.island_distance); } @@ -61,16 +87,13 @@ public synchronized Location getNextIslandLocation(Player player) { private void reserve(Location islandLocation) { final String islandName = LocationUtil.getIslandName(islandLocation); - final long tstamp = System.currentTimeMillis(); - reservations.put(islandName, tstamp); - plugin.async(new Runnable() { - @Override - public void run() { - synchronized (reservations) { - Long tReserved = reservations.get(islandName); - if (tReserved != null && tReserved == tstamp) { - reservations.remove(islandName); - } + final Instant timeStamp = Instant.now(); + reservations.put(islandName, timeStamp); + scheduler.async(() -> { + synchronized (reservations) { + Instant tReserved = reservations.get(islandName); + if (tReserved != null && tReserved == timeStamp) { + reservations.remove(islandName); } } }, reservationTimeout); @@ -78,7 +101,7 @@ public void run() { private synchronized Location getNext(Player player) { Location last = getLastIsland(); - if (plugin.getWorldManager().isSkyWorld(player.getWorld()) && !plugin.islandInSpawn(player.getLocation())) { + if (worldManager.isSkyWorld(player.getWorld()) && !plugin.islandInSpawn(player.getLocation())) { Location location = LocationUtil.alignToDistance(player.getLocation(), Settings.island_distance); if (isAvailableLocation(location)) { player.sendMessage(tr("\u00a79Creating an island at your location")); @@ -91,7 +114,7 @@ private synchronized Location getNext(Player player) { return location; } } - Location next = plugin.getOrphanLogic().getNextValidOrphan(); + Location next = orphanLogic.getNextValidOrphan(this); if (next == null) { next = last; // Ensure the found location is valid (or find one that is). @@ -106,16 +129,13 @@ private synchronized Location getNext(Player player) { private void save() { final Location locationToSave = lastIsland; - plugin.async(new Runnable() { - @Override - public void run() { - try { - config.set("options.general.lastIslandX", locationToSave.getBlockX()); - config.set("options.general.lastIslandZ", locationToSave.getBlockZ()); - config.save(configFile); - } catch (IOException e) { - log.warning("Unable to save " + configFile); - } + scheduler.async(() -> { + try { + config.set("options.general.lastIslandX", locationToSave.getBlockX()); + config.set("options.general.lastIslandZ", locationToSave.getBlockZ()); + config.save(configFile); + } catch (IOException e) { + logger.warning("Unable to save " + configFile); } }); } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/IslandLogic.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/IslandLogic.java index 096047dd3..2ccddf907 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/IslandLogic.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/IslandLogic.java @@ -4,8 +4,9 @@ import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; import com.google.common.cache.RemovalListener; -import com.google.common.cache.RemovalNotification; import com.google.gson.GsonBuilder; +import com.google.inject.Inject; +import com.google.inject.Singleton; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.CuboidRegion; import com.sk89q.worldguard.protection.regions.ProtectedRegion; @@ -17,21 +18,32 @@ import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import org.bukkit.scheduler.BukkitTask; +import org.jetbrains.annotations.NotNull; +import us.talabrek.ultimateskyblock.PluginConfig; import us.talabrek.ultimateskyblock.Settings; import us.talabrek.ultimateskyblock.api.IslandLevel; import us.talabrek.ultimateskyblock.api.IslandRank; import us.talabrek.ultimateskyblock.api.event.uSkyBlockEvent; +import us.talabrek.ultimateskyblock.bootstrap.PluginDataDir; import us.talabrek.ultimateskyblock.handler.WorldEditHandler; import us.talabrek.ultimateskyblock.handler.WorldGuardHandler; import us.talabrek.ultimateskyblock.handler.task.WorldEditClearFlatlandTask; import us.talabrek.ultimateskyblock.island.level.IslandScore; import us.talabrek.ultimateskyblock.player.PlayerInfo; +import us.talabrek.ultimateskyblock.player.TeleportLogic; import us.talabrek.ultimateskyblock.uSkyBlock; import us.talabrek.ultimateskyblock.util.IslandUtil; import us.talabrek.ultimateskyblock.util.LocationUtil; -import us.talabrek.ultimateskyblock.util.PlayerUtil; +import us.talabrek.ultimateskyblock.util.Scheduler; +import us.talabrek.ultimateskyblock.uuid.PlayerDB; +import us.talabrek.ultimateskyblock.world.WorldManager; import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.time.Duration; +import java.time.Instant; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -49,11 +61,17 @@ /** * Responsible for island creation, locating locations, purging, clearing etc. */ +@Singleton public class IslandLogic { - private static final Logger log = Logger.getLogger(IslandLogic.class.getName()); + private final Logger logger; private final uSkyBlock plugin; - private final File directoryIslands; + private final WorldManager worldManager; + private final TeleportLogic teleportLogic; + private final Scheduler scheduler; + private final PluginConfig config; + private final Path directoryIslands; private final OrphanLogic orphanLogic; + private final PlayerDB playerDB; private final LoadingCache cache; private final boolean showMembers; @@ -62,41 +80,56 @@ public class IslandLogic { private final BukkitTask saveTask; private final double topTenCutoff; - private volatile long lastGenerate = 0; + private volatile Instant lastUpdated = Instant.MIN; private final List ranks = new ArrayList<>(); - public IslandLogic(uSkyBlock plugin, File directoryIslands, OrphanLogic orphanLogic) { + @Inject + public IslandLogic( + @NotNull Logger logger, + @NotNull uSkyBlock plugin, + @NotNull WorldManager worldManager, + @NotNull TeleportLogic teleportLogic, + @NotNull Scheduler scheduler, + @NotNull PluginConfig config, + @NotNull @PluginDataDir Path dataPath, + @NotNull OrphanLogic orphanLogic, + @NotNull PlayerDB playerDB + ) { + this.logger = logger; this.plugin = plugin; - this.directoryIslands = directoryIslands; + this.worldManager = worldManager; + this.teleportLogic = teleportLogic; + this.scheduler = scheduler; + this.config = config; + this.playerDB = playerDB; + Path islandDirectory = dataPath.resolve("islands"); + try { + Files.createDirectories(islandDirectory); + } catch (IOException e) { + logger.severe("Unable to create island directory: " + islandDirectory); + } + this.directoryIslands = islandDirectory; this.orphanLogic = orphanLogic; - this.showMembers = plugin.getConfig().getBoolean("options.island.topTenShowMembers", true); - this.flatlandFix = plugin.getConfig().getBoolean("options.island.fixFlatland", false); - this.useDisplayNames = plugin.getConfig().getBoolean("options.advanced.useDisplayNames", false); - topTenCutoff = plugin.getConfig().getDouble("options.advanced.topTenCutoff", plugin.getConfig().getDouble("options.advanced.purgeLevel", 10)); + this.showMembers = config.getYamlConfig().getBoolean("options.island.topTenShowMembers", true); + this.flatlandFix = config.getYamlConfig().getBoolean("options.island.fixFlatland", false); + this.useDisplayNames = config.getYamlConfig().getBoolean("options.advanced.useDisplayNames", false); + topTenCutoff = config.getYamlConfig().getDouble("options.advanced.topTenCutoff", config.getYamlConfig().getDouble("options.advanced.purgeLevel", 10)); cache = CacheBuilder - .from(plugin.getConfig().getString("options.advanced.islandCache", - "maximumSize=200,expireAfterWrite=15m,expireAfterAccess=10m")) - .removalListener(new RemovalListener() { - @Override - public void onRemoval(RemovalNotification removal) { - log.fine("Removing island-info " + removal.getKey() + " from cache"); - removal.getValue().saveToFile(); - } - }) - .build(new CacheLoader() { - @Override - public IslandInfo load(String islandName) throws Exception { - log.fine("Loading island-info " + islandName + " to cache!"); - return new IslandInfo(islandName, plugin); - } - }); - long every = TimeUtil.secondsAsMillis(plugin.getConfig().getInt("options.advanced.island.saveEvery", 30)); - saveTask = plugin.async(new Runnable() { - @Override - public void run() { - saveDirtyToFiles(); - } - }, every, every); + .from(config.getYamlConfig().getString("options.advanced.islandCache", + "maximumSize=200,expireAfterWrite=15m,expireAfterAccess=10m")) + .removalListener((RemovalListener) removal -> { + logger.fine("Removing island-info " + removal.getKey() + " from cache"); + removal.getValue().saveToFile(); + }) + .build(new CacheLoader<>() { + @Override + public @NotNull IslandInfo load(@NotNull String islandName) { + logger.fine("Loading island-info " + islandName + " to cache!"); + return new IslandInfo(islandName, plugin, directoryIslands); + } + }); + Duration every = Duration.ofSeconds(config.getYamlConfig().getInt("options.advanced.island.saveEvery", 30)); + saveTask = scheduler.async(this::saveDirtyToFiles, every, every); } private void saveDirtyToFiles() { @@ -118,7 +151,7 @@ public synchronized IslandInfo getIslandInfo(String islandName) { throw new IllegalStateException("Unable to load island", e); } } - + public IslandInfo getIslandInfo(PlayerInfo playerInfo) { if (playerInfo != null && playerInfo.getHasIsland()) { return getIslandInfo(playerInfo.locationForParty()); @@ -127,89 +160,83 @@ public IslandInfo getIslandInfo(PlayerInfo playerInfo) { } public void clearIsland(final Location loc, final Runnable afterDeletion) { - log.log(Level.FINE, "clearing island at {0}", loc); - Runnable clearNether = new Runnable() { - @Override - public void run() { - Location netherIsland = getNetherLocation(loc); - ProtectedRegion netherRegion = WorldGuardHandler.getNetherRegionAt(netherIsland); - if (netherRegion != null) { - for (Player player : WorldGuardHandler.getPlayersInRegion(netherIsland.getWorld(), netherRegion)) { - if (player != null && player.isOnline() && plugin.getWorldManager().isSkyNether(player.getWorld()) && !player.isFlying()) { - player.sendMessage(tr("\u00a7cThe island owning this piece of nether is being deleted! Sending you to spawn.")); - plugin.getTeleportLogic().spawnTeleport(player, true); - } + logger.log(Level.FINE, "clearing island at {0}", loc); + Runnable clearNether = () -> { + Location netherIsland = getNetherLocation(loc); + ProtectedRegion netherRegion = WorldGuardHandler.getNetherRegionAt(netherIsland); + if (netherRegion != null) { + for (Player player : WorldGuardHandler.getPlayersInRegion(netherIsland.getWorld(), netherRegion)) { + if (player != null && player.isOnline() && worldManager.isSkyNether(player.getWorld()) && !player.isFlying()) { + player.sendMessage(tr("\u00a7cThe island owning this piece of nether is being deleted! Sending you to spawn.")); + teleportLogic.spawnTeleport(player, true); } - WorldEditHandler.clearIsland(netherIsland.getWorld(), netherRegion, afterDeletion); - } else { - afterDeletion.run(); } + WorldEditHandler.clearIsland(netherIsland.getWorld(), netherRegion, afterDeletion); + } else { + afterDeletion.run(); } }; - World skyBlockWorld = plugin.getWorldManager().getWorld(); + World skyBlockWorld = worldManager.getWorld(); ProtectedRegion region = WorldGuardHandler.getIslandRegionAt(loc); if (region != null) { - for (Player player : WorldGuardHandler.getPlayersInRegion(plugin.getWorldManager().getWorld(), region)) { - if (player != null && player.isOnline() && plugin.getWorldManager().isSkyWorld(player.getWorld()) && !player.isFlying()) { + for (Player player : WorldGuardHandler.getPlayersInRegion(worldManager.getWorld(), region)) { + if (player != null && player.isOnline() && worldManager.isSkyWorld(player.getWorld()) && !player.isFlying()) { player.sendMessage(tr("\u00a7cThe island you are on is being deleted! Sending you to spawn.")); - plugin.getTeleportLogic().spawnTeleport(player, true); + teleportLogic.spawnTeleport(player, true); } } WorldEditHandler.clearIsland(skyBlockWorld, region, clearNether); } else { - log.log(Level.WARNING, "Trying to delete an island - with no WG region! ({0})", LocationUtil.asString(loc)); + logger.log(Level.WARNING, "Trying to delete an island - with no WG region! ({0})", LocationUtil.asString(loc)); clearNether.run(); } } private Location getNetherLocation(Location loc) { Location netherIsland = loc.clone(); - netherIsland.setWorld(plugin.getWorldManager().getNetherWorld()); - netherIsland.setY(loc.getY()/2); + netherIsland.setWorld(worldManager.getNetherWorld()); + netherIsland.setY(loc.getY() / 2); return netherIsland; } - public boolean clearFlatland(final CommandSender sender, final Location loc, final int delay) { + public boolean clearFlatland(final CommandSender sender, final Location loc, Duration delay) { if (loc == null) { return false; } - if (delay > 0 && !flatlandFix) { + if (delay.isPositive() && !flatlandFix) { return false; // Skip } - Runnable runnable = new Runnable() { - @Override - public void run() { - final World w = loc.getWorld(); - final int px = loc.getBlockX(); - final int pz = loc.getBlockZ(); - final int py = 0; - final int range = Math.max(Settings.island_protectionRange, Settings.island_distance) + 1; - final int radius = range/2; - // 5 sampling points... - if (w.getBlockAt(px, py, pz).getType() == BEDROCK - || w.getBlockAt(px+radius, py, pz+radius).getType() == BEDROCK - || w.getBlockAt(px+radius, py, pz-radius).getType() == BEDROCK - || w.getBlockAt(px-radius, py, pz+radius).getType() == BEDROCK - || w.getBlockAt(px-radius, py, pz-radius).getType() == BEDROCK) - { - sender.sendMessage(String.format("\u00a7c-----------------------------------\n\u00a7cFlatland detected under your island!\n\u00a7e Clearing it in %s, stay clear.\n\u00a7c-----------------------------------\n", TimeUtil.ticksAsString(delay))); - new WorldEditClearFlatlandTask(plugin, sender, new CuboidRegion(BlockVector3.at(px-radius, 0, pz-radius), - BlockVector3.at(px+radius, 4, pz+radius)), - "\u00a7eFlatland was cleared under your island (%s). Take care.").runTaskLater(plugin, delay); - } + Runnable runnable = () -> { + final World w = loc.getWorld(); + final int px = loc.getBlockX(); + final int pz = loc.getBlockZ(); + final int py = 0; + final int range = Math.max(Settings.island_protectionRange, Settings.island_distance) + 1; + final int radius = range / 2; + // 5 sampling points... + if (w.getBlockAt(px, py, pz).getType() == BEDROCK + || w.getBlockAt(px + radius, py, pz + radius).getType() == BEDROCK + || w.getBlockAt(px + radius, py, pz - radius).getType() == BEDROCK + || w.getBlockAt(px - radius, py, pz + radius).getType() == BEDROCK + || w.getBlockAt(px - radius, py, pz - radius).getType() == BEDROCK) { + sender.sendMessage(String.format("\u00a7c-----------------------------------\n\u00a7cFlatland detected under your island!\n\u00a7e Clearing it in %s, stay clear.\n\u00a7c-----------------------------------\n", TimeUtil.durationAsString(delay))); + scheduler.sync(new WorldEditClearFlatlandTask(scheduler, config, worldManager, logger, sender, + new CuboidRegion(BlockVector3.at(px - radius, 0, pz - radius), + BlockVector3.at(px + radius, 4, pz + radius)), + "\u00a7eFlatland was cleared under your island (%s). Take care."), delay); } }; if (Bukkit.isPrimaryThread()) { runnable.run(); } else { - plugin.sync(runnable); + scheduler.sync(runnable); } return false; } public void displayTopTen(final CommandSender sender, int page) { synchronized (ranks) { - int maxpage = (( ranks.size()-1) / 10) + 1; + int maxpage = ((ranks.size() - 1) / 10) + 1; if (page > maxpage) { page = maxpage; } @@ -217,7 +244,7 @@ public void displayTopTen(final CommandSender sender, int page) { page = 1; } sender.sendMessage(tr("\u00a7eWALL OF FAME (page {0} of {1}):", page, maxpage)); - if (ranks == null || ranks.isEmpty()) { + if (ranks.isEmpty()) { if (Settings.island_useTopTen) { sender.sendMessage(tr("\u00a74Top ten list is empty! Only islands above level {0} is considered.", topTenCutoff)); } else { @@ -230,24 +257,23 @@ public void displayTopTen(final CommandSender sender, int page) { if (playerInfo != null && playerInfo.getHasIsland()) { rank = getRank(playerInfo.locationForParty()); } - int offset = (page-1) * 10; + int offset = (page - 1) * 10; place += offset; - for (final IslandLevel level : ranks.subList(offset, Math.min(ranks.size(), 10*page))) { + for (final IslandLevel level : ranks.subList(offset, Math.min(ranks.size(), 10 * page))) { String members = ""; if (showMembers && !level.getMembers().isEmpty()) { - members = Arrays.toString(level.getMembers().toArray(new String[level.getMembers().size()])); + members = Arrays.toString(level.getMembers().toArray(new String[0])); } String message = String.format(tr("\u00a7a#%2d \u00a77(%5.2f): \u00a7e%s \u00a77%s"), - place, level.getScore(), level.getLeaderName(), members); - if (sender instanceof Player) { - Player target = (Player) sender; + place, level.getScore(), level.getLeaderName(), members); + if (sender instanceof Player target) { String warpString = getJsonWarpString( - message, - tr("Click to warp to the island!"), - String.format("/is w %s", level.getLeaderName()) + message, + tr("Click to warp to the island!"), + String.format("/is w %s", level.getLeaderName()) ); uSkyBlock.getInstance().execCommand(target, "console:tellraw " + - target.getName() + " " + warpString, false); + target.getName() + " " + warpString, false); } else { sender.sendMessage(message); } @@ -280,10 +306,10 @@ private String getJsonWarpString(String text, String hoverText, String command) } public void showTopTen(final CommandSender sender, final int page) { - long t = System.currentTimeMillis(); - if (t > (lastGenerate + (Settings.island_topTenTimeout*60000)) || (sender.hasPermission("usb.admin.topten") || sender.isOp())) { - lastGenerate = t; - plugin.getServer().getScheduler().runTaskAsynchronously(plugin, () -> { + Instant now = Instant.now(); + if (now.isAfter(lastUpdated.plus(Settings.island_topTenTimeout)) || (sender.hasPermission("usb.admin.topten") || sender.isOp())) { + lastUpdated = now; + scheduler.async(() -> { generateTopTen(sender); displayTopTen(sender, page); }); @@ -298,13 +324,13 @@ public List getRanks(int offset, int length) { if (size <= offset) { return Collections.emptyList(); } - return ranks.subList(offset, Math.min(size-offset, length)); + return ranks.subList(offset, Math.min(size - offset, length)); } } public void generateTopTen(final CommandSender sender) { List topTen = new ArrayList<>(); - final String[] listOfFiles = directoryIslands.list(IslandUtil.createIslandFilenameFilter()); + final String[] listOfFiles = directoryIslands.toFile().list(IslandUtil.createIslandFilenameFilter()); for (String file : listOfFiles) { String islandName = FileUtil.getBasename(file); try { @@ -319,12 +345,12 @@ public void generateTopTen(final CommandSender sender) { cache.invalidate(islandName); } } catch (Exception e) { - plugin.getLogger().log(Level.WARNING, "Error during rank generation", e); + logger.log(Level.WARNING, "Error during rank generation", e); } } Collections.sort(topTen); synchronized (ranks) { - lastGenerate = System.currentTimeMillis(); + lastUpdated = Instant.now(); ranks.clear(); ranks.addAll(topTen); } @@ -338,15 +364,15 @@ private IslandLevel createIslandLevel(IslandInfo islandInfo, double level) { memberList.remove(partyLeader); List names = new ArrayList<>(); if (useDisplayNames) { - partyLeaderName = PlayerUtil.getPlayerDisplayName(partyLeader); - for (String name : memberList) { - String displayName = PlayerUtil.getPlayerDisplayName(name); - if (displayName != null) { - names.add(displayName); - } - } + partyLeaderName = playerDB.getDisplayName(partyLeader); + for (String name : memberList) { + String displayName = playerDB.getDisplayName(name); + if (displayName != null) { + names.add(displayName); + } + } } else { - names = memberList; + names = memberList; } return new IslandLevel(islandInfo.getName(), partyLeaderName, names, level); } @@ -360,7 +386,7 @@ public synchronized IslandInfo createIslandInfo(String location, String player) public synchronized void deleteIslandConfig(final String location) { try { IslandInfo islandInfo = cache.get(location); - updateRank(islandInfo, new IslandScore(0, Collections.EMPTY_LIST)); + updateRank(islandInfo, new IslandScore(0, Collections.emptyList())); if (islandInfo.exists()) { islandInfo.delete(); } @@ -385,17 +411,15 @@ public void updateRank(IslandInfo islandInfo, IslandScore score) { } public boolean hasIsland(Location loc) { - return loc == null || new File(directoryIslands, LocationUtil.getIslandName(loc) + ".yml").exists(); + return loc == null || new File(directoryIslands.toFile(), LocationUtil.getIslandName(loc) + ".yml").exists(); } public IslandRank getRank(String islandName) { - if (ranks != null) { - ArrayList rankList = new ArrayList<>(ranks); - for (int i = 0; i < rankList.size(); i++) { - IslandLevel level = rankList.get(i); - if (level.getIslandName().equalsIgnoreCase(islandName)) { - return new IslandRank(level, i+1); - } + ArrayList rankList = new ArrayList<>(ranks); + for (int i = 0; i < rankList.size(); i++) { + IslandLevel level = rankList.get(i); + if (level.getIslandName().equalsIgnoreCase(islandName)) { + return new IslandRank(level, i + 1); } } return null; @@ -430,7 +454,14 @@ public long flushCache() { } public int getSize() { - String[] list = directoryIslands.list(); - return list != null ? list.length : 0; + try (var stream = Files.list(directoryIslands)) { + return (int) stream.count(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public Path getIslandDirectory() { + return directoryIslands; } } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/LimitLogic.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/LimitLogic.java index 0ae7fbd56..0a959eb33 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/LimitLogic.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/LimitLogic.java @@ -1,22 +1,17 @@ package us.talabrek.ultimateskyblock.island; +import com.google.inject.Inject; +import com.google.inject.Singleton; import com.sk89q.worldguard.protection.regions.ProtectedRegion; import dk.lockfuglsang.minecraft.util.ItemStackUtil; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.World; -import org.bukkit.entity.Animals; -import org.bukkit.entity.EntityType; -import org.bukkit.entity.Ghast; -import org.bukkit.entity.Golem; -import org.bukkit.entity.LivingEntity; -import org.bukkit.entity.Monster; -import org.bukkit.entity.Slime; -import org.bukkit.entity.Villager; -import org.bukkit.entity.WaterMob; +import org.bukkit.entity.*; import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; import us.talabrek.ultimateskyblock.handler.WorldGuardHandler; -import us.talabrek.ultimateskyblock.uSkyBlock; +import us.talabrek.ultimateskyblock.world.WorldManager; import java.util.HashMap; import java.util.LinkedHashMap; @@ -26,8 +21,10 @@ import static dk.lockfuglsang.minecraft.po.I18nUtil.marktr; import static dk.lockfuglsang.minecraft.po.I18nUtil.tr; +@Singleton public class LimitLogic { - public enum CreatureType { UNKNOWN, ANIMAL, MONSTER, VILLAGER, GOLEM } + public enum CreatureType {UNKNOWN, ANIMAL, MONSTER, VILLAGER, GOLEM} + static { marktr("UNKNOWN"); marktr("ANIMAL"); @@ -36,10 +33,13 @@ public enum CreatureType { UNKNOWN, ANIMAL, MONSTER, VILLAGER, GOLEM } marktr("GOLEM"); } - private final uSkyBlock plugin; + private final WorldManager worldManager; + private final BlockLimitLogic blockLimitLogic; - public LimitLogic(uSkyBlock plugin) { - this.plugin = plugin; + @Inject + public LimitLogic(@NotNull WorldManager worldManager, @NotNull BlockLimitLogic blockLimitLogic) { + this.worldManager = worldManager; + this.blockLimitLogic = blockLimitLogic; } public Map getCreatureCount(us.talabrek.ultimateskyblock.api.IslandInfo islandInfo) { @@ -51,9 +51,9 @@ public Map getCreatureCount(us.talabrek.ultimateskyblock. ProtectedRegion islandRegionAt = WorldGuardHandler.getIslandRegionAt(islandLocation); if (islandRegionAt != null) { // Nether and Overworld regions are more or less equal (same x,z coords) - List creatures = WorldGuardHandler.getCreaturesInRegion(plugin.getWorldManager().getWorld(), - islandRegionAt); - World nether = plugin.getWorldManager().getNetherWorld(); + List creatures = WorldGuardHandler.getCreaturesInRegion(worldManager.getWorld(), + islandRegionAt); + World nether = worldManager.getNetherWorld(); if (nether != null) { creatures.addAll(WorldGuardHandler.getCreaturesInRegion(nether, islandRegionAt)); } @@ -78,15 +78,17 @@ public Map getCreatureMax(us.talabrek.ultimateskyblock.ap public CreatureType getCreatureType(LivingEntity creature) { if (creature instanceof Monster - || creature instanceof WaterMob - || creature instanceof Slime - || creature instanceof Ghast) { + || creature instanceof Slime + || creature instanceof Ghast + || creature instanceof Shulker) { return CreatureType.MONSTER; - } else if (creature instanceof Animals) { + } else if (creature instanceof Animals + || creature instanceof WaterMob) { return CreatureType.ANIMAL; } else if (creature instanceof Villager) { return CreatureType.VILLAGER; - } else if (creature instanceof Golem) { + } else if (creature instanceof IronGolem + || creature instanceof Snowman) { return CreatureType.GOLEM; } return CreatureType.UNKNOWN; @@ -94,10 +96,10 @@ public CreatureType getCreatureType(LivingEntity creature) { public CreatureType getCreatureType(EntityType entityType) { if (Monster.class.isAssignableFrom(entityType.getEntityClass()) - || WaterMob.class.isAssignableFrom(entityType.getEntityClass()) - || Slime.class.isAssignableFrom(entityType.getEntityClass()) - || Ghast.class.isAssignableFrom(entityType.getEntityClass()) - ) { + || WaterMob.class.isAssignableFrom(entityType.getEntityClass()) + || Slime.class.isAssignableFrom(entityType.getEntityClass()) + || Ghast.class.isAssignableFrom(entityType.getEntityClass()) + ) { return CreatureType.MONSTER; } else if (Animals.class.isAssignableFrom(entityType.getEntityClass())) { return CreatureType.ANIMAL; @@ -113,18 +115,19 @@ public boolean canSpawn(EntityType entityType, us.talabrek.ultimateskyblock.api. Map creatureCount = getCreatureCount(islandInfo); CreatureType creatureType = getCreatureType(entityType); int max = getMax(islandInfo, creatureType); - if (creatureCount.containsKey(creatureType) && creatureCount.get(creatureType) >= max) { - return false; - } - return true; + return !creatureCount.containsKey(creatureType) || creatureCount.get(creatureType) < max; } private int getMax(us.talabrek.ultimateskyblock.api.IslandInfo islandInfo, CreatureType creatureType) { switch (creatureType) { - case ANIMAL: return islandInfo.getMaxAnimals(); - case MONSTER: return islandInfo.getMaxMonsters(); - case VILLAGER: return islandInfo.getMaxVillagers(); - case GOLEM: return islandInfo.getMaxGolems(); + case ANIMAL: + return islandInfo.getMaxAnimals(); + case MONSTER: + return islandInfo.getMaxMonsters(); + case VILLAGER: + return islandInfo.getMaxVillagers(); + case GOLEM: + return islandInfo.getMaxGolems(); } return Integer.MAX_VALUE; } @@ -139,21 +142,25 @@ public String getSummary(us.talabrek.ultimateskyblock.api.IslandInfo islandInfo) } int cnt = count.containsKey(key) ? count.get(key) : 0; int max = creatureMax.get(key); - sb.append(tr("\u00a77{0}: \u00a7a{1}\u00a77 (max. {2})", tr(key.name()), cnt >= max ? tr("\u00a7c{0}",cnt) : cnt, max) + "\n"); + sb.append(tr("\u00a77{0}: \u00a7a{1}\u00a77 (max. {2})", tr(key.name()), cnt >= max ? tr("\u00a7c{0}", cnt) : cnt, max)).append("\n"); } - Map blockLimits = plugin.getBlockLimitLogic().getLimits(); - for (Map.Entry entry : blockLimits.entrySet()) { - int blockCount = plugin.getBlockLimitLogic().getCount(entry.getKey(), islandInfo.getIslandLocation()); + Map blockLimits = blockLimitLogic.getLimits(); + for (Map.Entry entry : blockLimits.entrySet()) { + int blockCount = blockLimitLogic.getCount(entry.getKey(), islandInfo.getIslandLocation()); + int val = entry.getValue(); + if (entry.getKey() == Material.HOPPER){ + val += islandInfo.getHopperLimit(); + } if (blockCount >= 0) { sb.append(tr("\u00a77{0}: \u00a7a{1}\u00a77 (max. {2})", - ItemStackUtil.getItemName(new ItemStack(entry.getKey())), - blockCount >= entry.getValue() ? tr("\u00a7c{0}", blockCount) : blockCount, - entry.getValue()) + "\n"); + ItemStackUtil.getItemName(new ItemStack(entry.getKey())), + blockCount >= val ? tr("\u00a7c{0}", blockCount) : blockCount, + val)).append("\n"); } else { sb.append(tr("\u00a77{0}: \u00a7a{1}\u00a77 (max. {2})", - ItemStackUtil.getItemName(new ItemStack(entry.getKey())), - tr("\u00a7c{0}", "?"), - entry.getValue()) + "\n"); + ItemStackUtil.getItemName(new ItemStack(entry.getKey())), + tr("\u00a7c{0}", "?"), + val)).append("\n"); } } return sb.toString().trim(); diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/OrphanLogic.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/OrphanLogic.java index 0f0ab283b..b9cb6a2e2 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/OrphanLogic.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/OrphanLogic.java @@ -1,37 +1,52 @@ package us.talabrek.ultimateskyblock.island; +import com.google.inject.Inject; +import com.google.inject.Singleton; import dk.lockfuglsang.minecraft.file.FileUtil; import org.bukkit.Location; import org.bukkit.World; import org.bukkit.configuration.file.FileConfiguration; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import us.talabrek.ultimateskyblock.Settings; -import us.talabrek.ultimateskyblock.uSkyBlock; +import us.talabrek.ultimateskyblock.bootstrap.PluginDataDir; +import us.talabrek.ultimateskyblock.world.WorldManager; import java.io.File; import java.io.IOException; +import java.nio.file.Path; import java.util.ArrayList; -import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.SortedSet; import java.util.TreeSet; +import java.util.logging.Level; +import java.util.logging.Logger; /** * Responsible for storing, accessing and handling orphans. */ +@Singleton public class OrphanLogic { // Used in a HACKY way to indicate the origin of an island location as an orphan. public static final float ORPHAN_PITCH = -30; public static final float ORPHAN_YAW = 90; - private final uSkyBlock plugin; + private final Logger logger; + private final WorldManager worldManager; private final FileConfiguration config; private final File configFile; - private SortedSet orphaned = new TreeSet<>(new OrphanComparator()); - - public OrphanLogic(uSkyBlock plugin) { - this.plugin = plugin; - configFile = new File(plugin.getDataFolder(), "orphans.yml"); + private final SortedSet orphaned = new TreeSet<>(new OrphanComparator()); + + @Inject + public OrphanLogic( + @NotNull @PluginDataDir Path pluginDir, + @NotNull Logger logger, + @NotNull WorldManager worldManager + ) { + this.logger = logger; + this.worldManager = worldManager; + configFile = pluginDir.resolve("orphans.yml").toFile(); config = FileUtil.getYmlConfiguration("orphans.yml"); readOrphans(); } @@ -65,7 +80,7 @@ public void save() { try { config.save(configFile); } catch (IOException e) { - plugin.getLogger().warning("Unable to store orphans: " + e); + logger.log(Level.SEVERE, "Unable to store orphans", e); } } @@ -80,18 +95,19 @@ public void addOrphan(Location location) { } } - public Location getNextValidOrphan() { + // This is a hacky way to break the dependency cycle between OrphanLogic and IslandLocatorLogic. Refactor. + public @Nullable Location getNextValidOrphan(IslandLocatorLogic islandLocatorLogic) { if (orphaned.isEmpty()) { return null; } try { - World world = plugin.getWorldManager().getWorld(); + World world = worldManager.getWorld(); for (Iterator it = orphaned.iterator(); it.hasNext(); ) { Orphan candidate = it.next(); if (candidate != null) { it.remove(); Location loc = new Location(world, candidate.getX(), Settings.island_height, candidate.getZ(), ORPHAN_YAW, ORPHAN_PITCH); - if (plugin.getIslandLocatorLogic().isAvailableLocation(loc)) { + if (islandLocatorLogic.isAvailableLocation(loc)) { return loc; } } @@ -112,7 +128,7 @@ public boolean wasOrphan(Location loc) { } public List getOrphans() { - return Collections.unmodifiableList(new ArrayList<>(orphaned)); + return List.copyOf(orphaned); } public static class Orphan { @@ -139,7 +155,7 @@ public int getZ() { } public int distanceSquared() { - return x*x + z * z; + return x * x + z * z; } @Override diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/TopTenComparator.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/TopTenComparator.java index 51ec9d549..609e35de8 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/TopTenComparator.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/TopTenComparator.java @@ -24,7 +24,7 @@ public int compare(String o1, String o2) { private int getValue(String key) { Double d = dataMap.get(key); if (d != null) { - return (int)(Math.round(d.doubleValue()*10)); + return (int)(Math.round(d * 10)); } return 0; } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/level/AutoIslandLevelRefresh.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/level/AutoIslandLevelRefresh.java new file mode 100644 index 000000000..ee2c22c88 --- /dev/null +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/level/AutoIslandLevelRefresh.java @@ -0,0 +1,48 @@ +package us.talabrek.ultimateskyblock.island.level; + +import com.google.inject.Inject; +import com.google.inject.Singleton; +import org.bukkit.scheduler.BukkitTask; +import org.jetbrains.annotations.NotNull; +import us.talabrek.ultimateskyblock.PluginConfig; +import us.talabrek.ultimateskyblock.island.task.RecalculateRunnable; +import us.talabrek.ultimateskyblock.uSkyBlock; +import us.talabrek.ultimateskyblock.util.Scheduler; + +import java.time.Duration; + +@Singleton +public class AutoIslandLevelRefresh { + + private final uSkyBlock plugin; + private final PluginConfig config; + private final Scheduler scheduler; + + private BukkitTask autoRecalculateTask = null; + + @Inject + public AutoIslandLevelRefresh( + @NotNull uSkyBlock plugin, + @NotNull PluginConfig config, + @NotNull Scheduler scheduler + ) { + this.plugin = plugin; + this.config = config; + this.scheduler = scheduler; + } + + public void startup() { + int refreshEveryMinute = config.getYamlConfig().getInt("options.island.autoRefreshScore", 0); + if (refreshEveryMinute > 0) { + Duration refreshRate = Duration.ofMinutes(refreshEveryMinute); + autoRecalculateTask = scheduler.sync(new RecalculateRunnable(plugin), refreshRate, refreshRate); + } + } + + public void shutdown() { + if (autoRecalculateTask != null) { + autoRecalculateTask.cancel(); + autoRecalculateTask = null; + } + } +} diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/level/AweLevelLogic.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/level/AweLevelLogic.java index cd7147d94..32c68ad77 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/level/AweLevelLogic.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/level/AweLevelLogic.java @@ -2,8 +2,10 @@ import org.bukkit.Location; import org.bukkit.configuration.file.FileConfiguration; +import us.talabrek.ultimateskyblock.PluginConfig; import us.talabrek.ultimateskyblock.api.async.Callback; import us.talabrek.ultimateskyblock.uSkyBlock; +import us.talabrek.ultimateskyblock.world.WorldManager; import java.util.HashSet; import java.util.Set; @@ -12,8 +14,8 @@ public class AweLevelLogic extends CommonLevelLogic { private Set running = new HashSet<>(); - public AweLevelLogic(uSkyBlock plugin, FileConfiguration config) { - super(plugin, config); + public AweLevelLogic(WorldManager worldManager, FileConfiguration config) { + super(config, worldManager); } @Override diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/level/BlockCountCollection.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/level/BlockCountCollection.java index d19f97184..10bc41350 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/level/BlockCountCollection.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/level/BlockCountCollection.java @@ -21,16 +21,16 @@ public BlockCountCollection(BlockLevelConfigMap configMap) { countMap = new ConcurrentHashMap<>(); } - public int add(Material type, byte dataValue, int blockCount) { - BlockLevelConfig blockLevelConfig = configMap.get(type, dataValue); + public int add(Material type, int blockCount) { + BlockLevelConfig blockLevelConfig = configMap.get(type); BlockMatch key = blockLevelConfig.getKey(); LongAdder count = countMap.computeIfAbsent(key, k -> new LongAdder()); count.add(blockCount); return count.intValue(); } - public int add(Material type, byte dataValue) { - return add(type, dataValue, 1); + public int add(Material type) { + return add(type, 1); } public List calculateScore(double pointsPerLevel) { diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/level/BlockKey.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/level/BlockKey.java index f9c6f96cd..70dc1fb83 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/level/BlockKey.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/level/BlockKey.java @@ -5,39 +5,32 @@ import java.util.Objects; public class BlockKey implements Comparable { - private Material type; - private byte dataValue; + private final Material type; - public BlockKey(Material type, byte dataValue) { + public BlockKey(Material type) { this.type = type; - this.dataValue = dataValue; } public Material getType() { return type; } - public byte getDataValue() { - return dataValue; - } - @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; BlockKey blockKey = (BlockKey) o; - return dataValue == blockKey.dataValue && - type == blockKey.type; + return type == blockKey.type; } @Override public int hashCode() { - return Objects.hash(type, dataValue); + return Objects.hash(type); } @Override public String toString() { - return type.name() + (dataValue != 0 ? "/" + dataValue : ""); + return type.name(); } @Override diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/level/BlockLevelConfig.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/level/BlockLevelConfig.java index f445ae997..99d262ab6 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/level/BlockLevelConfig.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/level/BlockLevelConfig.java @@ -40,12 +40,8 @@ public BlockLevelConfig(BlockMatch baseBlock, Set additionalBlocks, this.negativeReturns = negativeReturns; } - public boolean matches(Material material, byte dataValue) { - return baseBlock.matches(material, dataValue) || additionalBlocks.stream().anyMatch(b -> b.matches(material, dataValue)); - } - - public BlockScore calculateScore(int count) { - return calculateScore(count, 1); + public boolean matches(Material material) { + return baseBlock.matches(material) || additionalBlocks.stream().anyMatch(b -> b.matches(material)); } public BlockScore calculateScore(int count, double pointsPerLevel) { @@ -64,7 +60,7 @@ public BlockScore calculateScore(int count, double pointsPerLevel) { adjustedCount = dReturns(adjustedCount, diminishingReturns); } double blockScore = adjustedCount * scorePerBlock; - return new BlockScoreImpl(baseBlock.asItemStack(), count, blockScore/pointsPerLevel, state); + return new BlockScoreImpl(baseBlock.getType().createBlockData(), count, blockScore/pointsPerLevel, state); } private double dReturns(final double val, final double scale) { diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/level/BlockLevelConfigBuilder.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/level/BlockLevelConfigBuilder.java index 305a2b7b8..c22f2ee24 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/level/BlockLevelConfigBuilder.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/level/BlockLevelConfigBuilder.java @@ -23,11 +23,6 @@ public BlockLevelConfigBuilder base(Material baseBlock) { return this; } - public BlockLevelConfigBuilder base(Material baseBlock, byte dataValue) { - this.baseBlock = new BlockMatch(baseBlock, dataValue); - return this; - } - public BlockLevelConfigBuilder base(BlockMatch baseBlock) { this.baseBlock = baseBlock; return this; @@ -75,7 +70,6 @@ public BlockLevelConfig build() { // merge any additionalBlocks of the same type as baseBlock into the baseblock additionalBlocks.forEach(c -> { if (baseBlock.getType() == c.getType()) { - baseBlock.getDataValues().addAll(c.getDataValues()); } }); additionalBlocks = additionalBlocks.stream().filter(f -> f.getType() != baseBlock.getType()).collect(Collectors.toSet()); diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/level/BlockLevelConfigMap.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/level/BlockLevelConfigMap.java index f2d6937f9..515268f14 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/level/BlockLevelConfigMap.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/level/BlockLevelConfigMap.java @@ -25,7 +25,7 @@ public synchronized BlockLevelConfig get(BlockMatch blockMatch) { } private BlockKey asBlockKey(BlockMatch blockMatch) { - return new BlockKey(blockMatch.getType(), blockMatch.getDataValues().isEmpty() ? (byte) 0 : blockMatch.getDataValues().iterator().next()); + return new BlockKey(blockMatch.getType()); } public synchronized BlockLevelConfig get(BlockKey key) { @@ -35,7 +35,7 @@ public synchronized BlockLevelConfig get(BlockKey key) { if (existing != null) { return existing; } - BlockLevelConfig newConfig = defaultBuilder.copy().base(new BlockMatch(key.getType(), key.getDataValue())).build(); + BlockLevelConfig newConfig = defaultBuilder.copy().base(new BlockMatch(key.getType())).build(); searchSet.add(newConfig); searchMap.put(key.getType(), searchSet); return newConfig; @@ -43,7 +43,7 @@ public synchronized BlockLevelConfig get(BlockKey key) { private BlockLevelConfig search(Set searchSet, BlockKey key) { List match = searchSet.stream() - .filter(p -> p.matches(key.getType(), key.getDataValue())) + .filter(p -> p.matches(key.getType())) .distinct() .sorted((a,b) -> -a.getKey().compareTo(b.getKey())) // best match = longest string = desc ordering .collect(Collectors.toList()); @@ -57,16 +57,8 @@ public synchronized BlockLevelConfig get(Material type) { return get(createKey(type)); } - public synchronized BlockLevelConfig get(Material type, byte dataValue) { - return get(createKey(type, dataValue)); - } - - private BlockKey createKey(Material material, byte dataValue) { - return new BlockKey(material, dataValue); - } - private BlockKey createKey(Material material) { - return createKey(material, (byte)0); + return new BlockKey(material); } public BlockLevelConfig getDefault() { diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/level/BlockMatch.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/level/BlockMatch.java index 7145860be..0afff1bae 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/level/BlockMatch.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/level/BlockMatch.java @@ -1,56 +1,23 @@ package us.talabrek.ultimateskyblock.island.level; -import com.google.common.primitives.Bytes; import org.bukkit.Material; -import org.bukkit.inventory.ItemStack; - -import java.util.Collections; -import java.util.Set; -import java.util.TreeSet; /** * Holds the identification of a unit to be matched against a block */ public class BlockMatch implements Comparable { - private Material type; - private Set dataValues; + private final Material type; public BlockMatch(Material type) { - this(type, new byte[0]); - } - - public BlockMatch(Material type, byte dataValue) { - this(type, new byte[]{dataValue}); - } - - public BlockMatch(Material type, byte[] dataValues) { this.type = type; - this.dataValues = dataValues != null && dataValues.length > 0 ? new TreeSet<>(Bytes.asList(dataValues)) : Collections.emptySet(); } public Material getType() { return type; } - public Set getDataValues() { - return dataValues; - } - - public ItemStack asItemStack() { - if (dataValues.size() == 1) { - return new ItemStack(type, 1, dataValues.iterator().next()); - } - return new ItemStack(type, 1); - } - - public boolean matches(Material material, byte dataValue) { - if (this.type != material) { - return false; - } - if (!dataValues.isEmpty()) { - return dataValues.contains(dataValue); - } - return true; + public boolean matches(Material material) { + return this.type == material; } public void accept(BlockMatchVisitor visitor) { @@ -59,16 +26,7 @@ public void accept(BlockMatchVisitor visitor) { @Override public String toString() { - String dvString = ""; - if (dataValues.size() > 1) { - dvString = String.format("/%d-%d", dataValues.stream().min(Byte::compareTo).get(), dataValues.stream().max(Byte::compareTo).get()); - } else if (!dataValues.isEmpty()){ - dvString = String.format("/%d", dataValues.iterator().next()); - } - if (dvString.equals("/0-15")) { - dvString = ""; - } - return type.name() + dvString; + return type.name(); } @Override diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/level/BlockScoreComparator.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/level/BlockScoreComparator.java index 6abd61d8d..99ca9dd97 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/level/BlockScoreComparator.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/level/BlockScoreComparator.java @@ -1,7 +1,7 @@ package us.talabrek.ultimateskyblock.island.level; +import dk.lockfuglsang.minecraft.util.ItemStackUtil; import us.talabrek.ultimateskyblock.api.model.BlockScore; -import us.talabrek.ultimateskyblock.handler.VaultHandler; import java.util.Comparator; @@ -16,7 +16,7 @@ public int compare(BlockScore o1, BlockScore o2) { cmp = o2.getCount() - o1.getCount(); } if (cmp == 0) { - cmp = VaultHandler.getItemName(o2.getBlock()).compareTo(VaultHandler.getItemName(o1.getBlock())); + cmp = ItemStackUtil.getBlockName(o2.getBlockData()).compareTo(ItemStackUtil.getBlockName(o1.getBlockData())); } return cmp; } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/level/BlockScoreImpl.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/level/BlockScoreImpl.java index a6a16f501..fa594bb2c 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/level/BlockScoreImpl.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/level/BlockScoreImpl.java @@ -1,27 +1,26 @@ package us.talabrek.ultimateskyblock.island.level; -import org.bukkit.inventory.ItemStack; -import us.talabrek.ultimateskyblock.handler.VaultHandler; +import dk.lockfuglsang.minecraft.util.ItemStackUtil; +import org.bukkit.block.data.BlockData; public class BlockScoreImpl implements us.talabrek.ultimateskyblock.api.model.BlockScore { - - private final ItemStack block; + private final BlockData block; private final int count; private final double score; private final State state; private final String name; - public BlockScoreImpl(ItemStack block, int count, double score, State state) { + public BlockScoreImpl(BlockData block, int count, double score, State state) { this(block, count, score, state, null); } - public BlockScoreImpl(ItemStack block, int count, double score, State state, String name) { + public BlockScoreImpl(BlockData block, int count, double score, State state, String name) { this.block = block; this.count = count; this.score = score; this.state = state; - this.name = name != null ? name : VaultHandler.getItemName(getBlock()); + this.name = name != null ? name : ItemStackUtil.getBlockName(getBlockData()); } @Override @@ -35,9 +34,8 @@ public String toString() { '}'; } - @Override - public ItemStack getBlock() { + public BlockData getBlockData() { return block; } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/level/ChunkSnapshotLevelLogic.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/level/ChunkSnapshotLevelLogic.java index 7e4fedc10..02ac60b3d 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/level/ChunkSnapshotLevelLogic.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/level/ChunkSnapshotLevelLogic.java @@ -1,44 +1,70 @@ package us.talabrek.ultimateskyblock.island.level; +import com.google.inject.Inject; +import com.google.inject.Singleton; import com.sk89q.worldguard.protection.regions.ProtectedRegion; +import dk.lockfuglsang.minecraft.file.FileUtil; import org.bukkit.ChunkSnapshot; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.plugin.Plugin; import org.bukkit.scheduler.BukkitRunnable; +import org.jetbrains.annotations.NotNull; +import us.talabrek.ultimateskyblock.PluginConfig; import us.talabrek.ultimateskyblock.api.async.Callback; import us.talabrek.ultimateskyblock.handler.WorldGuardHandler; import us.talabrek.ultimateskyblock.island.task.ChunkSnapShotTask; import us.talabrek.ultimateskyblock.uSkyBlock; +import us.talabrek.ultimateskyblock.util.Scheduler; +import us.talabrek.ultimateskyblock.world.WorldManager; import java.util.List; import java.util.logging.Level; +import java.util.logging.Logger; /** * Business logic regarding the calculation of level */ +@Singleton public class ChunkSnapshotLevelLogic extends CommonLevelLogic { - public ChunkSnapshotLevelLogic(uSkyBlock plugin, FileConfiguration config) { - super(plugin, config); + private final Plugin plugin; + private final PluginConfig pluginConfig; + private final Scheduler scheduler; + private final Logger logger; + + @Inject + public ChunkSnapshotLevelLogic( + @NotNull uSkyBlock plugin, + @NotNull WorldManager worldManager, + @NotNull PluginConfig pluginConfig, + @NotNull Scheduler scheduler, + @NotNull Logger logger + ) { + super(FileUtil.getYmlConfiguration("levelConfig.yml"), worldManager); + this.plugin = plugin; + this.pluginConfig = pluginConfig; + this.scheduler = scheduler; + this.logger = logger; } @Override public void calculateScoreAsync(final Location l, final Callback callback) { // TODO: 10/05/2015 - R4zorax: Ensure no overlapping calls to this one happen... - log.entering(CN, "calculateScoreAsync"); + logger.entering(this.getClass().getName(), "calculateScoreAsync"); // is further threading needed here? final ProtectedRegion region = WorldGuardHandler.getIslandRegionAt(l); if (region == null) { return; } - new ChunkSnapShotTask(plugin, l, region, new Callback>() { + new ChunkSnapShotTask(scheduler, pluginConfig, l, region, new Callback<>() { @Override public void run() { final List snapshotsOverworld = getState(); Location netherLoc = getNetherLocation(l); final ProtectedRegion netherRegion = WorldGuardHandler.getNetherRegionAt(netherLoc); - new ChunkSnapShotTask(plugin, netherLoc, netherRegion, new Callback>() { + new ChunkSnapShotTask(scheduler, pluginConfig, netherLoc, netherRegion, new Callback<>() { @Override public void run() { final List snapshotsNether = getState(); @@ -57,33 +83,35 @@ public void run() { private void calculateScoreAndCallback(ProtectedRegion region, List snapshotsOverworld, ProtectedRegion netherRegion, List snapshotsNether, Callback callback) { IslandScore islandScore = calculateScore(region, snapshotsOverworld, netherRegion, snapshotsNether); callback.setState(islandScore); - plugin.sync(callback); - log.exiting(CN, "calculateScoreAsync"); + scheduler.sync(callback); + logger.exiting(this.getClass().getName(), "calculateScoreAsync"); } private IslandScore calculateScore(ProtectedRegion region, List snapshotsOverworld, ProtectedRegion netherRegion, List snapshotsNether) { final BlockCountCollection counts = new BlockCountCollection(scoreMap); int minX = region.getMinimumPoint().getBlockX(); int maxX = region.getMaximumPoint().getBlockX(); + int minY = region.getMinimumPoint().getBlockY(); + int maxY = region.getMaximumPoint().getBlockY(); int minZ = region.getMinimumPoint().getBlockZ(); int maxZ = region.getMaximumPoint().getBlockZ(); + for (int x = minX; x <= maxX; ++x) { for (int z = minZ; z <= maxZ; ++z) { ChunkSnapshot chunk = getChunkSnapshot(x >> 4, z >> 4, snapshotsOverworld); if (chunk == null) { // This should NOT happen! - log.log(Level.WARNING, "Missing chunk in snapshot for x,z = " + x + "," + z); + logger.log(Level.WARNING, "Missing chunk in snapshot for x,z = " + x + "," + z); continue; } int cx = (x & 0xf); int cz = (z & 0xf); - for (int y = 0; y <= 255; y++) { + for (int y = minY; y < maxY; y++) { Material blockType = chunk.getBlockType(cx, y, cz); if (blockType == Material.AIR) { continue; } - byte blockData = (byte) (chunk.getData(cx, y, cz) & 0xff); - counts.add(blockType, blockData); + counts.add(blockType); } } } @@ -99,7 +127,7 @@ private IslandScore calculateScore(ProtectedRegion region, List s ChunkSnapshot chunk = getChunkSnapshot(x >> 4, z >> 4, snapshotsNether); if (chunk == null) { // This should NOT happen! - log.log(Level.WARNING, "Missing nether-chunk in snapshot for x,z = " + x + "," + z); + logger.log(Level.WARNING, "Missing nether-chunk in snapshot for x,z = " + x + "," + z); continue; } int cx = (x & 0xf); @@ -109,8 +137,7 @@ private IslandScore calculateScore(ProtectedRegion region, List s if (blockType == Material.AIR) { continue; } - byte blockData = (byte) (chunk.getData(cx, y, cz) & 0xff); - counts.add(blockType, blockData); + counts.add(blockType); } } } @@ -127,5 +154,4 @@ private static ChunkSnapshot getChunkSnapshot(int x, int z, List } return null; } - } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/level/CommonLevelLogic.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/level/CommonLevelLogic.java index 66e0fff59..07e2bd3c7 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/level/CommonLevelLogic.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/level/CommonLevelLogic.java @@ -2,41 +2,39 @@ import org.bukkit.Location; import org.bukkit.configuration.file.FileConfiguration; +import us.talabrek.ultimateskyblock.PluginConfig; import us.talabrek.ultimateskyblock.Settings; import us.talabrek.ultimateskyblock.api.model.BlockScore; import us.talabrek.ultimateskyblock.island.level.yml.LevelConfigYmlReader; -import us.talabrek.ultimateskyblock.uSkyBlock; +import us.talabrek.ultimateskyblock.world.WorldManager; import java.util.List; -import java.util.logging.Logger; public abstract class CommonLevelLogic implements LevelLogic { - static final String CN = CommonLevelLogic.class.getName(); - protected static final Logger log = Logger.getLogger(CN); - protected final uSkyBlock plugin; - protected final FileConfiguration config; + FileConfiguration levelConfig; + private final WorldManager worldManager; BlockLevelConfigMap scoreMap; private final int pointsPerLevel; final int activateNetherAtLevel; - CommonLevelLogic(uSkyBlock plugin, FileConfiguration config) { - this.plugin = plugin; - this.config = config; - activateNetherAtLevel = config.getInt("nether.activate-at.level", 100); - pointsPerLevel = config.getInt("general.pointsPerLevel"); + CommonLevelLogic(FileConfiguration levelConfig, WorldManager worldManager) { + this.levelConfig = levelConfig; + activateNetherAtLevel = levelConfig.getInt("nether.activate-at.level", 100); + pointsPerLevel = levelConfig.getInt("general.pointsPerLevel"); + this.worldManager = worldManager; load(); } private void load() { - scoreMap = new LevelConfigYmlReader().readLevelConfig(config); + scoreMap = new LevelConfigYmlReader().readLevelConfig(levelConfig); } - Location getNetherLocation(Location l) { - Location netherLoc = l.clone(); - netherLoc.setWorld(plugin.getWorldManager().getNetherWorld()); - netherLoc.setY(Settings.nether_height); - return netherLoc; + Location getNetherLocation(Location location) { + Location netherLocation = location.clone(); + netherLocation.setWorld(worldManager.getNetherWorld()); + netherLocation.setY(Settings.nether_height); + return netherLocation; } IslandScore createIslandScore(BlockCountCollection blockCollection) { diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/level/IslandScore.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/level/IslandScore.java index 486aa5476..1afa19624 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/level/IslandScore.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/level/IslandScore.java @@ -3,7 +3,6 @@ import us.talabrek.ultimateskyblock.api.model.BlockScore; import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -42,12 +41,11 @@ private BlockScoreImpl add(BlockScore score, BlockScore existing) { if (score.getState().ordinal() > existing.getState().ordinal()) { state = existing.getState(); } - return new BlockScoreImpl(existing.getBlock(), + return new BlockScoreImpl(existing.getBlockData(), score.getCount() + existing.getCount(), score.getScore() + existing.getScore(), state, score.getName()); } - @Override public double getScore() { return score; @@ -71,7 +69,7 @@ public List getTop(int offset, int num) { throw new IllegalArgumentException("Offset must be a non-negative integer."); } if (!isSorted) { - Collections.sort(top, new BlockScoreComparator()); + top.sort(new BlockScoreComparator()); isSorted = true; } return top.subList(offset, Math.min(offset+num, top.size())); @@ -81,5 +79,4 @@ public List getTop(int offset, int num) { public int getSize() { return top.size(); } - } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/level/yml/LevelConfigYmlReader.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/level/yml/LevelConfigYmlReader.java index ef91afdec..0c7e34126 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/level/yml/LevelConfigYmlReader.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/level/yml/LevelConfigYmlReader.java @@ -14,11 +14,10 @@ import java.util.logging.Level; import java.util.regex.Matcher; import java.util.regex.Pattern; -import java.util.stream.Collectors; public class LevelConfigYmlReader { - private static final Pattern BLOCK_KEY_PATTERN = Pattern.compile("(?[A-Z0-9_]+)(/(?[0-9\\-]+))?"); - + private static final Pattern BLOCK_KEY_PATTERN = Pattern.compile("(?[A-Z0-9_]+)?"); + public BlockLevelConfigMap readLevelConfig(FileConfiguration config) { double defaultScore = config.getDouble("general.default", 10d); int defaultLimit = config.getInt("general.limit", Integer.MAX_VALUE); @@ -62,7 +61,7 @@ private BlockLevelConfig readBlockSection(ConfigurationSection section, BlockMat } List additionBlocks = section.getStringList("additionalBlocks"); if (!additionBlocks.isEmpty()) { - builder.additionalBlocks(additionBlocks.stream().map(s -> getBlockMatch(s)).collect(Collectors.toList()).toArray(new BlockMatch[0])); + builder.additionalBlocks(additionBlocks.stream().map(this::getBlockMatch).toArray(BlockMatch[]::new)); } return builder.build(); } @@ -70,7 +69,7 @@ private BlockLevelConfig readBlockSection(ConfigurationSection section, BlockMat private BlockMatch getBlockMatch(String blockKey) { Matcher m = BLOCK_KEY_PATTERN.matcher(blockKey); if (m.matches()) { - Material material = null; + Material material; String materialName = m.group("type"); material = Material.matchMaterial(materialName); if (material == null) { @@ -80,41 +79,15 @@ private BlockMatch getBlockMatch(String blockKey) { LogUtil.log(Level.WARNING, "Invalid key '" + blockKey + "' in levelConfig, could not lookup Material"); return null; } - byte[] dataValues = getDataValues(m.group("sub")); - return new BlockMatch(material, dataValues); + return new BlockMatch(material); } else { LogUtil.log(Level.WARNING, "Invalid key '" + blockKey + "' in levelConfig"); } return null; } - private byte[] getDataValues(String sub) { - if (sub == null) { - return new byte[0]; - } - if (sub.equalsIgnoreCase("*") || sub.equalsIgnoreCase("0-15")) { - return new byte[0]; - } else if (!sub.isEmpty()) { - String[] split = sub.split("-"); - if (split.length == 1) { - return new byte[]{(byte) (Integer.parseInt(split[0]) & 0x0f)}; - } else { - int min = Integer.parseInt(split[0]); - int max = Integer.parseInt(split[1]); - byte[] data = new byte[max - min + 1]; - for (int i = 0; i < data.length; i++) { - data[i] = (byte) ((min + i) & 0x0f); - } - return data; - } - } - return new byte[0]; - } - private void addDefaults(List blocks, BlockLevelConfigBuilder defaultBuilder) { BlockLevelConfigBuilder nullScore = defaultBuilder.copy().scorePerBlock(0).limit(0); blocks.add(nullScore.base(Material.AIR).build()); - blocks.add(nullScore.base(Material.LEGACY_AIR).build()); } - } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/level/yml/LevelConfigYmlWriter.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/level/yml/LevelConfigYmlWriter.java deleted file mode 100644 index 1b983a08b..000000000 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/level/yml/LevelConfigYmlWriter.java +++ /dev/null @@ -1,54 +0,0 @@ -package us.talabrek.ultimateskyblock.island.level.yml; - -import dk.lockfuglsang.minecraft.yml.YmlConfiguration; -import org.bukkit.configuration.Configuration; -import org.bukkit.configuration.ConfigurationSection; -import us.talabrek.ultimateskyblock.island.level.BlockLevelConfig; -import us.talabrek.ultimateskyblock.island.level.BlockLevelConfigMap; - -import java.util.Comparator; -import java.util.stream.Collectors; - -public class LevelConfigYmlWriter { - - public YmlConfiguration writeToConfig(YmlConfiguration config, BlockLevelConfigMap map) { - Configuration root = config.getRoot(); - ConfigurationSection blocks = root.createSection("blocks"); - BlockLevelConfig mapDefault = map.getDefault(); - map.values().stream() - .distinct() - .filter(f -> !isDefaultValues(f, mapDefault)) - .sorted(Comparator.comparing(BlockLevelConfig::getKey)) - .forEach(e -> writeToSection(blocks.createSection(createSectionKey(e)), e, mapDefault)); - return config; - } - - private String createSectionKey(BlockLevelConfig e) { - return e.getKey().toString(); - } - - private static boolean isDefaultValues(BlockLevelConfig c1, BlockLevelConfig c2) { - return c1.getLimit() == c2.getLimit() - && c1.getScorePerBlock() == c2.getScorePerBlock() - && c1.getDiminishingReturns() == c2.getDiminishingReturns() - && c1.getNegativeReturns() == c2.getNegativeReturns(); - } - - private void writeToSection(ConfigurationSection section, BlockLevelConfig config, BlockLevelConfig mapDefault) { - if (!config.getAdditionalBlocks().isEmpty()) { - section.set("additionalBlocks", config.getAdditionalBlocks().stream().distinct().map(m -> m.toString()).collect(Collectors.toList())); - } - if (config.getScorePerBlock() >= 0 && config.getScorePerBlock() != mapDefault.getScorePerBlock()) { - section.set("score", config.getScorePerBlock()); - } - if (config.getLimit() >= 0 && config.getLimit() != mapDefault.getLimit()) { - section.set("limit", config.getLimit()); - } - if (config.getDiminishingReturns() > 0 && config.getLimit() != mapDefault.getDiminishingReturns()) { - section.set("diminishingReturns", config.getDiminishingReturns()); - } - if (config.getNegativeReturns() > 0 && config.getLimit() != mapDefault.getNegativeReturns()) { - section.set("negativeReturns", config.getNegativeReturns()); - } - } -} diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/task/ChunkSnapShotTask.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/task/ChunkSnapShotTask.java index 529f86bf8..19777e26d 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/task/ChunkSnapShotTask.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/task/ChunkSnapShotTask.java @@ -6,10 +6,11 @@ import org.bukkit.Chunk; import org.bukkit.ChunkSnapshot; import org.bukkit.Location; +import us.talabrek.ultimateskyblock.PluginConfig; import us.talabrek.ultimateskyblock.api.async.Callback; import us.talabrek.ultimateskyblock.async.IncrementalRunnable; import us.talabrek.ultimateskyblock.handler.WorldEditHandler; -import us.talabrek.ultimateskyblock.uSkyBlock; +import us.talabrek.ultimateskyblock.util.Scheduler; import java.util.ArrayList; import java.util.List; @@ -20,10 +21,10 @@ public class ChunkSnapShotTask extends IncrementalRunnable { private final Location location; private final List chunks; - private List snapshots = new ArrayList<>(); + private final List snapshots = new ArrayList<>(); - public ChunkSnapShotTask(uSkyBlock plugin, Location location, ProtectedRegion region, final Callback> callback) { - super(plugin, callback); + public ChunkSnapShotTask(Scheduler scheduler, PluginConfig config, Location location, ProtectedRegion region, final Callback> callback) { + super(scheduler, config, callback); this.location = location; if (region != null) { chunks = new ArrayList<>(WorldEditHandler.getChunks(new CuboidRegion(region.getMinimumPoint(), region.getMaximumPoint()))); @@ -48,5 +49,4 @@ protected boolean execute() { } return chunks.isEmpty(); } - } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/task/CreateIslandTask.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/task/CreateIslandTask.java index da1632797..d737b50c5 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/task/CreateIslandTask.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/task/CreateIslandTask.java @@ -5,7 +5,8 @@ import org.bukkit.scheduler.BukkitRunnable; import us.talabrek.ultimateskyblock.player.PlayerPerk; import us.talabrek.ultimateskyblock.uSkyBlock; -import dk.lockfuglsang.minecraft.util.TimeUtil; + +import java.time.Duration; import static dk.lockfuglsang.minecraft.po.I18nUtil.tr; @@ -33,8 +34,7 @@ public void run() { player.sendMessage(tr("Unable to locate schematic {0}, contact a server-admin", cSchem)); } GenerateTask generateTask = new GenerateTask(plugin, player, playerPerk.getPlayerInfo(), next, playerPerk, cSchem); - final int heartBeatTicks = (int) TimeUtil.millisAsTicks(plugin.getConfig().getInt("asyncworldedit.watchDog.heartBeatMs", 2000)); final BukkitRunnable completionWatchDog = new LocateChestTask(plugin, player, next, generateTask); - completionWatchDog.runTaskTimer(plugin, 0, heartBeatTicks); + completionWatchDog.runTask(plugin); } } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/task/GenerateTask.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/task/GenerateTask.java index 0e10d8a38..99046fa67 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/task/GenerateTask.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/task/GenerateTask.java @@ -11,6 +11,9 @@ import us.talabrek.ultimateskyblock.player.PlayerInfo; import us.talabrek.ultimateskyblock.player.PlayerPerk; import us.talabrek.ultimateskyblock.uSkyBlock; +import us.talabrek.ultimateskyblock.util.Scheduler; + +import java.time.Duration; import static dk.lockfuglsang.minecraft.po.I18nUtil.tr; @@ -19,6 +22,7 @@ */ public class GenerateTask extends BukkitRunnable { private final uSkyBlock plugin; + private final Scheduler scheduler; private final Player player; private final PlayerInfo pi; private final Location next; @@ -29,6 +33,7 @@ public class GenerateTask extends BukkitRunnable { public GenerateTask(uSkyBlock plugin, final Player player, final PlayerInfo pi, final Location next, PlayerPerk playerPerk, String schematicName) { this.plugin = plugin; + this.scheduler = plugin.getScheduler(); this.player = player; this.pi = pi; this.next = next; @@ -59,33 +64,30 @@ public void run() { WorldGuardHandler.updateRegion(islandInfo); plugin.getCooldownHandler().resetCooldown(player, "restart", Settings.general_cooldownRestart); - plugin.sync(new Runnable() { - @Override - public void run() { - if (pi != null) { - pi.setIslandGenerating(false); - } - plugin.clearPlayerInventory(player); - if (player != null && player.isOnline()) { - if (plugin.getConfig().getBoolean("options.restart.teleportWhenReady", true)) { - player.sendMessage(tr("\u00a7aCongratulations! \u00a7eYour island has appeared.")); - if (AsyncWorldEditHandler.isAWE()) { - player.sendMessage(tr("\u00a7cNote:\u00a7e Construction might still be ongoing.")); - } - plugin.getTeleportLogic().homeTeleport(player, true); - } else { - player.sendMessage(new String[]{ - tr("\u00a7aCongratulations! \u00a7eYour island has appeared."), - tr("Use \u00a79/is h\u00a7r or the \u00a79/is\u00a7r menu to go there."), - tr("\u00a7cNote:\u00a7e Construction might still be ongoing.")}); - } - } - for (String command : plugin.getConfig().getStringList("options.restart.extra-commands")) { - plugin.execCommand(player, command, true); + scheduler.sync(() -> { + if (pi != null) { + pi.setIslandGenerating(false); + } + plugin.clearPlayerInventory(player); + if (player != null && player.isOnline()) { + if (plugin.getConfig().getBoolean("options.restart.teleportWhenReady", true)) { + player.sendMessage(tr("\u00a7aCongratulations! \u00a7eYour island has appeared.")); + if (AsyncWorldEditHandler.isAWE()) { + player.sendMessage(tr("\u00a7cNote:\u00a7e Construction might still be ongoing.")); } + plugin.getTeleportLogic().homeTeleport(player, true); + } else { + player.sendMessage( + tr("\u00a7aCongratulations! \u00a7eYour island has appeared."), + tr("Use \u00a79/is h\u00a7r or the \u00a79/is\u00a7r menu to go there."), + tr("\u00a7cNote:\u00a7e Construction might still be ongoing.") + ); } - }, plugin.getConfig().getInt("options.restart.teleportDelay", 2000) + } + for (String command : plugin.getConfig().getStringList("options.restart.extra-commands")) { + plugin.execCommand(player, command, true); + } + }, Duration.ofMillis(plugin.getConfig().getInt("options.restart.teleportDelay", 2000)) ); } } - diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/task/LocateChestTask.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/task/LocateChestTask.java index ea6fc1802..7b553fa70 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/task/LocateChestTask.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/task/LocateChestTask.java @@ -1,50 +1,54 @@ package us.talabrek.ultimateskyblock.island.task; import dk.lockfuglsang.minecraft.po.I18nUtil; +import dk.lockfuglsang.minecraft.util.TimeUtil; import org.bukkit.Location; import org.bukkit.entity.Player; import org.bukkit.scheduler.BukkitRunnable; import us.talabrek.ultimateskyblock.uSkyBlock; import us.talabrek.ultimateskyblock.util.LocationUtil; -import dk.lockfuglsang.minecraft.util.TimeUtil; +import us.talabrek.ultimateskyblock.util.Scheduler; + +import java.time.Duration; +import java.time.Instant; /** * A task that looks for a chest at an island location. */ public class LocateChestTask extends BukkitRunnable { - private final uSkyBlock plugin; private final Player player; private final Location islandLocation; private final GenerateTask onCompletion; - private final long timeout; + private final Scheduler scheduler; + private final Instant timeout; - private long tStart; + private Instant start; public LocateChestTask(uSkyBlock plugin, Player player, Location islandLocation, GenerateTask onCompletion) { - this.plugin = plugin; + this.scheduler = plugin.getScheduler(); this.player = player; this.islandLocation = islandLocation; this.onCompletion = onCompletion; - timeout = System.currentTimeMillis() + TimeUtil.stringAsMillis(plugin.getConfig().getString("asyncworldedit.watchDog.timeout", "5m")); + this.timeout = Instant.now().plus(TimeUtil.stringAsDuration(plugin.getConfig().getString("asyncworldedit.watchDog.timeout", "5m"))); } @Override public void run() { - long now = System.currentTimeMillis(); - if (tStart == 0) { - tStart = now; + if (start == null) { + start = Instant.now(); } Location chestLocation = LocationUtil.findChestLocation(islandLocation); - if (chestLocation == null && now < timeout) { + if (chestLocation == null && Instant.now().isBefore(timeout)) { // Just run again + // TODO: this is hacky, waiting for async generation to complete. Should ideally be launched once the generation has finished. } else { cancel(); if (chestLocation == null && player != null && player.isOnline()) { - player.sendMessage(I18nUtil.tr("\u00a7cWatchdog!\u00a79 Unable to locate a chest within {0}, bailing out.", TimeUtil.millisAsString(timeout-tStart))); + player.sendMessage(I18nUtil.tr("\u00a7cWatchdog!\u00a79 Unable to locate a chest within {0}, bailing out.", TimeUtil.durationAsString(Duration.between(start, timeout)))); } if (onCompletion != null) { onCompletion.setChestLocation(chestLocation); - plugin.sync(onCompletion); + scheduler.sync(onCompletion); } } } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/task/RecalculateRunnable.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/task/RecalculateRunnable.java index 7b2628e70..1b4ee07dc 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/task/RecalculateRunnable.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/task/RecalculateRunnable.java @@ -23,7 +23,7 @@ public void run() { } } if (!recalcIslands.isEmpty()) { - RecalculateTopTen runnable = new RecalculateTopTen(plugin, recalcIslands); + RecalculateTopTen runnable = new RecalculateTopTen(plugin, plugin.getScheduler(), recalcIslands); runnable.runTaskAsynchronously(plugin); } } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/task/RecalculateTopTen.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/task/RecalculateTopTen.java index 08eb208ea..3b19352d1 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/task/RecalculateTopTen.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/task/RecalculateTopTen.java @@ -1,38 +1,39 @@ package us.talabrek.ultimateskyblock.island.task; import org.bukkit.scheduler.BukkitRunnable; -import us.talabrek.ultimateskyblock.api.event.uSkyBlockEvent; import us.talabrek.ultimateskyblock.api.async.Callback; +import us.talabrek.ultimateskyblock.api.event.uSkyBlockEvent; import us.talabrek.ultimateskyblock.uSkyBlock; +import us.talabrek.ultimateskyblock.util.Scheduler; -import java.util.ArrayList; -import java.util.List; -import java.util.Set; +import java.util.Collection; +import java.util.Queue; +import java.util.concurrent.ConcurrentLinkedQueue; -/** - */ public class RecalculateTopTen extends BukkitRunnable { - private final List locations; + private final Queue locations; private final uSkyBlock plugin; + private final Scheduler scheduler; - public RecalculateTopTen(uSkyBlock plugin, Set locations) { + public RecalculateTopTen(uSkyBlock plugin, Scheduler scheduler, Collection locations) { + this.locations = new ConcurrentLinkedQueue<>(locations); this.plugin = plugin; - this.locations = new ArrayList<>(locations); + this.scheduler = scheduler; } @Override public void run() { - if (!locations.isEmpty()) { - String islandName = locations.remove(0); - plugin.calculateScoreAsync(null, islandName, new Callback() { + String islandName = locations.poll(); + if (islandName != null) { + plugin.calculateScoreAsync(null, islandName, new Callback<>() { @Override public void run() { // We use the deprecated on purpose (the other would fail). - plugin.async(RecalculateTopTen.this); + scheduler.async(RecalculateTopTen.this); } }); } else { - plugin.fireAsyncEvent(new uSkyBlockEvent(null, plugin.getAPI(), uSkyBlockEvent.Cause.RANK_UPDATED)); + plugin.fireAsyncEvent(new uSkyBlockEvent(null, uSkyBlock.getAPI(), uSkyBlockEvent.Cause.RANK_UPDATED)); } } } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/task/SetBiomeTask.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/task/SetBiomeTask.java index b7f36784c..a9b9739ae 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/task/SetBiomeTask.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/island/task/SetBiomeTask.java @@ -26,7 +26,7 @@ public class SetBiomeTask extends IncrementalRunnable { private final Set chunks; public SetBiomeTask(uSkyBlock plugin, Location loc, Biome biome, Runnable onCompletion) { - super(plugin, onCompletion); + super(plugin.getScheduler(), plugin.getPluginConfig(), onCompletion); this.biome = biome; ProtectedRegion region = WorldGuardHandler.getIslandRegionAt(loc); if (region != null) { @@ -41,7 +41,7 @@ public SetBiomeTask(uSkyBlock plugin, Location loc, Biome biome, Runnable onComp } public SetBiomeTask(uSkyBlock plugin, World world, BlockVector3 minP, BlockVector3 maxP, Biome biome, Runnable onCompletion) { - super(plugin, onCompletion); + super(plugin.getScheduler(), plugin.getPluginConfig(), onCompletion); this.biome = biome; this.minP = minP; this.maxP = maxP; @@ -77,7 +77,7 @@ protected boolean execute() { } for (int x = cx; x <= mx; x++) { for (int z = cz; z <= mz; z++) { - for (int y = 0; y < world.getMaxHeight(); y++) { + for (int y = world.getMinHeight(); y < world.getMaxHeight(); y++) { world.setBiome(x, y, z, biome); } } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/menu/AbstractConfigMenu.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/menu/AbstractConfigMenu.java deleted file mode 100644 index a5448cac6..000000000 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/menu/AbstractConfigMenu.java +++ /dev/null @@ -1,92 +0,0 @@ -package us.talabrek.ultimateskyblock.menu; - -import dk.lockfuglsang.minecraft.yml.YmlConfiguration; -import org.bukkit.Bukkit; -import org.bukkit.Material; -import org.bukkit.inventory.ItemStack; -import org.bukkit.inventory.meta.ItemMeta; - -import java.text.MessageFormat; -import java.text.ParseException; -import java.util.Arrays; -import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import static dk.lockfuglsang.minecraft.po.I18nUtil.tr; - -/** - * Created by R4zorax on 03/10/2015. - */ -public class AbstractConfigMenu { - private static final Pattern UUID_PATTERN = Pattern.compile("^.*Id:\\\"(?[^\\\"]+)\\\".*"); - private YmlConfiguration menuConfig; - - public AbstractConfigMenu(YmlConfiguration menuConfig) { - this.menuConfig = menuConfig; - } - - public YmlConfiguration getMenuConfig() { - return menuConfig; - } - - protected static void ensureCapacity(List menuList, int index) { - if (index >= menuList.size()) { - menuList.addAll(Arrays.asList(new ItemStack[Math.max(index - menuList.size(), 9)])); - } - } - - protected ItemStack createItem(Material icon, String title, List lore) { - return createItem(icon, (short) 0, title, lore); - } - - protected ItemStack createItem(Material icon, int subType, String title, List lore) { - ItemStack itemStack = new ItemStack(icon, 1, (short) (subType & 0xff)); - ItemMeta meta = itemStack.getItemMeta(); - meta.setDisplayName(title); - meta.setLore(lore); - itemStack.setItemMeta(meta); - return itemStack; - } - - protected static int getIndex(int row, int col) { - return row*9 + col; - } - - boolean isBlackListed(String file, String currentPath) { - return menuConfig.getStringList(file + ".blacklist").contains(currentPath); - } - - boolean isReadonly(String file, String path) { - return menuConfig.getStringList(file + ".readonly").contains(path); - } - - protected int getPage(String s) { - String format = tr("\u00a77Page {0}"); - try { - Object[] parsed = new MessageFormat(format).parse(s); - if (parsed != null && parsed.length > 0) { - return Integer.parseInt("" + parsed[0]); - } - } catch (ParseException e) { - // Ignore - } - return 1; - } - - protected ItemStack createItem(String item) { - if (item == null) { - return null; - } - Matcher m = UUID_PATTERN.matcher(item); - if (m.matches()) { - ItemStack itemStack = new ItemStack(Material.PLAYER_HEAD, 1); - Bukkit.getUnsafe().modifyItemStack(itemStack, item); - ItemMeta itemMeta = itemStack.getItemMeta(); - itemMeta.setDisplayName(tr(itemMeta.getDisplayName())); - itemStack.setItemMeta(itemMeta); - return itemStack; - } - return null; - } -} diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/menu/BiomeMenuItem.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/menu/BiomeMenuItem.java deleted file mode 100644 index 513a3c5ca..000000000 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/menu/BiomeMenuItem.java +++ /dev/null @@ -1,33 +0,0 @@ -package us.talabrek.ultimateskyblock.menu; - -import org.bukkit.inventory.ItemStack; - -public class BiomeMenuItem { - private final ItemStack icon; - private final String name; - private final String title; - private final String description; - - public BiomeMenuItem(ItemStack icon, String name, String title, String description) { - this.icon = icon; - this.name = name; - this.title = title; - this.description = description; - } - - public ItemStack getIcon() { - return icon.clone(); - } - - public String getId() { - return name; - } - - public String getTitle() { - return title; - } - - public String getDescription() { - return description; - } -} diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/menu/BooleanEditMenu.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/menu/BooleanEditMenu.java deleted file mode 100644 index c09fc5d20..000000000 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/menu/BooleanEditMenu.java +++ /dev/null @@ -1,33 +0,0 @@ -package us.talabrek.ultimateskyblock.menu; - -import dk.lockfuglsang.minecraft.file.FileUtil; -import dk.lockfuglsang.minecraft.yml.YmlConfiguration; -import org.bukkit.event.inventory.InventoryClickEvent; -import org.bukkit.inventory.Inventory; - -/** - * Simple menu-less editor for boolean values. - */ -public class BooleanEditMenu extends AbstractConfigMenu implements EditMenu { - public BooleanEditMenu(YmlConfiguration menuConfig) { - super(menuConfig); - } - - @Override - public boolean onClick(InventoryClickEvent e) { - // The boolean menu is never active - return false; - } - - @Override - public Inventory createEditMenu(String configName, String path, int page) { - YmlConfiguration config = FileUtil.getYmlConfiguration(configName); - if (config.isBoolean(path)) { - boolean value = config.getBoolean(path); - config.set(path, !value); - config.set("dirty", true); - } - // never returns an editor... - return null; - } -} diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/menu/ConfigMenu.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/menu/ConfigMenu.java deleted file mode 100644 index 55922bfe2..000000000 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/menu/ConfigMenu.java +++ /dev/null @@ -1,47 +0,0 @@ -package us.talabrek.ultimateskyblock.menu; - -import dk.lockfuglsang.minecraft.file.FileUtil; -import dk.lockfuglsang.minecraft.yml.YmlConfiguration; -import org.bukkit.entity.Player; -import org.bukkit.event.inventory.InventoryClickEvent; -import us.talabrek.ultimateskyblock.uSkyBlock; - -import java.util.ArrayList; -import java.util.List; - -/** - * A GUI for managing the uSkyBlock config-files - */ -public class ConfigMenu { - - private final YmlConfiguration menuConfig; - private final uSkyBlock plugin; - private final MenuItemFactory factory; - private final List editMenus = new ArrayList<>(); - private final MainConfigMenu mainMenu; - - public ConfigMenu(uSkyBlock plugin) { - this.plugin = plugin; - menuConfig = new YmlConfiguration(); - FileUtil.readConfig(menuConfig, getClass().getClassLoader().getResourceAsStream("configmenu.yml")); - factory = new MenuItemFactory(); - mainMenu = new MainConfigMenu(plugin, menuConfig, factory, editMenus); - editMenus.add(new IntegerEditMenu(menuConfig, factory, mainMenu)); - editMenus.add(new BooleanEditMenu(menuConfig)); - editMenus.add(new StringEditMenu(menuConfig, mainMenu)); - editMenus.add(mainMenu); // mainMenu goes last (catch all) - } - - public void showMenu(Player player, String configName, int page) { - player.openInventory(mainMenu.createEditMenu(configName, null, page)); - } - - public void onClick(InventoryClickEvent event) { - event.setCancelled(true); - for (EditMenu editMenu : editMenus) { - if (editMenu.onClick(event)) { - break; - } - } - } -} diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/menu/EditMenu.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/menu/EditMenu.java deleted file mode 100644 index 08b478f2c..000000000 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/menu/EditMenu.java +++ /dev/null @@ -1,25 +0,0 @@ -package us.talabrek.ultimateskyblock.menu; - -import org.bukkit.event.inventory.InventoryClickEvent; -import org.bukkit.inventory.Inventory; - -/** - * Common interface for menus. - */ -public interface EditMenu { - /** - * Returns true if the action belonged to the menu. - * @param e A click on an item, not sure if it belongs on this menu. - * @return true if no further processing is needed. - */ - boolean onClick(InventoryClickEvent e); - - /** - * Creates an edit menu for this type. - * @param configName Configuration name (filename) - * @param path Path to node - * @param page The page to show (for multi-page editors) - * @return An editor or null if this is not the right edit-menu. - */ - Inventory createEditMenu(String configName, String path, int page); -} diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/menu/IntegerEditMenu.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/menu/IntegerEditMenu.java deleted file mode 100644 index 89215f265..000000000 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/menu/IntegerEditMenu.java +++ /dev/null @@ -1,178 +0,0 @@ -package us.talabrek.ultimateskyblock.menu; - -import dk.lockfuglsang.minecraft.file.FileUtil; -import dk.lockfuglsang.minecraft.yml.YmlConfiguration; -import org.bukkit.Bukkit; -import org.bukkit.Material; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.entity.Player; -import org.bukkit.event.inventory.ClickType; -import org.bukkit.event.inventory.InventoryClickEvent; -import org.bukkit.event.inventory.InventoryType; -import org.bukkit.inventory.Inventory; -import org.bukkit.inventory.ItemStack; -import dk.lockfuglsang.minecraft.util.ItemStackUtil; - -import java.util.Arrays; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import us.talabrek.ultimateskyblock.player.UltimateHolder; -import us.talabrek.ultimateskyblock.player.UltimateHolder.MenuType; - -import static dk.lockfuglsang.minecraft.po.I18nUtil.tr; -import static dk.lockfuglsang.minecraft.util.FormatUtil.stripFormatting; - -/** - * Editor for integer nodes. - */ -public class IntegerEditMenu extends AbstractConfigMenu implements EditMenu { - private static final String DEFAULT_NUMBER_ICON = "STAINED_GLASS_PANE:11"; - private final YmlConfiguration menuConfig; - private final MenuItemFactory factory; - private final EditMenu parent; - - private final Map increments = new LinkedHashMap<>(); - - public IntegerEditMenu(YmlConfiguration menuConfig, MenuItemFactory factory, EditMenu parent) { - super(menuConfig); - this.menuConfig = menuConfig; - this.factory = factory; - this.parent = parent; - ConfigurationSection incSection = menuConfig.getConfigurationSection("integer-menu.increment"); - if (incSection != null) { - for (String inc : incSection.getKeys(false)) { - int incValue = Integer.parseInt(inc, 10); - increments.put(inc, ItemStackUtil.createItemStack(incSection.getString(inc, "IRON_INGOT"), - incValue < 0 ? tr("\u00a7c{0,number,#}", incValue) : tr("\u00a7a+{0,number,#}", incValue), - null)); - } - } - } - - @Override - public boolean onClick(InventoryClickEvent event) { - if (!(event.getInventory().getHolder() instanceof UltimateHolder) || - ((UltimateHolder) event.getInventory().getHolder()).getTitle() == null || - !stripFormatting(((UltimateHolder) event.getInventory().getHolder()).getTitle()).contains(stripFormatting(getTitle()))) { - return false; - } - if (event.getSlotType() != InventoryType.SlotType.CONTAINER) { - return true; - } - Player player = (Player) event.getWhoClicked(); - Inventory menu = event.getInventory(); - ItemStack returnItem = menu.getItem(getIndex(5, 0)); - String configName = returnItem.getItemMeta().getLore().get(0); - String path = returnItem.getItemMeta().getLore().get(1); - int page = getPage(returnItem.getItemMeta().getLore().get(2)); - int slot = event.getSlot(); - int row = slot / 9; - int col = slot % 9; - ItemStack clickedItem = event.getCurrentItem(); - if (slot >= getIndex(3, 0) && slot <= getIndex(3, 8)) { - // increment buttons - YmlConfiguration config = FileUtil.getYmlConfiguration(configName); - int value = config.getInt(path, 0); - int increment = getDisplayNameAsInt(clickedItem); - if (event.getClick() == ClickType.LEFT) { - value += increment; - } else if (event.getClick() == ClickType.RIGHT) { - value = increment; - } - config.set(path, value); - config.set("dirty", true); - } - if (slot != getIndex(5,0)) { - player.openInventory(createEditMenu(configName, path, page)); - } else { - player.openInventory(parent.createEditMenu(configName, path, page)); - } - return true; - } - - private int getDisplayNameAsInt(ItemStack clickedItem) { - int number = 0; - try { - number = Integer.parseInt(stripFormatting(clickedItem.getItemMeta().getDisplayName()).replaceAll("[^0-9\\-]+", ""), 10); - } catch (NumberFormatException ex) {} - return number; - } - - /** - * An editor for integers. - *
-     *     Config: Integer Editor
-     *     +---+---+---+---+---+---+---+---+---+
-     *  0  |   |   |   |   |   |   |   | G |   |
-     *     +---+---+---+---+---+---+---+---+---+
-     *  1  | R | R | R |   | I |   | G | G | G |
-     *     +---+---+---+---+---+---+---+---+---+
-     *  2  |   |   |   |   |   |   |   | G |   |
-     *     +---+---+---+---+---+---+---+---+---+
-     *  3  |-1k|100|-10|-1 | 0 |+1 |+10|100|+1k|
-     *     +---+---+---+---+---+---+---+---+---+
-     *  4  |   |   |   |   |   |   | ^ |   |   |
-     *     +---+---+---+---+---+---+---+---+---+
-     *  5  |SAV|   |   |   |   |   |   |   |   |
-     *     +---+---+---+---+---+---+---+---+---+
-     *       0   1   2   3   4   5   6   7   8
-     * 
- */ - @Override - public Inventory createEditMenu(String configName, String path, int page) { - YmlConfiguration config = FileUtil.getYmlConfiguration(configName); - if (!config.isInt(path)) { - return null; - } - int value = config.getInt(path, 0); - Inventory menu = Bukkit.createInventory(new UltimateHolder(null, getTitle(), MenuType.CONFIG), 6 * 9, getTitle()); - menu.setMaxStackSize(MenuItemFactory.MAX_INT_VALUE); - ItemStack frame = createItem(Material.BLACK_STAINED_GLASS_PANE, 0, null, null); - for (int i = 0; i < 27; i++) { - menu.setItem(i, frame); - } - int nvalue = Math.abs(value); - int col = 7; - do { - int tenValue = nvalue % 10; - String specificIcon = menuConfig.getString("integer-menu.number-items." + tenValue, null); - ItemStack numberItem = specificIcon != null ? ItemStackUtil.createItemStack(specificIcon) : ItemStackUtil.createItemStack(DEFAULT_NUMBER_ICON); - ItemStackUtil.Builder builder = ItemStackUtil.builder(numberItem); - if (specificIcon == null) { - builder.amount(tenValue); - } - builder.displayName(value < 0 ? tr("\u00a7c{0,number,#}", value) : tr("\u00a7a{0,number,#}", value)); - menu.setItem(getIndex(1, col), builder.build()); - nvalue = (nvalue - tenValue) / 10; - col--; - } while (nvalue != 0 && col > 0); - if (value < 0) { - menu.setItem(getIndex(1, col), createItem(Material.RED_CARPET, 0, factory.INT + value, null)); - } - ItemStack valueItem = factory.createIntegerItem(value, path, config, false); - List lore = valueItem.getItemMeta().getLore(); - menu.setItem(getIndex(3, 4), valueItem); - col = 0; - for (ItemStack inc : increments.values()) { - if (col == 4) { // Skip center - col++; - } - int incValue = getDisplayNameAsInt(inc); - ItemStack icon = ItemStackUtil.builder(inc) - .lore(tr("&aLeft:&7 Increment with {0}", inc.getItemMeta().getDisplayName())) - .lore(tr("&cRight-Click:&7 Set to {0}", incValue)) - .lore(lore) - .build(); - menu.setItem(getIndex(3, col), icon); - col++; - } - menu.setItem(getIndex(5, 0), createItem(Material.OAK_DOOR, "\u00a79" + tr("Return"), - Arrays.asList(configName, path, tr("\u00a77Page {0}", page)))); - return menu; - } - - private String getTitle() { - return tr("Config:") + " " + tr("\u00a79Integer Editor"); - } -} diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/menu/MainConfigMenu.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/menu/MainConfigMenu.java deleted file mode 100644 index 419397ef5..000000000 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/menu/MainConfigMenu.java +++ /dev/null @@ -1,333 +0,0 @@ -package us.talabrek.ultimateskyblock.menu; - -import dk.lockfuglsang.minecraft.file.FileUtil; -import dk.lockfuglsang.minecraft.yml.YmlConfiguration; -import org.bukkit.Bukkit; -import org.bukkit.Material; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.entity.Player; -import org.bukkit.event.inventory.InventoryClickEvent; -import org.bukkit.inventory.Inventory; -import org.bukkit.inventory.ItemStack; -import org.bukkit.inventory.meta.ItemMeta; -import us.talabrek.ultimateskyblock.player.UltimateHolder; -import us.talabrek.ultimateskyblock.player.UltimateHolder.MenuType; -import us.talabrek.ultimateskyblock.uSkyBlock; - -import java.io.File; -import java.io.IOException; -import java.text.MessageFormat; -import java.text.ParseException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import static dk.lockfuglsang.minecraft.po.I18nUtil.pre; -import static dk.lockfuglsang.minecraft.po.I18nUtil.tr; -import static us.talabrek.ultimateskyblock.menu.MenuItemFactory.READONLY; -import static dk.lockfuglsang.minecraft.util.FormatUtil.stripFormatting; -import static dk.lockfuglsang.minecraft.util.FormatUtil.wordWrap; -import static dk.lockfuglsang.minecraft.util.ItemStackUtil.builder; - -/** - * The primary config menu. - */ -public class MainConfigMenu extends AbstractConfigMenu implements EditMenu { - private final uSkyBlock plugin; - private final MenuItemFactory factory; - private final List editMenus; - - public MainConfigMenu(uSkyBlock plugin, YmlConfiguration menuConfig, MenuItemFactory factory, List editMenus) { - super(menuConfig); - this.plugin = plugin; - this.factory = factory; - this.editMenus = editMenus; - } - - @Override - public boolean onClick(InventoryClickEvent event) { - if (!(event.getInventory().getHolder() instanceof UltimateHolder)) - return false; - String title = stripFormatting(((UltimateHolder) event.getInventory().getHolder()).getTitle()); - final Player player = (Player) event.getWhoClicked(); - if (!title.contains(".yml")) { - return false; - } - ItemStack item = event.getCurrentItem(); - if (item == null) { - return true; - } - if(event.getInventory().equals(event.getView().getBottomInventory())) { - return true; //we clicked on the players inventory, lets not try to do anyting - } - if (event.getSlot() % 9 == 0 && (item.getType() == Material.BOOK || item.getType() == Material.WRITABLE_BOOK)) { - String configName = getConfigName(item); - int page = getConfigPage(item); - if (event.isShiftClick()) { - if (event.getSlot() == getIndex(1,0) && page > 10) { - page -= 10; - } else if (event.getSlot() == getIndex(3,0)) { - page += 10; - } - } - Inventory menu = createFileMenu(configName, page); - player.openInventory(menu); - } else if (event.getSlot() == getIndex(5, 0) && item.getType() == Material.ENDER_CHEST) { - ItemStack currentMenu = getCurrentMenu(event); - String configName = getConfigName(currentMenu); - int page = getConfigPage(currentMenu); - player.closeInventory(); - saveConfig(player, configName, page); - } else { - ItemStack currentMenu = getCurrentMenu(event); - String configName = getConfigName(currentMenu); - int page = getConfigPage(currentMenu); - String path = findPath(event.getInventory(), event.getSlot()); - if (path != null && !isReadonly(configName, path)) { - Inventory editor = null; - for (EditMenu editMenu : editMenus) { - if (editMenu != this) { - editor = editMenu.createEditMenu(configName, path, page); - } - if (editor != null) { - break; - } - } - if (editor == null) { - player.openInventory(createFileMenu(configName, page)); - } else { - player.openInventory(editor); - } - } - } - return true; - } - - private String findPath(Inventory inventory, int slot) { - ItemStack item = inventory.getItem(slot); - if (item == null) { - return null; - } - StringBuilder sb = new StringBuilder(); - sb.append(stripFormatting(item.getItemMeta().getDisplayName())); - int row = slot / 9; - int col = slot % 9; - while (col >= 1) { - ItemStack parent = inventory.getItem(getIndex(row, col)); - if(parent != null && parent.getType() != Material.PAPER) { - col--; - parent = inventory.getItem(getIndex(row, col)); - } - else if(parent == null || parent.getType() != Material.PAPER) { - row--; - parent = inventory.getItem(getIndex(row, col)); - } - else if (parent != null && parent.getType() == Material.PAPER) { - sb.insert(0, stripFormatting(parent.getItemMeta().getDisplayName()) + "."); - col--; - } - } - return sb.toString(); - } - - private void saveConfig(final Player player, final String configName, final int page) { - plugin.async(new Runnable() { - @Override - public void run() { - try { - YmlConfiguration config = FileUtil.getYmlConfiguration(configName); - config.set("dirty", null); - config.save(new File(plugin.getDataFolder(), configName)); - plugin.sync(new Runnable() { - @Override - public void run() { - plugin.reloadConfig(); - player.sendMessage(tr("\u00a7eConfiguration saved and reloaded.")); - player.openInventory(createEditMenu(configName, null, page)); - } - }); - } catch (IOException e) { - player.sendMessage(tr("\u00a7cError! \u00a79Unable to save config file!")); - } - } - }); - - } - - private Inventory createFileMenu(String filename, int page) { - YmlConfiguration config = FileUtil.getYmlConfiguration(filename); - int row = 0; - int col = 1; - ArrayList menuList = new ArrayList<>(54); - for (String key : config.getKeys(false)) { - if (config.isConfigurationSection(key)) { - row = addSection(menuList, config.getConfigurationSection(key), row, col, config, filename); - } - } - int maxPages = (int) Math.ceil(menuList.size() / 54d); - if (page < 1) { - page = 1; - } - if (page > maxPages) { - page = maxPages; - } - String title = tr("Config:") + " " + pre("{0} ({1}/{2})", filename, page, maxPages); - Inventory menu = Bukkit.createInventory(new UltimateHolder(null, title, MenuType.CONFIG), 6 * 9, title); - menu.setMaxStackSize(MenuItemFactory.MAX_INT_VALUE); - int startOffset = (page-1)*54; - // Add section markers on top line - for (int i = 1; i <= 8; i++) { - if (menuList.get(startOffset+i) != null) { - break; // Already an item here... we are done - } - int offset = 9; - while (menuList.get(startOffset+i) == null) { - // find on "higher (hidden) row" - if (menuList.get(startOffset+i-offset) != null) { - menuList.set(startOffset+i, menuList.get(startOffset+i-offset).clone()); - break; - } - offset += 9; - } - } - for (int i = startOffset; i < (page*54); i++) { - if (i >= menuList.size()) { - break; - } - ItemStack itemStack = menuList.get(i); - if (itemStack != null) { - menu.setItem(i-startOffset, itemStack); - } - } - menu.setItem(0, builder(new ItemStack(page == 1 ? Material.WRITABLE_BOOK : Material.BOOK, 1)) - .displayName(filename) - .lore(tr("\u00a77Page {0}", 1)) - .lore(tr("\u00a73First Page")) - .build()); - int offset = 2; - if (page > 3) { - offset = page-1; - } - if (offset > maxPages-3 && maxPages > 5) { - offset = maxPages-3; - } - for (int i = offset; maxPages > offset && i <= Math.min(offset+2, maxPages-1); i++) { - menu.setItem((1 + i - offset) * 9, builder(new ItemStack(page == i ? Material.WRITABLE_BOOK : Material.BOOK, i)) - .displayName(filename) - .lore(tr("\u00a77Page {0}", i)) - .build()); - } - menu.setItem(getIndex(4,0), builder(new ItemStack(page == maxPages ? Material.WRITABLE_BOOK : Material.BOOK, maxPages)) - .displayName(filename) - .lore(tr("\u00a77Page {0}", maxPages)) - .lore(tr("\u00a73Last Page")) - .build()); - ItemStack itemStack = builder(new ItemStack(Material.ENDER_CHEST, 1)) - .displayName(tr("\u00a7cSave & Reload config")) - .lore(Arrays.asList(tr("\u00a77Saves the settings to\n\u00a77file & reloads again.\n\u00a7cNote: \u00a77Use with care!").split("\n"))) - .select(config.getBoolean("dirty", false)) - .build(); - menu.setItem(getIndex(5, 0), itemStack); - return menu; - } - - private String getConfigName(ItemStack currentMenu) { - if (currentMenu != null && currentMenu.getItemMeta().getDisplayName() != null) { - return stripFormatting(currentMenu.getItemMeta().getDisplayName()); - } - return "config.yml"; - } - - private int getConfigPage(ItemStack currentMenu) { - if (currentMenu != null && currentMenu.getItemMeta() != null - && currentMenu.getItemMeta().getLore() != null - && !currentMenu.getItemMeta().getLore().isEmpty()) { - try { - Object[] parts = new MessageFormat(tr("\u00a77Page {0}")).parse(currentMenu.getItemMeta().getLore().get(0)); - if (parts != null && parts.length == 1) { - return Integer.parseInt("" + parts[0]); - } - } catch (ParseException e) { - // Ignore - } - } - return 1; - } - private ItemStack getCurrentMenu(InventoryClickEvent event) { - int index = 0; - ItemStack currentMenu = event.getInventory().getItem(index); - while (currentMenu != null && currentMenu.getType() != Material.WRITABLE_BOOK) { - index += 9; - currentMenu = event.getInventory().getItem(index); - } - return currentMenu; - } - - @Override - public Inventory createEditMenu(String configName, String path, int page) { - return createFileMenu(configName, page); - } - - private int addSection(ArrayList menuList, ConfigurationSection sec, int row, int col, YmlConfiguration config, String filename) { - if (isBlackListed(filename, sec.getCurrentPath())) { - return row; - } - ItemStack item = new ItemStack(Material.PAPER, 1); - ItemMeta meta = item.getItemMeta(); - meta.setDisplayName("\u00a77\u00a7o" + sec.getName()); - String comment = config.getComment(sec.getCurrentPath()); - if (comment != null) { - meta.setLore(wordWrap(comment.replaceAll("\n", " "), 20, 20)); - } - item.setItemMeta(meta); - int index = getIndex(row, col); - ensureCapacity(menuList, index); - menuList.set(index, item); - int colbase = ++col; - boolean lastWasSection = true; - for (String key : sec.getKeys(false)) { - index = getIndex(row, col); - ensureCapacity(menuList, index); - if (sec.isConfigurationSection(key)) { - if (!lastWasSection && col != colbase) { - row++; - col = colbase; - } - row = addSection(menuList, sec.getConfigurationSection(key), row, col, config, filename); - col = colbase; - lastWasSection = true; - } else { - String path = sec.getCurrentPath() + "." + key; - if (isBlackListed(filename, path)) { - continue; // Skip - } - boolean readonly = isReadonly(filename, path); - item = null; - if (sec.isBoolean(key)) { - item = factory.createBooleanItem(sec.getBoolean(key), path, config, readonly); - } else if (sec.isInt(key)) { - item = factory.createIntegerItem(sec.getInt(key), path, config, readonly); - } else { - item = factory.createStringItem(sec.getString(key, ""), path, config, readonly); - } - if (item != null) { - if (readonly) { - ItemMeta itemMeta = item.getItemMeta(); - List lore = itemMeta.getLore(); - lore.set(0, READONLY + lore.get(0) + tr("\u00a77 (readonly)")); - itemMeta.setLore(lore); - item.setItemMeta(itemMeta); - } - menuList.set(index, item); - col++; - lastWasSection = false; - } - } - if (col >= 9) { - row++; - col = colbase; - } - } - return col != colbase ? row+1 : row; - } -} diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/menu/MenuItemFactory.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/menu/MenuItemFactory.java deleted file mode 100644 index 2bd9ef489..000000000 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/menu/MenuItemFactory.java +++ /dev/null @@ -1,62 +0,0 @@ -package us.talabrek.ultimateskyblock.menu; - -import dk.lockfuglsang.minecraft.yml.YmlConfiguration; -import org.bukkit.Material; -import org.bukkit.inventory.ItemStack; -import org.bukkit.inventory.meta.ItemMeta; - -import java.util.ArrayList; -import java.util.List; - -import static dk.lockfuglsang.minecraft.po.I18nUtil.tr; -import static dk.lockfuglsang.minecraft.util.FormatUtil.wordWrap; - -/** - * A convenience factory for creating the itemstacks used in menus. - */ -public class MenuItemFactory { - public static final String INT = "\u00a7b"; - public static final String FALSE = "\u00a7c"; - public static final String TRUE = "\u00a7a"; - public static final String STRING = "\u00a79"; - public static final int MAX_INT_VALUE = 64; - public static final String READONLY = "\u00a77"; - - public ItemStack createStringItem(String value, String path, YmlConfiguration config, boolean readonly) { - return createLeafItem(new ItemStack(readonly ? Material.FLINT_AND_STEEL : Material.NAME_TAG, 1), value, path, config); - } - - public ItemStack createLeafItem(ItemStack item, String value, String path, YmlConfiguration config) { - ItemMeta meta = item.getItemMeta(); - meta.setDisplayName("\u00a77\u00a7o" + path.substring(path.lastIndexOf('.')+1)); - List lore = new ArrayList<>(); - lore.add(STRING + value); - String comment = config.getComment(path); - if (comment != null) { - lore.addAll(wordWrap(comment.replaceAll("\n", " "), 20, 20)); - } - meta.setLore(lore); - item.setItemMeta(meta); - return item; - } - - public ItemStack createIntegerItem(int value, String path, YmlConfiguration config, boolean readonly) { - ItemStack item = createIntegerIcon(value, readonly); - return createLeafItem( - item, - INT + value, - path, config); - } - - public ItemStack createIntegerIcon(int value, boolean readonly) { - return Math.abs(value) <= MAX_INT_VALUE - ? (readonly ? new ItemStack(Material.DETECTOR_RAIL, value) : new ItemStack(Material.RAIL, value)) - : (readonly ? new ItemStack(Material.IRON_BARS, 1, (short)1) : new ItemStack(Material.ACTIVATOR_RAIL, 1)); - } - - public ItemStack createBooleanItem(boolean value, String path, YmlConfiguration config, boolean readonly) { - ItemStack icon = new ItemStack(readonly ? (value ? Material.GREEN_WOOL : Material.RED_WOOL) : (value ? Material.LIME_WOOL : Material.PINK_WOOL), 1); - return createLeafItem(icon, value ? TRUE + tr("true") : FALSE + tr("false"), - path, config); - } -} diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/menu/SkyBlockMenu.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/menu/SkyBlockMenu.java index e66d870c9..de61a9089 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/menu/SkyBlockMenu.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/menu/SkyBlockMenu.java @@ -1,5 +1,8 @@ package us.talabrek.ultimateskyblock.menu; +import com.google.common.base.Preconditions; +import com.google.inject.Inject; +import com.google.inject.Singleton; import dk.lockfuglsang.minecraft.util.ItemStackUtil; import dk.lockfuglsang.minecraft.util.TimeUtil; import org.bukkit.Bukkit; @@ -11,21 +14,30 @@ import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.inventory.meta.SkullMeta; +import org.bukkit.profile.PlayerProfile; import org.bukkit.scheduler.BukkitTask; +import org.jetbrains.annotations.NotNull; +import us.talabrek.ultimateskyblock.PluginConfig; import us.talabrek.ultimateskyblock.challenge.ChallengeLogic; -import us.talabrek.ultimateskyblock.command.island.BiomeCommand; +import us.talabrek.ultimateskyblock.handler.ConfirmHandler; +import us.talabrek.ultimateskyblock.island.IslandGenerator; import us.talabrek.ultimateskyblock.island.IslandInfo; +import us.talabrek.ultimateskyblock.island.LimitLogic; import us.talabrek.ultimateskyblock.player.IslandPerk; +import us.talabrek.ultimateskyblock.player.PerkLogic; import us.talabrek.ultimateskyblock.player.PlayerInfo; import us.talabrek.ultimateskyblock.player.UltimateHolder; import us.talabrek.ultimateskyblock.player.UltimateHolder.MenuType; import us.talabrek.ultimateskyblock.uSkyBlock; -import us.talabrek.ultimateskyblock.util.PlayerUtil; +import us.talabrek.ultimateskyblock.util.GuiItemUtil; +import us.talabrek.ultimateskyblock.util.Scheduler; +import java.time.Duration; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Set; +import java.util.UUID; import java.util.logging.Level; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -33,6 +45,7 @@ import static dk.lockfuglsang.minecraft.po.I18nUtil.pre; import static dk.lockfuglsang.minecraft.po.I18nUtil.tr; import static dk.lockfuglsang.minecraft.util.FormatUtil.stripFormatting; +import static java.util.Objects.requireNonNull; import static us.talabrek.ultimateskyblock.challenge.ChallengeLogic.CHALLENGE_PAGESIZE; import static us.talabrek.ultimateskyblock.challenge.ChallengeLogic.COLS_PER_ROW; import static us.talabrek.ultimateskyblock.util.LogUtil.log; @@ -42,143 +55,105 @@ /** * The UI menu of uSkyBlock (using the inventory UI). */ +@Singleton public class SkyBlockMenu { - public static final Material SIGN_MATERIAL = Material.getMaterial("SIGN") == null - ? Material.getMaterial("OAK_SIGN") - : Material.getMaterial("SIGN"); - public static final Material WALL_SIGN_MATERIAL = Material.getMaterial("WALL_SIGN") == null - ? Material.getMaterial("OAK_WALL_SIGN") - : Material.getMaterial("WALL_SIGN"); private final Pattern PERM_VALUE_PATTERN = Pattern.compile("(\\[(?(?[!])?[^\\]]+)\\])?(?.*)"); private final Pattern CHALLENGE_PAGE_HEADER = Pattern.compile(tr("Challenge Menu") + ".*\\((?

[0-9]+)/(?[0-9]+)\\)"); - private uSkyBlock plugin; + + private final uSkyBlock plugin; private final ChallengeLogic challengeLogic; - private ItemStack sign = new ItemStack(SIGN_MATERIAL, 1); - private ItemStack biome = new ItemStack(Material.JUNGLE_SAPLING, 1); - private ItemStack lock = new ItemStack(Material.IRON_BARS, 1); - private ItemStack warpset = new ItemStack(Material.END_PORTAL_FRAME, 1); - private ItemStack warptoggle = new ItemStack(Material.LEVER, 1); - private ItemStack invite = new ItemStack(Material.CARROT_ON_A_STICK, 1); - private ItemStack kick = new ItemStack(Material.LEATHER_BOOTS, 1); - private List permissionMenuItems = Arrays.asList( - new PartyPermissionMenuItem(biome, "canChangeBiome", tr("Change Biome"), - tr("change the island''s biome.")), - new PartyPermissionMenuItem(lock, "canToggleLock", tr("Toggle Island Lock"), - tr("toggle the island''s lock.")), - new PartyPermissionMenuItem(warpset, "canChangeWarp", tr("Set Island Warp"), - tr("set the island''s warp."), - tr("set the island''s warp,\nwhich allows non-group\nmembers to teleport to\nthe island.")), - new PartyPermissionMenuItem(warptoggle, "canToggleWarp", tr("Toggle Island Warp"), - tr("toggle the island''s warp."), - tr("toggle the island''s warp,\nallowing them to turn it\non or off at anytime, but\nnot set the location.")), - new PartyPermissionMenuItem(invite, "canInviteOthers", tr("Invite Players"), - tr("invite others to the island."), - tr("invite\n" + - "other players to the island if\n" + - "there is enough room for more\n" + - "members")), - new PartyPermissionMenuItem(kick, "canKickOthers", tr("Kick Players"), - tr("kick others from the island."), - tr("kick\n" + - "other players from the island,\n" + - "but they are unable to kick\n" + - "the island leader.")) - ); - private List biomeMenus = Arrays.asList( - new BiomeMenuItem(new ItemStack(Material.TROPICAL_FISH, 1), - "ocean", tr("Ocean"), - tr("The ocean biome is the basic\nstarting biome for all islands.\npassive mobs like animals will\nnot spawn. Hostile mobs will\nspawn normally.") - ), - new BiomeMenuItem(new ItemStack(Material.SPRUCE_SAPLING, 1), - "forest", tr("Forest"), - tr("The forest biome will allow\nyour island to spawn passive.\nmobs like animals (including\nwolves). Hostile mobs will\nspawn normally.") - ), - new BiomeMenuItem(new ItemStack(Material.SAND, 1), - "desert", tr("Desert"), - tr("The desert biome makes it so\nthat there is no rain or snow\non your island. Passive mobs\nwon't spawn. Hostile mobs will\nspawn normally.") - ), - new BiomeMenuItem(new ItemStack(Material.JUNGLE_SAPLING, 1), - "jungle", tr("Jungle"), - tr("The jungle biome is bright\nand colorful. Passive mobs\n(including ocelots) will\nspawn. Hostile mobs will\nspawn normally.") - ), - new BiomeMenuItem(new ItemStack(Material.LILY_PAD, 1), - "swampland", tr("Swampland"), - tr("The swamp biome is dark\nand dull. Passive mobs\nwill spawn normally and\nslimes have a small chance\nto spawn at night depending\non the moon phase.") - ), - new BiomeMenuItem(new ItemStack(Material.SNOW, 1), - "taiga", tr("Taiga"), - tr("The taiga biome has snow\ninstead of rain. Passive\nmobs will spawn normally\n(including wolves) and\nhostile mobs will spawn.") - ), - new BiomeMenuItem(new ItemStack(Material.RED_MUSHROOM, 1), - "mushroom", tr("Mushroom"), - tr("The mushroom biome is\nbright and colorful.\nMooshrooms are the only\nmobs that will spawn.\nNo other passive or\nhostile mobs will spawn.") - ), - new BiomeMenuItem(new ItemStack(Material.NETHER_BRICK, 1), - "hell", tr("Hell"), - tr("The hell biome looks\ndark and dead. Some\nmobs from the nether will\nspawn in this biome\n(excluding ghasts and\nblazes).") - ), - new BiomeMenuItem(new ItemStack(Material.ENDER_EYE, 1), - "sky", tr("Sky"), - tr("The sky biome gives your\nisland a special dark sky.\nOnly endermen will spawn\nin this biome.") - ), - new BiomeMenuItem(new ItemStack(Material.TALL_GRASS, 1), - "plains", tr("Plains"), - tr("The plains biome has rain\ninstead of snow. Passive\nmobs will spawn normally\n(including horses) and\nhostile mobs will spawn.") - ), - new BiomeMenuItem(new ItemStack(Material.EMERALD_ORE, 1), - "extreme_hills", tr("Extreme Hills"), - tr("The extreme hills biome.\nPassive mobs will spawn \nnormally and hostile\nmobs will spawn.") - ), - new BiomeMenuItem(new ItemStack(Material.ROSE_BUSH, 1), - "flower_forest", tr("Flower Forest"), - tr("The flower forest biome.\nPassive mobs will spawn \nnormally and hostile\nmobs will spawn.") - ), - new BiomeMenuItem(new ItemStack(Material.PRISMARINE_SHARD, 1), - "deep_ocean", tr("Deep Ocean"), - tr("The deep-ocean biome is an advanced\n" + - "biome. Passive mobs like animals will\n" + - "not spawn. Hostile mobs \n"+ - "(including Guardians) will\n" + - "spawn normally.") - ), - new BiomeMenuItem(new ItemStack(Material.PACKED_ICE, 1), - "ice_plains", tr("Ice Plains"), - tr("The ice-plains biome is an advanced biome.\nMobs will spawn naturally.\nincluding polar-bears") - ) + private final PluginConfig config; + private final PerkLogic perkLogic; + private final IslandGenerator islandGenerator; + private final LimitLogic limitLogic; + private final ConfirmHandler confirmHandler; + private final Scheduler scheduler; + + private final ItemStack sign = new ItemStack(Material.OAK_SIGN, 1); + private final ItemStack biome = new ItemStack(Material.JUNGLE_SAPLING, 1); + private final ItemStack lock = new ItemStack(Material.IRON_BARS, 1); + private final ItemStack warpset = new ItemStack(Material.END_PORTAL_FRAME, 1); + private final ItemStack warptoggle = new ItemStack(Material.LEVER, 1); + private final ItemStack invite = new ItemStack(Material.CARROT_ON_A_STICK, 1); + private final ItemStack kick = new ItemStack(Material.LEATHER_BOOTS, 1); + private final List permissionMenuItems = Arrays.asList( + new PartyPermissionMenuItem(biome, "canChangeBiome", tr("Change Biome"), + tr("change the island''s biome.")), + new PartyPermissionMenuItem(lock, "canToggleLock", tr("Toggle Island Lock"), + tr("toggle the island''s lock.")), + new PartyPermissionMenuItem(warpset, "canChangeWarp", tr("Set Island Warp"), + tr("set the island''s warp."), + tr("set the island''s warp,\nwhich allows non-group\nmembers to teleport to\nthe island.")), + new PartyPermissionMenuItem(warptoggle, "canToggleWarp", tr("Toggle Island Warp"), + tr("toggle the island''s warp."), + tr("toggle the island''s warp,\nallowing them to turn it\non or off at anytime, but\nnot set the location.")), + new PartyPermissionMenuItem(invite, "canInviteOthers", tr("Invite Players"), + tr("invite others to the island."), + tr(""" + invite + other players to the island if + there is enough room for more + members""")), + new PartyPermissionMenuItem(kick, "canKickOthers", tr("Kick Players"), + tr("kick others from the island."), + tr(""" + kick + other players from the island, + but they are unable to kick + the island leader.""")) ); - public SkyBlockMenu(uSkyBlock plugin, ChallengeLogic challengeLogic) { + @Inject + public SkyBlockMenu( + @NotNull uSkyBlock plugin, + @NotNull ChallengeLogic challengeLogic, + @NotNull PluginConfig config, + @NotNull PerkLogic perkLogic, + @NotNull IslandGenerator islandGenerator, + @NotNull LimitLogic limitLogic, + @NotNull ConfirmHandler confirmHandler, + @NotNull Scheduler scheduler + ) { this.plugin = plugin; this.challengeLogic = challengeLogic; + this.config = config; + this.perkLogic = perkLogic; + this.islandGenerator = islandGenerator; + this.limitLogic = limitLogic; + this.confirmHandler = confirmHandler; + this.scheduler = scheduler; } - public Inventory displayPartyPlayerGUI(final Player player, final String pname) { + public Inventory displayPartyPlayerGUI(final Player inventoryViewer, final PlayerProfile partyMember) { + Preconditions.checkNotNull(partyMember.getName(), "Player name must not be null"); + Preconditions.checkNotNull(partyMember.getUniqueId(), "Player UUID must not be null"); List lores = new ArrayList<>(); String emptyTitle = tr("{0} <{1}>", "", tr("Permissions")); - String title = tr("{0} <{1}>", pname.substring(0, Math.min(32-emptyTitle.length(), pname.length())), tr("Permissions")); - Inventory menu = Bukkit.createInventory(new UltimateHolder(player, title, MenuType.DEFAULT), 9, title); + String name = partyMember.getName(); + String title = tr("{0} <{1}>", name.substring(0, Math.min(32 - emptyTitle.length(), name.length())), tr("Permissions")); + Inventory menu = Bukkit.createInventory(new UltimateHolder(inventoryViewer, title, MenuType.DEFAULT), 9, title); final ItemStack pHead = new ItemStack(Material.PLAYER_HEAD, 1); - final SkullMeta meta3 = (SkullMeta) pHead.getItemMeta(); - ItemMeta meta2 = sign.getItemMeta(); + final SkullMeta meta3 = requireNonNull((SkullMeta) requireNonNull(pHead.getItemMeta())); + ItemMeta meta2 = requireNonNull(requireNonNull(sign.getItemMeta())); meta2.setDisplayName(tr("\u00a79Player Permissions")); addLore(lores, tr("\u00a7eClick here to return to\n\u00a7eyour island group''s info.")); meta2.setLore(lores); sign.setItemMeta(meta2); menu.addItem(sign); lores.clear(); - meta3.setOwner(pname); - meta3.setDisplayName(tr("\u00a7e{0}''\u00a79s Permissions", pname)); + meta3.setOwnerProfile(partyMember); + meta3.setDisplayName(tr("\u00a7e{0}''\u00a79s Permissions", name)); addLore(lores, tr("\u00a7eHover over an icon to view\n\u00a7ea permission. Change the\n\u00a7epermission by clicking it.")); meta3.setLore(lores); pHead.setItemMeta(meta3); menu.addItem(pHead); lores.clear(); - IslandInfo islandInfo = plugin.getIslandInfo(player); - boolean isLeader = islandInfo.isLeader(player); + IslandInfo islandInfo = plugin.getIslandInfo(inventoryViewer); + boolean isLeader = islandInfo.isLeader(inventoryViewer); for (PartyPermissionMenuItem menuItem : permissionMenuItems) { ItemStack itemStack = menuItem.getIcon(); - meta2 = itemStack.getItemMeta(); - if (islandInfo.hasPerm(pname, menuItem.getPerm())) { + meta2 = requireNonNull(requireNonNull(itemStack.getItemMeta())); + if (islandInfo.hasPerm(partyMember.getUniqueId(), menuItem.getPerm())) { meta2.setDisplayName("\u00a7a" + menuItem.getTitle()); lores.add(tr("\u00a7fThis player \u00a7acan")); addLore(lores, "\u00a7f", menuItem.getDescription()); @@ -206,6 +181,7 @@ private void addLore(List lores, String format, String multiLine) { lores.add(format + line); } } + private void addLore(List lores, String multiLine) { addLore(lores, "", multiLine); } @@ -215,8 +191,8 @@ public Inventory displayPartyGUI(final Player player) { String title = "\u00a79" + tr("Island Group Members"); Inventory menu = Bukkit.createInventory(new UltimateHolder(player, title, MenuType.DEFAULT), 18, title); IslandInfo islandInfo = plugin.getIslandInfo(player); - final Set memberList = islandInfo.getMembers(); - final ItemMeta meta2 = sign.getItemMeta(); + final Set memberList = islandInfo.getMemberUUIDs(); + final ItemMeta meta2 = requireNonNull(requireNonNull(sign.getItemMeta())); meta2.setDisplayName("\u00a7a" + tr("Island Group Members")); lores.add(tr("Group Members: \u00a72{0}\u00a77/\u00a7e{1}", islandInfo.getPartySize(), islandInfo.getMaxPartySize())); if (islandInfo.getPartySize() < islandInfo.getMaxPartySize()) { @@ -229,19 +205,19 @@ public Inventory displayPartyGUI(final Player player) { sign.setItemMeta(meta2); menu.addItem(sign.clone()); lores.clear(); - for (String temp : memberList) { + for (UUID memberId : memberList) { ItemStack headItem = new ItemStack(Material.PLAYER_HEAD, 1); - SkullMeta meta3 = (SkullMeta) headItem.getItemMeta(); - meta3.setDisplayName(tr("\u00a7e{0}''s\u00a79 Permissions", temp)); - meta3.setOwner(temp); - boolean isLeader = islandInfo.isLeader(temp); + SkullMeta meta3 = requireNonNull((SkullMeta) requireNonNull(headItem.getItemMeta())); + meta3.setDisplayName(tr("\u00a7e{0}''s\u00a79 Permissions", memberId)); + meta3.setOwnerProfile(Bukkit.createPlayerProfile(memberId)); + boolean isLeader = islandInfo.isLeader(memberId); if (isLeader) { addLore(lores, "\u00a7a\u00a7l", tr("Leader")); } else { addLore(lores, "\u00a7e\u00a7l", tr("Member")); } for (PartyPermissionMenuItem perm : permissionMenuItems) { - if (isLeader || islandInfo.hasPerm(temp, perm.getPerm())) { + if (isLeader || islandInfo.hasPerm(memberId, perm.getPerm())) { lores.add("\u00a7a" + tr("Can {0}", "\u00a7f" + perm.getShortDescription())); } else { lores.add("\u00a7c" + tr("Cannot {0}", "\u00a7f" + perm.getShortDescription())); @@ -262,19 +238,17 @@ public Inventory displayLogGUI(final Player player) { List lores = new ArrayList<>(); String title = "\u00a79" + tr("Island Log"); Inventory menu = Bukkit.createInventory(new UltimateHolder(player, title, MenuType.DEFAULT), 9, title); - ItemMeta meta4 = sign.getItemMeta(); + ItemMeta meta4 = requireNonNull(requireNonNull(sign.getItemMeta())); meta4.setDisplayName("\u00a79\u00a7l" + tr("Island Log")); addLore(lores, tr("\u00a7eClick here to return to\n\u00a7ethe main island screen.")); meta4.setLore(lores); sign.setItemMeta(meta4); - menu.addItem(new ItemStack[]{sign}); + menu.addItem(sign); lores.clear(); ItemStack menuItem = new ItemStack(Material.WRITABLE_BOOK, 1); - meta4 = menuItem.getItemMeta(); + meta4 = requireNonNull(requireNonNull(menuItem.getItemMeta())); meta4.setDisplayName(tr("\u00a7e\u00a7lIsland Log")); - for (String log : plugin.getIslandInfo(player).getLog()) { - lores.add(log); - } + lores.addAll(plugin.getIslandInfo(player).getLog()); meta4.setLore(lores); menuItem.setItemMeta(meta4); menu.setItem(8, menuItem); @@ -282,103 +256,8 @@ public Inventory displayLogGUI(final Player player) { return menu; } - public Inventory displayBiomeGUI(final Player player) { - List lores = new ArrayList<>(); - String title = "\u00a79" + tr("Island Biome"); - Inventory menu = Bukkit.createInventory(new UltimateHolder(player, title, MenuType.DEFAULT), 27, title); - ItemMeta meta4 = sign.getItemMeta(); - meta4.setDisplayName("\u00a7h" + tr("Island Biome")); - addLore(lores, tr("\u00a7eClick here to return to\n\u00a7ethe main island screen.")); - meta4.setLore(lores); - sign.setItemMeta(meta4); - menu.addItem(new ItemStack[]{sign}); - lores.clear(); - String currentBiome = plugin.getIslandInfo(player).getBiome(); - for (BiomeMenuItem biomeMenu : biomeMenus) { - if (!BiomeCommand.biomeExists(biomeMenu.getId())) { - continue; // Skip it - } - ItemStack menuItem = biomeMenu.getIcon(); - meta4 = menuItem.getItemMeta(); - if (player.hasPermission("usb.biome." + biomeMenu.getId())) { - meta4.setDisplayName("\u00a7a" + tr("Biome: {0}", biomeMenu.getTitle())); - addLore(lores, "\u00a7f", biomeMenu.getDescription()); - if (biomeMenu.getId().equalsIgnoreCase(currentBiome)) { - addLore(lores, tr("\u00a72\u00a7lThis is your current biome.")); - } else { - addLore(lores, tr("\u00a7e\u00a7lClick to change to this biome.")); - } - } else { - meta4.setDisplayName("\u00a78" + tr("Biome: {0}", biomeMenu.getTitle())); - lores.add("\u00a7c" + tr("You cannot use this biome.")); - addLore(lores, "\u00a77", biomeMenu.getDescription()); - } - meta4.setLore(lores); - menuItem.setItemMeta(meta4); - menu.addItem(menuItem); - lores.clear(); - } - - updateBiomeRadius(player, menu); - - - return menu; - } - - private void updateBiomeRadius(Player player, Inventory menu) { - String radius = PlayerUtil.getMetadata(player, "biome.radius", "all"); - String radiusDisplay; - switch (radius) { - case "chunk": radiusDisplay = tr("\u00a72chunk"); - break; - case "all": - radiusDisplay = tr("\u00a7call"); - break; - default: - radiusDisplay = tr("\u00a7e{0}", radius); - } - - List lores = new ArrayList<>(); - ItemStack menuItem = new ItemStack(Material.RED_CARPET); - ItemMeta itemMeta = menuItem.getItemMeta(); - itemMeta.setDisplayName(tr("\u00a7c-")); - lores.add(tr("Decrease radius of biome-change")); - lores.add(tr(tr("Current radius: {0}", radiusDisplay))); - itemMeta.setLore(lores); - menuItem.setItemMeta(itemMeta); - menu.setItem(21, menuItem); - - lores.clear(); - menuItem = new ItemStack(Material.GRASS_BLOCK); - if (radius.matches("[0-9]+")) { - int radiusInt = Integer.parseInt(radius, 10); - if (radiusInt <= menuItem.getType().getMaxStackSize()) { - menuItem.setAmount(radiusInt); - } else { - menuItem.setAmount(1); - } - } else { - menuItem.setAmount(1); - } - itemMeta = menuItem.getItemMeta(); - itemMeta.setDisplayName(tr("Current radius: {0}", radiusDisplay)); - itemMeta.setLore(lores); - menuItem.setItemMeta(itemMeta); - menu.setItem(22, menuItem); - - lores.clear(); - menuItem = new ItemStack(Material.GREEN_CARPET); - itemMeta = menuItem.getItemMeta(); - itemMeta.setDisplayName(tr("\u00a72+")); - lores.add(tr("Increase radius of biome-change")); - lores.add(tr(tr("Current radius: {0}", radiusDisplay))); - itemMeta.setLore(lores); - menuItem.setItemMeta(itemMeta); - menu.setItem(23, menuItem); - } - private void addExtraMenus(Player player, Inventory menu) { - ConfigurationSection extras = plugin.getConfig().getConfigurationSection("options.extra-menus"); + ConfigurationSection extras = config.getYamlConfig().getConfigurationSection("options.extra-menus"); if (extras == null) { return; } @@ -409,9 +288,8 @@ private void addExtraMenus(Player player, Inventory menu) { } } // Only SIMPLE icons supported... - ItemStack item = new ItemStack(Material.matchMaterial(icon), 1); - ItemMeta meta = item.getItemMeta(); - meta.setDisplayName(title); + ItemStack item = GuiItemUtil.createGuiDisplayItem(icon, title); + ItemMeta meta = requireNonNull(requireNonNull(item.getItemMeta())); meta.setLore(lores); item.setItemMeta(meta); menu.setItem(index, item); @@ -422,12 +300,12 @@ private void addExtraMenus(Player player, Inventory menu) { } private boolean isExtraMenuAction(Player player, ItemStack currentItem) { - ConfigurationSection extras = plugin.getConfig().getConfigurationSection("options.extra-menus"); + ConfigurationSection extras = config.getYamlConfig().getConfigurationSection("options.extra-menus"); if (extras == null || currentItem == null || currentItem.getItemMeta() == null) { return false; } Material itemType = currentItem.getType(); - String itemTitle = currentItem.getItemMeta().getDisplayName(); + String itemTitle = requireNonNull(currentItem.getItemMeta()).getDisplayName(); for (String sIndex : extras.getKeys(false)) { ConfigurationSection menuSection = extras.getConfigurationSection(sIndex); if (menuSection == null) { @@ -468,7 +346,7 @@ private boolean isExtraMenuAction(Player player, ItemStack currentItem) { public Inventory displayChallengeGUI(final Player player, int page, String playerName) { int total = challengeLogic.getTotalPages(); String title = "\u00a79" + pre("{0} ({1}/{2})", tr("Challenge Menu"), page, total); - Inventory menu = Bukkit.createInventory(new UltimateHolder(player, title, MenuType.DEFAULT), CHALLENGE_PAGESIZE+COLS_PER_ROW, title); + Inventory menu = Bukkit.createInventory(new UltimateHolder(player, title, MenuType.DEFAULT), CHALLENGE_PAGESIZE + COLS_PER_ROW, title); final PlayerInfo pi = playerName == null ? plugin.getPlayerInfo(player) : plugin.getPlayerInfo(playerName); challengeLogic.populateChallengeRank(menu, pi, page, playerName != null && player.hasPermission("usb.mod.bypassrestriction")); int[] pages = new int[9]; @@ -476,28 +354,28 @@ public Inventory displayChallengeGUI(final Player player, int page, String playe pages[8] = total; int startOffset = 2; if (page > 5) { - startOffset = (int) ((Math.round(page/2d)) - 1); - if (startOffset > total-7) { - startOffset = total-7; + startOffset = (int) ((Math.round(page / 2d)) - 1); + if (startOffset > total - 7) { + startOffset = total - 7; } } for (int i = 0; i < 7; i++) { - pages[i+1] = startOffset+i; + pages[i + 1] = startOffset + i; } for (int i = 0; i < pages.length; i++) { int p = pages[i]; if (p >= 1 && p <= total) { ItemStack pageItem; if (p == page) { - pageItem = ItemStackUtil.createItemStack("WRITABLE_BOOK", tr("\u00a77Current page"), null); + pageItem = GuiItemUtil.createGuiDisplayItem(Material.WRITABLE_BOOK, tr("\u00a77Current page")); } else { - pageItem = ItemStackUtil.createItemStack("BOOK", tr("\u00a77Page {0}", p), null); + pageItem = GuiItemUtil.createGuiDisplayItem(Material.BOOK, tr("\u00a77Page {0}", p)); } if (i == 0) { pageItem = ItemStackUtil.builder(pageItem) - .displayName(tr("\u00a77First Page")) - .lore(playerName != null ? playerName : "") - .build(); + .displayName(tr("\u00a77First Page")) + .lore(playerName != null ? playerName : "") + .build(); } else if (i == 8) { pageItem = ItemStackUtil.builder(pageItem).displayName(tr("\u00a77Last Page")).build(); } @@ -517,13 +395,13 @@ public Inventory displayIslandGUI(final Player player) { } private Inventory createInitMenu(Player player) { - List schemeNames = plugin.getIslandGenerator().getSchemeNames(); - int menuSize = (int) Math.ceil(getMaxSchemeIndex(schemeNames) / 9d)*9; + List schemeNames = islandGenerator.getSchemeNames(); + int menuSize = (int) Math.ceil(getMaxSchemeIndex(schemeNames) / 9d) * 9; String title = "\u00a79" + tr("Island Create Menu"); Inventory menu = Bukkit.createInventory(new UltimateHolder(player, title, MenuType.DEFAULT), menuSize, title); List lores = new ArrayList<>(); ItemStack menuItem = new ItemStack(Material.OAK_SAPLING, 1); - ItemMeta meta = menuItem.getItemMeta(); + ItemMeta meta = requireNonNull(requireNonNull(menuItem.getItemMeta())); meta.setDisplayName(tr("\u00a7a\u00a7lStart an Island")); addLore(lores, "\u00a7f", tr("Start your skyblock journey\nby starting your own island.\nComplete challenges to earn\nitems and skybucks to help\nexpand your skyblock. You can\ninvite others to join in\nbuilding your island empire!\n\u00a7e\u00a7lClick here to start!")); meta.setLore(lores); @@ -531,17 +409,17 @@ private Inventory createInitMenu(Player player) { menu.addItem(menuItem); lores.clear(); - if (plugin.getConfig().getBoolean("island-schemes-enabled", true) && schemeNames.size() > 1) { + if (config.getYamlConfig().getBoolean("island-schemes-enabled", true) && schemeNames.size() > 1) { int index = 1; for (String schemeName : schemeNames) { - IslandPerk islandPerk = plugin.getPerkLogic().getIslandPerk(schemeName); - boolean enabled = plugin.getConfig().getBoolean("island-schemes." + islandPerk.getSchemeName() + ".enabled", true); + IslandPerk islandPerk = perkLogic.getIslandPerk(schemeName); + boolean enabled = config.getYamlConfig().getBoolean("island-schemes." + islandPerk.getSchemeName() + ".enabled", true); if (!enabled) { continue; // Skip } - index = Math.max(plugin.getConfig().getInt("island-schemes." + islandPerk.getSchemeName() + ".index", index), 1); + index = Math.max(config.getYamlConfig().getInt("island-schemes." + islandPerk.getSchemeName() + ".index", index), 1); menuItem = islandPerk.getDisplayItem(); - meta = menuItem.getItemMeta(); + meta = requireNonNull(requireNonNull(menuItem.getItemMeta())); lores = meta.getLore(); if (lores == null) { lores = new ArrayList<>(); @@ -558,29 +436,29 @@ private Inventory createInitMenu(Player player) { } lores.clear(); - menuItem = new ItemStack(Material.GRASS, 1); - meta = menuItem.getItemMeta(); + menuItem = new ItemStack(Material.SHORT_GRASS, 1); + meta = requireNonNull(requireNonNull(menuItem.getItemMeta())); meta.setDisplayName(tr("\u00a7a\u00a7lReturn to Spawn")); addLore(lores, "\u00a7f", tr("Teleport to the spawn area.")); meta.setLore(lores); menuItem.setItemMeta(meta); - menu.setItem(menuSize-2, menuItem); + menu.setItem(menuSize - 2, menuItem); lores.clear(); menuItem = new ItemStack(Material.PLAYER_HEAD, 1); - final SkullMeta meta2 = (SkullMeta) menuItem.getItemMeta(); + final SkullMeta meta2 = requireNonNull((SkullMeta) requireNonNull(menuItem.getItemMeta())); meta2.setDisplayName(tr("\u00a7a\u00a7lJoin an Island")); addLore(lores, "\u00a7f", tr("Want to join another player''s\nisland instead of starting\nyour own? If another player\ninvites you to their island\nyou can click here or use\n\u00a7e/island accept\u00a7f to join them.\n\u00a7e\u00a7lClick here to accept an invite!\n\u00a7e\u00a7l(You must be invited first)")); meta2.setLore(lores); menuItem.setItemMeta(meta2); - menu.setItem(menuSize-1, menuItem); + menu.setItem(menuSize - 1, menuItem); return menu; } private int getMaxSchemeIndex(List schemeNames) { int index = 1; for (String schemeName : schemeNames) { - int nextIndex = plugin.getConfig().getInt("island-schemes." + schemeName + ".index", index); + int nextIndex = config.getYamlConfig().getInt("island-schemes." + schemeName + ".index", index); if (nextIndex > index) { index = nextIndex; } else { @@ -591,11 +469,11 @@ private int getMaxSchemeIndex(List schemeNames) { } private Inventory createMainMenu(Player player) { - String title = "\u00a79" + tr("Island Menu"); + String title = "\u00a79" + tr("Island Menu"); Inventory menu = Bukkit.createInventory(new UltimateHolder(player, title, MenuType.DEFAULT), 18, title); List lores = new ArrayList<>(); ItemStack menuItem = new ItemStack(Material.OAK_DOOR, 1); - ItemMeta meta4 = menuItem.getItemMeta(); + ItemMeta meta4 = requireNonNull(requireNonNull(menuItem.getItemMeta())); meta4.setDisplayName(tr("\u00a7a\u00a7lReturn Home")); addLore(lores, "\u00a7f", tr("Return to your island''s home\npoint. You can change your home\npoint to any location on your\nisland using \u00a7b/island sethome\n\u00a7e\u00a7lClick here to return home.")); meta4.setLore(lores); @@ -606,10 +484,10 @@ private Inventory createMainMenu(Player player) { IslandInfo islandInfo = plugin.getIslandInfo(player); menuItem = new ItemStack(Material.DIAMOND_ORE, 1); - meta4 = menuItem.getItemMeta(); + meta4 = requireNonNull(requireNonNull(menuItem.getItemMeta())); meta4.setDisplayName(tr("\u00a7a\u00a7lChallenges")); addLore(lores, "\u00a7f", tr("View a list of \u00a79challenges that\nyou can complete on your island\nto earn skybucks, items, perks,\nand titles.")); - if (plugin.getChallengeLogic().isEnabled()) { + if (challengeLogic.isEnabled()) { addLore(lores, tr("\u00a7e\u00a7lClick here to view challenges.")); } else { addLore(lores, tr("\u00a74\u00a7lChallenges disabled.")); @@ -620,10 +498,10 @@ private Inventory createMainMenu(Player player) { lores.clear(); menuItem = new ItemStack(Material.EXPERIENCE_BOTTLE, 1); - meta4 = menuItem.getItemMeta(); + meta4 = requireNonNull(requireNonNull(menuItem.getItemMeta())); meta4.setDisplayName(tr("\u00a7a\u00a7lIsland Level")); addLore(lores, tr("\u00a7eCurrent Level: \u00a7a{0,number,##.#}", islandInfo.getLevel())); - addLore(lores, plugin.getLimitLogic().getSummary(islandInfo)); + addLore(lores, limitLogic.getSummary(islandInfo)); addLore(lores, "\u00a7f", tr("Gain island levels by expanding\nyour skyblock and completing\ncertain challenges. Rarer blocks\nwill add more to your level.\n\u00a7e\u00a7lClick here to refresh.\n\u00a7e\u00a7l(must be on island)")); meta4.setLore(lores); menuItem.setItemMeta(meta4); @@ -631,7 +509,7 @@ private Inventory createMainMenu(Player player) { lores.clear(); menuItem = new ItemStack(Material.PLAYER_HEAD, 1); - final SkullMeta meta2 = (SkullMeta) menuItem.getItemMeta(); + final SkullMeta meta2 = requireNonNull((SkullMeta) requireNonNull(menuItem.getItemMeta())); meta2.setDisplayName("\u00a7a\u00a7l" + tr("Island Group")); lores.add(tr("\u00a7eMembers: \u00a72{0}/{1}", islandInfo.getPartySize(), islandInfo.getMaxPartySize())); addLore(lores, "\u00a7f", tr("View the members of your island\ngroup and their permissions. If\nyou are the island leader, you\ncan change the member permissions.\n\u00a7e\u00a7lClick here to view or change.")); @@ -641,9 +519,9 @@ private Inventory createMainMenu(Player player) { lores.clear(); menuItem = new ItemStack(Material.JUNGLE_SAPLING, 1); - meta4 = menuItem.getItemMeta(); + meta4 = requireNonNull(requireNonNull(menuItem.getItemMeta())); meta4.setDisplayName("\u00a7a\u00a7l" + tr("Change Island Biome")); - lores.add(tr("\u00a7eCurrent Biome: \u00a7b{0}", islandInfo.getBiome())); + lores.add(tr("\u00a7eCurrent Biome: \u00a7b{0}", islandInfo.getBiomeName())); addLore(lores, "\u00a7f", tr("The island biome affects things\nlike grass color and spawning\nof both animals and monsters.")); if (islandInfo.hasPerm(player, "canChangeBiome")) { addLore(lores, tr("\u00a7e\u00a7lClick here to change biomes.")); @@ -652,11 +530,11 @@ private Inventory createMainMenu(Player player) { } meta4.setLore(lores); menuItem.setItemMeta(meta4); - menu.addItem(menuItem); + //menu.addItem(menuItem); lores.clear(); menuItem = new ItemStack(Material.IRON_BARS, 1); - meta4 = menuItem.getItemMeta(); + meta4 = requireNonNull(requireNonNull(menuItem.getItemMeta())); meta4.setDisplayName(tr("\u00a7a\u00a7lIsland Lock")); if (plugin.getIslandInfo(player).isLocked()) { addLore(lores, tr("\u00a7eLock Status: \u00a7aActive\n\u00a7fYour island is currently \u00a7clocked.\n\u00a7fPlayers outside of your group\n\u00a7fare unable to enter your island.")); @@ -675,12 +553,12 @@ private Inventory createMainMenu(Player player) { } meta4.setLore(lores); menuItem.setItemMeta(meta4); - menu.addItem(menuItem); + //menu.addItem(menuItem); lores.clear(); if (plugin.getIslandInfo(player).hasWarp()) { menuItem = new ItemStack(Material.END_PORTAL_FRAME, 1); - meta4 = menuItem.getItemMeta(); + meta4 = requireNonNull(requireNonNull(menuItem.getItemMeta())); meta4.setDisplayName(tr("\u00a7a\u00a7lIsland Warp")); addLore(lores, tr("\u00a7eWarp Status: \u00a7aActive\n\u00a7fOther players may warp to your\n\u00a7fisland at anytime to the point\n\u00a7fyou set using \u00a7d/island setwarp.")); if (islandInfo.hasPerm(player, "canToggleWarp") && player.hasPermission("usb.island.togglewarp")) { @@ -690,7 +568,7 @@ private Inventory createMainMenu(Player player) { } } else { menuItem = new ItemStack(Material.END_STONE, 1); - meta4 = menuItem.getItemMeta(); + meta4 = requireNonNull(menuItem.getItemMeta()); meta4.setDisplayName(tr("\u00a7a\u00a7lIsland Warp")); addLore(lores, tr("\u00a7eWarp Status: \u00a78Inactive\n\u00a7fOther players can't warp to your\n\u00a7fisland. Set a warp point using\n\u00a7d/island setwarp \u00a7fbefore activating.")); if (islandInfo.hasPerm(player, "canToggleWarp") && player.hasPermission("usb.island.togglewarp")) { @@ -704,8 +582,17 @@ private Inventory createMainMenu(Player player) { menu.addItem(menuItem); lores.clear(); - menuItem = new ItemStack(Material.GRASS, 1); + menuItem = new ItemStack(Material.HOPPER, 1); meta4 = menuItem.getItemMeta(); + meta4.setDisplayName(tr("\u00a7b\u00a7lBuy Extra Hopper Limit")); + addLore(lores, tr("\u00a7eCurrent Limit: \u00a7a{0,number,##}\u00a77(Default) + \u00a7a{1,number,##}\u00a77(Extra)", plugin.getBlockLimitLogic().getLimits().getOrDefault(Material.HOPPER, 0),islandInfo.getHopperLimit())); + meta4.setLore(lores); + menuItem.setItemMeta(meta4); + menu.addItem(menuItem); + lores.clear(); + + menuItem = new ItemStack(Material.SHORT_GRASS, 1); + meta4 = requireNonNull(menuItem.getItemMeta()); meta4.setDisplayName(tr("\u00a7a\u00a7lReturn to Spawn")); addLore(lores, "\u00a7f", tr("Teleport to the spawn area.")); meta4.setLore(lores); @@ -714,7 +601,7 @@ private Inventory createMainMenu(Player player) { lores.clear(); menuItem = new ItemStack(Material.WRITABLE_BOOK, 1); - meta4 = menuItem.getItemMeta(); + meta4 = requireNonNull(menuItem.getItemMeta()); meta4.setDisplayName(tr("\u00a7a\u00a7lIsland Log")); addLore(lores, "\u00a7f", tr("View a log of events from\nyour island such as member,\nbiome, and warp changes.\n\u00a7e\u00a7lClick to view the log.")); meta4.setLore(lores); @@ -723,7 +610,7 @@ private Inventory createMainMenu(Player player) { lores.clear(); menuItem = new ItemStack(Material.RED_BED, 1); // red bed - meta4 = menuItem.getItemMeta(); + meta4 = requireNonNull(menuItem.getItemMeta()); meta4.setDisplayName(tr("\u00a7a\u00a7lChange Home Location")); addLore(lores, "\u00a7f", tr("When you teleport to your\nisland you will be taken to\nthis location.\n\u00a7e\u00a7lClick here to change.")); meta4.setLore(lores); @@ -731,8 +618,8 @@ private Inventory createMainMenu(Player player) { menu.setItem(9, menuItem); // First item, 2nd line lores.clear(); - menuItem = new ItemStack(Material.HOPPER, 1); - meta4 = menuItem.getItemMeta(); + menuItem = new ItemStack(Material.BEACON, 1); + meta4 = requireNonNull(menuItem.getItemMeta()); meta4.setDisplayName(tr("\u00a7a\u00a7lChange Warp Location")); addLore(lores, "\u00a7f", tr("When your warp is activated,\nother players will be taken to\nthis point when they teleport\nto your island.")); if (islandInfo.hasPerm(player, "canChangeWarp") && player.hasPermission("usb.island.setwarp")) { @@ -742,12 +629,12 @@ private Inventory createMainMenu(Player player) { } meta4.setLore(lores); menuItem.setItemMeta(meta4); - menu.setItem(15, menuItem); + menu.setItem(13, menuItem); lores.clear(); if (islandInfo.isLeader(player)) { - if (plugin.getConfig().getBoolean("island-schemes-enabled", true)) { + if (config.getYamlConfig().getBoolean("island-schemes-enabled", true)) { menuItem = new ItemStack(Material.PODZOL, 1); - meta4 = menuItem.getItemMeta(); + meta4 = requireNonNull(menuItem.getItemMeta()); meta4.setDisplayName(tr("\u00a7c\u00a7lRestart Island")); addLore(lores, "\u00a7f", tr("Restarts your island.\n\u00a74WARNING! \u00a7cwill remove your items and island!")); meta4.setLore(lores); @@ -757,7 +644,7 @@ private Inventory createMainMenu(Player player) { } } else { menuItem = new ItemStack(Material.IRON_DOOR, 1); - meta4 = menuItem.getItemMeta(); + meta4 = requireNonNull(menuItem.getItemMeta()); meta4.setDisplayName(tr("\u00a7c\u00a7lLeave Island")); addLore(lores, "\u00a7f", tr("Leaves your island.\n\u00a74WARNING! \u00a7cwill remove all your items!")); addLore(lores, tr("\u00a7cClick to leave")); @@ -765,8 +652,8 @@ private Inventory createMainMenu(Player player) { menuItem.setItemMeta(meta4); menu.setItem(17, menuItem); lores.clear(); - long millisLeft = plugin.getConfirmHandler().millisLeft(player, "/is leave"); - if (millisLeft > 0) { + Duration durationLeft = confirmHandler.durationLeft(player, "/is leave"); + if (durationLeft.isPositive()) { updateLeaveMenuItemTimer(player, menu, menuItem); } } @@ -780,10 +667,10 @@ public void onClick(InventoryClickEvent event) { return; // Bail out, nothing we can do anyway } Player p = (Player) event.getWhoClicked(); - ItemMeta meta = currentItem.getItemMeta(); + ItemMeta meta = requireNonNull(currentItem.getItemMeta()); SkullMeta skull = meta instanceof SkullMeta ? (SkullMeta) meta : null; if (!(event.getInventory().getHolder() instanceof UltimateHolder)) - return; + return; String inventoryName = stripFormatting(((UltimateHolder) event.getInventory().getHolder()).getTitle()); int slotIndex = event.getSlot(); int menuSize = event.getInventory().getSize(); @@ -791,8 +678,6 @@ public void onClick(InventoryClickEvent event) { onClickPartyMenu(event, currentItem, p, meta, skull, slotIndex); } else if (inventoryName.contains(stripFormatting(tr("Permissions")))) { onClickPermissionMenu(event, currentItem, p, inventoryName, slotIndex); - } else if (inventoryName.equalsIgnoreCase(stripFormatting(tr("Island Biome")))) { - onClickBiomeMenu(event, currentItem, p, slotIndex); } else if (inventoryName.contains(stripFormatting(tr("Challenge Menu")))) { onClickChallengeMenu(event, currentItem, p, inventoryName); } else if (inventoryName.equalsIgnoreCase(stripFormatting(tr("Island Log")))) { @@ -803,22 +688,18 @@ public void onClick(InventoryClickEvent event) { onClickCreateMenu(event, p, meta, slotIndex, menuSize); } else if (inventoryName.equalsIgnoreCase(stripFormatting(tr("Island Restart Menu")))) { onClickRestartMenu(event, p, meta, slotIndex, currentItem); - } else if (inventoryName.startsWith(stripFormatting(tr("Config:"))) && event.getWhoClicked() instanceof Player) { - plugin.getConfigMenu().onClick(event); } } private void onClickRestartMenu(final InventoryClickEvent event, final Player p, ItemMeta meta, int slotIndex, ItemStack currentItem) { event.setCancelled(true); if (slotIndex == 0) { - p.closeInventory(); p.openInventory(createMainMenu(p)); - } else if (currentItem != null && meta != null && meta.getDisplayName() != null) { + } else if (currentItem != null && meta != null && meta.hasDisplayName()) { String schemeName = stripFormatting(meta.getDisplayName()); - IslandPerk islandPerk = plugin.getPerkLogic().getIslandPerk(schemeName); - if (plugin.getPerkLogic().getSchemes(p).contains(schemeName) && p.hasPermission(islandPerk.getPermission())) { - if (plugin.getConfirmHandler().millisLeft(p, "/is restart") > 0) { - p.closeInventory(); + IslandPerk islandPerk = perkLogic.getIslandPerk(schemeName); + if (perkLogic.getSchemes(p).contains(schemeName) && p.hasPermission(islandPerk.getPermission())) { + if (confirmHandler.durationLeft(p, "/is restart").isPositive()) { p.performCommand("island restart " + schemeName); } else { p.performCommand("island restart " + schemeName); @@ -830,33 +711,29 @@ private void onClickRestartMenu(final InventoryClickEvent event, final Player p, private void updateRestartMenuTimer(final Player p, final Inventory inventory) { final BukkitTask[] hackySharing = new BukkitTask[1]; - hackySharing[0] = plugin.sync(() -> { + hackySharing[0] = scheduler.sync(() -> { if (inventory.getViewers().contains(p)) { - updateRestartMenu(inventory, p, plugin.getIslandGenerator().getSchemeNames()); + updateRestartMenu(inventory, p, islandGenerator.getSchemeNames()); } - if (plugin.getConfirmHandler().millisLeft(p, "/is restart") <= 0 || !inventory.getViewers().contains(p)) { + if (!confirmHandler.durationLeft(p, "/is restart").isPositive() || !inventory.getViewers().contains(p)) { if (hackySharing.length > 0 && hackySharing[0] != null) { hackySharing[0].cancel(); } } - }, 0, 1000); + }, Duration.ZERO, Duration.ofSeconds(1)); } private void onClickCreateMenu(InventoryClickEvent event, Player p, ItemMeta meta, int slotIndex, int menuSize) { event.setCancelled(true); if (slotIndex == 0) { - p.closeInventory(); p.performCommand("island create"); - } else if (slotIndex == menuSize-2) { - p.closeInventory(); + } else if (slotIndex == menuSize - 2) { p.performCommand("island spawn"); - } else if (slotIndex == menuSize-1) { - p.closeInventory(); + } else if (slotIndex == menuSize - 1) { p.performCommand("island accept"); - } else if (meta != null && meta.getDisplayName() != null) { + } else if (meta != null && meta.hasDisplayName()) { String schemeName = stripFormatting(meta.getDisplayName()); - if (plugin.getPerkLogic().getSchemes(p).contains(schemeName)) { - p.closeInventory(); + if (perkLogic.getSchemes(p).contains(schemeName)) { p.performCommand("island create " + schemeName); } else { p.sendMessage(tr("\u00a7eYou do not have access to that island-schematic!")); @@ -864,100 +741,88 @@ private void onClickCreateMenu(InventoryClickEvent event, Player p, ItemMeta met } } - private void onClickMainMenu(InventoryClickEvent event, ItemStack currentItem, Player p, int slotIndex) { + private void onClickMainMenu(InventoryClickEvent event, ItemStack currentItem, Player player, int slotIndex) { event.setCancelled(true); if (slotIndex < 0 || slotIndex > 35) { return; } - PlayerInfo playerInfo = plugin.getPlayerInfo(p); + PlayerInfo playerInfo = plugin.getPlayerInfo(player); IslandInfo islandInfo = plugin.getIslandInfo(playerInfo); if (currentItem.getType() == Material.JUNGLE_SAPLING) { - p.closeInventory(); - p.performCommand("island biome"); + player.performCommand("island biome"); } else if (currentItem.getType() == Material.PLAYER_HEAD) { - p.closeInventory(); - p.performCommand("island party"); + player.performCommand("island party"); } else if (currentItem.getType() == Material.RED_BED) { - p.closeInventory(); - p.performCommand("island sethome"); - p.performCommand("island"); - } else if (currentItem.getType() == Material.GRASS) { - p.closeInventory(); - p.performCommand("island spawn"); + player.performCommand("island sethome"); + player.performCommand("island"); + } else if (currentItem.getType() == Material.SHORT_GRASS) { + player.performCommand("island spawn"); + } else if (currentItem.getType() == Material.BEACON) { + player.performCommand("island setwarp"); + player.performCommand("island"); } else if (currentItem.getType() == Material.HOPPER) { - p.closeInventory(); - p.performCommand("island setwarp"); - p.performCommand("island"); + player.closeInventory(); + player.performCommand("island hopper"); } else if (currentItem.getType() == Material.WRITABLE_BOOK) { - p.closeInventory(); - p.performCommand("island log"); + player.performCommand("island log"); } else if (currentItem.getType() == Material.OAK_DOOR) { - p.closeInventory(); - p.performCommand("island home"); + player.performCommand("island home"); } else if (currentItem.getType() == Material.EXPERIENCE_BOTTLE) { - p.closeInventory(); - p.performCommand("island level"); + player.performCommand("island level"); } else if (currentItem.getType() == Material.DIAMOND_ORE) { - p.closeInventory(); - p.performCommand("c"); + player.performCommand("c"); } else if (currentItem.getType() == Material.END_STONE || currentItem.getType() == Material.END_PORTAL_FRAME) { - p.closeInventory(); - p.performCommand("island togglewarp"); - p.performCommand("island"); + player.performCommand("island togglewarp"); + player.performCommand("island"); } else if (currentItem.getType() == Material.IRON_BARS && islandInfo.isLocked()) { - p.closeInventory(); - p.performCommand("island unlock"); - p.performCommand("island"); + player.performCommand("island unlock"); + player.performCommand("island"); } else if (currentItem.getType() == Material.IRON_BARS && !islandInfo.isLocked()) { - p.closeInventory(); - p.performCommand("island lock"); - p.performCommand("island"); + player.performCommand("island lock"); + player.performCommand("island"); } else if (slotIndex == 17) { - if (islandInfo.isLeader(p) && plugin.getConfig().getBoolean("island-schemes-enabled", true)) { - p.closeInventory(); - p.openInventory(createRestartGUI(p)); + if (islandInfo.isLeader(player) && config.getYamlConfig().getBoolean("island-schemes-enabled", true)) { + player.openInventory(createRestartGUI(player)); } else { - if (plugin.getConfirmHandler().millisLeft(p, "/is leave") > 0) { - p.closeInventory(); - p.performCommand("island leave"); + if (confirmHandler.durationLeft(player, "/is leave").isPositive()) { + player.performCommand("island leave"); } else { - p.performCommand("island leave"); - updateLeaveMenuItemTimer(p, event.getInventory(), currentItem); + player.performCommand("island leave"); + updateLeaveMenuItemTimer(player, event.getInventory(), currentItem); } } } else { - if (!isExtraMenuAction(p, currentItem)) { - p.closeInventory(); - p.performCommand("island"); + if (!isExtraMenuAction(player, currentItem)) { + player.performCommand("island"); } } } - private void updateLeaveMenuItemTimer(final Player p, final Inventory inventory, final ItemStack currentItem) { - final BukkitTask[] hackySharing = new BukkitTask[1]; - hackySharing[0] = plugin.sync(() -> { - long millisLeft = plugin.getConfirmHandler().millisLeft(p, "/is leave"); - if (inventory.getViewers().contains(p)) { - updateLeaveMenuItem(inventory, currentItem, millisLeft); + private void updateLeaveMenuItemTimer(final Player player, final Inventory inventory, final ItemStack currentItem) { + BukkitTask[] hackySharing = new BukkitTask[1]; + hackySharing[0] = scheduler.sync(() -> { + Duration durationLeft = confirmHandler.durationLeft(player, "/is leave"); + if (inventory.getViewers().contains(player)) { + updateLeaveMenuItem(inventory, currentItem, durationLeft); } - if (millisLeft <= 0 || !inventory.getViewers().contains(p)) { + if (!durationLeft.isPositive() || !inventory.getViewers().contains(player)) { if (hackySharing.length > 0 && hackySharing[0] != null) { hackySharing[0].cancel(); } } - }, 0, 1000); + }, Duration.ZERO, Duration.ofSeconds(1)); } - private void updateLeaveMenuItem(Inventory inventory, ItemStack currentItem, long millisLeft) { - if (currentItem == null || currentItem.getItemMeta() == null ||currentItem.getItemMeta().getLore() == null) { + private void updateLeaveMenuItem(Inventory inventory, ItemStack currentItem, Duration durationLeft) { + if (currentItem == null || currentItem.getItemMeta() == null || currentItem.getItemMeta().getLore() == null) { return; } - ItemMeta meta = currentItem.getItemMeta(); + ItemMeta meta = requireNonNull(currentItem.getItemMeta()); List lore = meta.getLore(); - if (millisLeft >= 0) { - lore.set(lore.size()-1, tr("\u00a7cClick within \u00a79{0}\u00a7c to leave!", TimeUtil.millisAsString(millisLeft))); + if (!durationLeft.isNegative()) { + lore.set(lore.size() - 1, tr("\u00a7cClick within \u00a79{0}\u00a7c to leave!", TimeUtil.durationAsString(durationLeft))); } else { - lore.set(lore.size()-1, tr("\u00a7cClick to leave")); + lore.set(lore.size() - 1, tr("\u00a7cClick to leave")); } meta.setLore(lore); currentItem.setItemMeta(meta); @@ -965,13 +830,13 @@ private void updateLeaveMenuItem(Inventory inventory, ItemStack currentItem, lon } public Inventory createRestartGUI(Player player) { - List schemeNames = plugin.getIslandGenerator().getSchemeNames(); - int menuSize = (int) Math.ceil(getMaxSchemeIndex(schemeNames) / 9d)*9; + List schemeNames = islandGenerator.getSchemeNames(); + int menuSize = (int) Math.ceil(getMaxSchemeIndex(schemeNames) / 9d) * 9; String title = "\u00a79" + tr("Island Restart Menu"); Inventory menu = Bukkit.createInventory(new UltimateHolder(player, title, MenuType.DEFAULT), menuSize, title); List lores = new ArrayList<>(); - ItemStack menuItem = new ItemStack(SIGN_MATERIAL, 1); - ItemMeta meta = menuItem.getItemMeta(); + ItemStack menuItem = new ItemStack(Material.OAK_SIGN, 1); + ItemMeta meta = requireNonNull(menuItem.getItemMeta()); meta.setDisplayName(tr("\u00a7a\u00a7lReturn to the main menu")); meta.setLore(lores); menuItem.setItemMeta(meta); @@ -979,7 +844,7 @@ public Inventory createRestartGUI(Player player) { lores.clear(); updateRestartMenu(menu, player, schemeNames); - if (plugin.getConfirmHandler().millisLeft(player, "/is restart") > 0) { + if (confirmHandler.durationLeft(player, "/is restart").isPositive()) { updateRestartMenuTimer(player, menu); } return menu; @@ -991,22 +856,22 @@ private void updateRestartMenu(Inventory menu, Player player, List schem List lores; int index = 1; for (String schemeName : schemeNames) { - IslandPerk islandPerk = plugin.getPerkLogic().getIslandPerk(schemeName); - boolean enabled = plugin.getConfig().getBoolean("island-schemes." + islandPerk.getSchemeName() + ".enabled", true); + IslandPerk islandPerk = perkLogic.getIslandPerk(schemeName); + boolean enabled = config.getYamlConfig().getBoolean("island-schemes." + islandPerk.getSchemeName() + ".enabled", true); if (!enabled) { continue; // Skip } - index = plugin.getConfig().getInt("island-schemes." + islandPerk.getSchemeName() + ".index", index); + index = config.getYamlConfig().getInt("island-schemes." + islandPerk.getSchemeName() + ".index", index); menuItem = islandPerk.getDisplayItem(); - meta = menuItem.getItemMeta(); + meta = requireNonNull(menuItem.getItemMeta()); lores = meta.getLore(); if (lores == null) { lores = new ArrayList<>(); } if (player.hasPermission(islandPerk.getPermission())) { - long millisLeft = plugin.getConfirmHandler().millisLeft(player, "/is restart"); - if (millisLeft > 0) { - addLore(lores, tr("\u00a7cClick within \u00a79{0}\u00a7c to restart!", TimeUtil.millisAsString(millisLeft))); + Duration durationLeft = confirmHandler.durationLeft(player, "/is restart"); + if (durationLeft.isPositive()) { + addLore(lores, tr("\u00a7cClick within \u00a79{0}\u00a7c to restart!", TimeUtil.durationAsString(durationLeft))); } else { addLore(lores, tr("\u00a7aClick to restart!")); } @@ -1017,7 +882,6 @@ private void updateRestartMenu(Inventory menu, Player player, List schem menuItem.setItemMeta(meta); menu.setItem(index++, menuItem); } - player.updateInventory(); } private void onClickLogMenu(InventoryClickEvent event, Player p, int slotIndex) { @@ -1025,7 +889,6 @@ private void onClickLogMenu(InventoryClickEvent event, Player p, int slotIndex) if (slotIndex < 0 || slotIndex > 35) { return; } - p.closeInventory(); p.performCommand("island"); } @@ -1040,19 +903,17 @@ private void onClickChallengeMenu(InventoryClickEvent event, ItemStack currentIt max = Integer.parseInt(m.group("max")); } ItemStack item = event.getInventory().getItem(event.getInventory().getSize() - 9); - String playerName = item != null && item.hasItemMeta() && item.getItemMeta().getLore() != null - && item.getItemMeta().getLore().size() > 0 - ? item.getItemMeta().getLore().get(0) - : null; + String playerName = item != null && item.hasItemMeta() && requireNonNull(item.getItemMeta()).getLore() != null + && !item.getItemMeta().getLore().isEmpty() + ? item.getItemMeta().getLore().getFirst() + : null; if (playerName != null && playerName.trim().isEmpty()) { playerName = null; } // Last row is pagination if (slotIndex >= CHALLENGE_PAGESIZE && slotIndex < CHALLENGE_PAGESIZE + COLS_PER_ROW - && currentItem != null && currentItem.getType() != Material.AIR) - { + && currentItem != null && currentItem.getType() != Material.AIR) { // Pagination - p.closeInventory(); p.openInventory(displayChallengeGUI(p, currentItem.getAmount(), playerName)); return; } @@ -1061,16 +922,14 @@ private void onClickChallengeMenu(InventoryClickEvent event, ItemStack currentIt return; } if ((slotIndex % 9) > 0) { // 0,9... are the rank-headers... - p.closeInventory(); if (currentItem.getItemMeta() != null) { - String challenge = currentItem.getItemMeta().getDisplayName(); + String challenge = requireNonNull(currentItem.getItemMeta()).getDisplayName(); String challengeName = stripFormatting(challenge); p.performCommand("c c " + challengeName); } p.openInventory(displayChallengeGUI(p, page, playerName)); } else { - p.closeInventory(); - if (slotIndex < (CHALLENGE_PAGESIZE/2)) { // Upper half + if (slotIndex < (CHALLENGE_PAGESIZE / 2)) { // Upper half if (page > 1) { p.openInventory(displayChallengeGUI(p, page - 1, playerName)); } else { @@ -1086,46 +945,7 @@ private void onClickChallengeMenu(InventoryClickEvent event, ItemStack currentIt private boolean isAirOrLocked(ItemStack currentItem) { return currentItem != null && currentItem.getType() == Material.AIR || - currentItem != null && currentItem.getItemMeta() != null && currentItem.getItemMeta().getDisplayName().equals(tr("\u00a74\u00a7lLocked Challenge")); - } - - private void onClickBiomeMenu(InventoryClickEvent event, ItemStack currentItem, Player p, int slotIndex) { - event.setCancelled(true); - if (slotIndex < 0 || slotIndex > 35) { - return; - } - if (slotIndex == 0 && currentItem.getType() == SIGN_MATERIAL) { - p.closeInventory(); - p.performCommand("island"); - return; - } - if (slotIndex >= 21 && slotIndex <= 23) { - List radii = Arrays.asList("10", "chunk", "20", "30", "40", "50", "60", "70", "80", "90", "100", "all"); - String radius = PlayerUtil.getMetadata(p, "biome.radius", "all"); - int ix = radii.indexOf(radius); - if (ix == -1) { - ix = 1; - } - if (currentItem.getType() == Material.RED_CARPET && ix > 0) { - ix--; - } else if (currentItem.getType() == Material.GREEN_CARPET && ix < radii.size()-1) { - ix++; - } - radius = radii.get(ix); - PlayerUtil.setMetadata(p, "biome.radius", radius); - updateBiomeRadius(p, event.getInventory()); - event.setCancelled(true); - return; - } - for (BiomeMenuItem biomeMenu : biomeMenus) { - ItemStack menuIcon = biomeMenu.getIcon(); - if (currentItem.getType() == menuIcon.getType() && currentItem.getDurability() == menuIcon.getDurability()) { - String radius = PlayerUtil.getMetadata(p, "biome.radius", "all"); - p.closeInventory(); - p.performCommand("island biome " + biomeMenu.getId() + " " + radius); - return; - } - } + currentItem != null && currentItem.getItemMeta() != null && currentItem.getItemMeta().getDisplayName().equals(tr("\u00a74\u00a7lLocked Challenge")); } private void onClickPermissionMenu(InventoryClickEvent event, ItemStack currentItem, Player p, String inventoryName, int slotIndex) { @@ -1135,32 +955,30 @@ private void onClickPermissionMenu(InventoryClickEvent event, ItemStack currentI } IslandInfo islandInfo = plugin.getIslandInfo(p); if (!plugin.getIslandInfo(p).isLeader(p)) { - p.closeInventory(); p.openInventory(displayPartyGUI(p)); } String[] playerPerm = inventoryName.split(" "); - String pname = playerPerm[0]; + String name = playerPerm[0]; + UUID uuid = plugin.getPlayerDB().getUUIDFromName(name); + PlayerProfile profile = Bukkit.createPlayerProfile(uuid, name); ItemStack skullItem = event.getInventory().getItem(1); if (skullItem != null && skullItem.getType().equals(Material.PLAYER_HEAD)) { - ItemMeta meta = skullItem.getItemMeta(); + ItemMeta meta = requireNonNull(skullItem.getItemMeta()); if (meta instanceof SkullMeta) { - pname = ((SkullMeta) meta).getOwner(); + profile = ((SkullMeta) meta).getOwnerProfile(); } } for (PartyPermissionMenuItem item : permissionMenuItems) { if (currentItem.getType() == item.getIcon().getType()) { - p.closeInventory(); - islandInfo.togglePerm(pname, item.getPerm()); - p.openInventory(displayPartyPlayerGUI(p, pname)); + islandInfo.togglePerm(profile.getUniqueId(), item.getPerm()); + p.openInventory(displayPartyPlayerGUI(p, profile)); return; } } - if (currentItem.getType() == SIGN_MATERIAL) { - p.closeInventory(); + if (currentItem.getType() == Material.OAK_SIGN) { p.openInventory(displayPartyGUI(p)); } else { - p.closeInventory(); - p.openInventory(displayPartyPlayerGUI(p, pname)); + p.openInventory(displayPartyPlayerGUI(p, profile)); } } @@ -1169,12 +987,10 @@ private void onClickPartyMenu(InventoryClickEvent event, ItemStack currentItem, if (slotIndex < 0 || slotIndex > 35) { return; } - if (meta == null || currentItem.getType() == SIGN_MATERIAL) { - p.closeInventory(); + if (meta == null || currentItem.getType() == Material.OAK_SIGN) { p.performCommand("island"); - } else if (skull != null && plugin.getIslandInfo(p).isLeader(p)) { - p.closeInventory(); - p.openInventory(displayPartyPlayerGUI(p, skull.getOwner())); + } else if (skull != null && skull.hasOwner() && plugin.getIslandInfo(p).isLeader(p)) { + p.openInventory(displayPartyPlayerGUI(p, skull.getOwnerProfile())); } } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/menu/StringEditMenu.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/menu/StringEditMenu.java deleted file mode 100644 index 3772afe08..000000000 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/menu/StringEditMenu.java +++ /dev/null @@ -1,173 +0,0 @@ -package us.talabrek.ultimateskyblock.menu; - -import dk.lockfuglsang.minecraft.file.FileUtil; -import dk.lockfuglsang.minecraft.yml.YmlConfiguration; -import org.bukkit.Bukkit; -import org.bukkit.Material; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.entity.Player; -import org.bukkit.event.inventory.InventoryClickEvent; -import org.bukkit.inventory.Inventory; -import org.bukkit.inventory.ItemStack; -import org.bukkit.inventory.meta.ItemMeta; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import us.talabrek.ultimateskyblock.player.UltimateHolder; -import us.talabrek.ultimateskyblock.player.UltimateHolder.MenuType; - -import static dk.lockfuglsang.minecraft.po.I18nUtil.tr; -import static dk.lockfuglsang.minecraft.util.FormatUtil.stripFormatting; - -/** - * Complex editor for string values (only simple keyboard, no extended). - */ -public class StringEditMenu extends AbstractConfigMenu implements EditMenu { - private final EditMenu parent; - private ArrayList keyboard = new ArrayList<>(); - private ArrayList capslockOverlay = new ArrayList<>(); - - private ItemStack capsOn; - private ItemStack capsOff; - - private final int capsIndex; - private final int backspaceIndex; - private final int returnIndex; - - public StringEditMenu(YmlConfiguration menuConfig, EditMenu parent) { - super(menuConfig); - this.parent = parent; - List characterMap = menuConfig.getList("keyboard.en"); - ensureCapacity(keyboard, 9*6); - ensureCapacity(capslockOverlay, 9*6); - int row = 0; - for (Object o : characterMap) { - int col = 0; - if (o instanceof List) { - for (String item : (List)o) { - ItemStack character = createItem(item); - if (character != null) { - keyboard.set(getIndex(row, col), character); - } - col++; - } - } - row++; - } - ConfigurationSection overlay = menuConfig.getConfigurationSection("keyboard.capslock.overlay"); - if (overlay != null) { - for (String key : overlay.getKeys(false)) { - int index = Integer.parseInt(key, 10); - ItemStack item = createItem(overlay.getString(key)); - if (item != null) { - capslockOverlay.set(index, item); - } - } - } - capsOn = createItem(menuConfig.getString("keyboard.capslock.true")); - capsOff = createItem(menuConfig.getString("keyboard.capslock.false")); - keyboard.set(getIndex(0,0), createItem(Material.OAK_DOOR, "\u00a79" + tr("Return"), null)); - capsIndex = getIndex(5, 0); - keyboard.set(capsIndex, capsOff); - backspaceIndex = getIndex(0, 5); - returnIndex = 0; - } - - @Override - public boolean onClick(InventoryClickEvent e) { - if (!(e.getInventory().getHolder() instanceof UltimateHolder) || - !stripFormatting(((UltimateHolder) e.getInventory().getHolder()).getTitle()).equals(stripFormatting(getTitle()))) { - return false; - } - Player player = (Player) e.getWhoClicked(); - ItemStack returnItem = e.getInventory().getItem(0); - String configName = returnItem.getItemMeta().getLore().get(0); - String path = returnItem.getItemMeta().getLore().get(1); - int page = getPage(returnItem.getItemMeta().getLore().get(2)); - ItemStack currentItem = e.getCurrentItem(); - boolean isCaps = e.getInventory().getItem(capsIndex).getItemMeta().getDisplayName().equals(tr("Caps On")); - if (currentItem != null) { - YmlConfiguration config = FileUtil.getYmlConfiguration(configName); - String value = config.getString(path); - if (e.getSlot() == capsIndex) { - // Toggle caps - ItemStack capsItem = isCaps ? capsOff.clone() : capsOn.clone(); - ItemMeta meta = capsItem.getItemMeta(); - meta.setLore(Arrays.asList(value)); - capsItem.setItemMeta(meta); - e.getInventory().setItem(capsIndex, capsItem); - isCaps = !isCaps; - } else if (e.getSlot() == backspaceIndex) { - if (value.length() > 0) { - value = value.substring(0, value.length() - 1); - config.set(path, value); - config.set("dirty", true); - } - } else if (e.getSlot() == returnIndex) { - player.openInventory(parent.createEditMenu(configName, path, page)); - return true; - } else if (currentItem.getType() == Material.PLAYER_HEAD) { - String character = stripFormatting(currentItem.getItemMeta().getDisplayName()); - if (character.isEmpty()) { - character = " "; - } - value += isCaps ? character.toUpperCase() : character.toLowerCase(); - config.set(path, value); - config.set("dirty", true); - } - // re-load the ui (refresh) - player.openInventory(createEditMenuInternal(configName, path, page, isCaps)); - } - return true; - } - - @Override - public Inventory createEditMenu(String configName, String path, int page) { - return createEditMenuInternal(configName, path, page, false); - } - - private Inventory createEditMenuInternal(String configName, String path, int page, boolean isCaps) { - YmlConfiguration config = FileUtil.getYmlConfiguration(configName); - if (!config.isString(path)) { - return null; - } - String value = config.getString(path, ""); - Inventory menu = Bukkit.createInventory(new UltimateHolder(null, getTitle(), MenuType.DEFAULT), 9 * 6, getTitle()); - setCharacters(menu, value, keyboard); - if (isCaps) { - setCharacters(menu, value, capslockOverlay); - } - ItemStack returnItem = menu.getItem(0).clone(); - ItemMeta meta = returnItem.getItemMeta(); - meta.setLore(Arrays.asList(configName, path, tr("\u00a77Page {0}", page))); - returnItem.setItemMeta(meta); - menu.setItem(0, returnItem); - - ItemStack capsItem = isCaps ? capsOn.clone() : capsOff.clone(); - ItemMeta itemMeta = capsItem.getItemMeta(); - itemMeta.setLore(Arrays.asList(value)); - capsItem.setItemMeta(itemMeta); - menu.setItem(capsIndex, capsItem); - - return menu; - } - - private void setCharacters(Inventory menu, String value, ArrayList itemList) { - int index = 0; - for (ItemStack item : itemList) { - if (item != null) { - ItemStack clone = item.clone(); - ItemMeta itemMeta = clone.getItemMeta(); - itemMeta.setLore(Arrays.asList(value)); - clone.setItemMeta(itemMeta); - menu.setItem(index, clone); - } - index++; - } - } - - private String getTitle() { - return tr("Config:") + " " + tr("\u00a79Text Editor"); - } -} diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/mojang/MojangAPI.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/mojang/MojangAPI.java deleted file mode 100644 index ffa9b9314..000000000 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/mojang/MojangAPI.java +++ /dev/null @@ -1,219 +0,0 @@ -/** - * Features include: - *

- *     * Mojang API auto-throttling when receiving HTTP 429 (TOO MANY REQUESTS)
- *     * Incremental consumer strategy (consume each response, instead of blocking for everything).
- * 
- * - * Inspired by https://gist.github.com/evilmidget38/26d70114b834f71fb3b4 - */ -package us.talabrek.ultimateskyblock.mojang; - - -import org.bukkit.Bukkit; -import org.bukkit.OfflinePlayer; -import org.json.simple.JSONArray; -import org.json.simple.JSONObject; -import org.json.simple.parser.JSONParser; -import dk.lockfuglsang.minecraft.util.TimeUtil; -import us.talabrek.ultimateskyblock.util.UUIDUtil; - -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.net.HttpURLConnection; -import java.net.URL; -import java.text.MessageFormat; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.UUID; -import java.util.logging.Logger; - -import static dk.lockfuglsang.minecraft.po.I18nUtil.tr; - -public class MojangAPI { - private static final Logger log = Logger.getLogger(MojangAPI.class.getName()); - private static final int PROFILES_PER_REQUEST = 100; - private static final String PROFILE_URL = "https://api.mojang.com/profiles/minecraft"; - private static final String NAME_URL = "https://api.mojang.com/users/profiles/minecraft/{0}?at=0"; - private static final int MAX_RETRIES = 3; - // Mojang API has a cap at 600 requests per 10 minutes (1 per second). - private static final long THROTTLE_SUCCESS = 200L; - private static final long THROTTLE_CONSERVATIVE = 1000L; - private static final long THROTTLE_FAILURE = 3000L; - private static final int BAD_REQUEST = 400; - private static final int TOO_MANY_REQUESTS = 429; - private static final long FAILURE_WINDOW = 60000; - - private static long tStart = 0; - private static long numRequests = 0; - - private final JSONParser jsonParser = new JSONParser(); - - private int failuresInRow = 0; - private long lastFailure = 0; - - public MojangAPI() { - } - - public void fetchUUIDs(List names, NameUUIDConsumer consumer, ProgressCallback callback) { - if (consumer == null) { - throw new IllegalArgumentException("consumer cannot be null"); - } - if (isOnlineMode()) { - try { - fetchCurrent(names, consumer, callback); - callback.complete(true); - } catch (Exception e) { - callback.error("" + e); - callback.complete(false); - } - } else { - fetchOfflineMode(names, consumer, callback); - } - } - - private boolean isOnlineMode() { - return Bukkit.getOnlineMode(); - } - - private void fetchOfflineMode(List names, NameUUIDConsumer consumer, ProgressCallback callback) { - int failed = 0; - int success = 0; - int total = names.size(); - for (String name : names) { - //noinspection deprecation - OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayer(name); - Map map = new HashMap<>(); - map.put(offlinePlayer.getName(), offlinePlayer.getUniqueId()); - consumer.success(map); - success++; - if (callback != null) { - callback.progress(success+failed, failed, total, "OfflineMode"); - } - } - if (callback != null) { - callback.complete(true); - } - } - - private void fetchCurrent(List names, NameUUIDConsumer consumer, ProgressCallback callback) throws Exception { - int requests = (int) Math.ceil(names.size() / PROFILES_PER_REQUEST); - int failed = 0; - for (int i = 0; i < requests; i++) { - int fromIndex = i * PROFILES_PER_REQUEST; - int toIndex = Math.min((i + 1) * 100, names.size()); - List segment = names.subList(fromIndex, toIndex); - HttpURLConnection connection = createPostConnection(); - String body = JSONArray.toJSONString(segment); - writeBody(connection, body); - int responseCode = connection.getResponseCode(); - if (responseCode == HttpURLConnection.HTTP_OK) { - Map tempMap = new HashMap<>(); - JSONArray array = (JSONArray) jsonParser.parse(new InputStreamReader(connection.getInputStream())); - for (Object profile : array) { - JSONObject jsonProfile = (JSONObject) profile; - String id = (String) jsonProfile.get("id"); - String name = (String) jsonProfile.get("name"); - UUID uuid = getUUID(id); - tempMap.put(name, uuid); - } - int missing = segment.size() - tempMap.size(); - consumer.success(tempMap); - if (missing > 0) { - List unknown = new ArrayList<>(segment); - unknown.removeAll(tempMap.keySet()); - consumer.unknown(unknown); - } - if (callback != null) { - failed += missing; - callback.progress(toIndex, failed, names.size(), "Online"); - } - } else if (responseCode == TOO_MANY_REQUESTS) { - i--; - } else if (responseCode == BAD_REQUEST) { - if (callback != null) { - callback.error(connection.getResponseMessage()); - } - if (consumer != null) { - consumer.unknown(segment); - } - log.warning(connection.getResponseMessage() + "\nBODY:\n" + body); - } else { - i--; // retry segment - if (callback != null) { - callback.error(connection.getResponseMessage()); - } - } - throttle(responseCode, i != requests - 1, callback); - } - } - - private void throttle(int responseCode, boolean hasMore, ProgressCallback callback) throws InterruptedException { - if (!hasMore) { - return; - } - long now = System.currentTimeMillis(); - if (responseCode == TOO_MANY_REQUESTS) { - lastFailure = now; - failuresInRow++; - long throttle = THROTTLE_FAILURE*failuresInRow; - callback.error(tr("Too many requests for Mojangs API ({0} within {1}), sleeping {2}", - numRequests, - TimeUtil.millisAsString(now -tStart), - TimeUtil.millisAsShort(throttle))); - Thread.sleep(throttle); - } else { - if (lastFailure > now - FAILURE_WINDOW) { - Thread.sleep(THROTTLE_CONSERVATIVE); - } else { - Thread.sleep(THROTTLE_SUCCESS); - } - failuresInRow = 0; - } - } - - private static HttpURLConnection createGetConnection(String name) throws IOException { - URL url = new URL(MessageFormat.format(NAME_URL, name)); - stat(); - HttpURLConnection connection = (HttpURLConnection) url.openConnection(); - connection.setRequestMethod("GET"); - connection.setRequestProperty("Content-Type", "application/json"); - connection.setUseCaches(false); - connection.setDoInput(true); - connection.setDoOutput(false); - return connection; - } - - private static HttpURLConnection createPostConnection() throws Exception { - URL url = new URL(PROFILE_URL); - stat(); - HttpURLConnection connection = (HttpURLConnection) url.openConnection(); - connection.setRequestMethod("POST"); - connection.setRequestProperty("Content-Type", "application/json"); - connection.setUseCaches(false); - connection.setDoInput(true); - connection.setDoOutput(true); - return connection; - } - - private static void stat() { - if (tStart == 0) { - tStart = System.currentTimeMillis(); - } - numRequests++; - } - - private static void writeBody(HttpURLConnection connection, String body) throws Exception { - OutputStream stream = connection.getOutputStream(); - stream.write(body.getBytes("UTF-8")); - stream.flush(); - stream.close(); - } - - private static UUID getUUID(String id) { - return UUIDUtil.fromString(id); - } -} \ No newline at end of file diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/mojang/NameUUIDConsumer.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/mojang/NameUUIDConsumer.java deleted file mode 100644 index 2c426c68b..000000000 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/mojang/NameUUIDConsumer.java +++ /dev/null @@ -1,31 +0,0 @@ -package us.talabrek.ultimateskyblock.mojang; - -import java.util.List; -import java.util.Map; -import java.util.UUID; - -/** - * Consumer for Mojang API queries for Name-UUID pairs. - * This allows for async. querying, and updating/caching of large data-sets. - */ -public interface NameUUIDConsumer { - /** - * Map from name to UUID - * @param names Map from name to UUID - */ - void success(Map names); - - /** - * A player that was renamed. - * @param oldName The old-name (the one queried). - * @param newName The new-name (the current). - * @param id The UUID. - */ - void renamed(String oldName, String newName, UUID id); - - /** - * List of names unknown to Mojang. - * @param unknownNames List of names unknown to Mojang. - */ - void unknown(List unknownNames); -} diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/mojang/ProgressCallback.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/mojang/ProgressCallback.java deleted file mode 100644 index cfa7cddc5..000000000 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/mojang/ProgressCallback.java +++ /dev/null @@ -1,10 +0,0 @@ -package us.talabrek.ultimateskyblock.mojang; - -/** - * A simple progress callback interface. - */ -public interface ProgressCallback { - void progress(int progress, int failed, int total, String message); - void complete(boolean success); - void error(String message); -} diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/player/NotificationManager.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/player/NotificationManager.java new file mode 100644 index 000000000..3954f6666 --- /dev/null +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/player/NotificationManager.java @@ -0,0 +1,55 @@ +package us.talabrek.ultimateskyblock.player; + +import com.google.inject.Inject; +import net.kyori.adventure.platform.bukkit.BukkitAudiences; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; +import org.bukkit.entity.Player; +import org.bukkit.plugin.Plugin; +import org.jetbrains.annotations.NotNull; + +public class NotificationManager { + private final BukkitAudiences audiences; + private LegacyComponentSerializer legacySerializer; + + @Inject + public NotificationManager(Plugin plugin) { + audiences = BukkitAudiences.create(plugin); + } + + /** + * Gets a {@link LegacyComponentSerializer} configured for uSkyBlock's (translatable) messages. + * + * @return LegacyComponentSerializer configured for uSkyblock's (translatable) messages. + */ + public @NotNull LegacyComponentSerializer getLegacySerializer() { + if (legacySerializer == null) { + legacySerializer = LegacyComponentSerializer.builder().character('\u00a7').build(); + } + return legacySerializer; + } + + /** + * Sends the given {@link String} as message to the {@link Player}'s ActionBar. + * + * @param player Player to send the given message to + * @param message Message to send to the given player + */ + public void sendActionBar(@NotNull Player player, @NotNull String message) { + sendActionBar(player, getLegacySerializer().deserialize(message)); + } + + /** + * Sends the given {@link Component} as message to the {@link Player}'s ActionBar. + * + * @param player Player to send the given message to + * @param component Component to send to the given player + */ + public void sendActionBar(@NotNull Player player, @NotNull Component component) { + audiences.player(player).sendActionBar(component); + } + + public void shutdown() { + audiences.close(); + } +} diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/player/PatienceTester.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/player/PatienceTester.java index 9e35de074..58c9e673a 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/player/PatienceTester.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/player/PatienceTester.java @@ -5,40 +5,45 @@ import org.bukkit.metadata.MetadataValue; import us.talabrek.ultimateskyblock.uSkyBlock; +import java.time.Duration; +import java.time.Instant; import java.util.List; import static dk.lockfuglsang.minecraft.po.I18nUtil.tr; public class PatienceTester { - private static final long maxRunning = 30000; + + private static final Duration COOLDOWN = Duration.ofSeconds(30); public static boolean isRunning(Player player, String key) { if (player.hasMetadata(key)) { List metadata = player.getMetadata(key); - MetadataValue metadataValue = metadata.size() > 0 ? metadata.get(0) : null; - if (metadataValue == null || metadataValue.asLong() < System.currentTimeMillis()) { + MetadataValue metadataValue = !metadata.isEmpty() ? metadata.getFirst() : null; + Instant cooldownEnd = metadataValue != null ? (Instant) metadataValue.value() : null; + if (cooldownEnd == null || Instant.now().isAfter(cooldownEnd)) { player.removeMetadata(key, uSkyBlock.getInstance()); return false; + } else { + player.sendMessage(getMessage()); + return true; } - player.sendMessage(getMessage()); - return true; } return false; } private static String getMessage() { - String[] messages = new String[] { - tr("\u00a79Hold your horses! You have to be patient..."), - tr("\u00a79Not really patient, are you?"), - tr("\u00a79Be patient, young padawan"), - tr("\u00a79Patience you MUST have, young padawan"), - tr("\u00a79The two most powerful warriors are patience and time."), + String[] messages = new String[]{ + tr("\u00a79Hold your horses! You have to be patient..."), + tr("\u00a79Not really patient, are you?"), + tr("\u00a79Be patient, young padawan"), + tr("\u00a79Patience you MUST have, young padawan"), + tr("\u00a79The two most powerful warriors are patience and time."), }; - return messages[(int)Math.floor(Math.random() * messages.length)]; + return messages[(int) Math.floor(Math.random() * messages.length)]; } public static void startRunning(Player player, String key) { - player.setMetadata(key, new FixedMetadataValue(uSkyBlock.getInstance(), System.currentTimeMillis() + maxRunning)); + player.setMetadata(key, new FixedMetadataValue(uSkyBlock.getInstance(), Instant.now().plus(COOLDOWN))); } public static void stopRunning(Player player, String key) { diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/player/Perk.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/player/Perk.java index 2e044d53a..ea75e27cb 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/player/Perk.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/player/Perk.java @@ -1,16 +1,10 @@ package us.talabrek.ultimateskyblock.player; +import dk.lockfuglsang.minecraft.util.ItemStackUtil; import org.bukkit.Material; import org.bukkit.inventory.ItemStack; -import dk.lockfuglsang.minecraft.util.ItemStackUtil; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; +import java.util.*; /** * Perk is an immutable object holding all the perks, @@ -23,7 +17,7 @@ public class Perk { private final double rewBonus; private final double hungerReduction; private final Set schematics; - private Map blockLimits; + private final Map blockLimits; private final int villagers; private final int golems; @@ -37,15 +31,15 @@ public class Perk { double hungerReduction, List schematics, Map blockLimits) { - this.maxPartySize = maxPartySize >= 0 ? maxPartySize : 0; - this.animals = animals >= 0 ? animals : 0; - this.monsters = monsters >= 0 ? monsters : 0; - this.villagers = villagers >= 0 ? villagers : 0; - this.golems = golems >= 0 ? golems : 0; - this.extraItems = extraItems != null ? extraItems : Collections.emptyList(); + this.maxPartySize = Math.max(maxPartySize, 0); + this.animals = Math.max(animals, 0); + this.monsters = Math.max(monsters, 0); + this.villagers = Math.max(villagers, 0); + this.golems = Math.max(golems, 0); + this.extraItems = extraItems != null ? extraItems : Collections.emptyList(); this.rewBonus = rewBonus >= 0 ? rewBonus : 0; this.hungerReduction = hungerReduction >= 0 && hungerReduction <= 1 ? hungerReduction : 0; - this.schematics = schematics != null ? new HashSet<>(schematics) : Collections.emptySet(); + this.schematics = schematics != null ? new HashSet<>(schematics) : Collections.emptySet(); this.blockLimits = blockLimits != null ? new HashMap<>(blockLimits) : Collections.emptyMap(); } @@ -70,10 +64,12 @@ public int getGolems() { } public List getExtraItems() { - return extraItems; + return ItemStackUtil.clone(extraItems); } - public Map getBlockLimits() { return blockLimits; } + public Map getBlockLimits() { + return blockLimits; + } public double getRewBonus() { return rewBonus; @@ -95,29 +91,28 @@ public Perk combine(Perk other) { schems.addAll(this.schematics); schems.addAll(other.getSchematics()); return new Perk( - items, - Math.max(maxPartySize, other.getMaxPartySize()), - Math.max(animals, other.getAnimals()), - Math.max(monsters, other.getMonsters()), - Math.max(villagers, other.getVillagers()), - Math.max(golems, other.getGolems()), - Math.max(rewBonus, other.getRewBonus()), - Math.max(hungerReduction, other.getHungerReduction()), - schems, null); + items, + Math.max(maxPartySize, other.getMaxPartySize()), + Math.max(animals, other.getAnimals()), + Math.max(monsters, other.getMonsters()), + Math.max(villagers, other.getVillagers()), + Math.max(golems, other.getGolems()), + Math.max(rewBonus, other.getRewBonus()), + Math.max(hungerReduction, other.getHungerReduction()), + schems, null); } @Override public String toString() { - return (maxPartySize > 0 ? "maxPartySize:" + maxPartySize + "\n" : "") + - (animals > 0 ? "animals:" + animals +"\n" : "") + - (monsters > 0 ? "monsters:" + monsters +"\n" : "") + - (villagers > 0 ? "villagers:" + villagers + "\n" : "") + - (golems > 0 ? "golems:" + golems + "\n" : "") + - (!extraItems.isEmpty() ? "extraItems:" + ItemStackUtil.asShortString(extraItems) +"\n" : "") + - (rewBonus > 0 ? "rewBonus:" + rewBonus +"\n" : "") + - (hungerReduction > 0 ? "hungerReduction:" + hungerReduction +"\n" : "") + - (!schematics.isEmpty() ? "schematics:" + schematics +"\n" : "") + - (!blockLimits.isEmpty() ? "blockLimits:" + blockLimits +"\n" : "") - ; + return (maxPartySize > 0 ? "maxPartySize:" + maxPartySize + "\n" : "") + + (animals > 0 ? "animals:" + animals + "\n" : "") + + (monsters > 0 ? "monsters:" + monsters + "\n" : "") + + (villagers > 0 ? "villagers:" + villagers + "\n" : "") + + (golems > 0 ? "golems:" + golems + "\n" : "") + + (!extraItems.isEmpty() ? "extraItems:" + ItemStackUtil.asShortString(extraItems) + "\n" : "") + + (rewBonus > 0 ? "rewBonus:" + rewBonus + "\n" : "") + + (hungerReduction > 0 ? "hungerReduction:" + hungerReduction + "\n" : "") + + (!schematics.isEmpty() ? "schematics:" + schematics + "\n" : "") + + (!blockLimits.isEmpty() ? "blockLimits:" + blockLimits + "\n" : ""); } -} \ No newline at end of file +} diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/player/PerkLogic.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/player/PerkLogic.java index 90271cbc9..a0a49ac22 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/player/PerkLogic.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/player/PerkLogic.java @@ -1,12 +1,16 @@ package us.talabrek.ultimateskyblock.player; +import com.google.inject.Inject; +import com.google.inject.Singleton; +import dk.lockfuglsang.minecraft.util.ItemStackUtil; +import org.bukkit.Material; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; +import us.talabrek.ultimateskyblock.PluginConfig; import us.talabrek.ultimateskyblock.Settings; import us.talabrek.ultimateskyblock.island.IslandGenerator; -import us.talabrek.ultimateskyblock.uSkyBlock; -import dk.lockfuglsang.minecraft.util.ItemStackUtil; import java.util.Arrays; import java.util.Collections; @@ -19,26 +23,29 @@ /** * Responsible for calculating player specific perks based on permissions. */ +@Singleton public class PerkLogic { - private final uSkyBlock plugin; private final Perk defaultPerk; - private Map donorPerks; - private Map islandPerks; - - public PerkLogic(uSkyBlock plugin, IslandGenerator islandGenerator) { - this.plugin = plugin; + private final Map donorPerks; + private final Map islandPerks; + + @Inject + public PerkLogic( + @NotNull IslandGenerator islandGenerator, + @NotNull PluginConfig config + ) { defaultPerk = new Perk(Collections.emptyList(), Settings.general_maxPartySize, - plugin.getConfig().getInt("options.island.spawn-limits.animals", 30), - plugin.getConfig().getInt("options.island.spawn-limits.monsters", 50), - plugin.getConfig().getInt("options.island.spawn-limits.villagers", 16), - plugin.getConfig().getInt("options.island.spawn-limits.golems", 4), - 0, - 0, - null, null); + config.getYamlConfig().getInt("options.island.spawn-limits.animals", 30), + config.getYamlConfig().getInt("options.island.spawn-limits.monsters", 50), + config.getYamlConfig().getInt("options.island.spawn-limits.villagers", 16), + config.getYamlConfig().getInt("options.island.spawn-limits.golems", 4), + 0, + 0, + null, null); donorPerks = new ConcurrentHashMap<>(); - addDonorPerks(null, plugin.getConfig().getConfigurationSection("donor-perks")); - addExtraPermissionPerks(plugin.getConfig().getConfigurationSection("options.island.extraPermissions")); - addPartyPermissionPerks(null, plugin.getConfig().getConfigurationSection("options.party.maxPartyPermissions")); + addDonorPerks(null, config.getYamlConfig().getConfigurationSection("donor-perks")); + addExtraPermissionPerks(config.getYamlConfig().getConfigurationSection("options.island.extraPermissions")); + addPartyPermissionPerks(null, config.getYamlConfig().getConfigurationSection("options.party.maxPartyPermissions")); addHungerPerms(); addDonorRewardPerks(); List schemeNames = islandGenerator.getSchemeNames(); @@ -46,37 +53,37 @@ public PerkLogic(uSkyBlock plugin, IslandGenerator islandGenerator) { islandPerks = new ConcurrentHashMap<>(); - ConfigurationSection islandSchemes = plugin.getConfig().getConfigurationSection("island-schemes"); + ConfigurationSection islandSchemes = config.getYamlConfig().getConfigurationSection("island-schemes"); if (islandSchemes != null) { for (String schemeName : islandSchemes.getKeys(false)) { - ConfigurationSection config = islandSchemes.getConfigurationSection(schemeName); - String perm = config.getString("permission", "usb.schematic." + schemeName); + ConfigurationSection configSection = islandSchemes.getConfigurationSection(schemeName); + String perm = configSection.getString("permission", "usb.schematic." + schemeName); Perk perk = new PerkBuilder() - .schematics(schemeName) - .maxPartySize(config.getInt("maxPartySize", 0)) - .animals(config.getInt("animals", 0)) - .monsters(config.getInt("monsters", 0)) - .villagers(config.getInt("villagers", 0)) - .golems(config.getInt("golems", 0)) - .rewBonus(config.getInt("rewardBonus", 0)) - .hungerReduction(config.getInt("hungerReduction", 0)) - .extraItems(ItemStackUtil.createItemList(config.getStringList("extraItems"))) - .build(); + .schematics(schemeName) + .maxPartySize(configSection.getInt("maxPartySize", 0)) + .animals(configSection.getInt("animals", 0)) + .monsters(configSection.getInt("monsters", 0)) + .villagers(configSection.getInt("villagers", 0)) + .golems(configSection.getInt("golems", 0)) + .rewBonus(configSection.getInt("rewardBonus", 0)) + .hungerReduction(configSection.getInt("hungerReduction", 0)) + .extraItems(ItemStackUtil.createItemList(configSection.getStringList("extraItems"))) + .build(); ItemStack itemStack = ItemStackUtil.createItemStack( - config.getString("displayItem", "SAPLING"), - schemeName, - config.getString("description", null) + configSection.getString("displayItem", Material.OAK_SAPLING.toString()), + schemeName, + configSection.getString("description", null) ); islandPerks.put(schemeName, new IslandPerk(schemeName, perm, itemStack, perk, - config.getDouble("scoreMultiply", 1d), config.getDouble("scoreOffset", 0d))); + configSection.getDouble("scoreMultiply", 1d), configSection.getDouble("scoreOffset", 0d))); } } for (String schemeName : schemeNames) { Perk perk = new PerkBuilder(defaultPerk).schematics(schemeName).build(); if (!islandPerks.containsKey(schemeName)) { islandPerks.put(schemeName, new IslandPerk(schemeName, "usb.schematic." + schemeName, - ItemStackUtil.createItemStack("SAPLING", schemeName, null), perk, - 1d, 0d)); + ItemStackUtil.createItemStack(Material.OAK_SAPLING.toString(), schemeName, null), perk, + 1d, 0d)); } } } @@ -104,7 +111,7 @@ public IslandPerk getIslandPerk(String schemeName) { return islandPerks.get(schemeName); } return new IslandPerk(schemeName, "usb.schematic." + schemeName, - ItemStackUtil.createItemStack("GRASS", schemeName, null), defaultPerk); + ItemStackUtil.createItemStack(Material.GRASS_BLOCK.toString(), schemeName, null), defaultPerk); } private Perk createPerk(Player player) { @@ -127,15 +134,15 @@ private void addDonorPerks(String perm, ConfigurationSection config) { } else { // Read leaf donorPerks.put(perm, new Perk( - ItemStackUtil.createItemList(config.getStringList("extraItems")), - config.getInt("maxPartySize", defaultPerk.getMaxPartySize()), - config.getInt("animals", defaultPerk.getAnimals()), - config.getInt("monsters", defaultPerk.getMonsters()), - config.getInt("villagers", defaultPerk.getVillagers()), - config.getInt("golems", defaultPerk.getGolems()), - config.getDouble("rewardBonus", defaultPerk.getRewBonus()), - config.getDouble("hungerReduction", defaultPerk.getHungerReduction()), - config.getStringList("schematics"), null)); + ItemStackUtil.createItemList(config.getStringList("extraItems")), + config.getInt("maxPartySize", defaultPerk.getMaxPartySize()), + config.getInt("animals", defaultPerk.getAnimals()), + config.getInt("monsters", defaultPerk.getMonsters()), + config.getInt("villagers", defaultPerk.getVillagers()), + config.getInt("golems", defaultPerk.getGolems()), + config.getDouble("rewardBonus", defaultPerk.getRewBonus()), + config.getDouble("hungerReduction", defaultPerk.getHungerReduction()), + config.getStringList("schematics"), null)); } } } @@ -149,20 +156,20 @@ private void addExtraPermissionPerks(ConfigurationSection config) { if (items != null && !items.isEmpty()) { String perm = "usb." + key; donorPerks.put(perm, new PerkBuilder(donorPerks.get(perm)) - .extraItems(items) - .build()); + .extraItems(items) + .build()); } } } private void addPartyPermissionPerks(String perm, ConfigurationSection config) { int[] values = {5, 6, 7, 8}; - String[] perms = {"usb.extra.partysize1","usb.extra.partysize2","usb.extra.partysize3","usb.extra.partysize"}; + String[] perms = {"usb.extra.partysize1", "usb.extra.partysize2", "usb.extra.partysize3", "usb.extra.partysize"}; for (int i = 0; i < values.length; i++) { donorPerks.put(perms[i], - new PerkBuilder(donorPerks.get(perms[i])) - .maxPartySize(values[i]) - .build()); + new PerkBuilder(donorPerks.get(perms[i])) + .maxPartySize(values[i]) + .build()); } if (config == null) { @@ -174,8 +181,8 @@ private void addPartyPermissionPerks(String perm, ConfigurationSection config) { } else if (config.isInt(key)) { // Read leaf donorPerks.put(perm, new PerkBuilder(donorPerks.get(perm)) - .maxPartySize(config.getInt(key, 0)) - .build()); + .maxPartySize(config.getInt(key, 0)) + .build()); } } } @@ -185,9 +192,9 @@ private void addHungerPerms() { String[] perms = {"usb.extra.hunger4", "usb.extra.hunger3", "usb.extra.hunger2", "usb.extra.hunger"}; for (int i = 0; i < values.length; i++) { donorPerks.put(perms[i], - new PerkBuilder(donorPerks.get(perms[i])) - .hungerReduction(values[i]) - .build()); + new PerkBuilder(donorPerks.get(perms[i])) + .hungerReduction(values[i]) + .build()); } } @@ -197,9 +204,9 @@ private void addDonorRewardPerks() { String[] perms = {"group.memberplus", "usb.donor.all", "usb.donor.25", "usb.donor.50", "usb.donor.75", "usb.donor.100"}; for (int i = 0; i < values.length; i++) { donorPerks.put(perms[i], - new PerkBuilder(donorPerks.get(perms[i])) - .rewBonus(values[i]) - .build()); + new PerkBuilder(donorPerks.get(perms[i])) + .rewBonus(values[i]) + .build()); } } @@ -210,8 +217,8 @@ private void addSchemePerks(List schemeNames) { for (String scheme : schemeNames) { String perm = "usb.schematic." + scheme; donorPerks.put(perm, new PerkBuilder(donorPerks.get(perm)) - .schematics(scheme) - .build()); + .schematics(scheme) + .build()); } } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/player/PlayerInfo.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/player/PlayerInfo.java index cb5c917a3..9b21f9089 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/player/PlayerInfo.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/player/PlayerInfo.java @@ -1,32 +1,38 @@ package us.talabrek.ultimateskyblock.player; import dk.lockfuglsang.minecraft.file.FileUtil; -import dk.lockfuglsang.minecraft.yml.YmlConfiguration; -import org.apache.commons.lang.Validate; +import dk.lockfuglsang.minecraft.util.TimeUtil; +import org.apache.commons.lang3.Validate; import org.bukkit.ChatColor; import org.bukkit.Location; import org.bukkit.OfflinePlayer; import org.bukkit.World; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import us.talabrek.ultimateskyblock.challenge.Challenge; import us.talabrek.ultimateskyblock.challenge.ChallengeCompletion; -import us.talabrek.ultimateskyblock.handler.VaultHandler; +import us.talabrek.ultimateskyblock.hook.permissions.PermissionsHook; import us.talabrek.ultimateskyblock.island.IslandInfo; import us.talabrek.ultimateskyblock.uSkyBlock; import us.talabrek.ultimateskyblock.util.LocationUtil; import us.talabrek.ultimateskyblock.util.LogUtil; +import us.talabrek.ultimateskyblock.util.Scheduler; import us.talabrek.ultimateskyblock.util.UUIDUtil; import us.talabrek.ultimateskyblock.uuid.PlayerDB; import java.io.File; import java.io.IOException; +import java.io.Serial; import java.io.Serializable; +import java.nio.file.Path; import java.util.ArrayList; import java.util.Collection; import java.util.List; +import java.util.Optional; import java.util.UUID; import java.util.logging.Level; import java.util.logging.Logger; @@ -36,9 +42,12 @@ public class PlayerInfo implements Serializable, us.talabrek.ultimateskyblock.api.PlayerInfo { private static final String CN = PlayerInfo.class.getName(); private static final Logger log = Logger.getLogger(CN); + @Serial private static final long serialVersionUID = 1L; private static final int YML_VERSION = 1; - private String playerName; + private final uSkyBlock plugin; + private final Scheduler scheduler; + private final String playerName; private String displayName; private UUID uuid; @@ -46,22 +55,26 @@ public class PlayerInfo implements Serializable, us.talabrek.ultimateskyblock.ap private Location homeLocation; - private volatile FileConfiguration playerData; - private File playerConfigFile; + private final FileConfiguration playerData; + private final File playerConfigFile; private boolean islandGenerating = false; private boolean dirty = false; - public PlayerInfo(String currentPlayerName, UUID playerUUID) { + public PlayerInfo(String currentPlayerName, UUID playerUUID, uSkyBlock plugin, Path playerDataDirectory) { + this.plugin = plugin; + this.scheduler = plugin.getScheduler(); this.uuid = playerUUID; this.playerName = currentPlayerName; // Prefer UUID over Name - playerConfigFile = new File(uSkyBlock.getInstance().directoryPlayers, UUIDUtil.asString(playerUUID) + ".yml"); - File nameFile = new File(uSkyBlock.getInstance().directoryPlayers, playerName + ".yml"); + // TODO: decouple serialization from player data. + // TODO: remove legacy player name support - all data should be converted by now. + playerConfigFile = playerDataDirectory.resolve(UUIDUtil.asString(playerUUID) + ".yml").toFile(); + File nameFile = playerDataDirectory.resolve(playerName + ".yml").toFile(); if (!playerConfigFile.exists() && nameFile.exists() && !currentPlayerName.equals(PlayerDB.UNKNOWN_PLAYER_NAME)) { nameFile.renameTo(playerConfigFile); } - playerData = new YmlConfiguration(); + playerData = new YamlConfiguration(); if (playerConfigFile.exists()) { FileUtil.readConfig(playerData, playerConfigFile); } @@ -112,6 +125,10 @@ public String getPlayerName() { return this.playerName; } + public UUID getPlayerId() { + return this.uuid; + } + public void setIslandLocation(final Location l) { this.islandLocation = l != null ? l.clone() : null; } @@ -159,14 +176,15 @@ public void setJoinParty(final Location l) { this.homeLocation = l != null ? l.clone() : null; } - public void completeChallenge(final String challenge, boolean silent) { - uSkyBlock.getInstance().getChallengeLogic().completeChallenge(this, challenge); + public void completeChallenge(Challenge challenge, boolean silent) { + uSkyBlock.getInstance().getChallengeLogic().completeChallenge(this, challenge.getName()); if (silent) { return; } IslandInfo island = getIslandInfo(); if (island != null) { - island.sendMessageToOnlineMembers(tr("\u00a79{0}\u00a7f has completed the \u00a79{1}\u00a7f challenge!", getPlayerName(), challenge)); + island.sendMessageToOnlineMembers(tr("\u00a79{0}\u00a7f has completed the \u00a79{1}\u00a7f challenge!", + getPlayerName(), challenge.getDisplayName())); } } @@ -211,11 +229,11 @@ private PlayerInfo loadPlayer() { this.displayName = playerData.getString("player.displayName", playerName); this.uuid = UUIDUtil.fromString(playerData.getString("player.uuid", null)); this.islandLocation = new Location(uSkyBlock.getInstance().getWorldManager().getWorld(), - playerData.getInt("player.islandX"), playerData.getInt("player.islandY"), playerData.getInt("player.islandZ")); + playerData.getInt("player.islandX"), playerData.getInt("player.islandY"), playerData.getInt("player.islandZ")); this.homeLocation = new Location(uSkyBlock.getInstance().getWorldManager().getWorld(), - playerData.getInt("player.homeX") + 0.5, playerData.getInt("player.homeY") + 0.2, playerData.getInt("player.homeZ") + 0.5, - (float) playerData.getDouble("player.homeYaw", 0.0), - (float) playerData.getDouble("player.homePitch", 0.0)); + playerData.getInt("player.homeX") + 0.5, playerData.getInt("player.homeY") + 0.2, playerData.getInt("player.homeZ") + 0.5, + (float) playerData.getDouble("player.homeYaw", 0.0), + (float) playerData.getDouble("player.homePitch", 0.0)); log.exiting(CN, "loadPlayer"); return this; @@ -294,9 +312,7 @@ public void saveToFile() { @Override public Collection getChallenges() { - Collection copy = new ArrayList<>(); - copy.addAll(uSkyBlock.getInstance().getChallengeLogic().getChallenges(this)); - return copy; + return new ArrayList<>(uSkyBlock.getInstance().getChallengeLogic().getChallenges(this)); } @Override @@ -394,11 +410,11 @@ public boolean isClearInventoryOnNextEntry() { public void onTeleport(final Player player) { if (isClearInventoryOnNextEntry()) { - uSkyBlock.getInstance().sync(() -> uSkyBlock.getInstance().clearPlayerInventory(player), 50); + scheduler.sync(() -> plugin.clearPlayerInventory(player), TimeUtil.ticksAsDuration(1)); } List pending = playerData.getStringList("pending-commands"); if (!pending.isEmpty()) { - uSkyBlock.getInstance().execCommands(player, pending); + plugin.execCommands(player, pending); playerData.set("pending-commands", null); save(); } @@ -432,13 +448,18 @@ public boolean addPermissions(@Nullable List perms) { if (perms == null || perms.isEmpty()) { return true; } + Player target = getPlayer(); - if (target != null && target.isOnline()) { + Optional hook = plugin.getHookManager().getPermissionsHook(); + + if (target != null && target.isOnline() && hook.isPresent()) { List permList = playerData.getStringList("player.perms"); + PermissionsHook pHook = hook.get(); + for (String perm : perms) { - if (!VaultHandler.hasPermission(target, perm)) { + if (!pHook.hasPermission(target, perm)) { permList.add(perm); - VaultHandler.addPermission(target, perm); + pHook.addPermission(target, perm); } } playerData.set("player.perms", permList); @@ -458,9 +479,11 @@ public void clearPerms(@NotNull Player target) { final List perms = playerData.getStringList("player.perms"); if (!perms.isEmpty()) { - for (String perm : perms) { - VaultHandler.removePermission(target, perm); - } + plugin.getHookManager().getPermissionsHook().ifPresent((hook) -> { + for (String perm : perms) { + hook.removePermission(target, perm); + } + }); playerData.set("player.perms", null); playerData.set("pending-permissions", null); save(); diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/player/PlayerLogic.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/player/PlayerLogic.java index 7253ef6f3..fe1fe457d 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/player/PlayerLogic.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/player/PlayerLogic.java @@ -4,15 +4,25 @@ import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; import com.google.common.cache.RemovalListener; -import com.google.common.cache.RemovalNotification; +import com.google.inject.Inject; +import com.google.inject.Singleton; import org.bukkit.entity.Player; import org.bukkit.scheduler.BukkitTask; +import org.jetbrains.annotations.NotNull; +import us.talabrek.ultimateskyblock.PluginConfig; +import us.talabrek.ultimateskyblock.bootstrap.PluginDataDir; import us.talabrek.ultimateskyblock.handler.WorldGuardHandler; import us.talabrek.ultimateskyblock.island.IslandInfo; +import us.talabrek.ultimateskyblock.island.IslandLogic; import us.talabrek.ultimateskyblock.uSkyBlock; -import dk.lockfuglsang.minecraft.util.TimeUtil; +import us.talabrek.ultimateskyblock.util.Scheduler; import us.talabrek.ultimateskyblock.uuid.PlayerDB; +import us.talabrek.ultimateskyblock.world.WorldManager; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.time.Duration; import java.util.UUID; import java.util.concurrent.ExecutionException; import java.util.logging.Level; @@ -23,44 +33,70 @@ /** * Holds the active players */ +@Singleton public class PlayerLogic { - private static final Logger log = Logger.getLogger(PlayerLogic.class.getName()); - private static final PlayerInfo UNKNOWN_PLAYER = new PlayerInfo(PlayerDB.UNKNOWN_PLAYER_NAME, PlayerDB.UNKNOWN_PLAYER_UUID); private final LoadingCache playerCache; private final uSkyBlock plugin; private final BukkitTask saveTask; private final PlayerDB playerDB; - - public PlayerLogic(uSkyBlock plugin) { + private final PerkLogic perkLogic; + private final IslandLogic islandLogic; + private final WorldManager worldManager; + private final TeleportLogic teleportLogic; + private final Scheduler scheduler; + private final NotificationManager notificationManager; + private final Logger logger; + private final Path playerDataDirectory; + + @Inject + public PlayerLogic( + @NotNull uSkyBlock plugin, + @NotNull PluginConfig config, + @NotNull PlayerDB playerDB, + @NotNull Logger logger, + @NotNull PerkLogic perkLogic, + @NotNull IslandLogic islandLogic, + @NotNull WorldManager worldManager, + @NotNull TeleportLogic teleportLogic, + @NotNull Scheduler scheduler, + @NotNull NotificationManager notificationManager, + @NotNull @PluginDataDir Path pluginDataDir + ) { this.plugin = plugin; - playerDB = plugin.getPlayerDB(); - playerCache = CacheBuilder - .from(plugin.getConfig().getString("options.advanced.playerCache", "maximumSize=200,expireAfterWrite=15m,expireAfterAccess=10m")) - .removalListener(new RemovalListener() { - @Override - public void onRemoval(RemovalNotification removal) { - log.fine("Removing player-info for " + removal.getKey() + " from cache"); - PlayerInfo playerInfo = removal.getValue(); - if (playerInfo.isDirty()) { - playerInfo.saveToFile(); - } - } - }) - .build(new CacheLoader() { - @Override - public PlayerInfo load(UUID s) throws Exception { - log.fine("Loading player-info from " + s + " into cache!"); - return loadPlayerData(s); - } + this.playerDB = playerDB; + this.perkLogic = perkLogic; + this.islandLogic = islandLogic; + this.worldManager = worldManager; + this.teleportLogic = teleportLogic; + this.scheduler = scheduler; + this.notificationManager = notificationManager; + this.logger = logger; + this.playerDataDirectory = pluginDataDir.resolve("players"); + try { + Files.createDirectories(playerDataDirectory); + } catch (IOException e) { + logger.log(Level.SEVERE, "Failed to create player data directory", e); + } + + this.playerCache = CacheBuilder + .from(config.getYamlConfig().getString("options.advanced.playerCache", "maximumSize=200,expireAfterWrite=15m,expireAfterAccess=10m")) + .removalListener((RemovalListener) removal -> { + logger.fine("Removing player-info for " + removal.getKey() + " from cache"); + PlayerInfo playerInfo = removal.getValue(); + if (playerInfo.isDirty()) { + playerInfo.saveToFile(); + } + }) + .build(new CacheLoader<>() { + @Override + public @NotNull PlayerInfo load(@NotNull UUID s) { + logger.fine("Loading player-info from " + s + " into cache!"); + return loadPlayerData(s); } - ); - long every = TimeUtil.secondsAsMillis(plugin.getConfig().getInt("options.advanced.player.saveEvery", 2*60)); - saveTask = plugin.async(new Runnable() { - @Override - public void run() { - saveDirtyToFiles(); - } - }, every, every); + } + ); + Duration every = Duration.ofSeconds(plugin.getConfig().getInt("options.advanced.player.saveEvery", 2 * 60)); + this.saveTask = scheduler.async(this::saveDirtyToFiles, every, every); } private void saveDirtyToFiles() { @@ -73,12 +109,16 @@ private void saveDirtyToFiles() { } private PlayerInfo loadPlayerData(UUID uuid) { - if (UNKNOWN_PLAYER.getUniqueId().equals(uuid)) { - return UNKNOWN_PLAYER; + if (PlayerDB.UNKNOWN_PLAYER_UUID.equals(uuid)) { + return loadUnknownPlayer(); } return loadPlayerData(uuid, playerDB.getName(uuid)); } + private PlayerInfo loadUnknownPlayer() { + return new PlayerInfo(PlayerDB.UNKNOWN_PLAYER_NAME, PlayerDB.UNKNOWN_PLAYER_UUID, plugin, playerDataDirectory); + } + private PlayerInfo loadPlayerData(UUID playerUUID, String playerName) { if (playerUUID == null) { playerUUID = PlayerDB.UNKNOWN_PLAYER_UUID; @@ -86,47 +126,40 @@ private PlayerInfo loadPlayerData(UUID playerUUID, String playerName) { if (playerName == null) { playerName = "__UNKNOWN__"; } - log.log(Level.FINER, "Loading player data for " + playerUUID + "/" + playerName); + logger.log(Level.FINER, "Loading player data for " + playerUUID + "/" + playerName); - final PlayerInfo playerInfo = new PlayerInfo(playerName, playerUUID); + final PlayerInfo playerInfo = new PlayerInfo(playerName, playerUUID, plugin, playerDataDirectory); final Player onlinePlayer = uSkyBlock.getInstance().getPlayerDB().getPlayer(playerUUID); if (onlinePlayer != null && onlinePlayer.isOnline()) { if (playerInfo.getHasIsland()) { IslandInfo islandInfo = plugin.getIslandInfo(playerInfo); if (islandInfo != null) { - islandInfo.updatePermissionPerks(onlinePlayer, plugin.getPerkLogic().getPerk(onlinePlayer)); + islandInfo.updatePermissionPerks(onlinePlayer, perkLogic.getPerk(onlinePlayer)); } } - plugin.sync(new Runnable() { - @Override - public void run() { - if (playerInfo.getHasIsland()) { - WorldGuardHandler.protectIsland(onlinePlayer, playerInfo); - plugin.getIslandLogic().clearFlatland(onlinePlayer, playerInfo.getIslandLocation(), 400); - } - if (plugin.getWorldManager().isSkyAssociatedWorld(onlinePlayer.getWorld()) && !plugin.playerIsOnIsland(onlinePlayer)) { - // Check if banned - String islandName = WorldGuardHandler.getIslandNameAt(onlinePlayer.getLocation()); - IslandInfo islandInfo = plugin.getIslandInfo(islandName); - if (islandInfo != null && islandInfo.isBanned(onlinePlayer)) { - onlinePlayer.sendMessage(new String[]{ - tr("\u00a7eYou have been §cBANNED§e from {0}§e''s island.", islandInfo.getLeader()), - tr("\u00a7eSending you to spawn.") - }); - plugin.getTeleportLogic().spawnTeleport(onlinePlayer, true); - } else if (islandInfo != null && islandInfo.isLocked()) { - if (!onlinePlayer.hasPermission("usb.mod.bypassprotection")) { - onlinePlayer.sendMessage(new String[]{ - tr("\u00a7eThe island has been §cLOCKED§e.", islandInfo.getLeader()), - tr("\u00a7eSending you to spawn.") - }); - plugin.getTeleportLogic().spawnTeleport(onlinePlayer, true); - } - } + scheduler.sync(() -> { + if (playerInfo.getHasIsland()) { + WorldGuardHandler.protectIsland(onlinePlayer, playerInfo); + islandLogic.clearFlatland(onlinePlayer, playerInfo.getIslandLocation(), Duration.ofSeconds(20)); + } + if (worldManager.isSkyAssociatedWorld(onlinePlayer.getWorld()) && !plugin.playerIsOnIsland(onlinePlayer)) { + // Check if banned + String islandName = WorldGuardHandler.getIslandNameAt(onlinePlayer.getLocation()); + IslandInfo islandInfo = plugin.getIslandInfo(islandName); + if (islandInfo != null && islandInfo.isBanned(onlinePlayer)) { + onlinePlayer.sendMessage(tr("\u00a7eYou have been §cBANNED§e from {0}§e''s island.", islandInfo.getLeader()), + tr("\u00a7eSending you to spawn.")); + teleportLogic.spawnTeleport(onlinePlayer, true); + } else if (islandInfo != null && islandInfo.isLocked()) { + if (!onlinePlayer.hasPermission("usb.mod.bypassprotection")) { + onlinePlayer.sendMessage(tr("\u00a7eThe island has been §cLOCKED§e.", islandInfo.getLeader()), + tr("\u00a7eSending you to spawn.")); + teleportLogic.spawnTeleport(onlinePlayer, true); } } } + } ); } return playerInfo; @@ -153,21 +186,17 @@ public PlayerInfo getPlayerInfo(UUID uuid) { } public void loadPlayerDataAsync(final Player player) { - plugin.async(new Runnable() { - @Override - public void run() { - playerCache.refresh(player.getUniqueId()); - } - }); + scheduler.async(() -> playerCache.refresh(player.getUniqueId())); } public void removeActivePlayer(PlayerInfo pi) { - playerCache.invalidate(pi.getPlayerName()); + playerCache.invalidate(pi.getPlayerId()); } public void shutdown() { saveTask.cancel(); flushCache(); + notificationManager.shutdown(); } public long flushCache() { @@ -177,7 +206,14 @@ public long flushCache() { } public int getSize() { - String[] list = plugin.directoryPlayers.list(); - return list != null ? list.length : 0; + try (var stream = Files.list(playerDataDirectory)) { + return (int) stream.count(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public @NotNull NotificationManager getNotificationManager() { + return notificationManager; } } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/player/PlayerNotifier.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/player/PlayerNotifier.java index 7fb1e4b53..82c50c703 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/player/PlayerNotifier.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/player/PlayerNotifier.java @@ -3,9 +3,14 @@ import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; -import org.bukkit.configuration.file.FileConfiguration; +import com.google.inject.Inject; +import com.google.inject.Singleton; import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import us.talabrek.ultimateskyblock.PluginConfig; +import java.time.Duration; +import java.time.Instant; import java.util.UUID; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; @@ -13,31 +18,33 @@ /** * Notifier that tries to minimize spam. */ +@Singleton public class PlayerNotifier { - private final long maxSpam; + private final Duration spawnThreshold; private final LoadingCache cache = CacheBuilder - .newBuilder() - .expireAfterAccess(10, TimeUnit.SECONDS) - .maximumSize(500) - .build( - new CacheLoader() { - @Override - public NotifyMessage load(UUID uuid) throws Exception { - return new NotifyMessage(null, 0); - } - } - ); + .newBuilder() + .expireAfterAccess(10, TimeUnit.SECONDS) + .maximumSize(500) + .build( + new CacheLoader<>() { + @Override + public @NotNull NotifyMessage load(@NotNull UUID uuid) { + return new NotifyMessage(null, Instant.MIN); + } + } + ); - public PlayerNotifier(FileConfiguration config) { - maxSpam = config.getInt("general.maxSpam", 3000); // every 3 seconds. + @Inject + public PlayerNotifier(@NotNull PluginConfig config) { + spawnThreshold = Duration.ofMillis(config.getYamlConfig().getInt("general.maxSpam", 3000)); // every 3 seconds. } public synchronized void notifyPlayer(Player player, String message) { UUID uuid = player.getUniqueId(); try { NotifyMessage last = cache.get(uuid); - long now = System.currentTimeMillis(); - if (now >= last.getTime() + maxSpam || !message.equals(last.getMessage())) { + Instant now = Instant.now(); + if (now.isAfter(last.time().plus(spawnThreshold)) || !message.equals(last.message())) { cache.put(uuid, new NotifyMessage(message, now)); player.sendMessage("\u00a7e" + message); } @@ -46,21 +53,6 @@ public synchronized void notifyPlayer(Player player, String message) { } } - private static class NotifyMessage { - private final String message; - private final long time; - - private NotifyMessage(String message, long time) { - this.message = message; - this.time = time; - } - - public String getMessage() { - return message; - } - - public long getTime() { - return time; - } + private record NotifyMessage(String message, Instant time) { } } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/player/TeleportLogic.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/player/TeleportLogic.java index 00bb9c137..fbc963a49 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/player/TeleportLogic.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/player/TeleportLogic.java @@ -1,7 +1,9 @@ package us.talabrek.ultimateskyblock.player; +import com.google.inject.Inject; +import com.google.inject.Singleton; import io.papermc.lib.PaperLib; -import org.apache.commons.lang.Validate; +import org.apache.commons.lang3.Validate; import org.bukkit.Location; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; @@ -14,8 +16,10 @@ import us.talabrek.ultimateskyblock.Settings; import us.talabrek.ultimateskyblock.uSkyBlock; import us.talabrek.ultimateskyblock.util.LocationUtil; -import dk.lockfuglsang.minecraft.util.TimeUtil; +import us.talabrek.ultimateskyblock.util.Scheduler; +import us.talabrek.ultimateskyblock.world.WorldManager; +import java.time.Duration; import java.util.Map; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; @@ -27,31 +31,43 @@ /** * Responsible for teleporting (and cancelling teleporting) of players. */ +@Singleton public class TeleportLogic implements Listener { - private static final Logger log = Logger.getLogger(TeleportLogic.class.getName()); - + private final Logger logger; private final uSkyBlock plugin; - private final int teleportDelay; + private final WorldManager worldManager; + private final Scheduler scheduler; + private final Duration teleportDelay; private final Map pendingTeleports = new ConcurrentHashMap<>(); private final double cancelDistance; - public TeleportLogic(uSkyBlock plugin) { + @Inject + public TeleportLogic( + @NotNull Logger logger, + @NotNull uSkyBlock plugin, + @NotNull WorldManager worldManager, + @NotNull Scheduler scheduler + ) { + this.logger = logger; this.plugin = plugin; - teleportDelay = plugin.getConfig().getInt("options.island.islandTeleportDelay", 2); + teleportDelay = Duration.ofSeconds(plugin.getConfig().getInt("options.island.islandTeleportDelay", 2)); cancelDistance = plugin.getConfig().getDouble("options.island.teleportCancelDistance", 0.2); + this.worldManager = worldManager; + this.scheduler = scheduler; plugin.getServer().getPluginManager().registerEvents(this, plugin); } /** * Teleport the given {@link Player} to his island home. + * * @param player Player to teleport - * @param force True to override teleport delay, false otherwise. + * @param force True to override teleport delay, false otherwise. */ public void homeTeleport(@NotNull Player player, boolean force) { Validate.notNull(player, "Player cannot be null"); Location homeLocation = null; - PlayerInfo playerInfo = plugin.getPlayerLogic().getPlayerInfo(player); + PlayerInfo playerInfo = plugin.getPlayerInfo(player); if (playerInfo != null) { homeLocation = plugin.getSafeHomeLocation(playerInfo); @@ -66,7 +82,7 @@ public void homeTeleport(@NotNull Player player, boolean force) { return; } - plugin.getWorldManager().removeCreatures(homeLocation); + worldManager.removeCreatures(homeLocation); player.sendMessage(tr("\u00a7aTeleporting you to your island.")); safeTeleport(player, homeLocation, force); } @@ -74,24 +90,25 @@ public void homeTeleport(@NotNull Player player, boolean force) { /** * Teleport the given {@link Player} to the given {@link Location}, loading the {@link org.bukkit.Chunk} before * teleporting and with the configured teleport delay if applicable. - * @param player Player to teleport. + * + * @param player Player to teleport. * @param targetLocation Location to teleport the player to. - * @param force True to override teleport delay, false otherwise. + * @param force True to override teleport delay, false otherwise. */ public void safeTeleport(@NotNull Player player, @NotNull Location targetLocation, boolean force) { Validate.notNull(player, "Player cannot be null"); Validate.notNull(targetLocation, "TargetLocation cannot be null"); - log.log(Level.FINER, "safeTeleport " + player + " to " + targetLocation + (force ? " with force" : "")); + logger.log(Level.FINER, "safeTeleport " + player + " to " + targetLocation + (force ? " with force" : "")); final Location targetLoc = LocationUtil.centerOnBlock(targetLocation.clone()); - if (player.hasPermission("usb.mod.bypassteleport") || (teleportDelay == 0) || force) { + if (player.hasPermission("usb.mod.bypassteleport") || teleportDelay.isZero() || force) { PaperLib.teleportAsync(player, targetLoc); } else { player.sendMessage(tr("\u00a7aYou will be teleported in {0} seconds.", teleportDelay)); - BukkitTask tpTask = plugin.sync(() -> { + BukkitTask tpTask = scheduler.sync(() -> { pendingTeleports.remove(player.getUniqueId()); PaperLib.teleportAsync(player, targetLoc); - }, TimeUtil.secondsAsMillis(teleportDelay)); + }, teleportDelay); pendingTeleports.put(player.getUniqueId(), new PendingTeleport(player.getLocation(), tpTask)); } } @@ -99,14 +116,15 @@ public void safeTeleport(@NotNull Player player, @NotNull Location targetLocatio /** * Teleport the given {@link Player} to the spawn in the island world, loading the {@link org.bukkit.Chunk} before * teleporting and with the configured teleport delay if applicable. + * * @param player Player to teleport. - * @param force True to override teleport delay, false otherwise. + * @param force True to override teleport delay, false otherwise. */ public void spawnTeleport(@NotNull Player player, boolean force) { Validate.notNull(player, "Player cannot be null"); - Location spawnLocation = LocationUtil.centerOnBlock(plugin.getWorldManager().getWorld().getSpawnLocation()); - if (player.hasPermission("usb.mod.bypassteleport") || (teleportDelay == 0) || force) { + Location spawnLocation = LocationUtil.centerOnBlock(worldManager.getWorld().getSpawnLocation()); + if (player.hasPermission("usb.mod.bypassteleport") || teleportDelay.isZero() || force) { if (Settings.extras_sendToSpawn) { plugin.execCommand(player, "op:spawn", false); } else { @@ -114,23 +132,24 @@ public void spawnTeleport(@NotNull Player player, boolean force) { } } else { player.sendMessage(tr("\u00a7aYou will be teleported in {0} seconds.", teleportDelay)); - BukkitTask tpTask = plugin.sync(() -> { + BukkitTask tpTask = scheduler.sync(() -> { pendingTeleports.remove(player.getUniqueId()); if (Settings.extras_sendToSpawn) { plugin.execCommand(player, "op:spawn", false); } else { PaperLib.teleportAsync(player, spawnLocation); } - }, TimeUtil.secondsAsMillis(teleportDelay)); + }, teleportDelay); pendingTeleports.put(player.getUniqueId(), new PendingTeleport(player.getLocation(), tpTask)); } } /** * Teleport the given {@link Player} to the warp location for the given {@link PlayerInfo}. - * @param player Player to teleport. + * + * @param player Player to teleport. * @param playerInfo PlayerInfo to lookup the target warp location. - * @param force True to override teleport delay, false otherwise. + * @param force True to override teleport delay, false otherwise. */ public void warpTeleport(@NotNull Player player, @Nullable PlayerInfo playerInfo, boolean force) { Validate.notNull(player, "Player cannot be null"); @@ -150,7 +169,7 @@ public void warpTeleport(@NotNull Player player, @Nullable PlayerInfo playerInfo safeTeleport(player, warpLocation, force); } - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) @SuppressWarnings("unused") public void onPlayerMove(PlayerMoveEvent e) { PendingTeleport pendingTeleport = pendingTeleports.get(e.getPlayer().getUniqueId()); diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/signs/SignEvents.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/signs/SignEvents.java index 28362b24b..d870faa37 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/signs/SignEvents.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/signs/SignEvents.java @@ -1,5 +1,7 @@ package us.talabrek.ultimateskyblock.signs; +import com.google.inject.Inject; +import com.google.inject.Singleton; import dk.lockfuglsang.minecraft.util.FormatUtil; import org.bukkit.GameMode; import org.bukkit.Location; @@ -10,6 +12,7 @@ import org.bukkit.block.Sign; import org.bukkit.block.data.type.WallSign; import org.bukkit.entity.Player; +import org.bukkit.event.Event; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; @@ -20,32 +23,29 @@ import org.bukkit.event.inventory.InventoryMoveItemEvent; import org.bukkit.event.inventory.InventoryType; import org.bukkit.event.player.PlayerInteractEvent; -import us.talabrek.ultimateskyblock.menu.SkyBlockMenu; +import org.jetbrains.annotations.NotNull; import us.talabrek.ultimateskyblock.uSkyBlock; -import java.util.logging.Logger; - /** * Handles USB Signs */ +@Singleton public class SignEvents implements Listener { - private static final Logger log = Logger.getLogger(SignEvents.class.getName()); private final uSkyBlock plugin; private final SignLogic logic; - public SignEvents(uSkyBlock plugin, SignLogic logic) { + @Inject + public SignEvents(@NotNull uSkyBlock plugin, @NotNull SignLogic logic) { this.plugin = plugin; this.logic = logic; } @EventHandler(priority = EventPriority.HIGH) public void onPlayerHitSign(PlayerInteractEvent e) { - if (e.isCancelled() - || e.getPlayer() == null + if (e.useInteractedBlock() == Event.Result.DENY || (e.getAction() != Action.RIGHT_CLICK_BLOCK && e.getAction() != Action.LEFT_CLICK_BLOCK) || e.getClickedBlock() == null - || e.getClickedBlock().getType() != SkyBlockMenu.WALL_SIGN_MATERIAL - || !(e.getClickedBlock().getState() instanceof Sign) + || !(e.getClickedBlock().getState().getBlockData() instanceof WallSign) || !e.getPlayer().hasPermission("usb.island.signs.use") || !plugin.getWorldManager().isSkyAssociatedWorld(e.getPlayer().getWorld()) || !(plugin.playerIsOnOwnIsland(e.getPlayer())) @@ -59,22 +59,18 @@ public void onPlayerHitSign(PlayerInteractEvent e) { } } - @EventHandler(priority = EventPriority.MONITOR) + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) public void onSignChanged(SignChangeEvent e) { - if (e.isCancelled() || e.getPlayer() == null - || !plugin.getWorldManager().isSkyAssociatedWorld(e.getPlayer().getWorld()) + if (!plugin.getWorldManager().isSkyAssociatedWorld(e.getPlayer().getWorld()) || !e.getLines()[0].equalsIgnoreCase("[usb]") || e.getLines()[1].trim().isEmpty() || !e.getPlayer().hasPermission("usb.island.signs.place") - || !(e.getBlock().getType() == SkyBlockMenu.WALL_SIGN_MATERIAL) - || !(e.getBlock().getState() instanceof Sign) + || !(e.getBlock().getState() instanceof Sign sign) ) { return; } - Sign sign = (Sign) e.getBlock().getState(); - if(sign.getBlock().getState().getBlockData() instanceof WallSign) { - WallSign data = (WallSign) sign.getBlock().getState().getBlockData(); + if (sign.getBlock().getState().getBlockData() instanceof WallSign data) { BlockFace attached = data.getFacing().getOppositeFace(); Block wallBlock = sign.getBlock().getRelative(attached); if (isChest(wallBlock)) { @@ -91,8 +87,7 @@ private boolean isChest(Block wallBlock) { @EventHandler(priority = EventPriority.MONITOR) public void onInventoryMovedEvent(InventoryMoveItemEvent e) { - if (e.getDestination() == null - || e.getDestination().getLocation() == null + if (e.getDestination().getLocation() == null || !plugin.getWorldManager().isSkyAssociatedWorld(e.getDestination().getLocation().getWorld())) { return; } @@ -112,9 +107,7 @@ public void onInventoryMovedEvent(InventoryMoveItemEvent e) { @EventHandler(priority = EventPriority.MONITOR) public void onChestClosed(InventoryCloseEvent e) { - if (e.getPlayer() == null - || e.getPlayer().getLocation() == null - || !plugin.getWorldManager().isSkyAssociatedWorld(e.getPlayer().getLocation().getWorld()) + if (!plugin.getWorldManager().isSkyAssociatedWorld(e.getPlayer().getLocation().getWorld()) || e.getInventory().getType() != InventoryType.CHEST ) { return; @@ -125,47 +118,37 @@ public void onChestClosed(InventoryCloseEvent e) { } } - @EventHandler(priority = EventPriority.MONITOR) + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) public void onSignOrChestBreak(BlockBreakEvent e) { - if (e.isCancelled() - || e.getBlock() == null - || (e.getBlock().getType() != SkyBlockMenu.WALL_SIGN_MATERIAL && !(e.getBlock().getType() == Material.CHEST || e.getBlock().getType() == Material.TRAPPED_CHEST)) - || e.getBlock().getLocation() == null + if ((!(e.getBlock().getState().getBlockData() instanceof WallSign) && + !(e.getBlock().getType() == Material.CHEST || e.getBlock().getType() == Material.TRAPPED_CHEST)) || !plugin.getWorldManager().isSkyAssociatedWorld(e.getBlock().getLocation().getWorld()) ) { return; } - if (e.getBlock().getType() == SkyBlockMenu.WALL_SIGN_MATERIAL) { + if (e.getBlock().getState().getBlockData() instanceof WallSign) { logic.removeSign(e.getBlock().getLocation()); } else { logic.removeChest(e.getBlock().getLocation()); } } - private boolean isSign(Material material) { - return material == SkyBlockMenu.WALL_SIGN_MATERIAL || material == SkyBlockMenu.SIGN_MATERIAL; - } - @EventHandler(priority = EventPriority.HIGHEST) - public void onBlockHit(PlayerInteractEvent e) { - Player player = e.getPlayer(); - if (e.getPlayer() == null - || e.getClickedBlock() == null - || e.getAction() != Action.RIGHT_CLICK_BLOCK - || e.getPlayer().getGameMode() != GameMode.SURVIVAL - || !isSign(e.getClickedBlock().getType()) + public void onBlockHit(PlayerInteractEvent event) { + Player player = event.getPlayer(); + if (event.getClickedBlock() == null + || event.getAction() != Action.RIGHT_CLICK_BLOCK + || event.getPlayer().getGameMode() != GameMode.SURVIVAL + || !(event.getClickedBlock().getState() instanceof Sign sign) || !player.hasPermission("usb.island.signs.use") || !plugin.getWorldManager().isSkyAssociatedWorld(player.getWorld())) { return; } - if (e.getClickedBlock().getState() instanceof Sign) { - Sign sign = (Sign) e.getClickedBlock().getState(); - String firstLine = FormatUtil.stripFormatting(sign.getLine(0)).trim(); - if (firstLine.startsWith("/")) { - e.setCancelled(true); - player.performCommand(firstLine.substring(1)); - } + String firstLine = FormatUtil.stripFormatting(sign.getLine(0)).trim(); + if (firstLine.startsWith("/")) { + event.setCancelled(true); + player.performCommand(firstLine.substring(1)); } } } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/signs/SignLogic.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/signs/SignLogic.java index c3bf54037..d756dca53 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/signs/SignLogic.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/signs/SignLogic.java @@ -1,32 +1,39 @@ package us.talabrek.ultimateskyblock.signs; +import com.google.inject.Inject; +import com.google.inject.Singleton; import dk.lockfuglsang.minecraft.file.FileUtil; -import dk.lockfuglsang.minecraft.yml.YmlConfiguration; +import dk.lockfuglsang.minecraft.util.ItemStackUtil; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.block.Block; import org.bukkit.block.BlockState; import org.bukkit.block.Chest; import org.bukkit.block.Sign; +import org.bukkit.block.data.type.WallSign; import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; import us.talabrek.ultimateskyblock.challenge.Challenge; import us.talabrek.ultimateskyblock.challenge.ChallengeCompletion; import us.talabrek.ultimateskyblock.challenge.ChallengeLogic; import us.talabrek.ultimateskyblock.handler.WorldGuardHandler; import us.talabrek.ultimateskyblock.island.IslandInfo; -import us.talabrek.ultimateskyblock.menu.SkyBlockMenu; import us.talabrek.ultimateskyblock.player.PlayerInfo; import us.talabrek.ultimateskyblock.uSkyBlock; -import dk.lockfuglsang.minecraft.util.ItemStackUtil; import us.talabrek.ultimateskyblock.util.LocationUtil; +import us.talabrek.ultimateskyblock.util.Scheduler; +import us.talabrek.ultimateskyblock.world.WorldManager; import java.io.File; import java.io.IOException; -import java.util.ArrayList; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; import java.util.logging.Logger; import static dk.lockfuglsang.minecraft.po.I18nUtil.tr; @@ -35,19 +42,33 @@ /** * Responsible for keeping track of signs. */ +@Singleton public class SignLogic { - private static final Logger log = Logger.getLogger(SignLogic.class.getName()); private static final int SIGN_LINE_WIDTH = 11; // Actually more like 15, but we break after. - private final YmlConfiguration config; + + private final FileConfiguration config; private final File configFile; + private final Logger logger; private final uSkyBlock plugin; + private final Scheduler scheduler; private final ChallengeLogic challengeLogic; + private final WorldManager worldManager; - public SignLogic(uSkyBlock plugin) { + @Inject + public SignLogic( + @NotNull Logger logger, + @NotNull uSkyBlock plugin, + @NotNull Scheduler scheduler, + @NotNull ChallengeLogic challengeLogic, + @NotNull WorldManager worldManager + ) { + this.logger = logger; this.plugin = plugin; - configFile = new File(plugin.getDataFolder(), "signs.yml"); - config = FileUtil.getYmlConfiguration("signs.yml"); - challengeLogic = plugin.getChallengeLogic(); + this.scheduler = scheduler; + this.challengeLogic = challengeLogic; + this.worldManager = worldManager; + this.configFile = new File(plugin.getDataFolder(), "signs.yml"); + this.config = FileUtil.getYmlConfiguration("signs.yml"); } void addSign(Sign block, String[] lines, Chest chest) { @@ -77,7 +98,7 @@ void addSign(Sign block, String[] lines, Chest chest) { } void removeSign(final Location loc) { - plugin.async(() -> removeSignAsync(loc)); + scheduler.async(() -> removeSignAsync(loc)); } private void removeSignAsync(Location loc) { @@ -96,7 +117,7 @@ private void removeSignAsync(Location loc) { } void removeChest(final Location loc) { - plugin.async(() -> removeChestAsync(loc)); + scheduler.async(() -> removeChestAsync(loc)); } private void removeChestAsync(Location loc) { @@ -110,7 +131,7 @@ private void removeChestAsync(Location loc) { } void updateSignsOnContainer(final Location... containerLocations) { - plugin.async(() -> { + scheduler.async(() -> { for (Location loc : containerLocations) { if (loc == null) { continue; @@ -152,7 +173,7 @@ void updateSign(Location signLocation) { } private void updateSignAsync(final Location chestLoc) { - if (chestLoc == null || !plugin.getWorldManager().isSkyAssociatedWorld(chestLoc.getWorld())) { + if (chestLoc == null || !worldManager.isSkyAssociatedWorld(chestLoc.getWorld())) { return; } String locString = LocationUtil.asKey(chestLoc); @@ -169,6 +190,7 @@ private void updateSignAsync(final Location chestLoc) { } } + // TODO: This method accesses a lot of unsynchronized data, and should be refactored to be sync. private void updateSignAsync(final Location chestLoc, String islandName, String signLoc) { String challengeName = config.getString("signs." + signLoc + ".challenge", null); if (challengeName == null) { @@ -178,12 +200,12 @@ private void updateSignAsync(final Location chestLoc, String islandName, String if (challenge == null || challenge.getType() != Challenge.Type.PLAYER) { return; } - final List requiredItems = new ArrayList<>(); + Map requiredItems = new LinkedHashMap<>(); boolean isChallengeAvailable = false; if (challengeLogic.isIslandSharing()) { final ChallengeCompletion completion = challengeLogic.getIslandCompletion(islandName, challengeName); if (completion != null) { - requiredItems.addAll(challenge.getRequiredItems(completion.getTimesCompletedInCooldown())); + requiredItems = challenge.getRequiredItems(completion.getTimesCompletedInCooldown()); } } IslandInfo islandInfo = plugin.getIslandInfo(islandName); @@ -197,26 +219,26 @@ private void updateSignAsync(final Location chestLoc, String islandName, String String signLocString = config.getString("signs." + signLoc + ".location", null); final Location signLocation = LocationUtil.fromString(signLocString); final boolean challengeLocked = !isChallengeAvailable; + final Map requiredItemsFinal = requiredItems; // Back to sync - plugin.sync(() -> updateSignFromChestSync(chestLoc, signLocation, challenge, requiredItems, challengeLocked)); + scheduler.sync(() -> updateSignFromChestSync(chestLoc, signLocation, challenge, requiredItemsFinal, challengeLocked)); } - private void updateSignFromChestSync(Location chestLoc, Location signLoc, Challenge challenge, List requiredItems, boolean challengeLocked) { + private void updateSignFromChestSync(Location chestLoc, Location signLoc, Challenge challenge, Map requiredItems, boolean challengeLocked) { Block chestBlock = chestLoc.getBlock(); Block signBlock = signLoc != null ? signLoc.getBlock() : null; - if (chestBlock != null && signBlock != null - && isChest(chestBlock) - && signBlock.getType() == SkyBlockMenu.WALL_SIGN_MATERIAL && signBlock.getState() instanceof Sign - ) { + if (signBlock != null && isChest(chestBlock) && signBlock.getState().getBlockData() instanceof WallSign) { Sign sign = (Sign) signBlock.getState(); Chest chest = (Chest) chestBlock.getState(); int missing = -1; if (!requiredItems.isEmpty() && !challengeLocked) { missing = 0; - for (ItemStack required : requiredItems) { - if (!chest.getInventory().containsAtLeast(required, required.getAmount())) { + for (Map.Entry required : requiredItems.entrySet()) { + ItemStack requiredType = required.getKey(); + int requiredAmount = required.getValue(); + if (!chest.getInventory().containsAtLeast(requiredType, requiredAmount)) { // Max shouldn't be needed, provided containsAtLeast matches getCountOf... but it might not - missing += Math.max(0, required.getAmount() - plugin.getChallengeLogic().getCountOf(chest.getInventory(), required)); + missing += Math.max(0, requiredAmount - challengeLogic.getCountOf(chest.getInventory(), requiredType)); } } } @@ -247,7 +269,7 @@ && isChest(chestBlock) sign.setLine(3, ""); } if (!sign.update()) { - log.info("Unable to update sign at " + LocationUtil.asString(signLoc)); + logger.info("Unable to update sign at " + LocationUtil.asString(signLoc)); } } } @@ -257,7 +279,7 @@ private boolean isChest(Block chestBlock) { } private void saveAsync() { - plugin.async(this::save); + scheduler.async(this::save); } private void save() { @@ -265,13 +287,13 @@ private void save() { try { config.save(configFile); } catch (IOException e) { - log.info("Unable to save to " + configFile); + logger.info("Unable to save to " + configFile); } } } void signClicked(final Player player, final Location location) { - plugin.async(() -> tryCompleteAsync(player, location)); + scheduler.async(() -> tryCompleteAsync(player, location)); } private void tryCompleteAsync(final Player player, Location location) { @@ -294,14 +316,18 @@ private void tryCompleteAsync(final Player player, Location location) { player.sendMessage(tr("\u00a74The {0} challenge is not available yet!", challenge.getDisplayName())); return; } - plugin.sync(() -> tryComplete(player, chestLoc, challenge)); + scheduler.sync(() -> tryComplete(player, chestLoc, challenge)); } } } + // This logic is duplicated in ChallengeLogic.tryCompleteOnPlayer. It has a lot of the same checks. If they + // pass, it moves the items to the player inventory and then calls the challengeLogic to complete the challenge. + // This is prone to bugs and exploits, and should be refactored. Ideally we get rid of the transfer and just + // refactor the challenge logic to accept multiple inventories as source. private void tryComplete(Player player, Location chestLoc, Challenge challenge) { BlockState state = chestLoc.getBlock().getState(); - if (!(state instanceof Chest)) { + if (!(state instanceof Chest chest)) { return; } PlayerInfo playerInfo = plugin.getPlayerInfo(player); @@ -309,35 +335,27 @@ private void tryComplete(Player player, Location chestLoc, Challenge challenge) return; } ChallengeCompletion completion = challengeLogic.getChallenge(playerInfo, challenge.getName()); - List requiredItems = challenge.getRequiredItems(completion.getTimesCompletedInCooldown()); - Chest chest = (Chest) state; + Map requiredItems = challenge.getRequiredItems(completion.getTimesCompletedInCooldown()); int missing = 0; - for (ItemStack required : requiredItems) { + for (Map.Entry required : requiredItems.entrySet()) { + ItemStack requiredType = required.getKey(); + int requiredAmount = required.getValue(); int diff = 0; - if (!player.getInventory().containsAtLeast(required, required.getAmount())) { - diff = required.getAmount() - plugin.getChallengeLogic().getCountOf(player.getInventory(), required); + if (!player.getInventory().containsAtLeast(requiredType, requiredAmount)) { + diff = requiredAmount - challengeLogic.getCountOf(player.getInventory(), requiredType); } - if (diff > 0 && !chest.getInventory().containsAtLeast(required, diff)) { - diff -= plugin.getChallengeLogic().getCountOf(chest.getInventory(), required); + if (diff > 0 && !chest.getInventory().containsAtLeast(requiredType, diff)) { + diff -= challengeLogic.getCountOf(chest.getInventory(), requiredType); } else { diff = 0; } missing += diff; } if (missing == 0) { - ItemStack[] items = requiredItems.toArray(new ItemStack[0]); - ItemStack[] copy = ItemStackUtil.clone(requiredItems).toArray(new ItemStack[requiredItems.size()]); - HashMap missingItems = player.getInventory().removeItem(items); - missingItems = chest.getInventory().removeItem(missingItems.values().toArray(new ItemStack[0])); - if (!missingItems.isEmpty()) { - // This effectively means, we just donated some items to the player (exploit!!) - log.warning("Not all items removed from chest and player: " + missingItems.values()); - } - HashMap leftOvers = player.getInventory().addItem(copy); - if (leftOvers.isEmpty()) { - plugin.getChallengeLogic().completeChallenge(player, challenge.getName()); + boolean successfulItemTransfer = attemptToMoveItemsToPlayerInventory(player.getInventory(), chest.getInventory(), requiredItems); + if (successfulItemTransfer) { + challengeLogic.completeChallenge(player, challenge.getName()); } else { - chest.getInventory().addItem(leftOvers.values().toArray(new ItemStack[0])); player.sendMessage(tr("\u00a7cWARNING:\u00a7e Could not transfer all the required items to your inventory!")); } updateSignsOnContainer(chest.getLocation()); @@ -346,4 +364,25 @@ private void tryComplete(Player player, Location chestLoc, Challenge challenge) } } + // This is a counter-intuitive way transfer the required items from the chest to the player inventory. + // It's prone to bugs and exploits, and should be refactored. Ideally we get rid of the transfer and just + // refactor the challenge logic to accept multiple inventories as source. + private static boolean attemptToMoveItemsToPlayerInventory(Inventory player, Inventory chest, Map requiredItems) { + ItemStack[] itemsToRemove = ItemStackUtil.asValidItemStacksWithAmount(requiredItems); + ItemStack[] itemCopy = ItemStackUtil.asValidItemStacksWithAmount(requiredItems); + + HashMap missingItems = player.removeItem(itemsToRemove); + missingItems = chest.removeItem(missingItems.values().toArray(new ItemStack[0])); + if (!missingItems.isEmpty()) { + // This effectively means, we just donated some items to the player (exploit!!) + throw new IllegalStateException("Not all items removed from chest and player: " + missingItems.values()); + } + HashMap leftOvers = player.addItem(itemCopy); + if (leftOvers.isEmpty()) { + return true; + } else { + chest.addItem(leftOvers.values().toArray(new ItemStack[0])); + return false; + } + } } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/storage/yml/IslandInfoYml.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/storage/yml/IslandInfoYml.java deleted file mode 100644 index 7a9c5176f..000000000 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/storage/yml/IslandInfoYml.java +++ /dev/null @@ -1,7 +0,0 @@ -package us.talabrek.ultimateskyblock.storage.yml; - -/** - * Created by R4zorax on 18-12-2016. - */ -public class IslandInfoYml { -} diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/uSkyBlock.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/uSkyBlock.java index b0bafb2c2..fa32a3c11 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/uSkyBlock.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/uSkyBlock.java @@ -1,68 +1,53 @@ package us.talabrek.ultimateskyblock; +import com.google.inject.Guice; +import com.google.inject.Inject; +import com.google.inject.Injector; import com.sk89q.worldguard.protection.regions.ProtectedRegion; -import dk.lockfuglsang.minecraft.animation.AnimationHandler; import dk.lockfuglsang.minecraft.command.Command; import dk.lockfuglsang.minecraft.command.CommandManager; import dk.lockfuglsang.minecraft.file.FileUtil; import dk.lockfuglsang.minecraft.po.I18nUtil; import dk.lockfuglsang.minecraft.util.TimeUtil; import dk.lockfuglsang.minecraft.util.VersionUtil; -import dk.lockfuglsang.minecraft.yml.YmlConfiguration; -import io.papermc.lib.PaperLib; -import org.bstats.bukkit.Metrics; import org.bukkit.Bukkit; import org.bukkit.Location; +import org.bukkit.Registry; import org.bukkit.block.Biome; import org.bukkit.command.CommandSender; import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.entity.Player; import org.bukkit.event.Event; -import org.bukkit.event.HandlerList; import org.bukkit.generator.ChunkGenerator; import org.bukkit.inventory.ItemStack; -import org.bukkit.plugin.Plugin; import org.bukkit.plugin.PluginDescriptionFile; import org.bukkit.plugin.PluginManager; +import org.bukkit.plugin.ServicePriority; import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.scheduler.BukkitRunnable; -import org.bukkit.scheduler.BukkitTask; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import us.talabrek.ultimateskyblock.api.IslandLevel; import us.talabrek.ultimateskyblock.api.IslandRank; +import us.talabrek.ultimateskyblock.api.UltimateSkyblock; +import us.talabrek.ultimateskyblock.api.UltimateSkyblockProvider; import us.talabrek.ultimateskyblock.api.async.Callback; import us.talabrek.ultimateskyblock.api.event.EventLogic; import us.talabrek.ultimateskyblock.api.event.uSkyBlockEvent; import us.talabrek.ultimateskyblock.api.event.uSkyBlockScoreChangedEvent; +import us.talabrek.ultimateskyblock.api.impl.UltimateSkyblockApi; import us.talabrek.ultimateskyblock.api.uSkyBlockAPI; +import us.talabrek.ultimateskyblock.bootstrap.SkyblockApp; +import us.talabrek.ultimateskyblock.bootstrap.SkyblockModule; import us.talabrek.ultimateskyblock.challenge.ChallengeLogic; -import us.talabrek.ultimateskyblock.chat.ChatEvents; -import us.talabrek.ultimateskyblock.chat.ChatLogic; -import us.talabrek.ultimateskyblock.chat.IslandTalkCommand; -import us.talabrek.ultimateskyblock.chat.PartyTalkCommand; import us.talabrek.ultimateskyblock.command.AdminCommand; -import us.talabrek.ultimateskyblock.command.ChallengeCommand; -import us.talabrek.ultimateskyblock.command.IslandCommand; -import us.talabrek.ultimateskyblock.command.admin.DebugCommand; import us.talabrek.ultimateskyblock.command.admin.SetMaintenanceCommand; -import us.talabrek.ultimateskyblock.command.island.BiomeCommand; -import us.talabrek.ultimateskyblock.event.ExploitEvents; -import us.talabrek.ultimateskyblock.event.GriefEvents; -import us.talabrek.ultimateskyblock.event.InternalEvents; -import us.talabrek.ultimateskyblock.event.ItemDropEvents; -import us.talabrek.ultimateskyblock.event.MenuEvents; -import us.talabrek.ultimateskyblock.event.NetherTerraFormEvents; -import us.talabrek.ultimateskyblock.event.PlayerEvents; -import us.talabrek.ultimateskyblock.event.SpawnEvents; -import us.talabrek.ultimateskyblock.event.ToolMenuEvents; -import us.talabrek.ultimateskyblock.event.WorldGuardEvents; -import us.talabrek.ultimateskyblock.handler.AsyncWorldEditHandler; import us.talabrek.ultimateskyblock.handler.ConfirmHandler; import us.talabrek.ultimateskyblock.handler.CooldownHandler; -import us.talabrek.ultimateskyblock.handler.VaultHandler; import us.talabrek.ultimateskyblock.handler.WorldGuardHandler; -import us.talabrek.ultimateskyblock.handler.placeholder.PlaceholderHandler; +import us.talabrek.ultimateskyblock.hook.HookManager; +import us.talabrek.ultimateskyblock.imports.BlockRequirementConverter; +import us.talabrek.ultimateskyblock.imports.ItemComponentConverter; import us.talabrek.ultimateskyblock.imports.USBImporterExecutor; import us.talabrek.ultimateskyblock.island.BlockLimitLogic; import us.talabrek.ultimateskyblock.island.IslandGenerator; @@ -71,13 +56,10 @@ import us.talabrek.ultimateskyblock.island.IslandLogic; import us.talabrek.ultimateskyblock.island.LimitLogic; import us.talabrek.ultimateskyblock.island.OrphanLogic; -import us.talabrek.ultimateskyblock.island.level.ChunkSnapshotLevelLogic; import us.talabrek.ultimateskyblock.island.level.IslandScore; import us.talabrek.ultimateskyblock.island.level.LevelLogic; import us.talabrek.ultimateskyblock.island.task.CreateIslandTask; -import us.talabrek.ultimateskyblock.island.task.RecalculateRunnable; import us.talabrek.ultimateskyblock.island.task.SetBiomeTask; -import us.talabrek.ultimateskyblock.menu.ConfigMenu; import us.talabrek.ultimateskyblock.menu.SkyBlockMenu; import us.talabrek.ultimateskyblock.player.IslandPerk; import us.talabrek.ultimateskyblock.player.PerkLogic; @@ -86,169 +68,153 @@ import us.talabrek.ultimateskyblock.player.PlayerNotifier; import us.talabrek.ultimateskyblock.player.PlayerPerk; import us.talabrek.ultimateskyblock.player.TeleportLogic; -import us.talabrek.ultimateskyblock.signs.SignEvents; -import us.talabrek.ultimateskyblock.signs.SignLogic; import us.talabrek.ultimateskyblock.util.IslandUtil; import us.talabrek.ultimateskyblock.util.LocationUtil; -import us.talabrek.ultimateskyblock.util.PlayerUtil; +import us.talabrek.ultimateskyblock.util.Scheduler; import us.talabrek.ultimateskyblock.util.ServerUtil; -import us.talabrek.ultimateskyblock.uuid.BukkitPlayerDB; -import us.talabrek.ultimateskyblock.uuid.FilePlayerDB; -import us.talabrek.ultimateskyblock.uuid.MemoryPlayerDB; import us.talabrek.ultimateskyblock.uuid.PlayerDB; import us.talabrek.ultimateskyblock.world.WorldManager; -import java.io.File; +import java.time.Duration; import java.util.Collections; import java.util.List; +import java.util.Optional; import java.util.Random; import java.util.UUID; import java.util.logging.Level; import java.util.regex.Matcher; import java.util.regex.Pattern; -import static dk.lockfuglsang.minecraft.po.I18nUtil.pre; import static dk.lockfuglsang.minecraft.po.I18nUtil.tr; -import static us.talabrek.ultimateskyblock.util.LocationUtil.isSafeLocation; +import static java.util.Objects.requireNonNull; import static us.talabrek.ultimateskyblock.util.LogUtil.log; public class uSkyBlock extends JavaPlugin implements uSkyBlockAPI, CommandManager.RequirementChecker { private static final String CN = uSkyBlock.class.getName(); - private static final String[][] depends = new String[][]{ - new String[]{"Vault", "1.7.0", "optional"}, - new String[]{"WorldEdit", "7.0", "optionalIf", "FastAsyncWorldEdit"}, - new String[]{"WorldGuard", "7.0"}, - new String[]{"FastAsyncWorldEdit", "1.13", "optional"}, - new String[]{"Multiverse-Core", "2.5", "optional"}, - new String[]{"Multiverse-Portals", "2.5", "optional"}, - new String[]{"Multiverse-NetherPortals", "2.5", "optional"}, + public static final String[][] depends = new String[][]{ + new String[]{"Vault", "1.7.1", "optional"}, + new String[]{"WorldEdit", "7.2.12", "optionalIf", "FastAsyncWorldEdit"}, + new String[]{"WorldGuard", "7.0.8"}, + new String[]{"FastAsyncWorldEdit", "2.4.3", "optional"}, + new String[]{"Multiverse-Core", "4.3.1", "optional"}, + new String[]{"Multiverse-Portals", "4.2.1", "optional"}, + new String[]{"Multiverse-NetherPortals", "4.2.1", "optional"}, }; private static String missingRequirements = null; - private static final Random RND = new Random(System.currentTimeMillis()); + private static final Random RND = new Random(); + private SkyblockApp skyBlock; + + // TODO: eventually get rid of these global references and move them to a proper API instead + @Inject private SkyBlockMenu menu; - private ConfigMenu configMenu; + @Inject private ChallengeLogic challengeLogic; + @Inject private EventLogic eventLogic; + @Inject private LevelLogic levelLogic; + @Inject private IslandLogic islandLogic; + @Inject private OrphanLogic orphanLogic; + @Inject private PerkLogic perkLogic; + @Inject private TeleportLogic teleportLogic; + @Inject private LimitLogic limitLogic; - - /* MANAGERS */ + @Inject + private HookManager hookManager; + @Inject private WorldManager worldManager; - + @Inject private IslandGenerator islandGenerator; + @Inject private PlayerNotifier notifier; - + @Inject private USBImporterExecutor importer; - - private static uSkyBlock instance; - // TODO: 28/06/2016 - R4zorax: These two should probably be moved to the proper classes - public File directoryPlayers; - public File directoryIslands; - - private BukkitTask autoRecalculateTask; - + @Inject private IslandLocatorLogic islandLocatorLogic; + @Inject private PlayerDB playerDB; + @Inject private ConfirmHandler confirmHandler; - private AnimationHandler animationHandler; - + @Inject private CooldownHandler cooldownHandler; + @Inject private PlayerLogic playerLogic; - private ChatLogic chatLogic; + // TODO: don't assign value directly, but use injection instead. Currently, legacy code in PlaceholderHandler accesses it too early for injection to work. + @Inject + private PluginConfig config = new PluginConfig(); + @Inject + private BlockLimitLogic blockLimitLogic; + @Inject + private SkyUpdateChecker updateChecker; + @Inject + private Scheduler scheduler; + + private UltimateSkyblockApi api; private volatile boolean maintenanceMode = false; - private BlockLimitLogic blockLimitLogic; - private Metrics metrics; - public uSkyBlock() { - } + private static uSkyBlock instance; @Override public void onDisable() { - HandlerList.unregisterAll(this); - Bukkit.getScheduler().cancelTasks(this); - try { - WorldManager.skyBlockWorld = null; // Force a reload on config. - } catch (Exception e) { - log(Level.INFO, tr("Something went wrong saving the island and/or party data!"), e); - } - PlaceholderHandler.unregister(this); - if (animationHandler != null) { - animationHandler.stop(); - } - challengeLogic.shutdown(); - eventLogic.shutdown(); - playerLogic.shutdown(); - islandLogic.shutdown(); - playerDB.shutdown(); // Must be before playerNameChangeManager!! - AsyncWorldEditHandler.onDisable(this); - DebugCommand.disableLogging(null); + deregisterApi(api); + api = null; + shutdown(); } @Override - public FileConfiguration getConfig() { - return FileUtil.getYmlConfiguration("config.yml"); + public @NotNull FileConfiguration getConfig() { + return config.getYamlConfig(); + } + + private void convertConfigItemsTo1_20_6IfRequired() { + var converter = new ItemComponentConverter(getLogger()); + converter.checkAndDoImport(getDataFolder()); + } + + private void convertConfigToBlockRequirements() { + var converter = new BlockRequirementConverter(getLogger()); + converter.checkAndDoImport(getDataFolder()); } @Override public void onEnable() { - WorldManager.skyBlockWorld = null; // Force a re-import or what-ever... - WorldManager.skyBlockNetherWorld = null; missingRequirements = null; instance = this; - CommandManager.registerRequirements(this); - FileUtil.setDataFolder(getDataFolder()); - FileUtil.setAllwaysOverwrite("levelConfig.yml"); - I18nUtil.setDataFolder(getDataFolder()); - reloadConfigs(); + // Converter has to run before the plugin loads its config files. + convertConfigItemsTo1_20_6IfRequired(); + convertConfigToBlockRequirements(); - getServer().getScheduler().runTaskLater(getInstance(), new Runnable() { - @Override - public void run() { - ServerUtil.init(uSkyBlock.this); - if (!isRequirementsMet(Bukkit.getConsoleSender(), null)) { - return; - } - if (VaultHandler.setupEconomy()) { - log(Level.INFO, "Hooked into Vault Economy"); - } - if (VaultHandler.setupPermissions()) { - log(Level.INFO, "Hooked into Vault Permissions"); - } - AsyncWorldEditHandler.onEnable(uSkyBlock.this); - WorldGuardHandler.setupGlobal(getWorldManager().getWorld()); - if (getWorldManager().getNetherWorld() != null) { - WorldGuardHandler.setupGlobal(getWorldManager().getNetherWorld()); - } - registerEventsAndCommands(); - if (!getConfig().getBoolean("importer.name2uuid.imported", false)) { - Bukkit.getConsoleSender().sendMessage(tr("Converting data to UUID, this make take a while!")); - getImporter().importUSB(Bukkit.getConsoleSender(), "name2uuid"); - } - log(Level.INFO, getVersionInfo(false)); + reloadLegacyStuff(); + startup(); + + api = new UltimateSkyblockApi(this); + registerApi(api); + + getScheduler().sync(() -> { + ServerUtil.init(uSkyBlock.this); + if (!isRequirementsMet(Bukkit.getConsoleSender(), null)) { + return; } - }, getConfig().getLong("init.initDelay", 50L)); - try { - metrics = new Metrics(this); - metrics.addCustomChart(new Metrics.SimplePie("language", () -> getConfig().getString("language", "en"))); - metrics.addCustomChart(new Metrics.SimplePie("radius_and_distance", () -> String.format("(%d,%d)", Settings.island_radius, Settings.island_distance))); - } catch (Exception e) { - log(Level.WARNING, "Failed to submit metrics data", e); - } - PaperLib.suggestPaper(this); + delayedEnable(); + + getServer().dispatchCommand(getServer().getConsoleSender(), "usb flush"); // See uskyblock#4 + }, TimeUtil.ticksAsDuration(getConfig().getLong("init.initDelay", 50L))); + + getScheduler().async(() -> getUpdateChecker().checkForUpdates(), Duration.ZERO, Duration.ofHours(4)); } public synchronized boolean isRequirementsMet(CommandSender sender, Command command, String... args) { if (maintenanceMode && !( - (command instanceof AdminCommand && args != null && args.length > 0 && args[0].equals("maintenance")) || - command instanceof SetMaintenanceCommand)) { + (command instanceof AdminCommand && args != null && args.length > 0 && args[0].equals("maintenance")) || + command instanceof SetMaintenanceCommand)) { sender.sendMessage(tr("\u00a7cMAINTENANCE:\u00a7e uSkyBlock is currently in maintenance mode")); return false; } @@ -268,7 +234,7 @@ public synchronized boolean isRequirementsMet(CommandSender sender, Command comm } } if (pluginManager.isPluginEnabled(pluginReq[0])) { - PluginDescriptionFile desc = pluginManager.getPlugin(pluginReq[0]).getDescription(); + PluginDescriptionFile desc = requireNonNull(pluginManager.getPlugin(pluginReq[0])).getDescription(); if (VersionUtil.getVersion(desc.getVersion()).isLT(pluginReq[1])) { missingRequirements += tr("\u00a7buSkyBlock\u00a7e depends on \u00a79{0}\u00a7e >= \u00a7av{1}\u00a7e but only \u00a7cv{2}\u00a7e was found!\n", pluginReq[0], pluginReq[1], desc.getVersion()); } @@ -285,56 +251,16 @@ public synchronized boolean isRequirementsMet(CommandSender sender, Command comm } } - private void createFolders() { + private void createDataFolder() { if (!getDataFolder().exists()) { getDataFolder().mkdirs(); } - directoryPlayers = new File(getDataFolder() + File.separator + "players"); - if (!directoryPlayers.exists()) { - directoryPlayers.mkdirs(); - } - directoryIslands = new File(getDataFolder() + File.separator + "islands"); - if (!directoryIslands.exists()) { - directoryIslands.mkdirs(); - } - IslandInfo.setDirectory(directoryIslands); } public static uSkyBlock getInstance() { return uSkyBlock.instance; } - public void registerEvents() { - final PluginManager manager = getServer().getPluginManager(); - manager.registerEvents(new InternalEvents(this), this); - manager.registerEvents(new PlayerEvents(this), this); - manager.registerEvents(new MenuEvents(this), this); - manager.registerEvents(new ExploitEvents(this), this); - if (getConfig().getBoolean("options.protection.enabled", true)) { - manager.registerEvents(new GriefEvents(this), this); - if (getConfig().getBoolean("options.protection.item-drops", true)) { - manager.registerEvents(new ItemDropEvents(this), this); - } - } - if (getConfig().getBoolean("options.island.spawn-limits.enabled", true)) { - manager.registerEvents(new SpawnEvents(this), this); - } - if (getConfig().getBoolean("options.protection.visitors.block-banned-entry", true)) { - manager.registerEvents(new WorldGuardEvents(this), this); - } - if (Settings.nether_enabled) { - manager.registerEvents(new NetherTerraFormEvents(this), this); - } - if (getConfig().getBoolean("tool-menu.enabled", true)) { - manager.registerEvents(new ToolMenuEvents(this), this); - } - if (getConfig().getBoolean("signs.enabled", true)) { - manager.registerEvents(new SignEvents(this, new SignLogic(this)), this); - } - PlaceholderHandler.register(this); - manager.registerEvents(new ChatEvents(chatLogic, this), this); - } - public Location getSafeHomeLocation(final PlayerInfo p) { Location home = LocationUtil.findNearestSafeLocation(p.getHomeLocation(), null); if (home == null) { @@ -368,13 +294,10 @@ private void postDelete(final IslandInfo islandInfo) { public boolean deleteEmptyIsland(String islandName, final Runnable runner) { final IslandInfo islandInfo = getIslandInfo(islandName); if (islandInfo != null && islandInfo.getMembers().isEmpty()) { - islandLogic.clearIsland(islandInfo.getIslandLocation(), new Runnable() { - @Override - public void run() { - postDelete(islandInfo); - if (runner != null) { - runner.run(); - } + islandLogic.clearIsland(islandInfo.getIslandLocation(), () -> { + postDelete(islandInfo); + if (runner != null) { + runner.run(); } }); return true; @@ -392,13 +315,10 @@ public void deletePlayerIsland(final String player, final Runnable runner) { pi = playerLogic.getPlayerInfo(member); islandInfo.removeMember(pi); } - islandLogic.clearIsland(islandLocation, new Runnable() { - @Override - public void run() { - postDelete(finalPI); - postDelete(islandInfo); - if (runner != null) runner.run(); - } + islandLogic.clearIsland(islandLocation, () -> { + postDelete(finalPI); + postDelete(islandInfo); + if (runner != null) runner.run(); }); } @@ -415,12 +335,7 @@ public boolean restartPlayerIsland(final Player player, final Location next, fin // Clear first, since the player could log out and we NEED to make sure their inventory gets cleared. clearPlayerInventory(player); } - islandLogic.clearIsland(next, new Runnable() { - @Override - public void run() { - generateIsland(player, playerInfo, next, cSchem); - } - }); + islandLogic.clearIsland(next, () -> generateIsland(player, playerInfo, next, cSchem)); return true; } @@ -450,8 +365,8 @@ public void clearPlayerInventory(Player player) { if (getConfig().getBoolean("options.restart.clearEnderChest", true)) { player.getEnderChest().clear(); } - if (getConfig().getBoolean("options.restart.clearCurrency", false) && VaultHandler.hasEcon()) { - VaultHandler.getEcon().withdrawPlayer(player, VaultHandler.getEcon().getBalance(player)); + if (getConfig().getBoolean("options.restart.clearCurrency", false)) { + getHookManager().getEconomyHook().ifPresent((hook) -> hook.withdrawPlayer(player, hook.getBalance(player))); } getLogger().exiting(CN, "clearPlayerInventory"); } @@ -470,7 +385,7 @@ public synchronized boolean devSetPlayerIsland(final Player sender, final Locati if (pi.getHasIsland()) { Location oldLoc = pi.getIslandLocation(); if (oldLoc != null - && !(newLoc.getBlockX() == oldLoc.getBlockX() && newLoc.getBlockZ() == oldLoc.getBlockZ())) { + && !(newLoc.getBlockX() == oldLoc.getBlockX() && newLoc.getBlockZ() == oldLoc.getBlockZ())) { deleteOldIsland = true; } } @@ -499,43 +414,21 @@ public synchronized boolean devSetPlayerIsland(final Player sender, final Locati return true; } - public boolean homeSet(final Player player) { - if (!player.getWorld().getName().equalsIgnoreCase(getWorldManager().getWorld().getName())) { - player.sendMessage(tr("\u00a74You must be closer to your island to set your skyblock home!")); - return true; - } - if (playerIsOnOwnIsland(player)) { - PlayerInfo playerInfo = playerLogic.getPlayerInfo(player); - if (playerInfo != null && isSafeLocation(player.getLocation())) { - playerInfo.setHomeLocation(player.getLocation()); - playerInfo.save(); - player.sendMessage(tr("\u00a7aYour skyblock home has been set to your current location.")); - } else { - player.sendMessage(tr("\u00a74Your current location is not a safe home-location.")); - } - return true; - } - player.sendMessage(tr("\u00a74You must be closer to your island to set your skyblock home!")); - return true; - } - public boolean playerIsOnIsland(final Player player) { return playerIsOnOwnIsland(player) - || playerIsTrusted(player); + || playerIsTrusted(player); } public boolean playerIsOnOwnIsland(Player player) { return locationIsOnIsland(player, player.getLocation()) - || locationIsOnNetherIsland(player, player.getLocation()); + || locationIsOnNetherIsland(player, player.getLocation()); } private boolean playerIsTrusted(Player player) { String islandName = WorldGuardHandler.getIslandNameAt(player.getLocation()); if (islandName != null) { us.talabrek.ultimateskyblock.api.IslandInfo islandInfo = islandLogic.getIslandInfo(islandName); - if (islandInfo != null && islandInfo.getTrustees().contains(player.getName())) { - return true; - } + return islandInfo != null && islandInfo.isTrusted(player); } return false; } @@ -578,10 +471,7 @@ public boolean hasIsland(final Player player) { } public boolean islandAtLocation(final Location loc) { - return ((WorldGuardHandler.getIntersectingRegions(loc).size() > 0) - || islandLogic.hasIsland(loc) - ); - + return ((!WorldGuardHandler.getIntersectingRegions(loc).isEmpty()) || islandLogic.hasIsland(loc)); } public boolean islandInSpawn(final Location loc) { @@ -608,43 +498,17 @@ public PlayerInfo getPlayerInfo(String playerName) { return playerLogic.getPlayerInfo(playerName); } - public boolean setBiome(final Location loc, final String bName) { - Biome biome = getBiome(bName); - if (biome == null) return false; - setBiome(loc, biome); - return true; + // TODO: move these to Biome/World related classes + @SuppressWarnings("removal") + public @Nullable Biome getBiome(String biomeName) { + if (biomeName == null) return null; + return Registry.BIOME.match(biomeName); } - public Biome getBiome(String bName) { - if (bName == null) return null; - return BiomeCommand.BIOMES.get(bName.toLowerCase()); - } - - private void setBiome(Location loc, Biome biome) { + public void setBiome(Location loc, Biome biome) { new SetBiomeTask(this, loc, biome, null).runTask(this); } - public void changePlayerBiome(Player player, final String bName, final Callback callback) { - callback.setState(false); - if (bName.equalsIgnoreCase("ocean") || player.hasPermission("usb.biome." + bName)) { - PlayerInfo playerInfo = getPlayerInfo(player); - final IslandInfo islandInfo = islandLogic.getIslandInfo(playerInfo); - if (islandInfo.hasPerm(player, "canChangeBiome")) { - player.sendMessage(tr("\u00a77The pixies are busy changing the biome of your island to \u00a79{0}\u00a77, be patient.", bName)); - new SetBiomeTask(this, playerInfo.getIslandLocation(), getBiome(bName), new Runnable() { - @Override - public void run() { - islandInfo.setBiome(bName); - callback.setState(true); - callback.run(); - } - }).runTask(this); - return; - } - } - callback.run(); - } - public void createIsland(final Player player, String cSchem) { PlayerInfo pi = getPlayerInfo(player); if (pi.isIslandGenerating()) { @@ -655,9 +519,7 @@ public void createIsland(final Player player, String cSchem) { player.sendMessage(tr("\u00a7eYou do not have access to that island-schematic!")); return; } - if (pi != null) { - pi.setIslandGenerating(true); - } + pi.setIslandGenerating(true); try { Location next = getIslandLocatorLogic().getNextIslandLocation(player); if (getWorldManager().isSkyWorld(player.getWorld())) { @@ -685,17 +547,15 @@ private void generateIsland(final Player player, final PlayerInfo pi, final Loca islandLogic.clearIsland(next, createTask); } - public Location getChestSpawnLoc(final Location loc) { - Location chestLocation = LocationUtil.findChestLocation(loc); - return LocationUtil.findNearestSpawnLocation(chestLocation != null ? chestLocation : loc); - } - public IslandInfo setNewPlayerIsland(final PlayerInfo playerInfo, final Location loc) { playerInfo.startNewIsland(loc); - Location chestSpawnLocation = getChestSpawnLoc(loc); - if (chestSpawnLocation != null) { - playerInfo.setHomeLocation(chestSpawnLocation); + Location chestLocation = LocationUtil.findChestLocation(loc); + Optional chestSpawnLocation = LocationUtil.findNearestSpawnLocation( + chestLocation != null ? chestLocation : loc); + + if (chestSpawnLocation.isPresent()) { + playerInfo.setHomeLocation(chestSpawnLocation.get()); } else { log(Level.SEVERE, "Could not find a safe chest within 15 blocks of the island spawn. Bad schematic!"); } @@ -738,10 +598,6 @@ public SkyBlockMenu getMenu() { return menu; } - public ConfigMenu getConfigMenu() { - return configMenu; - } - public ChallengeLogic getChallengeLogic() { return challengeLogic; } @@ -760,88 +616,57 @@ public IslandLocatorLogic getIslandLocatorLogic() { @Override public void reloadConfig() { - reloadConfigs(); - registerEventsAndCommands(); + reload(); } - private void reloadConfigs() { - createFolders(); - HandlerList.unregisterAll(this); - if (challengeLogic != null) { - challengeLogic.shutdown(); - } - if (playerLogic != null) { - playerLogic.shutdown(); - } - if (islandLogic != null) { - islandLogic.shutdown(); - } - PlaceholderHandler.unregister(this); - VaultHandler.setupEconomy(); - VaultHandler.setupPermissions(); - if (Settings.loadPluginConfig(getConfig())) { - saveConfig(); - } - I18nUtil.clearCache(); - // Update all of the loaded configs. - FileUtil.reload(); + private void reload() { + shutdown(); + reloadLegacyStuff(); + startup(); + delayedEnable(); + } - String playerDbStorage = getConfig().getString("options.advanced.playerdb.storage", "yml"); - if (playerDbStorage.equalsIgnoreCase("yml")) { - playerDB = new FilePlayerDB(this); - } else if (playerDbStorage.equalsIgnoreCase("memory")) { - playerDB = new MemoryPlayerDB(getConfig()); - } else { - playerDB = new BukkitPlayerDB(); + private void shutdown() { + if (this.skyBlock != null) { + this.skyBlock.shutdown(this); + this.skyBlock = null; } + WorldManager.skyBlockWorld = null; // Force a reload on config. + } - getServer().getPluginManager().registerEvents(playerDB, this); - worldManager = new WorldManager(this); - eventLogic = new EventLogic(this); - teleportLogic = new TeleportLogic(this); - PlayerUtil.loadConfig(playerDB, getConfig()); - islandGenerator = new IslandGenerator(getDataFolder(), getConfig()); - perkLogic = new PerkLogic(this, islandGenerator); - challengeLogic = new ChallengeLogic(FileUtil.getYmlConfiguration("challenges.yml"), this); - menu = new SkyBlockMenu(this, challengeLogic); - configMenu = new ConfigMenu(this); - YmlConfiguration levelConfig = FileUtil.getYmlConfiguration("levelConfig.yml"); - // Disabled until AWE/FAWE supports 1.13 - //levelLogic = AsyncWorldEditHandler.isAWE() ? new AweLevelLogic(this, levelConfig) : new ChunkSnapshotLevelLogic(this, levelConfig); - levelLogic = new ChunkSnapshotLevelLogic(this, levelConfig); - orphanLogic = new OrphanLogic(this); - islandLocatorLogic = new IslandLocatorLogic(this); - islandLogic = new IslandLogic(this, directoryIslands, orphanLogic); - limitLogic = new LimitLogic(this); - blockLimitLogic = new BlockLimitLogic(this); - notifier = new PlayerNotifier(getConfig()); - playerLogic = new PlayerLogic(this); - if (autoRecalculateTask != null) { - autoRecalculateTask.cancel(); + private void startup() { + if (this.skyBlock != null) { + throw new IllegalStateException("Skyblock already started"); } - chatLogic = new ChatLogic(this); + + WorldManager.skyBlockWorld = null; // Force a re-import or what-ever... + WorldManager.skyBlockNetherWorld = null; + + Injector injector = Guice.createInjector(new SkyblockModule(this)); + this.skyBlock = injector.getInstance(SkyblockApp.class); + injector.injectMembers(this); + this.skyBlock.startup(this); } - public void registerEventsAndCommands() { - if (!isRequirementsMet(Bukkit.getConsoleSender(), null)) { - return; - } - registerEvents(); - int refreshEveryMinute = getConfig().getInt("options.island.autoRefreshScore", 0); - if (refreshEveryMinute > 0) { - int refreshTicks = refreshEveryMinute * 1200; // Ticks per minute - autoRecalculateTask = new RecalculateRunnable(this).runTaskTimer(this, refreshTicks, refreshTicks); - } else { - autoRecalculateTask = null; - } - confirmHandler = new ConfirmHandler(this, getConfig().getInt("options.advanced.confirmTimeout", 10)); - cooldownHandler = new CooldownHandler(this); - animationHandler = new AnimationHandler(this); - getCommand("island").setExecutor(new IslandCommand(this, menu)); - getCommand("challenges").setExecutor(new ChallengeCommand(this)); - getCommand("usb").setExecutor(new AdminCommand(this, confirmHandler, animationHandler)); - getCommand("islandtalk").setExecutor(new IslandTalkCommand(this, chatLogic)); - getCommand("partytalk").setExecutor(new PartyTalkCommand(this, chatLogic)); + private void delayedEnable() { + this.skyBlock.delayedEnable(this); + } + + /** + * Initializes/reloads the legacy static services. This has to be done before the object-oriented services are + * created, as some use the static services in their constructors. This should be refactored and integrated with + * the new system in the future. + */ + private void reloadLegacyStuff() { + createDataFolder(); + CommandManager.registerRequirements(this); + FileUtil.setDataFolder(getDataFolder()); + FileUtil.setAlwaysOverwrite("levelConfig.yml"); + Settings.loadPluginConfig(new PluginConfig().getYamlConfig()); + I18nUtil.initialize(getDataFolder(), Settings.locale); + saveConfig(); + // Update all of the loaded configs. + FileUtil.reload(); } public IslandLogic getIslandLogic() { @@ -869,10 +694,10 @@ public void execCommand(Player player, String command, boolean onlyInSky) { return; } command = command - .replaceAll("\\{player\\}", Matcher.quoteReplacement(player.getName())) - .replaceAll("\\{playerName\\}", Matcher.quoteReplacement(player.getDisplayName())) - .replaceAll("\\{playername\\}", Matcher.quoteReplacement(player.getDisplayName())) - .replaceAll("\\{position\\}", Matcher.quoteReplacement(LocationUtil.asString(player.getLocation()))); // Figure out what this should be + .replaceAll("\\{player\\}", Matcher.quoteReplacement(player.getName())) + .replaceAll("\\{playerName\\}", Matcher.quoteReplacement(player.getDisplayName())) + .replaceAll("\\{playername\\}", Matcher.quoteReplacement(player.getDisplayName())) + .replaceAll("\\{position\\}", Matcher.quoteReplacement(LocationUtil.asString(player.getLocation()))); // Figure out what this should be Matcher m = Pattern.compile("^\\{p=(?0?\\.[0-9]+)\\}(.*)$").matcher(command); if (m.matches()) { double p = Double.parseDouble(m.group("prob")); @@ -882,9 +707,9 @@ public void execCommand(Player player, String command, boolean onlyInSky) { } } m = Pattern.compile("^\\{d=(?[0-9]+)\\}(.*)$").matcher(command); - int delay = 0; + Duration delay = Duration.ZERO; if (m.matches()) { - delay = Integer.parseInt(m.group("delay")); + delay = Duration.ofMillis(Long.parseLong(m.group("delay"))); command = m.group(2); } if (command.contains("{party}")) { @@ -898,21 +723,11 @@ public void execCommand(Player player, String command, boolean onlyInSky) { } } - private void doExecCommand(final Player player, final String command, int delay) { - if (delay == 0) { - sync(new Runnable() { - @Override - public void run() { - doExecCommand(player, command); - } - }); - } else if (delay > 0) { - sync(new Runnable() { - @Override - public void run() { - doExecCommand(player, command); - } - }, delay); + private void doExecCommand(final Player player, final String command, Duration delay) { + if (delay.isZero()) { + scheduler.sync(() -> doExecCommand(player, command)); + } else if (delay.isPositive()) { + scheduler.sync(() -> doExecCommand(player, command), delay); } else { log(Level.INFO, "WARN: Misconfigured command found, with negative delay! " + command); } @@ -974,7 +789,7 @@ public List getTopTen() { @Override public List getRanks(int offset, int length) { - return islandLogic != null ? islandLogic.getRanks(offset, length) : Collections.emptyList(); + return islandLogic != null ? islandLogic.getRanks(offset, length) : Collections.emptyList(); } @Override @@ -993,8 +808,8 @@ public double getIslandLevel(Player player) { public IslandRank getIslandRank(Player player) { PlayerInfo playerInfo = getPlayerInfo(player); return islandLogic != null && playerInfo != null && playerInfo.getHasIsland() ? - islandLogic.getRank(playerInfo.locationForParty()) - : null; + islandLogic.getRank(playerInfo.locationForParty()) + : null; } @Override @@ -1014,46 +829,10 @@ public void fireChangeEvent(CommandSender sender, uSkyBlockEvent.Cause cause) { public void fireAsyncEvent(final Event event) { getServer().getScheduler().runTaskAsynchronously(this, - () -> getServer().getPluginManager().callEvent(event) + () -> getServer().getPluginManager().callEvent(event) ); } - public String getVersionInfo(boolean checkEnabled) { - PluginDescriptionFile description = getDescription(); - String msg = pre("\u00a77Name: \u00a7b{0}\n", description.getName()); - msg += pre("\u00a77Version: \u00a7b{0}\n", description.getVersion()); - msg += pre("\u00a77Description: \u00a7b{0}\n", description.getDescription()); - msg += pre("\u00a77Language: \u00a7b{0} ({1})\n", getConfig().get("language", "en"), I18nUtil.getI18n().getLocale()); - msg += pre("\u00a79 State: d={0}, r={1}, i={2}, p={3}, n={4}, awe={5}\n", Settings.island_distance, Settings.island_radius, - islandLogic.getSize(), playerLogic.getSize(), - Settings.nether_enabled, AsyncWorldEditHandler.isAWE()); - msg += pre("\u00a77Server: \u00a7e{0} {1}\n", getServer().getName(), getServer().getVersion()); - msg += pre("\u00a79 State: online={0}, bungee={1}\n", ServerUtil.isOnlineMode(), - ServerUtil.isBungeeEnabled()); - msg += pre("\u00a77------------------------------\n"); - for (String[] dep : depends) { - Plugin dependency = getServer().getPluginManager().getPlugin(dep[0]); - if (dependency != null) { - String status = pre("N/A"); - if (checkEnabled) { - if (dependency.isEnabled()) { - if (VersionUtil.getVersion(dependency.getDescription().getVersion()).isLT(dep[1])) { - status = pre("\u00a7eWRONG-VERSION"); - } else { - status = pre("\u00a72ENABLED"); - } - } else { - status = pre("\u00a74DISABLED"); - } - } - msg += pre("\u00a77\u00a7d{0} \u00a7f{1} \u00a77({2}\u00a77)\n", dependency.getName(), - dependency.getDescription().getVersion(), status); - } - } - msg += pre("\u00a77------------------------------\n"); - return msg; - } - public PlayerDB getPlayerDB() { return playerDB; } @@ -1067,7 +846,7 @@ private IslandScore adjustScore(IslandScore score, IslandInfo islandInfo) { public void calculateScoreAsync(final Player player, String islandName, final Callback callback) { final IslandInfo islandInfo = getIslandInfo(islandName); - getLevelLogic().calculateScoreAsync(islandInfo.getIslandLocation(), new Callback() { + getLevelLogic().calculateScoreAsync(islandInfo.getIslandLocation(), new Callback<>() { @Override public void run() { IslandScore score = adjustScore(getState(), islandInfo); @@ -1108,6 +887,14 @@ public IslandGenerator getIslandGenerator() { return islandGenerator; } + public HookManager getHookManager() { + return hookManager; + } + + public SkyUpdateChecker getUpdateChecker() { + return updateChecker; + } + public WorldManager getWorldManager() { return worldManager; } @@ -1116,6 +903,8 @@ public boolean isMaintenanceMode() { return maintenanceMode; } + // TODO: minoneer 06.02.2025: Do we need this? It is currently very buggy and makes a lot of the logic more complex. + /** * CAUTION! If anyone calls this with true, they MUST ensure it is later called with false, * or the plugin will effectively be in a locked state. @@ -1135,46 +924,40 @@ public void setMaintenanceMode(boolean maintenanceMode) { } @Override - public boolean onCommand(CommandSender sender, org.bukkit.command.Command command, String label, String[] args) { + public boolean onCommand(@NotNull CommandSender sender, org.bukkit.command.@NotNull Command command, @NotNull String label, String[] args) { if (!isRequirementsMet(sender, null, args)) { sender.sendMessage(tr("\u00a7cCommand is currently disabled!")); } return true; } - public BukkitTask async(Runnable runnable) { - return Bukkit.getScheduler().runTaskAsynchronously(this, runnable); - } - - public BukkitTask async(Runnable runnable, long delayMs) { - return Bukkit.getScheduler().runTaskLaterAsynchronously(this, runnable, - TimeUtil.millisAsTicks(delayMs)); - } - - public BukkitTask async(Runnable runnable, long delay, long every) { - return Bukkit.getScheduler().runTaskTimerAsynchronously(this, runnable, - TimeUtil.millisAsTicks(delay), - TimeUtil.millisAsTicks(every)); + public void execCommands(Player player, List cmdList) { + for (String cmd : cmdList) { + execCommand(player, cmd, false); + } } - public BukkitTask sync(Runnable runnable) { - return Bukkit.getScheduler().runTask(this, runnable); + /** + * Register this uSkyBlock instance with our API provider and Bukkit's ServicesManager. + */ + private void registerApi(UltimateSkyblock api) { + UltimateSkyblockProvider.registerPlugin(api); + getServer().getServicesManager().register(UltimateSkyblock.class, api, this, ServicePriority.Normal); } - public BukkitTask sync(Runnable runnable, long delayMs) { - return Bukkit.getScheduler().runTaskLater(this, runnable, - TimeUtil.millisAsTicks(delayMs)); + /** + * Deregister this uSkyBlock instance with our API provider and Bukkit's ServicesManager. + */ + private void deregisterApi(UltimateSkyblock api) { + UltimateSkyblockProvider.deregisterPlugin(); + getServer().getServicesManager().unregister(api); } - public BukkitTask sync(Runnable runnable, long delay, long every) { - return Bukkit.getScheduler().runTaskTimer(this, runnable, - TimeUtil.millisAsTicks(delay), - TimeUtil.millisAsTicks(every)); + public PluginConfig getPluginConfig() { + return config; } - public void execCommands(Player player, List cmdList) { - for (String cmd : cmdList) { - execCommand(player, cmd, false); - } + public Scheduler getScheduler() { + return new Scheduler(this); } } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/util/BiomeUtil.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/util/BiomeUtil.java deleted file mode 100644 index d1247ea53..000000000 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/util/BiomeUtil.java +++ /dev/null @@ -1,28 +0,0 @@ -package us.talabrek.ultimateskyblock.util; - -import org.bukkit.block.Biome; - -import java.util.HashMap; -import java.util.Map; - -/** - * Wrapper to support the idiotic compatibility issues reg. biomes - */ -public enum BiomeUtil {; - private static final Map biomeAlias = new HashMap<>(); - static { - biomeAlias.put("ICE_PLAINS", "ICE_FLATS"); // Bukkit 1.9 -> 1.10 - biomeAlias.put("FLOWER_FOREST", "MUTATED_FOREST"); // Bukkit 1.8 -> 1.9 - } - - public static Biome getBiome(String name) { - try { - return Biome.valueOf(name.toUpperCase()); - } catch (IllegalArgumentException e) { - if (biomeAlias.containsKey(name)) { - return getBiome(biomeAlias.get(name)); - } - } - return null; - } -} diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/util/EntityUtil.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/util/EntityUtil.java index 112ddf914..6f52ee980 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/util/EntityUtil.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/util/EntityUtil.java @@ -1,41 +1,12 @@ package us.talabrek.ultimateskyblock.util; import dk.lockfuglsang.minecraft.util.FormatUtil; -import org.bukkit.entity.Animals; -import org.bukkit.entity.Entity; import org.bukkit.entity.EntityType; -import org.bukkit.entity.Monster; -import org.bukkit.entity.NPC; - -import java.util.ArrayList; -import java.util.List; /** * Handles various entity operations. */ public enum EntityUtil {; - public static List getAnimals(List creatures) { - return getEntity(creatures, Animals.class); - } - - public static List getMonsters(List creatures) { - return getEntity(creatures, Monster.class); - } - - public static List getNPCs(List creatures) { - return getEntity(creatures, NPC.class); - } - - public static List getEntity(List creatures, Class typeClass) { - List list = new ArrayList<>(); - for (Entity e : creatures) { - if (typeClass.isInstance(e)) { - //noinspection unchecked - list.add((T) e); - } - } - return list; - } public static String getEntityDisplayName(EntityType entityType) { return FormatUtil.camelcase(entityType.name()); diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/util/FileUtil.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/util/FileUtil.java index dbe03258b..54389fb6c 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/util/FileUtil.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/util/FileUtil.java @@ -1,114 +1,18 @@ package us.talabrek.ultimateskyblock.util; -import dk.lockfuglsang.minecraft.yml.YmlConfiguration; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.configuration.InvalidConfigurationException; -import org.bukkit.configuration.file.FileConfiguration; - import java.io.File; -import java.io.FileInputStream; -import java.io.FilenameFilter; import java.io.IOException; import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.Reader; import java.nio.file.Files; import java.nio.file.Paths; import java.nio.file.StandardCopyOption; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Date; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Properties; -import java.util.concurrent.ConcurrentHashMap; -import java.util.logging.Level; -import java.util.logging.Logger; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; /** * Common file-utilities. */ public enum FileUtil {; - private static final Logger log = Logger.getLogger(FileUtil.class.getName()); - private static final Collection allwaysOverwrite = new ArrayList<>(); - private static final Collection neverOverwrite = new ArrayList<>(); - private static final Map configFiles = new ConcurrentHashMap<>(); - private static Locale locale = Locale.getDefault(); - private static File dataFolder; - - public static void setAllwaysOverwrite(String... configs) { - for (String s : configs) { - if (!allwaysOverwrite.contains(s)) { - allwaysOverwrite.add(s); - } - } - } - - public static void setNeverOverwrite(String... configs) { - for (String s : configs) { - if (!neverOverwrite.contains(s)) { - neverOverwrite.add(s); - } - } - } - - public static YmlConfiguration loadConfig(File file) { - YmlConfiguration config = new YmlConfiguration(); - readConfig(config, file); - return config; - } - - public static void readConfig(FileConfiguration config, File file) { - if (file == null) { - log.log(Level.INFO, "No " + file + " found, it will be created"); - return; - } - File configFile = file; - File localeFile = new File(configFile.getParentFile(), getLocaleName(file.getName())); - if (localeFile.exists() && localeFile.canRead()) { - configFile = localeFile; - } - if (!configFile.exists()) { - log.log(Level.INFO, "No " + configFile + " found, it will be created"); - return; - } - try (Reader rdr = new InputStreamReader(new FileInputStream(configFile), "UTF-8")) { - config.load(rdr); - } catch (InvalidConfigurationException e) { - log.log(Level.SEVERE, "Unable to read config file " + configFile, e); - if (configFile.exists()) { - try { - Files.copy(Paths.get(configFile.toURI()), Paths.get(configFile.getParent(), configFile.getName() + ".err"), StandardCopyOption.REPLACE_EXISTING); - } catch (IOException e1) { - // Ignore - we tried... - } - } - } catch (IOException e) { - log.log(Level.SEVERE, "Unable to read config file " + configFile, e); - } - } - - public static void readConfig(FileConfiguration config, InputStream inputStream) { - if (inputStream == null) { - return; - } - try (Reader rdr = new InputStreamReader(inputStream, "UTF-8")) { - config.load(rdr); - } catch (InvalidConfigurationException | IOException e) { - log.log(Level.SEVERE, "Unable to read configuration", e); - } - } - - public static FilenameFilter createYmlFilenameFilter() { - return new FilenameFilter() { - @Override - public boolean accept(File dir, String name) { - return name != null && name.endsWith(".yml"); - } - }; - } public static String getBasename(File file) { return getBasename(file.getName()); @@ -123,103 +27,11 @@ public static String getBasename(String file) { public static String getExtension(String fileName) { if (fileName != null && !fileName.isEmpty()) { - return fileName.substring(getBasename(fileName).length()+1); + return fileName.substring(getBasename(fileName).length() + 1); } return ""; } - private static File getDataFolder() { - return dataFolder != null ? dataFolder : new File("."); - } - - /** - * System-encoding agnostic config-reader - * Reads and returns the configuration found in: - *
-     *   a) the datafolder
-     *
-     *     a.1) if a config named "config_en.yml" exists - that is read.
-     *
-     *     a.2) otherwise "config.yml" is read (created if need be).
-     *
-     *   b) if the version differs from the same resource on the classpath
-     *
-     *      b.1) all nodes in the jar-file-version is merged* into the local-file
-     *
-     *      b.2) unless the configName is in the allwaysOverwrite - then the jar-version wins
-     *
-     * *merged: using data-conversion of special nodes.
-     * 
- */ - public static YmlConfiguration getYmlConfiguration(String configName) { - // Caching, for your convenience! (and a bigger memory print!) - - if (!configFiles.containsKey(configName)) { - YmlConfiguration config = new YmlConfiguration(); - try { - // read from datafolder! - File configFile = getConfigFile(configName); - YmlConfiguration configJar = new YmlConfiguration(); - readConfig(config, configFile); - readConfig(configJar, getResource(configName)); - if (!configFile.exists() || config.getInt("version", 0) < configJar.getInt("version", 0)) { - if (configFile.exists()) { - if (neverOverwrite.contains(configName)) { - configFiles.put(configName, config); - return config; - } - File backupFolder = new File(getDataFolder(), "backup"); - backupFolder.mkdirs(); - String bakFile = String.format("%1$s-%2$tY%2$tm%2$td-%2$tH%2$tM.yml", getBasename(configName), new Date()); - log.log(Level.INFO, "Moving existing config " + configName + " to backup/" + bakFile); - Files.move(Paths.get(configFile.toURI()), - Paths.get(new File(backupFolder, bakFile).toURI()), - StandardCopyOption.REPLACE_EXISTING); - if (allwaysOverwrite.contains(configName)) { - FileUtil.copy(getResource(configName), configFile); - config = configJar; - } else { - config = mergeConfig(configJar, config); - config.save(configFile); - config.load(configFile); - } - } else { - config = mergeConfig(configJar, config); - config.save(configFile); - config.load(configFile); - } - } - } catch (Exception e) { - log.log(Level.SEVERE, "Unable to handle config-file " + configName, e); - } - configFiles.put(configName, config); - } - return configFiles.get(configName); - } - - private static InputStream getResource(String configName) { - String resourceName = getLocaleName(configName); - ClassLoader loader = FileUtil.class.getClassLoader(); - InputStream resourceAsStream = loader.getResourceAsStream(resourceName); - if (resourceAsStream != null) { - return resourceAsStream; - } - return loader.getResourceAsStream(configName); - } - - private static String getLocaleName(String fileName) { - String baseName = getBasename(fileName); - return baseName + "_" + locale + fileName.substring(baseName.length()); - } - - public static File getConfigFile(String configName) { - File file = new File(getDataFolder(), getLocaleName(configName)); - if (file.exists()) { - return file; - } - return new File(getDataFolder(), configName); - } - public static void copy(InputStream stream, File file) throws IOException { if (stream == null || file == null) { throw new IOException("Invalid resource for " + file); @@ -227,106 +39,9 @@ public static void copy(InputStream stream, File file) throws IOException { Files.copy(stream, Paths.get(file.toURI()), StandardCopyOption.REPLACE_EXISTING); } - /** - * Merges the important keys from src to destination. - * @param src The source (containing the new values). - * @param dest The destination (containing old-values). - */ - private static YmlConfiguration mergeConfig(YmlConfiguration src, YmlConfiguration dest) { - int existing = dest.getInt("version"); - int version = src.getInt("version", existing); - dest.setDefaults(src); - dest.options().copyDefaults(true); - dest.addComments(src.getComments()); - dest.set("version", version); - dest.options().copyHeader(false); - src.options().copyHeader(false); - removeExcludes(dest); - moveNodes(src, dest); - replaceDefaults(src, dest); - return dest; + public static String generateTimestamp() { + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss"); + LocalDateTime now = LocalDateTime.now(); + return now.format(formatter); } - - /** - * Removes nodes from dest.defaults, that are specifically excluded in the config - */ - private static void removeExcludes(YmlConfiguration dest) { - List keys = dest.getStringList("merge-ignore"); - for (String key : keys) { - dest.getDefaults().set(key, null); - } - } - - private static void replaceDefaults(YmlConfiguration src, YmlConfiguration dest) { - ConfigurationSection forceSection = src.getConfigurationSection("force-replace"); - if (forceSection != null) { - for (String key : forceSection.getKeys(true)) { - Object def = forceSection.get(key, null); - Object value = dest.get(key, def); - Object newDef = src.get(key, null); - if (def != null && def.equals(value)) { - dest.set(key, newDef); - } - } - } - dest.set("force-replace", null); - dest.getDefaults().set("force-replace", null); - } - - private static void moveNodes(YmlConfiguration src, YmlConfiguration dest) { - ConfigurationSection moveSection = src.getConfigurationSection("move-nodes"); - if (moveSection != null) { - List keys = new ArrayList<>(moveSection.getKeys(true)); - Collections.reverse(keys); // Depth first - for (String key : keys) { - if (moveSection.isString(key)) { - String srcPath = key; - String tgtPath = moveSection.getString(key, key); - Object value = dest.get(srcPath); - if (value != null) { - dest.set(tgtPath, value); - dest.set(srcPath, null); - } - } else if (moveSection.isConfigurationSection(key)) { - // Check to see if dest section should be nuked... - if (dest.isConfigurationSection(key) && dest.getConfigurationSection(key).getKeys(false).isEmpty()) { - dest.set(key, null); - } - } - } - } - dest.set("move-nodes", null); - dest.getDefaults().set("move-nodes", null); - } - - public static void setDataFolder(File dataFolder) { - FileUtil.dataFolder = dataFolder; - configFiles.clear(); - } - - public static void setLocale(Locale loc) { - locale = loc != null ? loc : locale; - } - - public static void reload() { - for (Map.Entry e : configFiles.entrySet()) { - File configFile = new File(getDataFolder(), e.getKey()); - readConfig(e.getValue(), configFile); - } - } - - public static Properties readProperties(String fileName) { - File configFile = getConfigFile(fileName); - if (configFile != null && configFile.exists() && configFile.canRead()) { - Properties prop = new Properties(); - try (InputStreamReader in = new InputStreamReader(new FileInputStream(configFile), "UTF-8")) { - prop.load(in); - return prop; - } catch (IOException e) { - log.log(Level.WARNING, "Error reading " + fileName, e); - } - } - return null; - } - } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/util/GuiItemUtil.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/util/GuiItemUtil.java new file mode 100644 index 000000000..dd65039a1 --- /dev/null +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/util/GuiItemUtil.java @@ -0,0 +1,45 @@ +package us.talabrek.ultimateskyblock.util; + +import dk.lockfuglsang.minecraft.util.FormatUtil; +import org.bukkit.Material; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; + +import java.util.Locale; + +public class GuiItemUtil { + private GuiItemUtil() { + // Uninstantiable static utility class + } + + public static ItemStack createGuiDisplayItem(String material, String name) { + return createGuiDisplayItem(material, name, null); + } + + public static ItemStack createGuiDisplayItem(String material, String name, String description) { + Material type = Material.matchMaterial(material.toUpperCase(Locale.ROOT)); + if (type == null) { + type = Material.BARRIER; + } + return createGuiDisplayItem(type, name, description); + } + + public static ItemStack createGuiDisplayItem(Material material, String name) { + return createGuiDisplayItem(material, name, null); + } + + public static ItemStack createGuiDisplayItem(Material material, String name, String description) { + ItemStack itemStack = new ItemStack(material); + ItemMeta meta = itemStack.getItemMeta(); + if (meta != null) { + if (name != null) { + meta.setDisplayName(FormatUtil.normalize(name)); + } + if (description != null) { + meta.setLore(FormatUtil.wordWrap(FormatUtil.normalize(description), 30, 30)); + } + itemStack.setItemMeta(meta); + } + return itemStack; + } +} diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/util/LocationUtil.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/util/LocationUtil.java index e5c60821c..a6c0be86d 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/util/LocationUtil.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/util/LocationUtil.java @@ -1,6 +1,7 @@ package us.talabrek.ultimateskyblock.util; import dk.lockfuglsang.minecraft.po.I18nUtil; +import org.apache.commons.lang3.Validate; import org.bukkit.Bukkit; import org.bukkit.ChunkSnapshot; import org.bukkit.Location; @@ -8,13 +9,15 @@ import org.bukkit.World; import org.bukkit.block.Block; import org.bukkit.block.BlockFace; -import org.bukkit.material.MaterialData; -import org.bukkit.plugin.java.JavaPlugin; +import org.bukkit.block.data.BlockData; +import org.bukkit.material.Directional; import org.bukkit.scheduler.BukkitRunnable; +import org.jetbrains.annotations.NotNull; import us.talabrek.ultimateskyblock.Settings; import us.talabrek.ultimateskyblock.api.async.Callback; import java.util.Locale; +import java.util.Optional; import java.util.logging.Level; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -44,7 +47,7 @@ public static String asString(Location loc) { return null; } String s = ""; - if (loc.getWorld() != null && loc.getWorld().getName() != null) { + if (loc.getWorld() != null) { s += loc.getWorld().getName() + ":"; } s += String.format(Locale.ENGLISH, "%.2f,%.2f,%.2f", loc.getX(), loc.getY(), loc.getZ()); @@ -155,24 +158,9 @@ public static Location findChestLocation(final Location loc) { return null; } - /** - * Finds the nearest block to loc that is a chest. - * - * @param loc The location to scan for a chest. - * @return The location of the chest - */ - public static void findChestLocationAsync(final JavaPlugin plugin, final Location loc, final Callback callback) { - ChunkUtil.Chunks snapshots; - if (loc.getBlockX() % 16 == 0 && loc.getBlockZ() % 16 == 0) { - // chunk aligned - snapshots = ChunkUtil.getSnapshots4x4(loc); - } else { - snapshots = ChunkUtil.getSnapshots(loc, 1); - } - new ScanChest(loc, callback, snapshots).runTaskAsynchronously(plugin); - } + public static Optional findNearestSpawnLocation(@NotNull Location loc) { + Validate.notNull(loc.getWorld(), "World in the given Location cannot be null."); - public static Location findNearestSpawnLocation(Location loc) { loadChunkAt(loc); World world = loc.getWorld(); int px = loc.getBlockX(); @@ -182,9 +170,9 @@ public static Location findNearestSpawnLocation(Location loc) { if (chestBlock.getType() == Material.CHEST) { BlockFace primaryDirection = null; // Start by checking in front of the chest. - MaterialData data = chestBlock.getState().getData(); - if (data instanceof org.bukkit.material.Chest) { - primaryDirection = ((org.bukkit.material.Chest) data).getFacing(); + BlockData data = chestBlock.getBlockData(); + if (data instanceof Directional) { + primaryDirection = ((Directional) data).getFacing(); } if (primaryDirection == BlockFace.NORTH) { // Neg Z @@ -200,7 +188,7 @@ public static Location findNearestSpawnLocation(Location loc) { px += 1; // start one block in the east dir } } - return findNearestSafeLocation(new Location(loc.getWorld(), px, py, pz), loc); + return Optional.ofNullable(findNearestSafeLocation(new Location(loc.getWorld(), px, py, pz), loc)); } public static Location findNearestSafeLocation(Location loc, Location lookAt) { diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/util/MaterialUtil.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/util/MaterialUtil.java index 150e2da6d..f646fd2ba 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/util/MaterialUtil.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/util/MaterialUtil.java @@ -16,13 +16,14 @@ */ public enum MaterialUtil { ; - private static final Pattern MATERIAL_PROBABILITY = Pattern.compile("(\\{p=(?0\\.[0-9]+)\\})?\\s*(?[A-Z_0-9]+)"); - private static final Collection SANDS = Arrays.asList(Material.SAND, Material.GRAVEL); + private static final Pattern MATERIAL_PROBABILITY = Pattern.compile("(\\{p=(?0\\.[0-9]+)\\})?\\s*(?[A-Z_0-9a-z]+)"); + private static final Collection SANDS = Arrays.asList(Material.SAND, Material.RED_SAND, Material.GRAVEL, Material.WHITE_CONCRETE_POWDER, Material.ORANGE_CONCRETE_POWDER, Material.MAGENTA_CONCRETE_POWDER, Material.LIGHT_BLUE_CONCRETE_POWDER, Material.YELLOW_CONCRETE_POWDER, Material.LIME_CONCRETE_POWDER, Material.PINK_CONCRETE_POWDER, Material.GRAY_CONCRETE_POWDER, Material.LIGHT_GRAY_CONCRETE_POWDER, Material.CYAN_CONCRETE_POWDER, Material.PURPLE_CONCRETE_POWDER, Material.BLUE_CONCRETE_POWDER, Material.BROWN_CONCRETE_POWDER, Material.GREEN_CONCRETE_POWDER, Material.RED_CONCRETE_POWDER, Material.BLACK_CONCRETE_POWDER); private static final Collection WOOD_TOOLS = Arrays.asList(Material.WOODEN_AXE, Material.WOODEN_HOE, Material.WOODEN_PICKAXE, Material.WOODEN_SHOVEL, Material.WOODEN_SWORD); private static final Collection STONE_TOOLS = Arrays.asList(Material.STONE_AXE, Material.STONE_HOE, Material.STONE_PICKAXE, Material.STONE_SHOVEL, Material.STONE_SWORD); private static final Collection IRON_TOOLS = Arrays.asList(Material.IRON_AXE, Material.IRON_HOE, Material.IRON_PICKAXE, Material.IRON_SHOVEL, Material.IRON_SWORD); private static final Collection GOLD_TOOLS = Arrays.asList(Material.GOLDEN_AXE, Material.GOLDEN_HOE, Material.GOLDEN_PICKAXE, Material.GOLDEN_SHOVEL, Material.GOLDEN_SWORD); private static final Collection DIAMOND_TOOLS = Arrays.asList(Material.DIAMOND_AXE, Material.DIAMOND_HOE, Material.DIAMOND_PICKAXE, Material.DIAMOND_SHOVEL, Material.DIAMOND_SWORD); + private static final Collection NETHERITE_TOOLS = Arrays.asList(Material.NETHERITE_AXE, Material.NETHERITE_HOE, Material.NETHERITE_PICKAXE, Material.NETHERITE_SHOVEL, Material.NETHERITE_SWORD); private static final Collection TOOLS = new ArrayList<>(); static { TOOLS.addAll(WOOD_TOOLS); @@ -30,6 +31,7 @@ public enum MaterialUtil { TOOLS.addAll(IRON_TOOLS); TOOLS.addAll(GOLD_TOOLS); TOOLS.addAll(DIAMOND_TOOLS); + TOOLS.addAll(NETHERITE_TOOLS); } public static boolean isTool(Material type) { @@ -66,7 +68,7 @@ public static List createProbabilityList(List matLi for (String line : matList) { Matcher m = MATERIAL_PROBABILITY.matcher(line); if (m.matches()) { - Material mat = Material.getMaterial(m.group("id")); + Material mat = Material.matchMaterial(m.group("id")); if (mat == null) { Bukkit.getLogger().log(Level.WARNING, "Unknown material: " + line); continue; diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/util/MetaUtil.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/util/MetaUtil.java index 9da7da164..a73d9ed28 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/util/MetaUtil.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/util/MetaUtil.java @@ -1,30 +1,26 @@ package us.talabrek.ultimateskyblock.util; -import org.json.simple.parser.JSONParser; -import org.json.simple.parser.ParseException; +import com.google.gson.Gson; +import com.google.gson.JsonSyntaxException; +import com.google.gson.reflect.TypeToken; +import org.jetbrains.annotations.NotNull; -import java.io.IOException; -import java.io.StringReader; +import java.lang.reflect.Type; import java.util.Collections; import java.util.Map; -/** - */ public enum MetaUtil {; + private static final Gson gson = new Gson(); - public static Map createMap(String mapString) { + public static @NotNull Map createMap(String mapString) { if (mapString == null || mapString.isEmpty()) { return Collections.emptyMap(); } try { - Object parse = new JSONParser().parse(new StringReader(mapString)); - if (parse instanceof Map) { - return (Map)parse; - } - } catch (IOException | ParseException e) { - throw new IllegalArgumentException("Not a valid map: " + mapString, e); + Type mapType = new TypeToken>() {}.getType(); + return gson.fromJson(mapString, mapType); + } catch (JsonSyntaxException ex) { + throw new IllegalArgumentException("Not a valid map: " + mapString, ex); } - throw new IllegalArgumentException("Not a map: " + mapString); } - } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/util/PlayerUtil.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/util/PlayerUtil.java deleted file mode 100644 index 3b30bf757..000000000 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/util/PlayerUtil.java +++ /dev/null @@ -1,45 +0,0 @@ -package us.talabrek.ultimateskyblock.util; - -import org.bukkit.configuration.file.FileConfiguration; -import org.bukkit.entity.Player; -import org.bukkit.metadata.FixedMetadataValue; -import org.bukkit.metadata.MetadataValue; -import us.talabrek.ultimateskyblock.uSkyBlock; -import us.talabrek.ultimateskyblock.uuid.PlayerDB; - -import java.util.List; - -/** - * Wrappers for most player-related functionality. - */ -public enum PlayerUtil {; - private static boolean skipDisplayName = false; - private static PlayerDB playerDB; - - public static String getPlayerDisplayName(String playerName) { - if (skipDisplayName) { - return playerName; - } - if (playerDB != null) { - return playerDB.getDisplayName(playerName); - } - return playerName; - } - - public static String getMetadata(Player player, String key, String defaultValue) { - if (player.hasMetadata(key) && !player.getMetadata(key).isEmpty()) { - List metadata = player.getMetadata(key); - return metadata.get(0).asString(); - } - return defaultValue; - } - - public static void setMetadata(Player player, String key, String value) { - player.setMetadata(key, new FixedMetadataValue(uSkyBlock.getInstance(), value)); - } - - public static void loadConfig(PlayerDB playerDB, FileConfiguration config) { - PlayerUtil.playerDB = playerDB; - skipDisplayName = config.getBoolean("options.advanced.useDisplayNames", false); - } -} diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/util/PluginInfo.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/util/PluginInfo.java new file mode 100644 index 000000000..a1c37a2c4 --- /dev/null +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/util/PluginInfo.java @@ -0,0 +1,93 @@ +package us.talabrek.ultimateskyblock.util; + +import com.google.inject.Inject; +import dk.lockfuglsang.minecraft.po.I18nUtil; +import dk.lockfuglsang.minecraft.util.FormatUtil; +import dk.lockfuglsang.minecraft.util.VersionUtil; +import org.bukkit.Bukkit; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.PluginDescriptionFile; +import org.jetbrains.annotations.NotNull; +import us.talabrek.ultimateskyblock.PluginConfig; +import us.talabrek.ultimateskyblock.Settings; +import us.talabrek.ultimateskyblock.api.plugin.UpdateChecker; +import us.talabrek.ultimateskyblock.handler.AsyncWorldEditHandler; +import us.talabrek.ultimateskyblock.island.IslandLogic; +import us.talabrek.ultimateskyblock.player.PlayerLogic; + +import java.util.logging.Logger; + +import static dk.lockfuglsang.minecraft.po.I18nUtil.pre; +import static us.talabrek.ultimateskyblock.uSkyBlock.depends; + +public class PluginInfo { + + private final Plugin plugin; + private final IslandLogic islandLogic; + private final PluginConfig config; + private final UpdateChecker updateChecker; + private final PlayerLogic playerLogic; + private final Logger logger; + + @Inject + public PluginInfo( + @NotNull Plugin plugin, + @NotNull IslandLogic islandLogic, + @NotNull PluginConfig config, + @NotNull UpdateChecker updateChecker, + @NotNull PlayerLogic playerLogic, Logger logger + ) { + this.plugin = plugin; + this.islandLogic = islandLogic; + this.config = config; + this.updateChecker = updateChecker; + this.playerLogic = playerLogic; + this.logger = logger; + } + + public void startup() { + logger.info(FormatUtil.stripFormatting(getVersionInfo(false))); + } + + public String getVersionInfo(boolean checkEnabled) { + PluginDescriptionFile description = plugin.getDescription(); + StringBuilder msg = new StringBuilder(pre("\u00a77Name: \u00a7b{0}\n", description.getName())); + msg.append(pre("\u00a77Version: \u00a7b{0}\n", description.getVersion())); + msg.append(pre("\u00a77Description: \u00a7b{0}\n", description.getDescription())); + msg.append(pre("\u00a77Language: \u00a7b{0} ({1})\n", config.getYamlConfig().get("language", "en"), I18nUtil.getI18n().getLocale())); + msg.append(pre("\u00a79 State: d={0}, r={1}, i={2}, p={3}, n={4}, awe={5}\n", Settings.island_distance, Settings.island_radius, + islandLogic.getSize(), playerLogic.getSize(), + Settings.nether_enabled, AsyncWorldEditHandler.isAWE())); + msg.append(pre("\u00a77Server: \u00a7e{0} {1}\n", Bukkit.getName(), Bukkit.getVersion())); + msg.append(pre("\u00a79 State: online={0}, bungee={1}\n", ServerUtil.isOnlineMode(), + ServerUtil.isBungeeEnabled())); + msg.append(pre("\u00a77------------------------------\n")); + for (String[] dep : depends) { + Plugin dependency = Bukkit.getPluginManager().getPlugin(dep[0]); + if (dependency != null) { + String status = pre("N/A"); + if (checkEnabled) { + if (dependency.isEnabled()) { + if (VersionUtil.getVersion(dependency.getDescription().getVersion()).isLT(dep[1])) { + status = pre("\u00a7eWRONG-VERSION"); + } else { + status = pre("\u00a72ENABLED"); + } + } else { + status = pre("\u00a74DISABLED"); + } + } + msg.append(pre("\u00a77\u00a7d{0} \u00a7f{1} \u00a77({2}\u00a77)\n", dependency.getName(), + dependency.getDescription().getVersion(), status)); + } + } + msg.append(pre("\u00a77------------------------------\n")); + + if (config.getYamlConfig().getBoolean("plugin-updates.check", true) && updateChecker.isUpdateAvailable()) { + msg.append(pre("\u00a7bA new update of uSkyBlock is available: \u00a7f{0}\n", updateChecker.getLatestVersion())); + msg.append(pre("\u00a7fVisit {0} to download.\n", "https://www.uskyblock.ovh/get")); + } + + return msg.toString(); + } +} diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/util/ProgressTracker.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/util/ProgressTracker.java index 0db27f8a5..a4e827bfd 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/util/ProgressTracker.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/util/ProgressTracker.java @@ -2,34 +2,38 @@ import org.bukkit.command.CommandSender; +import java.time.Duration; +import java.time.Instant; + import static dk.lockfuglsang.minecraft.po.I18nUtil.tr; /** * General progress tracker using throttling */ public class ProgressTracker { - private double progressEveryPct; - private long progressEveryMs; + private final double progressEveryPct; + private final Duration progressEvery; private final String format; private final CommandSender sender; - private long lastProgressTime; + private Instant lastProgressTime; private float lastProgressPct; - public ProgressTracker(CommandSender sender, String format, double progressEveryPct, long progressEveryMs) { + public ProgressTracker(CommandSender sender, String format, double progressEveryPct, Duration progressEvery) { this.progressEveryPct = progressEveryPct; - this.progressEveryMs = progressEveryMs; + this.progressEvery = progressEvery; this.format = format; this.sender = sender; } public void progressUpdate(long progress, long total, Object... args) { - long now = System.currentTimeMillis(); - float pct = 100f * progress / (total > 0 ? total : 1); - if (now > (lastProgressTime + progressEveryMs) || pct > (lastProgressPct + progressEveryPct)) { + Instant now = Instant.now(); + float pct = 100f * progress / (total > 0 ? total : 1f); + if (lastProgressTime == null) { lastProgressTime = now; } + if (now.isAfter(lastProgressTime.plus(progressEvery)) || pct > (lastProgressPct + progressEveryPct)) { lastProgressPct = pct; lastProgressTime = now; - Object[] newArgs = new Object[args.length+3]; + Object[] newArgs = new Object[args.length + 3]; newArgs[0] = pct; newArgs[1] = progress; newArgs[2] = total; diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/util/Scheduler.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/util/Scheduler.java new file mode 100644 index 000000000..1550c8367 --- /dev/null +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/util/Scheduler.java @@ -0,0 +1,45 @@ +package us.talabrek.ultimateskyblock.util; + +import com.google.inject.Inject; +import org.bukkit.Bukkit; +import org.bukkit.plugin.Plugin; +import org.bukkit.scheduler.BukkitTask; +import org.jetbrains.annotations.NotNull; + +import java.time.Duration; + +import static dk.lockfuglsang.minecraft.util.TimeUtil.durationAsTicks; + +public class Scheduler { + + private final Plugin plugin; + + @Inject + public Scheduler(@NotNull Plugin plugin) { + this.plugin = plugin; + } + + public BukkitTask async(Runnable runnable) { + return Bukkit.getScheduler().runTaskAsynchronously(plugin, runnable); + } + + public BukkitTask async(Runnable runnable, Duration delay) { + return Bukkit.getScheduler().runTaskLaterAsynchronously(plugin, runnable, durationAsTicks(delay)); + } + + public BukkitTask async(Runnable runnable, Duration delay, Duration every) { + return Bukkit.getScheduler().runTaskTimerAsynchronously(plugin, runnable, durationAsTicks(delay), durationAsTicks(every)); + } + + public BukkitTask sync(Runnable runnable) { + return Bukkit.getScheduler().runTask(plugin, runnable); + } + + public BukkitTask sync(Runnable runnable, Duration delay) { + return Bukkit.getScheduler().runTaskLater(plugin, runnable, durationAsTicks(delay)); + } + + public BukkitTask sync(Runnable runnable, Duration delay, Duration every) { + return Bukkit.getScheduler().runTaskTimer(plugin, runnable, durationAsTicks(delay), durationAsTicks(every)); + } +} diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/util/ServerUtil.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/util/ServerUtil.java index 951b1c72e..43699c0a0 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/util/ServerUtil.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/util/ServerUtil.java @@ -1,9 +1,10 @@ package us.talabrek.ultimateskyblock.util; import dk.lockfuglsang.minecraft.file.FileUtil; -import dk.lockfuglsang.minecraft.yml.YmlConfiguration; import org.bukkit.Bukkit; import org.bukkit.OfflinePlayer; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.plugin.java.JavaPlugin; import java.io.File; @@ -18,7 +19,7 @@ public static boolean isBungeeEnabled() { boolean isBungeeMode = false; File spigotYml = new File(".", "spigot.yml"); if (spigotYml.exists()) { - YmlConfiguration spigotConfig = new YmlConfiguration(); + FileConfiguration spigotConfig = new YamlConfiguration(); FileUtil.readConfig(spigotConfig, spigotYml); isBungeeMode = spigotConfig.getBoolean("settings.bungeecord", false); } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/uuid/BukkitPlayerDB.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/uuid/BukkitPlayerDB.java index fcc8af908..15ee6b954 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/uuid/BukkitPlayerDB.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/uuid/BukkitPlayerDB.java @@ -45,7 +45,6 @@ public String getDisplayName(UUID uuid) { @Override public String getDisplayName(String playerName) { - //noinspection deprecation Player player = Bukkit.getPlayer(playerName); return player != null ? player.getDisplayName() : null; } @@ -74,7 +73,6 @@ public Player getPlayer(UUID uuid) { @Override public Player getPlayer(String name) { - //noinspection deprecation return Bukkit.getPlayer(name); } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/uuid/FilePlayerDB.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/uuid/FilePlayerDB.java index 439218242..82a44c744 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/uuid/FilePlayerDB.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/uuid/FilePlayerDB.java @@ -1,21 +1,24 @@ package us.talabrek.ultimateskyblock.uuid; import dk.lockfuglsang.minecraft.file.FileUtil; -import dk.lockfuglsang.minecraft.yml.YmlConfiguration; import org.bukkit.Bukkit; import org.bukkit.OfflinePlayer; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.scheduler.BukkitTask; +import org.jetbrains.annotations.NotNull; import us.talabrek.ultimateskyblock.uSkyBlock; +import us.talabrek.ultimateskyblock.util.Scheduler; import us.talabrek.ultimateskyblock.util.UUIDUtil; import java.io.File; import java.io.IOException; +import java.time.Duration; import java.util.HashSet; -import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; @@ -28,45 +31,43 @@ * PlayerDB backed by a simple yml-uuid2NameFile. */ public class FilePlayerDB implements PlayerDB { - private static final Logger log = Logger.getLogger(FilePlayerDB.class.getName()); + private final Logger logger; + private final Scheduler scheduler; private final File uuid2NameFile; - private final YmlConfiguration uuid2NameConfig; - private final uSkyBlock plugin; + private final FileConfiguration uuid2NameConfig; private boolean isShuttingDown = false; private volatile BukkitTask saveTask; - private long saveDelay; + private final Duration saveDelay; // These caches should NOT be guavaCaches, we need them alive most of the time private final Map name2uuidCache = new ConcurrentHashMap<>(); private final Map uuid2nameCache = new ConcurrentHashMap<>(); - public FilePlayerDB(uSkyBlock plugin) { - this.plugin = plugin; + public FilePlayerDB(@NotNull uSkyBlock plugin, @NotNull Scheduler scheduler, @NotNull Logger logger) { + this.scheduler = scheduler; + this.logger = logger; uuid2NameFile = new File(plugin.getDataFolder(), "uuid2name.yml"); - uuid2NameConfig = new YmlConfiguration(); + uuid2NameConfig = new YamlConfiguration(); if (uuid2NameFile.exists()) { FileUtil.readConfig(uuid2NameConfig, uuid2NameFile); } // Save max every 10 seconds - saveDelay = plugin.getConfig().getInt("playerdb.saveDelay", 10000); - plugin.async(new Runnable() { - @Override - public void run() { - synchronized (uuid2NameConfig) { - Set uuids = uuid2NameConfig.getKeys(false); - for (String uuid : uuids) { - UUID id = UUIDUtil.fromString(uuid); - String name = uuid2NameConfig.getString(uuid + ".name", null); - if (name != null && id != null) { - uuid2nameCache.put(id, name); - name2uuidCache.put(name, id); - List akas = uuid2NameConfig.getStringList(uuid + ".aka"); - for (String aka : akas) { - if (!name2uuidCache.containsKey(aka)) { - name2uuidCache.put(aka, id); - } + saveDelay = Duration.ofMillis(plugin.getConfig().getInt("playerdb.saveDelay", 10000)); + scheduler.async(() -> { + synchronized (uuid2NameConfig) { + Set uuids = uuid2NameConfig.getKeys(false); + for (String uuid : uuids) { + UUID id = UUIDUtil.fromString(uuid); + String name = uuid2NameConfig.getString(uuid + ".name", null); + if (name != null && id != null) { + uuid2nameCache.put(id, name); + name2uuidCache.put(name, id); + List akas = uuid2NameConfig.getStringList(uuid + ".aka"); + for (String aka : akas) { + if (!name2uuidCache.containsKey(aka)) { + name2uuidCache.put(aka, id); } } } @@ -146,12 +147,7 @@ public String getDisplayName(String playerName) { public Set getNames(String search) { HashSet names = new HashSet<>(uuid2nameCache.values()); String lowerSearch = search != null ? search.toLowerCase() : null; - for (Iterator it = names.iterator(); it.hasNext(); ) { - String name = it.next(); - if (name == null || (search != null && !name.toLowerCase().startsWith(lowerSearch))) { - it.remove(); - } - } + names.removeIf(name -> name == null || (search != null && !name.toLowerCase().startsWith(lowerSearch))); return names; } @@ -163,12 +159,7 @@ public void updatePlayer(final UUID id, final String name, final String displayN } else { if (saveTask == null) { // Only have one pending save-task at a time - saveTask = plugin.async(new Runnable() { - @Override - public void run() { - saveToFile(); - } - }, saveDelay); + saveTask = scheduler.async(this::saveToFile, saveDelay); } } } @@ -192,7 +183,6 @@ public Player getPlayer(String name) { if (uuid != null) { return getPlayer(uuid); } - //noinspection deprecation Player player = Bukkit.getPlayer(name); if (player != null) { updatePlayer(player.getUniqueId(), player.getName(), player.getDisplayName()); @@ -213,7 +203,7 @@ private void saveToFile() { uuid2NameConfig.save(uuid2NameFile); } } catch (IOException e) { - log.log(Level.INFO, "Error saving playerdb", e); + logger.log(Level.INFO, "Error saving playerdb", e); } finally { saveTask = null; } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/uuid/MemoryPlayerDB.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/uuid/MemoryPlayerDB.java index fd2fff293..fd3818a91 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/uuid/MemoryPlayerDB.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/uuid/MemoryPlayerDB.java @@ -5,11 +5,11 @@ import com.google.common.cache.LoadingCache; import org.bukkit.Bukkit; import org.bukkit.OfflinePlayer; -import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import us.talabrek.ultimateskyblock.PluginConfig; import java.util.HashSet; -import java.util.Iterator; import java.util.Set; import java.util.UUID; @@ -21,31 +21,31 @@ public class MemoryPlayerDB implements PlayerDB { private final LoadingCache uuidCache; private static final OfflinePlayer NULL_PLAYER = NullPlayer.INSTANCE; - public MemoryPlayerDB(FileConfiguration config) { + public MemoryPlayerDB(PluginConfig config) { nameCache = CacheBuilder - .from(config.getString("options.advanced.playerdb.nameCache", "maximumSize=1500,expireAfterWrite=30m,expireAfterAccess=15m")) - .build(new CacheLoader() { - @Override - public OfflinePlayer load(String name) throws Exception { - //noinspection deprecation - OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayer(name); - uuidCache.put(offlinePlayer.getUniqueId(), offlinePlayer); - return offlinePlayer; - } - }); + .from(config.getYamlConfig().getString("options.advanced.playerdb.nameCache", "maximumSize=1500,expireAfterWrite=30m,expireAfterAccess=15m")) + .build(new CacheLoader<>() { + @Override + public @NotNull OfflinePlayer load(@NotNull String name) { + //noinspection deprecation + OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayer(name); + uuidCache.put(offlinePlayer.getUniqueId(), offlinePlayer); + return offlinePlayer; + } + }); uuidCache = CacheBuilder - .from(config.getString("options.advanced.playerdb.uuidCache", "maximumSize=1500,expireAfterWrite=30m,expireAfterAccess=15m")) - .build(new CacheLoader() { - @Override - public OfflinePlayer load(UUID uuid) throws Exception { - OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayer(uuid); - if (offlinePlayer.getName() != null) { - nameCache.put(offlinePlayer.getName(), offlinePlayer); - return offlinePlayer; - } - return NULL_PLAYER; + .from(config.getYamlConfig().getString("options.advanced.playerdb.uuidCache", "maximumSize=1500,expireAfterWrite=30m,expireAfterAccess=15m")) + .build(new CacheLoader<>() { + @Override + public @NotNull OfflinePlayer load(@NotNull UUID uuid) { + OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayer(uuid); + if (offlinePlayer.getName() != null) { + nameCache.put(offlinePlayer.getName(), offlinePlayer); + return offlinePlayer; } - }); + return NULL_PLAYER; + } + }); } @Override @@ -93,29 +93,24 @@ public OfflinePlayer getOfflinePlayer(UUID uuid) { @Override public String getDisplayName(UUID uuid) { OfflinePlayer offlinePlayer = getOfflinePlayer(uuid); - return offlinePlayer != null && offlinePlayer.isOnline() && offlinePlayer.getPlayer() != null - ? offlinePlayer.getPlayer().getDisplayName() - : null; + return offlinePlayer != null && offlinePlayer.isOnline() && offlinePlayer.getPlayer() != null + ? offlinePlayer.getPlayer().getDisplayName() + : null; } @Override public String getDisplayName(String playerName) { OfflinePlayer offlinePlayer = getOfflinePlayer(playerName, true); - return offlinePlayer != null && offlinePlayer.isOnline() && offlinePlayer.getPlayer() != null - ? offlinePlayer.getPlayer().getDisplayName() - : null; + return offlinePlayer != null && offlinePlayer.isOnline() && offlinePlayer.getPlayer() != null + ? offlinePlayer.getPlayer().getDisplayName() + : null; } @Override public Set getNames(String search) { Set names = new HashSet<>(nameCache.asMap().keySet()); String lowerSearch = search != null ? search.toLowerCase() : null; - for (Iterator it = names.iterator(); it.hasNext(); ) { - String name = it.next(); - if (name == null || (search != null && !name.toLowerCase().startsWith(lowerSearch))) { - it.remove(); - } - } + names.removeIf(name -> name == null || (search != null && !name.toLowerCase().startsWith(lowerSearch))); return names; } @@ -143,4 +138,4 @@ public void shutdown() { nameCache.cleanUp(); uuidCache.cleanUp(); } -} \ No newline at end of file +} diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/uuid/NullPlayer.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/uuid/NullPlayer.java index fb18bed42..85c24c490 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/uuid/NullPlayer.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/uuid/NullPlayer.java @@ -1,9 +1,20 @@ package us.talabrek.ultimateskyblock.uuid; +import io.papermc.paper.persistence.PersistentDataContainerView; +import org.bukkit.BanEntry; import org.bukkit.Location; +import org.bukkit.Material; import org.bukkit.OfflinePlayer; +import org.bukkit.Statistic; +import org.bukkit.entity.EntityType; import org.bukkit.entity.Player; +import org.bukkit.profile.PlayerProfile; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import java.time.Duration; +import java.time.Instant; +import java.util.Date; import java.util.Map; import java.util.UUID; @@ -17,6 +28,11 @@ public boolean isOnline() { return false; } + @Override + public boolean isConnected() { + return false; + } + @Override public String getName() { return PlayerDB.UNKNOWN_PLAYER_NAME; @@ -27,11 +43,34 @@ public UUID getUniqueId() { return PlayerDB.UNKNOWN_PLAYER_UUID; } + @Override + public com.destroystokyo.paper.profile.PlayerProfile getPlayerProfile() { + return null; + } + @Override public boolean isBanned() { return false; } + @Nullable + @Override + public BanEntry ban(@Nullable String s, @Nullable Date date, @Nullable String s1) { + return null; + } + + @Nullable + @Override + public BanEntry ban(@Nullable String s, @Nullable Instant instant, @Nullable String s1) { + return null; + } + + @Nullable + @Override + public BanEntry ban(@Nullable String s, @Nullable Duration duration, @Nullable String s1) { + return null; + } + @Override public boolean isWhitelisted() { return true; @@ -67,6 +106,129 @@ public Location getBedSpawnLocation() { return null; } + @Override + public long getLastLogin() { + return 0; + } + + @Override + public long getLastSeen() { + return 0; + } + + @Nullable + @Override + public Location getRespawnLocation() { + return null; + } + + @Override + public void incrementStatistic(@NotNull Statistic statistic) throws IllegalArgumentException { + + } + + @Override + public void decrementStatistic(@NotNull Statistic statistic) throws IllegalArgumentException { + + } + + @Override + public void incrementStatistic(@NotNull Statistic statistic, int amount) throws IllegalArgumentException { + + } + + @Override + public void decrementStatistic(@NotNull Statistic statistic, int amount) throws IllegalArgumentException { + + } + + @Override + public void setStatistic(@NotNull Statistic statistic, int newValue) throws IllegalArgumentException { + + } + + @Override + public int getStatistic(@NotNull Statistic statistic) throws IllegalArgumentException { + return 0; + } + + @Override + public void incrementStatistic(@NotNull Statistic statistic, @NotNull Material material) throws IllegalArgumentException { + + } + + @Override + public void decrementStatistic(@NotNull Statistic statistic, @NotNull Material material) throws IllegalArgumentException { + + } + + @Override + public int getStatistic(@NotNull Statistic statistic, @NotNull Material material) throws IllegalArgumentException { + return 0; + } + + @Override + public void incrementStatistic(@NotNull Statistic statistic, @NotNull Material material, int amount) throws IllegalArgumentException { + + } + + @Override + public void decrementStatistic(@NotNull Statistic statistic, @NotNull Material material, int amount) throws IllegalArgumentException { + + } + + @Override + public void setStatistic(@NotNull Statistic statistic, @NotNull Material material, int newValue) throws IllegalArgumentException { + + } + + @Override + public void incrementStatistic(@NotNull Statistic statistic, @NotNull EntityType entityType) throws IllegalArgumentException { + + } + + @Override + public void decrementStatistic(@NotNull Statistic statistic, @NotNull EntityType entityType) throws IllegalArgumentException { + + } + + @Override + public int getStatistic(@NotNull Statistic statistic, @NotNull EntityType entityType) throws IllegalArgumentException { + return 0; + } + + @Override + public void incrementStatistic(@NotNull Statistic statistic, @NotNull EntityType entityType, int amount) throws IllegalArgumentException { + + } + + @Override + public void decrementStatistic(@NotNull Statistic statistic, @NotNull EntityType entityType, int amount) { + + } + + @Override + public void setStatistic(@NotNull Statistic statistic, @NotNull EntityType entityType, int newValue) { + + } + + @Nullable + @Override + public Location getLastDeathLocation() { + return null; + } + + @Nullable + @Override + public Location getLocation() { + return null; + } + + @Override + public PersistentDataContainerView getPersistentDataContainer() { + return null; + } + @Override public Map serialize() { return null; @@ -81,4 +243,6 @@ public boolean isOp() { public void setOp(boolean b) { } + + } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/world/ChunkRegenerator.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/world/ChunkRegenerator.java index 52b4f2f59..291aa2def 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/world/ChunkRegenerator.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/world/ChunkRegenerator.java @@ -1,13 +1,13 @@ package us.talabrek.ultimateskyblock.world; -import org.apache.commons.lang.Validate; +import org.apache.commons.lang3.Validate; +import org.bukkit.Bukkit; import org.bukkit.Chunk; import org.bukkit.World; -import org.bukkit.block.Biome; import org.bukkit.entity.Entity; import org.bukkit.entity.Player; +import org.bukkit.generator.BiomeProvider; import org.bukkit.generator.ChunkGenerator; -import org.bukkit.generator.ChunkGenerator.BiomeGrid; import org.bukkit.generator.ChunkGenerator.ChunkData; import org.bukkit.scheduler.BukkitScheduler; import org.bukkit.scheduler.BukkitTask; @@ -19,8 +19,6 @@ import java.util.List; import java.util.Random; -import static org.bukkit.World.Environment; - /** * Class responsible for regenerating chunks. */ @@ -51,7 +49,7 @@ public void regenerateChunks(@NotNull List chunkList, @Nullable Runnable task = scheduler.runTaskTimer(plugin, () -> { for (int i = 0; i <= CHUNKS_PER_TICK; i++) { if (!chunkList.isEmpty()) { - Chunk chunk = chunkList.remove(0); + Chunk chunk = chunkList.removeFirst(); regenerateChunk(chunk); } else { if (onCompletion != null) { @@ -68,20 +66,30 @@ public void regenerateChunks(@NotNull List chunkList, @Nullable Runnable * Regenerates the given {@link Chunk}, removing all it's entities except players and setting the default biome. * @param chunk Chunk to regenerate. */ - private void regenerateChunk(@NotNull Chunk chunk) { + public void regenerateChunk(@NotNull Chunk chunk) { Validate.notNull(chunk, "Chunk cannot be null"); spawnTeleportPlayers(chunk); - Random random = new Random(); - BiomeGrid biomeGrid = new DefaultBiomeGrid(world.getEnvironment()); - ChunkData chunkData = chunkGen.generateChunkData(world, random, chunk.getX(), chunk.getZ(), biomeGrid); + Random random = new Random(world.getSeed() + (long) chunk.getX() * (long) chunk.getZ()); + + World world = chunk.getWorld(); + + ChunkData chunkData = Bukkit.createChunkData(world); + chunkGen.generateNoise(world, random, chunk.getX(), chunk.getZ(), chunkData); + chunkGen.generateSurface(world, random, chunk.getX(), chunk.getZ(), chunkData); + chunkGen.generateBedrock(world, random, chunk.getX(), chunk.getZ(), chunkData); + chunkGen.generateCaves(world, random, chunk.getX(), chunk.getZ(), chunkData); + + BiomeProvider biomeProvider = chunkGen.getDefaultBiomeProvider(world); for (int x = 0; x < 16; x++) { for (int z = 0; z < 16; z++) { - for (int y = 0; y < chunk.getWorld().getMaxHeight(); y++) { + for (int y = world.getMinHeight(); y < world.getMaxHeight(); y++) { + if (biomeProvider != null) { + chunk.getBlock(x, y, z).setBiome(biomeProvider.getBiome(world, x, y, z)); + } chunk.getBlock(x, y, z).setBlockData(chunkData.getBlockData(x, y, z)); - chunk.getBlock(x, y, z).setBiome(biomeGrid.getBiome(x, y, z)); } } } @@ -110,49 +118,4 @@ private void spawnTeleportPlayers(@NotNull Chunk chunk) { } } } - - /** - * Default BiomeGrid used by uSkyBlock when regenerating chunks. - */ - static class DefaultBiomeGrid implements BiomeGrid { - private Biome defaultBiome; - - DefaultBiomeGrid(Environment env) { - switch (env) { - case THE_END: - defaultBiome = Biome.THE_END; - break; - case NETHER: - defaultBiome = Biome.NETHER; - break; - default: - defaultBiome = Biome.OCEAN; - break; - } - } - - @NotNull - @Deprecated - @Override - public Biome getBiome(int x, int z) { - return defaultBiome; - } - - @NotNull - @Override - public Biome getBiome(int x, int y, int z) { - return defaultBiome; - } - - @Deprecated - @Override - public void setBiome(int x, int z, @NotNull Biome bio) { - defaultBiome = bio; - } - - @Override - public void setBiome(int x, int y, int z, @NotNull Biome bio) { - defaultBiome = bio; - } - } } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/world/SingleBiomeProvider.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/world/SingleBiomeProvider.java new file mode 100644 index 000000000..819a28403 --- /dev/null +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/world/SingleBiomeProvider.java @@ -0,0 +1,26 @@ +package us.talabrek.ultimateskyblock.world; + +import org.bukkit.block.Biome; +import org.bukkit.generator.BiomeProvider; +import org.bukkit.generator.WorldInfo; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +public class SingleBiomeProvider extends BiomeProvider { + private final Biome biome; + + public SingleBiomeProvider(Biome biome) { + this.biome = biome; + } + + @Override + public @NotNull Biome getBiome(@NotNull WorldInfo worldInfo, int x, int y, int z) { + return biome; + } + + @Override + public @NotNull List getBiomes(@NotNull WorldInfo worldInfo) { + return List.of(biome); + } +} diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/world/SkyBlockChunkGenerator.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/world/SkyBlockChunkGenerator.java index dc3e85149..f0ede4c3c 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/world/SkyBlockChunkGenerator.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/world/SkyBlockChunkGenerator.java @@ -2,38 +2,33 @@ import org.bukkit.Location; import org.bukkit.World; -import org.bukkit.block.Biome; +import org.bukkit.generator.BiomeProvider; import org.bukkit.generator.BlockPopulator; import org.bukkit.generator.ChunkGenerator; +import org.bukkit.generator.WorldInfo; +import org.jetbrains.annotations.NotNull; import us.talabrek.ultimateskyblock.Settings; -import java.util.ArrayList; import java.util.List; import java.util.Random; +/** + * A chunk generator that generates an empty world with a single biome. + */ public class SkyBlockChunkGenerator extends ChunkGenerator { - private static final List emptyBlockPopulatorList = new ArrayList<>(); - @Override - public ChunkData generateChunkData(World world, Random random, int cx, int cz, BiomeGrid biome) { - ChunkData chunkData = createChunkData(world); - for (int x = 0; x <= 15; x++) { - for (int z = 0; z <= 15; z++) { - for (int y = 0; y < world.getMaxHeight(); y++) { - biome.setBiome(x, y, z, Biome.OCEAN); - } - } - } - return chunkData; + @NotNull + public List getDefaultPopulators(@NotNull World world) { + return List.of(); } @Override - public List getDefaultPopulators(World world) { - return emptyBlockPopulatorList; + public BiomeProvider getDefaultBiomeProvider(@NotNull WorldInfo worldInfo) { + return new SingleBiomeProvider(Settings.general_defaultBiome); } @Override - public Location getFixedSpawnLocation(World world, Random random) { + public Location getFixedSpawnLocation(@NotNull World world, @NotNull Random random) { return new Location(world, 0.5d, Settings.island_height, 0.5d); } } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/world/SkyBlockNetherChunkGenerator.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/world/SkyBlockNetherChunkGenerator.java index 2871e200a..c6b826ded 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/world/SkyBlockNetherChunkGenerator.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/world/SkyBlockNetherChunkGenerator.java @@ -3,97 +3,91 @@ import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.World; -import org.bukkit.block.Biome; +import org.bukkit.generator.BiomeProvider; import org.bukkit.generator.BlockPopulator; import org.bukkit.generator.ChunkGenerator; +import org.bukkit.generator.WorldInfo; +import org.jetbrains.annotations.NotNull; import us.talabrek.ultimateskyblock.Settings; -import java.util.ArrayList; import java.util.List; import java.util.Random; public class SkyBlockNetherChunkGenerator extends ChunkGenerator { - private static final List emptyBlockPopulatorList = new ArrayList(); + + // The nether height limits intentionally differ from World.getMinHeight() and World.getMaxHeight() to reflect + // vanilla nether limits. + private static final int MIN_HEIGHT = 0; + private static final int MAX_HEIGHT = 128; @Override - public ChunkData generateChunkData(World world, Random random, int cx, int cz, BiomeGrid biome) { - ChunkData chunkData = createChunkData(world); - for (int x = 0; x <= 15; x++) { - for (int z = 0; z <= 15; z++) { - for (int y = 0; y < world.getMaxHeight(); y++) { - biome.setBiome(x, y, z, Biome.NETHER); - } - } - } - int y = 0; - // Solid floor + public void generateNoise(@NotNull WorldInfo worldInfo, @NotNull Random random, int chunkX, int chunkZ, @NotNull ChunkData chunkData) { + // Generate lava sea + chunkData.setRegion(0, MIN_HEIGHT, 0, 16, Settings.nether_lava_level + 1, 16, Material.LAVA); + + // Generate netherrack ceiling + + // First layer with holes + int y = MAX_HEIGHT - 8; for (int x = 0; x < 16; x++) { for (int z = 0; z < 16; z++) { - chunkData.setBlock(x, y, z, Material.BEDROCK); + if (random.nextDouble() >= 0.20) { // 20% air + chunkData.setBlock(x, y, z, Material.NETHERRACK); + } } } - // Bedrock with holes in it - for (y = 1; y <= 5; y++) { - double yThreshold = 0.10 * y; + + // Solid block above + chunkData.setRegion(0, MAX_HEIGHT - 7, 0, 16, MAX_HEIGHT, 16, Material.NETHERRACK); + } + + @Override + public void generateBedrock(@NotNull WorldInfo worldInfo, @NotNull Random random, int chunkX, int chunkZ, @NotNull ChunkData chunkData) { + // Solid bedrock floor + chunkData.setRegion(0, MIN_HEIGHT, 0, 16, MIN_HEIGHT + 1, 16, Material.BEDROCK); + + // Bedrock floor with holes in it + for (int yOffset = 1; yOffset < 6; yOffset++) { + double yThreshold = 0.10 * yOffset; // 90% - 50% bedrock + int y = MIN_HEIGHT + yOffset; for (int x = 0; x < 16; x++) { for (int z = 0; z < 16; z++) { - if (random.nextDouble() >= yThreshold) { // 10%-50% air + if (random.nextDouble() >= yThreshold) { chunkData.setBlock(x, y, z, Material.BEDROCK); - } else { - chunkData.setBlock(x, y, z, Material.LAVA); } } } } - for (y = 6; y <= Settings.nether_lava_level; y++) { - for (int x = 0; x < 16; x++) { - for (int z = 0; z < 16; z++) { - chunkData.setBlock(x, y, z, Material.LAVA); - } - } - } - y = 120; - for (int x = 0; x < 16; x++) { - for (int z = 0; z < 16; z++) { - if (random.nextDouble() >= 0.20) { // 20% air - chunkData.setBlock(x, y, z, Material.NETHERRACK); - } - } - } - y = 121; - for (int x = 0; x < 16; x++) { - for (int z = 0; z < 16; z++) { - chunkData.setBlock(x, y, z, Material.NETHERRACK); - } - } - for (y = 122; y <= 126; y++) { - double yThreashold = 0.20 * (127 - y); + + // Bedrock ceiling with holes in it + for (int yOffset = 1; yOffset < 6; yOffset++) { + double yThreshold = 0.20 * (yOffset); // 20% - 100% bedrock + int y = MAX_HEIGHT - yOffset - 1; for (int x = 0; x < 16; x++) { for (int z = 0; z < 16; z++) { - if (random.nextDouble() >= yThreashold) { // 20%-100% bedrock + if (random.nextDouble() >= yThreshold) { chunkData.setBlock(x, y, z, Material.BEDROCK); - } else { - chunkData.setBlock(x, y, z, Material.NETHERRACK); } } } } - y = 127; - for (int x = 0; x < 16; x++) { - for (int z = 0; z < 16; z++) { - chunkData.setBlock(x, y, z, Material.BEDROCK); - } - } - return chunkData; + + // Solid bedrock ceiling + chunkData.setRegion(0, MAX_HEIGHT - 1, 0, 16, MAX_HEIGHT, 16, Material.BEDROCK); + } + + @Override + public @NotNull List getDefaultPopulators(@NotNull World world) { + return List.of(); } @Override - public List getDefaultPopulators(World world) { - return emptyBlockPopulatorList; + public BiomeProvider getDefaultBiomeProvider(@NotNull WorldInfo worldInfo) { + return new SingleBiomeProvider(Settings.general_defaultNetherBiome); } @Override - public Location getFixedSpawnLocation(World world, Random random) { - return new Location(world, 0, Settings.nether_height, 0); + public Location getFixedSpawnLocation(@NotNull World world, @NotNull Random random) { + return new Location(world, 0, Settings.nether_height, 0); } } diff --git a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/world/WorldManager.java b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/world/WorldManager.java index 6cb567469..c9e61dca2 100644 --- a/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/world/WorldManager.java +++ b/uSkyBlock-Core/src/main/java/us/talabrek/ultimateskyblock/world/WorldManager.java @@ -1,6 +1,9 @@ package us.talabrek.ultimateskyblock.world; -import org.apache.commons.lang.Validate; +import com.google.inject.Inject; +import com.google.inject.Singleton; +import dk.lockfuglsang.minecraft.util.TimeUtil; +import org.apache.commons.lang3.Validate; import org.bukkit.Bukkit; import org.bukkit.Chunk; import org.bukkit.Location; @@ -15,22 +18,29 @@ import org.bukkit.generator.ChunkGenerator; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import us.talabrek.ultimateskyblock.PluginConfig; import us.talabrek.ultimateskyblock.Settings; import us.talabrek.ultimateskyblock.handler.AsyncWorldEditHandler; -import us.talabrek.ultimateskyblock.handler.MultiverseCoreHandler; -import us.talabrek.ultimateskyblock.handler.MultiverseInventoriesHandler; +import us.talabrek.ultimateskyblock.hook.HookManager; import us.talabrek.ultimateskyblock.uSkyBlock; import us.talabrek.ultimateskyblock.util.LocationUtil; +import us.talabrek.ultimateskyblock.util.Scheduler; -import java.io.File; +import java.lang.reflect.InvocationTargetException; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.Arrays; import java.util.logging.Level; import java.util.logging.Logger; import static us.talabrek.ultimateskyblock.Settings.island_height; +@Singleton public class WorldManager { - private final uSkyBlock plugin; + private final Path schematicPath; + private final HookManager hookManager; + private final PluginConfig config; + private final Scheduler scheduler; private final Logger logger; public static volatile World skyBlockWorld; @@ -40,13 +50,24 @@ public class WorldManager { skyBlockWorld = null; } - public WorldManager(@NotNull uSkyBlock plugin) { - this.plugin = plugin; - this.logger = plugin.getLogger(); + @Inject + public WorldManager( + @NotNull uSkyBlock plugin, + @NotNull Logger logger, + @NotNull PluginConfig config, + @NotNull HookManager hookManager, + @NotNull Scheduler scheduler + ) { + this.schematicPath = plugin.getDataFolder().toPath().resolve("schematics").resolve("spawn.schem"); + this.hookManager = hookManager; + this.config = config; + this.logger = logger; + this.scheduler = scheduler; } /** * Get the {@link ChunkRegenerator} for the given {@link World}. + * * @param world World to get the ChunkRegenerator for. * @return ChunkRegenerator for the given world. */ @@ -57,6 +78,7 @@ public ChunkRegenerator getChunkRegenerator(@NotNull World world) { /** * Removes all unnamed {@link Monster}'s at the given {@link Location}. + * * @param target Location to remove unnamed monsters. */ public void removeCreatures(@Nullable final Location target) { @@ -70,12 +92,12 @@ public void removeCreatures(@Nullable final Location target) { for (int x = -1; x <= 1; ++x) { for (int z = -1; z <= 1; ++z) { Chunk chunk = target.getWorld().getChunkAt( - new Location(target.getWorld(), (px + x * 16), py, (pz + z * 16))); + new Location(target.getWorld(), (px + x * 16), py, (pz + z * 16))); Arrays.stream(chunk.getEntities()) - .filter(entity -> entity instanceof Monster) - .filter(entity -> entity.getCustomName() == null) - .forEach(Entity::remove); + .filter(entity -> entity instanceof Monster) + .filter(entity -> entity.getCustomName() == null) + .forEach(Entity::remove); } } } @@ -83,13 +105,14 @@ public void removeCreatures(@Nullable final Location target) { /** * Sets the spawn location for the given {@link World} if currently unset. Creates a safe spawn location if * necessary by calling {@link WorldManager#createSpawn(Location)}. - * @param world World to setup. + * + * @param world World to setup. * @param islandHeight Height at which islands will be created. */ private void setupWorld(@NotNull World world, int islandHeight) { Validate.notNull(world, "World cannot be null"); - if (!plugin.getConfig().getBoolean("options.advanced.manageSpawn")) { + if (!config.getYamlConfig().getBoolean("options.advanced.manageSpawn")) { return; } @@ -99,6 +122,9 @@ private void setupWorld(@NotNull World world, int islandHeight) { Location spawnLocation = world.getSpawnLocation(); if (!LocationUtil.isSafeLocation(spawnLocation)) { + // Warn the user why we're doing this, because it's a FAQ on the forums: + logger.warning("Spawn location in " + world.getName() + " is considered unsafe. " + + "Placing default spawn. This check can be disabled in config.yml, option manageSpawn."); createSpawn(spawnLocation); } } @@ -106,18 +132,17 @@ private void setupWorld(@NotNull World world, int islandHeight) { /** * Creates the world spawn at the given {@link Location}. Places the spawn schematic if * configured and when it exists on disk. Places a gold block with two air above it otherwise. + * * @param spawnLocation Location to create the spawn at. */ private void createSpawn(@NotNull Location spawnLocation) { Validate.notNull(spawnLocation, "SpawnLocation cannot be null"); Validate.notNull(spawnLocation.getWorld(), "SpawnLocation#world cannot be null"); - File schematic = new File(plugin.getDataFolder() + File.separator + "schematics" + - File.separator + "spawn.schem"); World world = spawnLocation.getWorld(); - if (plugin.getConfig().getInt("options.general.spawnSize", 0) > 32 && schematic.exists()) { - AsyncWorldEditHandler.loadIslandSchematic(schematic, spawnLocation, null); + if (config.getYamlConfig().getInt("options.general.spawnSize", 0) > 32 && Files.exists(schematicPath)) { + AsyncWorldEditHandler.loadIslandSchematic(schematicPath.toFile(), spawnLocation, null); } else { Block spawnBlock = world.getBlockAt(spawnLocation).getRelative(BlockFace.DOWN); spawnBlock.setType(Material.GOLD_BLOCK); @@ -129,46 +154,55 @@ private void createSpawn(@NotNull Location spawnLocation) { /** * Gets the {@link ChunkGenerator} responsible for generating chunks in the overworld skyworld. + * * @return ChunkGenerator for overworld skyworld. */ @NotNull private ChunkGenerator getOverworldGenerator() { try { - String clazz = plugin.getConfig().getString("options.advanced.chunk-generator", - "us.talabrek.ultimateskyblock.world.SkyBlockChunkGenerator"); - Object generator = Class.forName(clazz).newInstance(); + String clazz = config.getYamlConfig().getString("options.advanced.chunk-generator", + "us.talabrek.ultimateskyblock.world.SkyBlockChunkGenerator"); + Object generator = Class.forName(clazz).getDeclaredConstructor().newInstance(); if (generator instanceof ChunkGenerator) { return (ChunkGenerator) generator; } - } catch (ClassNotFoundException | IllegalAccessException | InstantiationException ex) { + } catch (ClassNotFoundException ex) { logger.log(Level.WARNING, "Invalid overworld chunk-generator configured: " + ex); + } catch (IllegalAccessException | InstantiationException | InvocationTargetException | + NoSuchMethodException ex) { + logger.log(Level.WARNING, "Unable to instantiate overworld chunk-generator: " + ex); } return new SkyBlockChunkGenerator(); } /** * Gets the {@link ChunkGenerator} responsible for generating chunks in the nether skyworld. + * * @return ChunkGenerator for nether skyworld. */ @NotNull private ChunkGenerator getNetherGenerator() { try { - String clazz = plugin.getConfig().getString("nether.chunk-generator", - "us.talabrek.ultimateskyblock.world.SkyBlockNetherChunkGenerator"); - Object generator = Class.forName(clazz).newInstance(); + String clazz = config.getYamlConfig().getString("nether.chunk-generator", + "us.talabrek.ultimateskyblock.world.SkyBlockNetherChunkGenerator"); + Object generator = Class.forName(clazz).getDeclaredConstructor().newInstance(); if (generator instanceof ChunkGenerator) { return (ChunkGenerator) generator; } - } catch (ClassNotFoundException | IllegalAccessException | InstantiationException ex) { + } catch (ClassNotFoundException ex) { logger.log(Level.WARNING, "Invalid nether chunk-generator configured: " + ex); + } catch (IllegalAccessException | InstantiationException | InvocationTargetException | + NoSuchMethodException ex) { + logger.log(Level.WARNING, "Unable to instantiate nether chunk-generator: " + ex); } return new SkyBlockNetherChunkGenerator(); } /** * Gets a {@link ChunkGenerator} for use in a default world, as specified in the server configuration + * * @param worldName Name of the world that this will be applied to - * @param id Unique ID, if any, that was specified to indicate which generator was requested + * @param id Unique ID, if any, that was specified to indicate which generator was requested * @return ChunkGenerator for use in the default world generation */ @Nullable @@ -176,13 +210,14 @@ public ChunkGenerator getDefaultWorldGenerator(@NotNull String worldName, @Nulla Validate.notNull(worldName, "WorldName cannot be null"); return ((id != null && id.endsWith("nether")) || (worldName.endsWith("nether"))) - && Settings.nether_enabled - ? getNetherGenerator() - : getOverworldGenerator(); + && Settings.nether_enabled + ? getNetherGenerator() + : getOverworldGenerator(); } /** * Gets the skyblock island {@link World}. Creates and/or imports the world if necessary. + * * @return Skyblock island world. */ @NotNull @@ -192,57 +227,72 @@ public synchronized World getWorld() { ChunkGenerator skyGenerator = getOverworldGenerator(); ChunkGenerator worldGenerator = skyBlockWorld != null ? skyBlockWorld.getGenerator() : null; if (skyBlockWorld == null - || skyBlockWorld.canGenerateStructures() - || worldGenerator == null - || !worldGenerator.getClass().getName().equals(skyGenerator.getClass().getName())) { + || skyBlockWorld.canGenerateStructures() + || worldGenerator == null + || !worldGenerator.getClass().getName().equals(skyGenerator.getClass().getName())) { skyBlockWorld = WorldCreator - .name(Settings.general_worldName) - .type(WorldType.NORMAL) - .generateStructures(false) - .environment(World.Environment.NORMAL) - .generator(skyGenerator) - .createWorld(); + .name(Settings.general_worldName) + .type(WorldType.NORMAL) + .generateStructures(false) + .environment(World.Environment.NORMAL) + .generator(skyGenerator) + .createWorld(); skyBlockWorld.save(); } - MultiverseCoreHandler.importWorld(skyBlockWorld); - setupWorld(skyBlockWorld, island_height); + + scheduler.sync(() -> + hookManager.getMultiverse().ifPresent(hook -> { + hook.registerOverworld(skyBlockWorld); + setupWorld(skyBlockWorld, Settings.island_height); + }), TimeUtil.ticksAsDuration(config.getYamlConfig().getLong("init.initDelay", 50L) + 40L)); } + return skyBlockWorld; } /** * Gets the skyblock nether island {@link World}. Creates and/or imports the world if necessary. Returns null if * the nether is not enabled in the plugin configuration. + * * @return Skyblock nether island world, or null if nether is disabled. */ @Nullable public synchronized World getNetherWorld() { - if (skyBlockNetherWorld == null && Settings.nether_enabled) { + if (!Settings.nether_enabled) { + return null; + } + + if (skyBlockNetherWorld == null) { skyBlockNetherWorld = Bukkit.getWorld(Settings.general_worldName + "_nether"); ChunkGenerator skyGenerator = getNetherGenerator(); ChunkGenerator worldGenerator = skyBlockNetherWorld != null ? skyBlockNetherWorld.getGenerator() : null; if (skyBlockNetherWorld == null - || skyBlockNetherWorld.canGenerateStructures() - || worldGenerator == null - || !worldGenerator.getClass().getName().equals(skyGenerator.getClass().getName())) { + || skyBlockNetherWorld.canGenerateStructures() + || worldGenerator == null + || !worldGenerator.getClass().getName().equals(skyGenerator.getClass().getName())) { skyBlockNetherWorld = WorldCreator - .name(Settings.general_worldName + "_nether") - .type(WorldType.NORMAL) - .generateStructures(false) - .environment(World.Environment.NETHER) - .generator(skyGenerator) - .createWorld(); + .name(Settings.general_worldName + "_nether") + .type(WorldType.NORMAL) + .generateStructures(false) + .environment(World.Environment.NETHER) + .generator(skyGenerator) + .createWorld(); skyBlockNetherWorld.save(); } - MultiverseCoreHandler.importNetherWorld(skyBlockNetherWorld); - setupWorld(skyBlockNetherWorld, island_height / 2); - MultiverseInventoriesHandler.linkWorlds(getWorld(), skyBlockNetherWorld); + + scheduler.sync(() -> + hookManager.getMultiverse().ifPresent(hook -> { + hook.registerNetherworld(skyBlockNetherWorld); + setupWorld(skyBlockNetherWorld, island_height / 2); + }), TimeUtil.ticksAsDuration(config.getYamlConfig().getLong("init.initDelay", 50L) + 100L)); } + return skyBlockNetherWorld; } /** * Checks if the given {@link World} is the skyblock island world. + * * @param world World to check. * @return True if the given world is the skyblock island world, false otherwise. */ @@ -256,6 +306,7 @@ public boolean isSkyWorld(@Nullable World world) { /** * Checks if the given {@link World} is the skyblock nether island world. + * * @param world World to check. * @return True if the given world is the skyblock nether island world, false otherwise. */ @@ -270,6 +321,7 @@ public boolean isSkyNether(@Nullable World world) { /** * Checks if the given {@link World} is associated with Ultimate Skyblock. + * * @param world World to check. * @return True if the given world is associated with the plugin, false otherwise. */ @@ -279,7 +331,7 @@ public boolean isSkyAssociatedWorld(@Nullable World world) { } return world.getName().startsWith(WorldManager.skyBlockWorld.getName()) - && !(world.getEnvironment() == World.Environment.NETHER && !Settings.nether_enabled) - && !(world.getEnvironment() == World.Environment.THE_END); + && !(world.getEnvironment() == World.Environment.NETHER && !Settings.nether_enabled) + && !(world.getEnvironment() == World.Environment.THE_END); } } diff --git a/uSkyBlock-Core/src/main/po/de.po b/uSkyBlock-Core/src/main/po/de.po index 4aa4e30b4..d0c2ea510 100644 --- a/uSkyBlock-Core/src/main/po/de.po +++ b/uSkyBlock-Core/src/main/po/de.po @@ -3,7 +3,7 @@ msgstr "" "Project-Id-Version: 1.5\n" "Report-Msgid-Bugs-To: \n" "PO-Revision-Date: 2016-10-16 17:54+0200\n" -"Last-Translator: I9hdkill I9hdkill@spigotmc.org\n" +"Last-Translator: Treppenhaus\n" "Language-Team: DE \n" "Language: de\n" "MIME-Version: 1.0\n" @@ -93,19 +93,19 @@ msgid "{0}''s Wither" msgstr "{0}''s Wither" msgid "§eYou can not use another island''s portals!" -msgstr "" +msgstr "§eDu kannst keine Portale auf fremden Inseln benutzen!" msgid "§eVillager-trading isn't allowed." -msgstr "" +msgstr "§eDas Handeln mit Dorfbewohnern ist nicht erlaubt." msgid "§eTrading isn't allowed on other islands. Do it in spawn." msgstr "§eHandeln ist auf fremden Inseln nicht erlaubt. Handel am Spawn" msgid "§eRiding is only allowed on your own island!" -msgstr "" +msgstr "§eDu kannst nur auf deiner eigenen Insel reiten!" msgid "§eYou cannot break vehicles while being a visitor!" -msgstr "" +msgstr "§eAls Besucher kannst du keine Fahrzeuge zerstören!" msgid "§4You can only convert obsidian once every 10 seconds" msgstr "§4Du kannst Obsidian nur alle 10 Sekunden konvertieren" @@ -120,11 +120,10 @@ msgid "§4It''s a bad idea to replace your lava!" msgstr "§4Es ist eine schlechte Idee deine Lava zu ersetzen!" msgid "§eYou cannot hurt island-members." -msgstr "§eDu kannst keine Island Mitglieder angreifen" +msgstr "§eDu kannst keine Insel-Mitglieder angreifen" msgid "§4That player has forbidden you from teleporting to their island." -msgstr "" -"§4Dieser Spieler hat dir Verboten sich auf seine Insel zu Teleportieren." +msgstr "§4Dieser Spieler hat dir verboten, dich zu seiner Insel zu teleportieren!" msgid "§4That island is §clocked.§e No teleporting to the island." msgstr "§4Diese Insel ist gesperrt. Kein Teleport zu dieser Insel" @@ -134,21 +133,24 @@ msgid "" "§4{0} is limited. §eScanning your island to see if you are allowed to place " "more, please be patient" msgstr "" +"§4{0} sind begrenzt. §eDeine Insel wird gescannt, um zu sehen, ob du mehr davon platzieren kannst." +"Bitte Gedulde dich einen Moment..." msgid "§e... Scanning complete, you can try again" -msgstr "" +msgstr "§e... Scan abgeschlossen! Du kannst es erneut versuchen." #, java-format msgid "" "§4You''ve hit the {0} limit!§e You can''t have more of that type on your " "island!§9 Max: {1,number}" -msgstr "" +msgstr "§4Du hast das Limit an {0} erreicht!" +"§9 Max: {1,number}" msgid "§eYou can only use spawn-eggs on your own island." -msgstr "§eDu kannst Spawn-Eier nur auf Deiner eigenen Insel nutzen" +msgstr "§eDu kannst Spawneggs nur auf deiner eigenen Insel nutzen" msgid "§cYou have reached your spawn-limit for your island." -msgstr "§cDas Spawnlimit für Deine Insel wurde erreicht" +msgstr "§cDas Spawnlimit für deine Insel wurde erreicht" msgid "§eVisitors can't drop items!" msgstr "§eBesucher können keine Items droppen" @@ -167,11 +169,10 @@ msgid "§cLocked:§e That island is locked! No entry allowed." msgstr "§cAbgeschloßen:§e Diese Insel ist geschlossen, kein Eintritt möglich" msgid "Something went wrong saving the island and/or party data!" -msgstr "Etwas ist Schiefgelaufen beim speichern der Insel/Spieler Daten...!" +msgstr "Beim speichern deiner Insel/Spieler-Daten ist etwas schief gelaufen...!" msgid "Converting data to UUID, this make take a while!" -msgstr "" -"Die Daten werden in das UUID Format konvertiert. Dies kann lange dauern!" +msgstr "Die Daten werden in das UUID Format konvertiert. Dies kann lange dauern!" msgid "§cMAINTENANCE:§e uSkyBlock is currently in maintenance mode" msgstr "§cWARTUNGSMODUS:§e uSkyBlock ist derzeit im Wartungsmodus" @@ -193,12 +194,10 @@ msgid "§4Player is already assigned to this island!" msgstr "§4Dieser Spieler gehört bereits zu deiner Insel!" msgid "§4You must be closer to your island to set your skyblock home!" -msgstr "" -"§4Du musst Dich näher an Deiner Insel befinden, um dein SkyBlock Home zu " -"setzen!" +msgstr "§4Du musst Dich näher an deiner Insel befinden, um dein SkyBlock Home zu setzen!" msgid "§aYour skyblock home has been set to your current location." -msgstr "§aDein SkyBlock Home wurde an Deine aktuelle Position gesetzt." +msgstr "§aDein SkyBlock Home wurde an deine aktuelle Position gesetzt." msgid "§4Your current location is not a safe home-location." msgstr "§4Deine jetztige Home-Position ist nicht sicher" @@ -226,7 +225,7 @@ msgstr "§cDieses Kommando ist derzeit deaktiviert!" #, java-format msgid " §f{0}x §7{1}" -msgstr "" +msgstr " §f{0}x §7{1}" #, java-format msgid "§eStill the following blocks short: {0}" @@ -342,12 +341,16 @@ msgid "" "members to teleport to\n" "the island." msgstr "" +"setze den Insel-Warp,\n" +"welcher nicht-Gruppenmitgliedern\n" +"erlaubt, sich zu der Insel\n" +"zu teleportieren." msgid "Toggle Island Warp" msgstr "Schaltet den Insel Warp an und aus" msgid "toggle the island''s warp." -msgstr "kann den Insel Warp aktivieren und deaktivieren" +msgstr "den Insel Warp aktivieren und deaktivieren" msgid "" "toggle the island''s warp,\n" @@ -355,12 +358,16 @@ msgid "" "on or off at anytime, but\n" "not set the location." msgstr "" +"erlaubt, den Inselwarp zu,\n" +"de-/aktivieren, aber nicht\n" +"die Position zu setzen." + msgid "Invite Players" msgstr "Spieler einladen" msgid "invite others to the island." -msgstr "kann andere Spieler auf die Insel einladen" +msgstr "andere Spieler auf die Insel einladen" msgid "" "invite\n" @@ -370,13 +377,13 @@ msgid "" msgstr "" "Lade noch andere Spieler\n" "auf Deine Insel ein\n" -"Es ist noch mehr Platz!" +"Hier ist genug Platz!" msgid "Kick Players" msgstr "Spieler kicken" msgid "kick others from the island." -msgstr "kann andere von der Insel entfernen" +msgstr "andere von der Insel entfernen" msgid "" "kick\n" @@ -385,9 +392,9 @@ msgid "" "the island leader." msgstr "" "Rauswerfen\n" -"Kann anderen Mitglieder rauswerfen\n" -"aber können nicht den \n" -"Besitzer rauswerfen" +"andere Mitglieder rauswerfen\n" +"aber nicht den\n" +"Besitzer." msgid "Ocean" msgstr "Ocean" @@ -443,7 +450,7 @@ msgid "" "spawn. Hostile mobs will\n" "spawn normally." msgstr "" -"Das Dschungel biom ist farbenfroh\n" +"Das Dschungelbiom ist farbenfroh\n" "Passive Tiere (Inklusive Katzen) spawnen\n" "Monster spawnen normal" @@ -550,7 +557,7 @@ msgstr "" "spawnen normal" msgid "Flower Forest" -msgstr "" +msgstr "Blumenwald" msgid "" "The flower forest biome.\n" @@ -558,7 +565,7 @@ msgid "" "normally and hostile\n" "mobs will spawn." msgstr "" -"The flower forest biome.\n" +"The Blumenwald-Biom.\n" "normale und passive Mobs\n" "spawnen normal" @@ -579,14 +586,14 @@ msgstr "" "spawnen normal" msgid "Ice Plains" -msgstr "" +msgstr "Ice Plains" msgid "" "The ice-plains biome is an advanced biome.\n" "Mobs will spawn naturally.\n" "including polar-bears" msgstr "" -"The ice-plains biome is an advanced biome.\n" +"Das Ice-Plains Biom ist ein besonderes Biom.\n" "Mobs spawnen normal.\n" "inklusiv Polarbären" @@ -615,9 +622,10 @@ msgid "" "§ea permission. Change the\n" "§epermission by clicking it." msgstr "" -"§eZeige auf ein Icon um die \n" +"§eFahre mit der Maus\n" +"§eüber ein Icon um die \n" "§eRechte zu sehen. Klicke\n" -"§edarauf um diese zu ändern." +"§edarauf, um diese zu ändern." msgid "§fThis player §acan" msgstr "§fDieser Spieler §akann" @@ -650,6 +658,11 @@ msgid "" "§eleader can change permissions\n" "§eby clicking a player''s icon." msgstr "" +"§eFahre mit der Maus über\n" +"§edas Icon eines Spielers, um seine\n" +"§eRechte einzusehen. Der Inselbesitzer" +"§ekann die Rechte ändern, indem er\n" +"§edas Icon anklickt." #, java-format msgid "§e{0}''s§9 Permissions" @@ -673,7 +686,7 @@ msgid "§e" msgstr "§e" msgid "Island Log" -msgstr "Insel Log" +msgstr "Insel Logbuch" msgid "" "§eClick here to return to\n" @@ -686,7 +699,7 @@ msgid "§e§lIsland Log" msgstr "§e§lInsel Logbuch" msgid "Island Biome" -msgstr "Insel Biome" +msgstr "Insel Biom" #, java-format msgid "Biome: {0}" @@ -696,7 +709,7 @@ msgid "§2§lThis is your current biome." msgstr "§2§lDas ist dein aktuelles Biom." msgid "§e§lClick to change to this biome." -msgstr "§e§lKlick hier um zu diesem Biom zu wechseln" +msgstr "§e§lKlicke hier, um zu diesem Biom zu wechseln" msgid "You cannot use this biome." msgstr "Du kannst dieses Biom nicht nutzen" @@ -715,17 +728,17 @@ msgid "§c-" msgstr "" msgid "Decrease radius of biome-change" -msgstr "" +msgstr "Verringert den Radius des Biomwechsels" #, java-format msgid "Current radius: {0}" -msgstr "" +msgstr "Aktueller Radius: {0}" msgid "§2+" msgstr "" msgid "Increase radius of biome-change" -msgstr "" +msgstr "Vergrößert den Radius des Biomwechsels" msgid "§7Current page" msgstr "§7Derzeitige Seite" @@ -777,10 +790,10 @@ msgstr "" "§7({0})" msgid "§a§lReturn to Spawn" -msgstr "" +msgstr "§a§lZum Spawn zurückkehren" msgid "Teleport to the spawn area." -msgstr "" +msgstr "Zum Spawnbereich teleportieren" msgid "§a§lJoin an Island" msgstr "§a§lTrete einer Insel bei" @@ -795,6 +808,14 @@ msgid "" "§e§lClick here to accept an invite!\n" "§e§l(You must be invited first)" msgstr "" +"WMöchtest du der Insel eines anderen\n" +"Spielers beitreten, anstatt deine\n" +"eigene zu starten? Wenn dich ein\n" +"Spieler zu seiner Insel einlädt,\n" +"kannst du hier klicken, oder\n" +"§e/island accept§f nutzen, um beizutreten.\n" +"§e§lKlicke hier, um eine Einladung anzunehmen!\n" +"§e§l(Du musst eine Einladung erhalten haben)" msgid "Island Menu" msgstr "Insel Menü" @@ -809,6 +830,10 @@ msgid "" "island using §b/island sethome\n" "§e§lClick here to return home." msgstr "" +"Kehre zu deinem Insel-Home zurück.\n" +"Du kannst deinen Home-Punkt jederzeit ändern:\n" +"§b/island sethome\n" +"§e§lKlicke hier, um zum Home-Punkt zurückzukehren" msgid "§a§lChallenges" msgstr "§a§lAufgaben" @@ -820,11 +845,11 @@ msgid "" "and titles." msgstr "" "Zeigt eine Liste von §9Aufgaben§f, die\n" -"Du auf Deiner Insel abschliessen kannst\n" -"um Insel Level, Items und Titel freizuschalten" +"Du auf Deiner Insel abschliessen kannst.\n" +"Dafür erhälst du Insel-Level, Items und Titel!" msgid "§e§lClick here to view challenges." -msgstr "§e§lKlick hier um die Aufgaben anzuzeigen" +msgstr "§e§lKlicke hier, um die Aufgaben anzuzeigen" msgid "§4§lChallenges disabled." msgstr "§4§lAufgaben deaktiviert." @@ -848,7 +873,7 @@ msgstr "" "Deine Skyblock Insel vergrößerst und\n" "indem bestimmte Aufgaben abschliesst\n" "Seltene Blöcke fügen mehr Insel Level hinzu\n" -"§e§lKlick hier um das Level zu aktualiseren.\n" +"§e§lKlicek hier, um das Level zu aktualiseren.\n" "§e§l(Muss auf Deiner Insel ausgeführt werden)" msgid "Island Group" @@ -891,7 +916,7 @@ msgid "§e§lClick here to change biomes." msgstr "§e§lKlick hier um das Biom zu ändern." msgid "§c§lYou can't change the biome." -msgstr "§c§lDu kannst das Biome nicht ändern." +msgstr "§c§lDu kannst das Biom nicht ändern." msgid "§a§lIsland Lock" msgstr "§a§lInsel Sperre" @@ -908,10 +933,10 @@ msgstr "" "§fDu können die Insel betreten." msgid "§e§lClick here to unlock your island." -msgstr "§e§lKlick hier um Deine Insel zu entsperren." +msgstr "§e§lKlicke hier, um Deine Insel zu entsperren." msgid "§c§lYou can't change the lock." -msgstr "§c§lDu kannst die Insel nicht Ent,-Sperren" +msgstr "§c§lDu kannst die Sperr-Einstellungen nicht ändern." msgid "" "§eLock Status: §8Inactive\n" @@ -927,7 +952,7 @@ msgstr "" "§fDu dürfen auf der Insel bauen." msgid "§e§lClick here to lock your island." -msgstr "§e§lKlick hier um Deine Insel zu sperren." +msgstr "§e§lKlicke hier, um Deine Insel zu sperren." msgid "§a§lIsland Warp" msgstr "§a§lInsel Warp" @@ -944,7 +969,7 @@ msgstr "" "§fDu kannst ihn mit §d/island setwarp §fsetzen." msgid "§e§lClick here to deactivate." -msgstr "§e§lKlick hier zum Deaktivieren." +msgstr "§e§lKlicke hier zum Deaktivieren." msgid "§c§lYou can't change the warp." msgstr "§c§lDu kannst den Warppunkt nicht verändern." @@ -980,7 +1005,7 @@ msgstr "" "§e§lKlick hier um den Log zu öffnen" msgid "§a§lChange Home Location" -msgstr "§a§lWechsel die Home Position" +msgstr "§a§lÄnderel die Home Position" msgid "" "When you teleport to your\n" @@ -1001,9 +1026,13 @@ msgid "" "this point when they teleport\n" "to your island." msgstr "" +"Wenn der Warp aktiviert ist,\n" +"werden andere Spieler zu diesem\n" +"Punkt gebracht, wenn sie sich zu\n" +"deiner Insel teleportieren." msgid "§e§lClick here to change." -msgstr "§e§lKlick hier zum ändern." +msgstr "§e§lKlicke hier zum ändern." msgid "§c§lRestart Island" msgstr "§c§lInsel neustarten" @@ -1013,7 +1042,7 @@ msgid "" "§4WARNING! §cwill remove your items and island!" msgstr "" "Startet die Insel neu\n" -"§4ACHTUNG! §centfernt alle Deine Items und Deine Insel!" +"§4ACHTUNG! §cDeine Insel und alle Items werden vernichtet!" msgid "§c§lLeave Island" msgstr "§c§lInsel verlassen" @@ -1023,7 +1052,7 @@ msgid "" "§4WARNING! §cwill remove all your items!" msgstr "" "Verlässt Deine Insel.\n" -"§4ACHTUNG! §cDies entfernt alle Items!" +"§4ACHTUNG! §cDies entfernt all deine Items!" msgid "§cClick to leave" msgstr "§cKlicke um zu verlassen" @@ -1044,7 +1073,7 @@ msgstr "§a§lKehre zum Hauptmenü zurück" #, java-format msgid "§cClick within §9{0}§c to restart!" msgstr "" -"§cKlicke innerhalb von §9{0}§c,\n" +"§cKlicke innerhalb von §9{0}§c,\n" "§cwenn Du wirklich neu starten möchtest!" msgid "§aClick to restart!" @@ -1199,7 +1228,7 @@ msgid "§b{0}§d locked the island." msgstr "§b{0}§d hat die Insel gesperrt." msgid "§4Since your island is locked, your incoming warp has been deactivated." -msgstr "§4Seit deine Insel gesperrt ist, ist dein Warp deaktiviert." +msgstr "§4Weil deine Insel gesperrt ist, ist dein Warp deaktiviert." #, java-format msgid "§b{0}§d deactivated the island warp." @@ -1281,7 +1310,7 @@ msgstr "" #, java-format msgid "§eWALL OF FAME (page {0} of {1}):" -msgstr "§eRangliste der Inseln sortiert nach Insel Leveln (Seite {0} von {1}):" +msgstr "§eRangliste der Inseln sortiert nach Insel Level (Seite {0} von {1}):" #, java-format msgid "§4Top ten list is empty! Only islands above level {0} is considered." @@ -1296,7 +1325,7 @@ msgid "§a#%2d §7(%5.2f): §e%s §7%s" msgstr "§a#%2d §7(%5.2f): §e%s §7%s" msgid "Click to warp to the island!" -msgstr "" +msgstr "Klicke, um dich zur Insel zu teleportieren" #, java-format msgid "§eYour rank is: §f{0}" @@ -1315,7 +1344,7 @@ msgstr "um diesen Rang freizuschalten" #, java-format msgid "§4You can complete this {0} more time(s)." -msgstr "" +msgstr "§4Kann noch {0} mal abgeschlossen werden!" #, java-format msgid "§4Requirements will reset in {0} days." @@ -1330,19 +1359,19 @@ msgid "§4Requirements will reset in {0} minutes." msgstr "§4Anforderungen werden in {0} Minuten zurückgesetzt." msgid "§4This challenge is currently unavailable." -msgstr "" +msgstr "§4Diese Challenge ist momentan nicht verfügbar." #, java-format msgid "§4You can complete this again in {0} days." -msgstr "" +msgstr "§4Kann in {0} Tagen erneut abgeschlossen werden." #, java-format msgid "§4You can complete this again in {0} hours." -msgstr "" +msgstr "§4Kann in {0} Stunden erneut abgeschlossen werden." #, java-format msgid "§4You can complete this again in {0} minutes." -msgstr "" +msgstr "§4Kann in {0} Minuten erneut abgeschlossen werden." msgid "§eThis challenge requires:" msgstr "§eDiese Aufgabe erfordert:" @@ -1355,18 +1384,18 @@ msgstr "§eItems werden gegen die Belohnung eingetauscht." #, java-format msgid "§eMust be within {0} meters." -msgstr "§eMuss innerhalb von {0} Meter befinden." +msgstr "§eMuss sich innerhalb von {0} Meter befinden." msgid "§6Item Reward: §a" -msgstr "§6Gegenstandsbelohnung: §a" +msgstr "§6Item-Belohnungg: §a" #, java-format msgid "§6Currency Reward: §a{0}" -msgstr "§6Währungsbelohnung: §a{0}" +msgstr "§6Geld-Belohnung: §a{0}" #, java-format msgid "§6Exp Reward: §a{0}" -msgstr "§6Erfahrungsbelohnung: §a{0}" +msgstr "§6Erfahrungs-Belohnung: §a{0}" #, java-format msgid "§dTotal times completed: §f{0}" @@ -1389,7 +1418,7 @@ msgstr "§4Diese {0} Aufgabe kannst du nicht Wiederholen!" #, java-format msgid "§4You cannot complete the {0} challenge again yet!" -msgstr "" +msgstr "§4Die Challenge {0} kann noch nicht wieder abgeschlossen werden" #, java-format msgid "§eTrying to complete challenge §a{0}" @@ -1402,7 +1431,7 @@ msgstr "§4{0}" #, java-format msgid "§4You must be standing within {0} blocks of all required items." msgstr "" -"§4Du musst in {0} Blöcke Abstand von den benötigten Gegenständen stehen." +"§4Du musst innerhalb {0} Blöcke Abstand von den benötigten Gegenständen stehen." #, java-format msgid "§4Your island must be level {0} to complete this challenge!" @@ -1429,15 +1458,15 @@ msgstr "§eDir fehlen:{0}" #, java-format msgid "§aYou have completed the {0} challenge!" -msgstr "§aDu hast die {0} Aufgabe beendet!" +msgstr "§aDu hast die Aufgabe {0} beendet!" #, java-format msgid "§9{0}§f has completed the §9{1}§f challenge!" -msgstr "§9{0}§f hat die §9{1}§f Aufgabe abgeschlossen!" +msgstr "§9{0}§f hat §9{1}§f Aufgabe abgeschlossen!" #, java-format msgid "§eItem reward(s): §f{0}" -msgstr "§eGegenstandsbelohnung: §f{0}" +msgstr "§eItembelohnung: §f{0}" #, java-format msgid "§eExp reward: §f{0,number,#.#}" @@ -1451,7 +1480,7 @@ msgid "§eYour inventory is §4full§e. Items dropped on the ground." msgstr "§eDein Inventar ist §4voll§e. Die Gegenstände fallen auf den Boden!" msgid "§e§lClick to complete this challenge." -msgstr "§e§lKlick hier um die Aufgabe zu beenden!" +msgstr "§e§lKlicek hier, um die Aufgabe zu beenden!" msgid "§4§lYou can't repeat this challenge." msgstr "§4§lDu kannst diese Aufgabe nicht wiederholen!" @@ -1467,16 +1496,16 @@ msgid "§fthis rank to unlock the next rank." msgstr "§fdiesem Rang, um den nächsten Rang freizuschalten." msgid "§eClick here to show previous page" -msgstr "§eKlick hier um zur vorherigen Seite zu gelangen." +msgstr "§eKlicke hier, um zur vorherigen Seite zu gelangen." msgid "§eClick here to show next page" -msgstr "§eKlick hier um zur nächsten Seite zu gelangen." +msgstr "§eKlicke hier, um zur nächsten Seite zu gelangen." msgid "But you are ALLLLLLL ALOOOOONE!" msgstr "Du bist ALLLLLEEEEEEIIIIINE!" msgid "But you are Yelling in the wind!" -msgstr "But you are Yelling in the wind!" +msgstr "Aber du rufst dem Wind entgegen!" msgid "But your fantasy friends are gone!" msgstr "Deine Fantasiefreunde sind weg!" @@ -1489,7 +1518,7 @@ msgid "§cSorry! {0}" msgstr "§cTschuldigung! {0}" msgid "Either send a message directly to your group, or toggle it on/off." -msgstr "Sende eine Nachricht an deine Gruppe oder schalte es an oder aus." +msgstr "Sende eine Nachricht an deine Gruppe oder schalte Nachrichten an oder aus." msgid "party" msgstr "Party" @@ -1499,7 +1528,7 @@ msgstr "Insel" #, java-format msgid "§cToggled chat to {0} §aON" -msgstr "§cSchaltet den Chat zu {0} §aON" +msgstr "§cSchaltet den Chat zu {0} §aAN" #, java-format msgid "§cRepeat §9{0}§c to toggle it off" @@ -1513,19 +1542,19 @@ msgid "§cCommand only available to players" msgstr "§cKommando nur für Spieler verfügbar" msgid "talk to your island party" -msgstr "redet mit deinen Insel Mitgliedern" +msgstr "rede mit deinen Insel-Mitgliedern" msgid "talk to players on your island" msgstr "rede zu Spielern auf deiner Insel" msgid "manually update the top 10 list" -msgstr "Aktualisiere manuell die 10 Besten Liste!" +msgstr "Aktualisiere die Bestenliste!" msgid "§eGenerating the Top Ten list" -msgstr "§eErstellt die 10 Besten Liste" +msgstr "§eErstell die Bestenliste" msgid "§eFinished generation of the Top Ten list" -msgstr "§eDie 10 Besten Liste wurde erfolgreich erstellt" +msgstr "§eDie Bestenliste wurde erfolgreich erstellt" msgid "purges all abandoned islands" msgstr "Löscht alle verlassenen Inseln" @@ -1534,29 +1563,31 @@ msgid "§4You must provide the age in days to purge!" msgstr "§4Du musst die Zeit für die Säuberung in Tagen angeben!" msgid "§4The level must be a valid number" -msgstr "" +msgstr "§4Das Level muss eine Zahl sein" #, java-format msgid "" "§eFinding all islands that have been abandoned for more than {0} days below " "level {1}" msgstr "" +"§eSuche alle Inseln, die fpr {0} oder mehr Tage verlassen sind" +"level {1}" #, java-format msgid "§4PURGE:§e Do §9usb purge confirm§e within {0} to accept." -msgstr "" +msgstr "§4PURGE:§e Schreibe erneu §9usb purge confirm§einnerhalb {0} um zu akzeptieren." msgid "§4Trying to abort purge" msgstr "§4Versuche Purge abzubrechen" msgid "§4Purge aborted!" -msgstr "" +msgstr "§4Säuberung abgebrochen" msgid "§4A purge is already running.§e Either §9confirm§e or §9stop§e it." -msgstr "" +msgstr "§4Doe Säuberung läft bereits. §9Du kannst sie bestätigen oder abbrechen." msgid "§4Starting purge..." -msgstr "§4Starte Purge" +msgstr "§4Starte Säuberung" msgid "Controls player-cooldowns" msgstr "Kontrolliert Spieler-Abklingzeiten" @@ -1700,7 +1731,7 @@ msgstr "§eDerzeit sind keine verwaisten Inseln registriert" #, java-format msgid "§eOrphans ({0}/{1}): {2}" -msgstr "" +msgstr "§eOrphans ({0}/{1}): {2}" msgid "transfer leadership to another player" msgstr "Übergib die Führung an einen anderen Spieler" @@ -1719,13 +1750,13 @@ msgstr "" #, java-format msgid "§bLeadership transferred by {0}§b to {1}" -msgstr "§bFührung übergeben von {0}§b an {1}" +msgstr "§bDie Leitung von {0}§b an {1} übergeben" msgid "open GUI for config" msgstr "Öffne Konfigurations Menü" msgid "searches config for a specific key" -msgstr "" +msgstr "durchsucht die config nach einem bestimmten Wert" #, java-format msgid "§c{0}§9" @@ -1737,7 +1768,7 @@ msgstr "" #, java-format msgid "Found the following matching {0}:" -msgstr "" +msgstr "Folgende Treffer gefunden: {0}:" msgid "§a
" msgstr "" @@ -1833,7 +1864,7 @@ msgid "§eNo currently shown regions for this player" msgstr "§eFür diesen Spieler werden derzeit keine Regionen gezeigt" msgid "set the ticks between animations" -msgstr "" +msgstr "setze den Cooldown zwischen den Animationen in Ticks" #, java-format msgid "§eAnimation-tick changed to {0}." @@ -1843,15 +1874,16 @@ msgid "§eAnimation-tick must be a valid integer." msgstr "§eAnimation-tick muss ein gültiger Integer sein." msgid "refreshes the existing animations" -msgstr "" +msgstr "aktualisiert die bereits existierenden Animationen" #, java-format msgid "" "- PURGING: {0,number,##}% ({1}/{2}), elapsed {3}, estimated completion ~{4}" msgstr "" +"- SÄUBERT: {0,number,##}% ({1}/{2}), verstrichen {3}, geschätzte Vervollständigung ~{4}" msgid "§4PURGE:§9 Finished purging abandoned islands." -msgstr "§4PURGE:§9 Verlassene Inseln wurde entfernt." +msgstr "§4PURGE:§9 Verlassene Inseln wurden entfernt." msgid "§4PURGE:§9 Aborted purging abandoned islands." msgstr "§4SÄUBERUNG:§9 Das säubern von verlassenen Inseln wurde abgebrochen." @@ -1868,6 +1900,7 @@ msgid "" "§4PURGE:§9 Scanning done, found {0} candidates, below level {1}, ready for " "purgatory." msgstr "" +"§4SÄUBERUNG:§9 Scan abgeschlossen, {0} Inseln gefunden, die unter Level {1} sind." msgid "§cABORTED:§e Protect-All was aborted!" msgstr "§cABGEBROCHEN:§e Protect-All wurde unterbrochen!" @@ -1875,7 +1908,7 @@ msgstr "§cABGEBROCHEN:§e Protect-All wurde unterbrochen!" #, java-format msgid "§eCompleted protect-all in {0}, {1} new regions were created!" msgstr "" -"§eCompleted wurde in {0} $eabgeschlossen, {1} neue Regionen wurden erstellt!" +"§eProtect-All wurde in {0} $eabgeschlossen, {1} neue Regionen wurden erstellt!" msgid "control debugging" msgstr "Steuert Fehlerüberprüfung" @@ -2025,43 +2058,43 @@ msgid "§4No rank named {0} was found!" msgstr "§4Kein Rang namens {0} wurde gefunden" msgid "various chunk commands" -msgstr "" +msgstr "verschiedene Chunk-Befehle" msgid "regenerate current chunk" -msgstr "" +msgstr "regeneriere aktuellen Chunk" #, java-format msgid "successfully regenerated chunk at {0},{1}" -msgstr "" +msgstr "Chunk bei {0},{1} erfolgreich regeneriert" #, java-format msgid "§4FAILED!§e could not regenerate chunk at {0},{1}" -msgstr "" +msgstr "§4FEHLER!§e Chunk bei {0},{1} konnte nicht regeneriert werden!" msgid "unload current chunk" -msgstr "" +msgstr "aktuellen Chunk unloaden" #, java-format msgid "successfully unloaded chunk at {0},{1}" -msgstr "" +msgstr "Der Chunk bei {0},{1} wurde erfolgreich geunloaded!" #, java-format msgid "§4FAILED!§e could not unload chunk at {0},{1}" -msgstr "" +msgstr "§4FEHLER!§e Chunk bei {0},{1} konnte nicht geunloaded werden!" msgid "load current chunk" -msgstr "" +msgstr "lade aktuellen Chunk" #, java-format msgid "loaded chunk at {0},{1}" -msgstr "" +msgstr "Chunk bei {0},{1} wurde geladen" msgid "only available for players" -msgstr "" +msgstr "Nur für Spieler verfügbar" #, java-format msgid "§4ERROR:§e {0}" -msgstr "" +msgstr "§4FEHLER:§e {0}" msgid "protects all islands (time consuming)" msgstr "Schützt alle Inseln (Zeitaufwändig)" @@ -2101,7 +2134,7 @@ msgid "" "§4Island at this location has members!\n" "§eUse §9/usb island delete §e to delete it." msgstr "" -"§4Die Insel an ihrem Standort hat Mitglieder!\n" +"§4Die Insel an diesem Standort hat Mitglieder!\n" "§eBenutze §9/usb island delete §e um die Insel zu löschen." msgid "removes the player from the island" @@ -2208,10 +2241,10 @@ msgid "imports players and islands from other formats" msgstr "Importiere Spieler und Inseln aus anderen Formaten" msgid "shows perk-information" -msgstr "" +msgstr "zeigt Perk-Informationen" msgid "shows a specific players perks" -msgstr "" +msgstr "zeigt die Perks eines bestimmten Spielers" #, java-format msgid "additional perks {0}" @@ -2318,19 +2351,19 @@ msgid "general island command" msgstr "Allgemeine Insel Befehle" msgid "allows user to bypass cooldowns" -msgstr "" +msgstr "erlaubt, gegen Cooldowns immum zu sein" msgid "allows user to bypass visitor-protections" -msgstr "" +msgstr "erlaubt, trotz der Besucher-Protection abzubauen" msgid "allows user to bypass teleport-delay" -msgstr "" +msgstr "erlaubt, gegen das Teleport-Delay immun zu sein" msgid "allows user to use [usb] signs" -msgstr "" +msgstr "erlaubt, [usb] Schilder zu benutzen" msgid "allows user to place [usb] signs" -msgstr "" +msgstr "erlaubt [usb] Schilder zu platzieren" msgid "complete and list challenges" msgstr "vervollständige und zeige Aufgaben an" @@ -2585,24 +2618,23 @@ msgid "" "§cYour island is in the process of generating, you cannot teleport home " "right now." msgstr "" -"§cDeine Insel wird gerade erstellt. Du kannst dich jetzt nicht auf sie " -"teleportieren" +"§cDeine Insel wird gerade erstellt. Du kannst dich jetzt nicht teleportieren" msgid "§4This command can only be executed by a player" msgstr "§4Dieser Befehl kann nur von Spielern ausgeführt werden" msgid "transfer leadership to another member" -msgstr "Übertrage die Führerrolle zu einem anderen Spieler" +msgstr "Übertrage die Leitung einem anderen Spieler" msgid "§4You can only transfer ownership to party-members!" -msgstr "§4Du kannst die Führerschaft nur auf Gruppenmitglieder übertragen!" +msgstr "§4Du kannst die Leitung nur auf Gruppenmitglieder übertragen!" #, java-format msgid "{0}§e is already leader of your island!" msgstr "{0}§e ist bereits der Anführer der Insel!" msgid "§4Only leader can transfer leadership!" -msgstr "§4Nur Anführer können die Führerschaft übertragen!" +msgstr "§4Nur Anführer können die Leitung übertragen!" #, java-format msgid "{0} tried to take over the island!" @@ -2634,7 +2666,7 @@ msgid "allows user to see others island info" msgstr "erlaubt den Spieler Infos von anderen Insel anzugucken" msgid "§4Hold your horses! §eYou have to be patient..." -msgstr "" +msgstr "§4Gedulde dich doch einmal..." #, java-format msgid "§eBlocks on {0}s Island (page {1,number} of {2,number}):" @@ -2687,7 +2719,7 @@ msgid "delete your island and start a new one." msgstr "Löscht deine Insel und startet eine neue." msgid "exempt player from restart-cooldown" -msgstr "" +msgstr "befreit einen Spieler vom restart-cooldown" msgid "" "§4Only the owner may restart this island. Leave this island in order to " @@ -2860,13 +2892,13 @@ msgstr "" #, java-format msgid "§ePermissions for §9{0}§e:" -msgstr "" +msgstr "§eRechte für §9{0}§e:" msgid "§aON" -msgstr "" +msgstr "§aAN" msgid "§cOFF" -msgstr "" +msgstr "§cAUS" #, java-format msgid "§7 - §6{0}§7 : {1}" @@ -2888,22 +2920,21 @@ msgid "set the island-home" msgstr "Setze dein Home" msgid "show the islands limits" -msgstr "" +msgstr "zeige die Insel-Begrenzungen" msgid "enable/disable warping to your island." msgstr "Aktiviere/Deaktiviere Warping zu deiner Insel." msgid "§4Your island is locked. You must unlock it before enabling your warp." msgstr "" -"§4Deine Insel ist gesperrt. Du musst sie entsperren um das warpen freizu " -"schalten." +"§4Deine Insel ist gesperrt. Du musst sie entsperren um das warpen freizuschalten." #, java-format msgid "§b{0}§d activated the island warp." msgstr "§b{0}§d hat den Insel Warp aktiviert" msgid "§cYou do not have permission to enable/disable your island''s warp!" -msgstr "" +msgstr "§cDu hast keine Rechte den Inselwarp zu de-/aktivieren!" msgid "try to complete a challenge" msgstr "versucht eine Aufgabe abzuschliessen" @@ -2943,35 +2974,35 @@ msgid "§eThe island has been §cLOCKED§e." msgstr "§eDie Insel wurde §cGESPERRT§e." msgid "§9Hold your horses! You have to be patient..." -msgstr "" +msgstr "§9Gedulde dich doch einmal..." msgid "§9Not really patient, are you?" -msgstr "" +msgstr "§9Du bist nicht wirklich ausdauernd, oder?" msgid "§9Be patient, young padawan" -msgstr "" +msgstr "§9Gedulde dich, junger Padawan" msgid "§9Patience you MUST have, young padawan" -msgstr "" +msgstr "§9Du must geduldig sein, junger Padawan!" msgid "§9The two most powerful warriors are patience and time." -msgstr "" +msgstr "§9Die zwei stärksten Krieger sind Geduld und Zeit." msgid "§4Unable to find a safe home-location on your island!" msgstr "§4Es konnte kein sicherer Ort für dein Home gefunden werden!" msgid "§cWARNING: §eTeleporting you to mid-air." -msgstr "§cWARNUNG: §eDu wirst in die Luft geportet." +msgstr "§cWARNUNG: §eDu wirst in die Luft teleportiert." msgid "§aTeleporting you to your island." -msgstr "§aDu wirst zu Deiner Insel teleportiert." +msgstr "§aDu wirst zu deiner Insel teleportiert." #, java-format msgid "§aYou will be teleported in {0} seconds." -msgstr "§aDu wirst in {0} Sekunden Teleportiert." +msgstr "§aDu wirst in {0} Sekunden teleportiert." msgid "§4Unable to warp you to that player''s island!" -msgstr "§4Es ist nicht möglich Dich zu der Insel von dem Spieler zu Warpen!" +msgstr "§4Es ist nicht möglich, dich zur Insel von diesem Spieler zu Warpen!" #, java-format msgid "§aTeleporting you to {0}''s island." diff --git a/uSkyBlock-Core/src/main/po/keys.pot b/uSkyBlock-Core/src/main/po/keys.pot index e649725e9..0d6aa0fe1 100644 --- a/uSkyBlock-Core/src/main/po/keys.pot +++ b/uSkyBlock-Core/src/main/po/keys.pot @@ -16,30 +16,6 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -msgid "d" -msgstr "" - -msgid "h" -msgstr "" - -msgid "m" -msgstr "" - -msgid "s" -msgstr "" - -#, java-format -msgid "{0,number,0}:{1,number,00}.{2,number,000}" -msgstr "" - -#, java-format -msgid "§f{0}x §7{1}" -msgstr "" - -#, java-format -msgid "§7{0}" -msgstr "" - #, java-format msgid "§eYou do not have access (§4{0}§e)" msgstr "" @@ -48,15 +24,6 @@ msgstr "" msgid "§eInvalid command: {0}" msgstr "" -msgid "Command" -msgstr "" - -msgid "Permission" -msgstr "" - -msgid "Description" -msgstr "" - #, java-format msgid "§7Usage: {0}" msgstr "" @@ -80,2674 +47,2703 @@ msgstr "" msgid "§4Error writing documentation: {0}" msgstr "" -msgid "§cWither Despawned!§e It wandered too far from your island." +msgid "changes the language of the plugin, and reloads" msgstr "" #, java-format -msgid "{0}''s Wither" +msgid "§aSuccessfully changed language to §e{0}" msgstr "" -msgid "§eYou can not use another island''s portals!" +#, java-format +msgid "§cFailed to change language to §e{0}" msgstr "" -msgid "§eVillager-trading isn't allowed." +msgid "§9Supported Languages:\n" msgstr "" -msgid "§eTrading isn't allowed on other islands. Do it in spawn." +#, java-format +msgid "§f{0} §7{1} §9 by {2} §7{3}\n" msgstr "" -msgid "§eRiding is only allowed on your own island!" +msgid "§cUnable to locate any languages." msgstr "" -msgid "§eYou cannot break vehicles while being a visitor!" +msgid "Command" msgstr "" -msgid "§4You can only convert obsidian once every 10 seconds" +msgid "Permission" msgstr "" -msgid "§eChanging your obsidian back into lava. Be careful!" +msgid "Description" msgstr "" -msgid "§eYour inventory must have another empty space!" +#, java-format +msgid "§f{0}x §7{1}" msgstr "" -msgid "§4It''s a bad idea to replace your lava!" +#, java-format +msgid "§7{0}" msgstr "" -msgid "§eYou cannot hurt island-members." +msgid "d" msgstr "" -msgid "§4That player has forbidden you from teleporting to their island." +msgid "h" msgstr "" -msgid "§4That island is §clocked.§e No teleporting to the island." +msgid "m" msgstr "" -#, java-format -msgid "" -"§4{0} is limited. §eScanning your island to see if you are allowed to place " -"more, please be patient" +msgid "s" msgstr "" -msgid "§e... Scanning complete, you can try again" +#, java-format +msgid "{0,number,0}:{1,number,00}.{2,number,000}" msgstr "" #, java-format -msgid "" -"§4You''ve hit the {0} limit!§e You can''t have more of that type on your " -"island!§9 Max: {1,number}" +msgid " §f{0}x §7{1}" msgstr "" -msgid "§eYou can only use spawn-eggs on your own island." +#, java-format +msgid "§eStill the following blocks short: {0}" msgstr "" -msgid "§cYou have reached your spawn-limit for your island." +#, java-format +msgid "§4You can complete this {0} more time(s)." msgstr "" -msgid "§eVisitors can't drop items!" +#, java-format +msgid "§4Requirements will reset in {0} days." msgstr "" #, java-format -msgid "Owner: {0}" +msgid "§4Requirements will reset in {0} hours." msgstr "" -msgid "You cannot pick up other players' loot when you are a visitor!" +#, java-format +msgid "§4Requirements will reset in {0} minutes." msgstr "" -msgid "§cBanned:§e You are banned from this island." +msgid "§4This challenge is currently unavailable." msgstr "" -msgid "§cLocked:§e That island is locked! No entry allowed." +#, java-format +msgid "§4You can complete this again in {0} days." msgstr "" -msgid "Something went wrong saving the island and/or party data!" +#, java-format +msgid "§4You can complete this again in {0} hours." msgstr "" -msgid "Converting data to UUID, this make take a while!" +#, java-format +msgid "§4You can complete this again in {0} minutes." msgstr "" -msgid "§cMAINTENANCE:§e uSkyBlock is currently in maintenance mode" +msgid "§eThis challenge requires:" msgstr "" -#, java-format -msgid "" -"§buSkyBlock§e depends on §9{0}§e >= §av{1}§e but only §cv{2}§e was found!\n" +msgid "§7and more..." msgstr "" -#, java-format -msgid "§buSkyBlock§e depends on §9{0}§e >= §av{1}" +msgid "§eItems will be traded for reward." msgstr "" -msgid "§eYou do not have access to that island-schematic!" +#, java-format +msgid "§eMust be within {0} meters." msgstr "" -msgid "§4Player is already assigned to this island!" +msgid "§6Item Reward: §a" msgstr "" -msgid "§4You must be closer to your island to set your skyblock home!" +#, java-format +msgid "§6Currency Reward: §a{0}" msgstr "" -msgid "§aYour skyblock home has been set to your current location." +#, java-format +msgid "§6Exp Reward: §a{0}" msgstr "" -msgid "§4Your current location is not a safe home-location." +#, java-format +msgid "§dTotal times completed: §f{0}" msgstr "" #, java-format -msgid "" -"§7The pixies are busy changing the biome of your island to §9{0}§7, be " -"patient." +msgid "§7Requires {0}" msgstr "" -msgid "§cYour island is in the process of generating, you cannot create now." +#, java-format +msgid "§4No challenge named {0} found" msgstr "" -msgid "Could not create your Island. Please contact a server moderator." +msgid "§4You must be on your island to do that!" msgstr "" -msgid "§eGetting your island ready, please be patient, it can take a while." +#, java-format +msgid "§4The {0} challenge is not available yet!" msgstr "" -msgid "§cCommand is currently disabled!" +#, java-format +msgid "§4The {0} challenge is not repeatable!" msgstr "" #, java-format -msgid " §f{0}x §7{1}" +msgid "§4You cannot complete the {0} challenge again yet!" msgstr "" #, java-format -msgid "§eStill the following blocks short: {0}" +msgid "§eTrying to complete challenge §a{0}" msgstr "" #, java-format -msgid "§9{0}§7 timed out" +msgid "§4{0}" msgstr "" #, java-format -msgid "" -"§eDoing §9{0}§e is §cRISKY§e. Repeat the command within §a{1}§e seconds to " -"accept!" +msgid "§4You must be standing within {0} blocks of all required items." msgstr "" -msgid "§4** You are entering a protected - but abandoned - island area." +#, java-format +msgid "§4Your island must be level {0} to complete this challenge!" msgstr "" #, java-format -msgid "§d** You are entering §b{0}''s §disland." +msgid "§4Unknown type of challenge: {0}" msgstr "" -msgid "§4** You are leaving an abandoned island." +#, java-format +msgid "" +"§eStill the following entities short:\n" +"{0}" msgstr "" #, java-format -msgid "§d** You are leaving §b{0}''s §disland." +msgid " §4{0} §b{1}" msgstr "" -msgid "§eYour island is now locked. Only your party members may enter." +#, java-format +msgid "§eYou are the following items short:{0}" msgstr "" -msgid "§4You must be the party leader to lock your island!" +#, java-format +msgid "§aYou have completed the {0} challenge!" msgstr "" -msgid "" -"§eYour island is unlocked and anyone may enter, however only you and your " -"party members may build or remove blocks." +#, java-format +msgid "§9{0}§f has completed the §9{1}§f challenge!" msgstr "" -msgid "§4You must be the party leader to unlock your island!" +#, java-format +msgid "§eItem reward(s): §f{0}" msgstr "" #, java-format -msgid "§eWaiting for our turn §c{0,number,###}%" +msgid "§eExp reward: §f{0,number,#.#}" msgstr "" #, java-format -msgid "§9Creating island...§e{0,number,###}%" +msgid "§eCurrency reward: §f{0,number,###.##} {1} §a ({2,number,##.##})%" msgstr "" -msgid "N/A" +msgid "§eYour inventory is §4full§e. Items dropped on the ground." msgstr "" -msgid "§4§lLocked Challenge" +msgid "§e§lClick to complete this challenge." msgstr "" -msgid "READY" +msgid "§4§lYou can't repeat this challenge." msgstr "" #, java-format -msgid "§4The {0} challenge is not available yet!" +msgid "Rank: {0}" msgstr "" -msgid "" -"§cWARNING:§e Could not transfer all the required items to your inventory!" +msgid "§fComplete most challenges in" msgstr "" -msgid "§cNot enough items in chest to complete challenge!" +msgid "§fthis rank to unlock the next rank." msgstr "" -msgid "true" +msgid "§eClick here to show previous page" msgstr "" -msgid "false" +msgid "§eClick here to show next page" msgstr "" -msgid "Challenge Menu" +msgid "§4§lLocked Challenge" msgstr "" -msgid "Change Biome" +#, java-format +msgid "§7Complete {0} more {1} §7challenges" msgstr "" -msgid "change the island''s biome." +#, java-format +msgid "§7Complete {0}" msgstr "" -msgid "Toggle Island Lock" +msgid "to unlock this rank" msgstr "" -msgid "toggle the island''s lock." +msgid "But you are ALLLLLLL ALOOOOONE!" msgstr "" -msgid "Set Island Warp" +msgid "But you are Yelling in the wind!" msgstr "" -msgid "set the island''s warp." +msgid "But your fantasy friends are gone!" msgstr "" -msgid "" -"set the island''s warp,\n" -"which allows non-group\n" -"members to teleport to\n" -"the island." +msgid "But you are Talking to your self!" msgstr "" -msgid "Toggle Island Warp" +#, java-format +msgid "§cSorry! {0}" msgstr "" -msgid "toggle the island''s warp." +msgid "Either send a message directly to your group, or toggle it on/off." msgstr "" -msgid "" -"toggle the island''s warp,\n" -"allowing them to turn it\n" -"on or off at anytime, but\n" -"not set the location." +msgid "party" msgstr "" -msgid "Invite Players" +msgid "island" msgstr "" -msgid "invite others to the island." +#, java-format +msgid "§cToggled chat to {0} §aON" msgstr "" -msgid "" -"invite\n" -"other players to the island if\n" -"there is enough room for more\n" -"members" +#, java-format +msgid "§cRepeat §9{0}§c to toggle it off" msgstr "" -msgid "Kick Players" +#, java-format +msgid "§aToggled chat §cOFF§a for {0}" msgstr "" -msgid "kick others from the island." +msgid "§cCommand only available to players" msgstr "" -msgid "" -"kick\n" -"other players from the island,\n" -"but they are unable to kick\n" -"the island leader." +msgid "talk to players on your island" msgstr "" -msgid "Ocean" +msgid "talk to your island party" msgstr "" -msgid "" -"The ocean biome is the basic\n" -"starting biome for all islands.\n" -"passive mobs like animals will\n" -"not spawn. Hostile mobs will\n" -"spawn normally." +#, java-format +msgid "§ePlayer {0} has no island!" msgstr "" -msgid "Forest" +#, java-format +msgid "§eInvalid player {0} supplied." msgstr "" -msgid "" -"The forest biome will allow\n" -"your island to spawn passive.\n" -"mobs like animals (including\n" -"wolves). Hostile mobs will\n" -"spawn normally." +msgid "Manage challenges for a player" msgstr "" -msgid "Desert" +msgid "resets the challenge for the player" msgstr "" -msgid "" -"The desert biome makes it so\n" -"that there is no rain or snow\n" -"on your island. Passive mobs\n" -"won't spawn. Hostile mobs will\n" -"spawn normally." +msgid "§4Challenge has never been completed" msgstr "" -msgid "Jungle" +#, java-format +msgid "§echallenge: {0} has been reset for {1}" msgstr "" -msgid "" -"The jungle biome is bright\n" -"and colorful. Passive mobs\n" -"(including ocelots) will\n" -"spawn. Hostile mobs will\n" -"spawn normally." +msgid "resets all challenges for the player" msgstr "" -msgid "Swampland" +#, java-format +msgid "§e{0} has had all challenges reset." msgstr "" -msgid "" -"The swamp biome is dark\n" -"and dull. Passive mobs\n" -"will spawn normally and\n" -"slimes have a small chance\n" -"to spawn at night depending\n" -"on the moon phase." +msgid "complete all challenges in the rank" msgstr "" -msgid "Taiga" +#, java-format +msgid "§4No player named {0} was found!" msgstr "" -msgid "" -"The taiga biome has snow\n" -"instead of rain. Passive\n" -"mobs will spawn normally\n" -"(including wolves) and\n" -"hostile mobs will spawn." +#, java-format +msgid "§4Challenge {0} has already been completed" msgstr "" -msgid "Mushroom" +#, java-format +msgid "§eChallenge {0} has been completed for {1}" msgstr "" -msgid "" -"The mushroom biome is\n" -"bright and colorful.\n" -"Mooshrooms are the only\n" -"mobs that will spawn.\n" -"No other passive or\n" -"hostile mobs will spawn." +#, java-format +msgid "§4No challenge named {0} was found!" msgstr "" -msgid "Hell" +#, java-format +msgid "§4No rank named {0} was found!" msgstr "" -msgid "" -"The hell biome looks\n" -"dark and dead. Some\n" -"mobs from the nether will\n" -"spawn in this biome\n" -"(excluding ghasts and\n" -"blazes)." +msgid "manage islands" msgstr "" -msgid "Sky" +msgid "protects the island" msgstr "" -msgid "" -"The sky biome gives your\n" -"island a special dark sky.\n" -"Only endermen will spawn\n" -"in this biome." +msgid "delete the island (removes the blocks)" msgstr "" -msgid "Plains" +msgid "§9Deleted abandoned island at your current location." msgstr "" msgid "" -"The plains biome has rain\n" -"instead of snow. Passive\n" -"mobs will spawn normally\n" -"(including horses) and\n" -"hostile mobs will spawn." +"§4Island at this location has members!\n" +"§eUse §9/usb island delete §e to delete it." msgstr "" -msgid "Extreme Hills" +msgid "removes the player from the island" msgstr "" -msgid "" -"The extreme hills biome.\n" -"Passive mobs will spawn \n" -"normally and hostile\n" -"mobs will spawn." +msgid "adds the player to the island" msgstr "" -msgid "Flower Forest" +#, java-format +msgid "§b{0}§d has joined your island group." +msgstr "" + +#, java-format +msgid "§4No player named {0} found!" msgstr "" msgid "" -"The flower forest biome.\n" -"Passive mobs will spawn \n" -"normally and hostile\n" -"mobs will spawn." +"§4No valid island provided, either stand within one, or provide an island " +"name" msgstr "" -msgid "Deep Ocean" +msgid "print out info about the island" msgstr "" -msgid "" -"The deep-ocean biome is an advanced\n" -"biome. Passive mobs like animals will\n" -"not spawn. Hostile mobs \n" -"(including Guardians) will\n" -"spawn normally." +msgid "sets the biome of the island" msgstr "" -msgid "Ice Plains" +msgid "§4That player has no island." msgstr "" -msgid "" -"The ice-plains biome is an advanced biome.\n" -"Mobs will spawn naturally.\n" -"including polar-bears" +msgid "§4No valid island at your location" msgstr "" -msgid "Permissions" +msgid "purges the island" msgstr "" -#, java-format -msgid "{0} <{1}>" +msgid "§4Error! §9No valid island found for purging." msgstr "" -msgid "§9Player Permissions" +#, java-format +msgid "§cPURGE: §9Purged island at {0}" msgstr "" -msgid "" -"§eClick here to return to\n" -"§eyour island group''s info." +msgid "toggles the islands ignore status" msgstr "" #, java-format -msgid "§e{0}''§9s Permissions" +msgid "§cSet {0}s island to be ignored on top-ten and purge." msgstr "" +#, java-format msgid "" -"§eHover over an icon to view\n" -"§ea permission. Change the\n" -"§epermission by clicking it." +"§cRemoved ignore-flag of {0}s island, it will now show up on top-ten and " +"purge." msgstr "" -msgid "§fThis player §acan" +msgid "§4No valid player-name supplied." msgstr "" -msgid "Click here to remove this permission." +#, java-format +msgid "Removing {0} from island" msgstr "" -msgid "§fThis player §ccannot" +#, java-format +msgid "§eChanged biome of {0}s island to {1}." msgstr "" -msgid "Click here to grant this permission." +#, java-format +msgid "§eChanged biome of {0}s island to OCEAN." msgstr "" -msgid "Island Group Members" +msgid "§aYou may need to go to spawn, or relog, to see the changes." msgstr "" #, java-format -msgid "Group Members: §2{0}§7/§e{1}" +msgid "§e{0} has had their biome changed to {1}." msgstr "" -msgid "§aMore players can be invited to this island." +#, java-format +msgid "§e{0} has had their biome changed to OCEAN." msgstr "" -msgid "§cThis island is full." +#, java-format +msgid "§eRemoving {0}''s island." msgstr "" -msgid "" -"§eHover over a player''s icon to\n" -"§eview their permissions. The\n" -"§eleader can change permissions\n" -"§eby clicking a player''s icon." +msgid "Error: That player does not have an island!" msgstr "" #, java-format -msgid "§e{0}''s§9 Permissions" +msgid "§e{0}s island at {1} has been protected" msgstr "" -msgid "Leader" +#, java-format +msgid "§4{0}s island at {1} was already protected" msgstr "" -msgid "Member" +msgid "various chunk commands" msgstr "" -#, java-format -msgid "Can {0}" +msgid "regenerate current chunk" msgstr "" #, java-format -msgid "Cannot {0}" +msgid "successfully regenerated chunk at {0},{1}" msgstr "" -msgid "§e" +msgid "unload current chunk" msgstr "" -msgid "Island Log" +#, java-format +msgid "successfully unloaded chunk at {0},{1}" msgstr "" -msgid "" -"§eClick here to return to\n" -"§ethe main island screen." +#, java-format +msgid "§4FAILED!§e could not unload chunk at {0},{1}" msgstr "" -msgid "§e§lIsland Log" +msgid "load current chunk" msgstr "" -msgid "Island Biome" +#, java-format +msgid "loaded chunk at {0},{1}" msgstr "" -#, java-format -msgid "Biome: {0}" +msgid "only available for players" msgstr "" -msgid "§2§lThis is your current biome." +#, java-format +msgid "§4ERROR:§e {0}" msgstr "" -msgid "§e§lClick to change to this biome." +msgid "open GUI for config" msgstr "" -msgid "You cannot use this biome." +msgid "searches config for a specific key" msgstr "" -msgid "§2chunk" +#, java-format +msgid "§c{0}§9" msgstr "" -msgid "§call" +#, java-format +msgid "§9{0}§8: §e{1}" msgstr "" #, java-format -msgid "§e{0}" +msgid "Found the following matching {0}:" msgstr "" -msgid "§c-" +msgid "§a
" msgstr "" -msgid "Decrease radius of biome-change" +#, java-format +msgid "§3{0,number,#.##}" msgstr "" #, java-format -msgid "Current radius: {0}" +msgid "§2{0}" msgstr "" -msgid "§2+" +msgid "§eInvalid configuration name" msgstr "" -msgid "Increase radius of biome-change" +msgid "Controls player-cooldowns" msgstr "" -msgid "§7Current page" +msgid "clears the cooldown on a command (* = all)" +msgstr "" + +msgid "§eThe player is not currently online" msgstr "" #, java-format -msgid "§7Page {0}" +msgid "Cleared cooldown on {0} for {1}" msgstr "" -msgid "§7First Page" +#, java-format +msgid "No active cooldown on {0} for {1} detected!" msgstr "" -msgid "§7Last Page" +msgid "Invalid command supplied, only restart and biome supported!" msgstr "" -msgid "Island Create Menu" +msgid "restarts the cooldown on the command" msgstr "" -msgid "§a§lStart an Island" +#, java-format +msgid "§eReset cooldown on {0} for {1}§e to {2} seconds" msgstr "" -msgid "" -"Start your skyblock journey\n" -"by starting your own island.\n" -"Complete challenges to earn\n" -"items and skybucks to help\n" -"expand your skyblock. You can\n" -"invite others to join in\n" -"building your island empire!\n" -"§e§lClick here to start!" +msgid "lists all the active cooldowns" msgstr "" -msgid "§aClick to create!" +msgid "§eCmd Cooldown" msgstr "" #, java-format -msgid "" -"§cNo access!\n" -"§7({0})" +msgid "§a{0} §c{1}" msgstr "" -msgid "§a§lReturn to Spawn" +#, java-format +msgid "§eNo active cooldowns for §9{0}§e found." msgstr "" -msgid "Teleport to the spawn area." +msgid "control debugging" msgstr "" -msgid "§a§lJoin an Island" +msgid "set debug-level" msgstr "" -msgid "" -"Want to join another player''s\n" -"island instead of starting\n" -"your own? If another player\n" -"invites you to their island\n" -"you can click here or use\n" -"§e/island accept§f to join them.\n" -"§e§lClick here to accept an invite!\n" -"§e§l(You must be invited first)" +msgid "toggle debug-logging" msgstr "" -msgid "Island Menu" +msgid "§4Logging wasn't active, so you can't disable it!" msgstr "" -msgid "§a§lReturn Home" +msgid "flush current content of the logger to file." msgstr "" -msgid "" -"Return to your island''s home\n" -"point. You can change your home\n" -"point to any location on your\n" -"island using §b/island sethome\n" -"§e§lClick here to return home." +msgid "§eLog-file has been flushed." msgstr "" -msgid "§a§lChallenges" +msgid "§4Logging is not enabled, use §d/usb debug enable" msgstr "" -msgid "" -"View a list of §9challenges that\n" -"you can complete on your island\n" -"to earn skybucks, items, perks,\n" -"and titles." +msgid "§4Invalid argument, try INFO, FINE, FINER, FINEST" msgstr "" -msgid "§e§lClick here to view challenges." +msgid "§eLogging disabled!" msgstr "" -msgid "§4§lChallenges disabled." +msgid "tries to fix the the area of flatland." msgstr "" -msgid "§a§lIsland Level" +msgid "§4No valid island found" msgstr "" #, java-format -msgid "§eCurrent Level: §a{0,number,##.#}" +msgid "§4No flatland detected at {0}''s island!" +msgstr "" + +msgid "flushes all caches to files" msgstr "" +#, java-format msgid "" -"Gain island levels by expanding\n" -"your skyblock and completing\n" -"certain challenges. Rarer blocks\n" -"will add more to your level.\n" -"§e§lClick here to refresh.\n" -"§e§l(must be on island)" +"§eFlushed §a{0} islands§e, §b{1} players and §6{2} challenge-completions." msgstr "" -msgid "Island Group" +msgid "manually update the top 10 list" msgstr "" -#, java-format -msgid "§eMembers: §2{0}/{1}" +msgid "§eGenerating the Top Ten list" msgstr "" -msgid "" -"View the members of your island\n" -"group and their permissions. If\n" -"you are the island leader, you\n" -"can change the member permissions.\n" -"§e§lClick here to view or change." +msgid "§eFinished generation of the Top Ten list" msgstr "" -msgid "Change Island Biome" +msgid "advanced command for getting island-data" msgstr "" #, java-format -msgid "§eCurrent Biome: §b{0}" +msgid "§eCurrent value for {0} is ''{1}''" msgstr "" -msgid "" -"The island biome affects things\n" -"like grass color and spawning\n" -"of both animals and monsters." +#, java-format +msgid "§cUnable to get state for {0}" msgstr "" -msgid "§e§lClick here to change biomes." +#, java-format +msgid "§eValid fields are {0}" msgstr "" -msgid "§c§lYou can't change the biome." +msgid "teleport to another players island" msgstr "" -msgid "§a§lIsland Lock" +msgid "§4Only supported for players" msgstr "" -msgid "" -"§eLock Status: §aActive\n" -"§fYour island is currently §clocked.\n" -"§fPlayers outside of your group\n" -"§fare unable to enter your island." +msgid "§4That player does not have an island!" msgstr "" -msgid "§e§lClick here to unlock your island." +#, java-format +msgid "§aTeleporting to {0}''s island." msgstr "" -msgid "§c§lYou can't change the lock." +msgid "imports players and islands from other formats" msgstr "" -msgid "" -"§eLock Status: §8Inactive\n" -"§fYour island is currently §aunlocked.\n" -"§fAll players are able to enter your\n" -"§fisland, but only you and your group\n" -"§fmembers may build there." +msgid "controls async jobs" msgstr "" -msgid "§e§lClick here to lock your island." +msgid "§9Job Statistics" msgstr "" -msgid "§a§lIsland Warp" +msgid "§7----------------" msgstr "" -msgid "" -"§eWarp Status: §aActive\n" -"§fOther players may warp to your\n" -"§fisland at anytime to the point\n" -"§fyou set using §d/island setwarp." +msgid "#" msgstr "" -msgid "§e§lClick here to deactivate." +msgid "ms/job" msgstr "" -msgid "§c§lYou can't change the warp." +msgid "ms/tick" msgstr "" -msgid "" -"§eWarp Status: §8Inactive\n" -"§fOther players can't warp to your\n" -"§fisland. Set a warp point using\n" -"§d/island setwarp §fbefore activating." +msgid "ticks" msgstr "" -msgid "§e§lClick here to activate." +msgid "act" msgstr "" -msgid "§a§lIsland Log" +msgid "time" msgstr "" -msgid "" -"View a log of events from\n" -"your island such as member,\n" -"biome, and warp changes.\n" -"§e§lClick to view the log." +msgid "name" msgstr "" -msgid "§a§lChange Home Location" +msgid "transfer leadership to another player" msgstr "" -msgid "" -"When you teleport to your\n" -"island you will be taken to\n" -"this location.\n" -"§e§lClick here to change." +#, java-format +msgid "§4Player {0} has no island to transfer!" msgstr "" -msgid "§a§lChange Warp Location" +#, java-format +msgid "" +"§ePlayer §d{0}§e already has an island.§eUse §d/usb island remove §e " +"to remove him first." msgstr "" -msgid "" -"When your warp is activated,\n" -"other players will be taken to\n" -"this point when they teleport\n" -"to your island." +#, java-format +msgid "§bLeadership transferred by {0}§b to {1}" msgstr "" -msgid "§e§lClick here to change." +msgid "advanced info about NBT stuff" msgstr "" -msgid "§c§lRestart Island" +msgid "shows the NBTTag for the currently held item" msgstr "" -msgid "" -"Restarts your island.\n" -"§4WARNING! §cwill remove your items and island!" +msgid "§cNo item in hand!" msgstr "" -msgid "§c§lLeave Island" +#, java-format +msgid "§eInfo for §9{0}" msgstr "" -msgid "" -"Leaves your island.\n" -"§4WARNING! §cwill remove all your items!" +#, java-format +msgid "§7 - name: §9{0}" msgstr "" -msgid "§cClick to leave" +#, java-format +msgid "§7 - nbttag: §9{0}" msgstr "" -msgid "Island Restart Menu" +msgid "§eCan only be executed as a player" msgstr "" -msgid "Config:" +msgid "sets the NBTTag on the currently held item" msgstr "" #, java-format -msgid "§cClick within §9{0}§c to leave!" +msgid "§eSet §9{0}§e to §c{1}" msgstr "" -msgid "§a§lReturn to the main menu" +msgid "adds the NBTTag on the currently held item" msgstr "" #, java-format -msgid "§cClick within §9{0}§c to restart!" +msgid "§eAdded §9{0}§e to §c{1}" msgstr "" -msgid "§aClick to restart!" +msgid "manage orphans" msgstr "" -#, java-format -msgid "§c{0,number,#}" +msgid "count orphans" msgstr "" #, java-format -msgid "§a+{0,number,#}" +msgid "§e{0} old island locations will be used before new ones." msgstr "" -#, java-format -msgid "§a{0,number,#}" +msgid "clear orphans" msgstr "" -#, java-format -msgid "&aLeft:&7 Increment with {0}" +msgid "§eClearing all old (empty) island locations." msgstr "" -#, java-format -msgid "&cRight-Click:&7 Set to {0}" +msgid "list orphans" +msgstr "" + +msgid "§eNo orphans currently registered." msgstr "" -msgid "Return" +#, java-format +msgid "§eOrphans ({0}/{1}): {2}" msgstr "" -msgid "§9Integer Editor" +msgid "shows perk-information" msgstr "" -msgid "Caps On" +msgid "shows a specific players perks" msgstr "" -msgid "§9Text Editor" +#, java-format +msgid "additional perks {0}" msgstr "" -msgid "§eConfiguration saved and reloaded." +msgid "protects all islands (time consuming)" msgstr "" -msgid "§cError! §9Unable to save config file!" +msgid "§cTrying to abort protect-all task." msgstr "" -msgid "§3First Page" +msgid "" +"§4Sorry!§e A protect-all is already running. Let it complete first, or use " +"§9usb protectall §cstop" msgstr "" -msgid "§3Last Page" +msgid "§eStarting a protect-all task. It will take a while." msgstr "" -msgid "§cSave & Reload config" +msgid "purges all abandoned islands" msgstr "" -msgid "" -"§7Saves the settings to\n" -"§7file & reloads again.\n" -"§cNote: §7Use with care!" +msgid "§4You must provide the age in days to purge!" msgstr "" -msgid "§7 (readonly)" +msgid "§4The level must be a valid number" msgstr "" #, java-format msgid "" -"§eProgress: {0,number,##}% ({1}/{2} - success:{3}, failed:{4}, skipped:{5}) " -"~ {6}" +"§eFinding all islands that have been abandoned for more than {0} days below " +"level {1}" msgstr "" #, java-format -msgid "§4No importer named §e{0}§4 found" +msgid "§4PURGE:§e Do §9usb purge confirm§e within {0} to accept." msgstr "" -#, java-format -msgid "§eConverted {0}/{1} files in {2}" +msgid "§4Trying to abort purge" msgstr "" -#, java-format -msgid "§7PlayerDB: Filtering {0} players from uuid2name.yml" +msgid "§4Purge aborted!" msgstr "" -#, java-format -msgid "§7 - {0,number,##}% ({1}/{2}) ~ {3}" +msgid "§4A purge is already running.§e Either §9confirm§e or §9stop§e it." msgstr "" -#, java-format -msgid "§7PlayerDB: Filtered {0} names" +msgid "§4Starting purge..." msgstr "" -#, java-format -msgid "" -"§7 - MojangAPI:{4}: {0,number,##}% ({1}/{2}, failed:{3} ~ {5,number,##}%), " -"{6}" +msgid "region manipulations" msgstr "" -#, java-format -msgid "§7MojangAPI: Trying to fetch {0} players from Mojang" +msgid "shows the borders of the current island" msgstr "" -msgid "SUCCESS" +msgid "§eNo island found at your current location" msgstr "" -msgid "FAILED" +msgid "shows the borders of the current chunk" msgstr "" -#, java-format -msgid "§7 - MojangAPI:§aCOMPLETED: {0}" +msgid "shows the borders of the inner-chunks" msgstr "" -#, java-format -msgid "§7 - MojangAPI:§cERROR: {0}" +msgid "shows the non-chunk-aligned borders" msgstr "" -#, java-format -msgid "Too many requests for Mojangs API ({0} within {1}), sleeping {2}" +msgid "shows the borders of the outer-chunks" msgstr "" -msgid "North" +msgid "hides the regions again" msgstr "" -msgid "North-East" +msgid "§eStopped displaying regions" msgstr "" -msgid "East" +msgid "§eNo currently shown regions for this player" msgstr "" -msgid "South-East" +msgid "set the ticks between animations" msgstr "" -msgid "South" +#, java-format +msgid "§eAnimation-tick changed to {0}." msgstr "" -msgid "South-West" +msgid "§eAnimation-tick must be a valid integer." msgstr "" -msgid "West" +msgid "refreshes the existing animations" msgstr "" -msgid "North-West" +msgid "set a player''s island to your location" msgstr "" -msgid "The island has been created." +#, java-format +msgid "§aSet {0}''s island to the current island." msgstr "" -#, java-format -msgid "§b{0}§d locked the island." +msgid "§4Island not found: unable to set the island!" msgstr "" -msgid "§4Since your island is locked, your incoming warp has been deactivated." +msgid "reload configuration from file." msgstr "" -#, java-format -msgid "§b{0}§d deactivated the island warp." +msgid "§eConfiguration reloaded from file." +msgstr "" + +msgid "advanced command for setting island-data" msgstr "" #, java-format -msgid "§b{0}§d unlocked the island." +msgid "§c{0} was set to ''{1}''" msgstr "" #, java-format -msgid "§cSKY §f> §7 {0}" +msgid "§cUnable to set field {0} to ''{1}''" msgstr "" #, java-format -msgid "§b{0}§d has been removed from the island group." +msgid "§cUnable to set field {0} to ''{1}'', a number was expected" msgstr "" #, java-format -msgid "§9{1} §7- {0}" +msgid "§cInvalid field {0}" msgstr "" -msgid "§9Creating an island at your location" +msgid "toggles maintenance mode" msgstr "" -#, java-format -msgid "§9Creating an island §7{0}§9 of you" +msgid "§cMAINTENANCE: §aActivated§e all uSkyBlock features currently disabled." msgstr "" -msgid "§aCongratulations! §eYour island has appeared." +msgid "" +"§cMAINTENANCE: §4Deactivated§e all uSkyBlock features back to operational." msgstr "" -msgid "§cNote:§e Construction might still be ongoing." +msgid "§cMaintenance mode can only be changed from console!" msgstr "" -msgid "Use §9/is h§r or the §9/is§r menu to go there." +msgid "§cABORTED:§e Protect-All was aborted!" msgstr "" #, java-format -msgid "Unable to locate schematic {0}, contact a server-admin" +msgid "§eCompleted protect-all in {0}, {1} new regions were created!" msgstr "" #, java-format -msgid "§cWatchdog!§9 Unable to locate a chest within {0}, bailing out." +msgid "§7- SCANNING: {0,number,##}% ({1}/{2} failed: {3}) ~ {4}" msgstr "" -msgid "UNKNOWN" +msgid "§4PURGE:§9 Scanning aborted." msgstr "" -msgid "ANIMAL" +#, java-format +msgid "" +"§4PURGE:§9 Scanning done, found {0} candidates, below level {1}, ready for " +"purgatory." msgstr "" -msgid "MONSTER" +#, java-format +msgid "" +"- PURGING: {0,number,##}% ({1}/{2}), elapsed {3}, estimated completion ~{4}" msgstr "" -msgid "VILLAGER" +msgid "§4PURGE:§9 Finished purging abandoned islands." msgstr "" -msgid "GOLEM" +msgid "§4PURGE:§9 Aborted purging abandoned islands." msgstr "" -#, java-format -msgid "§c{0}" +msgid "displays version information" msgstr "" -#, java-format -msgid "§7{0}: §a{1}§7 (max. {2})" +msgid "various WorldGuard utilities" msgstr "" -msgid "" -"§cThe island owning this piece of nether is being deleted! Sending you to " -"spawn." +msgid "refreshes the chunks around the player" msgstr "" -msgid "§cThe island you are on is being deleted! Sending you to spawn." +msgid "§eResending chunks to the client" msgstr "" -#, java-format -msgid "§eWALL OF FAME (page {0} of {1}):" +msgid "load the region chunks" msgstr "" #, java-format -msgid "§4Top ten list is empty! Only islands above level {0} is considered." +msgid "§eLoading chunks at {0}" msgstr "" -msgid "§4Island level has been disabled, contact an administrator." +#, java-format +msgid "§eUnloading chunks at {0}" msgstr "" -msgid "§a#%2d §7(%5.2f): §e%s §7%s" +msgid "update the WG regions" msgstr "" -msgid "Click to warp to the island!" +#, java-format +msgid "§eIsland world-guard regions updated for {0}" msgstr "" -#, java-format -msgid "§eYour rank is: §f{0}" +msgid "§eNo island found at your location!" msgstr "" -#, java-format -msgid "§7Complete {0} more {1} §7challenges" +msgid "refreshes the chunk around the player" msgstr "" -#, java-format -msgid "§7Complete {0}" +msgid "Ultimate SkyBlock Admin" msgstr "" -msgid "to unlock this rank" +msgid "show player-information" msgstr "" -#, java-format -msgid "§4You can complete this {0} more time(s)." +msgid "try to complete a challenge" msgstr "" -#, java-format -msgid "§4Requirements will reset in {0} days." +msgid "§cCommand only available for players." msgstr "" -#, java-format -msgid "§4Requirements will reset in {0} hours." +msgid "show information about the challenge" msgstr "" -#, java-format -msgid "§4Requirements will reset in {0} minutes." +msgid "§eRank: " msgstr "" -msgid "§4This challenge is currently unavailable." +msgid "§4This Challenge is not repeatable!" msgstr "" -#, java-format -msgid "§4You can complete this again in {0} days." +msgid "§4You will lose all required items when you complete this challenge!" msgstr "" #, java-format -msgid "§4You can complete this again in {0} hours." +msgid "" +"§4All required items must be placed on your island, within {0} blocks of you." msgstr "" #, java-format -msgid "§4You can complete this again in {0} minutes." +msgid "§eTo complete this challenge, use §f/c c {0}" msgstr "" -msgid "§eThis challenge requires:" +msgid "§4Invalid challenge name! Use /c help for more information" msgstr "" -msgid "§7and more..." +msgid "complete and list challenges" msgstr "" -msgid "§eItems will be traded for reward." +msgid "§eChallenges has been disabled. Contact an administrator." msgstr "" -#, java-format -msgid "§eMust be within {0} meters." +msgid "§4You can only submit challenges in the skyblock world!" msgstr "" -msgid "§6Item Reward: §a" +msgid "§4You can only submit challenges when you have an island!" msgstr "" -#, java-format -msgid "§6Currency Reward: §a{0}" +msgid "" +"§4Your island is full, or you have too many pending invites. You can't " +"invite anyone else." msgstr "" -#, java-format -msgid "§6Exp Reward: §a{0}" +msgid "§4That player is already leader on another island." msgstr "" #, java-format -msgid "§dTotal times completed: §f{0}" +msgid "§e{0}§e tried to invite you, but you are already in a party." msgstr "" #, java-format -msgid "§7Requires {0}" +msgid "§aInvite sent to {0}" msgstr "" #, java-format -msgid "§4No challenge named {0} found" +msgid "{0}§e has invited you to join their island!" msgstr "" -msgid "§4You must be on your island to do that!" +msgid "§f/island [accept/reject]§e to accept or reject the invite." msgstr "" -#, java-format -msgid "§4The {0} challenge is not repeatable!" +msgid "§4WARNING: You will lose your current island if you accept!" msgstr "" #, java-format -msgid "§4You cannot complete the {0} challenge again yet!" +msgid "{0}§d invited {1}" msgstr "" #, java-format -msgid "§eTrying to complete challenge §a{0}" +msgid "{0}§e has rejected the invitation." msgstr "" -#, java-format -msgid "§4{0}" +msgid "§4You can't use that command right now. Leave your current party first." msgstr "" -#, java-format -msgid "§4You must be standing within {0} blocks of all required items." +msgid "" +"§aYou have joined an island! Use /island party to see the other members." msgstr "" #, java-format -msgid "§4Your island must be level {0} to complete this challenge!" +msgid "§eInvitation for {0}§e has timedout or been cancelled." msgstr "" #, java-format -msgid "§4Unknown type of challenge: {0}" +msgid "§eInvitation for {0}''s island has timedout or been cancelled." msgstr "" -#, java-format -msgid "" -"§eStill the following entities short:\n" -"{0}" +msgid "§eYou have accepted the invitation to join an island." msgstr "" -#, java-format -msgid " §4{0} §b{1}" +msgid "§4You haven't been invited." msgstr "" -#, java-format -msgid "§eYou are the following items short:{0}" +msgid "§eYou have rejected the invitation to join an island." msgstr "" -#, java-format -msgid "§aYou have completed the {0} challenge!" +msgid "accept/reject an invitation." msgstr "" -#, java-format -msgid "§9{0}§f has completed the §9{1}§f challenge!" +msgid "teleports you to your island (or create one)" msgstr "" -#, java-format -msgid "§eItem reward(s): §f{0}" +msgid "ban/unban a player from your island." msgstr "" -#, java-format -msgid "§eExp reward: §f{0,number,#.#}" +msgid "exempts user from being banned" msgstr "" -#, java-format -msgid "§eCurrency reward: §f{0,number,###.##} {1} §a ({2,number,##.##})%" +msgid "§eThe following players are banned from warping to your island:" msgstr "" -msgid "§eYour inventory is §4full§e. Items dropped on the ground." +msgid "§eTo ban/unban from your island, use /island ban " msgstr "" -msgid "§e§lClick to complete this challenge." +msgid "§4You can't ban members. Remove them first!" msgstr "" -msgid "§4§lYou can't repeat this challenge." +msgid "§4You do not have permission to kick/ban players." msgstr "" #, java-format -msgid "Rank: {0}" +msgid "§eUnable to ban unknown player {0}" msgstr "" -msgid "§fComplete most challenges in" +#, java-format +msgid "§4{0} tried to ban you from their island!" msgstr "" -msgid "§fthis rank to unlock the next rank." +#, java-format +msgid "§4{0} is exempt from being banned." msgstr "" -msgid "§eClick here to show previous page" +#, java-format +msgid "§eYou have banned §4{0}§e from warping to your island." msgstr "" -msgid "§eClick here to show next page" +#, java-format +msgid "§eYou have been §cBANNED§e from {0}§e''s island." msgstr "" -msgid "But you are ALLLLLLL ALOOOOONE!" +#, java-format +msgid "§eYou have unbanned §a{0}§e from warping to your island." msgstr "" -msgid "But you are Yelling in the wind!" +#, java-format +msgid "§eYou have been §aUNBANNED§e from {0}§e''s island." msgstr "" -msgid "But your fantasy friends are gone!" +msgid "change the biome of the island" msgstr "" -msgid "But you are Talking to your self!" +msgid "exempt player from biome-cooldown" msgstr "" #, java-format -msgid "§cSorry! {0}" +msgid "Let the player change their islands biome to {0}" msgstr "" -msgid "Either send a message directly to your group, or toggle it on/off." +msgid "" +"§cYou do not have permission to change the biome of your current island." msgstr "" -msgid "party" +msgid "§4You do not have permission to change the biome of this island!" msgstr "" -msgid "island" +msgid "§eYou must be on your island to change the biome!" msgstr "" #, java-format -msgid "§cToggled chat to {0} §aON" +msgid "§cYou have misspelled the biome name. Must be one of {0}" msgstr "" #, java-format -msgid "§cRepeat §9{0}§c to toggle it off" +msgid "§eYou can change your biome again in {0,number,#} minutes." +msgstr "" + +msgid "§cYou do not have permission to change your biome to that type." msgstr "" #, java-format -msgid "§aToggled chat §cOFF§a for {0}" +msgid "" +"§7The pixies are busy changing the biome near you to §9{0}§7, be patient." msgstr "" -msgid "§cCommand only available to players" +#, java-format +msgid "" +"§7The pixies are busy changing the biome in your current chunk to §9{0}§7, " +"be patient." msgstr "" -msgid "talk to your island party" +#, java-format +msgid "" +"§7The pixies are busy changing the biome of your island to §9{0}§7, be " +"patient." msgstr "" -msgid "talk to players on your island" +#, java-format +msgid "§eInvalid biome {0} supplied!" msgstr "" -msgid "manually update the top 10 list" +#, java-format +msgid "§aYou have changed your island''s biome to {0}" msgstr "" -msgid "§eGenerating the Top Ten list" +#, java-format +msgid "{0} changed the island biome to {1}" msgstr "" -msgid "§eFinished generation of the Top Ten list" +#, java-format +msgid "§aYou have changed {0} blocks around you to the {1} biome" msgstr "" -msgid "purges all abandoned islands" +#, java-format +msgid "{0} created an area with {1} biome" msgstr "" -msgid "§4You must provide the age in days to purge!" +msgid "create an island" msgstr "" -msgid "§4The level must be a valid number" +msgid "exempt player from create-cooldown" msgstr "" -#, java-format msgid "" -"§eFinding all islands that have been abandoned for more than {0} days below " -"level {1}" +"§4Island found!§e You already have an island. If you want a fresh island, " +"type§b /is restart§e to get one" msgstr "" -#, java-format -msgid "§4PURGE:§e Do §9usb purge confirm§e within {0} to accept." +msgid "" +"§4Island found!§e You are already a member of an island. To start your own, " +"first§b /is leave" msgstr "" -msgid "§4Trying to abort purge" +#, java-format +msgid "§eYou can create a new island in {0,number,#} seconds." msgstr "" -msgid "§4Purge aborted!" +msgid "teleport to the island home" msgstr "" -msgid "§4A purge is already running.§e Either §9confirm§e or §9stop§e it." +msgid "" +"§cYour island is in the process of generating, you cannot teleport home " +"right now." msgstr "" -msgid "§4Starting purge..." +msgid "check your or another''s island info" msgstr "" -msgid "Controls player-cooldowns" +msgid "allows user to see others island info" msgstr "" -msgid "clears the cooldown on a command (* = all)" +msgid "§4Island level has been disabled, contact an administrator." msgstr "" -msgid "§eThe player is not currently online" +msgid "§4Hold your horses! §eYou have to be patient..." msgstr "" -#, java-format -msgid "Cleared cooldown on {0} for {1}" +msgid "§eYou must be on your island to use this command." msgstr "" -#, java-format -msgid "No active cooldown on {0} for {1} detected!" +msgid "§4You do not have an island!" msgstr "" -msgid "Invalid command supplied, only restart and biome supported!" +msgid "§4You do not have access to that command!" msgstr "" -msgid "restarts the cooldown on the command" +msgid "§4That player is invalid or does not have an island!" msgstr "" #, java-format -msgid "§eReset cooldown on {0} for {1}§e to {2} seconds" +msgid "§eBlocks on {0}s Island (page {1,number} of {2,number}):" msgstr "" -msgid "lists all the active cooldowns" +msgid "Score Count Block" msgstr "" -msgid "§eCmd Cooldown" +#, java-format +msgid "{0,number,00.00} {1,number,#} {2}" msgstr "" #, java-format -msgid "§a{0} §c{1}" +msgid "§aIsland level is {0,number,###.##}" msgstr "" -#, java-format -msgid "§eNo active cooldowns for §9{0}§e found." +msgid "invite a player to your island" msgstr "" -msgid "toggles maintenance mode" +msgid "" +"§eUse§f /island invite §e to invite a player to your island." msgstr "" -msgid "§cMAINTENANCE: §aActivated§e all uSkyBlock features currently disabled." +msgid "§4Only the island''s owner can invite!" msgstr "" -msgid "" -"§cMAINTENANCE: §4Deactivated§e all uSkyBlock features back to operational." +#, java-format +msgid "§aYou can invite {0} more players." msgstr "" -msgid "§cMaintenance mode can only be changed from console!" +msgid "§4You can't invite any more players." msgstr "" -msgid "controls async jobs" +msgid "§4You do not have permission to invite others to this island!" msgstr "" -msgid "§9Job Statistics" +msgid "§4That player is offline or doesn't exist." msgstr "" -msgid "§7----------------" +msgid "§4You can't invite yourself!" msgstr "" -msgid "#" +msgid "§4That player is the leader of your island!" msgstr "" -msgid "ms/job" +msgid "remove a member from your island." msgstr "" -msgid "ms/tick" +msgid "§4You do not have permission to kick others from this island!" msgstr "" -msgid "ticks" +msgid "§4You can't remove the leader from the Island!" msgstr "" -msgid "act" +msgid "§4Stop kickin' yourself!" msgstr "" -msgid "time" +#, java-format +msgid "§4{0} has removed you from their island!" msgstr "" -msgid "name" +#, java-format +msgid "§4{0} has been removed from the island." msgstr "" #, java-format -msgid "§ePlayer {0} has no island!" +msgid "§4{0} tried to kick you from their island!" msgstr "" #, java-format -msgid "§eInvalid player {0} supplied." +msgid "§4{0} is exempt from being kicked." msgstr "" -msgid "flushes all caches to files" +#, java-format +msgid "§4{0} has kicked you from their island!" msgstr "" #, java-format -msgid "" -"§eFlushed §a{0} islands§e, §b{1} players and §6{2} challenge-completions." +msgid "§4{0} has been kicked from the island." msgstr "" -msgid "tries to fix the the area of flatland." +msgid "§4That player is not part of your island group, and not on your island!" msgstr "" -msgid "§4No valid island found" +msgid "leave your party" msgstr "" -#, java-format -msgid "§4No flatland detected at {0}''s island!" +msgid "" +"§4You can't leave your island if you are the only person. Try using /island " +"restart if you want a new one!" msgstr "" -msgid "manage orphans" +msgid "§eYou own this island, use /island remove instead." msgstr "" -msgid "count orphans" +msgid "§eYou have left the island and returned to the player spawn." msgstr "" #, java-format -msgid "§e{0} old island locations will be used before new ones." +msgid "§4{0} has left your island!" msgstr "" -msgid "clear orphans" +msgid "§4You must be in the skyblock world to leave your party!" msgstr "" -msgid "§eClearing all old (empty) island locations." +msgid "check your or anothers island level" msgstr "" -msgid "list orphans" +msgid "allows user to query for others levels" msgstr "" -msgid "§eNo orphans currently registered." +#, java-format +msgid "§eInformation about {0}''s Island:" msgstr "" #, java-format -msgid "§eOrphans ({0}/{1}): {2}" +msgid "§9Rank is {0}" msgstr "" -msgid "transfer leadership to another player" +#, java-format +msgid "§4Could not locate rank of {0}" msgstr "" -#, java-format -msgid "§4Player {0} has no island to transfer!" +msgid "lock your island to non-party members." msgstr "" -#, java-format -msgid "" -"§ePlayer §d{0}§e already has an island.§eUse §d/usb island remove §e " -"to remove him first." +msgid "§4You do not have permission to lock your island!" msgstr "" -#, java-format -msgid "§bLeadership transferred by {0}§b to {1}" +msgid "§4You don't have access to this command!" msgstr "" -msgid "open GUI for config" +msgid "§4You do not have permission to unlock your island!" msgstr "" -msgid "searches config for a specific key" +msgid "display log" msgstr "" -#, java-format -msgid "§c{0}§9" +msgid "transfer leadership to another member" msgstr "" -#, java-format -msgid "§9{0}§8: §e{1}" +msgid "§4You can only transfer ownership to party-members!" msgstr "" #, java-format -msgid "Found the following matching {0}:" +msgid "{0}§e is already leader of your island!" msgstr "" -msgid "§a
" +msgid "§4Only leader can transfer leadership!" msgstr "" #, java-format -msgid "§3{0,number,#.##}" +msgid "{0} tried to take over the island!" msgstr "" -#, java-format -msgid "§2{0}" +msgid "show the islands limits" msgstr "" -msgid "§eInvalid configuration name" +msgid "show party information" msgstr "" -msgid "teleport to another players island" +msgid "shows information about your party" msgstr "" -msgid "§4Only supported for players" +msgid "show pending invites" msgstr "" -msgid "§4That player does not have an island!" +msgid "§eNo pending invites" msgstr "" -#, java-format -msgid "§aTeleporting to {0}''s island." +msgid "withdraw an invite" msgstr "" -msgid "various WorldGuard utilities" +msgid "§4You don't have permissions to uninvite players." msgstr "" -msgid "refreshes the chunks around the player" +msgid "§4This command can only be executed by a player" msgstr "" -msgid "§eResending chunks to the client" +msgid "§4No Island. §eUse §b/is create§e to get one" msgstr "" -msgid "load the region chunks" +msgid "changes a members island-permissions" msgstr "" #, java-format -msgid "§eLoading chunks at {0}" +msgid "§ePermissions for §9{0}§e:" msgstr "" -#, java-format -msgid "§eUnloading chunks at {0}" +msgid "§aON" msgstr "" -msgid "update the WG regions" +msgid "§cOFF" msgstr "" #, java-format -msgid "§eIsland world-guard regions updated for {0}" +msgid "§7 - §6{0}§7 : {1}" msgstr "" -msgid "§eNo island found at your location!" +#, java-format +msgid "§cInvalid permission {0}. Must be one of {1}" msgstr "" -msgid "refreshes the chunk around the player" +#, java-format +msgid "§eToggled permission §9{0}§e for §9{1}§e to {2}" msgstr "" -msgid "region manipulations" +#, java-format +msgid "§eUnable to toggle permission §9{0}§e for §9{1}" msgstr "" -msgid "shows the borders of the current island" +msgid "delete your island and start a new one." msgstr "" -msgid "§eNo island found at your current location" +msgid "exempt player from restart-cooldown" msgstr "" -msgid "§eCan only be executed as a player" +msgid "" +"§4Only the owner may restart this island. Leave this island in order to " +"start your own (/island leave)." msgstr "" -msgid "shows the borders of the current chunk" +msgid "" +"§eYou must remove all players from your island before you can restart it (/" +"island kick ). See a list of players currently part of your island " +"using /island party." msgstr "" -msgid "shows the borders of the inner-chunks" +#, java-format +msgid "§cYou can restart your island in {0} seconds." msgstr "" -msgid "shows the non-chunk-aligned borders" +msgid "§cYour island is in the process of generating, you cannot restart now." msgstr "" -msgid "shows the borders of the outer-chunks" +msgid "§eNOTE: Your entire island and all your belongings will be RESET!" msgstr "" -msgid "hides the regions again" +msgid "set the island-home" msgstr "" -msgid "§eStopped displaying regions" +msgid "§4You must be closer to your island to set your skyblock home!" msgstr "" -msgid "§eNo currently shown regions for this player" +msgid "§4Your current location is not a safe home-location." msgstr "" -msgid "set the ticks between animations" +msgid "§aYour skyblock home has been set to your current location." msgstr "" -#, java-format -msgid "§eAnimation-tick changed to {0}." +msgid "set your island''s warp location" msgstr "" -msgid "§eAnimation-tick must be a valid integer." +msgid "§cYou do not have permission to set your island''s warp point!" msgstr "" -msgid "refreshes the existing animations" +msgid "§cYou need to be on your own island to set the warp!" msgstr "" #, java-format -msgid "" -"- PURGING: {0,number,##}% ({1}/{2}), elapsed {3}, estimated completion ~{4}" +msgid "§b{0}§d changed the island warp location." msgstr "" -msgid "§4PURGE:§9 Finished purging abandoned islands." +msgid "teleports you to the skyblock spawn" msgstr "" -msgid "§4PURGE:§9 Aborted purging abandoned islands." +msgid "enable/disable warping to your island." msgstr "" -#, java-format -msgid "§7- SCANNING: {0,number,##}% ({1}/{2} failed: {3}) ~ {4}" +msgid "§4Your island is locked. You must unlock it before enabling your warp." msgstr "" -msgid "§4PURGE:§9 Scanning aborted." +#, java-format +msgid "§b{0}§d activated the island warp." msgstr "" #, java-format -msgid "" -"§4PURGE:§9 Scanning done, found {0} candidates, below level {1}, ready for " -"purgatory." +msgid "§b{0}§d deactivated the island warp." msgstr "" -msgid "§cABORTED:§e Protect-All was aborted!" +msgid "§cYou do not have permission to enable/disable your island''s warp!" msgstr "" -#, java-format -msgid "§eCompleted protect-all in {0}, {1} new regions were created!" +msgid "display the top10 of islands" msgstr "" -msgid "control debugging" +msgid "enables user to all-ways generate top-ten (no caching)" msgstr "" -msgid "set debug-level" +msgid "trust/untrust a player to help on your island." msgstr "" -msgid "toggle debug-logging" +msgid "§eThe following players are trusted on your island:" msgstr "" -msgid "§4Logging wasn't active, so you can't disable it!" +msgid "§eThe following leaders trusts you:" msgstr "" -msgid "flush current content of the logger to file." +msgid "§eTo trust/untrust from your island, use /island trust " msgstr "" -msgid "§eLog-file has been flushed." +msgid "§4Members are already trusted!" msgstr "" -msgid "§4Logging is not enabled, use §d/usb debug enable" +#, java-format +msgid "§4Unknown player {0}" msgstr "" -msgid "§4Invalid argument, try INFO, FINE, FINER, FINEST" +#, java-format +msgid "§eYou are now trusted on §4{0}''s §eisland." msgstr "" -msgid "§eLogging disabled!" +#, java-format +msgid "§a{0} trusted {1} on the island" msgstr "" -msgid "advanced command for setting island-data" +#, java-format +msgid "§eYou are no longer trusted on §4{0}''s §eisland." msgstr "" #, java-format -msgid "§c{0} was set to ''{1}''" +msgid "§c{0} revoked trust in {1} on the island" msgstr "" -#, java-format -msgid "§cUnable to set field {0} to ''{1}''" +msgid "warp to another player''s island" msgstr "" -#, java-format -msgid "§cUnable to set field {0} to ''{1}'', a number was expected" +msgid "§aYour incoming warp is active, players may warp to your island." msgstr "" -#, java-format -msgid "§cInvalid field {0}" +msgid "§4Your incoming warp is inactive, players may not warp to your island." msgstr "" -#, java-format -msgid "§eCurrent value for {0} is ''{1}''" +msgid "§fSet incoming warp to your current location using §e/island setwarp" msgstr "" -#, java-format -msgid "§cUnable to get state for {0}" +msgid "§fToggle your warp on/off using §e/island togglewarp" msgstr "" -#, java-format -msgid "§eValid fields are {0}" +msgid "§4You do not have permission to create a warp on your island!" msgstr "" -msgid "set a player''s island to your location" +msgid "§fWarp to another island using §e/island warp " msgstr "" -#, java-format -msgid "§aSet {0}''s island to the current island." +msgid "§4You do not have permission to warp to other islands!" msgstr "" -msgid "§4Island not found: unable to set the island!" +msgid "" +"§cYour island is in the process of generating, you cannot warp to other " +"players islands right now." msgstr "" -msgid "advanced info about NBT stuff" +msgid "§4That player does not exist!" msgstr "" -msgid "shows the NBTTag for the currently held item" +msgid "§4That player does not have an active warp." msgstr "" -#, java-format -msgid "§eInfo for §9{0}" +msgid "" +"§cThat players island is in the process of generating, you cannot warp to it " +"right now." msgstr "" #, java-format -msgid "§7 - name: §9{0}" +msgid "§cWARNING: §9{0}§e is warping to your island!" msgstr "" -#, java-format -msgid "§7 - nbttag: §9{0}" +msgid "§4That player has forbidden you from warping to their island." msgstr "" -msgid "§cNo item in hand!" +msgid "general island command" msgstr "" -msgid "sets the NBTTag on the currently held item" +msgid "allows user to bypass cooldowns" msgstr "" -#, java-format -msgid "§eSet §9{0}§e to §c{1}" +msgid "allows user to bypass visitor-protections" msgstr "" -msgid "adds the NBTTag on the currently held item" +msgid "allows user to bypass teleport-delay" msgstr "" -#, java-format -msgid "§eAdded §9{0}§e to §c{1}" +msgid "allows user to use [usb] signs" msgstr "" -msgid "Manage challenges for a player" +msgid "allows user to place [usb] signs" msgstr "" -msgid "resets the challenge for the player" +msgid "§eYou can not use another island''s portals!" msgstr "" -msgid "§4Challenge has never been completed" +msgid "§eVillager-trading isn't allowed." msgstr "" -#, java-format -msgid "§echallenge: {0} has been reset for {1}" +msgid "§eTrading isn't allowed on other islands. Do it in spawn." msgstr "" -msgid "resets all challenges for the player" +msgid "§eRiding is only allowed on your own island!" msgstr "" -#, java-format -msgid "§e{0} has had all challenges reset." +msgid "§eYou cannot break vehicles while being a visitor!" msgstr "" -msgid "complete all challenges in the rank" +msgid "§cWither Despawned!§e It wandered too far from your island." msgstr "" #, java-format -msgid "§4No player named {0} was found!" +msgid "{0}''s Wither" msgstr "" -#, java-format -msgid "§4Challenge {0} has already been completed" +msgid "§eVisitors can't drop items!" msgstr "" #, java-format -msgid "§eChallenge {0} has been completed for {1}" +msgid "Owner: {0}" msgstr "" -#, java-format -msgid "§4No challenge named {0} was found!" +msgid "You cannot pick up other players' loot when you are a visitor!" msgstr "" -#, java-format -msgid "§4No rank named {0} was found!" +msgid "§4You can only convert obsidian once every 10 seconds" msgstr "" -msgid "various chunk commands" +msgid "§eChanging your obsidian back into lava. Be careful!" msgstr "" -msgid "regenerate current chunk" +msgid "§eYour inventory must have another empty space!" msgstr "" -#, java-format -msgid "successfully regenerated chunk at {0},{1}" +msgid "§4It''s a bad idea to replace your lava!" msgstr "" -#, java-format -msgid "§4FAILED!§e could not regenerate chunk at {0},{1}" +msgid "§eYou cannot hurt island-members." msgstr "" -msgid "unload current chunk" +msgid "§4That player has forbidden you from teleporting to their island." msgstr "" -#, java-format -msgid "successfully unloaded chunk at {0},{1}" +msgid "§4That island is §clocked.§e No teleporting to the island." msgstr "" #, java-format -msgid "§4FAILED!§e could not unload chunk at {0},{1}" +msgid "" +"§4{0} is limited. §eScanning your island to see if you are allowed to place " +"more, please be patient" msgstr "" -msgid "load current chunk" +msgid "§e... Scanning complete, you can try again" msgstr "" #, java-format -msgid "loaded chunk at {0},{1}" +msgid "" +"§4You''ve hit the {0} limit!§e You can''t have more of that type on your " +"island!§9 Max: {1,number}" msgstr "" -msgid "only available for players" +msgid "§eYou can only use spawn-eggs on your own island." +msgstr "" + +msgid "§cYou have reached your spawn-limit for your island." +msgstr "" + +msgid "§cBanned:§e You are banned from this island." +msgstr "" + +msgid "§cLocked:§e That island is locked! No entry allowed." msgstr "" #, java-format -msgid "§4ERROR:§e {0}" +msgid "§eWaiting for our turn §c{0,number,###}%" msgstr "" -msgid "protects all islands (time consuming)" +#, java-format +msgid "§9Creating island...§e{0,number,###}%" msgstr "" -msgid "§cTrying to abort protect-all task." +#, java-format +msgid "§9{0}§7 timed out" msgstr "" +#, java-format msgid "" -"§4Sorry!§e A protect-all is already running. Let it complete first, or use " -"§9usb protectall §cstop" +"§eDoing §9{0}§e is §cRISKY§e. Repeat the command within §a{1}§e seconds to " +"accept!" msgstr "" -msgid "§eStarting a protect-all task. It will take a while." +msgid "N/A" msgstr "" -msgid "reload configuration from file." +msgid "§4** You are entering a protected - but abandoned - island area." msgstr "" -msgid "§eConfiguration reloaded from file." +#, java-format +msgid "§d** You are entering §b{0}''s §disland." msgstr "" -msgid "manage islands" +msgid "§4** You are leaving an abandoned island." msgstr "" -msgid "protects the island" +#, java-format +msgid "§d** You are leaving §b{0}''s §disland." msgstr "" -msgid "delete the island (removes the blocks)" +msgid "§eYour island is now locked. Only your party members may enter." msgstr "" -msgid "§9Deleted abandoned island at your current location." +msgid "§4You must be the party leader to lock your island!" +msgstr "" + +msgid "" +"§eYour island is unlocked and anyone may enter, however only you and your " +"party members may build or remove blocks." msgstr "" -msgid "" -"§4Island at this location has members!\n" -"§eUse §9/usb island delete §e to delete it." +msgid "§4You must be the party leader to unlock your island!" msgstr "" -msgid "removes the player from the island" +#, java-format +msgid "§7PlayerDB: Filtering {0} players from uuid2name.yml" msgstr "" -msgid "adds the player to the island" +#, java-format +msgid "§7 - {0,number,##}% ({1}/{2}) ~ {3}" msgstr "" #, java-format -msgid "§b{0}§d has joined your island group." +msgid "§7PlayerDB: Filtered {0} names" msgstr "" #, java-format -msgid "§4No player named {0} found!" +msgid "" +"§7 - MojangAPI:{4}: {0,number,##}% ({1}/{2}, failed:{3} ~ {5,number,##}%), " +"{6}" msgstr "" -msgid "" -"§4No valid island provided, either stand within one, or provide an island " -"name" +#, java-format +msgid "§7MojangAPI: Trying to fetch {0} players from Mojang" msgstr "" -msgid "print out info about the island" +msgid "SUCCESS" msgstr "" -msgid "sets the biome of the island" +msgid "FAILED" msgstr "" -msgid "§4That player has no island." +#, java-format +msgid "§7 - MojangAPI:§aCOMPLETED: {0}" msgstr "" -msgid "§4No valid island at your location" +#, java-format +msgid "§7 - MojangAPI:§cERROR: {0}" msgstr "" -msgid "purges the island" +#, java-format +msgid "" +"§eProgress: {0,number,##}% ({1}/{2} - success:{3}, failed:{4}, skipped:{5}) " +"~ {6}" msgstr "" -msgid "§4Error! §9No valid island found for purging." +#, java-format +msgid "§4No importer named §e{0}§4 found" msgstr "" #, java-format -msgid "§cPURGE: §9Purged island at {0}" +msgid "§eConverted {0}/{1} files in {2}" msgstr "" -msgid "toggles the islands ignore status" +msgid "The island has been created." msgstr "" #, java-format -msgid "§cSet {0}s island to be ignored on top-ten and purge." +msgid "§b{0}§d locked the island." msgstr "" -#, java-format -msgid "" -"§cRemoved ignore-flag of {0}s island, it will now show up on top-ten and " -"purge." +msgid "§4Since your island is locked, your incoming warp has been deactivated." msgstr "" -msgid "§4No valid player-name supplied." +#, java-format +msgid "§b{0}§d unlocked the island." msgstr "" #, java-format -msgid "Removing {0} from island" +msgid "§cSKY §f> §7 {0}" msgstr "" #, java-format -msgid "§eChanged biome of {0}s island to {1}." +msgid "§b{0}§d has been removed from the island group." msgstr "" #, java-format -msgid "§eChanged biome of {0}s island to OCEAN." +msgid "§9{1} §7- {0}" msgstr "" -msgid "§aYou may need to go to spawn, or relog, to see the changes." +msgid "§9Creating an island at your location" msgstr "" #, java-format -msgid "§e{0} has had their biome changed to {1}." +msgid "§9Creating an island §7{0}§9 of you" +msgstr "" + +msgid "" +"§cThe island owning this piece of nether is being deleted! Sending you to " +"spawn." +msgstr "" + +msgid "§cThe island you are on is being deleted! Sending you to spawn." msgstr "" #, java-format -msgid "§e{0} has had their biome changed to OCEAN." +msgid "§eWALL OF FAME (page {0} of {1}):" msgstr "" #, java-format -msgid "§eRemoving {0}''s island." +msgid "§4Top ten list is empty! Only islands above level {0} is considered." msgstr "" -msgid "Error: That player does not have an island!" +msgid "§a#%2d §7(%5.2f): §e%s §7%s" msgstr "" -#, java-format -msgid "§e{0}s island at {1} has been protected" +msgid "Click to warp to the island!" msgstr "" #, java-format -msgid "§4{0}s island at {1} was already protected" +msgid "§eYour rank is: §f{0}" msgstr "" -msgid "displays version information" +msgid "UNKNOWN" msgstr "" -msgid "imports players and islands from other formats" +msgid "ANIMAL" msgstr "" -msgid "shows perk-information" +msgid "MONSTER" msgstr "" -msgid "shows a specific players perks" +msgid "VILLAGER" msgstr "" -#, java-format -msgid "additional perks {0}" +msgid "GOLEM" msgstr "" -msgid "changes the language of the plugin, and reloads" +#, java-format +msgid "§c{0}" msgstr "" #, java-format -msgid "§aSuccessfully changed language to §e{0}" +msgid "§7{0}: §a{1}§7 (max. {2})" msgstr "" #, java-format -msgid "§cFailed to change language to §e{0}" +msgid "Unable to locate schematic {0}, contact a server-admin" msgstr "" -msgid "§9Supported Languages:\n" +msgid "§aCongratulations! §eYour island has appeared." msgstr "" -#, java-format -msgid "§f{0} §7{1} §9 by {2} §7{3}\n" +msgid "§cNote:§e Construction might still be ongoing." msgstr "" -msgid "§cUnable to locate any languages." +msgid "Use §9/is h§r or the §9/is§r menu to go there." msgstr "" -msgid "advanced command for getting island-data" +#, java-format +msgid "§cWatchdog!§9 Unable to locate a chest within {0}, bailing out." msgstr "" -msgid "Ultimate SkyBlock Admin" +#, java-format +msgid "§7Page {0}" msgstr "" -msgid "show player-information" +#, java-format +msgid "§c{0,number,#}" msgstr "" -msgid "" -"§4Your island is full, or you have too many pending invites. You can't " -"invite anyone else." +#, java-format +msgid "§a+{0,number,#}" msgstr "" -msgid "§4That player is already leader on another island." +#, java-format +msgid "§a{0,number,#}" msgstr "" #, java-format -msgid "§e{0}§e tried to invite you, but you are already in a party." +msgid "&aLeft:&7 Increment with {0}" msgstr "" #, java-format -msgid "§aInvite sent to {0}" +msgid "&cRight-Click:&7 Set to {0}" msgstr "" -#, java-format -msgid "{0}§e has invited you to join their island!" +msgid "Return" msgstr "" -msgid "§f/island [accept/reject]§e to accept or reject the invite." +msgid "Config:" msgstr "" -msgid "§4WARNING: You will lose your current island if you accept!" +msgid "§9Integer Editor" msgstr "" -#, java-format -msgid "{0}§d invited {1}" +msgid "§eConfiguration saved and reloaded." msgstr "" -#, java-format -msgid "{0}§e has rejected the invitation." +msgid "§cError! §9Unable to save config file!" msgstr "" -msgid "§4You can't use that command right now. Leave your current party first." +msgid "§3First Page" msgstr "" -msgid "" -"§aYou have joined an island! Use /island party to see the other members." +msgid "§3Last Page" msgstr "" -#, java-format -msgid "§eInvitation for {0}§e has timedout or been cancelled." +msgid "§cSave & Reload config" msgstr "" -#, java-format -msgid "§eInvitation for {0}''s island has timedout or been cancelled." +msgid "" +"§7Saves the settings to\n" +"§7file & reloads again.\n" +"§cNote: §7Use with care!" msgstr "" -msgid "§eYou have accepted the invitation to join an island." +msgid "§7 (readonly)" msgstr "" -msgid "§4You haven't been invited." +msgid "true" msgstr "" -msgid "§eYou have rejected the invitation to join an island." +msgid "false" msgstr "" -msgid "general island command" +msgid "Challenge Menu" msgstr "" -msgid "allows user to bypass cooldowns" +msgid "Change Biome" msgstr "" -msgid "allows user to bypass visitor-protections" +msgid "change the island''s biome." msgstr "" -msgid "allows user to bypass teleport-delay" +msgid "Toggle Island Lock" msgstr "" -msgid "allows user to use [usb] signs" +msgid "toggle the island''s lock." msgstr "" -msgid "allows user to place [usb] signs" +msgid "Set Island Warp" msgstr "" -msgid "complete and list challenges" +msgid "set the island''s warp." msgstr "" -msgid "§cCommand only available for players." +msgid "" +"set the island''s warp,\n" +"which allows non-group\n" +"members to teleport to\n" +"the island." msgstr "" -msgid "§eChallenges has been disabled. Contact an administrator." +msgid "Toggle Island Warp" msgstr "" -msgid "§4You can only submit challenges in the skyblock world!" +msgid "toggle the island''s warp." msgstr "" -msgid "§4You can only submit challenges when you have an island!" +msgid "" +"toggle the island''s warp,\n" +"allowing them to turn it\n" +"on or off at anytime, but\n" +"not set the location." msgstr "" -msgid "warp to another player''s island" +msgid "Invite Players" msgstr "" -msgid "§aYour incoming warp is active, players may warp to your island." +msgid "invite others to the island." msgstr "" -msgid "§4Your incoming warp is inactive, players may not warp to your island." +msgid "" +"invite\n" +"other players to the island if\n" +"there is enough room for more\n" +"members" msgstr "" -msgid "§fSet incoming warp to your current location using §e/island setwarp" +msgid "Kick Players" msgstr "" -msgid "§fToggle your warp on/off using §e/island togglewarp" +msgid "kick others from the island." msgstr "" -msgid "§4You do not have permission to create a warp on your island!" +msgid "" +"kick\n" +"other players from the island,\n" +"but they are unable to kick\n" +"the island leader." msgstr "" -msgid "§fWarp to another island using §e/island warp " +msgid "Ocean" msgstr "" -msgid "§4You do not have permission to warp to other islands!" +msgid "" +"The ocean biome is the basic\n" +"starting biome for all islands.\n" +"passive mobs like animals will\n" +"not spawn. Hostile mobs will\n" +"spawn normally." +msgstr "" + +msgid "Forest" msgstr "" msgid "" -"§cYour island is in the process of generating, you cannot warp to other " -"players islands right now." +"The forest biome will allow\n" +"your island to spawn passive.\n" +"mobs like animals (including\n" +"wolves). Hostile mobs will\n" +"spawn normally." msgstr "" -msgid "§4That player does not exist!" +msgid "Desert" msgstr "" -msgid "§4That player does not have an active warp." +msgid "" +"The desert biome makes it so\n" +"that there is no rain or snow\n" +"on your island. Passive mobs\n" +"won't spawn. Hostile mobs will\n" +"spawn normally." +msgstr "" + +msgid "Jungle" msgstr "" msgid "" -"§cThat players island is in the process of generating, you cannot warp to it " -"right now." +"The jungle biome is bright\n" +"and colorful. Passive mobs\n" +"(including ocelots) will\n" +"spawn. Hostile mobs will\n" +"spawn normally." msgstr "" -#, java-format -msgid "§cWARNING: §9{0}§e is warping to your island!" +msgid "Swampland" msgstr "" -msgid "§4That player has forbidden you from warping to their island." +msgid "" +"The swamp biome is dark\n" +"and dull. Passive mobs\n" +"will spawn normally and\n" +"slimes have a small chance\n" +"to spawn at night depending\n" +"on the moon phase." msgstr "" -msgid "§4No Island. §eUse §b/is create§e to get one" +msgid "Taiga" msgstr "" -msgid "accept/reject an invitation." +msgid "" +"The taiga biome has snow\n" +"instead of rain. Passive\n" +"mobs will spawn normally\n" +"(including wolves) and\n" +"hostile mobs will spawn." msgstr "" -msgid "leave your party" +msgid "Mushroom" msgstr "" msgid "" -"§4You can't leave your island if you are the only person. Try using /island " -"restart if you want a new one!" +"The mushroom biome is\n" +"bright and colorful.\n" +"Mooshrooms are the only\n" +"mobs that will spawn.\n" +"No other passive or\n" +"hostile mobs will spawn." msgstr "" -msgid "§eYou own this island, use /island remove instead." +msgid "Hell" msgstr "" -msgid "§eYou have left the island and returned to the player spawn." +msgid "" +"The hell biome looks\n" +"dark and dead. Some\n" +"mobs from the nether will\n" +"spawn in this biome\n" +"(excluding ghasts and\n" +"blazes)." msgstr "" -#, java-format -msgid "§4{0} has left your island!" +msgid "Sky" msgstr "" -msgid "§4You must be in the skyblock world to leave your party!" +msgid "" +"The sky biome gives your\n" +"island a special dark sky.\n" +"Only endermen will spawn\n" +"in this biome." msgstr "" -msgid "check your or anothers island level" +msgid "Plains" msgstr "" -msgid "allows user to query for others levels" +msgid "" +"The plains biome has rain\n" +"instead of snow. Passive\n" +"mobs will spawn normally\n" +"(including horses) and\n" +"hostile mobs will spawn." msgstr "" -msgid "§eYou must be on your island to use this command." +msgid "Extreme Hills" msgstr "" -msgid "§4You do not have an island!" +msgid "" +"The extreme hills biome.\n" +"Passive mobs will spawn \n" +"normally and hostile\n" +"mobs will spawn." msgstr "" -msgid "§4You do not have access to that command!" +msgid "Flower Forest" msgstr "" -msgid "§4That player is invalid or does not have an island!" +msgid "" +"The flower forest biome.\n" +"Passive mobs will spawn \n" +"normally and hostile\n" +"mobs will spawn." msgstr "" -#, java-format -msgid "§eInformation about {0}''s Island:" +msgid "Deep Ocean" msgstr "" -#, java-format -msgid "§aIsland level is {0,number,###.##}" +msgid "" +"The deep-ocean biome is an advanced\n" +"biome. Passive mobs like animals will\n" +"not spawn. Hostile mobs \n" +"(including Guardians) will\n" +"spawn normally." msgstr "" -#, java-format -msgid "§9Rank is {0}" +msgid "Ice Plains" msgstr "" -#, java-format -msgid "§4Could not locate rank of {0}" +msgid "" +"The ice-plains biome is an advanced biome.\n" +"Mobs will spawn naturally.\n" +"including polar-bears" msgstr "" -msgid "create an island" +msgid "Permissions" msgstr "" -msgid "exempt player from create-cooldown" +#, java-format +msgid "{0} <{1}>" msgstr "" -msgid "" -"§4Island found!§e You already have an island. If you want a fresh island, " -"type§b /is restart§e to get one" +msgid "§9Player Permissions" msgstr "" msgid "" -"§4Island found!§e You are already a member of an island. To start your own, " -"first§b /is leave" +"§eClick here to return to\n" +"§eyour island group''s info." msgstr "" #, java-format -msgid "§eYou can create a new island in {0,number,#} seconds." -msgstr "" - -msgid "invite a player to your island" +msgid "§e{0}''§9s Permissions" msgstr "" msgid "" -"§eUse§f /island invite §e to invite a player to your island." -msgstr "" - -msgid "§4Only the island''s owner can invite!" +"§eHover over an icon to view\n" +"§ea permission. Change the\n" +"§epermission by clicking it." msgstr "" -#, java-format -msgid "§aYou can invite {0} more players." +msgid "§fThis player §acan" msgstr "" -msgid "§4You can't invite any more players." +msgid "Click here to remove this permission." msgstr "" -msgid "§4You do not have permission to invite others to this island!" +msgid "§fThis player §ccannot" msgstr "" -msgid "§4That player is offline or doesn't exist." +msgid "Click here to grant this permission." msgstr "" -msgid "§4You can't invite yourself!" +msgid "Island Group Members" msgstr "" -msgid "§4That player is the leader of your island!" +#, java-format +msgid "Group Members: §2{0}§7/§e{1}" msgstr "" -msgid "lock your island to non-party members." +msgid "§aMore players can be invited to this island." msgstr "" -msgid "§4You do not have permission to lock your island!" +msgid "§cThis island is full." msgstr "" -msgid "§4You don't have access to this command!" +msgid "" +"§eHover over a player''s icon to\n" +"§eview their permissions. The\n" +"§eleader can change permissions\n" +"§eby clicking a player''s icon." msgstr "" -msgid "§4You do not have permission to unlock your island!" +#, java-format +msgid "§e{0}''s§9 Permissions" msgstr "" -msgid "display the top10 of islands" +msgid "Leader" msgstr "" -msgid "enables user to all-ways generate top-ten (no caching)" +msgid "Member" msgstr "" -msgid "remove a member from your island." +#, java-format +msgid "Can {0}" msgstr "" -msgid "§4You do not have permission to kick others from this island!" +#, java-format +msgid "Cannot {0}" msgstr "" -msgid "§4You can't remove the leader from the Island!" +msgid "§e" msgstr "" -msgid "§4Stop kickin' yourself!" +msgid "Island Log" msgstr "" -#, java-format -msgid "§4{0} has removed you from their island!" +msgid "" +"§eClick here to return to\n" +"§ethe main island screen." msgstr "" -#, java-format -msgid "§4{0} has been removed from the island." +msgid "§e§lIsland Log" msgstr "" -#, java-format -msgid "§4{0} tried to kick you from their island!" +msgid "Island Biome" msgstr "" #, java-format -msgid "§4{0} is exempt from being kicked." +msgid "Biome: {0}" msgstr "" -#, java-format -msgid "§4{0} has kicked you from their island!" +msgid "§2§lThis is your current biome." msgstr "" -#, java-format -msgid "§4{0} has been kicked from the island." +msgid "§e§lClick to change to this biome." msgstr "" -msgid "§4That player is not part of your island group, and not on your island!" +msgid "You cannot use this biome." msgstr "" -msgid "teleport to the island home" +msgid "§2chunk" msgstr "" -msgid "" -"§cYour island is in the process of generating, you cannot teleport home " -"right now." +msgid "§call" msgstr "" -msgid "§4This command can only be executed by a player" +#, java-format +msgid "§e{0}" msgstr "" -msgid "transfer leadership to another member" +msgid "§c-" msgstr "" -msgid "§4You can only transfer ownership to party-members!" +msgid "Decrease radius of biome-change" msgstr "" #, java-format -msgid "{0}§e is already leader of your island!" +msgid "Current radius: {0}" msgstr "" -msgid "§4Only leader can transfer leadership!" +msgid "§2+" msgstr "" -#, java-format -msgid "{0} tried to take over the island!" +msgid "Increase radius of biome-change" msgstr "" -msgid "show party information" +msgid "§7Current page" msgstr "" -msgid "shows information about your party" +msgid "§7First Page" msgstr "" -msgid "show pending invites" +msgid "§7Last Page" msgstr "" -msgid "§eNo pending invites" +msgid "Island Create Menu" msgstr "" -msgid "withdraw an invite" +msgid "§a§lStart an Island" msgstr "" -msgid "§4You don't have permissions to uninvite players." +msgid "" +"Start your skyblock journey\n" +"by starting your own island.\n" +"Complete challenges to earn\n" +"items and skybucks to help\n" +"expand your skyblock. You can\n" +"invite others to join in\n" +"building your island empire!\n" +"§e§lClick here to start!" msgstr "" -msgid "check your or another''s island info" +msgid "§aClick to create!" msgstr "" -msgid "allows user to see others island info" +#, java-format +msgid "" +"§cNo access!\n" +"§7({0})" msgstr "" -msgid "§4Hold your horses! §eYou have to be patient..." +msgid "§a§lReturn to Spawn" msgstr "" -#, java-format -msgid "§eBlocks on {0}s Island (page {1,number} of {2,number}):" +msgid "Teleport to the spawn area." msgstr "" -msgid "Score Count Block" +msgid "§a§lJoin an Island" msgstr "" -#, java-format -msgid "{0,number,00.00} {1,number,#} {2}" +msgid "" +"Want to join another player''s\n" +"island instead of starting\n" +"your own? If another player\n" +"invites you to their island\n" +"you can click here or use\n" +"§e/island accept§f to join them.\n" +"§e§lClick here to accept an invite!\n" +"§e§l(You must be invited first)" msgstr "" -msgid "trust/untrust a player to help on your island." +msgid "Island Menu" msgstr "" -msgid "§eThe following players are trusted on your island:" +msgid "§a§lReturn Home" msgstr "" -msgid "§eThe following leaders trusts you:" +msgid "" +"Return to your island''s home\n" +"point. You can change your home\n" +"point to any location on your\n" +"island using §b/island sethome\n" +"§e§lClick here to return home." msgstr "" -msgid "§eTo trust/untrust from your island, use /island trust " +msgid "§a§lChallenges" msgstr "" -msgid "§4Members are already trusted!" +msgid "" +"View a list of §9challenges that\n" +"you can complete on your island\n" +"to earn skybucks, items, perks,\n" +"and titles." msgstr "" -#, java-format -msgid "§4Unknown player {0}" +msgid "§e§lClick here to view challenges." msgstr "" -#, java-format -msgid "§eYou are now trusted on §4{0}''s §eisland." +msgid "§4§lChallenges disabled." msgstr "" -#, java-format -msgid "§a{0} trusted {1} on the island" +msgid "§a§lIsland Level" msgstr "" #, java-format -msgid "§eYou are no longer trusted on §4{0}''s §eisland." +msgid "§eCurrent Level: §a{0,number,##.#}" msgstr "" -#, java-format -msgid "§c{0} revoked trust in {1} on the island" +msgid "" +"Gain island levels by expanding\n" +"your skyblock and completing\n" +"certain challenges. Rarer blocks\n" +"will add more to your level.\n" +"§e§lClick here to refresh.\n" +"§e§l(must be on island)" msgstr "" -msgid "delete your island and start a new one." +msgid "Island Group" msgstr "" -msgid "exempt player from restart-cooldown" +#, java-format +msgid "§eMembers: §2{0}/{1}" msgstr "" msgid "" -"§4Only the owner may restart this island. Leave this island in order to " -"start your own (/island leave)." +"View the members of your island\n" +"group and their permissions. If\n" +"you are the island leader, you\n" +"can change the member permissions.\n" +"§e§lClick here to view or change." msgstr "" -msgid "" -"§eYou must remove all players from your island before you can restart it (/" -"island kick ). See a list of players currently part of your island " -"using /island party." +msgid "Change Island Biome" msgstr "" #, java-format -msgid "§cYou can restart your island in {0} seconds." +msgid "§eCurrent Biome: §b{0}" msgstr "" -msgid "§cYour island is in the process of generating, you cannot restart now." +msgid "" +"The island biome affects things\n" +"like grass color and spawning\n" +"of both animals and monsters." msgstr "" -msgid "§eNOTE: Your entire island and all your belongings will be RESET!" +msgid "§e§lClick here to change biomes." msgstr "" -msgid "teleports you to the skyblock spawn" +msgid "§c§lYou can't change the biome." msgstr "" -msgid "change the biome of the island" +msgid "§a§lIsland Lock" msgstr "" -msgid "exempt player from biome-cooldown" +msgid "" +"§eLock Status: §aActive\n" +"§fYour island is currently §clocked.\n" +"§fPlayers outside of your group\n" +"§fare unable to enter your island." msgstr "" -#, java-format -msgid "Let the player change their islands biome to {0}" +msgid "§e§lClick here to unlock your island." msgstr "" -msgid "" -"§cYou do not have permission to change the biome of your current island." +msgid "§c§lYou can't change the lock." msgstr "" -msgid "§4You do not have permission to change the biome of this island!" +msgid "" +"§eLock Status: §8Inactive\n" +"§fYour island is currently §aunlocked.\n" +"§fAll players are able to enter your\n" +"§fisland, but only you and your group\n" +"§fmembers may build there." msgstr "" -msgid "§eYou must be on your island to change the biome!" +msgid "§e§lClick here to lock your island." msgstr "" -#, java-format -msgid "§cYou have misspelled the biome name. Must be one of {0}" +msgid "§a§lIsland Warp" msgstr "" -#, java-format -msgid "§eYou can change your biome again in {0,number,#} minutes." +msgid "" +"§eWarp Status: §aActive\n" +"§fOther players may warp to your\n" +"§fisland at anytime to the point\n" +"§fyou set using §d/island setwarp." msgstr "" -msgid "§cYou do not have permission to change your biome to that type." +msgid "§e§lClick here to deactivate." msgstr "" -#, java-format -msgid "" -"§7The pixies are busy changing the biome near you to §9{0}§7, be patient." +msgid "§c§lYou can't change the warp." msgstr "" -#, java-format msgid "" -"§7The pixies are busy changing the biome in your current chunk to §9{0}§7, " -"be patient." -msgstr "" - -#, java-format -msgid "§eInvalid biome {0} supplied!" +"§eWarp Status: §8Inactive\n" +"§fOther players can't warp to your\n" +"§fisland. Set a warp point using\n" +"§d/island setwarp §fbefore activating." msgstr "" -#, java-format -msgid "§aYou have changed your island''s biome to {0}" +msgid "§e§lClick here to activate." msgstr "" -#, java-format -msgid "{0} changed the island biome to {1}" +msgid "§a§lIsland Log" msgstr "" -#, java-format -msgid "§aYou have changed {0} blocks around you to the {1} biome" +msgid "" +"View a log of events from\n" +"your island such as member,\n" +"biome, and warp changes.\n" +"§e§lClick to view the log." msgstr "" -#, java-format -msgid "{0} created an area with {1} biome" +msgid "§a§lChange Home Location" msgstr "" -msgid "set your island''s warp location" +msgid "" +"When you teleport to your\n" +"island you will be taken to\n" +"this location.\n" +"§e§lClick here to change." msgstr "" -msgid "§cYou do not have permission to set your island''s warp point!" +msgid "§a§lChange Warp Location" msgstr "" -msgid "§cYou need to be on your own island to set the warp!" +msgid "" +"When your warp is activated,\n" +"other players will be taken to\n" +"this point when they teleport\n" +"to your island." msgstr "" -#, java-format -msgid "§b{0}§d changed the island warp location." +msgid "§e§lClick here to change." msgstr "" -msgid "teleports you to your island (or create one)" +msgid "§c§lRestart Island" msgstr "" -msgid "ban/unban a player from your island." +msgid "" +"Restarts your island.\n" +"§4WARNING! §cwill remove your items and island!" msgstr "" -msgid "exempts user from being banned" +msgid "§c§lLeave Island" msgstr "" -msgid "§eThe following players are banned from warping to your island:" +msgid "" +"Leaves your island.\n" +"§4WARNING! §cwill remove all your items!" msgstr "" -msgid "§eTo ban/unban from your island, use /island ban " +msgid "§cClick to leave" msgstr "" -msgid "§4You can't ban members. Remove them first!" +msgid "Island Restart Menu" msgstr "" -msgid "§4You do not have permission to kick/ban players." +msgid "§eYou do not have access to that island-schematic!" msgstr "" #, java-format -msgid "§eUnable to ban unknown player {0}" +msgid "§cClick within §9{0}§c to leave!" msgstr "" -#, java-format -msgid "§4{0} tried to ban you from their island!" +msgid "§a§lReturn to the main menu" msgstr "" #, java-format -msgid "§4{0} is exempt from being banned." +msgid "§cClick within §9{0}§c to restart!" msgstr "" -#, java-format -msgid "§eYou have banned §4{0}§e from warping to your island." +msgid "§aClick to restart!" msgstr "" -#, java-format -msgid "§eYou have been §cBANNED§e from {0}§e''s island." +msgid "Caps On" msgstr "" -#, java-format -msgid "§eYou have unbanned §a{0}§e from warping to your island." +msgid "§9Text Editor" msgstr "" #, java-format -msgid "§eYou have been §aUNBANNED§e from {0}§e''s island." +msgid "Too many requests for Mojangs API ({0} within {1}), sleeping {2}" msgstr "" -msgid "display log" +msgid "§9Hold your horses! You have to be patient..." msgstr "" -msgid "changes a members island-permissions" +msgid "§9Not really patient, are you?" msgstr "" -#, java-format -msgid "§ePermissions for §9{0}§e:" +msgid "§9Be patient, young padawan" msgstr "" -msgid "§aON" +msgid "§9Patience you MUST have, young padawan" msgstr "" -msgid "§cOFF" +msgid "§9The two most powerful warriors are patience and time." msgstr "" -#, java-format -msgid "§7 - §6{0}§7 : {1}" +msgid "§eSending you to spawn." msgstr "" -#, java-format -msgid "§cInvalid permission {0}. Must be one of {1}" +msgid "§eThe island has been §cLOCKED§e." msgstr "" -#, java-format -msgid "§eToggled permission §9{0}§e for §9{1}§e to {2}" +msgid "§4Unable to find a safe home-location on your island!" msgstr "" -#, java-format -msgid "§eUnable to toggle permission §9{0}§e for §9{1}" +msgid "§cWARNING: §eTeleporting you to mid-air." msgstr "" -msgid "set the island-home" +msgid "§aTeleporting you to your island." msgstr "" -msgid "show the islands limits" +#, java-format +msgid "§aYou will be teleported in {0} seconds." msgstr "" -msgid "enable/disable warping to your island." +msgid "§4Unable to warp you to that player''s island!" msgstr "" -msgid "§4Your island is locked. You must unlock it before enabling your warp." +#, java-format +msgid "§aTeleporting you to {0}''s island." msgstr "" -#, java-format -msgid "§b{0}§d activated the island warp." +msgid "§7Teleport cancelled" msgstr "" -msgid "§cYou do not have permission to enable/disable your island''s warp!" +msgid "READY" msgstr "" -msgid "try to complete a challenge" +msgid "" +"§cWARNING:§e Could not transfer all the required items to your inventory!" msgstr "" -msgid "show information about the challenge" +msgid "§cNot enough items in chest to complete challenge!" msgstr "" -msgid "§eRank: " +msgid "Something went wrong saving the island and/or party data!" msgstr "" -msgid "§4This Challenge is not repeatable!" +msgid "Converting data to UUID, this make take a while!" msgstr "" -msgid "§4You will lose all required items when you complete this challenge!" +msgid "§cMAINTENANCE:§e uSkyBlock is currently in maintenance mode" msgstr "" #, java-format msgid "" -"§4All required items must be placed on your island, within {0} blocks of you." +"§buSkyBlock§e depends on §9{0}§e >= §av{1}§e but only §cv{2}§e was found!\n" msgstr "" #, java-format -msgid "§eTo complete this challenge, use §f/c c {0}" -msgstr "" - -msgid "§4Invalid challenge name! Use /c help for more information" -msgstr "" - -msgid "§eSending you to spawn." +msgid "§buSkyBlock§e depends on §9{0}§e >= §av{1}" msgstr "" -msgid "§eThe island has been §cLOCKED§e." +msgid "§4Player is already assigned to this island!" msgstr "" -msgid "§9Hold your horses! You have to be patient..." +msgid "§cYour island is in the process of generating, you cannot create now." msgstr "" -msgid "§9Not really patient, are you?" +msgid "Could not create your Island. Please contact a server moderator." msgstr "" -msgid "§9Be patient, young padawan" +msgid "§eGetting your island ready, please be patient, it can take a while." msgstr "" -msgid "§9Patience you MUST have, young padawan" +msgid "§cCommand is currently disabled!" msgstr "" -msgid "§9The two most powerful warriors are patience and time." +msgid "North" msgstr "" -msgid "§4Unable to find a safe home-location on your island!" +msgid "North-East" msgstr "" -msgid "§cWARNING: §eTeleporting you to mid-air." +msgid "East" msgstr "" -msgid "§aTeleporting you to your island." +msgid "South-East" msgstr "" -#, java-format -msgid "§aYou will be teleported in {0} seconds." +msgid "South" msgstr "" -msgid "§4Unable to warp you to that player''s island!" +msgid "South-West" msgstr "" -#, java-format -msgid "§aTeleporting you to {0}''s island." +msgid "West" msgstr "" -msgid "§7Teleport cancelled" +msgid "North-West" msgstr "" diff --git a/uSkyBlock-Core/src/main/po/zh_CN.po b/uSkyBlock-Core/src/main/po/zh_CN.po index 241c0c894..4c8ba92de 100644 --- a/uSkyBlock-Core/src/main/po/zh_CN.po +++ b/uSkyBlock-Core/src/main/po/zh_CN.po @@ -6,325 +6,377 @@ msgid "" msgstr "" "Project-Id-Version: v2.3-HF3\n" "Report-Msgid-Bugs-To: \n" -"PO-Revision-Date: 2015-04-27 13:13+0100\n" +"POT-Creation-Date: \n" +"PO-Revision-Date: 2025-04-25 21:25+0800\n" "Last-Translator: Miku_Snow \n" "Language-Team: 915mc \n" "Language: zh_CN\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Generator: Poedit 1.7.6\n" +"X-Generator: Poedit 3.6\n" -msgid "d" -msgstr "天" - -msgid "h" -msgstr "小时" +#, java-format +msgid "§eYou do not have access (§4{0}§e)" +msgstr "§e您无权访问(§4{0}§e)" -msgid "m" -msgstr "分" +#, java-format +msgid "§eInvalid command: {0}" +msgstr "§e无效命令: {0}" -msgid "s" -msgstr "秒" +msgid "changes the language of the plugin, and reloads" +msgstr "更改插件的语言,并重新加载" #, java-format -msgid "{0,number,0}:{1,number,00}.{2,number,000}" -msgstr "" +msgid "§aSuccessfully changed language to §e{0}" +msgstr "§a已成功将语言更改为§e{0}" #, java-format -msgid "§f{0}x §7{1}" -msgstr "" +msgid "§cFailed to change language to §e{0}" +msgstr "§c无法将语言更改为§e{0}" -#, java-format -msgid "§7{0}" -msgstr "" +msgid "§9Supported Languages:\n" +msgstr "§9支持的语言:\n" #, java-format -msgid "§eYou do not have access (§4{0}§e)" -msgstr "" +msgid "§f{0} §7{1} §9 by {2} §7{3}\n" +msgstr "§f{0}§7{1}§9由{2}§7{3}\n" -#, java-format -msgid "§eInvalid command: {0}" -msgstr "" +msgid "§cUnable to locate any languages." +msgstr "§c找不到任何语言。" msgid "Command" -msgstr "" +msgstr "命令" msgid "Permission" -msgstr "" +msgstr "许可" msgid "Description" -msgstr "" +msgstr "描述" + +msgid "saves documentation of the commands to a file" +msgstr "将命令文档保存到文件中" + +#, java-format +msgid "Wrote documentation to {0}" +msgstr "为 {0} 编写文档" + +#, java-format +msgid "§4Error writing documentation: {0}" +msgstr "§4编写文档时出错: {0} " #, java-format msgid "§7Usage: {0}" -msgstr "" +msgstr "§7用法: {0} " #, java-format msgid "§7Use §3/{0} ? {1}§7 to display next page\n" -msgstr "" +msgstr "§7使用§3/{0}?{1} §7显示下一页\n" #, java-format msgid "§7Use §3/{0} ? {1} §7 to display previous page\n" -msgstr "" +msgstr "§7使用§3/{0}?{1} §7显示上一页\n" -msgid "saves documentation of the commands to a file" -msgstr "" +msgid "d" +msgstr "天" -#, java-format -msgid "Wrote documentation to {0}" -msgstr "" +msgid "h" +msgstr "小时" -#, java-format -msgid "§4Error writing documentation: {0}" -msgstr "" +msgid "m" +msgstr "分" -msgid "§cWither Despawned!§e It wandered too far from your island." -msgstr "" +msgid "s" +msgstr "秒" #, java-format -msgid "{0}''s Wither" -msgstr "" +msgid "{0,number,0}:{1,number,00}.{2,number,000}" +msgstr "{0,number,0}:{1,number,00}.{2,number,000}" -msgid "§eYou can not use another island''s portals!" -msgstr "" +#, java-format +msgid "§f{0}x §7{1}" +msgstr "§f{0}x §7{1}" -msgid "§eVillager-trading isn't allowed." -msgstr "" +#, java-format +msgid "§7{0}" +msgstr "§7{0}" -msgid "§eTrading isn't allowed on other islands. Do it in spawn." -msgstr "" +#, java-format +msgid "§4You can complete this {0} more time(s)." +msgstr "§4您可以再完成 {0} 次。" -msgid "§eRiding is only allowed on your own island!" -msgstr "" +#, java-format +msgid "§4Requirements will reset in {0} days." +msgstr "§4需求将在 {0} 天后重置." -msgid "§eYou cannot break vehicles while being a visitor!" -msgstr "" +#, java-format +msgid "§4Requirements will reset in {0} hours." +msgstr "§4任务将在{0} 小时后重置" -msgid "§4You can only convert obsidian once every 10 seconds" -msgstr "" +#, java-format +msgid "§4Requirements will reset in {0} minutes." +msgstr "§4任务将在{0} 分钟后重置" -msgid "§eChanging your obsidian back into lava. Be careful!" -msgstr "§e已经将你的黑曜石变为岩浆.请务必当心!" +msgid "§4This challenge is currently unavailable." +msgstr "§4此挑战目前不可用。" -msgid "§eYour inventory must have another empty space!" -msgstr "" +#, java-format +msgid "§4You can complete this again in {0} days." +msgstr "§4您可以在 {0} 天后再次完成此操作。" -msgid "§4It''s a bad idea to replace your lava!" -msgstr "§4替换岩浆真是一个§f'不错'§4的注意呢~" +#, java-format +msgid "§4You can complete this again in {0} hours." +msgstr "§4您可以在 {0} 小时内再次完成此操作。" -msgid "§eYou cannot hurt island-members." -msgstr "" +#, java-format +msgid "§4You can complete this again in {0} minutes." +msgstr "§4您可以在 {0} 分钟内再次完成此操作。" -msgid "§4That player has forbidden you from teleporting to their island." -msgstr "§4玩家禁止你传送到他们的岛屿上." +msgid "§eThis challenge requires:" +msgstr "§e此挑战需要:" -msgid "§4That island is §clocked.§e No teleporting to the island." -msgstr "" +msgid "§7and more..." +msgstr "§7以及更多..." + +msgid "§eItems will be traded for reward." +msgstr "§e物品将被交易以获得奖励。" #, java-format -msgid "" -"§4{0} is limited. §eScanning your island to see if you are allowed to place " -"more, please be patient" -msgstr "" +msgid "§eMust be within {0} meters." +msgstr "§e必须在 {0} 米以内。" -msgid "§e... Scanning complete, you can try again" -msgstr "" +msgid "§6Item Reward: §a" +msgstr "§6物品奖励:" #, java-format -msgid "" -"§4You''ve hit the {0} limit!§e You can''t have more of that type on your " -"island!§9 Max: {1,number}" -msgstr "" - -msgid "§eYou can only use spawn-eggs on your own island." -msgstr "" +msgid "§6Currency Reward: §a{0}" +msgstr "§6货币奖励: §a{0}" -msgid "§cYou have reached your spawn-limit for your island." -msgstr "" +#, java-format +msgid "§6Exp Reward: §a{0}" +msgstr "§6经验奖励: §a{0}" -msgid "§eVisitors can't drop items!" -msgstr "" +#, java-format +msgid "§dTotal times completed: §f{0}" +msgstr "§d总完成次数: §f{0}" #, java-format -msgid "Owner: {0}" -msgstr "" +msgid "§7Requires {0}" +msgstr "§7需要 {0} " -msgid "You cannot pick up other players' loot when you are a visitor!" -msgstr "" +#, java-format +msgid "§7Complete {0} more {1} §7challenges" +msgstr "§7完成 {0} 更多 {1} §7挑战" -msgid "§cBanned:§e You are banned from this island." -msgstr "" +#, java-format +msgid "§7Complete {0}" +msgstr "§7完成 {0} " -msgid "§cLocked:§e That island is locked! No entry allowed." -msgstr "" +msgid "to unlock this rank" +msgstr "解锁此排名" -msgid "Something went wrong saving the island and/or party data!" -msgstr "在存储岛屿或者团队记录的时候发生了一些错误!" +#, java-format +msgid "§4No challenge named {0} found" +msgstr "§4未找到名为 {0} 的挑战" -msgid "Converting data to UUID, this make take a while!" -msgstr "" +msgid "§4You must be on your island to do that!" +msgstr "§4你必须在你的岛屿上才能这样做!" -msgid "§cMAINTENANCE:§e uSkyBlock is currently in maintenance mode" -msgstr "" +#, java-format +msgid "§4The {0} challenge is not available yet!" +msgstr "§4 {0} 挑战尚不可用!" #, java-format -msgid "" -"§buSkyBlock§e depends on §9{0}§e >= §av{1}§e but only §cv{2}§e was found!\n" -msgstr "" +msgid "§4The {0} challenge is not repeatable!" +msgstr "§4挑战:§e{0} §4不可重复完成!" #, java-format -msgid "§buSkyBlock§e depends on §9{0}§e >= §av{1}" -msgstr "" +msgid "§4You cannot complete the {0} challenge again yet!" +msgstr "§4您还不能再次完成 {0} 挑战!" -msgid "§eYou do not have access to that island-schematic!" -msgstr "" +#, java-format +msgid "§eTrying to complete challenge §a{0}" +msgstr "§e尝试完成挑战§a {0} " -msgid "§4Player is already assigned to this island!" -msgstr "§4玩家早就已经分配给这个岛屿了!" +#, java-format +msgid "§4{0}" +msgstr "§4{0}" -msgid "§4You must be closer to your island to set your skyblock home!" -msgstr "§4你必须在岛屿上才能设置你的家!" +#, java-format +msgid "§4You must be standing within {0} blocks of all required items." +msgstr "§4在你周围 {0} 格以内必须包含挑战所需物品。" -msgid "§aYour skyblock home has been set to your current location." -msgstr "§a你岛屿家的位置已经设置在你当前位置." +#, java-format +msgid "§4Your island must be level {0} to complete this challenge!" +msgstr "§4您的岛屿必须达到 {0} 级,才能完成当前挑战!" -msgid "§4Your current location is not a safe home-location." -msgstr "" +#, java-format +msgid "§4Unknown type of challenge: {0}" +msgstr "§4未知难度的挑战: {0}" #, java-format msgid "" -"§7The pixies are busy changing the biome of your island to §9{0}§7, be " -"patient." -msgstr "" - -msgid "§cYour island is in the process of generating, you cannot create now." +"§eStill the following entities short:\n" +"{0}" msgstr "" +"§e你缺少以下物品完成此任务:\n" +"{0}" -msgid "Could not create your Island. Please contact a server moderator." -msgstr "无法为你创建岛屿.请联系管理员." +#, java-format +msgid " §4{0} §b{1}" +msgstr "§4{0}§b{1}" -msgid "§eGetting your island ready, please be patient, it can take a while." -msgstr "" +#, java-format +msgid "§eYou are the following items short:{0}" +msgstr "§e你缺少以下物品: {0}" -msgid "§cCommand is currently disabled!" -msgstr "" +#, java-format +msgid "§aYou have completed the {0} challenge!" +msgstr "§a你完成了挑战:§e {0}" #, java-format -msgid " §f{0}x §7{1}" -msgstr "" +msgid "§9{0}§f has completed the §9{1}§f challenge!" +msgstr "§9{0}§f已完成§9{1}§f挑战!" #, java-format -msgid "§eStill the following blocks short: {0}" -msgstr "§e任然缺少以下方块: {0}" +msgid "§eItem reward(s): §f{0}" +msgstr "§e物品奖励: §f{0}" #, java-format -msgid "§9{0}§7 timed out" -msgstr "§9{0}§7超时" +msgid "§eExp reward: §f{0,number,#.#}" +msgstr "§e经验奖励: §f{0,number,#.#}" #, java-format -msgid "" -"§eDoing §9{0}§e is §cRISKY§e. Repeat the command within §a{1}§e seconds to " -"accept!" -msgstr "§9{0}§e是§c危险的§e.请等待§a{1}§e秒后在这样做." +msgid "§eCurrency reward: §f{0,number,###.##} {1} §a ({2,number,##.##})%" +msgstr "§e货币奖励: §f{0,number,###.##} {1} §a ({2,number,##.##})%" -msgid "§4** You are entering a protected - but abandoned - island area." -msgstr "" +msgid "§eYour inventory is §4full§e. Items dropped on the ground." +msgstr "§e背包§4已满§e 多出的奖励物品将掉落到地上" -#, java-format -msgid "§d** You are entering §b{0}''s §disland." -msgstr "" +msgid "§e§lClick to complete this challenge." +msgstr "§e§l点击来完成这个挑战。" -msgid "§4** You are leaving an abandoned island." -msgstr "" +msgid "§4§lYou can't repeat this challenge." +msgstr "§4§l你无法重复完成这个挑战。" #, java-format -msgid "§d** You are leaving §b{0}''s §disland." -msgstr "" +msgid "Rank: {0}" +msgstr "排名: {0} " -msgid "§eYour island is now locked. Only your party members may enter." -msgstr "§e你的岛屿当前处于锁定状态.只有团队成员才能进入." +msgid "§fComplete most challenges in" +msgstr "§f完成大部分挑战在" -msgid "§4You must be the party leader to lock your island!" -msgstr "§4你必须为岛屿主人才能锁定岛屿!" +msgid "§fthis rank to unlock the next rank." +msgstr "§f这个难度中,才能解锁下一个等级。" -msgid "" -"§eYour island is unlocked and anyone may enter, however only you and your " -"party members may build or remove blocks." -msgstr "" -"§e你岛屿当前处于未锁定状态,s所有人都可以访问你的岛屿不过只有你和岛屿成员才能" -"进行建造." +msgid "§eClick here to show previous page" +msgstr "§e单击此处显示上一页" -msgid "§4You must be the party leader to unlock your island!" -msgstr "§4你必须得是岛屿的主人才能解锁你的岛屿!" +msgid "§eClick here to show next page" +msgstr "§e单击此处显示下一页" -#, java-format -msgid "§eWaiting for our turn §c{0,number,###}%" -msgstr "" +msgid "§4§lLocked Challenge" +msgstr "§4§l挑战锁定" + +msgid "Return" +msgstr "返回" + +msgid "Caps On" +msgstr "大写锁定已开启" #, java-format -msgid "§9Creating island...§e{0,number,###}%" -msgstr "" +msgid "§7Page {0}" +msgstr "§7第 {0} 页" -msgid "N/A" -msgstr "" +msgid "Config:" +msgstr "配置:" -msgid "§4§lLocked Challenge" -msgstr "§4§l挑战锁定" +msgid "§9Text Editor" +msgstr "§9文本编辑器" -msgid "READY" -msgstr "" +msgid "§eConfiguration saved and reloaded." +msgstr "§e配置已保存并重新加载。" -#, java-format -msgid "§4The {0} challenge is not available yet!" -msgstr "" +msgid "§cError! §9Unable to save config file!" +msgstr "§c错误!§9无法保存配置文件!" + +msgid "§3First Page" +msgstr "§3第一页" + +msgid "§3Last Page" +msgstr "§3最后一页" + +msgid "§cSave & Reload config" +msgstr "§c保存并重新加载配置" msgid "" -"§cWARNING:§e Could not transfer all the required items to your inventory!" +"§7Saves the settings to\n" +"§7file & reloads again.\n" +"§cNote: §7Use with care!" msgstr "" +"§7将设置保存到\n" +"§7文件并再次重新加载。\n" +"§c注意:§7请谨慎使用!" -msgid "§cNot enough items in chest to complete challenge!" -msgstr "" +msgid "§7 (readonly)" +msgstr "§7(只读)" -msgid "true" -msgstr "" +#, java-format +msgid "§c{0,number,#}" +msgstr "§c{0,number,#}" -msgid "false" -msgstr "" +#, java-format +msgid "§a+{0,number,#}" +msgstr "§a+{0,number,#}" + +#, java-format +msgid "§a{0,number,#}" +msgstr "§a{0,number,#}" + +#, java-format +msgid "&aLeft:&7 Increment with {0}" +msgstr "&aLeft:使用 {0} 递增7" + +#, java-format +msgid "&cRight-Click:&7 Set to {0}" +msgstr "&c右键单击:&7设置为 {0} " + +msgid "§9Integer Editor" +msgstr "§9整数编辑器" msgid "Challenge Menu" -msgstr "" +msgstr "挑战菜单" msgid "Change Biome" -msgstr "" +msgstr "更改生物群系" msgid "change the island''s biome." -msgstr "" +msgstr "改变岛上的生物群系" msgid "Toggle Island Lock" -msgstr "" +msgstr "岛屿锁" msgid "toggle the island''s lock." -msgstr "" +msgstr "锁定/解锁岛屿" msgid "Set Island Warp" -msgstr "" +msgstr "设置岛屿传送点" msgid "set the island''s warp." -msgstr "" +msgstr "设置岛屿传送点" msgid "" "set the island''s warp,\n" "which allows non-group\n" "members to teleport to\n" "the island." -msgstr "" +msgstr "设置岛屿传送点,岛屿未锁定时,非岛屿成员可以传送至此" msgid "Toggle Island Warp" -msgstr "" +msgstr "切换岛屿传送" msgid "toggle the island''s warp." -msgstr "" +msgstr "打开/关闭岛屿传送功能" msgid "" "toggle the island''s warp,\n" @@ -332,203 +384,54 @@ msgid "" "on or off at anytime, but\n" "not set the location." msgstr "" +"切换岛屿的传送功能,\n" +"可以随时开启或关闭,\n" +"不是设置传送点位置。" msgid "Invite Players" -msgstr "" +msgstr "邀请玩家" msgid "invite others to the island." -msgstr "" +msgstr "邀请其他人到岛上。" msgid "" "invite\n" "other players to the island if\n" "there is enough room for more\n" "members" -msgstr "" +msgstr "邀请" msgid "Kick Players" -msgstr "" +msgstr "踢出玩家" msgid "kick others from the island." -msgstr "" +msgstr "把其他人踢出岛屿" msgid "" "kick\n" "other players from the island,\n" "but they are unable to kick\n" "the island leader." -msgstr "" - -msgid "Ocean" -msgstr "" - -msgid "" -"The ocean biome is the basic\n" -"starting biome for all islands.\n" -"passive mobs like animals will\n" -"not spawn. Hostile mobs will\n" -"spawn normally." -msgstr "" - -msgid "Forest" -msgstr "" - -msgid "" -"The forest biome will allow\n" -"your island to spawn passive.\n" -"mobs like animals (including\n" -"wolves). Hostile mobs will\n" -"spawn normally." -msgstr "" - -msgid "Desert" -msgstr "" - -msgid "" -"The desert biome makes it so\n" -"that there is no rain or snow\n" -"on your island. Passive mobs\n" -"won't spawn. Hostile mobs will\n" -"spawn normally." -msgstr "" - -msgid "Jungle" -msgstr "" - -msgid "" -"The jungle biome is bright\n" -"and colorful. Passive mobs\n" -"(including ocelots) will\n" -"spawn. Hostile mobs will\n" -"spawn normally." -msgstr "" - -msgid "Swampland" -msgstr "" - -msgid "" -"The swamp biome is dark\n" -"and dull. Passive mobs\n" -"will spawn normally and\n" -"slimes have a small chance\n" -"to spawn at night depending\n" -"on the moon phase." -msgstr "" - -msgid "Taiga" -msgstr "" - -msgid "" -"The taiga biome has snow\n" -"instead of rain. Passive\n" -"mobs will spawn normally\n" -"(including wolves) and\n" -"hostile mobs will spawn." -msgstr "" - -msgid "Mushroom" -msgstr "" - -msgid "" -"The mushroom biome is\n" -"bright and colorful.\n" -"Mooshrooms are the only\n" -"mobs that will spawn.\n" -"No other passive or\n" -"hostile mobs will spawn." -msgstr "" - -msgid "Hell" -msgstr "" - -msgid "" -"The hell biome looks\n" -"dark and dead. Some\n" -"mobs from the nether will\n" -"spawn in this biome\n" -"(excluding ghasts and\n" -"blazes)." -msgstr "" - -msgid "Sky" -msgstr "" - -msgid "" -"The sky biome gives your\n" -"island a special dark sky.\n" -"Only endermen will spawn\n" -"in this biome." -msgstr "" - -msgid "Plains" -msgstr "" - -msgid "" -"The plains biome has rain\n" -"instead of snow. Passive\n" -"mobs will spawn normally\n" -"(including horses) and\n" -"hostile mobs will spawn." -msgstr "" - -msgid "Extreme Hills" -msgstr "" - -msgid "" -"The extreme hills biome.\n" -"Passive mobs will spawn \n" -"normally and hostile\n" -"mobs will spawn." -msgstr "" - -msgid "Flower Forest" -msgstr "" - -msgid "" -"The flower forest biome.\n" -"Passive mobs will spawn \n" -"normally and hostile\n" -"mobs will spawn." -msgstr "" - -msgid "Deep Ocean" -msgstr "" - -msgid "" -"The deep-ocean biome is an advanced\n" -"biome. Passive mobs like animals will\n" -"not spawn. Hostile mobs \n" -"(including Guardians) will\n" -"spawn normally." -msgstr "" - -msgid "Ice Plains" -msgstr "" - -msgid "" -"The ice-plains biome is an advanced biome.\n" -"Mobs will spawn naturally.\n" -"including polar-bears" -msgstr "" +msgstr "将其他玩家从岛上踢出,但他们无法踢出岛主。" msgid "Permissions" -msgstr "" +msgstr "权限" #, java-format msgid "{0} <{1}>" -msgstr "" +msgstr "{0} <{1}>" msgid "§9Player Permissions" -msgstr "" +msgstr "§9玩家权限" msgid "" "§eClick here to return to\n" "§eyour island group''s info." -msgstr "" +msgstr "§e单击此处返回" #, java-format msgid "§e{0}''§9s Permissions" -msgstr "" +msgstr "§e {0} “”§9s权限" msgid "" "§eHover over an icon to view\n" @@ -540,23 +443,23 @@ msgstr "" "§e即可更改权限状态." msgid "§fThis player §acan" -msgstr "" +msgstr "§f此玩家§acan" msgid "Click here to remove this permission." -msgstr "" +msgstr "单击此处删除此权限。" msgid "§fThis player §ccannot" -msgstr "" +msgstr "§f这个玩家§ccannot" msgid "Click here to grant this permission." -msgstr "" +msgstr "单击此处授予此权限。" msgid "Island Group Members" -msgstr "" +msgstr "岛屿成员列表" #, java-format msgid "Group Members: §2{0}§7/§e{1}" -msgstr "" +msgstr "岛屿成员:§2{0}§7/§e{1}" msgid "§aMore players can be invited to this island." msgstr "§a可以邀请更多的玩家加入岛屿." @@ -570,30 +473,32 @@ msgid "" "§eleader can change permissions\n" "§eby clicking a player''s icon." msgstr "" +"将鼠标悬停在玩家的图标上以查看其权限。\n" +"岛主可以通过点击玩家的图标来更改权限。" #, java-format msgid "§e{0}''s§9 Permissions" -msgstr "" +msgstr "§e {0} 的§9权限" msgid "Leader" -msgstr "" +msgstr "岛主" msgid "Member" -msgstr "" +msgstr "成员" #, java-format msgid "Can {0}" -msgstr "" +msgstr "可以 {0} " #, java-format msgid "Cannot {0}" -msgstr "" +msgstr "无法 {0} " msgid "§e" msgstr "§e<点击将更改玩家的权限>" msgid "Island Log" -msgstr "" +msgstr "岛屿日志" msgid "" "§eClick here to return to\n" @@ -605,63 +510,17 @@ msgstr "" msgid "§e§lIsland Log" msgstr "§e§l岛屿日志" -msgid "Island Biome" -msgstr "岛屿生物群系" - -#, java-format -msgid "Biome: {0}" -msgstr "" - -msgid "§2§lThis is your current biome." -msgstr "§2§l这是你当前的生物群系." - -msgid "§e§lClick to change to this biome." -msgstr "§e§l点击将会更改为这个生物群系." - -msgid "You cannot use this biome." -msgstr "" - -msgid "§2chunk" -msgstr "" - -msgid "§call" -msgstr "" - -#, java-format -msgid "§e{0}" -msgstr "" - -msgid "§c-" -msgstr "" - -msgid "Decrease radius of biome-change" -msgstr "" - -#, java-format -msgid "Current radius: {0}" -msgstr "" - -msgid "§2+" -msgstr "" - -msgid "Increase radius of biome-change" -msgstr "" - msgid "§7Current page" -msgstr "" - -#, java-format -msgid "§7Page {0}" -msgstr "" +msgstr "§7当前页面" msgid "§7First Page" -msgstr "" +msgstr "§7第一页" msgid "§7Last Page" -msgstr "" +msgstr "§7最后一页" msgid "Island Create Menu" -msgstr "" +msgstr "岛屿创建菜单" msgid "§a§lStart an Island" msgstr "§a§l开始岛屿生涯" @@ -676,21 +535,30 @@ msgid "" "building your island empire!\n" "§e§lClick here to start!" msgstr "" +"开启你的空岛之旅,\n" +"从创建自己的岛屿开始。\n" +"完成挑战以赚取物品和空岛币,\n" +"助力扩展你的空岛。\n" +"你可以邀请他人加入,\n" +"共同打造你的岛屿帝国!\n" +"§e§l点击此处开始!" msgid "§aClick to create!" -msgstr "" +msgstr "§a单击创建!" #, java-format msgid "" "§cNo access!\n" "§7({0})" msgstr "" +"§c禁止进入!\n" +"§7({0})" msgid "§a§lReturn to Spawn" -msgstr "" +msgstr "§a§l返回大厅" msgid "Teleport to the spawn area." -msgstr "" +msgstr "传送到大厅" msgid "§a§lJoin an Island" msgstr "§a§l加入一个岛屿" @@ -705,9 +573,14 @@ msgid "" "§e§lClick here to accept an invite!\n" "§e§l(You must be invited first)" msgstr "" +"想要加入另一个玩家的岛屿而不是自己创建一个吗?\n" +"如果另一个玩家邀请您去他们的岛屿,\n" +"您可以点击此处或使用§e/island accept§f 来加入他们。\n" +"§e§l点击此处接受邀请!\n" +"§e§l(您必须先被邀请)" msgid "Island Menu" -msgstr "" +msgstr "岛屿菜单" msgid "§a§lReturn Home" msgstr "§a§l返回你的岛屿" @@ -719,6 +592,10 @@ msgid "" "island using §b/island sethome\n" "§e§lClick here to return home." msgstr "" +"返回您的岛屿出生点。\n" +"您可以使用§b/island sethome\n" +"将您的家园点更改为您岛屿上的任何位置\n" +"§e§l点击此处返回岛屿。" msgid "§a§lChallenges" msgstr "§a§l挑战" @@ -729,12 +606,14 @@ msgid "" "to earn skybucks, items, perks,\n" "and titles." msgstr "" +"查看§9任务列表\n" +"完成任务以赚取天空币、物品、经验值和称号" msgid "§e§lClick here to view challenges." -msgstr "§e§l点击图标查询所有挑战." +msgstr "§e点击图标打开任务列表" msgid "§4§lChallenges disabled." -msgstr "§4§l挑战不可用" +msgstr "§4§l任务已锁定" msgid "§a§lIsland Level" msgstr "§a§l岛屿等级" @@ -751,13 +630,17 @@ msgid "" "§e§lClick here to refresh.\n" "§e§l(must be on island)" msgstr "" +"通过扩展您的空岛并完成某些挑战来提升岛屿等级。\n" +"更稀有的方块会为您的等级增加更多分数。\n" +"§e§l点击此处刷新。\n" +"§e§l(你必须在岛上)" msgid "Island Group" -msgstr "" +msgstr "岛屿队伍" #, java-format msgid "§eMembers: §2{0}/{1}" -msgstr "" +msgstr "§e成员:§2{0}/{1}" msgid "" "View the members of your island\n" @@ -766,25 +649,30 @@ msgid "" "can change the member permissions.\n" "§e§lClick here to view or change." msgstr "" +"查看您的岛屿队伍的成员及其权限。\n" +"如果您是岛主,您可以更改成员权限。\n" +"§e点击此处查看或更改。" msgid "Change Island Biome" -msgstr "" +msgstr "更改岛屿生物群落" #, java-format msgid "§eCurrent Biome: §b{0}" -msgstr "" +msgstr "§e当前生物群系:§b {0} " msgid "" "The island biome affects things\n" "like grass color and spawning\n" "of both animals and monsters." msgstr "" +"该岛屿生物群系会影响诸如草的颜色\n" +"以及动物和怪物的生成等事物。" msgid "§e§lClick here to change biomes." -msgstr "§e§l点击图标更改生物群系." +msgstr "§e点击图标更改生物群系." msgid "§c§lYou can't change the biome." -msgstr "§c§l你没有权限更改生物群系." +msgstr "§c你没有权限更改生物群系." msgid "§a§lIsland Lock" msgstr "§a§l岛屿锁定" @@ -800,7 +688,7 @@ msgstr "" "§f非岛屿成员将无法访问你的岛屿." msgid "§e§lClick here to unlock your island." -msgstr "§e§l点击这里将可以解锁你的岛屿." +msgstr "§e§l点击这里解锁你的岛屿." msgid "§c§lYou can't change the lock." msgstr "§c§l你无法更改锁定状态." @@ -861,6 +749,9 @@ msgid "" "biome, and warp changes.\n" "§e§lClick to view the log." msgstr "" +"查看您岛屿上的事件日志,\n" +"例如成员、生物群系和传送点的变化。\n" +"§e§l点击以查看日志。" msgid "§a§lChange Home Location" msgstr "§a§l更改岛屿家" @@ -871,6 +762,8 @@ msgid "" "this location.\n" "§e§lClick here to change." msgstr "" +"当你传送到你的岛屿时,你将被传送到这个位置。\n" +"§e§l点击此处进行更改。" msgid "§a§lChange Warp Location" msgstr "§a§l更改传送点" @@ -880,684 +773,674 @@ msgid "" "other players will be taken to\n" "this point when they teleport\n" "to your island." -msgstr "" +msgstr "当你启用岛屿传送时,其他玩家在传送到你的岛屿时将被带到此处。" msgid "§e§lClick here to change." -msgstr "" +msgstr "§e§l单击此处进行更改。" msgid "§c§lRestart Island" -msgstr "" +msgstr "§c重置岛屿" msgid "" "Restarts your island.\n" "§4WARNING! §cwill remove your items and island!" msgstr "" +"重置您的岛屿\n" +"§4 警告!§c 将移除您的所有物品和岛屿!" msgid "§c§lLeave Island" -msgstr "" +msgstr "退出岛屿" msgid "" "Leaves your island.\n" "§4WARNING! §cwill remove all your items!" msgstr "" +"离开你的岛屿。\n" +"§4 警告!§c 将移除您的所有物品" msgid "§cClick to leave" -msgstr "" +msgstr "§c点击退出" msgid "Island Restart Menu" -msgstr "" +msgstr "岛屿重启菜单" -msgid "Config:" -msgstr "" +msgid "§eYou do not have access to that island-schematic!" +msgstr "§e您无权访问该岛屿蓝图!" #, java-format msgid "§cClick within §9{0}§c to leave!" -msgstr "" +msgstr "§c点击§9 {0} §c离开!" msgid "§a§lReturn to the main menu" -msgstr "" +msgstr "§a§l返回主菜单" #, java-format msgid "§cClick within §9{0}§c to restart!" -msgstr "" +msgstr "§c单击§9 {0} §c重新启动!" msgid "§aClick to restart!" -msgstr "" +msgstr "§a单击重新启动!" + +msgid "true" +msgstr "启用" + +msgid "false" +msgstr "禁用" + +msgid "Either send a message directly to your group, or toggle it on/off." +msgstr "发送队内消息,或将其打开/关闭此功能" + +msgid "party" +msgstr "岛屿队伍" + +msgid "island" +msgstr "岛" #, java-format -msgid "§c{0,number,#}" -msgstr "" +msgid "§cToggled chat to {0} §aON" +msgstr "§c已将聊天切换到 {0} §aON" #, java-format -msgid "§a+{0,number,#}" -msgstr "" +msgid "§cRepeat §9{0}§c to toggle it off" +msgstr "§c重复§9{0}§c将其关闭" #, java-format -msgid "§a{0,number,#}" -msgstr "" - -#, java-format -msgid "&aLeft:&7 Increment with {0}" -msgstr "" - -#, java-format -msgid "&cRight-Click:&7 Set to {0}" -msgstr "" +msgid "§aToggled chat §cOFF§a for {0}" +msgstr "§a切换聊天§cOFF§a用于 {0} " -msgid "Return" -msgstr "" +msgid "§cCommand only available to players" +msgstr "§c命令仅对玩家可用" -msgid "§9Integer Editor" -msgstr "" +msgid "talk to your island party" +msgstr "与你的岛方交谈" -msgid "Caps On" -msgstr "" +msgid "But you are ALLLLLLL ALOOOOONE!" +msgstr "但你只有一个人~" -msgid "§9Text Editor" -msgstr "" +msgid "But you are Yelling in the wind!" +msgstr "但你只能对着空气自说自话~" -msgid "§eConfiguration saved and reloaded." -msgstr "" +msgid "But your fantasy friends are gone!" +msgstr "醒醒,不要幻想你有朋友了~" -msgid "§cError! §9Unable to save config file!" -msgstr "" +msgid "But you are Talking to your self!" +msgstr "你在自言自语啥呢,杂鱼~" -msgid "§3First Page" -msgstr "" +#, java-format +msgid "§cSorry! {0}" +msgstr "§c对不起!{0}" -msgid "§3Last Page" -msgstr "" +msgid "talk to players on your island" +msgstr "与岛上的玩家交谈" -msgid "§cSave & Reload config" -msgstr "" +msgid "READY" +msgstr "准备好了" msgid "" -"§7Saves the settings to\n" -"§7file & reloads again.\n" -"§cNote: §7Use with care!" -msgstr "" - -msgid "§7 (readonly)" -msgstr "" +"§cWARNING:§e Could not transfer all the required items to your inventory!" +msgstr "§c警告:§e无法将所有必需的物品转移到您的库存中!" -#, java-format -msgid "" -"§eProgress: {0,number,##}% ({1}/{2} - success:{3}, failed:{4}, skipped:{5}) " -"~ {6}" -msgstr "" +msgid "§cNot enough items in chest to complete challenge!" +msgstr "§c箱子里没有足够的物品来完成挑战!" -#, java-format -msgid "§4No importer named §e{0}§4 found" -msgstr "§4输入的名字§e{0}§4未找到" +msgid "Island Biome" +msgstr "岛屿生物群系" #, java-format -msgid "§eConverted {0}/{1} files in {2}" -msgstr "" +msgid "Biome: {0}" +msgstr "生物群系: {0} " -#, java-format -msgid "§7PlayerDB: Filtering {0} players from uuid2name.yml" -msgstr "" +msgid "§2§lThis is your current biome." +msgstr "§2§l这是你当前的生物群系." -#, java-format -msgid "§7 - {0,number,##}% ({1}/{2}) ~ {3}" -msgstr "" +msgid "§e§lClick to change to this biome." +msgstr "§e§l点击将会更改为这个生物群系." -#, java-format -msgid "§7PlayerDB: Filtered {0} names" -msgstr "" +msgid "§c-" +msgstr "§c-" -#, java-format -msgid "" -"§7 - MojangAPI:{4}: {0,number,##}% ({1}/{2}, failed:{3} ~ {5,number,##}%), " -"{6}" -msgstr "" +msgid "Decrease radius of biome change" +msgstr "减小生物群落变化半径" #, java-format -msgid "§7MojangAPI: Trying to fetch {0} players from Mojang" -msgstr "" +msgid "Current radius: {0}" +msgstr "当前半径: {0} " -msgid "SUCCESS" -msgstr "" +msgid "§2+" +msgstr "§2+" -msgid "FAILED" -msgstr "" +msgid "Increase radius of biome change" +msgstr "增加生物群落变化半径" -#, java-format -msgid "§7 - MojangAPI:§aCOMPLETED: {0}" -msgstr "" +msgid "§2chunk" +msgstr "§2章" -#, java-format -msgid "§7 - MojangAPI:§cERROR: {0}" -msgstr "" +msgid "§call" +msgstr "§呼叫" #, java-format -msgid "Too many requests for Mojangs API ({0} within {1}), sleeping {2}" -msgstr "" - -msgid "North" -msgstr "" - -msgid "North-East" -msgstr "" - -msgid "East" -msgstr "" +msgid "§e{0}" +msgstr "§e{0}" -msgid "South-East" -msgstr "" +msgid "§cBack to Main Menu" +msgstr "§c返回主菜单" -msgid "South" -msgstr "" +msgid "§eClick here to return to the main island screen." +msgstr "§e单击此处返回主岛屏幕。" -msgid "South-West" -msgstr "" +msgid "try to complete a challenge" +msgstr "尝试完成挑战" -msgid "West" -msgstr "" +msgid "§cCommand only available for players." +msgstr "§c命令仅适用于玩家。" -msgid "North-West" -msgstr "" +msgid "show information about the challenge" +msgstr "显示有关挑战的信息" -msgid "The island has been created." -msgstr "" +msgid "§eRank: " +msgstr "§eRank:" -#, java-format -msgid "§b{0}§d locked the island." -msgstr "" +msgid "§4This Challenge is not repeatable!" +msgstr "§4此任务仅可完成一次" -msgid "§4Since your island is locked, your incoming warp has been deactivated." -msgstr "§4因为你的岛屿处于锁定状态,岛屿传送点也将禁用." +msgid "§4You will lose all required items when you complete this challenge!" +msgstr "§4任务物品会从你的背包中扣除" #, java-format -msgid "§b{0}§d deactivated the island warp." -msgstr "§b{0}§d禁止了岛屿传送点." +msgid "" +"§4All required items must be placed on your island, within {0} blocks of you." +msgstr "§4所有必需物品必须放置在您的岛屿上,距离您{0}个方块以内。" #, java-format -msgid "§b{0}§d unlocked the island." -msgstr "" +msgid "§eTo complete this challenge, use §f/c c {0}" +msgstr "§e要完成此挑战,请使用§f/c c{0}" -#, java-format -msgid "§cSKY §f> §7 {0}" -msgstr "" +msgid "§4Invalid challenge name! Use /c help for more information" +msgstr "§4错误的挑战名!输入/c help 获取更多信息" -#, java-format -msgid "§b{0}§d has been removed from the island group." -msgstr "" +msgid "manage islands" +msgstr "管理岛屿" -#, java-format -msgid "§9{1} §7- {0}" -msgstr "" +msgid "protects the island" +msgstr "保护岛屿" -msgid "§9Creating an island at your location" -msgstr "" +msgid "delete the island (removes the blocks)" +msgstr "删除岛屿(移除所有方块)" -#, java-format -msgid "§9Creating an island §7{0}§9 of you" -msgstr "" +msgid "§9Deleted abandoned island at your current location." +msgstr "§9你当前位置的岛屿已经删除。" -msgid "§aCongratulations! §eYour island has appeared." +msgid "" +"§4Island at this location has members!\n" +"§eUse §9/usb island delete §e to delete it." msgstr "" +"§4这个岛上有其他成员!\n" +"§e输入§9/usb island delete <玩家名> §e来删除" -msgid "§cNote:§e Construction might still be ongoing." -msgstr "" +msgid "removes the player from the island" +msgstr "将玩家从岛上移除" -msgid "Use §9/is h§r or the §9/is§r menu to go there." -msgstr "" +msgid "adds the player to the island" +msgstr "将玩家添加到岛上" #, java-format -msgid "Unable to locate schematic {0}, contact a server-admin" -msgstr "" +msgid "§b{0}§d has joined your island group." +msgstr "§b{0}§d加入了你的岛屿团队。" #, java-format -msgid "§cWatchdog!§9 Unable to locate a chest within {0}, bailing out." -msgstr "" +msgid "§4No player named {0} found!" +msgstr "§4未找到名为 {0} 的玩家!" -msgid "UNKNOWN" -msgstr "" +msgid "" +"§4No valid island provided, either stand within one, or provide an island " +"name" +msgstr "§4未提供有效岛屿,请站在一个岛上,或提供岛屿名称" -msgid "ANIMAL" -msgstr "" +msgid "print out info about the island" +msgstr "列出此岛屿信息" -msgid "MONSTER" -msgstr "" +msgid "sets the biome of the island" +msgstr "设置岛上的生物群落" -msgid "VILLAGER" -msgstr "" +msgid "§4That player has no island." +msgstr "§4玩家仍未拥有岛屿。" -msgid "GOLEM" -msgstr "" +msgid "§4No valid island at your location" +msgstr "§4此处岛屿无效" -#, java-format -msgid "§c{0}" -msgstr "" +msgid "purges the island" +msgstr "清理该岛屿" -#, java-format -msgid "§7{0}: §a{1}§7 (max. {2})" -msgstr "" +msgid "§4Error! §9No valid island found for purging." +msgstr "§4错误!§9未找到可清理的有效岛屿。" -msgid "" -"§cThe island owning this piece of nether is being deleted! Sending you to " -"spawn." -msgstr "" +#, java-format +msgid "§cPURGE: §9Purged island at {0}" +msgstr "§cPURGE: §9 清理:在 {0} 处清理的岛屿" -msgid "§cThe island you are on is being deleted! Sending you to spawn." -msgstr "§c你所处的岛屿正在被删除!将你传送到出生点." +msgid "toggles the islands ignore status" +msgstr "切换岛屿忽略状态" #, java-format -msgid "§eWALL OF FAME (page {0} of {1}):" -msgstr "§e荣誉墙 (页数 {0} - {1}):" +msgid "§cSet {0}s island to be ignored on top-ten and purge." +msgstr "§cSet{0}s该岛在前十名中被忽视并被清理。" #, java-format -msgid "§4Top ten list is empty! Only islands above level {0} is considered." -msgstr "" - -msgid "§4Island level has been disabled, contact an administrator." -msgstr "§4岛屿等级系统未启用,请联系管理员." - -msgid "§a#%2d §7(%5.2f): §e%s §7%s" -msgstr "§a#%2d §7(%5.2f): §e%s §7%s" - -msgid "Click to warp to the island!" -msgstr "" +msgid "" +"§cRemoved ignore-flag of {0}s island, it will now show up on top-ten and " +"purge." +msgstr "§c删除了忽略标志{0}s,现在它将出现在前十名并被清理。" -#, java-format -msgid "§eYour rank is: §f{0}" -msgstr "§e你的排名是: §f{0}" +msgid "§4No valid player-name supplied." +msgstr "§4无效的玩家名" #, java-format -msgid "§7Complete {0} more {1} §7challenges" -msgstr "" +msgid "Removing {0} from island" +msgstr "从岛屿上移除了 {0}" #, java-format -msgid "§7Complete {0}" -msgstr "" +msgid "§eChanged biome of {0}s island to {1}." +msgstr "§e将 {0} 的岛屿的生物群系更改为 {1}" -msgid "to unlock this rank" -msgstr "" +msgid "§aYou may need to go to spawn, or relog, to see the changes." +msgstr "§a你需要返回出生点,或者重新登录才能看到改变。" #, java-format -msgid "§4You can complete this {0} more time(s)." -msgstr "" +msgid "§e{0} has had their biome changed to {1}." +msgstr "§e{0} 将他们的生物群系更改为了 {1}" #, java-format -msgid "§4Requirements will reset in {0} days." -msgstr "§4需求将在 {0} 天后重置." +msgid "§eRemoving {0}''s island." +msgstr "§e移除了 {0} 的岛屿。" + +msgid "Error: That player does not have an island!" +msgstr "错误: 玩家未拥有岛屿。" #, java-format -msgid "§4Requirements will reset in {0} hours." -msgstr "§4需求将在 {0} 小时后重置" +msgid "§e{0}s island at {1} has been protected" +msgstr "§e{0} 的岛屿在 {1} 被保护" #, java-format -msgid "§4Requirements will reset in {0} minutes." -msgstr "§4需求将在 {0} 分钟后重置" +msgid "§4{0}s island at {1} was already protected" +msgstr "§4{0}s位于{1}的岛屿已经受到保护" -msgid "§4This challenge is currently unavailable." -msgstr "" +msgid "flushes all caches to files" +msgstr "将所有缓存刷新为文件" #, java-format -msgid "§4You can complete this again in {0} days." -msgstr "" +msgid "" +"§eFlushed §a{0} islands§e, §b{1} players and §6{2} challenge-completions." +msgstr "§eFlushed§a {0} 个岛屿§e、§b {1} 个玩家和§6 {2} 个挑战完成。" -#, java-format -msgid "§4You can complete this again in {0} hours." -msgstr "" +msgid "Controls player-cooldowns" +msgstr "控制玩家冷却时间" + +msgid "clears the cooldown on a command (* = all)" +msgstr "根据命令清除冷却时间(*=all)" + +msgid "§eThe player is not currently online" +msgstr "§e玩家未在线" #, java-format -msgid "§4You can complete this again in {0} minutes." -msgstr "" +msgid "Cleared cooldown on {0} for {1}" +msgstr "清除冷却时间在{0} ~ {1}" -msgid "§eThis challenge requires:" -msgstr "" +#, java-format +msgid "No active cooldown on {0} for {1} detected!" +msgstr "在{0} ~ {1} 之间没有活跃的冷却时间" -msgid "§7and more..." -msgstr "" +msgid "Invalid command supplied, only restart and biome supported!" +msgstr "无效的命令,只支持restart 和 biome" -msgid "§eItems will be traded for reward." -msgstr "" +msgid "restarts the cooldown on the command" +msgstr "根据命令重新启动冷却" #, java-format -msgid "§eMust be within {0} meters." -msgstr "" +msgid "§eReset cooldown on {0} for {1}§e to {2} seconds" +msgstr "§e重置冷却时间在{0} ~ {1} §e ~ {2} 秒" -msgid "§6Item Reward: §a" -msgstr "§6物品奖励:" +msgid "lists all the active cooldowns" +msgstr "列出所有活动冷却" -#, java-format -msgid "§6Currency Reward: §a{0}" -msgstr "§6货币奖励: §a{0}" +msgid "§eCmd Cooldown" +msgstr "§e 指令冷却时间" #, java-format -msgid "§6Exp Reward: §a{0}" -msgstr "§6经验奖励: §a{0}" +msgid "§a{0} §c{1}" +msgstr "§a{0} §c{1}" #, java-format -msgid "§dTotal times completed: §f{0}" -msgstr "§d总完成次数: §f{0}" +msgid "§eNo active cooldowns for §9{0}§e found." +msgstr "§e没有活跃的冷却时间 §9{0} 被找到" -#, java-format -msgid "§7Requires {0}" -msgstr "" +msgid "region manipulations" +msgstr "区域操纵" -#, java-format -msgid "§4No challenge named {0} found" -msgstr "" +msgid "shows the borders of the current island" +msgstr "显示了当前岛屿的边界" -msgid "§4You must be on your island to do that!" -msgstr "§4你必须在你的岛屿上才能这样做!" +msgid "§eNo island found at your current location" +msgstr "§e 在您当前位置未找到岛屿" -#, java-format -msgid "§4The {0} challenge is not repeatable!" -msgstr "§4挑战:§e{0} §4不可重复完成!" +msgid "§eCan only be executed as a player" +msgstr "§e只能作为玩家执行" -#, java-format -msgid "§4You cannot complete the {0} challenge again yet!" -msgstr "" +msgid "shows the borders of the current chunk" +msgstr "显示当前块的边界" -#, java-format -msgid "§eTrying to complete challenge §a{0}" -msgstr "" +msgid "shows the borders of the inner-chunks" +msgstr "显示了内部块的边界" -#, java-format -msgid "§4{0}" -msgstr "§4{0}" +msgid "shows the non-chunk-aligned borders" +msgstr "显示了非块对齐的边框" -#, java-format -msgid "§4You must be standing within {0} blocks of all required items." -msgstr "§4在你周围 {0} 格以内必须包含挑战所需物品。" +msgid "shows the borders of the outer-chunks" +msgstr "显示了外部块的边界" -#, java-format -msgid "§4Your island must be level {0} to complete this challenge!" -msgstr "§4您的岛屿必须达到 {0} 级,才能完成当前挑战!" +msgid "hides the regions again" +msgstr "再次隐藏区域" -#, java-format -msgid "§4Unknown type of challenge: {0}" -msgstr "§4未知难度的挑战: {0}" +msgid "§eStopped displaying regions" +msgstr "§电子停止显示区域" -#, java-format -msgid "" -"§eStill the following entities short:\n" -"{0}" -msgstr "" -"§e任然缺少以下物品:\n" -"{0}" +msgid "§eNo currently shown regions for this player" +msgstr "§目前没有显示此玩家的区域" -#, java-format -msgid " §4{0} §b{1}" -msgstr "" +msgid "set the ticks between animations" +msgstr "设置动画之间的刻度" #, java-format -msgid "§eYou are the following items short:{0}" -msgstr "§e你缺少以下物品: {0}" +msgid "§eAnimation-tick changed to {0}." +msgstr "§eAnimation标记更改为 {0} 。" -#, java-format -msgid "§aYou have completed the {0} challenge!" -msgstr "§a你完成了挑战:§e {0}" +msgid "§eAnimation-tick must be a valid integer." +msgstr "§eAnimation勾号必须是有效整数。" -#, java-format -msgid "§9{0}§f has completed the §9{1}§f challenge!" -msgstr "" +msgid "refreshes the existing animations" +msgstr "刷新现有动画" -#, java-format -msgid "§eItem reward(s): §f{0}" -msgstr "§e物品奖励: §f{0}" +msgid "teleport to another players island" +msgstr "传送到另一个玩家岛" -#, java-format -msgid "§eExp reward: §f{0,number,#.#}" -msgstr "§e经验奖励: §f{0,number,#.#}" +msgid "§4Only supported for players" +msgstr "§4近支持玩家" + +msgid "§4That player does not have an island!" +msgstr "§4玩家仍未拥有岛屿。" #, java-format -msgid "§eCurrency reward: §f{0,number,###.##} {1} §a ({2,number,##.##})%" -msgstr "§e货币奖励: §f{0,number,###.##} {1} §a ({2,number,##.##})%" +msgid "§aTeleporting to {0}''s island." +msgstr "§a 传送到 {0}的岛屿。" -msgid "§eYour inventory is §4full§e. Items dropped on the ground." -msgstr "§e背包§4已满§e。奖励物品掉落到了地上。" +msgid "various WorldGuard utilities" +msgstr "各种WorldGuard实用程序" -msgid "§e§lClick to complete this challenge." -msgstr "§e§l点击来完成这个挑战。" +msgid "refreshes the chunks around the player" +msgstr "刷新玩家周围的区块" -msgid "§4§lYou can't repeat this challenge." -msgstr "§4§l你无法重复完成这个挑战。" +msgid "§eResending chunks to the client" +msgstr "§e重新发送区块数据至客户端" + +msgid "load the region chunks" +msgstr "加载区域块" #, java-format -msgid "Rank: {0}" -msgstr "" +msgid "§eLoading chunks at {0}" +msgstr "§e加载区块在{0}" -msgid "§fComplete most challenges in" -msgstr "§f完成大部分挑战在" +#, java-format +msgid "§eUnloading chunks at {0}" +msgstr "§e卸载区块在{0}" -msgid "§fthis rank to unlock the next rank." -msgstr "§f这个难度中,才能解锁下一个等级。" +msgid "update the WG regions" +msgstr "更新工作组区域" -msgid "§eClick here to show previous page" -msgstr "" +#, java-format +msgid "§eIsland world-guard regions updated for {0}" +msgstr "§e岛屿世界保护区域已更新为{0}" -msgid "§eClick here to show next page" -msgstr "" +msgid "§eNo island found at your location!" +msgstr "§在您的位置没有发现小岛!" -msgid "But you are ALLLLLLL ALOOOOONE!" -msgstr "" +msgid "refreshes the chunk around the player" +msgstr "刷新玩家周围的区块" -msgid "But you are Yelling in the wind!" -msgstr "" +msgid "advanced info about items" +msgstr "有关项目的高级信息" -msgid "But your fantasy friends are gone!" -msgstr "" +msgid "shows the component format for the currently held item" +msgstr "显示当前持有项目的组件格式" -msgid "But you are Talking to your self!" -msgstr "" +msgid "§cNo item in hand!" +msgstr "§c手中没有物品!" #, java-format -msgid "§cSorry! {0}" -msgstr "" +msgid "§eInfo for §9{0}" +msgstr "§9 {0} 的信息" -msgid "Either send a message directly to your group, or toggle it on/off." -msgstr "" +#, java-format +msgid "§7 - name: §9{0}" +msgstr "§7-名称:§9{0}" -msgid "party" +msgid "manage orphans" msgstr "" -msgid "island" +msgid "count orphans" msgstr "" #, java-format -msgid "§cToggled chat to {0} §aON" -msgstr "" +msgid "§e{0} old island locations will be used before new ones." +msgstr "§e{0} 旧的岛屿位置将会被新岛屿所替换。" -#, java-format -msgid "§cRepeat §9{0}§c to toggle it off" +msgid "clear orphans" msgstr "" -#, java-format -msgid "§aToggled chat §cOFF§a for {0}" -msgstr "" +msgid "§eClearing all old (empty) island locations." +msgstr "§e清理所有旧(空)的岛屿坐标。" -msgid "§cCommand only available to players" +msgid "list orphans" msgstr "" -msgid "talk to your island party" +msgid "§eNo orphans currently registered." msgstr "" -msgid "talk to players on your island" -msgstr "" +#, java-format +msgid "§eOrphans ({0}/{1}): {2}" +msgstr "§eOrphans ({0}/{1}): {2}" -msgid "manually update the top 10 list" -msgstr "" +msgid "open GUI for config" +msgstr "打开GUI进行配置" -msgid "§eGenerating the Top Ten list" -msgstr "§e生存排行榜前十名单" +msgid "searches config for a specific key" +msgstr "在配置中搜索特定密钥" -msgid "§eFinished generation of the Top Ten list" -msgstr "§e排行榜前十名单生成完毕" +#, java-format +msgid "§c{0}§9" +msgstr "§c{0}§9" -msgid "purges all abandoned islands" -msgstr "" +#, java-format +msgid "§9{0}§8: §e{1}" +msgstr "§9{0}§8:§e{1}" -msgid "§4You must provide the age in days to purge!" -msgstr "§4你必须指定一个天数来删除。" +#, java-format +msgid "Found the following matching {0}:" +msgstr "找到以下匹配项 {0} :" -msgid "§4The level must be a valid number" -msgstr "" +msgid "§a
" +msgstr "§a<区域>" #, java-format -msgid "" -"§eFinding all islands that have been abandoned for more than {0} days below " -"level {1}" -msgstr "" +msgid "§3{0,number,#.##}" +msgstr "§3{0,number,#.##}" #, java-format -msgid "§4PURGE:§e Do §9usb purge confirm§e within {0} to accept." -msgstr "" +msgid "§2{0}" +msgstr "§2{0}" -msgid "§4Trying to abort purge" -msgstr "" +msgid "§eInvalid configuration name" +msgstr "§e有效配置名称" -msgid "§4Purge aborted!" -msgstr "" +msgid "set a player''s island to your location" +msgstr "将玩家的岛屿设置为您的位置" -msgid "§4A purge is already running.§e Either §9confirm§e or §9stop§e it." -msgstr "" +#, java-format +msgid "§aSet {0}''s island to the current island." +msgstr "§a将 {0} 的岛屿设置为当前岛屿。" -msgid "§4Starting purge..." -msgstr "" +msgid "§4Island not found: unable to set the island!" +msgstr "§4未找到岛屿:无法设置岛屿!" -msgid "Controls player-cooldowns" -msgstr "" +#, java-format +msgid "§eInvalid player {0} supplied." +msgstr "§e无效的玩家 {0}" -msgid "clears the cooldown on a command (* = all)" -msgstr "" +msgid "toggles maintenance mode" +msgstr "切换维护模式" -msgid "§eThe player is not currently online" -msgstr "§e玩家未在线" +msgid "§cMAINTENANCE: §aActivated§e all uSkyBlock features currently disabled." +msgstr "§c维护:§a激活§e当前禁用的所有uSkyBlock功能。" -#, java-format -msgid "Cleared cooldown on {0} for {1}" -msgstr "清除冷却时间在{0} ~ {1}" +msgid "" +"§cMAINTENANCE: §4Deactivated§e all uSkyBlock features back to operational." +msgstr "§c维护:§4停用§e所有uSkyBlock功能恢复运行。" -#, java-format -msgid "No active cooldown on {0} for {1} detected!" -msgstr "在{0} ~ {1} 之间没有活跃的冷却时间" +msgid "§cMaintenance mode can only be changed from console!" +msgstr "§c维护模式只能从控制台更改!" -msgid "Invalid command supplied, only restart and biome supported!" -msgstr "无效的命令,只支持restart 和 biome" +msgid "protects all islands (time consuming)" +msgstr "保护所有岛屿(耗时)" -msgid "restarts the cooldown on the command" +msgid "§cTrying to abort protect-all task." +msgstr "§c试图中止保护所有任务。" + +msgid "" +"§4Sorry!§e A protect-all is already running. Let it complete first, or use " +"§9usb protectall §cstop" msgstr "" +"§4抱歉!§e 一个全方位保护程序已经在运行。请先让它完成,或者使用 §9usb " +"protectall stop 命令来停止它。" -#, java-format -msgid "§eReset cooldown on {0} for {1}§e to {2} seconds" -msgstr "§e重置冷却时间在{0} ~ {1} §e ~ {2} 秒" +msgid "§eStarting a protect-all task. It will take a while." +msgstr "§e开始执行 全局保护,将会花费一定时间。" -msgid "lists all the active cooldowns" -msgstr "" +msgid "manually update the top 10 list" +msgstr "手动更新前10名列表" -msgid "§eCmd Cooldown" -msgstr "§e 指令冷却时间" +msgid "§eGenerating the Top Ten list" +msgstr "§e生存排行榜前十名单" -#, java-format -msgid "§a{0} §c{1}" -msgstr "§a{0} §c{1}" +msgid "§eFinished generation of the Top Ten list" +msgstr "§e排行榜前十名单生成完毕" #, java-format -msgid "§eNo active cooldowns for §9{0}§e found." -msgstr "§e没有活跃的冷却时间 §9{0} 被找到" - -msgid "toggles maintenance mode" -msgstr "" +msgid "" +"- PURGING: {0,number,##}% ({1}/{2}), elapsed {3}, estimated completion ~{4}" +msgstr "- 清理:{0,number,##}%({1}/{2}),已用时间 {3},预计完成时间约 ~{4}" -msgid "§cMAINTENANCE: §aActivated§e all uSkyBlock features currently disabled." -msgstr "" +msgid "§4PURGE:§9 Finished purging abandoned islands." +msgstr "§4清理:§9已完成对废弃岛屿的清理。" -msgid "" -"§cMAINTENANCE: §4Deactivated§e all uSkyBlock features back to operational." -msgstr "" +msgid "§4PURGE:§9 Aborted purging abandoned islands." +msgstr "§4清理:§9废弃岛屿的清理被中止。" -msgid "§cMaintenance mode can only be changed from console!" -msgstr "" +#, java-format +msgid "§7- SCANNING: {0,number,##}% ({1}/{2} failed: {3}) ~ {4}" +msgstr "§7-扫描: {0,number,##}% ({1}/{2} 失败: {3}) ~ {4}" -msgid "controls async jobs" -msgstr "" +msgid "§4PURGE:§9 Scanning aborted." +msgstr "§4清理:§9扫描中止。" -msgid "§9Job Statistics" +#, java-format +msgid "" +"§4PURGE:§9 Scanning done, found {0} candidates, below level {1}, ready for " +"purgatory." msgstr "" +"§4PURGE:§9 扫描已完成,发现 {0} 个候选对象,低于 {1} 级,准备进入下界。" -msgid "§7----------------" -msgstr "" +msgid "§cABORTED:§e Protect-All was aborted!" +msgstr "§cABORTED:§e保护所有已中止!" -msgid "#" -msgstr "" +#, java-format +msgid "§eCompleted protect-all in {0}, {1} new regions were created!" +msgstr "§已在{0}, {1} 个新区域中完成全部保护!" -msgid "ms/job" -msgstr "" +msgid "shows perk-information" +msgstr "显示福利信息" -msgid "ms/tick" -msgstr "" +msgid "shows a specific players perks" +msgstr "显示特定玩家的福利" -msgid "ticks" -msgstr "" +#, java-format +msgid "§4No player named {0} was found!" +msgstr "§4没有名字为 {0} 的玩家被找到" -msgid "act" -msgstr "" +#, java-format +msgid "additional perks {0}" +msgstr "额外福利{0}" -msgid "time" -msgstr "" +msgid "advanced command for getting island-data" +msgstr "获取岛屿数据的高级命令" -msgid "name" -msgstr "" +#, java-format +msgid "§eCurrent value for {0} is ''{1}''" +msgstr "§e{0}的e当前值为“{1}”" #, java-format -msgid "§ePlayer {0} has no island!" -msgstr "§e玩家 {0} 还未获得岛屿!" +msgid "§cUnable to get state for {0}" +msgstr "§c无法获取{0}的状态" #, java-format -msgid "§eInvalid player {0} supplied." -msgstr "§e无效的玩家 {0}" +msgid "§eValid fields are {0}" +msgstr "§eValid字段为{0}" -msgid "flushes all caches to files" -msgstr "" +msgid "control debugging" +msgstr "控制调试" -#, java-format -msgid "" -"§eFlushed §a{0} islands§e, §b{1} players and §6{2} challenge-completions." -msgstr "" +msgid "set debug-level" +msgstr "设置调试级别" -msgid "tries to fix the the area of flatland." -msgstr "" +msgid "toggle debug-logging" +msgstr "切换调试日志记录" -msgid "§4No valid island found" -msgstr "§4没有找到有效的岛屿" +msgid "§4Logging wasn't active, so you can't disable it!" +msgstr "§4记录未启用,所以你无法关闭。" -#, java-format -msgid "§4No flatland detected at {0}''s island!" -msgstr "§4在{0} 的岛上未检测到平地。" +msgid "flush current content of the logger to file." +msgstr "将记录器的当前内容刷新到文件中。" -msgid "manage orphans" -msgstr "" +msgid "§eLog-file has been flushed." +msgstr "§e日志文件已经更新。" -msgid "count orphans" -msgstr "" +msgid "§4Logging is not enabled, use §d/usb debug enable" +msgstr "§4记录未启用,输入§d/usb debug 启用" -#, java-format -msgid "§e{0} old island locations will be used before new ones." -msgstr "§e{0} 旧的岛屿位置将会被新岛屿所替换。" +msgid "§4Invalid argument, try INFO, FINE, FINER, FINEST" +msgstr "§4未知参数,请尝试 INFO, FINE, FINER, FINEST" -msgid "clear orphans" -msgstr "" +msgid "§eLogging disabled!" +msgstr "§e记录已经关闭" -msgid "§eClearing all old (empty) island locations." -msgstr "§e清除所有旧(空)的岛屿坐标。" +msgid "advanced command for setting island-data" +msgstr "设置岛屿数据的高级命令" -msgid "list orphans" -msgstr "" +#, java-format +msgid "§c{0} was set to ''{1}''" +msgstr "§c{0}已设置为“{1}”" -msgid "§eNo orphans currently registered." -msgstr "" +#, java-format +msgid "§cUnable to set field {0} to ''{1}''" +msgstr "§c无法将字段{0}设置为“{1}”" #, java-format -msgid "§eOrphans ({0}/{1}): {2}" -msgstr "" +msgid "§cUnable to set field {0} to ''{1}'', a number was expected" +msgstr "§c无法将字段{0}设置为“{1}”,需要一个数字" + +#, java-format +msgid "§cInvalid field {0}" +msgstr "§c无效字段{0}" msgid "transfer leadership to another player" -msgstr "" +msgstr "将领导权转移给另一名球员" #, java-format msgid "§4Player {0} has no island to transfer!" @@ -1573,621 +1456,575 @@ msgstr "" #, java-format msgid "§bLeadership transferred by {0}§b to {1}" -msgstr "§b岛屿主人由{0} 变更为{1}" +msgstr "§b岛主已由{0} 变更为{1}" -msgid "open GUI for config" -msgstr "" +msgid "purges all abandoned islands" +msgstr "清理所有废弃岛屿" -msgid "searches config for a specific key" -msgstr "" +msgid "§4You must provide the age in days to purge!" +msgstr "§4你必须指定一个天数来删除。" -#, java-format -msgid "§c{0}§9" -msgstr "" +msgid "§4The level must be a valid number" +msgstr "§4级别必须是有效数字" #, java-format -msgid "§9{0}§8: §e{1}" -msgstr "" +msgid "" +"§eFinding all islands that have been abandoned for more than {0} days below " +"level {1}" +msgstr "§e查找级别{0}以下被遗弃超过{1}天的所有岛屿" #, java-format -msgid "Found the following matching {0}:" -msgstr "" +msgid "§4PURGE:§e Do §9usb purge confirm§e within {0} to accept." +msgstr "§4清理:§e执行§9usb purge comfirm§e在{0}内接受。" -msgid "§a
" -msgstr "" +msgid "§4Trying to abort purge" +msgstr "§4试图中止清理" + +msgid "§4Purge aborted!" +msgstr "§4清理中止!" + +msgid "§4A purge is already running.§e Either §9confirm§e or §9stop§e it." +msgstr "§4A清理已在运行。§e§9确认§e或§9停止§e。" + +msgid "§4Starting purge..." +msgstr "§4开始清洗。。。" + +msgid "various chunk commands" +msgstr "各种区块命令" + +msgid "regenerate current chunk" +msgstr "重新生成当前区块" #, java-format -msgid "§3{0,number,#.##}" -msgstr "" +msgid "successfully regenerated chunk at {0},{1}" +msgstr "已成功在{0}、{1}处重新生成区块" + +msgid "unload current chunk" +msgstr "卸载当前区块" #, java-format -msgid "§2{0}" -msgstr "" +msgid "successfully unloaded chunk at {0},{1}" +msgstr "已成功卸载{0}、{1}处的区块" -msgid "§eInvalid configuration name" -msgstr "" +#, java-format +msgid "§4FAILED!§e could not unload chunk at {0},{1}" +msgstr "§4失败!§e无法在{0}、{1}卸载区块" -msgid "teleport to another players island" -msgstr "" +msgid "load current chunk" +msgstr "正在加载所在区块" -msgid "§4Only supported for players" -msgstr "§4近支持玩家" +#, java-format +msgid "loaded chunk at {0},{1}" +msgstr "已在{0}、{1}加载块" -msgid "§4That player does not have an island!" -msgstr "§4玩家仍未拥有岛屿。" +msgid "only available for players" +msgstr "仅适用于玩家" #, java-format -msgid "§aTeleporting to {0}''s island." -msgstr "" +msgid "§4ERROR:§e {0}" +msgstr "§4错误:§e{0}" -msgid "various WorldGuard utilities" -msgstr "" +msgid "imports players and islands from other formats" +msgstr "从其他格式导入玩家和岛屿" -msgid "refreshes the chunks around the player" -msgstr "" +msgid "Manage challenges for a player" +msgstr "管理玩家的挑战" -msgid "§eResending chunks to the client" -msgstr "§e重新发送区块数据至客户端" +msgid "resets the challenge for the player" +msgstr "为玩家重置挑战" -msgid "load the region chunks" -msgstr "" +msgid "§4Challenge has never been completed" +msgstr "§4挑战从未完成过" #, java-format -msgid "§eLoading chunks at {0}" -msgstr "§e加载区块在{0}" +msgid "§echallenge: {0} has been reset for {1}" +msgstr "§e挑战: {0} 已经为玩家 {1} 重置" + +msgid "resets all challenges for the player" +msgstr "重置玩家的所有挑战" #, java-format -msgid "§eUnloading chunks at {0}" -msgstr "§e卸载区块在{0}" +msgid "§e{0} has had all challenges reset." +msgstr "§e{0} 的所有挑战已经重置" -msgid "update the WG regions" -msgstr "" +msgid "complete all challenges in the rank" +msgstr "完成排名中的所有挑战" #, java-format -msgid "§eIsland world-guard regions updated for {0}" -msgstr "" +msgid "§4Challenge {0} has already been completed" +msgstr "§4挑战{0}已完成" -msgid "§eNo island found at your location!" -msgstr "" +#, java-format +msgid "§eChallenge {0} has been completed for {1}" +msgstr "§e挑战 {0} 已为 {1} 完成" -msgid "refreshes the chunk around the player" -msgstr "" +#, java-format +msgid "§4No challenge named {0} was found!" +msgstr "§4挑战: {0} 未找到!" -msgid "region manipulations" -msgstr "" +#, java-format +msgid "§4No rank named {0} was found!" +msgstr "§4未找到名为{0}的排名!" -msgid "shows the borders of the current island" -msgstr "" +msgid "displays version information" +msgstr "显示版本信息" -msgid "§eNo island found at your current location" -msgstr "" +msgid "controls async jobs" +msgstr "控制异步作业" -msgid "§eCan only be executed as a player" -msgstr "" +msgid "§9Job Statistics" +msgstr "§9工作统计" -msgid "shows the borders of the current chunk" -msgstr "" +msgid "§7----------------" +msgstr "§7----------------" -msgid "shows the borders of the inner-chunks" -msgstr "" +msgid "#" +msgstr "#" -msgid "shows the non-chunk-aligned borders" -msgstr "" +msgid "ms/job" +msgstr "ms/job" -msgid "shows the borders of the outer-chunks" -msgstr "" - -msgid "hides the regions again" -msgstr "" +msgid "ms/tick" +msgstr "ms/tick" -msgid "§eStopped displaying regions" -msgstr "" +msgid "ticks" +msgstr "ticks" -msgid "§eNo currently shown regions for this player" -msgstr "" +msgid "act" +msgstr "行为" -msgid "set the ticks between animations" -msgstr "" +msgid "time" +msgstr "时间" -#, java-format -msgid "§eAnimation-tick changed to {0}." -msgstr "" +msgid "name" +msgstr "名称" -msgid "§eAnimation-tick must be a valid integer." -msgstr "" +msgid "tries to fix the the area of flatland." +msgstr "试图修复平地的面积。" -msgid "refreshes the existing animations" -msgstr "" +msgid "§4No valid island found" +msgstr "§4没有找到有效的岛屿" #, java-format -msgid "" -"- PURGING: {0,number,##}% ({1}/{2}), elapsed {3}, estimated completion ~{4}" -msgstr "" +msgid "§4No flatland detected at {0}''s island!" +msgstr "§4在{0} 的岛上未检测到平地。" -msgid "§4PURGE:§9 Finished purging abandoned islands." -msgstr "" +msgid "reload configuration from file." +msgstr "从文件重新加载配置。" -msgid "§4PURGE:§9 Aborted purging abandoned islands." -msgstr "" +msgid "§eConfiguration reloaded from file." +msgstr "§e配置重读。" #, java-format -msgid "§7- SCANNING: {0,number,##}% ({1}/{2} failed: {3}) ~ {4}" -msgstr "" +msgid "§ePlayer {0} has no island!" +msgstr "§e玩家 {0} 还未获得岛屿!" -msgid "§4PURGE:§9 Scanning aborted." -msgstr "" +msgid "Ultimate SkyBlock Admin" +msgstr "空岛超级管理员" -#, java-format -msgid "" -"§4PURGE:§9 Scanning done, found {0} candidates, below level {1}, ready for " -"purgatory." -msgstr "" +msgid "show player-information" +msgstr "显示玩家信息" -msgid "§cABORTED:§e Protect-All was aborted!" -msgstr "" +msgid "complete and list challenges" +msgstr "完成并列出挑战" -#, java-format -msgid "§eCompleted protect-all in {0}, {1} new regions were created!" -msgstr "" +msgid "§eChallenges has been disabled. Contact an administrator." +msgstr "§e挑战不可用,请联系服务器管理员." -msgid "control debugging" -msgstr "" +msgid "§4You can only submit challenges in the skyblock world!" +msgstr "§4你只能在空岛世界提交挑战。" -msgid "set debug-level" -msgstr "" +msgid "§4You can only submit challenges when you have an island!" +msgstr "§4你只能在拥有一个岛屿的情况下提交挑战。" -msgid "toggle debug-logging" -msgstr "" +msgid "" +"§4Your island is full, or you have too many pending invites. You can't " +"invite anyone else." +msgstr "§4你的岛屿已经满员,或者你有太多等待邀请请求。你无法再邀请其他人。" -msgid "§4Logging wasn't active, so you can't disable it!" -msgstr "§4记录未启用,所以你无法关闭。" +msgid "§4That player is already member on another island. " +msgstr "§4该玩家已经是另一个岛屿的会员。" -msgid "flush current content of the logger to file." +#, java-format +msgid "" +"§e{0}§e tried to invite you, but you are already in a party.To leave your " +"current party, use: /island leave." msgstr "" +"§e{0}§e试图邀请你,但你已经参加了一个聚会。要离开当前的聚会,请使用:/island " +"leave。" -msgid "§eLog-file has been flushed." -msgstr "§e日志文件已经更新。" - -msgid "§4Logging is not enabled, use §d/usb debug enable" -msgstr "§4记录未启用,输入§d/usb debug 启用" +#, java-format +msgid "§aInvite sent to {0}" +msgstr "§a邀请已发送到{0}" -msgid "§4Invalid argument, try INFO, FINE, FINER, FINEST" -msgstr "§4未知参数,请尝试 INFO, FINE, FINER, FINEST" +#, java-format +msgid "{0}§e has invited you to join their island!" +msgstr "{0}§e邀请你加入他们的岛屿。" -msgid "§eLogging disabled!" -msgstr "§e记录已经关闭" +msgid "§f/island [accept/reject]§e to accept or reject the invite." +msgstr "§f/island [accept/reject]§e 接受或者拒绝一个邀请。" -msgid "advanced command for setting island-data" -msgstr "" +msgid "§4WARNING: You will lose your current island if you accept!" +msgstr "§4警告: 如果你接受请求,你当前的岛屿将会被清空!" #, java-format -msgid "§c{0} was set to ''{1}''" -msgstr "" +msgid "{0}§d invited {1}" +msgstr "{0}§d邀请了{1}" #, java-format -msgid "§cUnable to set field {0} to ''{1}''" -msgstr "" +msgid "{0}§e has rejected the invitation." +msgstr "{0}§e拒绝了邀请" -#, java-format -msgid "§cUnable to set field {0} to ''{1}'', a number was expected" -msgstr "" +msgid "§4You can't use that command right now. Leave your current party first." +msgstr "§4你无法使用当前命令。请先离开你当前的团队。" -#, java-format -msgid "§cInvalid field {0}" -msgstr "" +msgid "" +"§aYou have joined an island! Use /island party to see the other members." +msgstr "§a你加入了一个岛屿!输入/island party 来查看团队成员。" #, java-format -msgid "§eCurrent value for {0} is ''{1}''" -msgstr "" +msgid "§eInvitation for {0}§e has timedout or been cancelled." +msgstr "§e来自{0}§e的邀请已经过时或被取消。" #, java-format -msgid "§cUnable to get state for {0}" -msgstr "" +msgid "§eInvitation for {0}''s island has timedout or been cancelled." +msgstr "§e来自{0}§e岛屿的邀请已经过时或被取消。" -#, java-format -msgid "§eValid fields are {0}" -msgstr "" +msgid "§eYou have accepted the invitation to join an island." +msgstr "§e你接受了加入岛屿的邀请." -msgid "set a player''s island to your location" -msgstr "" +msgid "§4You haven't been invited." +msgstr "§4你没有被任何人邀请." -#, java-format -msgid "§aSet {0}''s island to the current island." -msgstr "" +msgid "§eYou have rejected the invitation to join an island." +msgstr "§e你拒绝了加入岛屿的邀请" -msgid "§4Island not found: unable to set the island!" -msgstr "" +msgid "general island command" +msgstr "岛屿综合控制台" -msgid "advanced info about NBT stuff" -msgstr "" +msgid "allows user to bypass cooldowns" +msgstr "允许用户绕过冷却" -msgid "shows the NBTTag for the currently held item" -msgstr "" +msgid "allows user to bypass visitor-protections" +msgstr "允许用户绕过访问者保护" -#, java-format -msgid "§eInfo for §9{0}" -msgstr "" +msgid "allows user to bypass teleport-delay" +msgstr "允许用户绕过传送延迟" -#, java-format -msgid "§7 - name: §9{0}" -msgstr "" +msgid "allows user to use [usb] signs" +msgstr "允许用户使用[usb]符号" -#, java-format -msgid "§7 - nbttag: §9{0}" -msgstr "" +msgid "allows user to place [usb] signs" +msgstr "允许用户放置[usb]标志" -msgid "§cNo item in hand!" -msgstr "" +msgid "lock your island to non-party members." +msgstr "当你的岛上没有成员时锁定你的岛屿" -msgid "sets the NBTTag on the currently held item" -msgstr "" +msgid "§4You do not have permission to lock your island!" +msgstr "§4你没有权限锁定你的岛屿!" -#, java-format -msgid "§eSet §9{0}§e to §c{1}" -msgstr "" +msgid "§4You don't have access to this command!" +msgstr "§4你无法使用当前指令。" -msgid "adds the NBTTag on the currently held item" -msgstr "" +msgid "§4You do not have permission to unlock your island!" +msgstr "§4你没有权限解锁你的岛屿!" -#, java-format -msgid "§eAdded §9{0}§e to §c{1}" -msgstr "" +msgid "check your or another''s island info" +msgstr "查看您或他人的岛屿信息" -msgid "Manage challenges for a player" -msgstr "" +msgid "allows user to see others island info" +msgstr "允许用户查看其他岛屿信息" -msgid "resets the challenge for the player" -msgstr "" +msgid "§4Island level has been disabled, contact an administrator." +msgstr "§4岛屿等级系统未启用,请联系管理员." -msgid "§4Challenge has never been completed" -msgstr "§4挑战从未完成过" +msgid "§4Hold your horses! §eYou have to be patient..." +msgstr "§4把你的马拉下马!§e你必须有耐心。。。" -#, java-format -msgid "§echallenge: {0} has been reset for {1}" -msgstr "§e挑战: {0} 已经为玩家 {1} 重置" +msgid "§eYou must be on your island to use this command." +msgstr "§e你必须在你的岛屿上来使用这个指令." -msgid "resets all challenges for the player" -msgstr "" +msgid "§4You do not have an island!" +msgstr "§4你还未拥有一个岛屿!" -#, java-format -msgid "§e{0} has had all challenges reset." -msgstr "§e{0} 的所有挑战已经重置" +msgid "§4You do not have access to that command!" +msgstr "§4你没有权限使用这个指令!" -msgid "complete all challenges in the rank" -msgstr "" +msgid "§4That player is invalid or does not have an island!" +msgstr "§4玩家不存在或仍未拥有一个岛屿!" #, java-format -msgid "§4No player named {0} was found!" -msgstr "§4没有名字为 {0} 的玩家被找到" +msgid "§eBlocks on {0}s Island (page {1,number} of {2,number}):" +msgstr "§e在{0}岛上的方块(第{1,number}页,共{2,number}页):" -#, java-format -msgid "§4Challenge {0} has already been completed" -msgstr "" +msgid "Score Count Block" +msgstr "计分方块" #, java-format -msgid "§eChallenge {0} has been completed for {1}" -msgstr "" +msgid "{0,number,00.00} {1,number,#} {2}" +msgstr "{0,number,00.00} {1,number,#} {2}" #, java-format -msgid "§4No challenge named {0} was found!" -msgstr "§4挑战: {0} 未找到!" +msgid "§aIsland level is {0,number,###.##}" +msgstr "§a岛屿等级: {0,number,###.##}" -#, java-format -msgid "§4No rank named {0} was found!" -msgstr "" +msgid "teleports you to your island (or create one)" +msgstr "将你传送到你的岛屿(或创建一个)" -msgid "various chunk commands" -msgstr "" +msgid "§4This command can only be executed by a player" +msgstr "§4这个指令只能由玩家来执行." -msgid "regenerate current chunk" -msgstr "" +msgid "changes a members island-permissions" +msgstr "更改成员岛权限" #, java-format -msgid "successfully regenerated chunk at {0},{1}" -msgstr "" +msgid "§ePermissions for §9{0}§e:" +msgstr "§e§9{0}§e的许可:" -#, java-format -msgid "§4FAILED!§e could not regenerate chunk at {0},{1}" -msgstr "" +msgid "§aON" +msgstr "§aoon" -msgid "unload current chunk" -msgstr "" +msgid "§cOFF" +msgstr "§cOFF" #, java-format -msgid "successfully unloaded chunk at {0},{1}" -msgstr "" +msgid "§7 - §6{0}§7 : {1}" +msgstr "§7 - §6{0}§7 : {1}" #, java-format -msgid "§4FAILED!§e could not unload chunk at {0},{1}" -msgstr "" - -msgid "load current chunk" -msgstr "" +msgid "§cInvalid permission {0}. Must be one of {1}" +msgstr "§c权限{0}无效。必须是{1}之一" #, java-format -msgid "loaded chunk at {0},{1}" -msgstr "" - -msgid "only available for players" -msgstr "" +msgid "§eToggled permission §9{0}§e for §9{1}§e to {2}" +msgstr "§e为§9{1}§e至{2}记录了许可§9{0}§e" #, java-format -msgid "§4ERROR:§e {0}" -msgstr "" - -msgid "protects all islands (time consuming)" -msgstr "" - -msgid "§cTrying to abort protect-all task." -msgstr "" - -msgid "" -"§4Sorry!§e A protect-all is already running. Let it complete first, or use " -"§9usb protectall §cstop" -msgstr "" - -msgid "§eStarting a protect-all task. It will take a while." -msgstr "§e开始执行 全局保护,将会花费一定时间。" +msgid "§eUnable to toggle permission §9{0}§e for §9{1}" +msgstr "§e 无法切换权限 §9{0}§e 对于 §9{1}" -msgid "reload configuration from file." -msgstr "" +msgid "accept/reject an invitation." +msgstr "接受/拒绝邀请。" -msgid "§eConfiguration reloaded from file." -msgstr "§e配置重读。" +msgid "set the island-home" +msgstr "设置岛屿出生点" -msgid "manage islands" -msgstr "" +msgid "§4You must be closer to your island to set your skyblock home!" +msgstr "§4你必须在岛屿上才能设置你的家!" -msgid "protects the island" -msgstr "" +msgid "§4Your current location is not a safe home-location." +msgstr "§4您当前的位置不是一个安全的家。" -msgid "delete the island (removes the blocks)" -msgstr "" +msgid "§aYour skyblock home has been set to your current location." +msgstr "§a你岛屿家的位置已经设置在你当前位置." -msgid "§9Deleted abandoned island at your current location." -msgstr "§9你当前位置的岛屿已经删除。" +msgid "remove a member from your island." +msgstr "从您的岛屿中删除一个成员。" -msgid "" -"§4Island at this location has members!\n" -"§eUse §9/usb island delete §e to delete it." -msgstr "" -"§4当前位置的岛屿拥有其他的成员!\n" -"§e输入§9/usb island delete <玩家名> §e来删除" +msgid "§4You do not have permission to kick others from this island!" +msgstr "§4你没有在当前岛屿踢出玩家的权限!" -msgid "removes the player from the island" -msgstr "" +msgid "§4You can't remove the leader from the Island!" +msgstr "§4你无法将岛屿的主人移除!" -msgid "adds the player to the island" -msgstr "" +msgid "§4Stop kickin' yourself!" +msgstr "§4请不要尝试踢出你自己!" #, java-format -msgid "§b{0}§d has joined your island group." -msgstr "§b{0}§d加入了你的岛屿团队。" +msgid "§4{0} has removed you from their island!" +msgstr "§4{0}把你从他们的岛上赶走了!" #, java-format -msgid "§4No player named {0} found!" -msgstr "" - -msgid "" -"§4No valid island provided, either stand within one, or provide an island " -"name" -msgstr "" - -msgid "print out info about the island" -msgstr "" +msgid "§4{0} has been removed from the island." +msgstr "§4{0}被从当前岛屿上移除." -msgid "sets the biome of the island" -msgstr "" +#, java-format +msgid "§4{0} tried to kick you from their island!" +msgstr "§4{0}试图把你踢出他们的岛!" -msgid "§4That player has no island." -msgstr "§4玩家仍未拥有岛屿。" +#, java-format +msgid "§4{0} is exempt from being kicked." +msgstr "§4{0}免于被踢。" -msgid "§4No valid island at your location" -msgstr "§4无效的岛屿在你的位置上。" +#, java-format +msgid "§4{0} has kicked you from their island!" +msgstr "§4{0}把你踢出了他们的岛!" -msgid "purges the island" -msgstr "" +#, java-format +msgid "§4{0} has been kicked from the island." +msgstr "§4{0}被从当前岛屿上踢出." -msgid "§4Error! §9No valid island found for purging." -msgstr "" +msgid "§4That player is not part of your island group, and not on your island!" +msgstr "§4玩家不是你岛屿上的一员,并且不在你的岛屿上!" -#, java-format -msgid "§cPURGE: §9Purged island at {0}" -msgstr "" +msgid "change the biome of the island" +msgstr "改变岛上的生物群落" -msgid "toggles the islands ignore status" -msgstr "" +msgid "exempt player from biome-cooldown" +msgstr "免除玩家生物群系冷却时间" #, java-format -msgid "§cSet {0}s island to be ignored on top-ten and purge." -msgstr "" +msgid "Let the player change their islands biome to {0}" +msgstr "让玩家将他们的岛屿生物群系更改为{0}" -#, java-format msgid "" -"§cRemoved ignore-flag of {0}s island, it will now show up on top-ten and " -"purge." -msgstr "" +"§cYou do not have permission to change the biome of your current island." +msgstr "§c你没有权限更改你当前岛屿的生物群系." -msgid "§4No valid player-name supplied." -msgstr "§4无效的玩家名" +msgid "§4You do not have permission to change the biome of this island!" +msgstr "§4你没有权限去变更岛屿的生物群系!" -#, java-format -msgid "Removing {0} from island" -msgstr "从岛屿上移除了 {0}" +msgid "§eYou must be on your island to change the biome!" +msgstr "§e你必须在你的岛屿上才能更改生物群系!" #, java-format -msgid "§eChanged biome of {0}s island to {1}." -msgstr "§e将 {0} 的岛屿的生物群系更改为 {1}" +msgid "§cYou have misspelled the biome name. Must be one of {0}" +msgstr "§c你拼错了生物群名。必须是{0}之一" #, java-format -msgid "§eChanged biome of {0}s island to OCEAN." -msgstr "" +msgid "§eYou can change your biome again in {0,number,#} minutes." +msgstr "§e你需要等待{0,number,#}分钟才能再次更改生物群系." -msgid "§aYou may need to go to spawn, or relog, to see the changes." -msgstr "§a你需要返回出生点,或者重新登录才能看到改变。" +msgid "§cYou do not have permission to change your biome to that type." +msgstr "§c您无权将您的生物群落更改为该类型。" #, java-format -msgid "§e{0} has had their biome changed to {1}." -msgstr "§e{0} 将他们的生物群系更改为了 {1}" +msgid "" +"§7The pixies are busy changing the biome near you to §9{0}§7, be patient." +msgstr "§7小精灵们正忙着把你附近的生物群系改成§9{0}§7,耐心点。" #, java-format -msgid "§e{0} has had their biome changed to OCEAN." -msgstr "§e{0} 将他们的生物群系更改为了 OCEAN(海洋生物群系)" +msgid "" +"§7The pixies are busy changing the biome in your current chunk to §9{0}§7, " +"be patient." +msgstr "§7小精灵们正忙于将你当前区块中的生物群系更改为§9{0}§7,请耐心等待。" #, java-format -msgid "§eRemoving {0}''s island." -msgstr "§e移除了 {0} 的岛屿。" +msgid "" +"§7The pixies are busy changing the biome of your island to §9{0}§7, be " +"patient." +msgstr "§7小精灵们正忙着把你岛上的生物群系改成§9{0}§7,耐心点。" -msgid "Error: That player does not have an island!" -msgstr "错误: 玩家未拥有岛屿。" +msgid "§cInvalid arguments. Use /is biome [radius|chunk|all]" +msgstr "§c无效的参数。使用 /is biome <群落名称> [半径|区块|全部]" #, java-format -msgid "§e{0}s island at {1} has been protected" -msgstr "§e{0} 的岛屿在 {1} 被保护" +msgid "§aYou have changed your island''s biome to {0}" +msgstr "§a您已将岛屿的生物群系更改为{0}" #, java-format -msgid "§4{0}s island at {1} was already protected" -msgstr "" - -msgid "displays version information" -msgstr "" - -msgid "imports players and islands from other formats" -msgstr "" - -msgid "shows perk-information" -msgstr "" - -msgid "shows a specific players perks" -msgstr "" +msgid "{0} changed the island biome to {1}" +msgstr "{0}将岛屿的生物群系更改为{1}" #, java-format -msgid "additional perks {0}" -msgstr "" - -msgid "changes the language of the plugin, and reloads" -msgstr "" +msgid "§aYou have changed {0} blocks around you to the {1} biome" +msgstr "§a您已将周围的{0}个方块更改为{1}个生物群系" #, java-format -msgid "§aSuccessfully changed language to §e{0}" -msgstr "" +msgid "{0} created an area with {1} biome" +msgstr "{0}创建了一个包含{1}个生物群系的区域" -#, java-format -msgid "§cFailed to change language to §e{0}" -msgstr "" +msgid "enable/disable warping to your island." +msgstr "启用/禁用传送到您的岛屿。" -msgid "§9Supported Languages:\n" -msgstr "" +msgid "§4Your island is locked. You must unlock it before enabling your warp." +msgstr "§4你的岛屿已经锁定,在启用传送点之前请先解锁." #, java-format -msgid "§f{0} §7{1} §9 by {2} §7{3}\n" -msgstr "" - -msgid "§cUnable to locate any languages." -msgstr "" - -msgid "advanced command for getting island-data" -msgstr "" +msgid "§b{0}§d activated the island warp." +msgstr "§b{0}§d激活了岛屿传送。" -msgid "Ultimate SkyBlock Admin" -msgstr "" +#, java-format +msgid "§b{0}§d deactivated the island warp." +msgstr "§b{0}§d禁止了岛屿传送." -msgid "show player-information" -msgstr "" +msgid "§cYou do not have permission to enable/disable your island''s warp!" +msgstr "§c你没有权限启用/禁用岛屿的传送点!" -msgid "" -"§4Your island is full, or you have too many pending invites. You can't " -"invite anyone else." -msgstr "§4你的岛屿已经满员,或者你有太多等待邀请请求。你无法再邀请其他人。" +msgid "teleports you to the skyblock spawn" +msgstr "将你传送到空岛大厅" -msgid "§4That player is already leader on another island." -msgstr "§4那个玩家早就是另外岛屿的主人了。" +msgid "set your island''s warp location" +msgstr "设置岛屿的传送点位置" -#, java-format -msgid "§e{0}§e tried to invite you, but you are already in a party." -msgstr "§e{0}§e 尝试邀请你加入他的岛屿,但是你早就在其他团队了。" +msgid "§cYou do not have permission to set your island''s warp point!" +msgstr "§c你没有权限设置你岛屿的传送点!" -#, java-format -msgid "§aInvite sent to {0}" -msgstr "" +msgid "§cYou need to be on your own island to set the warp!" +msgstr "§c你需要在你的岛屿上才能设置传送点!" #, java-format -msgid "{0}§e has invited you to join their island!" -msgstr "{0}§e邀请你加入他们的岛屿。" +msgid "§b{0}§d changed the island warp location." +msgstr "§b{0}§d更改岛屿传送坐标." -msgid "§f/island [accept/reject]§e to accept or reject the invite." -msgstr "§f/island [accept/reject]§e 接受或者拒绝一个邀请。" +msgid "§4No Island. §eUse §b/is create§e to get one" +msgstr "§4未拥有岛屿.§e输入§b/is create§e创建一个岛屿" -msgid "§4WARNING: You will lose your current island if you accept!" -msgstr "§4警告: 你当前的岛屿将会被清空,如果你接受请求的话。" +msgid "transfer leadership to another member" +msgstr "将领导权移交给另一名成员" -#, java-format -msgid "{0}§d invited {1}" -msgstr "{0}§d邀请了{1}" +msgid "§4You can only transfer ownership to party-members!" +msgstr "§4你只能将岛主的权限转移给成员玩家!" #, java-format -msgid "{0}§e has rejected the invitation." -msgstr "{0}§e拒绝了邀请" - -msgid "§4You can't use that command right now. Leave your current party first." -msgstr "§4你无法使用当前命令。请先离开你当前的团队。" - -msgid "" -"§aYou have joined an island! Use /island party to see the other members." -msgstr "§a你加入了一个岛屿!输入/island party 来查看团队成员。" +msgid "{0}§e is already leader of your island!" +msgstr "{0}§e早就是你们岛屿的成员了!" -#, java-format -msgid "§eInvitation for {0}§e has timedout or been cancelled." -msgstr "§e来自{0}§e的邀请已经过时或被取消。" +msgid "§4Only leader can transfer leadership!" +msgstr "§4只有岛主才能转移权限!" #, java-format -msgid "§eInvitation for {0}''s island has timedout or been cancelled." -msgstr "§e来自{0}§e岛屿的邀请已经过时或被取消。" - -msgid "§eYou have accepted the invitation to join an island." -msgstr "§e你接受了加入岛屿的邀请." +msgid "{0} tried to take over the island!" +msgstr "{0}试图接管当前岛屿!" -msgid "§4You haven't been invited." -msgstr "§4你没有被任何人邀请." +msgid "ban/unban a player from your island." +msgstr "封禁/解禁您所在岛屿的玩家。" -msgid "§eYou have rejected the invitation to join an island." -msgstr "§e你拒接了加入岛屿的邀请" +msgid "exempts user from being banned" +msgstr "使用户免于被封禁" -msgid "general island command" -msgstr "" +msgid "§eThe following players are banned from warping to your island:" +msgstr "§e下列玩家被禁止传送到你的岛屿:" -msgid "allows user to bypass cooldowns" -msgstr "" +msgid "§eTo ban/unban from your island, use /island ban " +msgstr "§e禁止/允许一个玩家前往你的岛屿,输入§c/island ban §a<玩家名字>" -msgid "allows user to bypass visitor-protections" -msgstr "" +msgid "§4You can't ban members. Remove them first!" +msgstr "§4你无法封禁玩家.请先将他们从你的岛屿上移除!" -msgid "allows user to bypass teleport-delay" -msgstr "" +msgid "§4You do not have permission to kick/ban players." +msgstr "§4你没有权限踢出/禁止一个玩家" -msgid "allows user to use [usb] signs" -msgstr "" +#, java-format +msgid "§eUnable to ban unknown player {0}" +msgstr "§e无法禁止未知玩家{0}" -msgid "allows user to place [usb] signs" -msgstr "" +#, java-format +msgid "§4{0} tried to ban you from their island!" +msgstr "§4{0}试图禁止你进入他们的岛屿!" -msgid "complete and list challenges" -msgstr "" +#, java-format +msgid "§4{0} is exempt from being banned." +msgstr "§4{0}免于被禁止。" -msgid "§cCommand only available for players." -msgstr "" +#, java-format +msgid "§eYou have banned §4{0}§e from warping to your island." +msgstr "§e你禁止了玩家§4{0}§e前往你的岛屿上." -msgid "§eChallenges has been disabled. Contact an administrator." -msgstr "§e挑战不可用,请联系服务器管理员." +#, java-format +msgid "§eYou have been §cBANNED§e from {0}§e''s island." +msgstr "§e你已被{0}§e岛的§c禁止§e。" -msgid "§4You can only submit challenges in the skyblock world!" -msgstr "§4你只能在空岛世界提交挑战。" +#, java-format +msgid "§eYou have unbanned §a{0}§e from warping to your island." +msgstr "§e你解除了玩家§a{0}§e的封禁状态,现在他可以访问你的岛屿了." -msgid "§4You can only submit challenges when you have an island!" -msgstr "§4你只能在拥有一个岛屿的情况下提交挑战。" +#, java-format +msgid "§eYou have been §aUNBANNED§e from {0}§e''s island." +msgstr "§e您已被{0}§e岛禁止入境。" msgid "warp to another player''s island" -msgstr "" +msgstr "传送到另一个玩家的岛屿" msgid "§aYour incoming warp is active, players may warp to your island." msgstr "§a你启用了传送点,玩家可以传送到你的岛屿上了." @@ -2196,7 +2033,7 @@ msgid "§4Your incoming warp is inactive, players may not warp to your island." msgstr "§4你禁用了传送点,玩家无法传送到你的岛屿上." msgid "§fSet incoming warp to your current location using §e/island setwarp" -msgstr "§f设置岛屿传送点在你当前位置上请收入与§e/island setwarp" +msgstr "§f要设置岛屿传送点在你当前位置上,请输入§e/island setwarp" msgid "§fToggle your warp on/off using §e/island togglewarp" msgstr "§f启用/禁止岛屿的传送点请输入§e/island togglewarp" @@ -2213,7 +2050,7 @@ msgstr "§4你没有权限传送到其他玩家的岛屿上" msgid "" "§cYour island is in the process of generating, you cannot warp to other " "players islands right now." -msgstr "" +msgstr "§c你的岛屿正在生成过程中,你现在不能传送到其他玩家的岛屿。" msgid "§4That player does not exist!" msgstr "§4玩家不存在!" @@ -2224,23 +2061,126 @@ msgstr "§4玩家未拥有激活的传送点." msgid "" "§cThat players island is in the process of generating, you cannot warp to it " "right now." -msgstr "" +msgstr "§c玩家岛正在生成过程中,您现在不能传送到他。" #, java-format msgid "§cWARNING: §9{0}§e is warping to your island!" -msgstr "" +msgstr "§c警告:§9{0}§e正在传送到你的岛屿!" msgid "§4That player has forbidden you from warping to their island." msgstr "§4玩家禁止你传送到他们的岛屿上." -msgid "§4No Island. §eUse §b/is create§e to get one" -msgstr "§4未拥有岛屿.§e输入§b/is create§e创建一个岛屿" +msgid "create an island" +msgstr "创建一个岛屿" + +msgid "exempt player from create-cooldown" +msgstr "免除玩家创建冷却时间" + +msgid "" +"§4Island found!§e You already have an island. If you want a fresh island, " +"type§b /is restart§e to get one" +msgstr "" +"§4岛屿已存在!§e你已经拥有一个岛屿了,你可以输入§b /is restart §e来重新开始岛" +"屿生涯." + +msgid "" +"§4Island found!§e You are already a member of an island. To start your own, " +"first§b /is leave" +msgstr "" +"§4岛屿已存在!§e你已经加入了一个岛屿.如果你想开始一个属于自己的岛屿生涯,请先" +"输入§b /is leave §e来离开当前团队." + +#, java-format +msgid "§eYou can create a new island in {0,number,#} seconds." +msgstr "§e你需要等待{0,number,#}秒才能创建一个新的岛屿." + +msgid "show party information" +msgstr "显示聚会信息" + +msgid "shows information about your party" +msgstr "显示有关您聚会的信息" + +msgid "show pending invites" +msgstr "显示待处理的邀请" + +msgid "§eNo pending invites" +msgstr "§e没有等待的邀请" + +msgid "withdraw an invite" +msgstr "撤回邀请" + +msgid "§4You don't have permissions to uninvite players." +msgstr "§4您无权邀请未受邀请的玩家。" + +msgid "teleport to the island home" +msgstr "传送到岛上的家" + +msgid "" +"§cYour island is in the process of generating, you cannot teleport home " +"right now." +msgstr "§c你的岛屿正在生成过程中,你现在无法传送回家。" + +msgid "check your or anothers island level" +msgstr "查看你的岛屿等级" + +msgid "allows user to query for others levels" +msgstr "允许用户查询其他级别" + +#, java-format +msgid "§eInformation about {0}''s Island:" +msgstr "§e有关{0}岛屿的信息:" + +#, java-format +msgid "§9Rank is {0}" +msgstr "§9排名: {0}" + +#, java-format +msgid "§4Could not locate rank of {0}" +msgstr "§4无法定位{0}的排名" + +msgid "display the top10 of islands" +msgstr "显示前10个岛屿" + +msgid "enables user to all-ways generate top-ten (no caching)" +msgstr "使用户能够以各种方式生成前十名(无缓存)" + +msgid "trust/untrust a player to help on your island." +msgstr "信任/不信任玩家在你的岛上帮忙。" + +msgid "§eThe following players are trusted on your island:" +msgstr "§e在您的岛屿上,以下玩家值得信赖:" + +msgid "§eThe following leaders trusts you:" +msgstr "§e以下领导信任您:" + +msgid "§eTo trust/untrust from your island, use /island trust " +msgstr "§e要信任/不信任您的岛屿,请使用/岛屿信任" + +msgid "§4Members are already trusted!" +msgstr "§4会员已被信任!" + +#, java-format +msgid "§4Unknown player {0}" +msgstr "§4未知玩家{0}" + +#, java-format +msgid "§eYou are now trusted on §4{0}''s §eisland." +msgstr "§e您现在在§4{0}的§eisland上受到信任。" + +#, java-format +msgid "§a{0} trusted {1} on the island" +msgstr "§岛上{0}个受信任的{1}" + +#, java-format +msgid "§eYou are no longer trusted on §4{0}''s §eisland." +msgstr "§e您在§4{0}的§eisland上不再被信任。" -msgid "accept/reject an invitation." -msgstr "" +#, java-format +msgid "§c{0} revoked trust in {1} on the island" +msgstr "§c{0}撤销了对岛上{1}的信任" msgid "leave your party" -msgstr "" +msgstr "离开你的聚会" msgid "" "§4You can't leave your island if you are the only person. Try using /island " @@ -2250,7 +2190,7 @@ msgstr "" "restart" msgid "§eYou own this island, use /island remove instead." -msgstr "§e你拥有了当前岛屿,输入/island remove <玩家ID> 来取代他" +msgstr "§e这个岛屿属于你,输入/island remove <玩家ID> 来转让归属权" msgid "§eYou have left the island and returned to the player spawn." msgstr "§e你离开了当前岛屿,并且传送到了玩家出生点." @@ -2262,66 +2202,39 @@ msgstr "§4{0}离开了你的岛屿!" msgid "§4You must be in the skyblock world to leave your party!" msgstr "§4你必须在岛屿世界才能离开你的团队!" -msgid "check your or anothers island level" -msgstr "" - -msgid "allows user to query for others levels" -msgstr "" - -msgid "§eYou must be on your island to use this command." -msgstr "§e你必须在你的岛屿上来使用这个指令." - -msgid "§4You do not have an island!" -msgstr "§4你还未拥有一个岛屿!" - -msgid "§4You do not have access to that command!" -msgstr "§4你没有权限使用这个指令!" - -msgid "§4That player is invalid or does not have an island!" -msgstr "§4玩家不存在或仍未拥有一个岛屿!" - -#, java-format -msgid "§eInformation about {0}''s Island:" -msgstr "" - -#, java-format -msgid "§aIsland level is {0,number,###.##}" -msgstr "§a岛屿等级: {0,number,###.##}" - -#, java-format -msgid "§9Rank is {0}" -msgstr "§9排名: {0}" - -#, java-format -msgid "§4Could not locate rank of {0}" -msgstr "" - -msgid "create an island" -msgstr "" +msgid "delete your island and start a new one." +msgstr "删除你的岛屿,开始一个新的岛屿。" -msgid "exempt player from create-cooldown" -msgstr "" +msgid "exempt player from restart-cooldown" +msgstr "免除玩家重新启动冷却时间" msgid "" -"§4Island found!§e You already have an island. If you want a fresh island, " -"type§b /is restart§e to get one" +"§4Only the owner may restart this island. Leave this island in order to " +"start your own (/island leave)." msgstr "" -"§4岛屿已存在!§e你早就拥有一个岛屿了.如果你想要一个权限的岛屿,输入§b /is " -"restart §e来重新开始岛屿生涯." +"§4只有岛主才能重置岛屿.若要离开当前岛屿开始属于你自己的岛屿生涯请输入(/" +"island leave)" msgid "" -"§4Island found!§e You are already a member of an island. To start your own, " -"first§b /is leave" +"§eYou must remove all players from your island before you can restart it (/" +"island kick ). See a list of players currently part of your island " +"using /island party." msgstr "" -"§4岛屿已存在!§e你已经加入了一个岛屿.如果你想开始一个属于自己的岛屿生涯,请先" -"输入§b /is leave §e来离开当前团队." +"§e想要重新开始你的岛屿,你必须移除所有岛屿成员(/island kick <玩家ID>).查看当" +"前岛屿玩家列表请输入/island party" #, java-format -msgid "§eYou can create a new island in {0,number,#} seconds." -msgstr "§e你需要等待{0,number,#}秒才能创建一个新的岛屿." +msgid "§cYou can restart your island in {0} seconds." +msgstr "§c您可以在{0}秒内重新启动您的岛屿。" + +msgid "§cYour island is in the process of generating, you cannot restart now." +msgstr "§c您的岛屿正在生成过程中,现在无法重新启动。" + +msgid "§eNOTE: Your entire island and all your belongings will be RESET!" +msgstr "§e注意: 你所有财产和你的岛屿将被§c§l重置" msgid "invite a player to your island" -msgstr "" +msgstr "邀请玩家到您的岛屿" msgid "" "§eUse§f /island invite §e to invite a player to your island." @@ -2349,440 +2262,362 @@ msgstr "§4你无法邀请你自己!" msgid "§4That player is the leader of your island!" msgstr "§4玩家是你当前所处岛屿的主人!" -msgid "lock your island to non-party members." -msgstr "" - -msgid "§4You do not have permission to lock your island!" -msgstr "§4你没有权限锁定你的岛屿!" - -msgid "§4You don't have access to this command!" -msgstr "§4你无法使用当前指令。" - -msgid "§4You do not have permission to unlock your island!" -msgstr "§4你没有权限解锁你的岛屿!" - -msgid "display the top10 of islands" -msgstr "" - -msgid "enables user to all-ways generate top-ten (no caching)" -msgstr "" - -msgid "remove a member from your island." -msgstr "" - -msgid "§4You do not have permission to kick others from this island!" -msgstr "§4你没有在当前岛屿踢出玩家的权限!" - -msgid "§4You can't remove the leader from the Island!" -msgstr "§4你无法将岛屿的主人移除!" - -msgid "§4Stop kickin' yourself!" -msgstr "§4请不要尝试踢出你自己!" +msgid "display log" +msgstr "显示日志" -#, java-format -msgid "§4{0} has removed you from their island!" -msgstr "" +msgid "show the islands limits" +msgstr "查询岛屿生物/方块数量限制" #, java-format -msgid "§4{0} has been removed from the island." -msgstr "§4{0}被从当前岛屿上移除." +msgid " §f{0}x §7{1}" +msgstr "§f{0}x §7{1}" #, java-format -msgid "§4{0} tried to kick you from their island!" -msgstr "" +msgid "§eStill the following blocks short: {0}" +msgstr "§e任然缺少以下方块: {0}" #, java-format -msgid "§4{0} is exempt from being kicked." -msgstr "" +msgid "" +"§eProgress: {0,number,##}% ({1}/{2} - success:{3}, failed:{4}, skipped:{5}) " +"~ {6}" +msgstr "§e进程: {0,number,##}% ({1}/{2} - 成功:{3},失败:{4}, 跳过:{5}) ~ {6}" #, java-format -msgid "§4{0} has kicked you from their island!" -msgstr "" +msgid "§4No importer named §e{0}§4 found" +msgstr "§4输入的名字§e{0}§4未找到" #, java-format -msgid "§4{0} has been kicked from the island." -msgstr "§4{0}被从当前岛屿上踢出." +msgid "§eConverted {0}/{1} files in {2}" +msgstr "§在{2}中转换了 {0}/{1} 个文件" -msgid "§4That player is not part of your island group, and not on your island!" -msgstr "§4玩家不是你岛屿上的一员,并且不在你的岛屿上!" +msgid "§9Hold your horses! You have to be patient..." +msgstr "§9把你的马牵起来!你必须耐心点..." -msgid "teleport to the island home" -msgstr "" +msgid "§9Not really patient, are you?" +msgstr "§9你真的没有耐心,是吗?" -msgid "" -"§cYour island is in the process of generating, you cannot teleport home " -"right now." -msgstr "" +msgid "§9Be patient, young padawan" +msgstr "§9耐心点,年轻学徒" -msgid "§4This command can only be executed by a player" -msgstr "§4这个指令只能由玩家来执行." +msgid "§9Patience you MUST have, young padawan" +msgstr "§9你必须有耐心,年轻的学徒" -msgid "transfer leadership to another member" -msgstr "" +msgid "§9The two most powerful warriors are patience and time." +msgstr "§9最强大的两个战士是耐心和时间。" -msgid "§4You can only transfer ownership to party-members!" -msgstr "§4你只能将岛屿主人的权限转移给他们的成员玩家!" +msgid "§4Unable to find a safe home-location on your island!" +msgstr "§4在你的岛上找不到安全的家!" -#, java-format -msgid "{0}§e is already leader of your island!" -msgstr "{0}§e早就是你们岛屿的成员了!" +msgid "§cWARNING: §eTeleporting you to mid-air." +msgstr "§c警告:§e将你吊到半空中。" -msgid "§4Only leader can transfer leadership!" -msgstr "§4只有岛屿的主人才能转移权限!" +msgid "§aTeleporting you to your island." +msgstr "§a将你传送回自己的岛屿." #, java-format -msgid "{0} tried to take over the island!" -msgstr "{0}试图接管当前岛屿!" - -msgid "show party information" -msgstr "" - -msgid "shows information about your party" -msgstr "" - -msgid "show pending invites" -msgstr "" - -msgid "§eNo pending invites" -msgstr "§e没有等待的邀请" - -msgid "withdraw an invite" -msgstr "" +msgid "§aYou will be teleported in {0} seconds." +msgstr "§a将在 {0} 秒后进行传送." -msgid "§4You don't have permissions to uninvite players." -msgstr "" +msgid "§4Unable to warp you to that player''s island!" +msgstr "§4无法传送至玩家的岛屿!" -msgid "check your or another''s island info" -msgstr "" +#, java-format +msgid "§aTeleporting you to {0}''s island." +msgstr "§a将您传送到 {0}的岛屿。" -msgid "allows user to see others island info" -msgstr "" +msgid "§7Teleport cancelled" +msgstr "§7 Teleport已取消" -msgid "§4Hold your horses! §eYou have to be patient..." -msgstr "" +msgid "§eSending you to spawn." +msgstr "§e将你传送到空岛大厅" -#, java-format -msgid "§eBlocks on {0}s Island (page {1,number} of {2,number}):" -msgstr "eBlocks on {0}s Island (page {1,number} of {2,number}):" +msgid "§eThe island has been §cLOCKED§e." +msgstr "§e该岛已被锁定。" -msgid "Score Count Block" -msgstr "计分方块" +msgid "§eVisitors can't drop items!" +msgstr "§e非岛屿成员不能在此丢弃物品!" #, java-format -msgid "{0,number,00.00} {1,number,#} {2}" -msgstr "" +msgid "Owner: {0}" +msgstr "所有者: {0}" -msgid "trust/untrust a player to help on your island." -msgstr "" +msgid "You cannot pick up other players' loot when you are a visitor!" +msgstr "非岛屿成员不能拾取此岛屿成员丢弃的物品!" -msgid "§eThe following players are trusted on your island:" -msgstr "" +msgid "§cWither Despawned!§e It wandered too far from your island." +msgstr "§c凋零已被清理!§e它离你的岛太远了。" -msgid "§eThe following leaders trusts you:" -msgstr "" +msgid "§4You can only convert obsidian once every 10 seconds" +msgstr "§4每10秒只能转换一次黑曜石" -msgid "§eTo trust/untrust from your island, use /island trust " -msgstr "" +msgid "§eChanging your obsidian back into lava. Be careful!" +msgstr "§e已经将你的黑曜石变为岩浆.请务必当心!" -msgid "§4Members are already trusted!" -msgstr "" +msgid "§eYour inventory must have another empty space!" +msgstr "§e您的库存必须有另一个空位!" -#, java-format -msgid "§4Unknown player {0}" -msgstr "" +msgid "§4It''s a bad idea to replace your lava!" +msgstr "§4替换岩浆真是一个§f'不错'§4的注意呢~" -#, java-format -msgid "§eYou are now trusted on §4{0}''s §eisland." -msgstr "" +msgid "§eYou cannot hurt island-members." +msgstr "§e你不能伤害岛屿成员。" -#, java-format -msgid "§a{0} trusted {1} on the island" -msgstr "" +msgid "§4That player has forbidden you from teleporting to their island." +msgstr "§4玩家禁止你传送到他们的岛屿上." -#, java-format -msgid "§eYou are no longer trusted on §4{0}''s §eisland." -msgstr "" +msgid "§4That island is §clocked.§e No teleporting to the island." +msgstr "§4目标岛屿已锁定。§e不得传送到岛上。" #, java-format -msgid "§c{0} revoked trust in {1} on the island" -msgstr "" - -msgid "delete your island and start a new one." -msgstr "" - -msgid "exempt player from restart-cooldown" -msgstr "" - msgid "" -"§4Only the owner may restart this island. Leave this island in order to " -"start your own (/island leave)." -msgstr "" -"§4只有岛屿的主人才能重新开始岛屿.离开当前岛屿开始属于你自己的岛屿生涯请输入(/" -"island leave)" +"§4{0} is limited. §eScanning your island to see if you are allowed to place " +"more, please be patient" +msgstr "§4{0}已达上限。§e检查你的到,看看是否允许您放置更多,请耐心等待" -msgid "" -"§eYou must remove all players from your island before you can restart it (/" -"island kick ). See a list of players currently part of your island " -"using /island party." -msgstr "" -"§e想要重新开始你的岛屿,你必须移除所有岛屿成员(/island kick <玩家ID>).查看当" -"前岛屿玩家列表请输入/island party" +msgid "§e... Scanning complete, you can try again" +msgstr "§e...扫描完成,您可以重试" #, java-format -msgid "§cYou can restart your island in {0} seconds." -msgstr "" - -msgid "§cYour island is in the process of generating, you cannot restart now." +msgid "" +"§4You''ve hit the {0} limit!§e You can''t have more of that type on your " +"island!§9 Max: {1,number}" msgstr "" +"§4 您已达到 {0} 限制!§e 您的岛上不能有更多该类型的东西!§9 最大值:" +"{1,number}" -msgid "§eNOTE: Your entire island and all your belongings will be RESET!" -msgstr "§e注意: 你所有财产和你的岛屿将被§c§l重置" +msgid "§eYou can only use spawn-eggs on your own island." +msgstr "§e您只能在自己的岛上使用刷怪蛋" -msgid "teleports you to the skyblock spawn" -msgstr "" +msgid "§cYou have reached your spawn-limit for your island." +msgstr "§c您的岛屿已达到刷怪数量限制" -msgid "change the biome of the island" -msgstr "" +msgid "§eYou can not use another island''s portals!" +msgstr "§e你不能使用其他岛屿的物品!" -msgid "exempt player from biome-cooldown" -msgstr "" +msgid "§eVillager-trading isn't allowed." +msgstr "§e村民交易已禁止。" -#, java-format -msgid "Let the player change their islands biome to {0}" -msgstr "" +msgid "§eTrading isn't allowed on other islands. Do it in spawn." +msgstr "§e在其他岛屿时不允许交易。回到大厅中执行此操作。" -msgid "" -"§cYou do not have permission to change the biome of your current island." -msgstr "§c你没有权限更改你当前岛屿的生物群系." +msgid "§eRiding is only allowed on your own island!" +msgstr "§e在您自己的岛屿上才允许骑行!" -msgid "§4You do not have permission to change the biome of this island!" -msgstr "§4你没有权限去变更岛屿的生物群系!" +msgid "§eYou cannot break vehicles while being a visitor!" +msgstr "§e非岛屿成员不能破坏矿车!" -msgid "§eYou must be on your island to change the biome!" -msgstr "§e你必须在你的岛屿上才能更改生物群系!" +msgid "§cBanned:§e You are banned from this island." +msgstr "§c禁止:§e你被禁止进入这个岛。" -#, java-format -msgid "§cYou have misspelled the biome name. Must be one of {0}" -msgstr "" +msgid "§cLocked:§e That island is locked! No entry allowed." +msgstr "§c锁定:§e此空岛已锁定!不允许进入。" #, java-format -msgid "§eYou can change your biome again in {0,number,#} minutes." -msgstr "§e你需要等待{0,number,#}分钟才能再次更改生物群系." +msgid "{0}''s Wither" +msgstr "{0}的凋零" -msgid "§cYou do not have permission to change your biome to that type." -msgstr "" +msgid "Something went wrong saving the island and/or party data!" +msgstr "在存储岛屿或者团队记录的时候发生了一些错误!" -#, java-format -msgid "" -"§7The pixies are busy changing the biome near you to §9{0}§7, be patient." -msgstr "" +msgid "§cMAINTENANCE:§e uSkyBlock is currently in maintenance mode" +msgstr "§c维护:§uSkyBlock目前处于维护模式" #, java-format msgid "" -"§7The pixies are busy changing the biome in your current chunk to §9{0}§7, " -"be patient." -msgstr "" - -#, java-format -msgid "§eInvalid biome {0} supplied!" +"§buSkyBlock§e depends on §9{0}§e >= §av{1}§e but only §cv{2}§e was found!\n" msgstr "" +"“§buSkyBlock插件§e 依赖于 §9{0}§e >= §av{1}§e,但只发现了 §cv{2}§e!”\n" #, java-format -msgid "§aYou have changed your island''s biome to {0}" -msgstr "" +msgid "§buSkyBlock§e depends on §9{0}§e >= §av{1}" +msgstr "§buSkyBlock§e取决于§9{0}§e>=§av{1}" -#, java-format -msgid "{0} changed the island biome to {1}" -msgstr "{0}将岛屿的生物群系更改为{1}" +msgid "§4Player is already assigned to this island!" +msgstr "§4玩家已经分配给这个岛屿了!" -#, java-format -msgid "§aYou have changed {0} blocks around you to the {1} biome" -msgstr "" +msgid "§cYour island is in the process of generating, you cannot create now." +msgstr "§c你的岛屿正在生成过程中,你现在无法创建。" -#, java-format -msgid "{0} created an area with {1} biome" -msgstr "" +msgid "Could not create your Island. Please contact a server moderator." +msgstr "无法为你创建岛屿.请联系管理员." -msgid "set your island''s warp location" -msgstr "" +msgid "§eGetting your island ready, please be patient, it can take a while." +msgstr "§e准备好你的岛屿,请耐心等待,这可能需要一段时间。" -msgid "§cYou do not have permission to set your island''s warp point!" -msgstr "§c你没有权限设置你岛屿的传送点!" +msgid "§cCommand is currently disabled!" +msgstr "§cCommand当前已禁用!" -msgid "§cYou need to be on your own island to set the warp!" -msgstr "§c你需要在你的岛屿上才能设置传送点!" +msgid "North" +msgstr "北" -#, java-format -msgid "§b{0}§d changed the island warp location." -msgstr "§b{0}§d更改岛屿传送坐标." +msgid "North-East" +msgstr "东北" -msgid "teleports you to your island (or create one)" -msgstr "" +msgid "East" +msgstr "东" -msgid "ban/unban a player from your island." -msgstr "" +msgid "South-East" +msgstr "东南" -msgid "exempts user from being banned" -msgstr "" +msgid "South" +msgstr "南" -msgid "§eThe following players are banned from warping to your island:" -msgstr "§e下列玩家被禁止传送到你的岛屿:" +msgid "South-West" +msgstr "西南" -msgid "§eTo ban/unban from your island, use /island ban " -msgstr "§e禁止/允许一个玩家前往你的岛屿,输入§c/island ban §a<玩家名字>" +msgid "West" +msgstr "西" -msgid "§4You can't ban members. Remove them first!" -msgstr "§4你无法封禁玩家.请先将他们从你的岛屿上移除!" +msgid "North-West" +msgstr "西北" -msgid "§4You do not have permission to kick/ban players." -msgstr "§4你没有权限踢出/禁止一个玩家" +msgid "N/A" +msgstr "无数据" #, java-format -msgid "§eUnable to ban unknown player {0}" -msgstr "" +msgid "§9{0}§7 timed out" +msgstr "§9{0}§7超时" #, java-format -msgid "§4{0} tried to ban you from their island!" -msgstr "" +msgid "" +"§eDoing §9{0}§e is §cRISKY§e. Repeat the command within §a{1}§e seconds to " +"accept!" +msgstr "§9{0}§e属于§c危险操作§e.请在§a{1}§e秒内重复命令以确认操作." #, java-format -msgid "§4{0} is exempt from being banned." -msgstr "" +msgid "§eWaiting for our turn §c{0,number,###}%" +msgstr "§e等待轮到我们§c{0,number,###}%" #, java-format -msgid "§eYou have banned §4{0}§e from warping to your island." -msgstr "§e你禁止了玩家§4{0}§e前往你的岛屿上." +msgid "§9Creating island...§e{0,number,###}%" +msgstr "§9创建岛屿...§e{0,number,###}%" -#, java-format -msgid "§eYou have been §cBANNED§e from {0}§e''s island." -msgstr "" +msgid "§4** You are entering a protected - but abandoned - island area." +msgstr "§4您正在进入一个受保护但被遗弃的岛屿地区" #, java-format -msgid "§eYou have unbanned §a{0}§e from warping to your island." -msgstr "§e你解除了玩家§a{0}§e的封禁状态,现在他可以访问你的岛屿了." +msgid "§d** You are entering §b{0}''s §disland." +msgstr "§d您正在进入§b{0}的§d岛屿" -#, java-format -msgid "§eYou have been §aUNBANNED§e from {0}§e''s island." -msgstr "" +msgid "§4** You are leaving an abandoned island." +msgstr "§4你离开了一个荒岛" -msgid "display log" -msgstr "" +#, java-format +msgid "§d** You are leaving §b{0}''s §disland." +msgstr "§d你离开了§b{0}的§d岛屿" -msgid "changes a members island-permissions" -msgstr "" +msgid "§eYour island is now locked. Only your party members may enter." +msgstr "§e你的岛屿当前处于锁定状态.只有团队成员才能进入." -#, java-format -msgid "§ePermissions for §9{0}§e:" -msgstr "" +msgid "§4You must be the party leader to lock your island!" +msgstr "§4你必须为岛屿主人才能锁定岛屿!" -msgid "§aON" +msgid "" +"§eYour island is unlocked and anyone may enter, however only you and your " +"party members may build or remove blocks." msgstr "" +"§e你岛屿当前处于未锁定状态,s所有人都可以访问你的岛屿,不过只有你和岛屿成员才" +"能进行建造." -msgid "§cOFF" -msgstr "" +msgid "§4You must be the party leader to unlock your island!" +msgstr "§4你必须得是岛屿的主人才能解锁你的岛屿!" -#, java-format -msgid "§7 - §6{0}§7 : {1}" -msgstr "" +msgid "" +"§cThe island owning this piece of nether is being deleted! Sending you to " +"spawn." +msgstr "§c 该拥有这片下界区域的岛屿正在被删除!将您传送至出生点。" -#, java-format -msgid "§cInvalid permission {0}. Must be one of {1}" -msgstr "" +msgid "§cThe island you are on is being deleted! Sending you to spawn." +msgstr "§c你所处的岛屿正在被删除!将你传送到出生点." #, java-format -msgid "§eToggled permission §9{0}§e for §9{1}§e to {2}" -msgstr "" +msgid "§eWALL OF FAME (page {0} of {1}):" +msgstr "§e岛屿排行 (页数 {0} - {1}):" #, java-format -msgid "§eUnable to toggle permission §9{0}§e for §9{1}" -msgstr "" - -msgid "set the island-home" -msgstr "" - -msgid "show the islands limits" -msgstr "" +msgid "§4Top ten list is empty! Only islands above level {0} is considered." +msgstr "§4前十名列表为空!仅考虑级别{0}以上的岛屿。" -msgid "enable/disable warping to your island." -msgstr "" +#, java-printf-format +msgid "§a#%2d §7(%5.2f): §e%s §7%s" +msgstr "§a#%2d §7(%5.2f): §e%s §7%s" -msgid "§4Your island is locked. You must unlock it before enabling your warp." -msgstr "§4你的岛屿已经锁定,在启用传送点之前请先解锁." +msgid "Click to warp to the island!" +msgstr "点击以传送到岛上!" #, java-format -msgid "§b{0}§d activated the island warp." -msgstr "" +msgid "§eYour rank is: §f{0}" +msgstr "§e你的排名是: §f{0}" -msgid "§cYou do not have permission to enable/disable your island''s warp!" -msgstr "§c你没有权限启用/禁用岛屿的传送点!" +msgid "§9Creating an island at your location" +msgstr "§9在您的位置创建一个岛屿" -msgid "try to complete a challenge" -msgstr "" +#, java-format +msgid "§9Creating an island §7{0}§9 of you" +msgstr "§9 创建一个属于你的岛屿 §7{0}§9" -msgid "show information about the challenge" -msgstr "" +msgid "The island has been created." +msgstr "岛屿已创建!" -msgid "§eRank: " -msgstr "" +#, java-format +msgid "§b{0}§d locked the island." +msgstr "§b{0}§d锁定了该岛。" -msgid "§4This Challenge is not repeatable!" -msgstr "§4当前任务不可重复。" +msgid "§4Since your island is locked, your incoming warp has been deactivated." +msgstr "§4因为你的岛屿处于锁定状态,岛屿传送点也将禁用." -msgid "§4You will lose all required items when you complete this challenge!" -msgstr "§4挑战所需物品将会从你背包内移除。" +#, java-format +msgid "§b{0}§d unlocked the island." +msgstr "§b{0}§d解锁了该岛。" #, java-format -msgid "" -"§4All required items must be placed on your island, within {0} blocks of you." -msgstr "" +msgid "§cSKY §f> §7 {0}" +msgstr "§cSKY§f>§7{0}" #, java-format -msgid "§eTo complete this challenge, use §f/c c {0}" -msgstr "" +msgid "§b{0}§d has been removed from the island group." +msgstr "§b{0}§d已从岛群中删除。" -msgid "§4Invalid challenge name! Use /c help for more information" -msgstr "§4错误的挑战名!输入/c help 获取更多信息" +#, java-format +msgid "§9{1} §7- {0}" +msgstr "§9{1} §7- {0}" -msgid "§eSending you to spawn." -msgstr "" +msgid "§aCongratulations! §eYour island has appeared." +msgstr "§a恭喜!§e您的岛屿已出现。" -msgid "§eThe island has been §cLOCKED§e." -msgstr "" +msgid "§cNote:§e Construction might still be ongoing." +msgstr "§c 注意:§e 建设工作可能仍在进行中。" -msgid "§9Hold your horses! You have to be patient..." -msgstr "" +msgid "Use §9/is h§r or the §9/is§r menu to go there." +msgstr "使用§9/is-h§r或§9/is§r菜单前往那里。" -msgid "§9Not really patient, are you?" -msgstr "" +#, java-format +msgid "Unable to locate schematic {0}, contact a server-admin" +msgstr "找不到蓝图 {0},请与服务器管理员联系" -msgid "§9Be patient, young padawan" -msgstr "" +#, java-format +msgid "§cWatchdog!§9 Unable to locate a chest within {0}, bailing out." +msgstr "§c看门狗!§9 无法在 {0} 内找到一个箱子,正在退出。" -msgid "§9Patience you MUST have, young padawan" -msgstr "" +msgid "UNKNOWN" +msgstr "未知" -msgid "§9The two most powerful warriors are patience and time." -msgstr "" +msgid "ANIMAL" +msgstr "动物" -msgid "§4Unable to find a safe home-location on your island!" -msgstr "" +msgid "MONSTER" +msgstr "怪物" -msgid "§cWARNING: §eTeleporting you to mid-air." -msgstr "" +msgid "VILLAGER" +msgstr "村民" -msgid "§aTeleporting you to your island." -msgstr "§a将你传送回自己的岛屿." +msgid "GOLEM" +msgstr "铁傀儡" #, java-format -msgid "§aYou will be teleported in {0} seconds." -msgstr "§a将在 {0} 秒后进行传送." - -msgid "§4Unable to warp you to that player''s island!" -msgstr "§4无法传送至玩家的岛屿!" +msgid "§c{0}" +msgstr "§c{0}" #, java-format -msgid "§aTeleporting you to {0}''s island." -msgstr "" - -msgid "§7Teleport cancelled" -msgstr "" +msgid "§7{0}: §a{1}§7 (max. {2})" +msgstr "§7{0}:§a{1}§7(最多{2})" diff --git a/uSkyBlock-Core/src/main/resources/biomes.yml b/uSkyBlock-Core/src/main/resources/biomes.yml new file mode 100644 index 000000000..bf00215b4 --- /dev/null +++ b/uSkyBlock-Core/src/main/resources/biomes.yml @@ -0,0 +1,173 @@ +biomes: + ocean: + displayItem: tropical_fish + name: Ocean + description: | + The ocean biome is the basic + starting biome for all islands. + Passive mobs like animals will + not spawn. Hostile mobs will + spawn normally. + + forest: + displayItem: spruce_sapling + name: Forest + description: | + The forest biome will allow + your island to spawn passive + mobs like animals (including + wolves). Hostile mobs will + spawn normally. + + desert: + displayItem: sand + name: Desert + description: | + The desert biome makes it so + that there is no rain or snow + on your island. Passive mobs + won't spawn. Hostile mobs will + spawn normally. + + jungle: + displayItem: jungle_sapling + name: Jungle + description: | + The jungle biome is bright + and colorful. Passive mobs + (including ocelots) will + spawn. Hostile mobs will + spawn normally. + + swamp: + displayItem: lily_pad + name: Swamp + description: | + The swamp biome is dark + and dull. Passive mobs + will spawn normally and + slimes have a small chance + to spawn at night depending + on the moon phase. + + taiga: + displayItem: snow + name: Taiga + description: | + The taiga biome has snow + instead of rain. Passive + mobs will spawn normally + (including wolves) and + hostile mobs will spawn. + + mushroom_fields: + displayItem: red_mushroom + name: Mushroom Fields + description: | + The mushroom biome is + bright and colorful. + Mooshrooms are the only + mobs that will spawn. + No other passive or + hostile mobs will spawn. + + nether_wastes: + displayItem: nether_brick + name: Nether Wastes + description: | + The nether wastes look + dark and dead. Some + mobs from the nether will + spawn in this biome + (excluding ghasts and + blazes). + + the_end: + displayItem: ender_eye + name: The End + description: | + The end biome gives your + island a special dark sky. + Only endermen will spawn + in this biome. + + plains: + displayItem: tall_grass + name: Plains + description: | + The plains biome has rain + instead of snow. Passive + mobs will spawn normally + (including horses) and + hostile mobs will spawn. + + windswept_hills: + displayItem: emerald_ore + name: Extreme Hills + description: | + The extreme hills biome. + Passive mobs will spawn + normally and hostile + mobs will spawn. + + flower_forest: + displayItem: rose_bush + name: Flower Forest + description: | + The flower forest biome. + Passive mobs will spawn + normally and hostile + mobs will spawn. + + savanna_plateau: + displayItem: acacia_sapling + name: Savanna Plateau + description: | + The savanna plateau biome. Passive + mobs will spawn normally, including + llamas, horses, and donkeys. + Hostile mobs will spawn. + + lush_caves: + displayItem: glow_berries + name: Lush Caves + description: | + The lush caves biome. Passive + mobs will spawn normally, including + axolotls and tropical fish. Hostile + mobs will spawn. + + lukewarm_ocean: + displayItem: tropical_fish + name: Lukewarm Ocean + description: | + The lukewarm ocean biome is an advanced + biome. Spawn includes cod, squid, dolphin, + puffer fish, and tropical fish. + + snowy_slopes: + displayItem: goat_horn + name: Snowy Slopes + description: | + The snowy slopes biome. Passive mobs include + rabbits and goats. Hostile mobs will spawn. + + deep_ocean: + displayItem: prismarine_shard + name: Deep Ocean + description: | + The deep ocean biome is an advanced + biome. Passive mobs like animals will + not spawn. Hostile mobs (including + Guardians) will spawn normally. + + snowy_plains: + displayItem: snow_block + name: Snowy Plains + description: | + The snowy plains biome is an advanced + biome. No passive mobs will spawn except + rabbits and polar bears. Strays can spawn + at night. + +version: 1 diff --git a/uSkyBlock-Core/src/main/resources/challenges.yml b/uSkyBlock-Core/src/main/resources/challenges.yml index 24aa1d83b..3c0d13931 100644 --- a/uSkyBlock-Core/src/main/resources/challenges.yml +++ b/uSkyBlock-Core/src/main/resources/challenges.yml @@ -1,3 +1,123 @@ +# ====================================================================================================================== +# +# Here follows the format and explanation for the challenge group and challenges setup. The single commented (#) are the +# most used settings while the double commented (##) are optional if you like to use those settings. +# +# Item type format +# Item types are defined as in the minecraft /give command, i.e. with their minecraft key and possible components in +# square brackets. For example, 'minecraft:diamond_sword[damage=42]'. Refer to the Minecraft wiki for more information: +# https://minecraft.wiki/w/Data_component_format. You can also use the command '/usb iteminfo' to get the information. +# +# This item type is supplemented with additional information depending on where it is used: +# +# display-item: +# An item to be displayed in a GUI. It only defines the item type as above, without any additional information. +# For example: 'cobblestone', 'minecraft:stone', 'diamond_sword[damage=42]'. +# +# item-requirement: :[;+] +# An item requirement for a challenge. The amount is the number of items required. The optional + is the number +# of items to add to the required amount for each repeat of the challenge. For example, 'cobblestone:64;+16' would +# require 64 cobblestone for the first completion and 80 cobblestone for the second completion. Other options are +# -, *, and / for subtraction, multiplication, and division, respectively. For example, 'cobblestone:64;*2' would +# require 64 cobblestone for the first completion, 128 cobblestone for the second completion, and 256 cobblestone for +# the third completion. +# +# item-reward: [{p=}]: +# An item reward for a challenge. The amount is the number of items to give. The optional {p=} is the +# probability of the item being given. For example, 'cobblestone:64' would give 64 cobblestone every time the challenge +# is completed, while '{p=0.1}cobblestone:64' would give 64 cobblestone 10% of the time. +# +# ====================================================================================================================== +# All challenges are defined in the ranks section. Each rank is a tier of challenges that players can complete. +# +# ranks: +# # [text] name of the challenge Rank. +# TierX: +# # [text] The name of the challenge rank that shows when you do /challenges (supports capitals and color codes). +# name: '&aCustom Challenges rank name' +# # [display-item] The item to be displayed in the challenge menu for completed challenges. +# displayItem: 'cyan_terracotta' +# # [integer] The time in hours before required items reset to default (this overwrites the main reset time) +# resetInHours: 20 +# # These requirements controls when a challenge group will be available to a player. +# requires: +# # [integer] The number of tasks per rank that can be left uncompleted to advance to the next rank. For example, +# if you have 4 challenges with a rankLeeway of 1, a player would only need to complete 3 to advance to +# the next rank. A rankLeeway of 0 would require them all. +# rankLeeway: 8 +# # [List[text]] Challenges that have to be completed before this group will available to a player. +# challenges: +# - a challenge name +# ====================================================================================================================== +# challenges: +# # [text] The name of the challenge. All challenge names should be lower-case. +# defaultchallenge: +# # [text] The name of the challenge that shows in /challenges (this supports capitals and color codes). +# name: '&a Default Challenge' +# # [text] The descriptions players see when they do /challenges +# description: +# # [onIsland/onPlayer/islandLevel] This defines whether the required blocks/items should be in the player's +# # inventory or on their island. When using onIsland, the player must within 10 blocks from the required blocks +# # on his island. When using islandLevel, the 'requiredItems' field should be the island level required. The +# # player must use /island level first to update their level. +# type: onPlayer +# ## type: islandLevel +# ## type: onIsland +# # [integer] Overrides the default radius of 10 blocks when using onIsland. +# ## radius: 20 +# # List[item-requirement] The items required to complete the challenge. +# requiredItems: +# - stone:64;+16 +# - cobblestone:64;+16 +# # List[block-requirement] The blocks required to complete the challenge. +# requiredBlocks: +# - stone:64 +# - cobblestone:64 +# # [true/false] If the challenge can be repeated or not. +# ## repeatable: true +# # [integer] The maximum number of times the challenge can be completed. Overrides the default repeatLimit. +# # A value of 0 means unlimited repeats. +# ## repeatLimit: 5 +# # [display-item] The item to be displayed in the challenge menu for completed challenges. +# displayItem: cobblestone +# # [integer] The time in hours before required items reset to default (overwrites the main and rank defaults). +# ## resetInHours: 4 +# # [true/false] Take required items on completing a challenge. +# ## takeItems: true +# # The rewards players get for completing the challenge +# reward: +# # [text] Description of the reward. +# text: 'Mossy cobblestone and an iron pickaxe with unbreaking 1' +# # [List[item-requirement]] A list of items given to the player for completing the challenge. +# items: +# - mossy_cobblestone:16 +# - iron_pickaxe[enchantments={levels:{unbreaking:1}}]:1 +# # [permission node] A permission granted for completion. Multiple permissions are space-separated. +# ## permission: 'test.permission' +# # [integer] How much currency to give for completion. (requires an economy plugin) +# ## currency: 0 +# # [integer] How much xp to give to the player for completion. +# ## xp: 0 +# # [List[Text]] Executes the given command upon completion. Prepend with "op" or "console" to run the commands +# as OP or from the Console. Examples: +# # Possible command arguments are: +# # {player} - The name of the player +# # {playerName} - The display name of the player +# # {challenge} - The name of the challenge +# # {position} - The position of the player +# # {party} - Execute the command once for each member of the party (substituting the name) +# ## commands: +# ## - 'op: me are the GOD of things' +# ## - 'console: give {party} aweseomestuff 32' +# # reward section to reward the player for completing a repeated challenge (any time after the first). The +# # structure is identical to the 'reward' section. +# repeatReward: +# text: 'Mossy cobblestone' +# items: +# - mossy_cobblestone:16 +# +# ====================================================================================================================== + # [true/false] Enable the use of the challenges command. allowChallenges: true @@ -18,53 +138,59 @@ requirePreviousRank: true # A rankLeeway of 0 would require them all. rankLeeway: 12 -#[integer] The time in hours before required items reset to default. (only if not specified in the challenges below) +# [integer] The time in hours before required items reset to default. (only if not specified in the challenges below) defaultResetInHours: 20 -#[color code] The color to use for uncompleted challenges in the list. +# [integer] The default radius in blocks when using onIsland. Can be overridden in the challenges below. +radius: 10 + +# [integer] The maximum number of times a challenge can be completed by default. 0 means unlimited. Can be overridden in the challenge definition below. +repeatLimit: 0 + +# [color code] The color to use for uncompleted challenges in the list. challengeColor: '&e' -#[color code] The color to use for completed challenges in the list. (non-repeatable) +# [color code] The color to use for completed challenges in the list. (non-repeatable) finishedColor: '&2' -#[color code] The color to use for completed challenges in the list. (repeatable) +# [color code] The color to use for completed challenges in the list. (repeatable) repeatableColor: '&a' -#[true/false] If true, enables vault to handle currency rewards. +# [true/false] If true, enables vault to handle currency rewards. enableEconomyPlugin: true # [true/false] If false, challenges are not reset on island creation (or restart) resetChallengesOnCreate: true # Material to show for locked challenges (i.e. STAINED_GLASS_PANE:14 for a red glass-pane, or 160:14) -lockedDisplayItem: RED_STAINED_GLASS_PANE +lockedDisplayItem: red_stained_glass_pane # Material to show for onIsland challenges when locked ISLAND: - lockedDisplayItem: BLUE_STAINED_GLASS_PANE + lockedDisplayItem: blue_stained_glass_pane # Material to show for islandLevel challenges when locked ISLAND_LEVEL: - lockedDisplayItem: BLACK_STAINED_GLASS_PANE + lockedDisplayItem: black_stained_glass_pane -# Whether or not to show the name of locked challenges +# Whether to show the name of locked challenges showLockedChallengeName: true # When creating your own challenges you will have # to uncomment the section below or the default challenges # will be re-added on every server restart. When altering # the default challenges this should be fine to leave. -#merge-ignore: +# merge-ignore: # - 'ranks' # -#=============================================== +# =============================================== # An explanation to setup your own challenges # can be found at the bottom of this file. -#=============================================== +# =============================================== ranks: Tier1: name: '&7Novice' - displayItem: CYAN_TERRACOTTA + displayItem: cyan_terracotta resetInHours: 20 challenges: cobblestonegenerator: @@ -72,24 +198,24 @@ ranks: description: Mine from a cobblestone generator. type: onPlayer requiredItems: - - COBBLESTONE:64;+2 - displayItem: COBBLESTONE - lockedDisplayItem: GRAY_STAINED_GLASS_PANE + - cobblestone:64;+2 + displayItem: cobblestone + lockedDisplayItem: gray_stained_glass_pane resetInHours: 12 reward: text: 3 leather, 20% chance to get a book items: - - LEATHER:3 - - '{p=0.2}BOOK:1' + - leather:3 + - '{p=0.2}book:1' currency: 10 xp: 10 commands: - - op:effect give {player} regeneration + - op:effect give {player} regeneration repeatReward: text: 1 leather, 10% chance to get a book items: - - LEATHER:1 - - '{p=0.1}BOOK:1' + - leather:1 + - '{p=0.1}book:1' currency: 5 xp: 5 applecollector: @@ -97,45 +223,45 @@ ranks: description: Collect apples from trees. type: onPlayer requiredItems: - - APPLE:2;+1 - displayItem: APPLE - lockedDisplayItem: BROWN_STAINED_GLASS_PANE + - apple:2;+1 + displayItem: apple + lockedDisplayItem: brown_stained_glass_pane resetInHours: 6 reward: text: 1 of each sapling, (4 dark oak) items: - - OAK_SAPLING:1 - - SPRUCE_SAPLING:1 - - BIRCH_SAPLING:1 - - JUNGLE_SAPLING:1 - - ACACIA_SAPLING:1 - - DARK_OAK_SAPLING:4 - - '{p=0.2}JUNGLE_SAPLING:3' + - oak_sapling:1 + - spruce_sapling:1 + - birch_sapling:1 + - jungle_sapling:1 + - acacia_sapling:1 + - dark_oak_sapling:4 + - '{p=0.2}jungle_sapling:3' currency: 20 xp: 20 repeatReward: text: 1 of each sapling (4 dark oak) items: - - OAK_SAPLING:1 - - SPRUCE_SAPLING:1 - - BIRCH_SAPLING:1 - - JUNGLE_SAPLING:1 - - ACACIA_SAPLING:1 - - DARK_OAK_SAPLING:4 - - '{p=0.1}JUNGLE_SAPLING:3' + - oak_sapling:1 + - spruce_sapling:1 + - birch_sapling:1 + - jungle_sapling:1 + - acacia_sapling:1 + - dark_oak_sapling:4 + - '{p=0.1}jungle_sapling:3' currency: 10 xp: 10 sugarplanter: name: '&9Sugar Planter' type: onIsland - displayItem: SUGAR_CANE - lockedDisplayItem: BROWN_STAINED_GLASS_PANE - requiredItems: - - SUGAR_CANE:4 + displayItem: sugar_cane + lockedDisplayItem: brown_stained_glass_pane + requiredBlocks: + - sugar_cane:4 reward: text: 4 dirt items: - - DIRT:4 + - dirt:4 currency: 20 xp: 20 sugarfarmer: @@ -143,22 +269,22 @@ ranks: description: Harvest sugarcane from a farm. type: onPlayer requiredItems: - - SUGAR_CANE:64;+16 + - sugar_cane:64;+16 offset: -1 - displayItem: SUGAR_CANE - lockedDisplayItem: BROWN_STAINED_GLASS_PANE + displayItem: sugar_cane + lockedDisplayItem: brown_stained_glass_pane reward: text: 8 dirt items: - - DIRT:8 - - '{p=0.1}BONE:1' + - dirt:8 + - '{p=0.1}bone:1' currency: 20 xp: 20 repeatReward: text: 4 dirt items: - - DIRT:4 - - '{p=0.05}DIRT:4' + - dirt:4 + - '{p=0.05}dirt:4' currency: 10 xp: 10 melonfarmer: @@ -166,19 +292,19 @@ ranks: description: Harvest slices of melon from a farm. type: onPlayer requiredItems: - - MELON_SLICE:128;+8 - displayItem: MELON - lockedDisplayItem: BROWN_STAINED_GLASS_PANE + - melon_slice:128;+8 + displayItem: melon + lockedDisplayItem: brown_stained_glass_pane reward: text: 8 dirt items: - - DIRT:8 + - dirt:8 currency: 20 xp: 20 repeatReward: text: 4 dirt items: - - DIRT:4 + - dirt:4 currency: 10 xp: 10 cactusfarmer: @@ -186,22 +312,22 @@ ranks: description: Harvest cacti from a farm. type: onPlayer requiredItems: - - CACTUS:64;+16 - displayItem: CACTUS - lockedDisplayItem: BROWN_STAINED_GLASS_PANE + - cactus:64;+16 + displayItem: cactus + lockedDisplayItem: brown_stained_glass_pane reward: text: 8 sand, 20% chance to get a bone items: - - SAND:8 - - '{p=0.2}BONE:1' - - '{p=0.1}WOODEN_HOE:1' + - sand:8 + - '{p=0.2}bone:1' + - '{p=0.1}wooden_hoe:1' currency: 20 xp: 20 repeatReward: text: 4 sand items: - - SAND:4 - - '{p=0.1}BONE:1' + - sand:4 + - '{p=0.1}bone:1' currency: 10 xp: 10 pumpkinfarmer: @@ -209,21 +335,21 @@ ranks: description: Harvest pumpkins from a farm. type: onPlayer requiredItems: - - PUMPKIN:64;+4 - displayItem: PUMPKIN - lockedDisplayItem: BROWN_STAINED_GLASS_PANE + - pumpkin:64;+4 + displayItem: pumpkin + lockedDisplayItem: brown_stained_glass_pane reward: text: 8 dirt items: - - DIRT:8 - - '{p=0.3}JACK_O_LANTERN:1' + - dirt:8 + - '{p=0.3}jack_o_lantern:1' currency: 20 xp: 20 repeatReward: text: 4 dirt items: - - DIRT:4 - - '{p=0.05}JACK_O_LANTERN:1' + - dirt:4 + - '{p=0.05}jack_o_lantern:1' currency: 10 xp: 10 stonebrickmaker: @@ -231,25 +357,25 @@ ranks: description: Make 64 Stone Bricks. type: onPlayer requiredItems: - - STONE_BRICKS:64;+8 - - STONE_BRICK_SLAB:30;+6 - - CHISELED_STONE_BRICKS:30;+6 - - STONE_BRICK_STAIRS:16;+4 - displayItem: STONE_BRICKS - lockedDisplayItem: GRAY_STAINED_GLASS_PANE + - stone_bricks:64;+8 + - stone_brick_slab:30;+6 + - chiseled_stone_bricks:30;+6 + - stone_brick_stairs:16;+4 + displayItem: stone_bricks + lockedDisplayItem: gray_stained_glass_pane reward: text: 4 redstone ore, 4 iron ore, 1 chicken items: - - REDSTONE_ORE:4 - - IRON_ORE:4 - - CHICKEN_SPAWN_EGG:1 + - redstone_ore:4 + - iron_ore:4 + - chicken_spawn_egg:1 currency: 30 xp: 30 repeatReward: text: 1 redstone ore, 1 iron ore items: - - REDSTONE_ORE:1 - - IRON_ORE:1 + - redstone_ore:1 + - iron_ore:1 currency: 15 xp: 15 novicebuilder: @@ -257,31 +383,31 @@ ranks: description: Reach island level 20. type: islandLevel requiredLevel: 20 - displayItem: COAL_ORE + displayItem: coal_ore reward: text: 8 dirt, 8 sand, 5 emeralds items: - - DIRT:8 - - SAND:8 - - EMERALD:5 - - DIAMOND:1 + - dirt:8 + - sand:8 + - emerald:5 + - diamond:1 currency: 20 xp: 20 commands: - - op:effect give {party} regeneration + - op:effect give {party} regeneration adeptbuilder: name: '&aAdept Builder' description: Reach island level 50. type: islandLevel requiredLevel: 50 - displayItem: IRON_ORE + displayItem: iron_ore offset: -1 reward: text: 10 obsidian, 2 diamonds, 5 emeralds items: - - OBSIDIAN:10 - - EMERALD:5 - - DIAMOND:2 + - obsidian:10 + - emerald:5 + - diamond:2 currency: 100 xp: 100 expertbuilder: @@ -289,15 +415,15 @@ ranks: description: Reach island level 100. type: islandLevel requiredLevel: 100 - displayItem: GOLD_ORE + displayItem: gold_ore offset: -1 reward: text: 16 dirt, 16 sand, 3 diamonds, 5 emeralds items: - - DIRT:16 - - SAND:16 - - EMERALD:5 - - DIAMOND:3 + - dirt:16 + - sand:16 + - emerald:5 + - diamond:3 currency: 250 xp: 250 masterbuilder: @@ -305,15 +431,15 @@ ranks: description: Reach island level 250. type: islandLevel requiredLevel: 250 - displayItem: DIAMOND_ORE + displayItem: diamond_ore offset: -1 reward: text: 32 dirt, 32 sand, 4 diamonds, 5 emeralds items: - - DIRT:32 - - SAND:32 - - EMERALD:5 - - DIAMOND:4 + - dirt:32 + - sand:32 + - emerald:5 + - diamond:4 currency: 500 xp: 500 skylord: @@ -321,55 +447,55 @@ ranks: description: Reach island level 500. type: islandLevel requiredLevel: 500 - displayItem: EMERALD_ORE + displayItem: emerald_ore offset: -1 reward: text: 64 dirt, 64 sand, 5 diamond, 5 emeralds items: - - DIRT:64 - - SAND:64 - - DIAMOND:5 - - EMERALD:5 + - dirt:64 + - sand:64 + - diamond:5 + - emerald:5 currency: 1000 xp: 1000 Tier2: name: '&aAdept' - displayItem: LIME_TERRACOTTA + displayItem: lime_terracotta resetInHours: 20 requires: # means disabled in effect rankLeeway: 99 challenges: - - cobblestonegenerator - - novicebuilder + - cobblestonegenerator + - novicebuilder challenges: lumberjack: name: '&3Lumberjack' description: Collect all types of wood logs. type: onPlayer requiredItems: - - OAK_LOG:16;+2 - - SPRUCE_LOG:16;+2 - - BIRCH_LOG:16;+2 - - JUNGLE_LOG:16;+2 - - ACACIA_LOG:16;+2 - - DARK_OAK_LOG:16;+2 - displayItem: OAK_LOG - lockedDisplayItem: CYAN_STAINED_GLASS_PANE + - oak_log:16;+2 + - spruce_log:16;+2 + - birch_log:16;+2 + - jungle_log:16;+2 + - acacia_log:16;+2 + - dark_oak_log:16;+2 + displayItem: oak_log + lockedDisplayItem: cyan_stained_glass_pane reward: text: 4 redstone ore, 4 iron ore, 1 wolf items: - - REDSTONE_ORE:4 - - IRON_ORE:4 - - WOLF_SPAWN_EGG:1 + - redstone_ore:4 + - iron_ore:4 + - wolf_spawn_egg:1 currency: 30 xp: 30 repeatReward: text: 1 redstone ore, 1 iron ore items: - - REDSTONE_ORE:1 - - IRON_ORE:1 + - redstone_ore:1 + - iron_ore:1 currency: 15 xp: 15 shroompicker: @@ -377,23 +503,23 @@ ranks: description: Collect red and brown mushrooms. type: onPlayer requiredItems: - - BROWN_MUSHROOM:64;+4 - - RED_MUSHROOM:64;+4 - displayItem: RED_MUSHROOM - lockedDisplayItem: BROWN_STAINED_GLASS_PANE + - brown_mushroom:64;+4 + - red_mushroom:64;+4 + displayItem: red_mushroom + lockedDisplayItem: brown_stained_glass_pane reward: text: 8 mycelium, 4 podzol items: - - MYCELIUM:8 - - PODZOL:4 + - mycelium:8 + - podzol:4 currency: 30 xp: 30 repeatReward: text: 4 mycelium items: - - MYCELIUM:4 - - '{p=0.02}MYCELIUM:4' - - '{p=0.02}PODZOL:2' + - mycelium:4 + - '{p=0.02}mycelium:4' + - '{p=0.02}podzol:2' currency: 15 xp: 15 potatofarmer: @@ -401,27 +527,27 @@ ranks: description: Harvest potato's from a farm. type: onPlayer requiredItems: - - POTATO:64;+16 - displayItem: POTATO - lockedDisplayItem: BROWN_STAINED_GLASS_PANE + - potato:64;+16 + displayItem: potato + lockedDisplayItem: brown_stained_glass_pane reward: text: 1 carrot, 4 dirt items: - - CARROT:1 - - DIRT:4 - - '{p=0.10}BEETROOT_SEEDS:1' - - '{p=0.05}RED_SAND:1' - - '{p=0.01}DIAMOND:1' + - carrot:1 + - dirt:4 + - '{p=0.10}beetroot_seeds:1' + - '{p=0.05}red_sand:1' + - '{p=0.01}diamond:1' currency: 50 xp: 50 repeatReward: text: 4 dirt and a baked potato items: - - DIRT:4 - - BAKED_POTATO:1 - - '{p=0.10}BEETROOT_SEEDS:1' - - '{p=0.05}RED_SAND:1' - - '{p=0.01}DIAMOND:1' + - dirt:4 + - baked_potato:1 + - '{p=0.10}beetroot_seeds:1' + - '{p=0.05}red_sand:1' + - '{p=0.01}diamond:1' currency: 25 xp: 25 carrotfarmer: @@ -429,25 +555,25 @@ ranks: description: Harvest carrot's from a farm. type: onPlayer requiredItems: - - CARROT:64;+16 - displayItem: CARROT - lockedDisplayItem: BROWN_STAINED_GLASS_PANE + - carrot:64;+16 + displayItem: carrot + lockedDisplayItem: brown_stained_glass_pane reward: text: a pig with saddle and a potato items: - - SADDLE:1 - - POTATO:1 - - PIG_SPAWN_EGG:1 - - '{p=0.05}RED_SAND:1' - - '{p=0.01}DIAMOND:1' + - saddle:1 + - potato:1 + - pig_spawn_egg:1 + - '{p=0.05}red_sand:1' + - '{p=0.01}diamond:1' currency: 50 xp: 50 repeatReward: text: 1 golden carrot items: - - GOLDEN_CARROT:1 - - '{p=0.05}SAND:1' - - '{p=0.01}DIAMOND:1' + - golden_carrot:1 + - '{p=0.05}sand:1' + - '{p=0.01}diamond:1' currency: 25 xp: 25 wheatfarmer: @@ -455,20 +581,20 @@ ranks: description: Harvest wheat from a farm. type: onPlayer requiredItems: - - WHEAT:64;+16 - displayItem: WHEAT - lockedDisplayItem: BROWN_STAINED_GLASS_PANE + - wheat:64;+16 + displayItem: wheat + lockedDisplayItem: brown_stained_glass_pane reward: text: 8 dirt items: - - DIRT:8 + - dirt:8 currency: 20 xp: 20 repeatReward: text: 4 dirt items: - - DIRT:4 - - '{p=0.2}BONE:1' + - dirt:4 + - '{p=0.2}bone:1' currency: 10 xp: 10 monsterfarm: @@ -476,32 +602,32 @@ ranks: description: Build a mob farm and collect mob loot. type: onPlayer requiredItems: - - ROTTEN_FLESH:64;+4 - - STRING:32;+2 - - ARROW:32;+2 - - BONE:32;+2 - - GUNPOWDER:16;+1 - - SPIDER_EYE:5 - displayItem: ROTTEN_FLESH - lockedDisplayItem: PURPLE_STAINED_GLASS_PANE + - rotten_flesh:64;+4 + - string:32;+2 + - arrow:32;+2 + - bone:32;+2 + - gunpowder:16;+1 + - spider_eye:5 + displayItem: rotten_flesh + lockedDisplayItem: purple_stained_glass_pane reward: text: 4 redstone ore, 4 iron ore and 1 flint items: - - REDSTONE_ORE:4 - - IRON_ORE:4 - - FLINT:1 - - '{p=0.10}POTATO:1' - - '{p=0.10}CARROT:1' + - redstone_ore:4 + - iron_ore:4 + - flint:1 + - '{p=0.10}potato:1' + - '{p=0.10}carrot:1' currency: 30 xp: 30 repeatReward: text: 1 redstone ore, 1 iron ore, 1 flint items: - - REDSTONE_ORE:1 - - IRON_ORE:4 - - FLINT:1 - - '{p=0.05}POTATO:1' - - '{p=0.05}CARROT:1' + - redstone_ore:1 + - iron_ore:4 + - flint:1 + - '{p=0.05}potato:1' + - '{p=0.05}carrot:1' currency: 15 xp: 15 homeowner: @@ -509,24 +635,24 @@ ranks: description: Build a house with furnishings. type: onIsland requiredChallenges: - - stonebrickmaker - requiredItems: - - RED_BED:1 - - CRAFTING_TABLE:1 - - GLASS:1 - - OAK_DOOR:1 - - FURNACE:1 - - BOOKSHELF:1 - - TORCH:1 - lockedDisplayItem: BLUE_STAINED_GLASS_PANE - displayItem: OAK_DOOR + - stonebrickmaker + requiredBlocks: + - red_bed:1 + - crafting_table:1 + - glass:1 + - oak_door:1 + - furnace:1 + - bookshelf:1 + - torch:1 + lockedDisplayItem: blue_stained_glass_pane + displayItem: oak_door reward: text: 4 redstone ore, 5 inksac, 4 iron ore and some seeds items: - - REDSTONE_ORE:4 - - IRON_ORE:4 - - INK_SAC:5 - - BEETROOT_SEEDS:1 + - redstone_ore:4 + - iron_ore:4 + - ink_sac:5 + - beetroot_seeds:1 currency: 40 xp: 40 netherportal: @@ -534,75 +660,75 @@ ranks: description: Build a nether portal on your island. type: onIsland requiredChallenges: - - adeptbuilder - - homeowner - requiredItems: - - OBSIDIAN:10 - - NETHER_PORTAL:1 - displayItem: OBSIDIAN - lockedDisplayItem: BLUE_STAINED_GLASS_PANE + - adeptbuilder + - homeowner + requiredBlocks: + - obsidian:10 + - nether_portal:1 + displayItem: obsidian + lockedDisplayItem: blue_stained_glass_pane reward: text: 1 iron pickaxe, 1 iron shovel items: - - IRON_SHOVEL:1 - - IRON_PICKAXE:1 + - iron_shovel:1 + - iron_pickaxe:1 currency: 40 xp: 40 nethermining: name: '&7Nether Mining' description: Mine from your Nether island. type: onPlayer - displayItem: NETHERRACK - lockedDisplayItem: GRAY_STAINED_GLASS_PANE + displayItem: netherrack + lockedDisplayItem: gray_stained_glass_pane offset: -1 requiredItems: - - NETHERRACK:64;+2 - - SOUL_SAND:16;+2 - - GRAVEL:16;+2 - - QUARTZ:32;+2 - - GLOWSTONE:16;+2 - - COARSE_DIRT:4;+2 + - netherrack:64;+2 + - soul_sand:16;+2 + - gravel:16;+2 + - quartz:32;+2 + - glowstone:16;+2 + - coarse_dirt:4;+2 reward: text: 1 ghast tear, 1 magic bow items: - - GHAST_TEAR:1 - - 'BOW:1 {Enchantments:[{id:"minecraft:infinity",lvl:1},{id:"minecraft:unbreaking",lvl:3}]}' + - ghast_tear:1 + - 'bow[enchantments={levels:{infinity:1,unbreaking:3}}]:1' currency: 40 xp: 40 repeatReward: text: a blaze-rod and a chance of ghast tear items: - - BLAZE_ROD:1 - - '{p=0.10}GHAST_TEAR:1' + - blaze_rod:1 + - '{p=0.10}ghast_tear:1' currency: 20 xp: 20 Tier3: name: '&eExpert' - displayItem: YELLOW_TERRACOTTA + displayItem: yellow_terracotta resetInHours: 20 requires: challenges: - - adeptbuilder + - adeptbuilder challenges: toolmaker: name: '&3Tool Maker' description: Make all stone tools type: onPlayer requiredChallenges: - - lumberjack + - lumberjack requiredItems: - - STONE_SHOVEL:1 - - STONE_PICKAXE:1 - - STONE_AXE:1 - - STONE_HOE:1 - displayItem: STONE_AXE - lockedDisplayItem: CYAN_STAINED_GLASS_PANE + - stone_shovel:1 + - stone_pickaxe:1 + - stone_axe:1 + - stone_hoe:1 + displayItem: stone_axe + lockedDisplayItem: cyan_stained_glass_pane reward: text: 4 redstone ore, 4 iron, 1 pig items: - - REDSTONE_ORE:4 - - IRON_ORE:4 - - PIG_SPAWN_EGG:1 + - redstone_ore:4 + - iron_ore:4 + - pig_spawn_egg:1 currency: 30 xp: 30 sawmill: @@ -611,31 +737,31 @@ ranks: type: onPlayer offset: -1 requiredChallenges: - - toolmaker + - toolmaker requiredItems: - - STRIPPED_OAK_LOG:8;+2 - - STRIPPED_SPRUCE_LOG:8;+2 - - STRIPPED_BIRCH_LOG:8;+2 - - STRIPPED_JUNGLE_LOG:8;+2 - - STRIPPED_ACACIA_LOG:8;+2 - - STRIPPED_DARK_OAK_LOG:8;+2 - displayItem: IRON_AXE - lockedDisplayItem: CYAN_STAINED_GLASS_PANE + - stripped_oak_log:8;+2 + - stripped_spruce_log:8;+2 + - stripped_birch_log:8;+2 + - stripped_jungle_log:8;+2 + - stripped_acacia_log:8;+2 + - stripped_dark_oak_log:8;+2 + displayItem: iron_axe + lockedDisplayItem: cyan_stained_glass_pane reward: text: 4 redstone ore, 4 iron, cocoa and a mule items: - - COCOA_BEANS:1 - - REDSTONE_ORE:4 - - IRON_ORE:4 - - MULE_SPAWN_EGG:1 + - cocoa_beans:1 + - redstone_ore:4 + - iron_ore:4 + - mule_spawn_egg:1 currency: 30 xp: 30 repeatReward: text: 1 redstone ore, 1 iron ore items: - - REDSTONE_ORE:1 - - IRON_ORE:1 - - '{p=0.05}GOLD_ORE:1' + - redstone_ore:1 + - iron_ore:1 + - '{p=0.05}gold_ore:1' currency: 15 xp: 15 torchmaker: @@ -643,62 +769,62 @@ ranks: description: Make 128 torches. type: onPlayer requiredChallenges: - - lumberjack + - lumberjack requiredItems: - - TORCH:128;+32 - displayItem: TORCH - lockedDisplayItem: CYAN_STAINED_GLASS_PANE + - torch:128;+32 + displayItem: torch + lockedDisplayItem: cyan_stained_glass_pane reward: text: 4 redstone ore, 4 iron ore items: - - REDSTONE_ORE:4 - - IRON_ORE:4 + - redstone_ore:4 + - iron_ore:4 currency: 30 xp: 30 repeatReward: text: 1 redstone ore, 1 iron ore items: - - REDSTONE_ORE:1 - - IRON_ORE:1 + - redstone_ore:1 + - iron_ore:1 currency: 15 xp: 15 expertfarmer: name: '&6Expert Farmer' description: Harvest many different farming resources. type: onPlayer - displayItem: IRON_HOE - lockedDisplayItem: BROWN_STAINED_GLASS_PANE + displayItem: iron_hoe + lockedDisplayItem: brown_stained_glass_pane requiredChallenges: - - applecollector - - melonfarmer - - pumpkinfarmer - - wheatfarmer - - carrotfarmer - - potatofarmer - - cactusfarmer - - sugarfarmer - - shroompicker + - applecollector + - melonfarmer + - pumpkinfarmer + - wheatfarmer + - carrotfarmer + - potatofarmer + - cactusfarmer + - sugarfarmer + - shroompicker requiredItems: - - MELON_SLICE:256;+2 - - SUGAR_CANE:128;+2 - - WHEAT:128;+2 - - POTATO:128;+2 - - CARROT:128;+2 - - PUMPKIN:128;+2 - - CACTUS:128;+2 - - BEETROOT:128;+2 + - melon_slice:256;+2 + - sugar_cane:128;+2 + - wheat:128;+2 + - potato:128;+2 + - carrot:128;+2 + - pumpkin:128;+2 + - cactus:128;+2 + - beetroot:128;+2 reward: text: a bucket, cocoa and a cow items: - - BUCKET:1 - - COCOA_BEANS:1 - - COW_SPAWN_EGG:1 + - bucket:1 + - cocoa_beans:1 + - cow_spawn_egg:1 currency: 50 xp: 50 repeatReward: text: a cow items: - - COW_SPAWN_EGG:1 + - cow_spawn_egg:1 currency: 25 xp: 25 fisherman: @@ -706,75 +832,75 @@ ranks: description: Catch different types of fish. type: onPlayer requiredItems: - - COD:5;+1 - - SALMON:5;+1 - - PUFFERFISH:3;+1 - - TROPICAL_FISH:1;+0 - - INK_SAC:5;+2 - displayItem: COD - lockedDisplayItem: PURPLE_STAINED_GLASS_PANE + - cod:5;+1 + - salmon:5;+1 + - pufferfish:3;+1 + - tropical_fish:1;+0 + - ink_sac:5;+2 + displayItem: cod + lockedDisplayItem: purple_stained_glass_pane reward: text: 10 lapis blocks, 20 prismarine, kelp, deep-ocean items: - - LAPIS_BLOCK:10 - - PRISMARINE:20 - - KELP:1 - - '{p=0.20}PRISMARINE_CRYSTALS:5' + - lapis_block:10 + - prismarine:20 + - kelp:1 + - '{p=0.20}prismarine_crystals:5' permission: usb.biome.deep_ocean currency: 50 xp: 50 repeatReward: text: 2 lapis, 5 prismarine, kelp and a chance at prismarine crystals items: - - LAPIS_LAZULI:2 - - PRISMARINE:5 - - KELP:1 - - '{p=0.20}PRISMARINE_CRYSTALS:5' + - lapis_lazuli:2 + - prismarine:5 + - kelp:1 + - '{p=0.20}prismarine_crystals:5' currency: 25 xp: 25 woolcollector: name: '&5Wool Collector' description: Collect every color of wool. type: onPlayer - displayItem: WHITE_WOOL - lockedDisplayItem: PURPLE_STAINED_GLASS_PANE + displayItem: white_wool + lockedDisplayItem: purple_stained_glass_pane requiredChallenges: - - monsterfarmer + - monsterfarmer requiredItems: - - WHITE_WOOL:2;+4 - - ORANGE_WOOL:2;+4 - - MAGENTA_WOOL:2;+4 - - LIGHT_BLUE_WOOL:2;+4 - - YELLOW_WOOL:2;+4 - - LIME_WOOL:2;+4 - - PINK_WOOL:2;+4 - - GRAY_WOOL:2;+4 - - LIGHT_GRAY_WOOL:2;+4 - - CYAN_WOOL:2;+4 - - PURPLE_WOOL:2;+4 - - BLUE_WOOL:2;+4 - - BROWN_WOOL:2;+4 - - GREEN_WOOL:2;+4 - - RED_WOOL:2;+4 - - BLACK_WOOL:2;+4 + - white_wool:2;+4 + - orange_wool:2;+4 + - magenta_wool:2;+4 + - light_blue_wool:2;+4 + - yellow_wool:2;+4 + - lime_wool:2;+4 + - pink_wool:2;+4 + - gray_wool:2;+4 + - light_gray_wool:2;+4 + - cyan_wool:2;+4 + - purple_wool:2;+4 + - blue_wool:2;+4 + - brown_wool:2;+4 + - green_wool:2;+4 + - red_wool:2;+4 + - black_wool:2;+4 reward: text: 2 diamonds, sheep, emerald, flowers items: - - DIAMOND:2 - - EMERALD:1 - - SHEEP_SPAWN_EGG:1 - - ROSE_BUSH:1 - - PEONY:1 + - diamond:2 + - emerald:1 + - sheep_spawn_egg:1 + - rose_bush:1 + - peony:1 permission: usb.biome.flower_forest currency: 70 xp: 70 repeatReward: text: emerald, sheep, flowers items: - - EMERALD:1 - - SHEEP_SPAWN_EGG:1 - - ROSE_BUSH:1 - - PEONY:1 + - emerald:1 + - sheep_spawn_egg:1 + - rose_bush:1 + - peony:1 currency: 35 xp: 35 maestro: @@ -782,57 +908,57 @@ ranks: description: Make a jukebox and collect all music discs. type: onPlayer requiredItems: - - MUSIC_DISC_13:1 - - MUSIC_DISC_CAT:1 - - MUSIC_DISC_BLOCKS:1 - - MUSIC_DISC_CHIRP:1 - - MUSIC_DISC_FAR:1 - - MUSIC_DISC_MALL:1 - - MUSIC_DISC_MELLOHI:1 - - MUSIC_DISC_STAL:1 - - MUSIC_DISC_STRAD:1 - - MUSIC_DISC_WARD:1 - - MUSIC_DISC_11:1 - - MUSIC_DISC_WAIT:1 - - JUKEBOX:1 - displayItem: JUKEBOX - lockedDisplayItem: PURPLE_STAINED_GLASS_PANE + - music_disc_13:1 + - music_disc_cat:1 + - music_disc_blocks:1 + - music_disc_chirp:1 + - music_disc_far:1 + - music_disc_mall:1 + - music_disc_mellohi:1 + - music_disc_stal:1 + - music_disc_strad:1 + - music_disc_ward:1 + - music_disc_11:1 + - music_disc_wait:1 + - jukebox:1 + displayItem: jukebox + lockedDisplayItem: purple_stained_glass_pane reward: text: 3 diamonds, 1 gold block, 10 emeralds items: - - DIAMOND:3 - - EMERALD:10 - - GOLD_BLOCK:1 + - diamond:3 + - emerald:10 + - gold_block:1 currency: 70 xp: 70 repeatReward: text: 2 gold ore, 1 diamond items: - - GOLD_ORE:2 - - DIAMOND:1 - - '{p=0.2}DIAMOND:1' + - gold_ore:2 + - diamond:1 + - '{p=0.2}diamond:1' currency: 35 xp: 35 ironfarm: name: '&9Iron Farm' description: Build an iron-farm. type: onIsland - displayItem: IRON_BLOCK - lockedDisplayItem: BLUE_STAINED_GLASS_PANE + displayItem: iron_block + lockedDisplayItem: blue_stained_glass_pane radius: 50 requiredChallenges: - - monsterfarm - - applecollector - requiredItems: - - OAK_DOOR:30 + - monsterfarm + - applecollector + requiredBlocks: + - oak_door:30 requiredEntities: - - Villager:10 - - IRON_GOLEM:1 + - Villager:10 + - IRON_GOLEM:1 reward: text: 2 gold blocks, 1 diamond block items: - - GOLD_BLOCK:2 - - DIAMOND_BLOCK:1 + - gold_block:2 + - diamond_block:1 currency: 500 xp: 400 netherfortress: @@ -840,79 +966,79 @@ ranks: description: Build a netherfortress. type: onIsland radius: 50 - requiredItems: - - NETHER_BRICKS:512 - - NETHER_BRICK_SLAB:64 - - NETHER_BRICK_FENCE:64 - - NETHER_BRICK_STAIRS:64 - - SOUL_SAND:32 - displayItem: NETHER_BRICK_FENCE - lockedDisplayItem: BLUE_STAINED_GLASS_PANE + requiredBlocks: + - nether_bricks:512 + - nether_brick_slab:64 + - nether_brick_fence:64 + - nether_brick_stairs:64 + - soul_sand:32 + displayItem: nether_brick_fence + lockedDisplayItem: blue_stained_glass_pane reward: text: a wither-skull and a blaze rod and a chance of diamonds items: - - WITHER_SKELETON_SKULL:1 - - BLAZE_ROD:1 - - '{p=0.05}DIAMOND:3' - - '{p=0.10}DIAMOND:2' - - '{p=0.15}DIAMOND:1' + - wither_skeleton_skull:1 + - blaze_rod:1 + - '{p=0.05}diamond:3' + - '{p=0.10}diamond:2' + - '{p=0.15}diamond:1' currency: 40 xp: 40 - #=============================================== + # =============================================== Tier4: name: '&cMaster' - displayItem: ORANGE_TERRACOTTA + displayItem: orange_terracotta resetInHours: 20 requires: # disabled rankLeeway: 99 challenges: - - expertbuilder + - expertbuilder challenges: glassmaker: name: '&3Glassmaker' description: Collect every color of stained glass. type: onPlayer - displayItem: WHITE_STAINED_GLASS - lockedDisplayItem: CYAN_STAINED_GLASS_PANE + displayItem: white_stained_glass + lockedDisplayItem: cyan_stained_glass_pane requiredItems: - - WHITE_STAINED_GLASS:8;+2 - - ORANGE_STAINED_GLASS:8;+2 - - MAGENTA_STAINED_GLASS:8;+2 - - LIGHT_BLUE_STAINED_GLASS:8;+2 - - YELLOW_STAINED_GLASS:8;+2 - - LIME_STAINED_GLASS:8;+2 - - PINK_STAINED_GLASS:8;+2 - - GRAY_STAINED_GLASS:8;+2 - - LIGHT_GRAY_STAINED_GLASS:8;+2 - - CYAN_STAINED_GLASS:8;+2 - - PURPLE_STAINED_GLASS:8;+2 - - BLUE_STAINED_GLASS:8;+2 - - BROWN_STAINED_GLASS:8;+2 - - GREEN_STAINED_GLASS:8;+2 - - RED_STAINED_GLASS:8;+2 - - BLACK_STAINED_GLASS:8;+2 + - white_stained_glass:8;+2 + - orange_stained_glass:8;+2 + - magenta_stained_glass:8;+2 + - light_blue_stained_glass:8;+2 + - yellow_stained_glass:8;+2 + - lime_stained_glass:8;+2 + - pink_stained_glass:8;+2 + - gray_stained_glass:8;+2 + - light_gray_stained_glass:8;+2 + - cyan_stained_glass:8;+2 + - purple_stained_glass:8;+2 + - blue_stained_glass:8;+2 + - brown_stained_glass:8;+2 + - green_stained_glass:8;+2 + - red_stained_glass:8;+2 + - black_stained_glass:8;+2 reward: text: 2 diamonds, 2 disks, 1 emeralds items: - - DIAMOND:2 - - MUSIC_DISC_WARD:1 - - MUSIC_DISC_11:1 - - EMERALD:1 - - SUNFLOWER:1 - - LILAC:1 + - diamond:2 + - music_disc_ward:1 + - music_disc_11:1 + - emerald:1 + - sunflower:1 + - lilac:1 permission: usb.biome.flower_forest currency: 70 xp: 70 repeatReward: text: 30% chance on 1 or 2 disks, 1 emeralds items: - - '{p=0.3}MUSIC_DISC_WARD:1' - - '{p=0.3}MUSIC_DISC_11:1' - - EMERALD:1 - - SUNFLOWER:1 - - LILAC:1 + - '{p=0.3}music_disc_ward:1' + - '{p=0.3}music_disc_11:1' + - emerald:1 + - sunflower:1 + - lilac:1 currency: 35 xp: 35 carpenter: @@ -920,34 +1046,34 @@ ranks: description: Collect all types of wood items. type: onPlayer requiredItems: - - JUNGLE_STAIRS:16;+8 - - SPRUCE_DOOR:1;+1 - - DARK_OAK_FENCE_GATE:2;+2 - - ACACIA_FENCE:30;+15 - - LADDER:30;+15 - - OAK_TRAPDOOR:4;+2 - - OAK_PRESSURE_PLATE:2;+1 + - jungle_stairs:16;+8 + - spruce_door:1;+1 + - dark_oak_fence_gate:2;+2 + - acacia_fence:30;+15 + - ladder:30;+15 + - oak_trapdoor:4;+2 + - oak_pressure_plate:2;+1 requiredChallenges: - - toolmaker - - lumberjack:2 - displayItem: CRAFTING_TABLE - lockedDisplayItem: CYAN_STAINED_GLASS_PANE + - toolmaker + - lumberjack:2 + displayItem: crafting_table + lockedDisplayItem: cyan_stained_glass_pane reward: text: a parrot and a jukebox with some redstone and iron items: - - PARROT_SPAWN_EGG:1 - - JUKEBOX:1 - - REDSTONE_ORE:4 - - IRON_ORE:4 + - parrot_spawn_egg:1 + - jukebox:1 + - redstone_ore:4 + - iron_ore:4 currency: 30 xp: 30 repeatReward: text: redstone, iron and a slim chance at a parrot items: - - REDSTONE_ORE:1 - - IRON_ORE:1 - - '{p=0.25}JUKEBOX:1' - - '{p=0.05}PARROT_SPAWN_EGG:1' + - redstone_ore:1 + - iron_ore:1 + - '{p=0.25}jukebox:1' + - '{p=0.05}parrot_spawn_egg:1' currency: 15 xp: 15 cookielover: @@ -955,29 +1081,29 @@ ranks: description: Make cookies and a bucket of milk. type: onPlayer requiredChallenges: - - expertfarmer + - expertfarmer requiredItems: - - COOKIE:128;+4 - - MILK_BUCKET:1 - displayItem: COOKIE - lockedDisplayItem: YELLOW_STAINED_GLASS_PANE + - cookie:128;+4 + - milk_bucket:1 + displayItem: cookie + lockedDisplayItem: yellow_stained_glass_pane reward: text: 4 redstone ore, clay a bucket and a diamond items: - - REDSTONE_ORE:4 - - CLAY:5 - - DIAMOND:1 - - BUCKET:1 + - redstone_ore:4 + - clay:5 + - diamond:1 + - bucket:1 currency: 80 xp: 80 repeatReward: text: iron ore and some clay items: - - IRON_ORE:1 - - CLAY:5 - - BUCKET:1 - - '{p=0.4}CLAY:3' - - '{p=0.3}IRON_ORE:2' + - iron_ore:1 + - clay:5 + - bucket:1 + - '{p=0.4}clay:3' + - '{p=0.3}iron_ore:2' currency: 40 xp: 40 deepseafisherman: @@ -985,33 +1111,33 @@ ranks: description: Farm the deep sea type: onPlayer requiredItems: - - COD:10;+5 - - SALMON:10;+5 - - PUFFERFISH:5;+3 - - TROPICAL_FISH:3;+2 - - PRISMARINE_CRYSTALS:16;+8 - - PRISMARINE_SHARD:16;+8 - - DRIED_KELP_BLOCK:64;+32 - - NAUTILUS_SHELL:1 - displayItem: HEART_OF_THE_SEA - lockedDisplayItem: PURPLE_STAINED_GLASS_PANE + - cod:10;+5 + - salmon:10;+5 + - pufferfish:5;+3 + - tropical_fish:3;+2 + - prismarine_crystals:16;+8 + - prismarine_shard:16;+8 + - dried_kelp_block:64;+32 + - nautilus_shell:1 + displayItem: heart_of_the_sea + lockedDisplayItem: purple_stained_glass_pane reward: text: heart-of-the-sea, nautilus shell and a turtle items: - - NAUTILUS_SHELL:1 - - HEART_OF_THE_SEA:1 - - TURTLE_SPAWN_EGG:1 - - '{p=0.1}NAUTILUS_SHELL:1' + - nautilus_shell:1 + - heart_of_the_sea:1 + - turtle_spawn_egg:1 + - '{p=0.1}nautilus_shell:1' permission: usb.biome.deep_ocean currency: 50 xp: 50 repeatReward: text: nautilus shell, turtle and a chance of a heart items: - - NAUTILUS_SHELL:1 - - TURTLE_SPAWN_EGG:1 - - '{p=0.05}HEART_OF_THE_SEA:1' - - '{p=0.1}NAUTILUS_SHELL:1' + - nautilus_shell:1 + - turtle_spawn_egg:1 + - '{p=0.05}heart_of_the_sea:1' + - '{p=0.1}nautilus_shell:1' currency: 25 xp: 25 horsingaround: @@ -1019,27 +1145,27 @@ ranks: description: Get hay bales for the horses. type: onPlayer requiredItems: - - HAY_BLOCK:32;+4 - - LEAD:8;+2 - - CARROT_ON_A_STICK:1 - - SHEARS:1 + - hay_block:32;+4 + - lead:8;+2 + - carrot_on_a_stick:1 + - shears:1 requirecChallenges: - - wheatfarmer - displayItem: HAY_BLOCK - lockedDisplayItem: BROWN_STAINED_GLASS_PANE + - wheatfarmer + displayItem: hay_block + lockedDisplayItem: brown_stained_glass_pane reward: text: 1 horse, 1 iron horse armor, 5% chance on diamond horse armor items: - - IRON_HORSE_ARMOR:1 - - HORSE_SPAWN_EGG:1 - - '{p=0.05}DIAMOND_HORSE_ARMOR:1' + - iron_horse_armor:1 + - horse_spawn_egg:1 + - '{p=0.05}diamond_horse_armor:1' currency: 50 xp: 50 repeatReward: text: 1 redstone ore, 1 emerald items: - - REDSTONE:1 - - EMERALD:1 + - redstone:1 + - emerald:1 currency: 25 xp: 25 slimefarmer: @@ -1047,72 +1173,72 @@ ranks: description: Collect slimeballs from slimes. type: onPlayer requiredItems: - - SLIME_BALL:64;+4 - displayItem: SLIME_BALL - lockedDisplayItem: PURPLE_STAINED_GLASS_PANE + - slime_ball:64;+4 + displayItem: slime_ball + lockedDisplayItem: purple_stained_glass_pane reward: text: 1 diamond, 1 emeralds, swampland items: - - DIAMOND:1 - - EMERALD:1 - permission: usb.biome.swampland + - diamond:1 + - emerald:1 + permission: usb.biome.swamp currency: 70 xp: 70 repeatReward: text: 1 redstone ore, 1 emeralds items: - - REDSTONE_ORE:1 - - EMERALD:1 + - redstone_ore:1 + - emerald:1 currency: 35 xp: 35 animalfarm: name: '&9Animal Farm' description: Create an animal farm. type: onIsland - displayItem: OAK_FENCE + displayItem: oak_fence radius: 40 requiredChallenges: - - woolcollector - - potatofarmer - - carrotfarmer + - woolcollector + - potatofarmer + - carrotfarmer requiredEntities: - - Cow:8 - - Pig:8 - - Chicken:16 - - Sheep:{"Color":0} - - Sheep:{"Color":1} - - Sheep:{"Color":2} - - Sheep:{"Color":3} - - Sheep:{"Color":4} - - Sheep:{"Color":5} - - Sheep:{"Color":6} - - Sheep:{"Color":7} - - Sheep:{"Color":8} - - Sheep:{"Color":9} - - Sheep:{"Color":10} - - Sheep:{"Color":11} - - Sheep:{"Color":12} - - Sheep:{"Color":13} - - Sheep:{"Color":14} - - Sheep:{"Color":15} + - Cow:8 + - Pig:8 + - Chicken:16 + - Sheep:{"Color":"WHITE"} + - Sheep:{"Color":"ORANGE"} + - Sheep:{"Color":"MAGENTA"} + - Sheep:{"Color":"LIGHT_BLUE"} + - Sheep:{"Color":"YELLOW"} + - Sheep:{"Color":"LIME"} + - Sheep:{"Color":"PINK"} + - Sheep:{"Color":"GRAY"} + - Sheep:{"Color":"LIGHT_GRAY"} + - Sheep:{"Color":"CYAN"} + - Sheep:{"Color":"PURPLE"} + - Sheep:{"Color":"BLUE"} + - Sheep:{"Color":"BROWN"} + - Sheep:{"Color":"GREEN"} + - Sheep:{"Color":"RED"} + - Sheep:{"Color":"BLACK"} reward: text: 1 horse, 1 iron horse armor, 5% chance horse armor items: - - IRON_HORSE_ARMOR:1 - - HORSE_SPAWN_EGG:1 - - '{p=0.05}IRON_HORSE_ARMOR:1' - - '{p=0.05}GOLDEN_HORSE_ARMOR:1' - - '{p=0.05}DIAMOND_HORSE_ARMOR:1' + - iron_horse_armor:1 + - horse_spawn_egg:1 + - '{p=0.05}iron_horse_armor:1' + - '{p=0.05}golden_horse_armor:1' + - '{p=0.05}diamond_horse_armor:1' currency: 50 xp: 50 repeatReward: text: 1 redstone ore, 1 emerald, 5% chance horse armor items: - - REDSTONE:1 - - EMERALD:1 - - '{p=0.05}IRON_HORSE_ARMOR:1' - - '{p=0.05}GOLDEN_HORSE_ARMOR:1' - - '{p=0.05}DIAMOND_HORSE_ARMOR:1' + - redstone:1 + - emerald:1 + - '{p=0.05}iron_horse_armor:1' + - '{p=0.05}golden_horse_armor:1' + - '{p=0.05}diamond_horse_armor:1' currency: 25 xp: 25 witherhunter: @@ -1120,24 +1246,24 @@ ranks: description: Collect some Wither Skeleton skulls. type: onPlayer requiredChallenges: - - netherfortress + - netherfortress requiredItems: - - WITHER_SKELETON_SKULL:10;+1 - displayItem: WITHER_SKELETON_SKULL - lockedDisplayItem: PURPLE_STAINED_GLASS_PANE + - wither_skeleton_skull:10;+1 + displayItem: wither_skeleton_skull + lockedDisplayItem: purple_stained_glass_pane offset: -1 reward: text: 5 diamonds, 5% chance of nether star items: - - DIAMOND:5 - - '{p=0.05}NETHER_STAR:1' + - diamond:5 + - '{p=0.05}nether_star:1' currency: 400 xp: 400 repeatReward: text: 2 gold ore, 5% chance of nether star items: - - GOLD_ORE:2 - - '{p=0.05}NETHER_STAR:1' + - gold_ore:2 + - '{p=0.05}nether_star:1' currency: 20 xp: 20 pearlcollector: @@ -1145,69 +1271,69 @@ ranks: description: Collect enderpearls from endermen. type: onPlayer requiredItems: - - ENDER_PEARL:10;+4 - displayItem: ENDER_PEARL - lockedDisplayItem: PURPLE_STAINED_GLASS_PANE + - ender_pearl:10;+4 + displayItem: ender_pearl + lockedDisplayItem: purple_stained_glass_pane reward: text: 5 gold ore, 1 blaze rod, 1 emerald items: - - GOLD_ORE:5 - - BLAZE_ROD:1 - - EMERALD:1 + - gold_ore:5 + - blaze_rod:1 + - emerald:1 currency: 70 xp: 70 repeatReward: text: 1 gold ore, 1 blaze rod items: - - GOLD_ORE:1 - - BLAZE_ROD:1 + - gold_ore:1 + - blaze_rod:1 currency: 35 xp: 35 - #=============================================== + # =============================================== Tier5: name: '&4Sky Lord' - displayItem: RED_TERRACOTTA + displayItem: red_terracotta resetInHours: 48 requires: challenges: - - masterbuilder + - masterbuilder challenges: technician: name: '&3Technician' description: Collect some of every type of redstone equipment. type: onPlayer requiredItems: - - REDSTONE:64;+16 - - REDSTONE_TORCH:32;+4 - - REPEATER:5;+1 - - COMPARATOR:3;+1 - - PISTON:2;+1 - - STICKY_PISTON:2;+1 - - LEVER:1;+1 - - STONE_BUTTON:1;+1 - - STONE_PRESSURE_PLATE:1;+1 - - HOPPER:1;+1 - - DISPENSER:1;+1 - - DROPPER:1;+1 - - DAYLIGHT_DETECTOR:1;+1 - displayItem: REDSTONE - lockedDisplayItem: CYAN_STAINED_GLASS_PANE + - redstone:64;+16 + - redstone_torch:32;+4 + - repeater:5;+1 + - comparator:3;+1 + - piston:2;+1 + - sticky_piston:2;+1 + - lever:1;+1 + - stone_button:1;+1 + - stone_pressure_plate:1;+1 + - hopper:1;+1 + - dispenser:1;+1 + - dropper:1;+1 + - daylight_detector:1;+1 + displayItem: redstone + lockedDisplayItem: cyan_stained_glass_pane reward: text: some snow, ice and packed ice items: - - SNOW_BLOCK:4 - - ICE:8 - - PACKED_ICE:8 - - REDSTONE_BLOCK:64 + - snow_block:4 + - ice:8 + - packed_ice:8 + - redstone_block:64 currency: 70 xp: 70 repeatReward: text: a stack of redstone blocks and some ice items: - - REDSTONE_BLOCK:64 - - ICE:4 - - PACKED_ICE:1 + - redstone_block:64 + - ice:4 + - packed_ice:1 currency: 35 xp: 35 emeraldcollector: @@ -1215,25 +1341,25 @@ ranks: description: Collect emeralds. type: onPlayer requiredItems: - - EMERALD:60;+10 - displayItem: EMERALD - lockedDisplayItem: PURPLE_STAINED_GLASS_PANE + - emerald:60;+10 + displayItem: emerald + lockedDisplayItem: purple_stained_glass_pane reward: text: a full set of diamond armor items: - - DIAMOND_HELMET:1 - - DIAMOND_CHESTPLATE:1 - - DIAMOND_LEGGINGS:1 - - DIAMOND_BOOTS:1 + - diamond_helmet:1 + - diamond_chestplate:1 + - diamond_leggings:1 + - diamond_boots:1 currency: 70 xp: 70 repeatReward: text: full diamond armor items: - - DIAMOND_HELMET:1 - - DIAMOND_CHESTPLATE:1 - - DIAMOND_LEGGINGS:1 - - DIAMOND_BOOTS:1 + - diamond_helmet:1 + - diamond_chestplate:1 + - diamond_leggings:1 + - diamond_boots:1 currency: 35 xp: 35 topchef: @@ -1241,40 +1367,40 @@ ranks: description: Collect every kind of edible food. type: onPlayer requiredItems: - - BAKED_POTATO:1 - - BREAD:1 - - CAKE:1 - - COOKED_CHICKEN:1 - - COOKED_COD:1 - - COOKED_SALMON:1 - - TROPICAL_FISH:1 - - COOKED_PORKCHOP:1 - - COOKIE:1 - - LAPIS_BLOCK:1 - - GOLDEN_CARROT:1 - - MUSHROOM_STEW:1 - - PUMPKIN_PIE:1 - - COOKED_BEEF:1 - - MELON:1 - - CARROT:1 + - baked_potato:1 + - bread:1 + - cake:1 + - cooked_chicken:1 + - cooked_cod:1 + - cooked_salmon:1 + - tropical_fish:1 + - cooked_porkchop:1 + - cookie:1 + - golden_apple:1 + - golden_carrot:1 + - mushroom_stew:1 + - pumpkin_pie:1 + - cooked_beef:1 + - melon:1 + - carrot:1 requiredChallenges: - - expertfarmer - - fisherman - - cookielover - displayItem: CARROT - lockedDisplayItem: YELLOW_STAINED_GLASS_PANE + - expertfarmer + - fisherman + - cookielover + displayItem: carrot + lockedDisplayItem: yellow_stained_glass_pane reward: text: 2 diamond, 1 mooshroom items: - - DIAMOND:2 - - MOOSHROOM_SPAWN_EGG:1 + - diamond:2 + - mooshroom_spawn_egg:1 currency: 80 xp: 80 repeatReward: text: 1 diamond, 1 mooshroom items: - - DIAMOND:1 - - MOOSHROOM_SPAWN_EGG:1 + - diamond:1 + - mooshroom_spawn_egg:1 currency: 40 xp: 40 tajmahal: @@ -1282,21 +1408,21 @@ ranks: description: Build a temple of quartz type: onIsland radius: 30 - displayItem: QUARTZ_BLOCK - lockedDisplayItem: BLUE_STAINED_GLASS_PANE + displayItem: quartz_block + lockedDisplayItem: blue_stained_glass_pane requiredChallenges: - - nethermining - requiredItems: - - QUARTZ_BLOCK:512 - - CHISELED_QUARTZ_BLOCK:64 - - QUARTZ_PILLAR:128 - - QUARTZ_STAIRS:64 - - QUARTZ_SLAB:192 + - nethermining + requiredBlocks: + - quartz_block:512 + - chiseled_quartz_block:64 + - quartz_pillar:128 + - quartz_stairs:64 + - quartz_slab:192 reward: text: 3 end-portal frames, red sand items: - - END_PORTAL_FRAME:3 - - RED_SAND:64 + - end_portal_frame:3 + - red_sand:64 currency: 1000 xp: 500 greatpyramid: @@ -1304,21 +1430,21 @@ ranks: description: Build a pyramid of sandstone type: onIsland radius: 30 - displayItem: SANDSTONE - lockedDisplayItem: BLUE_STAINED_GLASS_PANE - requiredItems: - - SANDSTONE:512 - - CHISELED_SANDSTONE:64 - - SMOOTH_SANDSTONE:128 - - SANDSTONE_STAIRS:64 - - SANDSTONE_SLAB:192 - - RED_SANDSTONE:16 + displayItem: sandstone + lockedDisplayItem: blue_stained_glass_pane + requiredBlocks: + - sandstone:512 + - chiseled_sandstone:64 + - smooth_sandstone:128 + - sandstone_stairs:64 + - sandstone_slab:192 + - red_sandstone:16 reward: text: 3 end-portal frames, 3 diamonds, bow items: - - END_PORTAL_FRAME:3 - - DIAMOND:3 - - BOW:1 {Enchantments:[{id:"minecraft:infinity",lvl:1},{id:"minecraft:unbreaking",lvl:3},{id:"minecraft:power",lvl:5}]} + - end_portal_frame:3 + - diamond:3 + - 'bow[enchantments={levels:{infinity:1,power:5,unbreaking:3}}]:1' currency: 1000 xp: 500 poseidonshalls: @@ -1326,36 +1452,36 @@ ranks: description: Build the halls of Poseidon type: onIsland radius: 50 - displayItem: PRISMARINE - lockedDisplayItem: BLUE_STAINED_GLASS_PANE + displayItem: prismarine + lockedDisplayItem: blue_stained_glass_pane requiredChallenges: - - deepseafisherman - requiredItems: - - PRISMARINE:512 - - PRISMARINE_BRICKS:128 - - DARK_PRISMARINE:64 - - SEA_LANTERN:32 - - CONDUIT:1 - - FLOWER_POT:16 - - VINE:128 - - POPPY:10 - - BLUE_ORCHID:10 - - ALLIUM:10 - - AZURE_BLUET:10 - - RED_TULIP:10 - - ORANGE_TULIP:10 - - WHITE_TULIP:10 - - PINK_TULIP:10 - - OXEYE_DAISY:10 - - SUNFLOWER:10 - - LILAC:10 - - ROSE_BUSH:10 - - PEONY:10 + - deepseafisherman + requiredBlocks: + - prismarine:512 + - prismarine_bricks:128 + - dark_prismarine:64 + - sea_lantern:32 + - conduit:1 + - flower_pot:16 + - vine:128 + - poppy:10 + - blue_orchid:10 + - allium:10 + - azure_bluet:10 + - red_tulip:10 + - orange_tulip:10 + - white_tulip:10 + - pink_tulip:10 + - oxeye_daisy:10 + - sunflower:10 + - lilac:10 + - rose_bush:10 + - peony:10 reward: text: 3 end-portal frames, 5 diamonds items: - - END_PORTAL_FRAME:3 - - DIAMOND:5 + - end_portal_frame:3 + - diamond:5 currency: 1000 xp: 500 beaconator: @@ -1363,23 +1489,23 @@ ranks: description: Build a very expensive beacon. type: onIsland radius: 15 - displayItem: BEACON - lockedDisplayItem: BLUE_STAINED_GLASS_PANE + displayItem: beacon + lockedDisplayItem: blue_stained_glass_pane requiredChallenges: - - netherfortress - - ironfarm - - maestro - - witherhunter - requiredItems: - - BEACON:1 - - DIAMOND_BLOCK:1 - - EMERALD_BLOCK:8 - - GOLD_BLOCK:25 - - IRON_BLOCK:49 + - netherfortress + - ironfarm + - maestro + - witherhunter + requiredBlocks: + - beacon:1 + - diamond_block:1 + - emerald_block:8 + - gold_block:25 + - iron_block:49 reward: items: - - END_PORTAL_FRAME:3 - - DIAMOND_SWORD:1 {Enchantments:[{id:"minecraft:sharpness",lvl:5},{id:"minecraft:unbreaking",lvl:3},{id:"minecraft:fire",lvl:1}]} + - end_portal_frame:3 + - 'diamond_sword[enchantments={levels:{fire_aspect:1,sharpness:5,unbreaking:3}}]:1' text: some end-portal-pieces and a magic diamond sword currency: 4000 xp: 1000 @@ -1388,380 +1514,281 @@ ranks: description: Build and activate an end-portal. type: onIsland radius: 15 - displayItem: END_PORTAL_FRAME - lockedDisplayItem: BLUE_STAINED_GLASS_PANE + displayItem: end_portal_frame + lockedDisplayItem: blue_stained_glass_pane requiredChallenges: - - beaconator - - tajmahal - - poseidonshalls - - greatpyramid - requiredItems: - - END_PORTAL_FRAME:12 - - END_PORTAL:9 + - beaconator + - tajmahal + - poseidonshalls + - greatpyramid + requiredBlocks: + - end_portal_frame:12 + - end_portal:9 reward: text: 5 diamonds and some nice boots items: - - DIAMOND:5 - - DIAMOND_BOOTS:1 {Enchantments:[{id:"minecraft:feather_falling",lvl:4},{id:"minecraft:protection",lvl:4},{id:"minecraft:blast_protection",lvl:4}]} + - diamond:5 + - 'diamond_boots[enchantments={levels:{blast_protection:4,feather_falling:4,protection:4}}]:1' currency: 4000 xp: 1000 - #=============================================== + # =============================================== Tier6: name: '&aWorld Foods' - displayItem: LIGHT_GRAY_TERRACOTTA + displayItem: light_gray_terracotta resetInHours: 20 requires: challenges: - - masterbuilder - - topchef + - masterbuilder + - topchef challenges: fishandchips: name: '&eFish & Chips' description: Create the famous English Fish & Chips. type: onPlayer requiredItems: - - COOKED_COD:32;+8 - - BAKED_POTATO:32;+8 - displayItem: COOKED_COD - lockedDisplayItem: YELLOW_STAINED_GLASS_PANE + - cooked_cod:32;+8 + - baked_potato:32;+8 + displayItem: cooked_cod + lockedDisplayItem: yellow_stained_glass_pane reward: text: 16 baked potatos, 1 disk, 4 emerald items: - - BAKED_POTATO:16 - - MUSIC_DISC_13:1 - - EMERALD:4 + - baked_potato:16 + - music_disc_13:1 + - emerald:4 currency: 60 xp: 60 commands: - - 'console: msg {player} &9https://en.wikipedia.org/wiki/Fish_and_chips + - 'console: msg {player} &9https://en.wikipedia.org/wiki/Fish_and_chips &2Click the link for more info' repeatReward: text: 30% chance on 1 disk, 1 emerald items: - - EMERALD:1 - - '{p=0.3}MUSIC_DISC_13:1' + - emerald:1 + - '{p=0.3}music_disc_13:1' currency: 30 xp: 30 commands: - - 'console: msg {player} &9https://en.wikipedia.org/wiki/Fish_and_chips + - 'console: msg {player} &9https://en.wikipedia.org/wiki/Fish_and_chips &2Click the link for more info' smorrebrod: name: '&eSmørrebrød' description: Create the famous Danish smørrebrød. type: onPlayer requiredItems: - - BREAD:16;+4 - - COOKED_SALMON:8;+4 - - TROPICAL_FISH:2;+1 - - COOKED_PORKCHOP:8;+4 - - COOKED_BEEF:8;+4 - displayItem: COOKED_COD - lockedDisplayItem: YELLOW_STAINED_GLASS_PANE + - bread:16;+4 + - cooked_salmon:8;+4 + - tropical_fish:2;+1 + - cooked_porkchop:8;+4 + - cooked_beef:8;+4 + displayItem: cooked_cod + lockedDisplayItem: yellow_stained_glass_pane reward: text: 1 rod, 2 name tags, 2 ocelots items: - - FISHING_ROD:1 - - NAME_TAG:2 - - OCELOT_SPAWN_EGG:2 + - fishing_rod:1 + - name_tag:2 + - ocelot_spawn_egg:2 currency: 60 xp: 60 commands: - - 'console: msg {player} &9https://en.wikipedia.org/wiki/Danish_cuisine + - 'console: msg {player} &9https://en.wikipedia.org/wiki/Danish_cuisine &2Click the link for more info' repeatReward: text: 1 emerald items: - - EMERALD:1 + - emerald:1 currency: 30 xp: 30 commands: - - 'console: msg {player} &9https://en.wikipedia.org/wiki/Danish_cuisine + - 'console: msg {player} &9https://en.wikipedia.org/wiki/Danish_cuisine &2Click the link for more info' hutspot: name: '&eHutspot' description: Create the famous Dutch hutspot. type: onPlayer requiredItems: - - POTATO:64;+8 - - CARROT:64;+8 - lockedDisplayItem: YELLOW_STAINED_GLASS_PANE - displayItem: GOLDEN_CARROT + - potato:64;+8 + - carrot:64;+8 + lockedDisplayItem: yellow_stained_glass_pane + displayItem: golden_carrot reward: text: 1 golden carrot, 1 disk, 4 emerald items: - - GOLDEN_CARROT:1 - - MUSIC_DISC_CAT:1 - - EMERALD:4 + - golden_carrot:1 + - music_disc_cat:1 + - emerald:4 currency: 60 xp: 60 commands: - - 'console: msg {player} &9https://en.wikipedia.org/wiki/Hutspot &2Click + - 'console: msg {player} &9https://en.wikipedia.org/wiki/Hutspot &2Click the link for more info' repeatReward: text: 30% chance on 1 disk, 1 emerald items: - - EMERALD:1 - - '{p=0.3}MUSIC_DISC_CAT:1' + - emerald:1 + - '{p=0.3}music_disc_cat:1' currency: 30 xp: 30 commands: - - 'console: msg {player} &9https://en.wikipedia.org/wiki/Hutspot &2Click + - 'console: msg {player} &9https://en.wikipedia.org/wiki/Hutspot &2Click the link for more info' apfelstrudel: name: '&eApfelstrudele' description: Create the famous German apfelstrudel. type: onPlayer requiredItems: - - APPLE:8;+5 - - WHEAT:16;+5 - - SUGAR:16;+5 - - MILK_BUCKET:1 - displayItem: GOLDEN_APPLE - lockedDisplayItem: YELLOW_STAINED_GLASS_PANE + - apple:8;+5 + - wheat:16;+5 + - sugar:16;+5 + - milk_bucket:1 + displayItem: golden_apple + lockedDisplayItem: yellow_stained_glass_pane reward: text: 1 bucket, 1 disk, 4 emerald items: - - BUCKET:1 - - EMERALD:4 - - MUSIC_DISC_BLOCKS:1 + - bucket:1 + - emerald:4 + - music_disc_blocks:1 currency: 60 xp: 60 commands: - - 'console: msg {player} &9https://en.wikipedia.org/wiki/Apple_strudel &2Click + - 'console: msg {player} &9https://en.wikipedia.org/wiki/Apple_strudel &2Click the link for more info' repeatReward: text: 1 bucket, 30% chance on 1 disk, 1 emerald items: - - BUCKET:1 - - EMERALD:1 - - '{p=0.3}MUSIC_DISC_BLOCKS:1' + - bucket:1 + - emerald:1 + - '{p=0.3}music_disc_blocks:1' currency: 30 xp: 30 commands: - - 'console: msg {player} &9https://en.wikipedia.org/wiki/Apple_strudel &2Click + - 'console: msg {player} &9https://en.wikipedia.org/wiki/Apple_strudel &2Click the link for more info' brownies: name: '&eBrownies' description: Create the famous American brownies. type: onPlayer requiredItems: - - WHEAT:16;+5 - - SUGAR:16;+5 - - EGG:16;+5 - - INK_SAC:16;+5 - - MILK_BUCKET:1 - displayItem: DARK_OAK_SLAB - lockedDisplayItem: YELLOW_STAINED_GLASS_PANE + - wheat:16;+5 + - sugar:16;+5 + - egg:16;+5 + - ink_sac:16;+5 + - milk_bucket:1 + displayItem: dark_oak_slab + lockedDisplayItem: yellow_stained_glass_pane reward: text: 1 bucket, 1 disk, 4 emerald items: - - BUCKET:1 - - EMERALD:4 - - MUSIC_DISC_FAR:1 + - bucket:1 + - emerald:4 + - music_disc_far:1 currency: 60 xp: 60 commands: - - 'console: msg {player} &9https://en.wikipedia.org/wiki/Chocolate_brownie + - 'console: msg {player} &9https://en.wikipedia.org/wiki/Chocolate_brownie &2Click the link for more info' repeatReward: text: 1 bucket, 30% chance on 1 disk, 1 emerald items: - - BUCKET:1 - - EMERALD:1 - - '{p=0.3}MUSIC_DISC_FAR:1' + - bucket:1 + - emerald:1 + - '{p=0.3}music_disc_far:1' currency: 30 xp: 30 commands: - - 'console: msg {player} &9https://en.wikipedia.org/wiki/Chocolate_brownie + - 'console: msg {player} &9https://en.wikipedia.org/wiki/Chocolate_brownie &2Click the link for more info' pastafunghi: name: '&ePasta Funghi' description: Create the famous Italian Pasta Funghi. type: onPlayer requiredItems: - - WHEAT:32;+8 - - EGG:32;+8 - - BROWN_MUSHROOM:16;+4 - - RED_MUSHROOM:16;+4 - - MILK_BUCKET:1 - displayItem: RED_MUSHROOM_BLOCK - lockedDisplayItem: YELLOW_STAINED_GLASS_PANE + - wheat:32;+8 + - egg:32;+8 + - brown_mushroom:16;+4 + - red_mushroom:16;+4 + - milk_bucket:1 + displayItem: red_mushroom_block + lockedDisplayItem: yellow_stained_glass_pane reward: text: 2 rabbits, 1 bucket items: - - BUCKET:1 - - RABBIT_SPAWN_EGG:2 + - bucket:1 + - rabbit_spawn_egg:2 currency: 60 xp: 60 commands: - - console:msg {player} &9https://en.wikipedia.org/wiki/Pasta &2Click the - link for more info + - console:msg {player} &9https://en.wikipedia.org/wiki/Pasta &2Click the + link for more info repeatReward: text: 30% chance on 1 disk, 1 emerald items: - - EMERALD:1 - - '{p=0.3}MUSIC_DISC_13:1' + - emerald:1 + - '{p=0.3}music_disc_13:1' currency: 30 xp: 30 commands: - - console:msg {player} &9https://en.wikipedia.org/wiki/Pasta &2Click the - link for more info + - console:msg {player} &9https://en.wikipedia.org/wiki/Pasta &2Click the + link for more info chocolate: name: '&eBelgian chocolate' description: Create the famous belgian chocolate. type: onPlayer requiredItems: - - SUGAR:16;+5 - - INK_SAC:16;+5 - - MILK_BUCKET:1 - displayItem: PIG_SPAWN_EGG - lockedDisplayItem: YELLOW_STAINED_GLASS_PANE + - sugar:16;+5 + - ink_sac:16;+5 + - milk_bucket:1 + displayItem: pig_spawn_egg + lockedDisplayItem: yellow_stained_glass_pane reward: text: 1 bucket, 1 disk, 4 emeralds items: - - BUCKET:1 - - EMERALD:4 - - MUSIC_DISC_MALL:1 + - bucket:1 + - emerald:4 + - music_disc_mall:1 currency: 60 xp: 60 commands: - - 'console: msg {player} &9https://en.wikipedia.org/wiki/Belgian_chocolate + - 'console: msg {player} &9https://en.wikipedia.org/wiki/Belgian_chocolate &2Click the link for more info' repeatReward: text: 1 bucket, 30% chance on 1 disk, 1 emerald items: - - BUCKET:1 - - EMERALD:1 - - '{p=0.3}MUSIC_DISC_MALL:1' + - bucket:1 + - emerald:1 + - '{p=0.3}music_disc_mall:1' currency: 30 xp: 30 commands: - - 'console: msg {player} &9https://en.wikipedia.org/wiki/Belgian_chocolate + - 'console: msg {player} &9https://en.wikipedia.org/wiki/Belgian_chocolate &2Click the link for more info' baker: name: '&ePâtisserie' description: Bake cakes, pumpkin pies, and cookies. type: onPlayer requiredItems: - - CAKE:5;+1 - - PUMPKIN_PIE:5;+1 - - COOKIE:128;+4 - displayItem: CAKE - lockedDisplayItem: YELLOW_STAINED_GLASS_PANE + - cake:5;+1 + - pumpkin_pie:5;+1 + - cookie:128;+4 + displayItem: cake + lockedDisplayItem: yellow_stained_glass_pane reward: text: 5 gold ore, 1 diamond items: - - GOLD_ORE:5 - - DIAMOND:1 + - gold_ore:5 + - diamond:1 currency: 80 xp: 80 repeatReward: text: 2 iron ore, 1 gold ore items: - - IRON_ORE:2 - - GOLD_ORE:1 + - iron_ore:2 + - gold_ore:1 currency: 40 xp: 40 -#=============================================== -# -# Here follows the format and explanation for the challenge group and challenges setup. The single commented (#) are the most used settings -# while the double commented (##) are optionnal if you like to use those settings. -# -#ranks: -# # [text] name of the challenge Rank. -# TierX: -# #[text] The name of the challenge rank that shows up when you do /challenges (this supports capitals and colorcodes). -# name: '&aCuston Challenges rank name' -# # [itemid] The itemid of the item to be displayed in the challenge menu for complete challenges. -# displayItem: 3 -# # [integer] The time in hours before required items reset to default (tis overwrites the Main reset time) -# resetInHours: 20 -# # The requierments will allow when a challenge groups will be available to see and complete for players. -# requires: -# # [integer] The number of tasks per rank that can be left uncompleted to advance to the next rank. For example, if you have 4 challenges -# # with a rankLeeway of 1, a player would only need to complete 3 to advance to the next rank. -# # A rankLeeway of 0 would require them all. -# rankLeeway: 8 -# # [text] Challenges that has to be completed before this group will show in the Challenges GUI -# challenges: -# - a challenge name -#=============================================== -# challenges: -# #[text] The name of the challenge. All challenge names should be lower case. -# defaultchallengename: -# #[text] The name of the challenge that shows up when you do /challenges (this supports capitals and colorcodes). -# name: '&a' -# # [text] What the player sees when they do /challenges -# description: -# # [onIsland/onPlayer/islandLevel] This tells whether the required blocks/items should be in the player's inventory or on their island -# # When using onIsland, the player must be 10 blocks away from the required blocks on his island. -# # When using onIsland, the default radius distance is set to 10, all items requierd must be within this distance. -# # When using islandLevel, the 'requiredItems' field should be the island level required. The player must use /island level first to update his level. -# type: onPlayer -## type: islandLevel -## type: onIsland -# # To override the default radius when using onIsland, uncommend the "radius: " and fill in your own value. -## radius: -# # [itemid list] The itemid:count of the items required for the challenge. -# requiredItems: '' -# # [true/false] If the challenge can repeated or not. -# # [itemid] The itemid of the item to be displayed in the challenge menu for complete challenges. -# displayItem: -## tool: -# # [integer] The time in hours before required items reset to default (this overwrites the Main and Ranks reset times). -## resetInHours: -# # [true/false] Take required items on completing a challenge. -# # # reward section to reward the player for completing the challenge. -# reward: -# # [text] Description of the reward. If omitted §4Unknown will be shown. -# text: -# # [itemid list] The itemid::count of the reward to give the player for completing the challenge. -# items: -## # [permission node] A permission granted for completion. Multiple permissions are space-separated, i.e. - "test.1 test.2" (use none to not give a permission) -# # # [integer] How much currency to give for the first time completion. (requires an economy plugin) -# currency: 0 -# # [integer] How much xp to give to the player for the first time completion. -# xp: 0 -# # Executes the given command upon completion. Prepend with "op" or "console" to run the commands as OP or from the Console. Examples: -# # commands: -# # - 'op: me are the GOD of things' -# # - 'console: give {party} aweseomestuff 32' -# # Possible command arguments are: -# # {player} - The name of the player -# # {playerName} - The display name of the player -# # {challenge} - The name of the challenge -# # {position} - The position of the player -# # {party} - Execute the command once for each member of the party (substituting the name) -## commands: -## - op: -## - console: -# # reward section to reward the player for completing a repeated challenge. -# repeatReward: -# # [text] Description of the reward. If omitted §4Unknown will be shown. -# text: -# # [itemid list] The itemid::count of the reward to give the player for completing the challenge. -# items: -## # [integer] How much currency to give for the first time completion. (requires an economy plugin) -# currency: 0 -# # [integer] How much xp to give to the player for the first time completion. -# xp: 0 -# # Executes the given command upon completion. Prepend with "op" or "console" to run the commands as OP or from the Console. Examples: -# # commands: -# # - 'op: me are the GOD of things' -# # - 'console: give {party} aweseomestuff 32' -# # Possible command arguments are: -# # {player} - The name of the player -# # {playerName} - The display name of the player -# # {challenge} - The name of the challenge -# # {position} - The position of the player -# # {party} - Execute the command once for each member of the party (substituting the name) -## commands: -## - op: -## - console: -# -# All commented settings (#) in the above challenges (not the explanation) can be removed to clean up the challenges.yml. -# -# DO NOT CHANGE! -version: 105 +# DO NOT CHANGE THE VERSION! You will break the conversion and unexpected things will happen! +version: 107 diff --git a/uSkyBlock-Core/src/main/resources/config.yml b/uSkyBlock-Core/src/main/resources/config.yml index bc429e80f..8f3ee1020 100644 --- a/uSkyBlock-Core/src/main/resources/config.yml +++ b/uSkyBlock-Core/src/main/resources/config.yml @@ -16,7 +16,10 @@ options: biomeChange: 60 # [string] The default biome assigned to new islands - defaultBiome: OCEAN + defaultBiome: ocean + + # [string] The default biome assigned to new islands + defaultNetherBiome: nether_wastes # [integer] The number of milliseconds between the same notification is sent to the player. # This is used when events are triggered heavily - i.e. item-pickup-prevention, damage-prevention etc. @@ -49,15 +52,15 @@ options: # [item list] The list of items to place in the chest when a player starts a new island. ITEM_ID:HOW_MANY. # default is 2 ice, 1 watermelon, 1 cactus, 1 lava bucket, 1 red & brown mushroom, 1 pumpkin seed, 1 sugar cane, 1 sign. chestItems: - - ICE:2 - - MELON_SLICE:1 - - CACTUS:1 - - LAVA_BUCKET:1 - - RED_MUSHROOM:1 - - BROWN_MUSHROOM:1 - - PUMPKIN_SEEDS:1 - - SUGAR_CANE:1 - - SIGN:1 + - ice:2 + - melon_slice:1 + - cactus:1 + - lava_bucket:1 + - red_mushroom:1 + - brown_mushroom:1 + - pumpkin_seeds:1 + - sugar_cane:1 + - oak_sign:1 addExtraItems: true islandTeleportDelay: 2 allowPvP: deny @@ -82,34 +85,38 @@ options: spawner: 10 extraPermissions: smallbonus: - - COBBLESTONE:16 - - COOKED_PORKCHOP:5 + - cobblestone:16 + - cooked_porkchop:5 mediumbonus: - - TORCH:16 - - LAVA_BUCKET:1 + - torch:16 + - lava_bucket:1 largebonus: - - DIRT:5 - - SAND:5 + - dirt:5 + - sand:5 giantbonus: - - GRASS:1 - - MYCELIUM:1 + - grass_block:1 + - mycelium:1 extremebonus: - - BONE:8 - - COAL:4 + - bone:8 + - coal:4 donorbonus: - - BOW:1 - - ARROW:32 - - STONE_SWORD:1 + - bow:1 + - arrow:32 + - stone_sword:1 netheraccess: - - OBSIDIAN:14 - - FLINT_AND_STEEL:1 + - obsidian:14 + - flint_and_steel:1 extras: # [true/false] If true, return players that don't have an island (this includes players removed from a party while offline), to the server spawn when they login. # NOTE: Requires EssentialsSpawn or another plugin with the "/spawn" command sendToSpawn: false - # [true/false] If true, a player can right-click on a block of obsidian on their island while holding an empty bucket to remove the obsidian and fill the bucket with lava. This is useful for people that accidently + # [true/false] If true, when a player respawns they will respawn on their island. Will first attempt to respawn them at the closest safe location to their island home, then their island spawn, then the server spawn. + # NOTE: If this is true, sendToSpawn is ignored unless the player has no island or no viable island home or spawn location. + respawnAtIsland: true + + # [true/false] If true, a player can right-click on a block of obsidian on their island while holding an empty bucket to remove the obsidian and fill the bucket with lava. This is useful for people that accidentally # turn their lava into obsidian with a bad cobblestone generator design. Will only work on the player's island and if there are no other obsidian blocks nearby (so can't be used on portals). obsidianToLava: true @@ -273,7 +280,7 @@ options: donor-perks: # the permission node to give to someone 'usb.donor.small': - extraItems: [BONE_MEAL:1] + extraItems: [bone_meal:1] maxPartySize: 5 animals: 84 monsters: 60 @@ -301,7 +308,7 @@ island-schemes: description: The default uSkyBlock island # item to display in the GUI - displayItem: OAK_SAPLING + displayItem: oak_sapling # optional, default true (true enabled in GUI, false disabled in GUI) enabled: true @@ -325,12 +332,12 @@ island-schemes: skySMP: permission: usb.schematic.skysmp description: The original SkySMP island - displayItem: OAK_LEAVES + displayItem: oak_leaves enabled: false index: 3 extraItems: - - OBSIDIAN:14 - - FLINT_AND_STEEL:1 + - obsidian:14 + - flint_and_steel:1 maxPartySize: 4 animals: 64 monsters: 50 @@ -415,6 +422,7 @@ nether: IRON: 0.9 GOLD: 1.5 DIAMOND: 0.2 + NETHERITE: 1.2 # The chances of changing a pigzombie when spawned on a netherbrick spawn-chances: @@ -424,11 +432,17 @@ nether: blaze: 0.2 tool-menu: enabled: true - tool: SAPLING + tool: OAK_SAPLING commands: CHEST: island + CRAFTING_TABLE: challenges BEDROCK: island spawn - WORKBENCH: challenges + +plugin-updates: + # Should we check for updates and log a message if an update is available + check: true + # Possible options: RELEASE or STAGING + branch: RELEASE # Placeholders - enable these to get placeholder substitution # usb_version @@ -441,9 +455,6 @@ tool-menu: # usb_island_golems, usb_island_monsters, usb_island_animals, usb_island_villagers placeholder: - # Hooks directly into DeluxeChatPlaceholderAPI - deluxechatplaceholderapi: false - # Hooks into MVdWPlaceholderAPI mvdwplaceholderapi: false @@ -454,7 +465,7 @@ placeholder: servercommandplaceholder: false # DO NOT TOUCH THE FIELDS BELOW -version: 106 +version: 110 force-replace: options.party.invite-timeout: 100 options.island.islandTeleportDelay: 5 @@ -465,4 +476,4 @@ move-nodes: options.party.leave.confirmation: confirmation.is leave options.island.hopperlimit: options.island.block-limits.hopper options.island.spawnerlimit: options.island.block-limits.mob_spawner - options.island.block-limits.mob_spawner: options.island.block-limits.spawner \ No newline at end of file + options.island.block-limits.mob_spawner: options.island.block-limits.spawner diff --git a/uSkyBlock-Core/src/main/resources/configmenu.yml b/uSkyBlock-Core/src/main/resources/configmenu.yml deleted file mode 100644 index eeb4c1c32..000000000 --- a/uSkyBlock-Core/src/main/resources/configmenu.yml +++ /dev/null @@ -1,121 +0,0 @@ -# -# Config Menu -# =========== -# This file holds configuration of the /usb config menu. -config.yml: - # blacklist contains paths that should be totally excluded from the menu - blacklist: - - version - - force-replace - - dirty - - # readonly contains paths that should not be editable from the menu - readonly: - - options.general.spawnSize - - options.general.worldName - - options.island.protectionRange - - options.island.distance - - options.island.height - - nether.enabled - -integer-menu: - increment: - '-1000': 'EMERALD' - '-100': 'DIAMOND' - '-10': 'GOLD_INGOT' - '-1': 'IRON_INGOT' - '1': 'IRON_INGOT' - '10': 'GOLD_INGOT' - '100': 'DIAMOND' - '1000': 'EMERALD' - number-items: - 0: 'ENDER_PEARL' - 1: 'LEVER' - -keyboard: - en: - - # row 0 - - null - - '{display:{Name:"-"},SkullOwner:{Id:"d59b62b4-0a1d-4475-a5b4-1fb0b58669ff",Properties:{textures:[{Value:"eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMzg2YzljOTc3YTM2ZDg5ZjVmNGU5ZDJhNmQ4YjhkNTg5YTc1MTE4YjFjZjllMzZjNGVhYzdlOWE2MTE2YSJ9fX0="}]}}}' - - '{display:{Name:"!"},SkullOwner:{Id:"0a3628f0-f5b5-4a6f-8058-860006ca8732",Properties:{textures:[{Value:"eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvODdkMTlhYWJmY2ZkOTlmZmFiYTQyMTRjYWVmMjk5NTE2Y2U1MmU2ZDEzYmYyZGRhMTI1OTg1ZTQ4MWI3MmY5In19fQ=="}]}}}' - - '{display:{Name:":"},SkullOwner:{Id:"2f380f4a-64c3-4b2c-af18-6c491692e649",Properties:{textures:[{Value:"eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYmQ4OThjNDBlNDdjNWQyZDc2OTI0MDY1MzYwNzY4MDY1ZDYyNGVlNWI5ZWUwYmU5ZTEyYjk4ZmI3N2M3NiJ9fX0="}]}}}' - - '{display:{Name:"?"},SkullOwner:{Id:"808ac216-799a-4d42-bd68-7c9f0c1e89d1",Properties:{textures:[{Value:"eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvM2FhYjI3Mjg0MGQ3OTBjMmVkMmJlNWM4NjAyODlmOTVkODhlMzE2YjY1YzQ1NmZmNmEzNTE4MGQyZTViZmY2In19fQ=="}]}}}' - - '{display:{Name:"Backspace"},SkullOwner:{Id:"87e36f19-408a-4066-94ef-b0db25b96c48",Properties:{textures:[{Value:"eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZjI1OTliZDk4NjY1OWI4Y2UyYzQ5ODg1MjVjOTRlMTlkZGQzOWZhZDA4YTM4Mjg0YTE5N2YxYjcwNjc1YWNjIn19fQ=="}]}}}' - - - '{display:{Name:"7"},SkullOwner:{Id:"cb9c723d-3663-4ef3-a83c-c7405fddd7c2",Properties:{textures:[{Value:"eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZDRiYTZhYzA3ZDQyMjM3N2E4NTU3OTNmMzZkZWEyZWQyNDAyMjNmNTJmZDE2NDgxODE2MTJlY2QxYTBjZmQ1In19fQ=="}]}}}' - - '{display:{Name:"8"},SkullOwner:{Id:"36118da7-6989-4602-af43-82589e1ed6cf",Properties:{textures:[{Value:"eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYzYxYThhNjQxNDM3YmU5YWVhMjA3MjUzZGQzZjI1NDQwZDk1NGVhMmI1ODY2YzU1MmYzODZiMjlhYzRkMDQ5In19fQ=="}]}}}' - - '{display:{Name:"9"},SkullOwner:{Id:"8b867e70-124a-4a4f-9782-3b5581ab709b",Properties:{textures:[{Value:"eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYTE5MjhlMWJmZDg2YTliNzkzOTdjNGNiNGI2NWVmOTlhZjQ5YjdkNWY3OTU3YWQ2MmMwYzY5OWE2MjJjZmJlIn19fQ=="}]}}}' - - - # row 1 - - '{display:{Name:"A"},SkullOwner:{Id:"e8e10bc5-b94e-4378-a54c-ac71a662fec9",Properties:{textures:[{Value:"eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYTY3ZDgxM2FlN2ZmZTViZTk1MWE0ZjQxZjJhYTYxOWE1ZTM4OTRlODVlYTVkNDk4NmY4NDk0OWM2M2Q3NjcyZSJ9fX0="}]}}}' - - '{display:{Name:"B"},SkullOwner:{Id:"d0f793ff-b041-427c-bc24-440834e986fa",Properties:{textures:[{Value:"eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNTBjMWI1ODRmMTM5ODdiNDY2MTM5Mjg1YjJmM2YyOGRmNjc4NzEyM2QwYjMyMjgzZDg3OTRlMzM3NGUyMyJ9fX0="}]}}}' - - '{display:{Name:"C"},SkullOwner:{Id:"db8ddc48-84d7-4f98-bce3-0fb2d43cd4dd",Properties:{textures:[{Value:"eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYWJlOTgzZWM0NzgwMjRlYzZmZDA0NmZjZGZhNDg0MjY3NjkzOTU1MWI0NzM1MDQ0N2M3N2MxM2FmMThlNmYifX19"}]}}}' - - '{display:{Name:"D"},SkullOwner:{Id:"c142dbd3-dbdf-4f46-a491-12162d3a3e8e",Properties:{textures:[{Value:"eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMzE5M2RjMGQ0YzVlODBmZjlhOGEwNWQyZmNmZTI2OTUzOWNiMzkyNzE5MGJhYzE5ZGEyZmNlNjFkNzEifX19"}]}}}' - - '{display:{Name:"E"},SkullOwner:{Id:"96eaca3a-0f22-484f-bad7-46153597e191",Properties:{textures:[{Value:"eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZGJiMjczN2VjYmY5MTBlZmUzYjI2N2RiN2Q0YjMyN2YzNjBhYmM3MzJjNzdiZDBlNGVmZjFkNTEwY2RlZiJ9fX0="}]}}}' - - '{display:{Name:"F"},SkullOwner:{Id:"5d098233-5c27-4e8a-a36f-959c9e35041b",Properties:{textures:[{Value:"eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYjE4M2JhYjUwYTMyMjQwMjQ4ODZmMjUyNTFkMjRiNmRiOTNkNzNjMjQzMjU1OWZmNDllNDU5YjRjZDZhIn19fQ=="}]}}}' - - - '{display:{Name:"4"},SkullOwner:{Id:"c2ba9ae6-7c8e-4cb0-9e79-459e2f6b96ec",Properties:{textures:[{Value:"eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNjJiZmNmYjQ4OWRhODY3ZGNlOTZlM2MzYzE3YTNkYjdjNzljYWU4YWMxZjlhNWE4YzhhYzk1ZTRiYTMifX19"}]}}}' - - '{display:{Name:"5"},SkullOwner:{Id:"463ae31c-73d3-4b97-a4e0-03777f8a43d2",Properties:{textures:[{Value:"eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZWY0ZWNmMTEwYjBhY2VlNGFmMWRhMzQzZmIxMzZmMWYyYzIxNjg1N2RmZGE2OTYxZGVmZGJlZTdiOTUyOCJ9fX0="}]}}}' - - '{display:{Name:"6"},SkullOwner:{Id:"e3cb6368-4c87-4e9b-a329-fbdca086bff1",Properties:{textures:[{Value:"eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZjMzMWE2YTZmY2Q2OTk1YjYyMDg4ZDM1M2JmYjY4ZDliODlhZTI1ODMyNWNhZjNmMjg4NjQ2NGY1NGE3MzI5In19fQ=="}]}}}' - - - # row 2 - - '{display:{Name:"G"},SkullOwner:{Id:"0b017eae-cd4d-48eb-9407-863e794c4bac",Properties:{textures:[{Value:"eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMWNhM2YzMjRiZWVlZmI2YTBlMmM1YjNjNDZhYmM5MWNhOTFjMTRlYmE0MTlmYTQ3NjhhYzMwMjNkYmI0YjIifX19"}]}}}' - - '{display:{Name:"H"},SkullOwner:{Id:"ba7a5f3c-db71-4827-8612-ce6bd82ee992",Properties:{textures:[{Value:"eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMzFmMzQ2MmE0NzM1NDlmMTQ2OWY4OTdmODRhOGQ0MTE5YmM3MWQ0YTVkODUyZTg1YzI2YjU4OGE1YzBjNzJmIn19fQ=="}]}}}' - - '{display:{Name:"I"},SkullOwner:{Id:"2ab25358-deaa-46b7-b351-d9732a448ff1",Properties:{textures:[{Value:"eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDYxNzhhZDUxZmQ1MmIxOWQwYTM4ODg3MTBiZDkyMDY4ZTkzMzI1MmFhYzZiMTNjNzZlN2U2ZWE1ZDMyMjYifX19"}]}}}' - - '{display:{Name:"J"},SkullOwner:{Id:"be1f1677-af64-4f59-85be-244f28eb1306",Properties:{textures:[{Value:"eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvM2E3OWRiOTkyMzg2N2U2OWMxZGJmMTcxNTFlNmY0YWQ5MmNlNjgxYmNlZGQzOTc3ZWViYmM0NGMyMDZmNDkifX19"}]}}}' - - '{display:{Name:"K"},SkullOwner:{Id:"4f264d8d-87a3-496a-ad1d-eda0a22a30f0",Properties:{textures:[{Value:"eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOTQ2MWIzOGM4ZTQ1NzgyYWRhNTlkMTYxMzJhNDIyMmMxOTM3NzhlN2Q3MGM0NTQyYzk1MzYzNzZmMzdiZTQyIn19fQ=="}]}}}' - - '{display:{Name:"L"},SkullOwner:{Id:"75ae0d27-f43c-416d-96a0-fdd6dbba7ab0",Properties:{textures:[{Value:"eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMzE5ZjUwYjQzMmQ4NjhhZTM1OGUxNmY2MmVjMjZmMzU0MzdhZWI5NDkyYmNlMTM1NmM5YWE2YmIxOWEzODYifX19"}]}}}' - - - '{display:{Name:"1"},SkullOwner:{Id:"cc70838c-9905-4fad-865f-8a5f83430272",Properties:{textures:[{Value:"eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMzFhOTQ2M2ZkM2M0MzNkNWUxZDlmZWM2ZDVkNGIwOWE4M2E5NzBiMGI3NGRkNTQ2Y2U2N2E3MzM0OGNhYWIifX19"}]}}}' - - '{display:{Name:"2"},SkullOwner:{Id:"8d5350b9-6464-4f4b-999e-462af24dc872",Properties:{textures:[{Value:"eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYWNiNDE5ZDk4NGQ4Nzk2MzczYzk2NDYyMzNjN2EwMjY2NGJkMmNlM2ExZDM0NzZkZDliMWM1NDYzYjE0ZWJlIn19fQ=="}]}}}' - - '{display:{Name:"3"},SkullOwner:{Id:"76480708-e78b-4bb9-9987-131f4f5b50f6",Properties:{textures:[{Value:"eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZjhlYmFiNTdiNzYxNGJiMjJhMTE3YmU0M2U4NDhiY2QxNGRhZWNiNTBlOGY1ZDA5MjZlNDg2NGRmZjQ3MCJ9fX0="}]}}}' - - - # row 3 - - '{display:{Name:"M"},SkullOwner:{Id:"a291bc00-e8d7-42cf-8d57-588291f3ebc2",Properties:{textures:[{Value:"eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDljNDVhMjRhYWFiZjQ5ZTIxN2MxNTQ4MzIwNDg0OGE3MzU4MmFiYTdmYWUxMGVlMmM1N2JkYjc2NDgyZiJ9fX0="}]}}}' - - '{display:{Name:"N"},SkullOwner:{Id:"439f4d78-dedd-427f-9973-c3906c4df529",Properties:{textures:[{Value:"eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMzViOGIzZDhjNzdkZmI4ZmJkMjQ5NWM4NDJlYWM5NGZmZmE2ZjU5M2JmMTVhMjU3NGQ4NTRkZmYzOTI4In19fQ=="}]}}}' - - '{display:{Name:"O"},SkullOwner:{Id:"4ec45cf0-b927-444b-85fb-f5910bfae296",Properties:{textures:[{Value:"eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZDExZGUxY2FkYjJhZGU2MTE0OWU1ZGVkMWJkODg1ZWRmMGRmNjI1OTI1NWIzM2I1ODdhOTZmOTgzYjJhMSJ9fX0="}]}}}' - - '{display:{Name:"P"},SkullOwner:{Id:"6c22192b-5af9-4d07-a96f-f95b9e81d163",Properties:{textures:[{Value:"eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYTBhNzk4OWI1ZDZlNjIxYTEyMWVlZGFlNmY0NzZkMzUxOTNjOTdjMWE3Y2I4ZWNkNDM2MjJhNDg1ZGMyZTkxMiJ9fX0="}]}}}' - - '{display:{Name:"Q"},SkullOwner:{Id:"047b3876-eac3-414b-8978-1c083fd8df86",Properties:{textures:[{Value:"eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDM2MDlmMWZhZjgxZWQ0OWM1ODk0YWMxNGM5NGJhNTI5ODlmZGE0ZTFkMmE1MmZkOTQ1YTU1ZWQ3MTllZDQifX19"}]}}}' - - '{display:{Name:"R"},SkullOwner:{Id:"9af104ca-dfbb-4616-9101-d10016c2b11a",Properties:{textures:[{Value:"eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYTVjZWQ5OTMxYWNlMjNhZmMzNTEzNzEzNzliZjA1YzYzNWFkMTg2OTQzYmMxMzY0NzRlNGU1MTU2YzRjMzcifX19"}]}}}' - - - '{display:{Name:"/"},SkullOwner:{Id:"015eeece-cc8f-46a1-acd5-cf010870730a",Properties:{textures:[{Value:"eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvN2Y5NWQ3YzFiYmYzYWZhMjg1ZDhkOTY3NTdiYjU1NzIyNTlhM2FlODU0ZjUzODlkYzUzMjA3Njk5ZDk0ZmQ4In19fQ=="}]}}}' - - '{display:{Name:"0"},SkullOwner:{Id:"a58ddb02-267b-4122-b8cc-5a6fbef9db04",Properties:{textures:[{Value:"eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNTVhMjI0ODA3NjkzOTc4ZWQ4MzQzNTVmOWU1MTQ1ZjljNTZlZjY4Y2Y2ZjJjOWUxNzM0YTQ2ZTI0NmFhZTEifX19"}]}}}' - - '{display:{Name:"."},SkullOwner:{Id:"361e6180-ceac-4838-89e5-be46fb40fe84",Properties:{textures:[{Value:"eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNzMzYWEyNDkxNmM4ODY5NmVlNzFkYjdhYzhjZDMwNmFkNzMwOTZiNWI2ZmZkODY4ZTFjMzg0YjFkNjJjZmIzYyJ9fX0="}]}}}' - - - # row 4 - - '{display:{Name:"S"},SkullOwner:{Id:"826bfe2f-387c-44d7-9cd3-fc3046a6204d",Properties:{textures:[{Value:"eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvM2U0MWM2MDU3MmM1MzNlOTNjYTQyMTIyODkyOWU1NGQ2Yzg1NjUyOTQ1OTI0OWMyNWMzMmJhMzNhMWIxNTE3In19fQ=="}]}}}' - - '{display:{Name:"T"},SkullOwner:{Id:"08c08ff3-95f3-44cc-8da3-d0e1331cf469",Properties:{textures:[{Value:"eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMTU2MmU4YzFkNjZiMjFlNDU5YmU5YTI0ZTVjMDI3YTM0ZDI2OWJkY2U0ZmJlZTJmNzY3OGQyZDNlZTQ3MTgifX19"}]}}}' - - '{display:{Name:"U"},SkullOwner:{Id:"dd376034-312d-4515-9c70-eafbba1a12e4",Properties:{textures:[{Value:"eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNjA3ZmJjMzM5ZmYyNDFhYzNkNjYxOWJjYjY4MjUzZGZjM2M5ODc4MmJhZjNmMWY0ZWZkYjk1NGY5YzI2In19fQ=="}]}}}' - - '{display:{Name:"V"},SkullOwner:{Id:"18724f9a-8aab-4f3f-a0ff-26e4b704d53f",Properties:{textures:[{Value:"eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvY2M5YTEzODYzOGZlZGI1MzRkNzk5Mjg4NzZiYWJhMjYxYzdhNjRiYTc5YzQyNGRjYmFmY2M5YmFjNzAxMGI4In19fQ=="}]}}}' - - '{display:{Name:"W"},SkullOwner:{Id:"20816a64-5743-4624-8d5e-00549a64179b",Properties:{textures:[{Value:"eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMjY5YWQxYTg4ZWQyYjA3NGUxMzAzYTEyOWY5NGU0YjcxMGNmM2U1YjRkOTk1MTYzNTY3ZjY4NzE5YzNkOTc5MiJ9fX0="}]}}}' - - '{display:{Name:"X"},SkullOwner:{Id:"33b5946c-20d1-466f-8dd5-11a0fdcd7105",Properties:{textures:[{Value:"eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNWE2Nzg3YmEzMjU2NGU3YzJmM2EwY2U2NDQ5OGVjYmIyM2I4OTg0NWU1YTY2YjVjZWM3NzM2ZjcyOWVkMzcifX19"}]}}}' - - '{display:{Name:"Y"},SkullOwner:{Id:"579ec4fb-0baf-4716-89ba-1c175fbd6e3c",Properties:{textures:[{Value:"eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYzUyZmIzODhlMzMyMTJhMjQ3OGI1ZTE1YTk2ZjI3YWNhNmM2MmFjNzE5ZTFlNWY4N2ExY2YwZGU3YjE1ZTkxOCJ9fX0="}]}}}' - - '{display:{Name:"Z"},SkullOwner:{Id:"0f3a3e42-4410-48a6-9d46-97392e7a6e27",Properties:{textures:[{Value:"eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOTA1ODJiOWI1ZDk3OTc0YjExNDYxZDYzZWNlZDg1ZjQzOGEzZWVmNWRjMzI3OWY5YzQ3ZTFlMzhlYTU0YWU4ZCJ9fX0="}]}}}' - - null - - null - - - # row 5 - - null # capslock - - null - - '{display:{Name:"<"},SkullOwner:{Id:"21ca358a-0bb9-4441-9c08-aefff75524a8",Properties:{textures:[{Value:"eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvY2ZjOWYyYWE4ZTk3OTg3NDIxZTdmYWIyNDhhZmMyNDJjYmU1MWY5ZWRiZmE0NzFjYTZjMDE0OTRlZGVkYTcifX19"}]}}}' - - '{display:{Name:" "},SkullOwner:{Id:"97ced8c3-6a7d-4478-af9b-f47e6051f357",Properties:{textures:[{Value:"eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMTZiNGI0MjMwYzhmYTI1NGJlNzUyNmEyYjI1M2RjMzUyYTUzYTVhOGVhZTZlZmI1MmVhMzRhOGY5YmQ5In19fQ=="}]}}}' - - '{display:{Name:" "},SkullOwner:{Id:"97ced8c3-6a7d-4478-af9b-f47e6051f357",Properties:{textures:[{Value:"eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMTZiNGI0MjMwYzhmYTI1NGJlNzUyNmEyYjI1M2RjMzUyYTUzYTVhOGVhZTZlZmI1MmVhMzRhOGY5YmQ5In19fQ=="}]}}}' - - '{display:{Name:" "},SkullOwner:{Id:"97ced8c3-6a7d-4478-af9b-f47e6051f357",Properties:{textures:[{Value:"eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMTZiNGI0MjMwYzhmYTI1NGJlNzUyNmEyYjI1M2RjMzUyYTUzYTVhOGVhZTZlZmI1MmVhMzRhOGY5YmQ5In19fQ=="}]}}}' - - null - - '{display:{Name:"&"},SkullOwner:{Id:"b9f96b3b-ff11-4017-b0ce-c0c9872d3556",Properties:{textures:[{Value:"eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMjJmMTQzZTgxZWM4NzI1YmQyNjcyOTNmMmY5N2EzZGFlMTNiNmE4NDhlNjZhZjAxZjJhNTk0ZGUwYzAzYjY4In19fQ=="}]}}}' - capslock: - on: '{display:{Name:"Caps On"},SkullOwner:{Id:"aef9582f-42f0-4566-8626-477245376ca2",Properties:{textures:[{Value:"eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZmE0ODUxYzg3OGJmNDJhMWNkYzAzYmMwNjcxZDExZTlkZDY5NzdlNzM4MTNhZmFmNTRjMjQzMjg5MjFiMSJ9fX0="}]}}}' - off: '{display:{Name:"Caps Off"},SkullOwner:{Id:"a8a8e02f-30f7-4b54-ad11-f1fa8f7830ea",Properties:{textures:[{Value:"eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNWE4ZWY5MmVmYTE1NjY5ZGY0ODMzMmQxMThhMmY3MDU3NzJhMmEzZmNkMGZiODJhNjFmMjc3Yjg0OWEyYmQ2In19fQ=="}]}}}' - - overlay: - 1: '{display:{Name:"+"},SkullOwner:{Id:"80e287ca-69a7-4a03-8811-324a469079b9",Properties:{textures:[{Value:"eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZWUzOTVlNzVlMmViOGM0YjcyNDRkY2RiNzVjNTdhNmQ3YWRmMzc1NTNmMjFkNDRlZmM3YmQ3MTQxODQzNDVkIn19fQ=="}]}}}' - 7: '{display:{Name:"("},SkullOwner:{Id:"dc61f26c-79f0-45b8-be4c-fad9fef6e0d1",Properties:{textures:[{Value:"eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMzA3MWNiMTc3ZDQ1OTdkNGY1MWE2NjM3NWVmOWIwMTQyY2Q2OGMzZTljNjRhZWJiNjJmMTZlM2MzOTAxNDJmIn19fQ=="}]}}}' - 8: '{display:{Name:")"},SkullOwner:{Id:"d9d19efe-cb96-4e6a-a7a0-29aed68e958c",Properties:{textures:[{Value:"eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYzNjNzJlZjBlOTQ0MmM1YjY3YTUwYTY5YjI5ZWUxMDRmODExZmFkYTZiZmVkZTJlY2RiM2I1YjU1NTAifX19"}]}}}' - 16: '{display:{Name:"''{''"},SkullOwner:{Id:"5e1368b1-c858-4748-b2bb-e92c76700554",Properties:{textures:[{Value:"eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvODY5OTMyY2ZhZTc1ZDM4ZjdjNzFmN2Y5NTNkMThmNmI2MGZjYjg4NThkMWQ5YjQ5YTE1ZDc2NmJjY2IyNThmNiJ9fX0="}]}}}' - 17: '{display:{Name:"''}''"},SkullOwner:{Id:"4fbb057e-7eeb-4cfb-8cfc-ad2739fddedf",Properties:{textures:[{Value:"eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZGJiOGIxMWQ2NWUwZDA2OGQzYTk0OWM0MWVmNGQxMmUxODNjZmFlZWE0NGZmYzY4ZWM0MzliNWQ5NzcxMjcifX19"}]}}}' - 25: '{display:{Name:"["},SkullOwner:{Id:"68f2f3b4-e105-4887-9765-68200e7a831b",Properties:{textures:[{Value:"eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZTM0YTlmYTlkMmNlNTY4YWUyYjIxZWRhMGNiMzM5NGZlNTJkNjc4M2I0ZmViYWFhOTFmMjg2ZmY5MzMyIn19fQ=="}]}}}' - 26: '{display:{Name:"]"},SkullOwner:{Id:"00962524-4195-4480-9a1b-f9e67178dca9",Properties:{textures:[{Value:"eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMTQ4YjY5ZWMxNTViMTg3Mzc2Yjg4YmMwMzg0ZDI1NTM4ZGRlZDlkYjgzOWVhYmI1OWU0NjE4ZWE0NiJ9fX0="}]}}}' - 31: '{display:{Name:"*"},SkullOwner:{Id:"0fa2efdb-cfca-4853-8335-9f0ae1e70b49",Properties:{textures:[{Value:"eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZmYwZThlZmExY2QwNzI4Mjk3NDdhNjk3NzM3OTE2OTVmODBjZmFlOGJmYTg3MmZiMWIyOGEyMTFlZDAzMSJ9fX0="}]}}}' - 47: '{display:{Name:">"},SkullOwner:{Id:"6d6a08e5-e1ad-4c81-b4cc-e7fbee8766db",Properties:{textures:[{Value:"eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYzEwZTE0OWI2NTE2MzgyZTM2ZGQ2YzRjOTk3ZDg4OWQwNDZkMTNiNTQzM2Y1YWZlYjNiYTVmYjlkYWI0MmY5In19fQ=="}]}}}' -# DO NOT TOUCH THESE -version: 2 \ No newline at end of file diff --git a/uSkyBlock-Core/src/main/resources/levelConfig.yml b/uSkyBlock-Core/src/main/resources/levelConfig.yml index 69f56ef16..ef24b430f 100644 --- a/uSkyBlock-Core/src/main/resources/levelConfig.yml +++ b/uSkyBlock-Core/src/main/resources/levelConfig.yml @@ -26,25 +26,77 @@ blocks: diminishingReturns: 100 ACACIA_STAIRS: score: 15.0 + ACACIA_TRAPDOOR: + score: 30.0 + ACACIA_WOOD: + additionalBlocks: + - STRIPPED_ACACIA_WOOD + diminishingReturns: 10000 ACTIVATOR_RAIL: score: 20.0 AIR: + additionalBlocks: + - CAVE_AIR + - VOID_AIR score: 0.0 limit: 0 - ANVIL/0-2: + ALLIUM: + diminishingReturns: 100 + AMETHYST_BLOCK: + score: 50.0 + ANCIENT_DEBRIS: + score: 500.0 + ANDESITE: + score: 12.5 + diminishingReturns: 10000 + ANDESITE_SLAB: + score: 6.5 + diminishingReturns: 10000 + ANDESITE_STAIRS: + score: 15.0 + diminishingReturns: 5000 + ANDESITE_WALL: + score: 12.5 + diminishingReturns: 5000 + ANVIL: + additionalBlocks: + - CHIPPED_ANVIL + - DAMAGED_ANVIL score: 150.0 limit: 5 + AZALEA: + diminishingReturns: 100 + AZURE_BLUET: + diminishingReturns: 100 + BAMBOO: + additionalBlocks: + - BAMBOO_SAPLING + limit: 1000 + BARREL: + score: 12.5 BARRIER: score: 0.0 + BASALT: + limit: 10000 BEACON: score: 2000.0 limit: 10 negativeReturns: 15 BEDROCK: score: 0.0 - BED_BLOCK: - score: 1000.0 - limit: 1 + BEEHIVE: + additionalBlocks: + - BEE_NEST + score: 30.0 + limit: 200 + BEETROOTS: + score: 0.0 + BELL: + score: 50.0 + BIG_DRIPLEAF: + additionalBlocks: + - SMALL_DRIPLEAF + diminishingReturns: 100 BIRCH_DOOR: score: 20.0 BIRCH_FENCE: @@ -53,52 +105,238 @@ blocks: BIRCH_FENCE_GATE: score: 40.0 diminishingReturns: 100 - BIRCH_WOOD_STAIRS: + BIRCH_STAIRS: + score: 15.0 + BIRCH_TRAPDOOR: + score: 30.0 + BIRCH_WOOD: + additionalBlocks: + - STRIPPED_BIRCH_WOOD + diminishingReturns: 10000 + BLACKSTONE: + limit: 10000 + BLACKSTONE_SLAB: + score: 5.0 + diminishingReturns: 10000 + BLACKSTONE_STAIRS: score: 15.0 + diminishingReturns: 5000 + BLACKSTONE_WALL: + diminishingReturns: 5000 + BLAST_FURNACE: + score: 50.0 + limit: 250 + BLUE_ICE: + score: 50.0 + BLUE_ORCHID: + diminishingReturns: 100 BOOKSHELF: score: 50.0 BREWING_STAND: score: 30.0 limit: 10 - BRICK: - score: 50.0 + BRICK_SLAB: + score: 25.0 + diminishingReturns: 10000 BRICK_STAIRS: score: 75.0 diminishingReturns: 5000 + BRICK_WALL: + score: 50.0 + diminishingReturns: 5000 + BRICKS: + score: 50.0 BROWN_MUSHROOM: diminishingReturns: 100 + BROWN_MUSHROOM_BLOCK: + diminishingReturns: 100 CACTUS: score: 0.0 - CAKE_BLOCK: + CAKE: score: 50.0 - CARPET: + CALCITE: score: 20.0 - diminishingReturns: 5000 - CARROT: + CAMPFIRE: + score: 15.0 + limit: 250 + CARTOGRAPHY_TABLE: + score: 15.0 + limit: 250 + CARROTS: score: 0.0 CAULDRON: + additionalBlocks: + - LAVA_CAULDRON + - WATER_CAULDRON + - POWDER_SNOW_CAULDRON score: 155.0 limit: 10 + CHAIN: + score: 20.0 + limit: 1000 + CHEST: + score: 15.0 + CHISELED_DEEPSLATE: + score: 17.5 + diminishingReturns: 10000 + CHISELED_NETHER_BRICKS: + score: 20.0 + diminishingReturns: 10000 + CHISELED_POLISHED_BLACKSTONE: + score: 12.0 + diminishingReturns: 10000 + CHISELED_RED_SANDSTONE: + score: 25.0 + diminishingReturns: 10000 + CHISELED_SANDSTONE: + score: 80.0 + diminishingReturns: 10000 + CHISELED_STONE_BRICKS: + additionalBlocks: + - INFESTED_CHISELED_STONE_BRICKS + score: 12.0 + diminishingReturns: 10000 CLAY: score: 50.0 COAL_BLOCK: score: 500.0 diminishingReturns: 100 COAL_ORE: + additionalBlocks: + - DEEPSLATE_COAL_ORE score: 200.0 + COBWEB: + score: 20.0 + COARSE_DIRT: + score: 25.0 + diminishingReturns: 10000 + COBBLED_DEEPSLATE: + diminishingReturns: 10000 + COBBLED_DEEPSLATE_SLAB: + score: 5.0 + diminishingReturns: 10000 + COBBLED_DEEPSLATE_STAIRS: + score: 15.0 + diminishingReturns: 5000 + COBBLED_DEEPSLATE_WALL: + diminishingReturns: 5000 COBBLESTONE: + additionalBlocks: + - INFESTED_COBBLESTONE limit: 10000 + COBBLESTONE_SLAB: + score: 5.0 + diminishingReturns: 10000 COBBLESTONE_STAIRS: score: 15.0 diminishingReturns: 5000 - COBBLE_WALL: + COBBLESTONE_WALL: diminishingReturns: 5000 COCOA: score: 0.0 - COMMAND: + COMMAND_BLOCK: + additionalBlocks: + - CHAIN_COMMAND_BLOCK + - REPEATING_COMMAND_BLOCK + - STRUCTURE_BLOCK + - STRUCTURE_VOID + score: 0.0 + COMPARATOR: + score: 15.0 + COMPOSTER: + limit: 250 + CONDUIT: + score: 2000.0 + limit: 10 + negativeReturns: 15 + COPPER_BLOCK: + additionalBlocks: + - EXPOSED_COPPER + - WEATHERED_COPPER + - OXIDIZED_COPPER + score: 300.0 + COPPER_ORE: + additionalBlocks: + - DEEPSLATE_COPPER_ORE + score: 200.0 + CORNFLOWER: + diminishingReturns: 100 + CRACKED_DEEPSLATE_BRICKS: + score: 17.5 + diminishingReturns: 10000 + CRACKED_DEEPSLATE_TILES: + score: 17.5 + diminishingReturns: 10000 + CRACKED_NETHER_BRICKS: + score: 22.5 + diminishingReturns: 10000 + CRACKED_POLISHED_BLACKSTONE_BRICKS: + score: 12.0 + diminishingReturns: 10000 + CRACKED_STONE_BRICKS: + additionalBlocks: + - INFESTED_CRACKED_STONE_BRICKS + score: 15.0 + diminishingReturns: 10000 + CRIMSON_DOOR: + score: 20.0 + CRIMSON_FENCE: + score: 17.5 + diminishingReturns: 5000 + CRIMSON_FENCE_GATE: + score: 40.0 + diminishingReturns: 100 + CRIMSON_FUNGUS: score: 0.0 - CROPS: + CRIMSON_HYPHAE: + additionalBlocks: + - STRIPPED_CRIMSON_HYPHAE + diminishingReturns: 10000 + CRIMSON_NYLIUM: + score: 15.0 + diminishingReturns: 10000 + CRIMSON_ROOTS: score: 0.0 + CRIMSON_STAIRS: + score: 15.0 + CRIMSON_STEM: + additionalBlocks: + - STRIPPED_CRIMSON_STEM + diminishingReturns: 100 + CRIMSON_TRAPDOOR: + score: 30.0 + CRYING_OBSIDIAN: + score: 100.0 + CUT_COPPER: + additionalBlocks: + - EXPOSED_CUT_COPPER + - WEATHERED_CUT_COPPER + - OXIDIZED_CUT_COPPER + score: 300.0 + CUT_COPPER_SLAB: + additionalBlocks: + - EXPOSED_CUT_COPPER_SLAB + - WEATHERED_CUT_COPPER_SLAB + - OXIDIZED_CUT_COPPER_SLAB + score: 150.0 + CUT_COPPER_STAIRS: + additionalBlocks: + - EXPOSED_CUT_COPPER_STAIRS + - WEATHERED_CUT_COPPER_STAIRS + - OXIDIZED_CUT_COPPER_STAIRS + score: 175.0 + CUT_RED_SANDSTONE: + score: 25.0 + CUT_RED_SANDSTONE_SLAB: + score: 12.5 + diminishingReturns: 10000 + CUT_SANDSTONE: + score: 80.0 + CUT_SANDSTONE_SLAB: + score: 40.0 + diminishingReturns: 10000 + DANDELION: + diminishingReturns: 100 DARK_OAK_DOOR: score: 20.0 DARK_OAK_FENCE: @@ -109,118 +347,183 @@ blocks: diminishingReturns: 100 DARK_OAK_STAIRS: score: 15.0 - DAYLIGHT_DETECTOR: - score: 200.0 - DAYLIGHT_DETECTOR_INVERTED: - score: 200.0 - DETECTOR_RAIL: - score: 20.0 - DIAMOND_BLOCK: - score: 5000.0 - DIAMOND_ORE: - score: 400.0 - DIRT: - score: 20.0 + DARK_OAK_TRAPDOOR: + score: 30.0 + DARK_OAK_WOOD: + additionalBlocks: + - STRIPPED_DARK_OAK_WOOD diminishingReturns: 10000 - DIRT/1: - score: 25.0 + DARK_PRISMARINE: + score: 50.0 diminishingReturns: 10000 - DIRT/2: + DARK_PRISMARINE_SLAB: score: 25.0 diminishingReturns: 10000 - DOUBLE_STEP/0: + DARK_PRISMARINE_STAIRS: + score: 75.0 + diminishingReturns: 5000 + DAYLIGHT_DETECTOR: + score: 200.0 + DEEPSLATE: + additionalBlocks: + - INFESTED_DEEPSLATE + score: 15.0 + diminishingReturns: 10000 + DEEPSLATE_BRICK_SLAB: + score: 8.5 diminishingReturns: 10000 - DOUBLE_STEP/1: + DEEPSLATE_BRICK_STAIRS: score: 20.0 + diminishingReturns: 5000 + DEEPSLATE_BRICK_WALL: + score: 17.5 + diminishingReturns: 5000 + DEEPSLATE_BRICKS: + score: 17.5 diminishingReturns: 10000 - DOUBLE_STEP/2: + DEEPSLATE_TILE_SLAB: + score: 8.5 diminishingReturns: 10000 - DOUBLE_STEP/3: + DEEPSLATE_TILE_STAIRS: + score: 20.0 + diminishingReturns: 5000 + DEEPSLATE_TILE_WALL: + score: 17.5 + diminishingReturns: 5000 + DEEPSLATE_TILES: + score: 17.5 diminishingReturns: 10000 - DOUBLE_STEP/4: - score: 50.0 + DETECTOR_RAIL: + score: 20.0 + DIAMOND_BLOCK: + score: 5000.0 + DIAMOND_ORE: + additionalBlocks: + - DEEPSLATE_DIAMOND_ORE + score: 400.0 + DIORITE: + score: 12.5 diminishingReturns: 10000 - DOUBLE_STEP/5: - score: 15.0 + DIORITE_SLAB: + score: 6.5 diminishingReturns: 10000 - DOUBLE_STEP/6: + DIORITE_STAIRS: + score: 15.0 + diminishingReturns: 5000 + DIORITE_WALL: + score: 12.5 + diminishingReturns: 5000 + DIRT: + additionalBlocks: + - FARMLAND + - ROOTED_DIRT score: 20.0 diminishingReturns: 10000 - DOUBLE_STEP/7: - score: 100.0 - diminishingReturns: 10000 - DOUBLE_STONE_SLAB2: + DIRT_PATH: score: 20.0 - diminishingReturns: 20000 + diminishingReturns: 5000 DRAGON_EGG: score: 5000.0 limit: 2 + DRIED_KELP_BLOCK: + score: 20.0 + DRIPSTONE_BLOCK: + score: 15.0 EMERALD_BLOCK: score: 1000.0 EMERALD_ORE: + additionalBlocks: + - DEEPSLATE_EMERALD_ORE score: 200.0 - ENCHANTMENT_TABLE: + ENCHANTING_TABLE: score: 150.0 - ENDER_CHEST: - score: 2000.0 - limit: 3 - ENDER_PORTAL: - score: 0.0 - ENDER_PORTAL_FRAME: + END_PORTAL: + additionalBlock: + - END_PORTAL_FRAME score: 0.0 - ENDER_STONE: + END_STONE: score: 15.0 limit: 10000 - FENCE: - score: 17.5 + END_STONE_BRICK_SLAB: + score: 7.5 + diminishingReturns: 10000 + END_STONE_BRICK_STAIRS: + score: 20.0 diminishingReturns: 5000 - FENCE_GATE: - score: 40.0 + END_STONE_BRICK_WALL: + score: 15.0 + diminishingReturns: 5000 + END_STONE_BRICKS: + score: 15.0 + limit: 10000 + ENDER_CHEST: + score: 2000.0 + limit: 3 + FERN: diminishingReturns: 100 FIRE: score: 0.0 + FLETCHING_TABLE: + score: 15.0 + limit: 250 + FROSTED_ICE: + score: 0.0 + GILDED_BLACKSTONE: + score: 50.0 GLASS: score: 50.0 + GLASS_PANE: + score: 10.0 GLOWSTONE: score: 20.0 + GLOW_LICHEN: + diminishingReturns: 100 GOLD_BLOCK: score: 1500.0 diminishingReturns: 100 GOLD_ORE: + additionalBlocks: + - DEEPSLATE_GOLD_ORE score: 500.0 - GOLD_PLATE: - score: 115.0 - GRASS: + GRANITE: + score: 12.5 + diminishingReturns: 10000 + GRANITE_SLAB: + score: 6.5 + diminishingReturns: 10000 + GRANITE_STAIRS: + score: 15.0 + diminishingReturns: 5000 + GRANITE_WALL: + score: 12.5 + diminishingReturns: 5000 + GRASS_BLOCK: score: 20.0 diminishingReturns: 10000 GRAVEL: score: 20.0 - HARD_CLAY: - score: 20.0 + GRINDSTONE: + limit: 250 HAY_BLOCK: score: 20.0 + HEAVY_WEIGHTED_PRESSURE_PLATE: + score: 50.0 HOPPER: score: 125.0 limit: 10 negativeReturns: 5 - HUGE_MUSHROOM_1: - score: 100.0 - diminishingReturns: 30 - HUGE_MUSHROOM_2: - score: 100.0 - diminishingReturns: 30 ICE: score: 50.0 + IRON_BARS: + score: 12.5 IRON_BLOCK: score: 300.0 - IRON_DOOR_BLOCK: + IRON_DOOR: score: 200.0 - IRON_FENCE: - score: 12.5 IRON_ORE: + additionalBlocks: + - DEEPSLATE_IRON_ORE score: 200.0 - IRON_PLATE: - score: 50.0 IRON_TRAPDOOR: score: 20.0 diminishingReturns: 10 @@ -236,150 +539,521 @@ blocks: JUNGLE_FENCE_GATE: score: 40.0 diminishingReturns: 100 - JUNGLE_WOOD_STAIRS: + JUNGLE_STAIRS: score: 15.0 + JUNGLE_TRAPDOOR: + score: 30.0 + JUNGLE_WOOD: + additionalBlocks: + - STRIPPED_JUNGLE_WOOD + diminishingReturns: 10000 + KELP: + additionalBlocks: + - KELP_PLANT + score: 0.0 LADDER: score: 0.0 + LANTERN: + score: 25.0 LAPIS_BLOCK: score: 500.0 LAPIS_ORE: + additionalBlocks: + - DEEPSLATE_LAPIS_ORE score: 300.0 LAVA: score: 0.0 - LEAVES/0-3: - score: 0.0 - LEAVES_2/0-1: - score: 0.0 - LOG/0-5: - additionalBlocks: - - LOG_2 - diminishingReturns: 100 - LOG_2: - diminishingReturns: 100 - LOG_2/1: + LECTERN: + score: 55.0 + limit: 250 + LEVER: + score: 5.0 + limit: 250 + LIGHT_WEIGHTED_PRESSURE_PLATE: + score: 115.0 + LILAC: diminishingReturns: 100 - LONG_GRASS: + LILY_OF_THE_VALLEY: diminishingReturns: 100 - MELON_BLOCK: + LODESTONE: + score: 500.0 + limit: 5 + LOOM: + limit: 250 + MAGMA_BLOCK: + score: 15.0 + MELON: score: 0.0 MELON_STEM: + additionalBlocks: + - ATTACHED_MELON_STEM score: 0.0 - MOSSY_COBBLESTONE: - score: 30.0 - MYCEL: + MOSS_BLOCK: score: 20.0 diminishingReturns: 10000 - NETHERRACK: - limit: 10000 - NETHER_BRICK_SLAB: - score: 10.0 - diminishingReturns: 20000 - NETHER_BRICK_FENCE: + MOSS_CARPET: + score: 15.0 + diminishingReturns: 5000 + MOSSY_COBBLESTONE: score: 30.0 + MOSSY_COBBLESTONE_SLAB: + score: 15.0 diminishingReturns: 10000 - NETHER_BRICKS: + MOSSY_COBBLESTONE_STAIRS: score: 20.0 - diminishingReturns: 20000 - NETHER_BRICK_STAIRS: + diminishingReturns: 5000 + MOSSY_COBBLESTONE_WALL: score: 30.0 diminishingReturns: 5000 - NETHER_FENCE: - score: 60.0 + MOSSY_STONE_BRICK_SLAB: + score: 16.0 + diminishingReturns: 10000 + MOSSY_STONE_BRICK_STAIRS: + score: 21.0 diminishingReturns: 5000 - NOTE_BLOCK: + MOSSY_STONE_BRICK_WALL: + score: 32.0 + diminishingReturns: 5000 + MOSSY_STONE_BRICKS: + additionalBlocks: + - INFESTED_MOSSY_STONE_BRICKS + score: 32.0 + MUSHROOM_STEM: + diminishingReturns: 100 + MYCELIUM: score: 20.0 - OBSIDIAN: + diminishingReturns: 10000 + NETHER_BRICK_FENCE: + score: 30.0 + diminishingReturns: 10000 + NETHER_BRICK_SLAB: + score: 10.0 + diminishingReturns: 20000 + NETHER_BRICK_STAIRS: + score: 30.0 + diminishingReturns: 5000 + NETHER_BRICK_WALL: + score: 20.0 + diminishingReturns: 5000 + NETHER_BRICKS: + score: 20.0 + diminishingReturns: 20000 + NETHER_GOLD_ORE: + score: 500.0 + NETHER_PORTAL: + score: 0.0 + NETHER_QUARTZ_ORE: + score: 50.0 + NETHER_WART: + score: 0.0 + NETHERITE_BLOCK: + score: 5000.0 + NETHERRACK: + limit: 10000 + NOTE_BLOCK: + score: 20.0 + OAK_DOOR: + score: 20.0 + OAK_FENCE: + score: 17.5 + diminishingReturns: 5000 + OAK_FENCE_GATE: + score: 40.0 + diminishingReturns: 100 + OAK_LEAVES: + additionalBlocks: + - ACACIA_LEAVES + - BIRCH_LEAVES + - DARK_OAK_LEAVES + - JUNGLE_LEAVES + - SPRUCE_LEAVES + - AZALEA_LEAVES + - FLOWERING_AZALEA_LEAVES + score: 0.0 + OAK_LOG: + additionalBlocks: + - ACACIA_LOG + - BIRCH_LOG + - DARK_OAK_LOG + - JUNGLE_LOG + - SPRUCE_LOG + diminishingReturns: 100 + OAK_PRESSURE_PLATE: + additionalBlocks: + - ACACIA_PRESSURE_PLATE + - BIRCH_PRESSURE_PLATE + - CRIMSON_PRESSURE_PLATE + - DARK_OAK_PRESSURE_PLATE + - JUNGLE_PRESSURE_PLATE + - SPRUCE_PRESSURE_PLATE + - WARPED_PRESSURE_PLATE + score: 0.0 + OAK_SAPLING: + additionalBlocks: + - ACACIA_SAPLING + - BIRCH_SAPLING + - DARK_OAK_SAPLING + - JUNGLE_SAPLING + - SPRUCE_SAPLING + - AZALEA + score: 0.0 + OAK_SIGN: + additionalBlocks: + - ACACIA_SIGN + - ACACIA_WALL_SIGN + - BIRCH_SIGN + - BIRCH_WALL_SIGN + - CRIMSON_SIGN + - CRIMSON_WALL_SIGN + - DARK_OAK_SIGN + - DARK_OAK_WALL_SIGN + - JUNGLE_SIGN + - JUNGLE_WALL_SIGN + - OAK_WALL_SIGN + - SPRUCE_SIGN + - SPRUCE_WALL_SIGN + - WARPED_SIGN + - WARPED_WALL_SIGN + score: 0.0 + OAK_SLAB: + additionalBlocks: + - ACACIA_SLAB + - BIRCH_SLAB + - CRIMSON_SLAB + - DARK_OAK_SLAB + - JUNGLE_SLAB + - SPRUCE_SLAB + - WARPED_SLAB + score: 5.0 + diminishingReturns: 15000 + OAK_STAIRS: + score: 15.0 + OAK_TRAPDOOR: + score: 30.0 + OAK_WOOD: + additionalBlocks: + - STRIPPED_OAK_WOOD + diminishingReturns: 10000 + OBSERVER: + score: 15.0 + OBSIDIAN: score: 200.0 diminishingReturns: 50 + ORANGE_TULIP: + diminishingReturns: 100 + OXEYE_DAISY: + diminishingReturns: 100 PACKED_ICE: score: 15.0 - PISTON_BASE: + PEONY: + diminishingReturns: 100 + PINK_TULIP: + diminishingReturns: 100 + PISTON: score: 40.0 - PISTON_EXTENSION: + PISTON_HEAD: + additionalBlocks: + - MOVING_PISTON score: 0.0 - PISTON_MOVING_PIECE: + PODZOL: + score: 25.0 + diminishingReturns: 10000 + POLISHED_ANDESITE: + score: 15.0 + diminishingReturns: 10000 + POLISHED_ANDESITE_SLAB: + score: 7.5 + diminishingReturns: 10000 + POLISHED_ANDESITE_STAIRS: + score: 17.5 + diminishingReturns: 5000 + POLISHED_BASALT: + score: 12.0 + diminishingReturns: 20000 + POLISHED_BLACKSTONE: + score: 12.0 + diminishingReturns: 20000 + POLISHED_BLACKSTONE_BRICK_SLAB: + score: 6.0 + diminishingReturns: 10000 + POLISHED_BLACKSTONE_BRICK_STAIRS: + score: 18.0 + diminishingReturns: 5000 + POLISHED_BLACKSTONE_BRICK_WALL: + score: 12.0 + diminishingReturns: 5000 + POLISHED_BLACKSTONE_BRICKS: + score: 12.0 + diminishingReturns: 20000 + POLISHED_BLACKSTONE_BUTTON: score: 0.0 - PISTON_STICKY_BASE: - score: 60.0 - PORTAL: + POLISHED_BLACKSTONE_PRESSURE_PLATE: score: 0.0 - POTATO: + POLISHED_BLACKSTONE_SLAB: + score: 6.0 + diminishingReturns: 10000 + POLISHED_BLACKSTONE_STAIRS: + score: 18.0 + diminishingReturns: 5000 + POLISHED_BLACKSTONE_WALL: + score: 12.0 + diminishingReturns: 5000 + POLISHED_DEEPSLATE: + score: 17.5 + diminishingReturns: 10000 + POLISHED_DEEPSLATE_SLAB: + score: 8.5 + diminishingReturns: 10000 + POLISHED_DEEPSLATE_STAIRS: + score: 20.0 + diminishingReturns: 5000 + POLISHED_DEEPSLATE_WALL: + score: 17.5 + diminishingReturns: 5000 + POLISHED_DIORITE: + score: 15.0 + diminishingReturns: 10000 + POLISHED_DIORITE_SLAB: + score: 7.5 + diminishingReturns: 10000 + POLISHED_DIORITE_STAIRS: + score: 17.5 + diminishingReturns: 5000 + POLISHED_GRANITE: + score: 15.0 + diminishingReturns: 10000 + POLISHED_GRANITE_SLAB: + score: 7.5 + diminishingReturns: 10000 + POLISHED_GRANITE_STAIRS: + score: 17.5 + diminishingReturns: 5000 + POPPY: + diminishingReturns: 100 + POTATOES: score: 0.0 POWERED_RAIL: score: 20.0 PRISMARINE: score: 50.0 - PRISMARINE/1: + diminishingReturns: 10000 + PRISMARINE_BRICK_SLAB: + score: 25.0 + diminishingReturns: 10000 + PRISMARINE_BRICK_STAIRS: + score: 75.0 + diminishingReturns: 5000 + PRISMARINE_BRICKS: score: 50.0 - PRISMARINE/2: + diminishingReturns: 10000 + PRISMARINE_SLAB: + score: 25.0 + diminishingReturns: 10000 + PRISMARINE_STAIRS: + score: 75.0 + diminishingReturns: 5000 + PRISMARINE_WALL: score: 50.0 + diminishingReturns: 5000 + PUMPKIN: + score: 0.0 PUMPKIN_STEM: + additionalBlocks: + - ATTACHED_PUMPKIN_STEM score: 0.0 - QUARTZ_BLOCK/0-2: + PURPUR_BLOCK: + score: 15.0 + diminishingReturns: 10000 + PURPUR_PILLAR: + score: 15.0 + diminishingReturns: 5000 + PURPUR_SLAB: + score: 7.5 + diminishingReturns: 10000 + PURPUR_STAIRS: + score: 22.5 + diminishingReturns: 5000 + QUARTZ_BLOCK: + additionalBlocks: + - CHISELED_QUARTZ_BLOCK + - QUARTZ_BRICKS + - QUARTZ_PILLAR score: 100.0 diminishingReturns: 5000 - QUARTZ_ORE: + QUARTZ_SLAB: score: 50.0 + diminishingReturns: 10000 QUARTZ_STAIRS: score: 150.0 diminishingReturns: 5000 - REDSTONE_BLOCK: - score: 150.0 - REDSTONE_LAMP_OFF: - score: 20.0 - REDSTONE_LAMP_ON: - score: 20.0 - REDSTONE_WIRE: - score: 0.0 + RAW_COPPER_BLOCK: + score: 250.0 + RAW_GOLD_BLOCK: + score: 750.0 + RAW_IRON_BLOCK: + score: 250.0 RED_MUSHROOM: diminishingReturns: 100 - RED_ROSE: + RED_MUSHROOM_BLOCK: diminishingReturns: 100 - RED_SANDSTONE/0-2: - score: 25.0 - RED_SANDSTONE_STAIRS: + RED_NETHER_BRICK_SLAB: + score: 10.0 + diminishingReturns: 10000 + RED_NETHER_BRICK_STAIRS: + score: 30.0 + diminishingReturns: 5000 + RED_NETHER_BRICK_WALL: score: 20.0 - SAND/0: + diminishingReturns: 5000 + RED_NETHER_BRICKS: score: 20.0 diminishingReturns: 10000 - SAND/1: + RED_SAND: score: 22.5 diminishingReturns: 10000 - SANDSTONE/0-2: + RED_SANDSTONE: + score: 25.0 + RED_SANDSTONE_SLAB: + score: 12.5 + diminishingReturns: 10000 + RED_SANDSTONE_STAIRS: + score: 20.0 + diminishingReturns: 5000 + RED_SANDSTONE_WALL: + score: 25.0 + diminishingReturns: 5000 + RED_TULIP: + diminishingReturns: 100 + REDSTONE_BLOCK: + score: 150.0 + REDSTONE_LAMP: + score: 20.0 + REDSTONE_ORE: + additionalBlocks: + - DEEPSLATE_REDSTONE_ORE + score: 25.0 + REDSTONE_WIRE: + score: 0.0 + REPEATER: + score: 15.0 + RESPAWN_ANCHOR: + score: 300.0 + limit: 1 + ROSE_BUSH: + diminishingReturns: 100 + SAND: + score: 20.0 + diminishingReturns: 10000 + SANDSTONE: score: 80.0 + SANDSTONE_SLAB: + score: 40.0 + diminishingReturns: 10000 SANDSTONE_STAIRS: score: 120.0 diminishingReturns: 5000 - SAPLING: - score: 0.0 + SANDSTONE_WALL: + score: 80.0 + diminishingReturns: 5000 SEA_LANTERN: score: 50.0 - SIGN_POST: - score: 0.0 - SKULL/0-4: + SEA_PICKLE: + limit: 100 + SHULKER_BOX: + additionalBlocks: + - BLACK_SHULKER_BOX + - BLUE_SHULKER_BOX + - BROWN_SHULKER_BOX + - CYAN_SHULKER_BOX + - GRAY_SHULKER_BOX + - GREEN_SHULKER_BOX + - LIGHT_BLUE_SHULKER_BOX + - LIGHT_GRAY_SHULKER_BOX + - LIME_SHULKER_BOX + - MAGENTA_SHULKER_BOX + - ORANGE_SHULKER_BOX + - PINK_SHULKER_BOX + - PURPLE_SHULKER_BOX + - RED_SHULKER_BOX + - WHITE_SHULKER_BOX + - YELLOW_SHULKER_BOX + score: 500.0 + SHROOMLIGHT: + score: 20.0 + SKELETON_SKULL: + additionalBlocks: + - CREEPER_HEAD + - CREEPER_WALL_HEAD + - DRAGON_HEAD + - DRAGON_WALL_HEAD + - PLAYER_HEAD + - PLAYER_WALL_HEAD + - SKELETON_WALL_SKULL + - ZOMBIE_HEAD + - ZOMBIE_WALL_HEAD score: 1000.0 + diminishingReturns: 10 SLIME_BLOCK: - score: 250.0 + score: 50.0 diminishingReturns: 100 - SMOOTH_BRICK: - score: 12.0 - diminishingReturns: 20000 - SMOOTH_STAIRS: + SMITHING_TABLE: score: 20.0 + limit: 250 + SMOKER: + score: 15.0 + limit: 250 + SMOOTH_BASALT: + score: 12.5 + limit: 10000 + SMOOTH_QUARTZ: + score: 100.0 + diminishingReturns: 5000 + SMOOTH_QUARTZ_SLAB: + score: 50.0 + diminishingReturns: 10000 + SMOOTH_QUARTZ_STAIRS: + score: 150.0 + diminishingReturns: 5000 + SMOOTH_RED_SANDSTONE: + score: 25.0 + SMOOTH_RED_SANDSTONE_SLAB: + score: 12.5 + diminishingReturns: 10000 + SMOOTH_RED_SANDSTONE_STAIRS: + score: 37.5 diminishingReturns: 5000 + SMOOTH_SANDSTONE: + score: 80.0 + SMOOTH_SANDSTONE_SLAB: + score: 40.0 + diminishingReturns: 10000 + SMOOTH_SANDSTONE_STAIRS: + score: 120.0 + diminishingReturns: 5000 + SMOOTH_STONE: + score: 12.0 + SMOOTH_STONE_SLAB: + score: 6.0 + diminishingReturns: 10000 SNOW: score: 0.0 SNOW_BLOCK: score: 50.0 - SOIL: - score: 40.0 + SOUL_CAMPFIRE: + score: 15.0 + SOUL_FIRE: + score: 0.0 + SOUL_LANTERN: + score: 25.0 SOUL_SAND: limit: 10000 + SOUL_SOIL: + limit: 10000 SPONGE: score: 50.0 limit: 20 - SPONGE/1: - score: 50.0 + SPORE_BLOSSOM: + diminishingReturns: 100 SPRUCE_DOOR: score: 20.0 SPRUCE_FENCE: @@ -388,103 +1062,346 @@ blocks: SPRUCE_FENCE_GATE: score: 40.0 diminishingReturns: 100 - SPRUCE_WOOD_STAIRS: + SPRUCE_STAIRS: score: 15.0 - STAINED_CLAY: - score: 50.0 - STAINED_GLASS: - score: 25.0 - STANDING_BANNER: - score: 20.0 - diminishingReturns: 20 - STATIONARY_LAVA: - score: 0.0 - STATIONARY_WATER: - score: 0.0 - STEP/0: - score: 5.0 + SPRUCE_TRAPDOOR: + score: 30.0 + SPRUCE_WOOD: + additionalBlocks: + - STRIPPED_SPRUCE_WOOD diminishingReturns: 10000 - STEP/1: - score: 40.0 + STICKY_PISTON: + score: 60.0 + STONE: + additionalBlocks: + - INFESTED_STONE diminishingReturns: 10000 - STEP/2: - score: 5.0 + STONE_BRICK_SLAB: + score: 6.0 diminishingReturns: 10000 - STEP/3: + STONE_BRICK_STAIRS: + score: 18.0 + diminishingReturns: 5000 + STONE_BRICK_WALL: + score: 12.0 + diminishingReturns: 5000 + STONE_BRICKS: + additionalBlocks: + - INFESTED_STONE_BRICKS + score: 12.0 + diminishingReturns: 20000 + STONE_PRESSURE_PLATE: + score: 0.0 + STONE_SLAB: score: 5.0 diminishingReturns: 10000 - STEP/4: - score: 25.0 - diminishingReturns: 10000 - STEP/5: - score: 7.0 - diminishingReturns: 10000 - STEP/6: - score: 20.0 - diminishingReturns: 10000 - STEP/7: - score: 50.0 - diminishingReturns: 10000 - STONE/0: - diminishingReturns: 10000 - STONE/1: - score: 12.5 - diminishingReturns: 10000 - STONE/2: - score: 15.0 - diminishingReturns: 10000 - STONE/3: - score: 12.5 - diminishingReturns: 10000 - STONE/4: - score: 15.0 - diminishingReturns: 10000 - STONE/5: - score: 12.5 - diminishingReturns: 10000 - STONE/6: + STONECUTTER: score: 15.0 - diminishingReturns: 10000 - STONE_PLATE: - score: 0.0 - STONE_SLAB2: - score: 12.5 - diminishingReturns: 20000 - SUGAR_CANE_BLOCK: + limit: 250 + SUGAR_CANE: score: 0.0 + SUNFLOWER: + diminishingReturns: 100 + SWEET_BERRY_BUSH: + diminishingReturns: 100 + TERRACOTTA: + additionalBlocks: + - BLACK_TERRACOTTA + - BLUE_TERRACOTTA + - BROWN_TERRACOTTA + - CYAN_TERRACOTTA + - GRAY_TERRACOTTA + - GREEN_TERRACOTTA + - LIGHT_BLUE_TERRACOTTA + - LIGHT_GRAY_TERRACOTTA + - LIME_TERRACOTTA + - MAGENTA_TERRACOTTA + - ORANGE_TERRACOTTA + - PINK_TERRACOTTA + - PURPLE_TERRACOTTA + - RED_TERRACOTTA + - WHITE_TERRACOTTA + - YELLOW_TERRACOTTA + score: 20.0 + TINTED_GLASS: + score: 75.0 TNT: score: 80.0 TRAPPED_CHEST: score: 20.0 - TRAP_DOOR: - score: 30.0 + TRIPWIRE: + score: 0.0 + TRIPWIRE_HOOK: + score: 15.0 + limit: 250 + TUFF: + score: 15.0 + diminishingReturns: 10000 + TWISTING_VINES: + additionalBlocks: + - TWISTING_VINES_PLANT + limit: 25 VINE: + additionalBlocks: + - CAVE_VINES + - CAVE_VINES_PLANT limit: 25 - WALL_BANNER: + WARPED_DOOR: score: 20.0 - diminishingReturns: 20 - WALL_SIGN: - score: 0.0 - WATER: + WARPED_FENCE: + score: 17.5 + diminishingReturns: 5000 + WARPED_FENCE_GATE: + score: 40.0 + diminishingReturns: 100 + WARPED_FUNGUS: score: 0.0 - WEB: - score: 20.0 - WOOD: + WARPED_HYPHAE: + additionalBlocks: + - STRIPPED_WARPED_HYPHAE diminishingReturns: 10000 - WOODEN_DOOR: - score: 20.0 - WOOD_DOUBLE_STEP/0-5: + WARPED_NYLIUM: + score: 15.0 diminishingReturns: 10000 - WOOD_PLATE: + WARPED_ROOTS: score: 0.0 - WOOD_STAIRS: + WARPED_STAIRS: score: 15.0 - WOOD_STEP/0-5: - score: 5.0 - diminishingReturns: 10000 - WOOL: + WARPED_STEM: + additionalBlocks: + - STRIPPED_WARPED_STEM + diminishingReturns: 100 + WARPED_TRAPDOOR: + score: 30.0 + WATER: + additionalBlocks: + - BUBBLE_COLUMN + score: 0.0 + WAXED_COPPER_BLOCK: + additionalBlocks: + - WAXED_EXPOSED_COPPER + - WAXED_WEATHERED_COPPER + - WAXED_OXIDIZED_COPPER + score: 300.0 + WAXED_CUT_COPPER: + additionalBlocks: + - WAXED_EXPOSED_CUT_COPPER + - WAXED_WEATHERED_CUT_COPPER + - WAXED_OXIDIZED_CUT_COPPER + score: 300.0 + WAXED_CUT_COPPER_STAIRS: + additionalBlocks: + - WAXED_EXPOSED_CUT_COPPER_STAIRS + - WAXED_WEATHERED_CUT_COPPER_STAIRS + - WAXED_OXIDIZED_CUT_COPPER_STAIRS + score: 175.0 + WAXED_CUT_COPPER_SLAB: + additionalBlocks: + - WAXED_EXPOSED_CUT_COPPER_SLAB + - WAXED_WEATHERED_CUT_COPPER_SLAB + - WAXED_OXIDIZED_CUT_COPPER_SLAB + score: 150.0 + WEEPING_VINES: + additionalBlocks: + - WEEPING_VINES_PLANT + limit: 25 + WET_SPONGE: + score: 50.0 + limit: 20 + WHEAT: + score: 0.0 + WHITE_BANNER: + additionalBlocks: + - BLACK_BANNER + - BLUE_BANNER + - BROWN_BANNER + - CYAN_BANNER + - GRAY_BANNER + - GREEN_BANNER + - LIGHT_BLUE_BANNER + - LIGHT_GRAY_BANNER + - LIME_BANNER + - MAGENTA_BANNER + - ORANGE_BANNER + - PINK_BANNER + - PURPLE_BANNER + - RED_BANNER + - YELLOW_BANNER + score: 20.0 + diminishingReturns: 20 + WHITE_BED: + additionalBlocks: + - BLACK_BED + - BLUE_BED + - BROWN_BED + - CYAN_BED + - GRAY_BED + - GREEN_BED + - LIGHT_BLUE_BED + - LIGHT_GRAY_BED + - LIME_BED + - MAGENTA_BED + - ORANGE_BED + - PINK_BED + - PURPLE_BED + - RED_BED + - YELLOW_BED + score: 1000.0 + limit: 1 + WHITE_CARPET: + additionalBlocks: + - BLACK_CARPET + - BLUE_CARPET + - BROWN_CARPET + - CYAN_CARPET + - GRAY_CARPET + - GREEN_CARPET + - LIGHT_BLUE_CARPET + - LIGHT_GRAY_CARPET + - LIME_CARPET + - MAGENTA_CARPET + - ORANGE_CARPET + - PINK_CARPET + - PURPLE_CARPET + - RED_CARPET + - YELLOW_CARPET + score: 20.0 + diminishingReturns: 5000 + WHITE_CONCRETE: + additionalBlocks: + - BLACK_CONCRETE + - BLUE_CONCRETE + - BROWN_CONCRETE + - CYAN_CONCRETE + - GRAY_CONCRETE + - GREEN_CONCRETE + - LIGHT_BLUE_CONCRETE + - LIGHT_GRAY_CONCRETE + - LIME_CONCRETE + - MAGENTA_CONCRETE + - ORANGE_CONCRETE + - PINK_CONCRETE + - PURPLE_CONCRETE + - RED_CONCRETE + - YELLOW_CONCRETE + score: 50.0 + WHITE_CONCRETE_POWDER: + additionalBlocks: + - BLACK_CONCRETE_POWDER + - BLUE_CONCRETE_POWDER + - BROWN_CONCRETE_POWDER + - CYAN_CONCRETE_POWDER + - GRAY_CONCRETE_POWDER + - GREEN_CONCRETE_POWDER + - LIGHT_BLUE_CONCRETE_POWDER + - LIGHT_GRAY_CONCRETE_POWDER + - LIME_CONCRETE_POWDER + - MAGENTA_CONCRETE_POWDER + - ORANGE_CONCRETE_POWDER + - PINK_CONCRETE_POWDER + - PURPLE_CONCRETE_POWDER + - RED_CONCRETE_POWDER + - YELLOW_CONCRETE_POWDER + score: 40.0 + WHITE_GLAZED_TERRACOTTA: + additionalBlocks: + - BLACK_GLAZED_TERRACOTTA + - BLUE_GLAZED_TERRACOTTA + - BROWN_GLAZED_TERRACOTTA + - CYAN_GLAZED_TERRACOTTA + - GRAY_GLAZED_TERRACOTTA + - GREEN_GLAZED_TERRACOTTA + - LIGHT_BLUE_GLAZED_TERRACOTTA + - LIGHT_GRAY_GLAZED_TERRACOTTA + - LIME_GLAZED_TERRACOTTA + - MAGENTA_GLAZED_TERRACOTTA + - ORANGE_GLAZED_TERRACOTTA + - PINK_GLAZED_TERRACOTTA + - PURPLE_GLAZED_TERRACOTTA + - RED_GLAZED_TERRACOTTA + - YELLOW_GLAZED_TERRACOTTA + score: 20.0 + WHITE_STAINED_GLASS: + additionalBlocks: + - BLACK_STAINED_GLASS + - BLUE_STAINED_GLASS + - BROWN_STAINED_GLASS + - CYAN_STAINED_GLASS + - GRAY_STAINED_GLASS + - GREEN_STAINED_GLASS + - LIGHT_BLUE_STAINED_GLASS + - LIGHT_GRAY_STAINED_GLASS + - LIME_STAINED_GLASS + - MAGENTA_STAINED_GLASS + - ORANGE_STAINED_GLASS + - PINK_STAINED_GLASS + - PURPLE_STAINED_GLASS + - RED_STAINED_GLASS + - YELLOW_STAINED_GLASS + score: 50.0 + WHITE_STAINED_GLASS_PANE: + additionalBlocks: + - BLACK_STAINED_GLASS_PANE + - BLUE_STAINED_GLASS_PANE + - BROWN_STAINED_GLASS_PANE + - CYAN_STAINED_GLASS_PANE + - GRAY_STAINED_GLASS_PANE + - GREEN_STAINED_GLASS_PANE + - LIGHT_BLUE_STAINED_GLASS_PANE + - LIGHT_GRAY_STAINED_GLASS_PANE + - LIME_STAINED_GLASS_PANE + - MAGENTA_STAINED_GLASS_PANE + - ORANGE_STAINED_GLASS_PANE + - PINK_STAINED_GLASS_PANE + - PURPLE_STAINED_GLASS_PANE + - RED_STAINED_GLASS_PANE + - YELLOW_STAINED_GLASS_PANE + score: 20.0 + WHITE_TULIP: + diminishingReturns: 100 + WHITE_WALL_BANNER: + additionalBlocks: + - BLACK_WALL_BANNER + - BLUE_WALL_BANNER + - BROWN_WALL_BANNER + - CYAN_WALL_BANNER + - GRAY_WALL_BANNER + - GREEN_WALL_BANNER + - LIGHT_BLUE_WALL_BANNER + - LIGHT_GRAY_WALL_BANNER + - LIME_WALL_BANNER + - MAGENTA_WALL_BANNER + - ORANGE_WALL_BANNER + - PINK_WALL_BANNER + - PURPLE_WALL_BANNER + - RED_WALL_BANNER + - YELLOW_WALL_BANNER + score: 20.0 + diminishingReturns: 20 + WHITE_WOOL: + additionalBlocks: + - BLACK_WOOL + - BLUE_WOOL + - BROWN_WOOL + - CYAN_WOOL + - GRAY_WOOL + - GREEN_WOOL + - LIGHT_BLUE_WOOL + - LIGHT_GRAY_WOOL + - LIME_WOOL + - MAGENTA_WOOL + - ORANGE_WOOL + - PINK_WOOL + - PURPLE_WOOL + - RED_WOOL + - YELLOW_WOOL score: 50.0 diminishingReturns: 64 - YELLOW_FLOWER: + WITHER_ROSE: diminishingReturns: 100 -version: 100 + WITHER_SKELETON_SKULL: + additionalBlocks: + - WITHER_SKELETON_WALL_SKULL + score: 500.0 + limit: 5 +version: 101 diff --git a/uSkyBlock-Core/src/main/resources/plugin.yml b/uSkyBlock-Core/src/main/resources/plugin.yml index eccbd1409..58712da9c 100644 --- a/uSkyBlock-Core/src/main/resources/plugin.yml +++ b/uSkyBlock-Core/src/main/resources/plugin.yml @@ -1,23 +1,33 @@ name: uSkyBlock main: us.talabrek.ultimateskyblock.uSkyBlock -version: ${project.version} -description: Ultimate SkyBlock v${project.version}-${buildNumber} +version: cong +description: Ultimate SkyBlock author: R4zorax (and more) load: STARTUP softdepend: # mandatory dependencies - - Vault - WorldEdit - WorldGuard - - Multiverse-Core # optional dependencies - ActionBarAPI + - Multiverse-Core + - Multiverse-Inventories + - Vault # WE/AWE - FastAsyncWorldEdit - - AsyncWorldEdit # placeholders - MVdWPlaceholderAPI -api-version: 1.15 +api-version: 1.21.4 +libraries: + - com.google.code.gson:gson:2.10.1 + - com.google.inject:guice:7.0.0 + - com.google.guava:guava:33.1.0-jre + - net.kyori:adventure-api:4.16.0 + - net.kyori:adventure-platform-bukkit:4.3.2 + - org.apache.commons:commons-lang3:3.14.0 + - org.apache.commons:commons-text:1.12.0 + - org.apache.httpcomponents:httpclient:4.5.14 + - org.apache.maven:maven-artifact:3.8.6 commands: challenges: @@ -173,15 +183,15 @@ permissions: children: usb.biome.deep_ocean: true usb.biome.desert: true - usb.biome.extreme_hills: true + usb.biome.windswept_hills: true usb.biome.forest: true - usb.biome.hell: true + usb.biome.nether_wastes: true usb.biome.jungle: true - usb.biome.mushroom: true + usb.biome.mushroom_fields: true usb.biome.ocean: false # default: true usb.biome.plains: true - usb.biome.sky: true - usb.biome.swampland: true + usb.biome.the_end: true + usb.biome.swamp: true usb.biome.taiga: true usb.exempt.*: @@ -389,25 +399,25 @@ permissions: default: false description: 'Let the player change their islands biome to DESERT' - usb.biome.extreme_hills: + usb.biome.windswept_hills: default: false - description: 'Let the player change their islands biome to EXTREME_HILLS' + description: 'Let the player change their islands biome to WINDSWEPT_HILLS' usb.biome.forest: default: false description: 'Let the player change their islands biome to FOREST' - usb.biome.hell: + usb.biome.nether_wastes: default: false - description: 'Let the player change their islands biome to HELL' + description: 'Let the player change their islands biome to NETHER_WASTES' usb.biome.jungle: default: false description: 'Let the player change their islands biome to JUNGLE' - usb.biome.mushroom: + usb.biome.mushroom_fields: default: false - description: 'Let the player change their islands biome to MUSHROOM' + description: 'Let the player change their islands biome to MUSHROOM_FIELDS' usb.biome.ocean: default: true @@ -417,13 +427,13 @@ permissions: default: false description: 'Let the player change their islands biome to PLAINS' - usb.biome.sky: + usb.biome.the_end: default: false - description: 'Let the player change their islands biome to SKY' + description: 'Let the player change their islands biome to THE_END' - usb.biome.swampland: + usb.biome.swamp: default: false - description: 'Let the player change their islands biome to SWAMPLAND' + description: 'Let the player change their islands biome to SWAMP' usb.biome.taiga: default: false diff --git a/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/TopTenComparatorTest.java b/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/TopTenComparatorTest.java index 99f396cf2..d912ee1ea 100644 --- a/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/TopTenComparatorTest.java +++ b/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/TopTenComparatorTest.java @@ -9,12 +9,12 @@ import java.util.TreeMap; import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertThat; +import static org.hamcrest.MatcherAssert.assertThat; public class TopTenComparatorTest { @Test - public void testCompare() throws Exception { + public void testCompare() { Map testMap = new HashMap<>(); testMap.put("test A", 10.2); testMap.put("test B", 10.3); @@ -29,4 +29,4 @@ public void testCompare() throws Exception { assertThat(sorted.toString(), is(expected)); assertThat(new LinkedHashMap<>(sorted).toString(), is(expected)); } -} \ No newline at end of file +} diff --git a/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/challenge/ChallengeFactoryTest.java b/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/challenge/ChallengeFactoryTest.java index ad5d23b94..d59d20f58 100644 --- a/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/challenge/ChallengeFactoryTest.java +++ b/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/challenge/ChallengeFactoryTest.java @@ -8,15 +8,16 @@ import org.bukkit.entity.EntityType; import org.bukkit.inventory.ItemStack; import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; import java.io.InputStream; import java.io.InputStreamReader; -import java.util.List; +import java.util.Map; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; -import static org.junit.Assert.assertThat; +import static org.hamcrest.MatcherAssert.assertThat; public class ChallengeFactoryTest { @@ -26,6 +27,7 @@ public void beforeEach() throws NoSuchFieldException, IllegalAccessException { } @Test + @Ignore public void createChallenge_IronGolem() { InputStream resourceAsStream = getClass().getClassLoader().getResourceAsStream("challengefactory/requiredEntities.yml"); YamlConfiguration config = YamlConfiguration.loadConfiguration(new InputStreamReader(resourceAsStream)); @@ -41,6 +43,7 @@ public void createChallenge_IronGolem() { } @Test + @Ignore public void createChallenge_ManyItems() { InputStream resourceAsStream = getClass().getClassLoader().getResourceAsStream("challengefactory/manyRequiredItems.yml"); YamlConfiguration config = YamlConfiguration.loadConfiguration(new InputStreamReader(resourceAsStream)); @@ -50,9 +53,8 @@ public void createChallenge_ManyItems() { Challenge challenge = ChallengeFactory.createChallenge(rank, rankSection.getConfigurationSection("challenges.villageguard"), defaults); assertThat(challenge, notNullValue()); - List requiredItems = challenge.getRequiredItems(0); + Map requiredItems = challenge.getRequiredItems(0); assertThat(requiredItems.size(), is(1)); - assertThat(ItemStackUtil.asString(requiredItems.get(0)), is(ItemStackUtil.asString(new ItemStack(Material.COBBLESTONE, 257)))); + assertThat(ItemStackUtil.asString(requiredItems.keySet().stream().findFirst().get()), is(ItemStackUtil.asString(new ItemStack(Material.COBBLESTONE, 257)))); } - -} \ No newline at end of file +} diff --git a/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/challenge/ChallengeFormatTest.java b/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/challenge/ChallengeFormatTest.java index 03cc1f8aa..6657aae30 100644 --- a/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/challenge/ChallengeFormatTest.java +++ b/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/challenge/ChallengeFormatTest.java @@ -1,17 +1,19 @@ package us.talabrek.ultimateskyblock.challenge; +import dk.lockfuglsang.minecraft.po.I18nUtil; import org.junit.BeforeClass; import org.junit.Test; import org.mockito.Mockito; import org.mockito.hamcrest.MockitoHamcrest; import us.talabrek.ultimateskyblock.player.PlayerInfo; +import java.io.File; import java.util.Arrays; +import java.util.Locale; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.nullValue; -import static org.junit.Assert.assertThat; -import static org.mockito.Matchers.argThat; +import static org.hamcrest.MatcherAssert.assertThat; import static org.mockito.Mockito.when; public class ChallengeFormatTest { @@ -21,6 +23,7 @@ public class ChallengeFormatTest { @BeforeClass public static void beforeAll() { + I18nUtil.initialize(new File("."), Locale.ENGLISH); playerInfo = Mockito.mock(PlayerInfo.class); challengeLogic = Mockito.mock(ChallengeLogic.class); @@ -80,4 +83,4 @@ public void getMissingRequirement_AllFullyCompleted() { String missingRequirement = ChallengeFormat.getMissingRequirement(playerInfo, Arrays.asList("cobblestonegenerator", "pumpkinfarmer:2"), challengeLogic); assertThat(missingRequirement, is(nullValue())); } -} \ No newline at end of file +} diff --git a/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/challenge/ChallengeLogicTest.java b/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/challenge/ChallengeLogicTest.java deleted file mode 100644 index 24182af90..000000000 --- a/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/challenge/ChallengeLogicTest.java +++ /dev/null @@ -1,90 +0,0 @@ -package us.talabrek.ultimateskyblock.challenge; - -import dk.lockfuglsang.minecraft.file.FileUtil; -import dk.lockfuglsang.minecraft.nbt.NBTItemStackTagger; -import dk.lockfuglsang.minecraft.nbt.NBTUtil; -import dk.lockfuglsang.minecraft.util.FormatUtil; -import dk.lockfuglsang.minecraft.yml.YmlConfiguration; -import org.bukkit.Bukkit; -import org.bukkit.Server; -import org.bukkit.inventory.ItemFactory; -import org.bukkit.inventory.ItemStack; -import org.bukkit.plugin.PluginManager; -import org.junit.Ignore; -import org.junit.Test; -import us.talabrek.ultimateskyblock.uSkyBlock; - -import java.io.File; -import java.lang.reflect.Field; -import java.util.Arrays; -import java.util.List; - -import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertThat; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -public class ChallengeLogicTest { - - @Test - public void testWordWrap() { - String test = "a little version that doesn't mean anything, but should be broken on multiple lines"; - List expected = Arrays.asList( - "a little", - "version that doesn't", - "mean anything,", - "but should be broken", - "on multiple lines"); - - assertThat(FormatUtil.wordWrap(test, 8, 15), is(expected)); - } - - @Test - @Ignore("Bukkit 1.13 uses server.Unsafe(), which is NULL in test-runner") - public void testDefaultChallengesYml() throws Exception { - setupServerMock(); - YmlConfiguration config = new YmlConfiguration(); - FileUtil.readConfig(config, new File("src/main/resources/challenges.yml")); - uSkyBlock plugin = mock(uSkyBlock.class); - when(plugin.getConfig()).thenReturn(config); - when(plugin.getDataFolder()).thenReturn(new File("target/test-classes")); - ChallengeLogic sut = new ChallengeLogic(config, plugin); - // Adjust these values accordingly - assertThat(sut.getRanks().size(), is(6)); - assertThat(sut.getAllChallengeNames().size(), is(54)); - } - - private static Server setupServerMock() throws NoSuchFieldException, IllegalAccessException { - NBTUtil.setNBTItemStackTagger(new NBTItemStackTagger() { - @Override - public String getNBTTag(ItemStack itemStack) { - return null; - } - - @Override - public ItemStack setNBTTag(ItemStack itemStack, String tag) { - return null; - } - - @Override - public ItemStack addNBTTag(ItemStack itemStack, String tag) { - return null; - } - }); - Field server = Bukkit.class.getDeclaredField("server"); - server.setAccessible(true); - Server serverMock = createServerMock(); - server.set(null, serverMock); - server.setAccessible(false); - return serverMock; - } - - private static Server createServerMock() { - Server serverMock = mock(Server.class); - ItemFactory itemFactoryMock = mock(ItemFactory.class); - when(serverMock.getItemFactory()).thenReturn(itemFactoryMock); - PluginManager pluginManagerMock = mock(PluginManager.class); - when(serverMock.getPluginManager()).thenReturn(pluginManagerMock); - return serverMock; - } -} \ No newline at end of file diff --git a/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/challenge/EntityMatchTest.java b/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/challenge/EntityMatchTest.java new file mode 100644 index 000000000..105f63ed6 --- /dev/null +++ b/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/challenge/EntityMatchTest.java @@ -0,0 +1,58 @@ +package us.talabrek.ultimateskyblock.challenge; + +import org.bukkit.DyeColor; +import org.bukkit.entity.Cow; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Sheep; +import org.bukkit.material.Colorable; +import org.junit.Test; + +import java.util.Map; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; + +public class EntityMatchTest { + @Test + public void testMatchColoredSheep() { + EntityMatch matcher = new EntityMatch(EntityType.SHEEP, Map.of("Color", "RED"), 1); + + Sheep fakeSheep = mock(Sheep.class, withSettings().extraInterfaces(Colorable.class)); + when(fakeSheep.getColor()).thenReturn(DyeColor.RED); + when(fakeSheep.getType()).thenReturn(EntityType.SHEEP); + + assertEquals("Red Sheep", matcher.getDisplayName()); + assertEquals(1, matcher.getCount()); + assertTrue(matcher.matches(fakeSheep)); + + when(fakeSheep.getColor()).thenReturn(DyeColor.WHITE); + assertFalse(matcher.matches(fakeSheep)); + } + + @Test + public void testMatchLegacyColoredSheep() { + EntityMatch matcher = new EntityMatch(EntityType.SHEEP, Map.of("color", 4.0), 9); + + Sheep fakeSheep = mock(Sheep.class, withSettings().extraInterfaces(Colorable.class)); + when(fakeSheep.getColor()).thenReturn(DyeColor.YELLOW); + when(fakeSheep.getType()).thenReturn(EntityType.SHEEP); + + assertEquals("Yellow Sheep", matcher.getDisplayName()); + assertEquals(9, matcher.getCount()); + assertTrue(matcher.matches(fakeSheep)); + + when(fakeSheep.getColor()).thenReturn(DyeColor.BLACK); + assertFalse(matcher.matches(fakeSheep)); + } + + @Test + public void testMatchColoredCow() { + EntityMatch matcher = new EntityMatch(EntityType.COW, Map.of("Color", "RED"), 1); + + Cow fakeCow = mock(Cow.class); + when(fakeCow.getType()).thenReturn(EntityType.COW); + + assertEquals("Red Cow", matcher.getDisplayName()); + assertTrue(matcher.matches(fakeCow)); + } +} diff --git a/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/chat/ChatEventsTest.java b/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/chat/ChatEventsTest.java index 4ea6c1aec..2e726c2fa 100644 --- a/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/chat/ChatEventsTest.java +++ b/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/chat/ChatEventsTest.java @@ -1,5 +1,6 @@ package us.talabrek.ultimateskyblock.chat; +import dk.lockfuglsang.minecraft.po.I18nUtil; import org.bukkit.Server; import org.bukkit.entity.Player; import org.bukkit.event.player.AsyncPlayerChatEvent; @@ -11,7 +12,9 @@ import us.talabrek.ultimateskyblock.api.event.IslandChatEvent; import us.talabrek.ultimateskyblock.uSkyBlock; +import java.io.File; import java.util.HashSet; +import java.util.Locale; import java.util.Set; import java.util.UUID; @@ -25,6 +28,7 @@ public class ChatEventsTest { @Before public void setUp() { + I18nUtil.initialize(new File("."), Locale.ENGLISH); uSkyBlock fakePlugin = mock(uSkyBlock.class); Server fakeServer = mock(Server.class); when(fakePlugin.getServer()).thenReturn(fakeServer); @@ -33,7 +37,7 @@ public void setUp() { when(fakeScheduler.runTask(any(Plugin.class), any(Runnable.class))).thenReturn(mock(BukkitTask.class)); when(fakeServer.getScheduler()).thenReturn(fakeScheduler); - fakeLogic = spy(mock(ChatLogic.class)); + fakeLogic = mock(ChatLogic.class); doNothing().when(fakeLogic).sendMessage(any(), any(), any()); listener = new ChatEvents(fakeLogic, fakePlugin); diff --git a/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/command/IslandChatCommandTest.java b/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/command/IslandChatCommandTest.java index 3662278ad..40147733f 100644 --- a/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/command/IslandChatCommandTest.java +++ b/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/command/IslandChatCommandTest.java @@ -3,10 +3,9 @@ import org.junit.Test; import java.util.regex.Matcher; -import java.util.regex.Pattern; import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.*; +import static org.hamcrest.MatcherAssert.assertThat; /** * Created by R4zorax on 04/07/2016. @@ -22,4 +21,4 @@ public void getFormat() throws Exception { assertThat(msg, is("\u00a7b\u00a7cR4zorax: \u00a7aHello world $123")); } -} \ No newline at end of file +} diff --git a/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/event/GriefEventsTest.java b/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/event/GriefEventsTest.java index 5ae692731..42fef2fb7 100644 --- a/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/event/GriefEventsTest.java +++ b/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/event/GriefEventsTest.java @@ -4,16 +4,19 @@ import org.hamcrest.CoreMatchers; import org.junit.Test; +import java.io.File; import java.text.MessageFormat; import java.text.ParseException; +import java.util.Locale; import static org.hamcrest.core.Is.is; -import static org.junit.Assert.*; +import static org.hamcrest.MatcherAssert.assertThat; public class GriefEventsTest { @Test public void testWitherNaming() throws ParseException { + I18nUtil.initialize(new File("."), Locale.ENGLISH); String name = I18nUtil.tr("{0}''s Wither", "R4zorax"); assertThat(name, is("R4zorax's Wither")); @@ -22,4 +25,4 @@ public void testWitherNaming() throws ParseException { assertThat(parse.length, is(1)); assertThat((String)parse[0], is("R4zorax")); } -} \ No newline at end of file +} diff --git a/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/event/InternalEventsTest.java b/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/event/InternalEventsTest.java index bb18bd6db..e5513cd7a 100644 --- a/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/event/InternalEventsTest.java +++ b/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/event/InternalEventsTest.java @@ -32,7 +32,7 @@ public class InternalEventsTest { @Before public void setUp() { - fakePlugin = spy(mock(uSkyBlock.class)); + fakePlugin = mock(uSkyBlock.class); internalEvents = new InternalEvents(fakePlugin); YamlConfiguration config = new YamlConfiguration(); @@ -40,7 +40,7 @@ public void setUp() { config.set("options.party.leave-commands", Arrays.asList("dont", "stop", "me", "now")); doReturn(config).when(fakePlugin).getConfig(); - fakeBlockLimitLogic = spy(mock(BlockLimitLogic.class)); + fakeBlockLimitLogic = mock(BlockLimitLogic.class); doNothing().when(fakeBlockLimitLogic).updateBlockCount(any(), any()); doReturn(fakeBlockLimitLogic).when(fakePlugin).getBlockLimitLogic(); @@ -73,7 +73,7 @@ public void testOnCreate() { @Test public void testOnMemberJoin() { IslandInfo fakeIslandInfo = mock(IslandInfo.class); - PlayerInfo fakePlayerInfo = spy(mock(PlayerInfo.class)); + PlayerInfo fakePlayerInfo = mock(PlayerInfo.class); doReturn(true).when(fakePlayerInfo).execCommands(any()); List commandList = fakePlugin.getConfig().getStringList("options.party.join-commands"); @@ -86,7 +86,7 @@ public void testOnMemberJoin() { @Test public void testOnMemberLeft() { IslandInfo fakeIslandInfo = mock(IslandInfo.class); - PlayerInfo fakePlayerInfo = spy(mock(PlayerInfo.class)); + PlayerInfo fakePlayerInfo = mock(PlayerInfo.class); doReturn(true).when(fakePlayerInfo).execCommands(any()); List commandList = fakePlugin.getConfig().getStringList("options.party.leave-commands"); @@ -103,7 +103,7 @@ public void testOnScoreChanged() { Location islandLocation = new Location(null, -10.00, 25.00, 10.00); uSkyBlockScoreChangedEvent event = new uSkyBlockScoreChangedEvent(fakePlayer, fakePlugin, - fakeIslandScore, islandLocation); + fakeIslandScore, islandLocation); internalEvents.onScoreChanged(event); verify(fakeBlockLimitLogic).updateBlockCount(islandLocation, fakeIslandScore); } @@ -113,12 +113,12 @@ public void testOnInfoEvent() { Player fakePlayer = getFakePlayer(); Location islandLocation = new Location(null, -10.00, 25.00, 10.00); Callback callback = - new Callback() { - @Override - public void run() { - // Do nothing - } - }; + new Callback<>() { + @Override + public void run() { + // Do nothing + } + }; IslandInfoEvent event = new IslandInfoEvent(fakePlayer, islandLocation, callback); internalEvents.onInfoEvent(event); diff --git a/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/event/MenuEventsTest.java b/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/event/MenuEventsTest.java index efa4a4db6..5aefa84d7 100644 --- a/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/event/MenuEventsTest.java +++ b/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/event/MenuEventsTest.java @@ -10,45 +10,32 @@ import org.bukkit.inventory.InventoryView; import org.junit.Before; import org.junit.Test; -import us.talabrek.ultimateskyblock.menu.ConfigMenu; import us.talabrek.ultimateskyblock.menu.SkyBlockMenu; import us.talabrek.ultimateskyblock.player.UltimateHolder; import us.talabrek.ultimateskyblock.player.UltimateHolder.MenuType; -import us.talabrek.ultimateskyblock.uSkyBlock; import java.util.UUID; -import static org.mockito.Mockito.*; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; public class MenuEventsTest { - private ConfigMenu fakeConfigMenu; private SkyBlockMenu fakeMenu; private MenuEvents menuEvents; @Before public void setUp() { - uSkyBlock fakePlugin = mock(uSkyBlock.class); - fakeConfigMenu = spy(mock(ConfigMenu.class)); - fakeMenu = spy(mock(SkyBlockMenu.class)); + fakeMenu = mock(SkyBlockMenu.class); - doNothing().when(fakeConfigMenu).onClick(any(InventoryClickEvent.class)); doNothing().when(fakeMenu).onClick(any(InventoryClickEvent.class)); - doReturn(fakeConfigMenu).when(fakePlugin).getConfigMenu(); - doReturn(fakeMenu).when(fakePlugin).getMenu(); - - menuEvents = new MenuEvents(fakePlugin); - } - - @Test - public void testOnGuiClick_configMenu() { - UltimateHolder holder = new UltimateHolder(getFakePlayer(), "Config: config.yml (1/2)", MenuType.CONFIG); - InventoryClickEvent event = getEvent(holder); - - menuEvents.guiClick(event); - verify(fakeConfigMenu).onClick(event); - verify(fakeMenu, times(0)).onClick(any()); + menuEvents = new MenuEvents(fakeMenu); } @Test @@ -57,7 +44,6 @@ public void testOnGuiClick_regularMenu() { InventoryClickEvent event = getEvent(holder); menuEvents.guiClick(event); - verify(fakeConfigMenu, times(0)).onClick(any()); verify(fakeMenu).onClick(event); } @@ -67,7 +53,6 @@ public void testOnGuiClick_noUltimateHolder() { InventoryClickEvent event = getEvent(holder); menuEvents.guiClick(event); - verify(fakeConfigMenu, times(0)).onClick(any()); verify(fakeMenu, times(0)).onClick(event); } diff --git a/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/handler/CooldownHandlerTest.java b/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/handler/CooldownHandlerTest.java new file mode 100644 index 000000000..8eb408a64 --- /dev/null +++ b/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/handler/CooldownHandlerTest.java @@ -0,0 +1,37 @@ +package us.talabrek.ultimateskyblock.handler; + +import org.bukkit.entity.Player; +import org.junit.Test; +import us.talabrek.ultimateskyblock.test.MutableClock; + +import java.time.Duration; +import java.time.Instant; +import java.util.UUID; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class CooldownHandlerTest { + + @Test + public void testCommandCooldown() { + Instant fixedInstant = Instant.parse("2025-02-10T12:00:00Z"); + MutableClock testClock = new MutableClock(fixedInstant); + var cooldownHandler = new CooldownHandler(testClock, mock()); + UUID testId = new UUID(1, 2); + Player mockPlayer = mock(); + when(mockPlayer.getUniqueId()).thenReturn(testId); + + assertEquals(Duration.ZERO, cooldownHandler.getCooldown(mockPlayer, "test")); + + cooldownHandler.resetCooldown(mockPlayer, "test", Duration.ofSeconds(1)); + assertEquals(Duration.ofSeconds(1), cooldownHandler.getCooldown(mockPlayer, "test")); + + testClock.advance(Duration.ofSeconds(1)); + assertEquals(Duration.ZERO, cooldownHandler.getCooldown(mockPlayer, "test")); + + testClock.advance(Duration.ofSeconds(1)); + assertEquals(Duration.ZERO, cooldownHandler.getCooldown(mockPlayer, "test")); + } +} diff --git a/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/handler/WorldEditHandlerTest.java b/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/handler/WorldEditHandlerTest.java index 4e0fae719..f91874621 100644 --- a/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/handler/WorldEditHandlerTest.java +++ b/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/handler/WorldEditHandlerTest.java @@ -12,7 +12,7 @@ import java.util.Set; import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.assertThat; +import static org.hamcrest.MatcherAssert.assertThat; public class WorldEditHandlerTest { @@ -35,10 +35,9 @@ public class WorldEditHandlerTest { * -16 -1 0 15 * X * - * @throws Exception */ @Test - public void testGetBorderRegionsAligned() throws Exception { + public void testGetBorderRegionsAligned() { // A Region region = new CuboidRegion(BlockVector3.at(0,0,0), BlockVector3.at(15, 15, 15)); Set borderRegions = WorldEditHandler.getBorderRegions(region); @@ -64,9 +63,7 @@ public void testGetBorderRegionsAligned() throws Exception { public void testBorderXPosMax() { Region region = new CuboidRegion(BlockVector3.at(0,0,0), BlockVector3.at(16, 15, 15)); Set borderRegions = WorldEditHandler.getBorderRegions(region); - Set expected = new HashSet<>(Arrays.asList( - new CuboidRegion(BlockVector3.at(16,0,0), BlockVector3.at(16,15,15)) - )); + Set expected = Set.of(new CuboidRegion(BlockVector3.at(16,0,0), BlockVector3.at(16,15,15))); verifySame(borderRegions, expected); } @@ -74,9 +71,7 @@ public void testBorderXPosMax() { public void testBorderXPosMin() { Region region = new CuboidRegion(BlockVector3.at(15,0,0), BlockVector3.at(31, 15, 15)); Set borderRegions = WorldEditHandler.getBorderRegions(region); - Set expected = new HashSet<>(Arrays.asList( - new CuboidRegion(BlockVector3.at(15,0,0), BlockVector3.at(15,15,15)) - )); + Set expected = Set.of(new CuboidRegion(BlockVector3.at(15,0,0), BlockVector3.at(15,15,15))); verifySame(borderRegions, expected); } @@ -84,9 +79,7 @@ public void testBorderXPosMin() { public void testBorderXNegMax() { Region region = new CuboidRegion(BlockVector3.at(-16,0,-16), BlockVector3.at(0, 15, -1)); Set borderRegions = WorldEditHandler.getBorderRegions(region); - Set expected = new HashSet<>(Arrays.asList( - new CuboidRegion(BlockVector3.at(0,0,-16), BlockVector3.at(0,15,-1)) - )); + Set expected = Set.of(new CuboidRegion(BlockVector3.at(0,0,-16), BlockVector3.at(0,15,-1))); verifySame(borderRegions, expected); } @@ -94,9 +87,7 @@ public void testBorderXNegMax() { public void testBorderXNegMin() { Region region = new CuboidRegion(BlockVector3.at(-17,0,-16), BlockVector3.at(-1, 15, -1)); Set borderRegions = WorldEditHandler.getBorderRegions(region); - Set expected = new HashSet<>(Arrays.asList( - new CuboidRegion(BlockVector3.at(-17,0,-16), BlockVector3.at(-17,15,-1)) - )); + Set expected = Set.of(new CuboidRegion(BlockVector3.at(-17,0,-16), BlockVector3.at(-17,15,-1))); verifySame(borderRegions, expected); } @@ -104,9 +95,7 @@ public void testBorderXNegMin() { public void testBorderZPos() { Region region = new CuboidRegion(BlockVector3.at(0,0,0), BlockVector3.at(15, 15, 16)); Set borderRegions = WorldEditHandler.getBorderRegions(region); - Set expected = new HashSet<>(Arrays.asList( - new CuboidRegion(BlockVector3.at(0,0,16), BlockVector3.at(15,15,16)) - )); + Set expected = Set.of(new CuboidRegion(BlockVector3.at(0,0,16), BlockVector3.at(15,15,16))); verifySame(borderRegions, expected); } @@ -183,21 +172,20 @@ private void verifySame(Set borderRegions, Set expected) { * A = 1, 1 * B = 32, 32 * - * @throws Exception */ @Test - public void testGetBorderRegionsUnalignedPos() throws Exception { + public void testGetBorderRegionsUnalignedPos() { Region region = new CuboidRegion(BlockVector3.at(1,0,1), BlockVector3.at(32, 15, 32)); Set borderRegions = WorldEditHandler.getBorderRegions(region); - Set expectedBorder = new HashSet<>(Arrays.asList( + Set expectedBorder = Set.of( new CuboidRegion(BlockVector3.at(1,0,1), BlockVector3.at(15,15,32)), new CuboidRegion(BlockVector3.at(32,0,1), BlockVector3.at(32,15,32)), new CuboidRegion(BlockVector3.at(16,0,1), BlockVector3.at(31,15,15)), new CuboidRegion(BlockVector3.at(16,0,32), BlockVector3.at(31,15,32)) - )); - Set expectedInner = new HashSet<>(Arrays.asList( + ); + Set expectedInner = Set.of( BlockVector2.at(1, 1) - )); + ); verifySame(borderRegions, expectedBorder); Set innerChunks = WorldEditHandler.getInnerChunks(region); assertThat(innerChunks, is(expectedInner)); @@ -207,7 +195,7 @@ public void testGetBorderRegionsUnalignedPos() throws Exception { public void testGetBorderRegionsAligned4Quadrants() throws Exception { Region region = new CuboidRegion(BlockVector3.at(-64,0,-64), BlockVector3.at(63, 15, 63)); Set borderRegions = WorldEditHandler.getBorderRegions(region); - Set expectedBorder = new HashSet<>(Arrays.asList()); + Set expectedBorder = new HashSet<>(); Set expectedInner = new HashSet<>(); for (int x = -4; x <= 3; x++) { for (int z = -4; z <= 3; z++) { @@ -241,10 +229,9 @@ public void testGetBorderRegionsAligned4Quadrants() throws Exception { * A = -31, -31 * B = 32, 32 * - * @throws Exception */ @Test - public void testGetBorderRegionsUnaligned4Quadrants() throws Exception { + public void testGetBorderRegionsUnaligned4Quadrants() { Region region = new CuboidRegion(BlockVector3.at(-31,0,-31), BlockVector3.at(32, 15, 32)); Set borderRegions = WorldEditHandler.getBorderRegions(region); Set expectedBorder = new HashSet<>(Arrays.asList( @@ -270,4 +257,4 @@ public void testGetBorderRegionsUnaligned4Quadrants() throws Exception { assertThat(WorldEditHandler.getInnerChunks(region), is(expectedInner)); assertThat(WorldEditHandler.getOuterChunks(region), is(expectedOuter)); } -} \ No newline at end of file +} diff --git a/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/handler/placeholder/TextPlaceholderTest.java b/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/handler/placeholder/TextPlaceholderTest.java index 2ea9859dc..133dd9c46 100644 --- a/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/handler/placeholder/TextPlaceholderTest.java +++ b/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/handler/placeholder/TextPlaceholderTest.java @@ -2,15 +2,15 @@ import org.bukkit.OfflinePlayer; import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; import org.junit.Test; -import java.util.Arrays; -import java.util.HashSet; +import java.util.Collection; import java.util.Set; import static org.hamcrest.CoreMatchers.nullValue; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.Is.is; -import static org.junit.Assert.*; /** * Created by R4zorax on 26/04/2016. @@ -18,11 +18,10 @@ public class TextPlaceholderTest { @Test public void replacePlaceholders() throws Exception { - TextPlaceholder placeholder = new TextPlaceholder(); - placeholder.registerPlaceholder(null, new PlaceholderAPI.PlaceholderReplacer() { + TextPlaceholder placeholder = new TextPlaceholder(new PlaceholderAPI.PlaceholderReplacer() { @Override - public Set getPlaceholders() { - return new HashSet<>(Arrays.asList("usb_replaceme")); + public @NotNull Collection getPlaceholders() { + return Set.of("usb_replaceme"); } @Override @@ -35,6 +34,4 @@ public String replace(OfflinePlayer offlinePlayer, Player player, String placeho assertThat(placeholder.replacePlaceholders(null, "Hi {usb_island_level}"), is("Hi {usb_island_level}")); assertThat(placeholder.replacePlaceholders(null, "Hi {usb_replaceme} please"), is("Hi replaced string please")); } - - -} \ No newline at end of file +} diff --git a/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/imports/ItemComponentConverterTest.java b/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/imports/ItemComponentConverterTest.java new file mode 100644 index 000000000..12f63010a --- /dev/null +++ b/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/imports/ItemComponentConverterTest.java @@ -0,0 +1,112 @@ +package us.talabrek.ultimateskyblock.imports; + +import org.bukkit.configuration.file.YamlConfiguration; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +import java.io.BufferedReader; +import java.io.File; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.util.Objects; +import java.util.logging.Logger; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public class ItemComponentConverterTest { + + @Rule + public TemporaryFolder testFolder = new TemporaryFolder(); + + @Test + public void testMinimalChallengeConversion() throws Exception { + testConfig("test-challenges.yml", "test-challenges-expected.yml", "challenges.yml"); + } + + @Test + public void testDefaultChallengeConversion() throws Exception { + testConfig("old-default-challenges.yml", "old-default-challenges-expected.yml", "challenges.yml"); + } + + @Test + public void testDefaultSettingsConversion() throws Exception { + testConfig("old-config.yml", "old-config-expected.yml", "config.yml"); + } + + @Test + public void testDefaultChallengeConversionBlockRequirements() throws Exception { + testBlockConverterConfig("old-block-challenges.yml", "expected-block-challenges.yml", "challenges.yml"); + } + + private void testBlockConverterConfig(String originalName, String expectedName, String fileName) throws Exception { + var testFile = new File(testFolder.getRoot(), fileName); + try (var reader = Objects.requireNonNull(getClass().getResourceAsStream(originalName))) { + Files.copy(reader, testFile.toPath()); + } + + var converter = new BlockRequirementConverter(Logger.getAnonymousLogger()); + converter.importFile(testFile); + + assertTrue(testFile.exists()); + long backupFiles; + try (var stream = Files.find(testFolder.getRoot().toPath(), 1, + (path, attr) -> + path.getFileName().toString().startsWith(fileName) && path.getFileName().toString().endsWith(".old") + )) { + backupFiles = stream.count(); + } + assertEquals(1, backupFiles); + + YamlConfiguration actual = new YamlConfiguration(); + actual.load(testFile); + YamlConfiguration expected = new YamlConfiguration(); + try (var reader = new BufferedReader(new InputStreamReader(Objects.requireNonNull( + getClass().getResourceAsStream(expectedName)), StandardCharsets.UTF_8))) { + expected.load(reader); + } + assertConfigsEquals(expected, actual); + } + + private void testConfig(String originalName, String expectedName, String fileName) throws Exception { + var testFile = new File(testFolder.getRoot(), fileName); + try (var reader = Objects.requireNonNull(getClass().getResourceAsStream(originalName))) { + Files.copy(reader, testFile.toPath()); + } + + var converter = new ItemComponentConverter(Logger.getAnonymousLogger()); + converter.importFile(testFile); + + assertTrue(testFile.exists()); + File backup = new File(testFolder.getRoot(), fileName + ".old"); + assertTrue(backup.isFile()); + + YamlConfiguration actual = new YamlConfiguration(); + actual.load(testFile); + YamlConfiguration expected = new YamlConfiguration(); + try (var reader = new BufferedReader(new InputStreamReader(Objects.requireNonNull( + getClass().getResourceAsStream(expectedName)), StandardCharsets.UTF_8))) { + expected.load(reader); + } + assertConfigsEquals(expected, actual); + } + + private void assertConfigsEquals(YamlConfiguration expected, YamlConfiguration actual) { + for (String key : expected.getKeys(true)) { + assertTrue("Missing key: " + key, actual.contains(key)); + if (expected.isConfigurationSection(key)) { + assertTrue("Key should be a section: " + key, actual.isConfigurationSection(key)); + } else { + assertEquals("Items mismatch at key: " + key, expected.get(key), actual.get(key)); + } + assertThat("Comments mismatch at key: " + key, actual.getComments(key), is(expected.getComments(key))); + assertThat("Inline comments mismatch at key: " + key, actual.getInlineComments(key), is(expected.getInlineComments(key))); + } + assertThat("Headers should be the same", actual.options().getHeader(), is(expected.options().getHeader())); + assertThat("Footers should be the same", actual.options().getFooter(), is(expected.options().getFooter())); + } +} diff --git a/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/imports/challenges/ConfigPre113ImporterTest.java b/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/imports/challenges/ConfigPre113ImporterTest.java deleted file mode 100644 index 43550a593..000000000 --- a/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/imports/challenges/ConfigPre113ImporterTest.java +++ /dev/null @@ -1,58 +0,0 @@ -package us.talabrek.ultimateskyblock.imports.challenges; - -import dk.lockfuglsang.minecraft.file.FileUtil; -import dk.lockfuglsang.minecraft.yml.YmlConfiguration; -import org.bukkit.Material; -import org.bukkit.Server; -import org.bukkit.UnsafeValues; -import org.junit.BeforeClass; -import org.junit.Test; -import us.talabrek.ultimateskyblock.uSkyBlock; - -import java.io.File; -import java.io.InputStream; -import java.lang.reflect.Field; -import java.util.logging.Logger; - -import static dk.lockfuglsang.minecraft.util.BukkitServerMock.setupServerMock; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.is; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -public class ConfigPre113ImporterTest { - - @BeforeClass - public static void SetupAll() throws Exception { - Server server = setupServerMock(); - UnsafeValues unsafeMock = mock(UnsafeValues.class); - when(unsafeMock.fromLegacy((Material) any())).thenAnswer(a -> a.getArguments()[0]); - when(server.getUnsafe()).thenReturn(unsafeMock); - } - - @Test - public void importFile_Default_ConfigYml() throws Exception { - File configFile = File.createTempFile("config", ".dir"); - configFile.delete(); - configFile.deleteOnExit(); - configFile = new File(configFile, "config.yml"); - configFile.deleteOnExit(); - configFile.getParentFile().mkdirs(); - InputStream resourceAsStream = ConfigPre113Importer.class.getClassLoader().getResourceAsStream("config.yml"); - FileUtil.copy(resourceAsStream, configFile); - YmlConfiguration config = new YmlConfiguration(); - FileUtil.readConfig(config, configFile); - - uSkyBlock plugin = mock(uSkyBlock.class); - when(plugin.getConfig()).thenReturn(config); - when(plugin.getLogger()).thenReturn(Logger.getGlobal()); - Field instanceField = uSkyBlock.class.getDeclaredField("instance"); - instanceField.setAccessible(true); - instanceField.set(null, plugin); - instanceField.setAccessible(false); - ConfigPre113Importer sut = new ConfigPre113Importer(); - sut.init(plugin); - assertThat(sut.importFile(configFile), is(true)); - } -} \ No newline at end of file diff --git a/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/imports/wolfwork/PlayerInfoTest.java b/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/imports/wolfwork/PlayerInfoTest.java deleted file mode 100644 index 236a9951e..000000000 --- a/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/imports/wolfwork/PlayerInfoTest.java +++ /dev/null @@ -1,23 +0,0 @@ -package us.talabrek.ultimateskyblock.imports.wolfwork; - -import org.junit.Test; - -import java.io.ObjectInputStream; - -import static org.hamcrest.core.IsNull.notNullValue; -import static org.junit.Assert.assertThat; - -public class PlayerInfoTest { - - @Test - public void testDeserialization() throws Exception { - for (String playerName : new String[]{"Block_Busta_7", "Moctezuma309", "solukkajr", - "xFreakyPVPx", "matt9959", "xXMasterOfPvpXx", "pi4music"}) { - try (ObjectInputStream in = new WolfWorkObjectInputStream(getClass().getClassLoader().getResourceAsStream(playerName))) { - final PlayerInfo p = (PlayerInfo) in.readObject(); - assertThat(p, notNullValue()); - System.out.println(p); - } - } - } -} \ No newline at end of file diff --git a/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/island/IslandLocatorLogicTest.java b/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/island/IslandLocatorLogicTest.java index e54d5b2ed..7efab3daa 100644 --- a/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/island/IslandLocatorLogicTest.java +++ b/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/island/IslandLocatorLogicTest.java @@ -1,11 +1,13 @@ package us.talabrek.ultimateskyblock.island; -import dk.lockfuglsang.minecraft.yml.YmlConfiguration; import org.bukkit.Location; import org.bukkit.World; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.entity.Player; +import org.junit.Rule; import org.junit.Test; -import org.mockito.invocation.InvocationOnMock; +import org.junit.rules.TemporaryFolder; import org.mockito.stubbing.Answer; import us.talabrek.ultimateskyblock.Settings; import us.talabrek.ultimateskyblock.uSkyBlock; @@ -23,12 +25,16 @@ import static org.hamcrest.core.Is.is; import static org.hamcrest.core.IsNot.not; import static org.hamcrest.core.IsNull.notNullValue; -import static org.junit.Assert.assertThat; -import static org.mockito.Matchers.any; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; public class IslandLocatorLogicTest { + + @Rule + public TemporaryFolder tempFolder = new TemporaryFolder(); + @Test public void testNextIslandLocation() throws Exception { Settings.island_distance = 1; @@ -46,7 +52,7 @@ public void testNextIslandLocation() throws Exception { public void testNextIslandLocationReservation() throws Exception { Settings.island_distance = 10; uSkyBlock plugin = createPluginMock(); - IslandLocatorLogic locator = new IslandLocatorLogic(plugin); + IslandLocatorLogic locator = new IslandLocatorLogic(plugin, tempFolder.newFolder().toPath(), mock(), mock(), mock(), mock()); Player player = createPlayerMock(); Location location1 = locator.getNextIslandLocation(player); assertThat(location1, notNullValue()); @@ -59,17 +65,14 @@ public void testNextIslandLocationReservation() throws Exception { public void testNextIslandLocationReservationConcurrency() throws Exception { Settings.island_distance = 10; uSkyBlock plugin = createPluginMock(); - final IslandLocatorLogic locator = new IslandLocatorLogic(plugin); + final IslandLocatorLogic locator = new IslandLocatorLogic(plugin, tempFolder.newFolder().toPath(), mock(), mock(), mock(), mock()); final List locations = new ArrayList<>(); ThreadGroup threadGroup = new ThreadGroup("My"); for (int i = 0; i < 10; i++) { - Thread t = new Thread(threadGroup, new Runnable() { - @Override - public void run() { - Player player = createPlayerMock(); - Location location = locator.getNextIslandLocation(player); - locations.add(location); - } + Thread t = new Thread(threadGroup, () -> { + Player player = createPlayerMock(); + Location location = locator.getNextIslandLocation(player); + locations.add(location); }); t.start(); } @@ -83,18 +86,13 @@ public void run() { private Player createPlayerMock() { Player player = mock(Player.class); - when(player.getLocation()).then(new Answer() { - @Override - public Location answer(InvocationOnMock invocationOnMock) throws Throwable { - return new Location(null, 100, 100, 100); - } - }); + when(player.getLocation()).then((Answer) invocationOnMock -> new Location(null, 100, 100, 100)); return player; } private uSkyBlock createPluginMock() { uSkyBlock plugin = mock(uSkyBlock.class); - YmlConfiguration config = new YmlConfiguration(); + FileConfiguration config = new YamlConfiguration(); when(plugin.getConfig()).thenReturn(config); OrphanLogic orphanLogic = mock(OrphanLogic.class); when(plugin.getOrphanLogic()).thenReturn(orphanLogic); @@ -103,4 +101,4 @@ private uSkyBlock createPluginMock() { when(plugin.getWorldManager()).thenReturn(worldManager); return plugin; } -} \ No newline at end of file +} diff --git a/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/island/OrphanComparatorTest.java b/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/island/OrphanComparatorTest.java index c32cb23db..c1f3fa9a9 100644 --- a/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/island/OrphanComparatorTest.java +++ b/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/island/OrphanComparatorTest.java @@ -2,15 +2,13 @@ import org.junit.Test; -import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.List; import java.util.Set; import java.util.TreeSet; import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.*; +import static org.hamcrest.MatcherAssert.assertThat; /** * Tests the OrphanComparator @@ -24,7 +22,7 @@ public void testSort() throws Exception { List list = Arrays.asList(o1,o2,o3); List expected = Arrays.asList(o3,o2,o1); - Collections.sort(list, new OrphanComparator()); + list.sort(new OrphanComparator()); assertThat(list, is(expected)); } @@ -38,4 +36,4 @@ public void testSet() throws Exception { assertThat(set.size(), is(3)); } -} \ No newline at end of file +} diff --git a/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/island/level/BlockLevelConfigMapTest.java b/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/island/level/BlockLevelConfigMapTest.java index 7c328fd0d..baa28dca3 100644 --- a/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/island/level/BlockLevelConfigMapTest.java +++ b/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/island/level/BlockLevelConfigMapTest.java @@ -18,11 +18,10 @@ public void get() { collection.add(defaultBuilder.copy().base(Material.AIR).scorePerBlock(0).build()); collection.add(defaultBuilder.copy().base(Material.STONE).scorePerBlock(12).build()); collection.add(defaultBuilder.copy().base(Material.OAK_WOOD).scorePerBlock(9) - .additionalBlocks(new BlockMatch(Material.OAK_LOG, new byte[] {0,1,2,3,4})) + .additionalBlocks(new BlockMatch(Material.OAK_LOG)) .build()); BlockLevelConfigMap map = new BlockLevelConfigMap(collection, defaultBuilder); assertThat(map.get(Material.AIR).getScorePerBlock(), is(0d)); - assertThat(map.get(Material.AIR, (byte) 1).getScorePerBlock(), is(0d)); } -} \ No newline at end of file +} diff --git a/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/island/level/LevelConfigFileTest.java b/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/island/level/LevelConfigFileTest.java new file mode 100644 index 000000000..a048802ff --- /dev/null +++ b/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/island/level/LevelConfigFileTest.java @@ -0,0 +1,28 @@ +package us.talabrek.ultimateskyblock.island.level; + +import org.bukkit.Material; +import org.bukkit.configuration.file.YamlConfiguration; +import org.junit.Test; + +import java.io.InputStream; +import java.io.InputStreamReader; + +import static org.junit.Assert.assertNotNull; + +public class LevelConfigFileTest { + @Test + public void testForInvalidMaterials() { + InputStream levelResource = getClass().getClassLoader().getResourceAsStream("imported/levelConfig.yml"); + YamlConfiguration levelConfig = YamlConfiguration.loadConfiguration(new InputStreamReader(levelResource)); + + for (String key : levelConfig.getConfigurationSection("blocks").getKeys(false)) { + assertNotNull(Material.getMaterial(key)); + + if (levelConfig.getConfigurationSection("blocks").getConfigurationSection(key).contains("additionalBlocks")) { + for (String additionalKey : levelConfig.getConfigurationSection("blocks").getConfigurationSection(key).getStringList("additionalBlocks")) { + assertNotNull(Material.getMaterial(additionalKey)); + } + } + } + } +} diff --git a/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/test/MutableClock.java b/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/test/MutableClock.java new file mode 100644 index 000000000..bdec2bebf --- /dev/null +++ b/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/test/MutableClock.java @@ -0,0 +1,33 @@ +package us.talabrek.ultimateskyblock.test; + +import java.time.Clock; +import java.time.Duration; +import java.time.Instant; +import java.time.ZoneId; + +public class MutableClock extends Clock { + private Instant instant; + + public MutableClock(Instant initialInstant) { + this.instant = initialInstant; + } + + @Override + public ZoneId getZone() { + return ZoneId.of("UTC"); + } + + @Override + public Clock withZone(ZoneId zone) { + return this; + } + + @Override + public Instant instant() { + return instant; + } + + public void advance(Duration duration) { + this.instant = this.instant.plus(duration); + } +} diff --git a/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/uSkyBlockTest.java b/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/uSkyBlockTest.java index 7784626e8..d5af730c8 100644 --- a/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/uSkyBlockTest.java +++ b/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/uSkyBlockTest.java @@ -1,7 +1,7 @@ package us.talabrek.ultimateskyblock; import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertThat; +import static org.hamcrest.MatcherAssert.assertThat; import static dk.lockfuglsang.minecraft.util.FormatUtil.stripFormatting; public class uSkyBlockTest { @@ -12,4 +12,4 @@ public void testStripFormatting() throws Exception { assertThat(stripFormatting(text), is("Hello Babe You wanna dance with somebody")); } -} \ No newline at end of file +} diff --git a/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/util/BiomeUtilTest.java b/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/util/BiomeUtilTest.java deleted file mode 100644 index 204ebaa67..000000000 --- a/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/util/BiomeUtilTest.java +++ /dev/null @@ -1,15 +0,0 @@ -package us.talabrek.ultimateskyblock.util; - -import org.bukkit.block.Biome; -import org.junit.Assert; -import org.junit.Test; - -public class BiomeUtilTest { - @Test - public void getBiomeTest() throws Exception { - Assert.assertEquals(Biome.DARK_FOREST, BiomeUtil.getBiome("DARK_FOREST")); - Assert.assertEquals(Biome.JUNGLE, BiomeUtil.getBiome("JUNGLE")); - - Assert.assertNull(BiomeUtil.getBiome("INVALID_BIOME")); - } -} diff --git a/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/util/BlockUtilTest.java b/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/util/BlockUtilTest.java index 854dc4d33..b128bc190 100644 --- a/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/util/BlockUtilTest.java +++ b/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/util/BlockUtilTest.java @@ -3,10 +3,13 @@ import org.bukkit.Material; import org.bukkit.block.Block; import org.junit.Assert; +import org.junit.Ignore; import org.junit.Test; import org.mockito.Mockito; public class BlockUtilTest { + + @Ignore @Test public void testIsBreathable() throws Exception { Block fakeBlock = Mockito.mock(Block.class); @@ -14,7 +17,7 @@ public void testIsBreathable() throws Exception { Mockito.when(fakeBlock.getType()).thenReturn(Material.DIRT); Assert.assertFalse(BlockUtil.isBreathable(fakeBlock)); - Mockito.when(fakeBlock.getType()).thenReturn(Material.GRASS); + Mockito.when(fakeBlock.getType()).thenReturn(Material.SHORT_GRASS); Assert.assertTrue(BlockUtil.isBreathable(fakeBlock)); Mockito.when(fakeBlock.getType()).thenReturn(Material.WATER); diff --git a/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/util/EntityUtilTest.java b/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/util/EntityUtilTest.java index aef130bd5..3f0591ba1 100644 --- a/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/util/EntityUtilTest.java +++ b/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/util/EntityUtilTest.java @@ -1,86 +1,11 @@ package us.talabrek.ultimateskyblock.util; -import org.bukkit.entity.Animals; -import org.bukkit.entity.ArmorStand; -import org.bukkit.entity.Cow; -import org.bukkit.entity.Entity; import org.bukkit.entity.EntityType; -import org.bukkit.entity.Evoker; -import org.bukkit.entity.Guardian; -import org.bukkit.entity.Monster; -import org.bukkit.entity.NPC; -import org.bukkit.entity.Pig; -import org.bukkit.entity.PigZombie; -import org.bukkit.entity.Pillager; -import org.bukkit.entity.Sheep; -import org.bukkit.entity.Skeleton; -import org.bukkit.entity.Turtle; -import org.bukkit.entity.Villager; -import org.bukkit.entity.WanderingTrader; import org.junit.Test; -import java.util.ArrayList; -import java.util.List; - import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.mock; public class EntityUtilTest { - @Test - public void testGetEntity() { - List testList = new ArrayList<>(); - - ArmorStand fakeArmorStand = mock(ArmorStand.class); - Cow fakeCow = mock(Cow.class); - Evoker fakeEvoker = mock(Evoker.class); - Guardian fakeGuardian = mock(Guardian.class); - Pig fakePig = mock(Pig.class); - PigZombie fakePigZombie = mock(PigZombie.class); - Pillager fakePillager = mock(Pillager.class); - Sheep fakeSheep = mock(Sheep.class); - Skeleton fakeSkeleton = mock(Skeleton.class); - Turtle fakeTurtle = mock(Turtle.class); - Villager fakeVillager = mock(Villager.class); - WanderingTrader fakeWanderingTrader = mock(WanderingTrader.class); - - testList.add(fakeArmorStand); - testList.add(fakeCow); - testList.add(fakeEvoker); - testList.add(fakeGuardian); - testList.add(fakePig); - testList.add(fakePigZombie); - testList.add(fakePillager); - testList.add(fakeSheep); - testList.add(fakeSkeleton); - testList.add(fakeTurtle); - testList.add(fakeVillager); - testList.add(fakeWanderingTrader); - - List sheepList = EntityUtil.getEntity(testList, Sheep.class); - assertEquals(1, sheepList.size()); - assertEquals(fakeSheep, sheepList.get(0)); - - List animalsList = EntityUtil.getAnimals(testList); - assertEquals(4, animalsList.size()); - assertTrue(animalsList.contains(fakeCow)); - assertTrue(animalsList.contains(fakePig)); - assertTrue(animalsList.contains(fakeSheep)); - assertTrue(animalsList.contains(fakeTurtle)); - - List monsterList = EntityUtil.getMonsters(testList); - assertEquals(5, monsterList.size()); - assertTrue(monsterList.contains(fakeEvoker)); - assertTrue(monsterList.contains(fakeGuardian)); - assertTrue(monsterList.contains(fakePigZombie)); - assertTrue(monsterList.contains(fakePillager)); - assertTrue(monsterList.contains(fakeSkeleton)); - - List npcList = EntityUtil.getNPCs(testList); - assertEquals(2, npcList.size()); - assertTrue(npcList.contains(fakeVillager)); - assertTrue(npcList.contains(fakeWanderingTrader)); - } @Test public void testGetEntityDisplayName() { diff --git a/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/util/FileUtilTest.java b/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/util/FileUtilTest.java index 8c8bdba8a..d06e32629 100644 --- a/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/util/FileUtilTest.java +++ b/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/util/FileUtilTest.java @@ -3,7 +3,7 @@ import org.junit.Test; import static org.hamcrest.core.Is.is; -import static org.junit.Assert.*; +import static org.hamcrest.MatcherAssert.assertThat; /** * JUnit tests for FileUtil @@ -14,4 +14,4 @@ public void testGetExtension() { assertThat(FileUtil.getExtension("basename.ext"), is("ext")); assertThat(FileUtil.getExtension("my file.with.dot.yml"), is("yml")); } -} \ No newline at end of file +} diff --git a/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/util/LocationUtilTest.java b/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/util/LocationUtilTest.java index d28be49ec..689e7e2d2 100644 --- a/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/util/LocationUtilTest.java +++ b/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/util/LocationUtilTest.java @@ -5,7 +5,7 @@ import us.talabrek.ultimateskyblock.Settings; import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.*; +import static org.hamcrest.MatcherAssert.assertThat; public class LocationUtilTest { @@ -36,4 +36,4 @@ public void testAlignToDistance1024x1024() throws Exception { loc.setZ(-512.01); assertThat(LocationUtil.alignToDistance(loc, 1024), is(new Location(null, -1024, Settings.island_height, -1024))); } -} \ No newline at end of file +} diff --git a/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/util/MaterialUtilTest.java b/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/util/MaterialUtilTest.java index e3bb4fc20..e76112c49 100644 --- a/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/util/MaterialUtilTest.java +++ b/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/util/MaterialUtilTest.java @@ -6,7 +6,7 @@ import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.nullValue; -import static org.junit.Assert.assertThat; +import static org.hamcrest.MatcherAssert.assertThat; public class MaterialUtilTest { @Test @@ -60,4 +60,4 @@ public void testGetToolType() { assertThat(MaterialUtil.getToolType(Material.DIAMOND_PICKAXE), is("DIAMOND")); assertThat(MaterialUtil.getToolType(Material.DIAMOND_SHOVEL), is("DIAMOND")); } -} \ No newline at end of file +} diff --git a/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/util/MetaUtilTest.java b/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/util/MetaUtilTest.java new file mode 100644 index 000000000..82fd17c88 --- /dev/null +++ b/uSkyBlock-Core/src/test/java/us/talabrek/ultimateskyblock/util/MetaUtilTest.java @@ -0,0 +1,17 @@ +package us.talabrek.ultimateskyblock.util; + +import org.junit.Assert; +import org.junit.Test; + +import java.util.Map; + +public class MetaUtilTest { + @Test + public void testSingleMapParse() throws Exception { + String inputMap = "{\"Color\":8}"; + + Map outputMap = MetaUtil.createMap(inputMap); + Assert.assertEquals(1, outputMap.size()); + Assert.assertEquals(8.0, outputMap.get("Color")); + } +} diff --git a/uSkyBlock-Core/src/test/resources/us/talabrek/ultimateskyblock/imports/expected-block-challenges.yml b/uSkyBlock-Core/src/test/resources/us/talabrek/ultimateskyblock/imports/expected-block-challenges.yml new file mode 100644 index 000000000..8e04580c2 --- /dev/null +++ b/uSkyBlock-Core/src/test/resources/us/talabrek/ultimateskyblock/imports/expected-block-challenges.yml @@ -0,0 +1,1794 @@ +# ====================================================================================================================== +# +# Here follows the format and explanation for the challenge group and challenges setup. The single commented (#) are the +# most used settings while the double commented (##) are optional if you like to use those settings. +# +# Item type format +# Item types are defined as in the minecraft /give command, i.e. with their minecraft key and possible components in +# square brackets. For example, 'minecraft:diamond_sword[damage=42]'. Refer to the Minecraft wiki for more information: +# https://minecraft.wiki/w/Data_component_format. You can also use the command '/usb iteminfo' to get the information. +# +# This item type is supplemented with additional information depending on where it is used: +# +# display-item: +# An item to be displayed in a GUI. It only defines the item type as above, without any additional information. +# For example: 'cobblestone', 'minecraft:stone', 'diamond_sword[damage=42]'. +# +# item-requirement: :[;+] +# An item requirement for a challenge. The amount is the number of items required. The optional + is the number +# of items to add to the required amount for each repeat of the challenge. For example, 'cobblestone:64;+16' would +# require 64 cobblestone for the first completion and 80 cobblestone for the second completion. Other options are +# -, *, and / for subtraction, multiplication, and division, respectively. For example, 'cobblestone:64;*2' would +# require 64 cobblestone for the first completion, 128 cobblestone for the second completion, and 256 cobblestone for +# the third completion. +# +# item-reward: [{p=}]: +# An item reward for a challenge. The amount is the number of items to give. The optional {p=} is the +# probability of the item being given. For example, 'cobblestone:64' would give 64 cobblestone every time the challenge +# is completed, while '{p=0.1}cobblestone:64' would give 64 cobblestone 10% of the time. +# +# ====================================================================================================================== +# All challenges are defined in the ranks section. Each rank is a tier of challenges that players can complete. +# +# ranks: +# # [text] name of the challenge Rank. +# TierX: +# # [text] The name of the challenge rank that shows when you do /challenges (supports capitals and color codes). +# name: '&aCustom Challenges rank name' +# # [display-item] The item to be displayed in the challenge menu for completed challenges. +# displayItem: 'cyan_terracotta' +# # [integer] The time in hours before required items reset to default (this overwrites the main reset time) +# resetInHours: 20 +# # These requirements controls when a challenge group will be available to a player. +# requires: +# # [integer] The number of tasks per rank that can be left uncompleted to advance to the next rank. For example, +# if you have 4 challenges with a rankLeeway of 1, a player would only need to complete 3 to advance to +# the next rank. A rankLeeway of 0 would require them all. +# rankLeeway: 8 +# # [List[text]] Challenges that have to be completed before this group will available to a player. +# challenges: +# - a challenge name +# ====================================================================================================================== +# challenges: +# # [text] The name of the challenge. All challenge names should be lower-case. +# defaultchallenge: +# # [text] The name of the challenge that shows in /challenges (this supports capitals and color codes). +# name: '&a Default Challenge' +# # [text] The descriptions players see when they do /challenges +# description: +# # [onIsland/onPlayer/islandLevel] This defines whether the required blocks/items should be in the player's +# # inventory or on their island. When using onIsland, the player must within 10 blocks from the required blocks +# # on his island. When using islandLevel, the 'requiredItems' field should be the island level required. The +# # player must use /island level first to update their level. +# type: onPlayer +# ## type: islandLevel +# ## type: onIsland +# # [integer] Overrides the default radius of 10 blocks when using onIsland. +# ## radius: 20 +# # List[item-requirement] The items required to complete the challenge. +# requiredItems: +# - stone:64;+16 +# - cobblestone:64;+16 +# # List[block-requirement] The blocks required to complete the challenge. +# requiredBlocks: +# - stone:64 +# - cobblestone:64 +# # [true/false] If the challenge can be repeated or not. +# ## repeatable: true +# # [integer] The maximum number of times the challenge can be completed. Overrides the default repeatLimit. +# # A value of 0 means unlimited repeats. +# ## repeatLimit: 5 +# # [display-item] The item to be displayed in the challenge menu for completed challenges. +# displayItem: cobblestone +# # [integer] The time in hours before required items reset to default (overwrites the main and rank defaults). +# ## resetInHours: 4 +# # [true/false] Take required items on completing a challenge. +# ## takeItems: true +# # The rewards players get for completing the challenge +# reward: +# # [text] Description of the reward. +# text: 'Mossy cobblestone and an iron pickaxe with unbreaking 1' +# # [List[item-requirement]] A list of items given to the player for completing the challenge. +# items: +# - mossy_cobblestone:16 +# - iron_pickaxe[enchantments={levels:{unbreaking:1}}]:1 +# # [permission node] A permission granted for completion. Multiple permissions are space-separated. +# ## permission: 'test.permission' +# # [integer] How much currency to give for completion. (requires an economy plugin) +# ## currency: 0 +# # [integer] How much xp to give to the player for completion. +# ## xp: 0 +# # [List[Text]] Executes the given command upon completion. Prepend with "op" or "console" to run the commands +# as OP or from the Console. Examples: +# # Possible command arguments are: +# # {player} - The name of the player +# # {playerName} - The display name of the player +# # {challenge} - The name of the challenge +# # {position} - The position of the player +# # {party} - Execute the command once for each member of the party (substituting the name) +# ## commands: +# ## - 'op: me are the GOD of things' +# ## - 'console: give {party} aweseomestuff 32' +# # reward section to reward the player for completing a repeated challenge (any time after the first). The +# # structure is identical to the 'reward' section. +# repeatReward: +# text: 'Mossy cobblestone' +# items: +# - mossy_cobblestone:16 +# +# ====================================================================================================================== + +# [true/false] Enable the use of the challenges command. +allowChallenges: true + +# [island/player] Whether challenges are tracked per player, or per island +challengeSharing: island + +# [true/false] If true, first time challenge completions are broadcast to the whole server. +broadcastCompletion: true + +# [text] The color/formatting of the broadcast text when showing first time completions. +broadcastText: '&6' + +# [true/false] If true, challenges in higher level ranks require challenges in lower level ranks to be completed. +requirePreviousRank: true + +# [integer] The number of tasks per rank that can be left uncompleted to advance to the next rank. For example, if you have 4 easy challenges +# with a rankLeeway of 1, a player would only need to complete 3 to advance to the next rank. +# A rankLeeway of 0 would require them all. +rankLeeway: 12 + +# [integer] The time in hours before required items reset to default. (only if not specified in the challenges below) +defaultResetInHours: 20 + +# [integer] The default radius in blocks when using onIsland. Can be overridden in the challenges below. +radius: 10 + +# [integer] The maximum number of times a challenge can be completed by default. 0 means unlimited. Can be overridden in the challenge definition below. +repeatLimit: 0 + +# [color code] The color to use for uncompleted challenges in the list. +challengeColor: '&e' + +# [color code] The color to use for completed challenges in the list. (non-repeatable) +finishedColor: '&2' + +# [color code] The color to use for completed challenges in the list. (repeatable) +repeatableColor: '&a' + +# [true/false] If true, enables vault to handle currency rewards. +enableEconomyPlugin: true + +# [true/false] If false, challenges are not reset on island creation (or restart) +resetChallengesOnCreate: true + +# Material to show for locked challenges (i.e. STAINED_GLASS_PANE:14 for a red glass-pane, or 160:14) +lockedDisplayItem: red_stained_glass_pane + +# Material to show for onIsland challenges when locked +ISLAND: + lockedDisplayItem: blue_stained_glass_pane + +# Material to show for islandLevel challenges when locked +ISLAND_LEVEL: + lockedDisplayItem: black_stained_glass_pane + +# Whether to show the name of locked challenges +showLockedChallengeName: true + +# When creating your own challenges you will have +# to uncomment the section below or the default challenges +# will be re-added on every server restart. When altering +# the default challenges this should be fine to leave. +# merge-ignore: +# - 'ranks' +# +# =============================================== +# An explanation to setup your own challenges +# can be found at the bottom of this file. +# =============================================== +ranks: + Tier1: + name: '&7Novice' + displayItem: cyan_terracotta + resetInHours: 20 + challenges: + cobblestonegenerator: + name: '&7Cobble Stone Generator' + description: Mine from a cobblestone generator. + type: onPlayer + requiredItems: + - cobblestone:64;+2 + displayItem: cobblestone + lockedDisplayItem: gray_stained_glass_pane + resetInHours: 12 + reward: + text: 3 leather, 20% chance to get a book + items: + - leather:3 + - '{p=0.2}book:1' + currency: 10 + xp: 10 + commands: + - op:effect give {player} regeneration + repeatReward: + text: 1 leather, 10% chance to get a book + items: + - leather:1 + - '{p=0.1}book:1' + currency: 5 + xp: 5 + applecollector: + name: '&6Apple Collector' + description: Collect apples from trees. + type: onPlayer + requiredItems: + - apple:2;+1 + displayItem: apple + lockedDisplayItem: brown_stained_glass_pane + resetInHours: 6 + reward: + text: 1 of each sapling, (4 dark oak) + items: + - oak_sapling:1 + - spruce_sapling:1 + - birch_sapling:1 + - jungle_sapling:1 + - acacia_sapling:1 + - dark_oak_sapling:4 + - '{p=0.2}jungle_sapling:3' + currency: 20 + xp: 20 + repeatReward: + text: 1 of each sapling (4 dark oak) + items: + - oak_sapling:1 + - spruce_sapling:1 + - birch_sapling:1 + - jungle_sapling:1 + - acacia_sapling:1 + - dark_oak_sapling:4 + - '{p=0.1}jungle_sapling:3' + currency: 10 + xp: 10 + sugarplanter: + name: '&9Sugar Planter' + type: onIsland + displayItem: sugar_cane + lockedDisplayItem: brown_stained_glass_pane + requiredBlocks: + - sugar_cane:4 + reward: + text: 4 dirt + items: + - dirt:4 + currency: 20 + xp: 20 + sugarfarmer: + name: '&6Sugar Farmer' + description: Harvest sugarcane from a farm. + type: onPlayer + requiredItems: + - sugar_cane:64;+16 + offset: -1 + displayItem: sugar_cane + lockedDisplayItem: brown_stained_glass_pane + reward: + text: 8 dirt + items: + - dirt:8 + - '{p=0.1}bone:1' + currency: 20 + xp: 20 + repeatReward: + text: 4 dirt + items: + - dirt:4 + - '{p=0.05}dirt:4' + currency: 10 + xp: 10 + melonfarmer: + name: '&6Melon Farmer' + description: Harvest slices of melon from a farm. + type: onPlayer + requiredItems: + - melon_slice:128;+8 + displayItem: melon + lockedDisplayItem: brown_stained_glass_pane + reward: + text: 8 dirt + items: + - dirt:8 + currency: 20 + xp: 20 + repeatReward: + text: 4 dirt + items: + - dirt:4 + currency: 10 + xp: 10 + cactusfarmer: + name: '&6Cactus Farmer' + description: Harvest cacti from a farm. + type: onPlayer + requiredItems: + - cactus:64;+16 + displayItem: cactus + lockedDisplayItem: brown_stained_glass_pane + reward: + text: 8 sand, 20% chance to get a bone + items: + - sand:8 + - '{p=0.2}bone:1' + - '{p=0.1}wooden_hoe:1' + currency: 20 + xp: 20 + repeatReward: + text: 4 sand + items: + - sand:4 + - '{p=0.1}bone:1' + currency: 10 + xp: 10 + pumpkinfarmer: + name: '&6Pumpkin Farmer' + description: Harvest pumpkins from a farm. + type: onPlayer + requiredItems: + - pumpkin:64;+4 + displayItem: pumpkin + lockedDisplayItem: brown_stained_glass_pane + reward: + text: 8 dirt + items: + - dirt:8 + - '{p=0.3}jack_o_lantern:1' + currency: 20 + xp: 20 + repeatReward: + text: 4 dirt + items: + - dirt:4 + - '{p=0.05}jack_o_lantern:1' + currency: 10 + xp: 10 + stonebrickmaker: + name: '&aStone Brick Maker' + description: Make 64 Stone Bricks. + type: onPlayer + requiredItems: + - stone_bricks:64;+8 + - stone_brick_slab:30;+6 + - chiseled_stone_bricks:30;+6 + - stone_brick_stairs:16;+4 + displayItem: stone_bricks + lockedDisplayItem: gray_stained_glass_pane + reward: + text: 4 redstone ore, 4 iron ore, 1 chicken + items: + - redstone_ore:4 + - iron_ore:4 + - chicken_spawn_egg:1 + currency: 30 + xp: 30 + repeatReward: + text: 1 redstone ore, 1 iron ore + items: + - redstone_ore:1 + - iron_ore:1 + currency: 15 + xp: 15 + novicebuilder: + name: '&7Novice Builder' + description: Reach island level 20. + type: islandLevel + requiredLevel: 20 + displayItem: coal_ore + reward: + text: 8 dirt, 8 sand, 5 emeralds + items: + - dirt:8 + - sand:8 + - emerald:5 + - diamond:1 + currency: 20 + xp: 20 + commands: + - op:effect give {party} regeneration + adeptbuilder: + name: '&aAdept Builder' + description: Reach island level 50. + type: islandLevel + requiredLevel: 50 + displayItem: iron_ore + offset: -1 + reward: + text: 10 obsidian, 2 diamonds, 5 emeralds + items: + - obsidian:10 + - emerald:5 + - diamond:2 + currency: 100 + xp: 100 + expertbuilder: + name: '&eExpert Builder' + description: Reach island level 100. + type: islandLevel + requiredLevel: 100 + displayItem: gold_ore + offset: -1 + reward: + text: 16 dirt, 16 sand, 3 diamonds, 5 emeralds + items: + - dirt:16 + - sand:16 + - emerald:5 + - diamond:3 + currency: 250 + xp: 250 + masterbuilder: + name: '&cMaster Builder' + description: Reach island level 250. + type: islandLevel + requiredLevel: 250 + displayItem: diamond_ore + offset: -1 + reward: + text: 32 dirt, 32 sand, 4 diamonds, 5 emeralds + items: + - dirt:32 + - sand:32 + - emerald:5 + - diamond:4 + currency: 500 + xp: 500 + skylord: + name: '&4Sky Lord' + description: Reach island level 500. + type: islandLevel + requiredLevel: 500 + displayItem: emerald_ore + offset: -1 + reward: + text: 64 dirt, 64 sand, 5 diamond, 5 emeralds + items: + - dirt:64 + - sand:64 + - diamond:5 + - emerald:5 + currency: 1000 + xp: 1000 + Tier2: + name: '&aAdept' + displayItem: lime_terracotta + resetInHours: 20 + requires: + + # means disabled in effect + rankLeeway: 99 + challenges: + - cobblestonegenerator + - novicebuilder + challenges: + lumberjack: + name: '&3Lumberjack' + description: Collect all types of wood logs. + type: onPlayer + requiredItems: + - oak_log:16;+2 + - spruce_log:16;+2 + - birch_log:16;+2 + - jungle_log:16;+2 + - acacia_log:16;+2 + - dark_oak_log:16;+2 + displayItem: oak_log + lockedDisplayItem: cyan_stained_glass_pane + reward: + text: 4 redstone ore, 4 iron ore, 1 wolf + items: + - redstone_ore:4 + - iron_ore:4 + - wolf_spawn_egg:1 + currency: 30 + xp: 30 + repeatReward: + text: 1 redstone ore, 1 iron ore + items: + - redstone_ore:1 + - iron_ore:1 + currency: 15 + xp: 15 + shroompicker: + name: '&6Shroom Picker' + description: Collect red and brown mushrooms. + type: onPlayer + requiredItems: + - brown_mushroom:64;+4 + - red_mushroom:64;+4 + displayItem: red_mushroom + lockedDisplayItem: brown_stained_glass_pane + reward: + text: 8 mycelium, 4 podzol + items: + - mycelium:8 + - podzol:4 + currency: 30 + xp: 30 + repeatReward: + text: 4 mycelium + items: + - mycelium:4 + - '{p=0.02}mycelium:4' + - '{p=0.02}podzol:2' + currency: 15 + xp: 15 + potatofarmer: + name: '&6Potato Farmer' + description: Harvest potato's from a farm. + type: onPlayer + requiredItems: + - potato:64;+16 + displayItem: potato + lockedDisplayItem: brown_stained_glass_pane + reward: + text: 1 carrot, 4 dirt + items: + - carrot:1 + - dirt:4 + - '{p=0.10}beetroot_seeds:1' + - '{p=0.05}red_sand:1' + - '{p=0.01}diamond:1' + currency: 50 + xp: 50 + repeatReward: + text: 4 dirt and a baked potato + items: + - dirt:4 + - baked_potato:1 + - '{p=0.10}beetroot_seeds:1' + - '{p=0.05}red_sand:1' + - '{p=0.01}diamond:1' + currency: 25 + xp: 25 + carrotfarmer: + name: '&6Carrot Farmer' + description: Harvest carrot's from a farm. + type: onPlayer + requiredItems: + - carrot:64;+16 + displayItem: carrot + lockedDisplayItem: brown_stained_glass_pane + reward: + text: a pig with saddle and a potato + items: + - saddle:1 + - potato:1 + - pig_spawn_egg:1 + - '{p=0.05}red_sand:1' + - '{p=0.01}diamond:1' + currency: 50 + xp: 50 + repeatReward: + text: 1 golden carrot + items: + - golden_carrot:1 + - '{p=0.05}sand:1' + - '{p=0.01}diamond:1' + currency: 25 + xp: 25 + wheatfarmer: + name: '&6Wheat Farmer' + description: Harvest wheat from a farm. + type: onPlayer + requiredItems: + - wheat:64;+16 + displayItem: wheat + lockedDisplayItem: brown_stained_glass_pane + reward: + text: 8 dirt + items: + - dirt:8 + currency: 20 + xp: 20 + repeatReward: + text: 4 dirt + items: + - dirt:4 + - '{p=0.2}bone:1' + currency: 10 + xp: 10 + monsterfarm: + name: '&5Monster Farm' + description: Build a mob farm and collect mob loot. + type: onPlayer + requiredItems: + - rotten_flesh:64;+4 + - string:32;+2 + - arrow:32;+2 + - bone:32;+2 + - gunpowder:16;+1 + - spider_eye:5 + displayItem: rotten_flesh + lockedDisplayItem: purple_stained_glass_pane + reward: + text: 4 redstone ore, 4 iron ore and 1 flint + items: + - redstone_ore:4 + - iron_ore:4 + - flint:1 + - '{p=0.10}potato:1' + - '{p=0.10}carrot:1' + currency: 30 + xp: 30 + repeatReward: + text: 1 redstone ore, 1 iron ore, 1 flint + items: + - redstone_ore:1 + - iron_ore:4 + - flint:1 + - '{p=0.05}potato:1' + - '{p=0.05}carrot:1' + currency: 15 + xp: 15 + homeowner: + name: '&9Home Owner' + description: Build a house with furnishings. + type: onIsland + requiredChallenges: + - stonebrickmaker + requiredBlocks: + - red_bed:1 + - crafting_table:1 + - glass:1 + - oak_door:1 + - furnace:1 + - bookshelf:1 + - torch:1 + lockedDisplayItem: blue_stained_glass_pane + displayItem: oak_door + reward: + text: 4 redstone ore, 5 inksac, 4 iron ore and some seeds + items: + - redstone_ore:4 + - iron_ore:4 + - ink_sac:5 + - beetroot_seeds:1 + currency: 40 + xp: 40 + netherportal: + name: '&9Nether Portal' + description: Build a nether portal on your island. + type: onIsland + requiredChallenges: + - adeptbuilder + - homeowner + requiredBlocks: + - obsidian:10 + - nether_portal:1 + displayItem: obsidian + lockedDisplayItem: blue_stained_glass_pane + reward: + text: 1 iron pickaxe, 1 iron shovel + items: + - iron_shovel:1 + - iron_pickaxe:1 + currency: 40 + xp: 40 + nethermining: + name: '&7Nether Mining' + description: Mine from your Nether island. + type: onPlayer + displayItem: netherrack + lockedDisplayItem: gray_stained_glass_pane + offset: -1 + requiredItems: + - netherrack:64;+2 + - soul_sand:16;+2 + - gravel:16;+2 + - quartz:32;+2 + - glowstone:16;+2 + - coarse_dirt:4;+2 + reward: + text: 1 ghast tear, 1 magic bow + items: + - ghast_tear:1 + - 'bow[enchantments={levels:{infinity:1,unbreaking:3}}]:1' + currency: 40 + xp: 40 + repeatReward: + text: a blaze-rod and a chance of ghast tear + items: + - blaze_rod:1 + - '{p=0.10}ghast_tear:1' + currency: 20 + xp: 20 + Tier3: + name: '&eExpert' + displayItem: yellow_terracotta + resetInHours: 20 + requires: + challenges: + - adeptbuilder + challenges: + toolmaker: + name: '&3Tool Maker' + description: Make all stone tools + type: onPlayer + requiredChallenges: + - lumberjack + requiredItems: + - stone_shovel:1 + - stone_pickaxe:1 + - stone_axe:1 + - stone_hoe:1 + displayItem: stone_axe + lockedDisplayItem: cyan_stained_glass_pane + reward: + text: 4 redstone ore, 4 iron, 1 pig + items: + - redstone_ore:4 + - iron_ore:4 + - pig_spawn_egg:1 + currency: 30 + xp: 30 + sawmill: + name: '&3Saw Mill' + description: Deliver a collection of processed wood + type: onPlayer + offset: -1 + requiredChallenges: + - toolmaker + requiredItems: + - stripped_oak_log:8;+2 + - stripped_spruce_log:8;+2 + - stripped_birch_log:8;+2 + - stripped_jungle_log:8;+2 + - stripped_acacia_log:8;+2 + - stripped_dark_oak_log:8;+2 + displayItem: iron_axe + lockedDisplayItem: cyan_stained_glass_pane + reward: + text: 4 redstone ore, 4 iron, cocoa and a mule + items: + - cocoa_beans:1 + - redstone_ore:4 + - iron_ore:4 + - mule_spawn_egg:1 + currency: 30 + xp: 30 + repeatReward: + text: 1 redstone ore, 1 iron ore + items: + - redstone_ore:1 + - iron_ore:1 + - '{p=0.05}gold_ore:1' + currency: 15 + xp: 15 + torchmaker: + name: '&3Torch Maker' + description: Make 128 torches. + type: onPlayer + requiredChallenges: + - lumberjack + requiredItems: + - torch:128;+32 + displayItem: torch + lockedDisplayItem: cyan_stained_glass_pane + reward: + text: 4 redstone ore, 4 iron ore + items: + - redstone_ore:4 + - iron_ore:4 + currency: 30 + xp: 30 + repeatReward: + text: 1 redstone ore, 1 iron ore + items: + - redstone_ore:1 + - iron_ore:1 + currency: 15 + xp: 15 + expertfarmer: + name: '&6Expert Farmer' + description: Harvest many different farming resources. + type: onPlayer + displayItem: iron_hoe + lockedDisplayItem: brown_stained_glass_pane + requiredChallenges: + - applecollector + - melonfarmer + - pumpkinfarmer + - wheatfarmer + - carrotfarmer + - potatofarmer + - cactusfarmer + - sugarfarmer + - shroompicker + requiredItems: + - melon_slice:256;+2 + - sugar_cane:128;+2 + - wheat:128;+2 + - potato:128;+2 + - carrot:128;+2 + - pumpkin:128;+2 + - cactus:128;+2 + - beetroot:128;+2 + reward: + text: a bucket, cocoa and a cow + items: + - bucket:1 + - cocoa_beans:1 + - cow_spawn_egg:1 + currency: 50 + xp: 50 + repeatReward: + text: a cow + items: + - cow_spawn_egg:1 + currency: 25 + xp: 25 + fisherman: + name: '&5Fisherman' + description: Catch different types of fish. + type: onPlayer + requiredItems: + - cod:5;+1 + - salmon:5;+1 + - pufferfish:3;+1 + - tropical_fish:1;+0 + - ink_sac:5;+2 + displayItem: cod + lockedDisplayItem: purple_stained_glass_pane + reward: + text: 10 lapis blocks, 20 prismarine, kelp, deep-ocean + items: + - lapis_block:10 + - prismarine:20 + - kelp:1 + - '{p=0.20}prismarine_crystals:5' + permission: usb.biome.deep_ocean + currency: 50 + xp: 50 + repeatReward: + text: 2 lapis, 5 prismarine, kelp and a chance at prismarine crystals + items: + - lapis_lazuli:2 + - prismarine:5 + - kelp:1 + - '{p=0.20}prismarine_crystals:5' + currency: 25 + xp: 25 + woolcollector: + name: '&5Wool Collector' + description: Collect every color of wool. + type: onPlayer + displayItem: white_wool + lockedDisplayItem: purple_stained_glass_pane + requiredChallenges: + - monsterfarmer + requiredItems: + - white_wool:2;+4 + - orange_wool:2;+4 + - magenta_wool:2;+4 + - light_blue_wool:2;+4 + - yellow_wool:2;+4 + - lime_wool:2;+4 + - pink_wool:2;+4 + - gray_wool:2;+4 + - light_gray_wool:2;+4 + - cyan_wool:2;+4 + - purple_wool:2;+4 + - blue_wool:2;+4 + - brown_wool:2;+4 + - green_wool:2;+4 + - red_wool:2;+4 + - black_wool:2;+4 + reward: + text: 2 diamonds, sheep, emerald, flowers + items: + - diamond:2 + - emerald:1 + - sheep_spawn_egg:1 + - rose_bush:1 + - peony:1 + permission: usb.biome.flower_forest + currency: 70 + xp: 70 + repeatReward: + text: emerald, sheep, flowers + items: + - emerald:1 + - sheep_spawn_egg:1 + - rose_bush:1 + - peony:1 + currency: 35 + xp: 35 + maestro: + name: '&5Maestro' + description: Make a jukebox and collect all music discs. + type: onPlayer + requiredItems: + - music_disc_13:1 + - music_disc_cat:1 + - music_disc_blocks:1 + - music_disc_chirp:1 + - music_disc_far:1 + - music_disc_mall:1 + - music_disc_mellohi:1 + - music_disc_stal:1 + - music_disc_strad:1 + - music_disc_ward:1 + - music_disc_11:1 + - music_disc_wait:1 + - jukebox:1 + displayItem: jukebox + lockedDisplayItem: purple_stained_glass_pane + reward: + text: 3 diamonds, 1 gold block, 10 emeralds + items: + - diamond:3 + - emerald:10 + - gold_block:1 + currency: 70 + xp: 70 + repeatReward: + text: 2 gold ore, 1 diamond + items: + - gold_ore:2 + - diamond:1 + - '{p=0.2}diamond:1' + currency: 35 + xp: 35 + ironfarm: + name: '&9Iron Farm' + description: Build an iron-farm. + type: onIsland + displayItem: iron_block + lockedDisplayItem: blue_stained_glass_pane + radius: 50 + requiredChallenges: + - monsterfarm + - applecollector + requiredBlocks: + - oak_door:30 + requiredEntities: + - Villager:10 + - IRON_GOLEM:1 + reward: + text: 2 gold blocks, 1 diamond block + items: + - gold_block:2 + - diamond_block:1 + currency: 500 + xp: 400 + netherfortress: + name: '&9Nether Fortress' + description: Build a netherfortress. + type: onIsland + radius: 50 + requiredBlocks: + - nether_bricks:512 + - nether_brick_slab:64 + - nether_brick_fence:64 + - nether_brick_stairs:64 + - soul_sand:32 + displayItem: nether_brick_fence + lockedDisplayItem: blue_stained_glass_pane + reward: + text: a wither-skull and a blaze rod and a chance of diamonds + items: + - wither_skeleton_skull:1 + - blaze_rod:1 + - '{p=0.05}diamond:3' + - '{p=0.10}diamond:2' + - '{p=0.15}diamond:1' + currency: 40 + xp: 40 + # =============================================== + Tier4: + name: '&cMaster' + displayItem: orange_terracotta + resetInHours: 20 + requires: + + # disabled + rankLeeway: 99 + challenges: + - expertbuilder + challenges: + glassmaker: + name: '&3Glassmaker' + description: Collect every color of stained glass. + type: onPlayer + displayItem: white_stained_glass + lockedDisplayItem: cyan_stained_glass_pane + requiredItems: + - white_stained_glass:8;+2 + - orange_stained_glass:8;+2 + - magenta_stained_glass:8;+2 + - light_blue_stained_glass:8;+2 + - yellow_stained_glass:8;+2 + - lime_stained_glass:8;+2 + - pink_stained_glass:8;+2 + - gray_stained_glass:8;+2 + - light_gray_stained_glass:8;+2 + - cyan_stained_glass:8;+2 + - purple_stained_glass:8;+2 + - blue_stained_glass:8;+2 + - brown_stained_glass:8;+2 + - green_stained_glass:8;+2 + - red_stained_glass:8;+2 + - black_stained_glass:8;+2 + reward: + text: 2 diamonds, 2 disks, 1 emeralds + items: + - diamond:2 + - music_disc_ward:1 + - music_disc_11:1 + - emerald:1 + - sunflower:1 + - lilac:1 + permission: usb.biome.flower_forest + currency: 70 + xp: 70 + repeatReward: + text: 30% chance on 1 or 2 disks, 1 emeralds + items: + - '{p=0.3}music_disc_ward:1' + - '{p=0.3}music_disc_11:1' + - emerald:1 + - sunflower:1 + - lilac:1 + currency: 35 + xp: 35 + carpenter: + name: '&3Carpenter' + description: Collect all types of wood items. + type: onPlayer + requiredItems: + - jungle_stairs:16;+8 + - spruce_door:1;+1 + - dark_oak_fence_gate:2;+2 + - acacia_fence:30;+15 + - ladder:30;+15 + - oak_trapdoor:4;+2 + - oak_pressure_plate:2;+1 + requiredChallenges: + - toolmaker + - lumberjack:2 + displayItem: crafting_table + lockedDisplayItem: cyan_stained_glass_pane + reward: + text: a parrot and a jukebox with some redstone and iron + items: + - parrot_spawn_egg:1 + - jukebox:1 + - redstone_ore:4 + - iron_ore:4 + currency: 30 + xp: 30 + repeatReward: + text: redstone, iron and a slim chance at a parrot + items: + - redstone_ore:1 + - iron_ore:1 + - '{p=0.25}jukebox:1' + - '{p=0.05}parrot_spawn_egg:1' + currency: 15 + xp: 15 + cookielover: + name: '&eCookie Lover' + description: Make cookies and a bucket of milk. + type: onPlayer + requiredChallenges: + - expertfarmer + requiredItems: + - cookie:128;+4 + - milk_bucket:1 + displayItem: cookie + lockedDisplayItem: yellow_stained_glass_pane + reward: + text: 4 redstone ore, clay a bucket and a diamond + items: + - redstone_ore:4 + - clay:5 + - diamond:1 + - bucket:1 + currency: 80 + xp: 80 + repeatReward: + text: iron ore and some clay + items: + - iron_ore:1 + - clay:5 + - bucket:1 + - '{p=0.4}clay:3' + - '{p=0.3}iron_ore:2' + currency: 40 + xp: 40 + deepseafisherman: + name: '&5Deep Sea Fishing' + description: Farm the deep sea + type: onPlayer + requiredItems: + - cod:10;+5 + - salmon:10;+5 + - pufferfish:5;+3 + - tropical_fish:3;+2 + - prismarine_crystals:16;+8 + - prismarine_shard:16;+8 + - dried_kelp_block:64;+32 + - nautilus_shell:1 + displayItem: heart_of_the_sea + lockedDisplayItem: purple_stained_glass_pane + reward: + text: heart-of-the-sea, nautilus shell and a turtle + items: + - nautilus_shell:1 + - heart_of_the_sea:1 + - turtle_spawn_egg:1 + - '{p=0.1}nautilus_shell:1' + permission: usb.biome.deep_ocean + currency: 50 + xp: 50 + repeatReward: + text: nautilus shell, turtle and a chance of a heart + items: + - nautilus_shell:1 + - turtle_spawn_egg:1 + - '{p=0.05}heart_of_the_sea:1' + - '{p=0.1}nautilus_shell:1' + currency: 25 + xp: 25 + horsingaround: + name: '&6Horsing Around' + description: Get hay bales for the horses. + type: onPlayer + requiredItems: + - hay_block:32;+4 + - lead:8;+2 + - carrot_on_a_stick:1 + - shears:1 + requirecChallenges: + - wheatfarmer + displayItem: hay_block + lockedDisplayItem: brown_stained_glass_pane + reward: + text: 1 horse, 1 iron horse armor, 5% chance on diamond horse armor + items: + - iron_horse_armor:1 + - horse_spawn_egg:1 + - '{p=0.05}diamond_horse_armor:1' + currency: 50 + xp: 50 + repeatReward: + text: 1 redstone ore, 1 emerald + items: + - redstone:1 + - emerald:1 + currency: 25 + xp: 25 + slimefarmer: + name: '&5Slime Farmer' + description: Collect slimeballs from slimes. + type: onPlayer + requiredItems: + - slime_ball:64;+4 + displayItem: slime_ball + lockedDisplayItem: purple_stained_glass_pane + reward: + text: 1 diamond, 1 emeralds, swampland + items: + - diamond:1 + - emerald:1 + permission: usb.biome.swamp + currency: 70 + xp: 70 + repeatReward: + text: 1 redstone ore, 1 emeralds + items: + - redstone_ore:1 + - emerald:1 + currency: 35 + xp: 35 + animalfarm: + name: '&9Animal Farm' + description: Create an animal farm. + type: onIsland + displayItem: oak_fence + radius: 40 + requiredChallenges: + - woolcollector + - potatofarmer + - carrotfarmer + requiredEntities: + - Cow:8 + - Pig:8 + - Chicken:16 + - Sheep:{"Color":"WHITE"} + - Sheep:{"Color":"ORANGE"} + - Sheep:{"Color":"MAGENTA"} + - Sheep:{"Color":"LIGHT_BLUE"} + - Sheep:{"Color":"YELLOW"} + - Sheep:{"Color":"LIME"} + - Sheep:{"Color":"PINK"} + - Sheep:{"Color":"GRAY"} + - Sheep:{"Color":"LIGHT_GRAY"} + - Sheep:{"Color":"CYAN"} + - Sheep:{"Color":"PURPLE"} + - Sheep:{"Color":"BLUE"} + - Sheep:{"Color":"BROWN"} + - Sheep:{"Color":"GREEN"} + - Sheep:{"Color":"RED"} + - Sheep:{"Color":"BLACK"} + reward: + text: 1 horse, 1 iron horse armor, 5% chance horse armor + items: + - iron_horse_armor:1 + - horse_spawn_egg:1 + - '{p=0.05}iron_horse_armor:1' + - '{p=0.05}golden_horse_armor:1' + - '{p=0.05}diamond_horse_armor:1' + currency: 50 + xp: 50 + repeatReward: + text: 1 redstone ore, 1 emerald, 5% chance horse armor + items: + - redstone:1 + - emerald:1 + - '{p=0.05}iron_horse_armor:1' + - '{p=0.05}golden_horse_armor:1' + - '{p=0.05}diamond_horse_armor:1' + currency: 25 + xp: 25 + witherhunter: + name: '&5Wither Hunter' + description: Collect some Wither Skeleton skulls. + type: onPlayer + requiredChallenges: + - netherfortress + requiredItems: + - wither_skeleton_skull:10;+1 + displayItem: wither_skeleton_skull + lockedDisplayItem: purple_stained_glass_pane + offset: -1 + reward: + text: 5 diamonds, 5% chance of nether star + items: + - diamond:5 + - '{p=0.05}nether_star:1' + currency: 400 + xp: 400 + repeatReward: + text: 2 gold ore, 5% chance of nether star + items: + - gold_ore:2 + - '{p=0.05}nether_star:1' + currency: 20 + xp: 20 + pearlcollector: + name: '&5Pearl Collector' + description: Collect enderpearls from endermen. + type: onPlayer + requiredItems: + - ender_pearl:10;+4 + displayItem: ender_pearl + lockedDisplayItem: purple_stained_glass_pane + reward: + text: 5 gold ore, 1 blaze rod, 1 emerald + items: + - gold_ore:5 + - blaze_rod:1 + - emerald:1 + currency: 70 + xp: 70 + repeatReward: + text: 1 gold ore, 1 blaze rod + items: + - gold_ore:1 + - blaze_rod:1 + currency: 35 + xp: 35 + + # =============================================== + Tier5: + name: '&4Sky Lord' + displayItem: red_terracotta + resetInHours: 48 + requires: + challenges: + - masterbuilder + challenges: + technician: + name: '&3Technician' + description: Collect some of every type of redstone equipment. + type: onPlayer + requiredItems: + - redstone:64;+16 + - redstone_torch:32;+4 + - repeater:5;+1 + - comparator:3;+1 + - piston:2;+1 + - sticky_piston:2;+1 + - lever:1;+1 + - stone_button:1;+1 + - stone_pressure_plate:1;+1 + - hopper:1;+1 + - dispenser:1;+1 + - dropper:1;+1 + - daylight_detector:1;+1 + displayItem: redstone + lockedDisplayItem: cyan_stained_glass_pane + reward: + text: some snow, ice and packed ice + items: + - snow_block:4 + - ice:8 + - packed_ice:8 + - redstone_block:64 + currency: 70 + xp: 70 + repeatReward: + text: a stack of redstone blocks and some ice + items: + - redstone_block:64 + - ice:4 + - packed_ice:1 + currency: 35 + xp: 35 + emeraldcollector: + name: '&5Emerald Collector' + description: Collect emeralds. + type: onPlayer + requiredItems: + - emerald:60;+10 + displayItem: emerald + lockedDisplayItem: purple_stained_glass_pane + reward: + text: a full set of diamond armor + items: + - diamond_helmet:1 + - diamond_chestplate:1 + - diamond_leggings:1 + - diamond_boots:1 + currency: 70 + xp: 70 + repeatReward: + text: full diamond armor + items: + - diamond_helmet:1 + - diamond_chestplate:1 + - diamond_leggings:1 + - diamond_boots:1 + currency: 35 + xp: 35 + topchef: + name: '&eTop Chef' + description: Collect every kind of edible food. + type: onPlayer + requiredItems: + - baked_potato:1 + - bread:1 + - cake:1 + - cooked_chicken:1 + - cooked_cod:1 + - cooked_salmon:1 + - tropical_fish:1 + - cooked_porkchop:1 + - cookie:1 + - golden_apple:1 + - golden_carrot:1 + - mushroom_stew:1 + - pumpkin_pie:1 + - cooked_beef:1 + - melon:1 + - carrot:1 + requiredChallenges: + - expertfarmer + - fisherman + - cookielover + displayItem: carrot + lockedDisplayItem: yellow_stained_glass_pane + reward: + text: 2 diamond, 1 mooshroom + items: + - diamond:2 + - mooshroom_spawn_egg:1 + currency: 80 + xp: 80 + repeatReward: + text: 1 diamond, 1 mooshroom + items: + - diamond:1 + - mooshroom_spawn_egg:1 + currency: 40 + xp: 40 + tajmahal: + name: '&9Taj Mahal' + description: Build a temple of quartz + type: onIsland + radius: 30 + displayItem: quartz_block + lockedDisplayItem: blue_stained_glass_pane + requiredChallenges: + - nethermining + requiredBlocks: + - quartz_block:512 + - chiseled_quartz_block:64 + - quartz_pillar:128 + - quartz_stairs:64 + - quartz_slab:192 + reward: + text: 3 end-portal frames, red sand + items: + - end_portal_frame:3 + - red_sand:64 + currency: 1000 + xp: 500 + greatpyramid: + name: '&9Great Pyramid' + description: Build a pyramid of sandstone + type: onIsland + radius: 30 + displayItem: sandstone + lockedDisplayItem: blue_stained_glass_pane + requiredBlocks: + - sandstone:512 + - chiseled_sandstone:64 + - smooth_sandstone:128 + - sandstone_stairs:64 + - sandstone_slab:192 + - red_sandstone:16 + reward: + text: 3 end-portal frames, 3 diamonds, bow + items: + - end_portal_frame:3 + - diamond:3 + - 'bow[enchantments={levels:{infinity:1,power:5,unbreaking:3}}]:1' + currency: 1000 + xp: 500 + poseidonshalls: + name: '&9Poseidon''s Halls' + description: Build the halls of Poseidon + type: onIsland + radius: 50 + displayItem: prismarine + lockedDisplayItem: blue_stained_glass_pane + requiredChallenges: + - deepseafisherman + requiredBlocks: + - prismarine:512 + - prismarine_bricks:128 + - dark_prismarine:64 + - sea_lantern:32 + - conduit:1 + - flower_pot:16 + - vine:128 + - poppy:10 + - blue_orchid:10 + - allium:10 + - azure_bluet:10 + - red_tulip:10 + - orange_tulip:10 + - white_tulip:10 + - pink_tulip:10 + - oxeye_daisy:10 + - sunflower:10 + - lilac:10 + - rose_bush:10 + - peony:10 + reward: + text: 3 end-portal frames, 5 diamonds + items: + - end_portal_frame:3 + - diamond:5 + currency: 1000 + xp: 500 + beaconator: + name: '&9Beaconator' + description: Build a very expensive beacon. + type: onIsland + radius: 15 + displayItem: beacon + lockedDisplayItem: blue_stained_glass_pane + requiredChallenges: + - netherfortress + - ironfarm + - maestro + - witherhunter + requiredBlocks: + - beacon:1 + - diamond_block:1 + - emerald_block:8 + - gold_block:25 + - iron_block:49 + reward: + items: + - end_portal_frame:3 + - 'diamond_sword[enchantments={levels:{fire_aspect:1,sharpness:5,unbreaking:3}}]:1' + text: some end-portal-pieces and a magic diamond sword + currency: 4000 + xp: 1000 + endportal: + name: '&9End Portal' + description: Build and activate an end-portal. + type: onIsland + radius: 15 + displayItem: end_portal_frame + lockedDisplayItem: blue_stained_glass_pane + requiredChallenges: + - beaconator + - tajmahal + - poseidonshalls + - greatpyramid + requiredBlocks: + - end_portal_frame:12 + - end_portal:9 + reward: + text: 5 diamonds and some nice boots + items: + - diamond:5 + - 'diamond_boots[enchantments={levels:{blast_protection:4,feather_falling:4,protection:4}}]:1' + currency: 4000 + xp: 1000 + + # =============================================== + Tier6: + name: '&aWorld Foods' + displayItem: light_gray_terracotta + resetInHours: 20 + requires: + challenges: + - masterbuilder + - topchef + challenges: + fishandchips: + name: '&eFish & Chips' + description: Create the famous English Fish & Chips. + type: onPlayer + requiredItems: + - cooked_cod:32;+8 + - baked_potato:32;+8 + displayItem: cooked_cod + lockedDisplayItem: yellow_stained_glass_pane + reward: + text: 16 baked potatos, 1 disk, 4 emerald + items: + - baked_potato:16 + - music_disc_13:1 + - emerald:4 + currency: 60 + xp: 60 + commands: + - 'console: msg {player} &9https://en.wikipedia.org/wiki/Fish_and_chips + &2Click the link for more info' + repeatReward: + text: 30% chance on 1 disk, 1 emerald + items: + - emerald:1 + - '{p=0.3}music_disc_13:1' + currency: 30 + xp: 30 + commands: + - 'console: msg {player} &9https://en.wikipedia.org/wiki/Fish_and_chips + &2Click the link for more info' + smorrebrod: + name: '&eSmørrebrød' + description: Create the famous Danish smørrebrød. + type: onPlayer + requiredItems: + - bread:16;+4 + - cooked_salmon:8;+4 + - tropical_fish:2;+1 + - cooked_porkchop:8;+4 + - cooked_beef:8;+4 + displayItem: cooked_cod + lockedDisplayItem: yellow_stained_glass_pane + reward: + text: 1 rod, 2 name tags, 2 ocelots + items: + - fishing_rod:1 + - name_tag:2 + - ocelot_spawn_egg:2 + currency: 60 + xp: 60 + commands: + - 'console: msg {player} &9https://en.wikipedia.org/wiki/Danish_cuisine + &2Click the link for more info' + repeatReward: + text: 1 emerald + items: + - emerald:1 + currency: 30 + xp: 30 + commands: + - 'console: msg {player} &9https://en.wikipedia.org/wiki/Danish_cuisine + &2Click the link for more info' + hutspot: + name: '&eHutspot' + description: Create the famous Dutch hutspot. + type: onPlayer + requiredItems: + - potato:64;+8 + - carrot:64;+8 + lockedDisplayItem: yellow_stained_glass_pane + displayItem: golden_carrot + reward: + text: 1 golden carrot, 1 disk, 4 emerald + items: + - golden_carrot:1 + - music_disc_cat:1 + - emerald:4 + currency: 60 + xp: 60 + commands: + - 'console: msg {player} &9https://en.wikipedia.org/wiki/Hutspot &2Click + the link for more info' + repeatReward: + text: 30% chance on 1 disk, 1 emerald + items: + - emerald:1 + - '{p=0.3}music_disc_cat:1' + currency: 30 + xp: 30 + commands: + - 'console: msg {player} &9https://en.wikipedia.org/wiki/Hutspot &2Click + the link for more info' + apfelstrudel: + name: '&eApfelstrudele' + description: Create the famous German apfelstrudel. + type: onPlayer + requiredItems: + - apple:8;+5 + - wheat:16;+5 + - sugar:16;+5 + - milk_bucket:1 + displayItem: golden_apple + lockedDisplayItem: yellow_stained_glass_pane + reward: + text: 1 bucket, 1 disk, 4 emerald + items: + - bucket:1 + - emerald:4 + - music_disc_blocks:1 + currency: 60 + xp: 60 + commands: + - 'console: msg {player} &9https://en.wikipedia.org/wiki/Apple_strudel &2Click + the link for more info' + repeatReward: + text: 1 bucket, 30% chance on 1 disk, 1 emerald + items: + - bucket:1 + - emerald:1 + - '{p=0.3}music_disc_blocks:1' + currency: 30 + xp: 30 + commands: + - 'console: msg {player} &9https://en.wikipedia.org/wiki/Apple_strudel &2Click + the link for more info' + brownies: + name: '&eBrownies' + description: Create the famous American brownies. + type: onPlayer + requiredItems: + - wheat:16;+5 + - sugar:16;+5 + - egg:16;+5 + - ink_sac:16;+5 + - milk_bucket:1 + displayItem: dark_oak_slab + lockedDisplayItem: yellow_stained_glass_pane + reward: + text: 1 bucket, 1 disk, 4 emerald + items: + - bucket:1 + - emerald:4 + - music_disc_far:1 + currency: 60 + xp: 60 + commands: + - 'console: msg {player} &9https://en.wikipedia.org/wiki/Chocolate_brownie + &2Click the link for more info' + repeatReward: + text: 1 bucket, 30% chance on 1 disk, 1 emerald + items: + - bucket:1 + - emerald:1 + - '{p=0.3}music_disc_far:1' + currency: 30 + xp: 30 + commands: + - 'console: msg {player} &9https://en.wikipedia.org/wiki/Chocolate_brownie + &2Click the link for more info' + pastafunghi: + name: '&ePasta Funghi' + description: Create the famous Italian Pasta Funghi. + type: onPlayer + requiredItems: + - wheat:32;+8 + - egg:32;+8 + - brown_mushroom:16;+4 + - red_mushroom:16;+4 + - milk_bucket:1 + displayItem: red_mushroom_block + lockedDisplayItem: yellow_stained_glass_pane + reward: + text: 2 rabbits, 1 bucket + items: + - bucket:1 + - rabbit_spawn_egg:2 + currency: 60 + xp: 60 + commands: + - console:msg {player} &9https://en.wikipedia.org/wiki/Pasta &2Click the + link for more info + repeatReward: + text: 30% chance on 1 disk, 1 emerald + items: + - emerald:1 + - '{p=0.3}music_disc_13:1' + currency: 30 + xp: 30 + commands: + - console:msg {player} &9https://en.wikipedia.org/wiki/Pasta &2Click the + link for more info + chocolate: + name: '&eBelgian chocolate' + description: Create the famous belgian chocolate. + type: onPlayer + requiredItems: + - sugar:16;+5 + - ink_sac:16;+5 + - milk_bucket:1 + displayItem: pig_spawn_egg + lockedDisplayItem: yellow_stained_glass_pane + reward: + text: 1 bucket, 1 disk, 4 emeralds + items: + - bucket:1 + - emerald:4 + - music_disc_mall:1 + currency: 60 + xp: 60 + commands: + - 'console: msg {player} &9https://en.wikipedia.org/wiki/Belgian_chocolate + &2Click the link for more info' + repeatReward: + text: 1 bucket, 30% chance on 1 disk, 1 emerald + items: + - bucket:1 + - emerald:1 + - '{p=0.3}music_disc_mall:1' + currency: 30 + xp: 30 + commands: + - 'console: msg {player} &9https://en.wikipedia.org/wiki/Belgian_chocolate + &2Click the link for more info' + baker: + name: '&ePâtisserie' + description: Bake cakes, pumpkin pies, and cookies. + type: onPlayer + requiredItems: + - cake:5;+1 + - pumpkin_pie:5;+1 + - cookie:128;+4 + displayItem: cake + lockedDisplayItem: yellow_stained_glass_pane + reward: + text: 5 gold ore, 1 diamond + items: + - gold_ore:5 + - diamond:1 + currency: 80 + xp: 80 + repeatReward: + text: 2 iron ore, 1 gold ore + items: + - iron_ore:2 + - gold_ore:1 + currency: 40 + xp: 40 + +# DO NOT CHANGE THE VERSION! You will break the conversion and unexpected things will happen! +version: 108 diff --git a/uSkyBlock-Core/src/test/resources/us/talabrek/ultimateskyblock/imports/old-block-challenges.yml b/uSkyBlock-Core/src/test/resources/us/talabrek/ultimateskyblock/imports/old-block-challenges.yml new file mode 100644 index 000000000..e97fca755 --- /dev/null +++ b/uSkyBlock-Core/src/test/resources/us/talabrek/ultimateskyblock/imports/old-block-challenges.yml @@ -0,0 +1,1790 @@ +# ====================================================================================================================== +# +# Here follows the format and explanation for the challenge group and challenges setup. The single commented (#) are the +# most used settings while the double commented (##) are optional if you like to use those settings. +# +# Item type format +# Item types are defined as in the minecraft /give command, i.e. with their minecraft key and possible components in +# square brackets. For example, 'minecraft:diamond_sword[damage=42]'. Refer to the Minecraft wiki for more information: +# https://minecraft.wiki/w/Data_component_format. You can also use the command '/usb iteminfo' to get the information. +# +# This item type is supplemented with additional information depending on where it is used: +# +# display-item: +# An item to be displayed in a GUI. It only defines the item type as above, without any additional information. +# For example: 'cobblestone', 'minecraft:stone', 'diamond_sword[damage=42]'. +# +# item-requirement: :[;+] +# An item requirement for a challenge. The amount is the number of items required. The optional + is the number +# of items to add to the required amount for each repeat of the challenge. For example, 'cobblestone:64;+16' would +# require 64 cobblestone for the first completion and 80 cobblestone for the second completion. Other options are +# -, *, and / for subtraction, multiplication, and division, respectively. For example, 'cobblestone:64;*2' would +# require 64 cobblestone for the first completion, 128 cobblestone for the second completion, and 256 cobblestone for +# the third completion. +# +# item-reward: [{p=}]: +# An item reward for a challenge. The amount is the number of items to give. The optional {p=} is the +# probability of the item being given. For example, 'cobblestone:64' would give 64 cobblestone every time the challenge +# is completed, while '{p=0.1}cobblestone:64' would give 64 cobblestone 10% of the time. +# +# ====================================================================================================================== +# All challenges are defined in the ranks section. Each rank is a tier of challenges that players can complete. +# +# ranks: +# # [text] name of the challenge Rank. +# TierX: +# # [text] The name of the challenge rank that shows when you do /challenges (supports capitals and color codes). +# name: '&aCustom Challenges rank name' +# # [display-item] The item to be displayed in the challenge menu for completed challenges. +# displayItem: 'cyan_terracotta' +# # [integer] The time in hours before required items reset to default (this overwrites the main reset time) +# resetInHours: 20 +# # These requirements controls when a challenge group will be available to a player. +# requires: +# # [integer] The number of tasks per rank that can be left uncompleted to advance to the next rank. For example, +# if you have 4 challenges with a rankLeeway of 1, a player would only need to complete 3 to advance to +# the next rank. A rankLeeway of 0 would require them all. +# rankLeeway: 8 +# # [List[text]] Challenges that have to be completed before this group will available to a player. +# challenges: +# - a challenge name +# ====================================================================================================================== +# challenges: +# # [text] The name of the challenge. All challenge names should be lower-case. +# defaultchallenge: +# # [text] The name of the challenge that shows in /challenges (this supports capitals and color codes). +# name: '&a Default Challenge' +# # [text] The descriptions players see when they do /challenges +# description: +# # [onIsland/onPlayer/islandLevel] This defines whether the required blocks/items should be in the player's +# # inventory or on their island. When using onIsland, the player must within 10 blocks from the required blocks +# # on his island. When using islandLevel, the 'requiredItems' field should be the island level required. The +# # player must use /island level first to update their level. +# type: onPlayer +# ## type: islandLevel +# ## type: onIsland +# # [integer] Overrides the default radius of 10 blocks when using onIsland. +# ## radius: 20 +# # [List[item-requirement] The items required to complete the challenge. +# requiredItems: +# - stone:64;+16 +# - cobblestone:64;+16 +# # [true/false] If the challenge can be repeated or not. +# ## repeatable: true +# # [integer] The maximum number of times the challenge can be completed. Overrides the default repeatLimit. +# # A value of 0 means unlimited repeats. +# ## repeatLimit: 5 +# # [display-item] The item to be displayed in the challenge menu for completed challenges. +# displayItem: cobblestone +# # [integer] The time in hours before required items reset to default (overwrites the main and rank defaults). +# ## resetInHours: 4 +# # [true/false] Take required items on completing a challenge. +# ## takeItems: true +# # The rewards players get for completing the challenge +# reward: +# # [text] Description of the reward. +# text: 'Mossy cobblestone and an iron pickaxe with unbreaking 1' +# # [List[item-requirement]] A list of items given to the player for completing the challenge. +# items: +# - mossy_cobblestone:16 +# - iron_pickaxe[enchantments={levels:{unbreaking:1}}]:1 +# # [permission node] A permission granted for completion. Multiple permissions are space-separated. +# ## permission: 'test.permission' +# # [integer] How much currency to give for completion. (requires an economy plugin) +# ## currency: 0 +# # [integer] How much xp to give to the player for completion. +# ## xp: 0 +# # [List[Text]] Executes the given command upon completion. Prepend with "op" or "console" to run the commands +# as OP or from the Console. Examples: +# # Possible command arguments are: +# # {player} - The name of the player +# # {playerName} - The display name of the player +# # {challenge} - The name of the challenge +# # {position} - The position of the player +# # {party} - Execute the command once for each member of the party (substituting the name) +# ## commands: +# ## - 'op: me are the GOD of things' +# ## - 'console: give {party} aweseomestuff 32' +# # reward section to reward the player for completing a repeated challenge (any time after the first). The +# # structure is identical to the 'reward' section. +# repeatReward: +# text: 'Mossy cobblestone' +# items: +# - mossy_cobblestone:16 +# +# ====================================================================================================================== + +# [true/false] Enable the use of the challenges command. +allowChallenges: true + +# [island/player] Whether challenges are tracked per player, or per island +challengeSharing: island + +# [true/false] If true, first time challenge completions are broadcast to the whole server. +broadcastCompletion: true + +# [text] The color/formatting of the broadcast text when showing first time completions. +broadcastText: '&6' + +# [true/false] If true, challenges in higher level ranks require challenges in lower level ranks to be completed. +requirePreviousRank: true + +# [integer] The number of tasks per rank that can be left uncompleted to advance to the next rank. For example, if you have 4 easy challenges +# with a rankLeeway of 1, a player would only need to complete 3 to advance to the next rank. +# A rankLeeway of 0 would require them all. +rankLeeway: 12 + +# [integer] The time in hours before required items reset to default. (only if not specified in the challenges below) +defaultResetInHours: 20 + +# [integer] The default radius in blocks when using onIsland. Can be overridden in the challenges below. +radius: 10 + +# [integer] The maximum number of times a challenge can be completed by default. 0 means unlimited. Can be overridden in the challenge definition below. +repeatLimit: 0 + +# [color code] The color to use for uncompleted challenges in the list. +challengeColor: '&e' + +# [color code] The color to use for completed challenges in the list. (non-repeatable) +finishedColor: '&2' + +# [color code] The color to use for completed challenges in the list. (repeatable) +repeatableColor: '&a' + +# [true/false] If true, enables vault to handle currency rewards. +enableEconomyPlugin: true + +# [true/false] If false, challenges are not reset on island creation (or restart) +resetChallengesOnCreate: true + +# Material to show for locked challenges (i.e. STAINED_GLASS_PANE:14 for a red glass-pane, or 160:14) +lockedDisplayItem: red_stained_glass_pane + +# Material to show for onIsland challenges when locked +ISLAND: + lockedDisplayItem: blue_stained_glass_pane + +# Material to show for islandLevel challenges when locked +ISLAND_LEVEL: + lockedDisplayItem: black_stained_glass_pane + +# Whether to show the name of locked challenges +showLockedChallengeName: true + +# When creating your own challenges you will have +# to uncomment the section below or the default challenges +# will be re-added on every server restart. When altering +# the default challenges this should be fine to leave. +# merge-ignore: +# - 'ranks' +# +# =============================================== +# An explanation to setup your own challenges +# can be found at the bottom of this file. +# =============================================== +ranks: + Tier1: + name: '&7Novice' + displayItem: cyan_terracotta + resetInHours: 20 + challenges: + cobblestonegenerator: + name: '&7Cobble Stone Generator' + description: Mine from a cobblestone generator. + type: onPlayer + requiredItems: + - cobblestone:64;+2 + displayItem: cobblestone + lockedDisplayItem: gray_stained_glass_pane + resetInHours: 12 + reward: + text: 3 leather, 20% chance to get a book + items: + - leather:3 + - '{p=0.2}book:1' + currency: 10 + xp: 10 + commands: + - op:effect give {player} regeneration + repeatReward: + text: 1 leather, 10% chance to get a book + items: + - leather:1 + - '{p=0.1}book:1' + currency: 5 + xp: 5 + applecollector: + name: '&6Apple Collector' + description: Collect apples from trees. + type: onPlayer + requiredItems: + - apple:2;+1 + displayItem: apple + lockedDisplayItem: brown_stained_glass_pane + resetInHours: 6 + reward: + text: 1 of each sapling, (4 dark oak) + items: + - oak_sapling:1 + - spruce_sapling:1 + - birch_sapling:1 + - jungle_sapling:1 + - acacia_sapling:1 + - dark_oak_sapling:4 + - '{p=0.2}jungle_sapling:3' + currency: 20 + xp: 20 + repeatReward: + text: 1 of each sapling (4 dark oak) + items: + - oak_sapling:1 + - spruce_sapling:1 + - birch_sapling:1 + - jungle_sapling:1 + - acacia_sapling:1 + - dark_oak_sapling:4 + - '{p=0.1}jungle_sapling:3' + currency: 10 + xp: 10 + sugarplanter: + name: '&9Sugar Planter' + type: onIsland + displayItem: sugar_cane + lockedDisplayItem: brown_stained_glass_pane + requiredItems: + - sugar_cane:4 + reward: + text: 4 dirt + items: + - dirt:4 + currency: 20 + xp: 20 + sugarfarmer: + name: '&6Sugar Farmer' + description: Harvest sugarcane from a farm. + type: onPlayer + requiredItems: + - sugar_cane:64;+16 + offset: -1 + displayItem: sugar_cane + lockedDisplayItem: brown_stained_glass_pane + reward: + text: 8 dirt + items: + - dirt:8 + - '{p=0.1}bone:1' + currency: 20 + xp: 20 + repeatReward: + text: 4 dirt + items: + - dirt:4 + - '{p=0.05}dirt:4' + currency: 10 + xp: 10 + melonfarmer: + name: '&6Melon Farmer' + description: Harvest slices of melon from a farm. + type: onPlayer + requiredItems: + - melon_slice:128;+8 + displayItem: melon + lockedDisplayItem: brown_stained_glass_pane + reward: + text: 8 dirt + items: + - dirt:8 + currency: 20 + xp: 20 + repeatReward: + text: 4 dirt + items: + - dirt:4 + currency: 10 + xp: 10 + cactusfarmer: + name: '&6Cactus Farmer' + description: Harvest cacti from a farm. + type: onPlayer + requiredItems: + - cactus:64;+16 + displayItem: cactus + lockedDisplayItem: brown_stained_glass_pane + reward: + text: 8 sand, 20% chance to get a bone + items: + - sand:8 + - '{p=0.2}bone:1' + - '{p=0.1}wooden_hoe:1' + currency: 20 + xp: 20 + repeatReward: + text: 4 sand + items: + - sand:4 + - '{p=0.1}bone:1' + currency: 10 + xp: 10 + pumpkinfarmer: + name: '&6Pumpkin Farmer' + description: Harvest pumpkins from a farm. + type: onPlayer + requiredItems: + - pumpkin:64;+4 + displayItem: pumpkin + lockedDisplayItem: brown_stained_glass_pane + reward: + text: 8 dirt + items: + - dirt:8 + - '{p=0.3}jack_o_lantern:1' + currency: 20 + xp: 20 + repeatReward: + text: 4 dirt + items: + - dirt:4 + - '{p=0.05}jack_o_lantern:1' + currency: 10 + xp: 10 + stonebrickmaker: + name: '&aStone Brick Maker' + description: Make 64 Stone Bricks. + type: onPlayer + requiredItems: + - stone_bricks:64;+8 + - stone_brick_slab:30;+6 + - chiseled_stone_bricks:30;+6 + - stone_brick_stairs:16;+4 + displayItem: stone_bricks + lockedDisplayItem: gray_stained_glass_pane + reward: + text: 4 redstone ore, 4 iron ore, 1 chicken + items: + - redstone_ore:4 + - iron_ore:4 + - chicken_spawn_egg:1 + currency: 30 + xp: 30 + repeatReward: + text: 1 redstone ore, 1 iron ore + items: + - redstone_ore:1 + - iron_ore:1 + currency: 15 + xp: 15 + novicebuilder: + name: '&7Novice Builder' + description: Reach island level 20. + type: islandLevel + requiredLevel: 20 + displayItem: coal_ore + reward: + text: 8 dirt, 8 sand, 5 emeralds + items: + - dirt:8 + - sand:8 + - emerald:5 + - diamond:1 + currency: 20 + xp: 20 + commands: + - op:effect give {party} regeneration + adeptbuilder: + name: '&aAdept Builder' + description: Reach island level 50. + type: islandLevel + requiredLevel: 50 + displayItem: iron_ore + offset: -1 + reward: + text: 10 obsidian, 2 diamonds, 5 emeralds + items: + - obsidian:10 + - emerald:5 + - diamond:2 + currency: 100 + xp: 100 + expertbuilder: + name: '&eExpert Builder' + description: Reach island level 100. + type: islandLevel + requiredLevel: 100 + displayItem: gold_ore + offset: -1 + reward: + text: 16 dirt, 16 sand, 3 diamonds, 5 emeralds + items: + - dirt:16 + - sand:16 + - emerald:5 + - diamond:3 + currency: 250 + xp: 250 + masterbuilder: + name: '&cMaster Builder' + description: Reach island level 250. + type: islandLevel + requiredLevel: 250 + displayItem: diamond_ore + offset: -1 + reward: + text: 32 dirt, 32 sand, 4 diamonds, 5 emeralds + items: + - dirt:32 + - sand:32 + - emerald:5 + - diamond:4 + currency: 500 + xp: 500 + skylord: + name: '&4Sky Lord' + description: Reach island level 500. + type: islandLevel + requiredLevel: 500 + displayItem: emerald_ore + offset: -1 + reward: + text: 64 dirt, 64 sand, 5 diamond, 5 emeralds + items: + - dirt:64 + - sand:64 + - diamond:5 + - emerald:5 + currency: 1000 + xp: 1000 + Tier2: + name: '&aAdept' + displayItem: lime_terracotta + resetInHours: 20 + requires: + + # means disabled in effect + rankLeeway: 99 + challenges: + - cobblestonegenerator + - novicebuilder + challenges: + lumberjack: + name: '&3Lumberjack' + description: Collect all types of wood logs. + type: onPlayer + requiredItems: + - oak_log:16;+2 + - spruce_log:16;+2 + - birch_log:16;+2 + - jungle_log:16;+2 + - acacia_log:16;+2 + - dark_oak_log:16;+2 + displayItem: oak_log + lockedDisplayItem: cyan_stained_glass_pane + reward: + text: 4 redstone ore, 4 iron ore, 1 wolf + items: + - redstone_ore:4 + - iron_ore:4 + - wolf_spawn_egg:1 + currency: 30 + xp: 30 + repeatReward: + text: 1 redstone ore, 1 iron ore + items: + - redstone_ore:1 + - iron_ore:1 + currency: 15 + xp: 15 + shroompicker: + name: '&6Shroom Picker' + description: Collect red and brown mushrooms. + type: onPlayer + requiredItems: + - brown_mushroom:64;+4 + - red_mushroom:64;+4 + displayItem: red_mushroom + lockedDisplayItem: brown_stained_glass_pane + reward: + text: 8 mycelium, 4 podzol + items: + - mycelium:8 + - podzol:4 + currency: 30 + xp: 30 + repeatReward: + text: 4 mycelium + items: + - mycelium:4 + - '{p=0.02}mycelium:4' + - '{p=0.02}podzol:2' + currency: 15 + xp: 15 + potatofarmer: + name: '&6Potato Farmer' + description: Harvest potato's from a farm. + type: onPlayer + requiredItems: + - potato:64;+16 + displayItem: potato + lockedDisplayItem: brown_stained_glass_pane + reward: + text: 1 carrot, 4 dirt + items: + - carrot:1 + - dirt:4 + - '{p=0.10}beetroot_seeds:1' + - '{p=0.05}red_sand:1' + - '{p=0.01}diamond:1' + currency: 50 + xp: 50 + repeatReward: + text: 4 dirt and a baked potato + items: + - dirt:4 + - baked_potato:1 + - '{p=0.10}beetroot_seeds:1' + - '{p=0.05}red_sand:1' + - '{p=0.01}diamond:1' + currency: 25 + xp: 25 + carrotfarmer: + name: '&6Carrot Farmer' + description: Harvest carrot's from a farm. + type: onPlayer + requiredItems: + - carrot:64;+16 + displayItem: carrot + lockedDisplayItem: brown_stained_glass_pane + reward: + text: a pig with saddle and a potato + items: + - saddle:1 + - potato:1 + - pig_spawn_egg:1 + - '{p=0.05}red_sand:1' + - '{p=0.01}diamond:1' + currency: 50 + xp: 50 + repeatReward: + text: 1 golden carrot + items: + - golden_carrot:1 + - '{p=0.05}sand:1' + - '{p=0.01}diamond:1' + currency: 25 + xp: 25 + wheatfarmer: + name: '&6Wheat Farmer' + description: Harvest wheat from a farm. + type: onPlayer + requiredItems: + - wheat:64;+16 + displayItem: wheat + lockedDisplayItem: brown_stained_glass_pane + reward: + text: 8 dirt + items: + - dirt:8 + currency: 20 + xp: 20 + repeatReward: + text: 4 dirt + items: + - dirt:4 + - '{p=0.2}bone:1' + currency: 10 + xp: 10 + monsterfarm: + name: '&5Monster Farm' + description: Build a mob farm and collect mob loot. + type: onPlayer + requiredItems: + - rotten_flesh:64;+4 + - string:32;+2 + - arrow:32;+2 + - bone:32;+2 + - gunpowder:16;+1 + - spider_eye:5 + displayItem: rotten_flesh + lockedDisplayItem: purple_stained_glass_pane + reward: + text: 4 redstone ore, 4 iron ore and 1 flint + items: + - redstone_ore:4 + - iron_ore:4 + - flint:1 + - '{p=0.10}potato:1' + - '{p=0.10}carrot:1' + currency: 30 + xp: 30 + repeatReward: + text: 1 redstone ore, 1 iron ore, 1 flint + items: + - redstone_ore:1 + - iron_ore:4 + - flint:1 + - '{p=0.05}potato:1' + - '{p=0.05}carrot:1' + currency: 15 + xp: 15 + homeowner: + name: '&9Home Owner' + description: Build a house with furnishings. + type: onIsland + requiredChallenges: + - stonebrickmaker + requiredItems: + - red_bed:1 + - crafting_table:1 + - glass:1 + - oak_door:1 + - furnace:1 + - bookshelf:1 + - torch:1 + lockedDisplayItem: blue_stained_glass_pane + displayItem: oak_door + reward: + text: 4 redstone ore, 5 inksac, 4 iron ore and some seeds + items: + - redstone_ore:4 + - iron_ore:4 + - ink_sac:5 + - beetroot_seeds:1 + currency: 40 + xp: 40 + netherportal: + name: '&9Nether Portal' + description: Build a nether portal on your island. + type: onIsland + requiredChallenges: + - adeptbuilder + - homeowner + requiredItems: + - obsidian:10 + - nether_portal:1 + displayItem: obsidian + lockedDisplayItem: blue_stained_glass_pane + reward: + text: 1 iron pickaxe, 1 iron shovel + items: + - iron_shovel:1 + - iron_pickaxe:1 + currency: 40 + xp: 40 + nethermining: + name: '&7Nether Mining' + description: Mine from your Nether island. + type: onPlayer + displayItem: netherrack + lockedDisplayItem: gray_stained_glass_pane + offset: -1 + requiredItems: + - netherrack:64;+2 + - soul_sand:16;+2 + - gravel:16;+2 + - quartz:32;+2 + - glowstone:16;+2 + - coarse_dirt:4;+2 + reward: + text: 1 ghast tear, 1 magic bow + items: + - ghast_tear:1 + - 'bow[enchantments={levels:{infinity:1,unbreaking:3}}]:1' + currency: 40 + xp: 40 + repeatReward: + text: a blaze-rod and a chance of ghast tear + items: + - blaze_rod:1 + - '{p=0.10}ghast_tear:1' + currency: 20 + xp: 20 + Tier3: + name: '&eExpert' + displayItem: yellow_terracotta + resetInHours: 20 + requires: + challenges: + - adeptbuilder + challenges: + toolmaker: + name: '&3Tool Maker' + description: Make all stone tools + type: onPlayer + requiredChallenges: + - lumberjack + requiredItems: + - stone_shovel:1 + - stone_pickaxe:1 + - stone_axe:1 + - stone_hoe:1 + displayItem: stone_axe + lockedDisplayItem: cyan_stained_glass_pane + reward: + text: 4 redstone ore, 4 iron, 1 pig + items: + - redstone_ore:4 + - iron_ore:4 + - pig_spawn_egg:1 + currency: 30 + xp: 30 + sawmill: + name: '&3Saw Mill' + description: Deliver a collection of processed wood + type: onPlayer + offset: -1 + requiredChallenges: + - toolmaker + requiredItems: + - stripped_oak_log:8;+2 + - stripped_spruce_log:8;+2 + - stripped_birch_log:8;+2 + - stripped_jungle_log:8;+2 + - stripped_acacia_log:8;+2 + - stripped_dark_oak_log:8;+2 + displayItem: iron_axe + lockedDisplayItem: cyan_stained_glass_pane + reward: + text: 4 redstone ore, 4 iron, cocoa and a mule + items: + - cocoa_beans:1 + - redstone_ore:4 + - iron_ore:4 + - mule_spawn_egg:1 + currency: 30 + xp: 30 + repeatReward: + text: 1 redstone ore, 1 iron ore + items: + - redstone_ore:1 + - iron_ore:1 + - '{p=0.05}gold_ore:1' + currency: 15 + xp: 15 + torchmaker: + name: '&3Torch Maker' + description: Make 128 torches. + type: onPlayer + requiredChallenges: + - lumberjack + requiredItems: + - torch:128;+32 + displayItem: torch + lockedDisplayItem: cyan_stained_glass_pane + reward: + text: 4 redstone ore, 4 iron ore + items: + - redstone_ore:4 + - iron_ore:4 + currency: 30 + xp: 30 + repeatReward: + text: 1 redstone ore, 1 iron ore + items: + - redstone_ore:1 + - iron_ore:1 + currency: 15 + xp: 15 + expertfarmer: + name: '&6Expert Farmer' + description: Harvest many different farming resources. + type: onPlayer + displayItem: iron_hoe + lockedDisplayItem: brown_stained_glass_pane + requiredChallenges: + - applecollector + - melonfarmer + - pumpkinfarmer + - wheatfarmer + - carrotfarmer + - potatofarmer + - cactusfarmer + - sugarfarmer + - shroompicker + requiredItems: + - melon_slice:256;+2 + - sugar_cane:128;+2 + - wheat:128;+2 + - potato:128;+2 + - carrot:128;+2 + - pumpkin:128;+2 + - cactus:128;+2 + - beetroot:128;+2 + reward: + text: a bucket, cocoa and a cow + items: + - bucket:1 + - cocoa_beans:1 + - cow_spawn_egg:1 + currency: 50 + xp: 50 + repeatReward: + text: a cow + items: + - cow_spawn_egg:1 + currency: 25 + xp: 25 + fisherman: + name: '&5Fisherman' + description: Catch different types of fish. + type: onPlayer + requiredItems: + - cod:5;+1 + - salmon:5;+1 + - pufferfish:3;+1 + - tropical_fish:1;+0 + - ink_sac:5;+2 + displayItem: cod + lockedDisplayItem: purple_stained_glass_pane + reward: + text: 10 lapis blocks, 20 prismarine, kelp, deep-ocean + items: + - lapis_block:10 + - prismarine:20 + - kelp:1 + - '{p=0.20}prismarine_crystals:5' + permission: usb.biome.deep_ocean + currency: 50 + xp: 50 + repeatReward: + text: 2 lapis, 5 prismarine, kelp and a chance at prismarine crystals + items: + - lapis_lazuli:2 + - prismarine:5 + - kelp:1 + - '{p=0.20}prismarine_crystals:5' + currency: 25 + xp: 25 + woolcollector: + name: '&5Wool Collector' + description: Collect every color of wool. + type: onPlayer + displayItem: white_wool + lockedDisplayItem: purple_stained_glass_pane + requiredChallenges: + - monsterfarmer + requiredItems: + - white_wool:2;+4 + - orange_wool:2;+4 + - magenta_wool:2;+4 + - light_blue_wool:2;+4 + - yellow_wool:2;+4 + - lime_wool:2;+4 + - pink_wool:2;+4 + - gray_wool:2;+4 + - light_gray_wool:2;+4 + - cyan_wool:2;+4 + - purple_wool:2;+4 + - blue_wool:2;+4 + - brown_wool:2;+4 + - green_wool:2;+4 + - red_wool:2;+4 + - black_wool:2;+4 + reward: + text: 2 diamonds, sheep, emerald, flowers + items: + - diamond:2 + - emerald:1 + - sheep_spawn_egg:1 + - rose_bush:1 + - peony:1 + permission: usb.biome.flower_forest + currency: 70 + xp: 70 + repeatReward: + text: emerald, sheep, flowers + items: + - emerald:1 + - sheep_spawn_egg:1 + - rose_bush:1 + - peony:1 + currency: 35 + xp: 35 + maestro: + name: '&5Maestro' + description: Make a jukebox and collect all music discs. + type: onPlayer + requiredItems: + - music_disc_13:1 + - music_disc_cat:1 + - music_disc_blocks:1 + - music_disc_chirp:1 + - music_disc_far:1 + - music_disc_mall:1 + - music_disc_mellohi:1 + - music_disc_stal:1 + - music_disc_strad:1 + - music_disc_ward:1 + - music_disc_11:1 + - music_disc_wait:1 + - jukebox:1 + displayItem: jukebox + lockedDisplayItem: purple_stained_glass_pane + reward: + text: 3 diamonds, 1 gold block, 10 emeralds + items: + - diamond:3 + - emerald:10 + - gold_block:1 + currency: 70 + xp: 70 + repeatReward: + text: 2 gold ore, 1 diamond + items: + - gold_ore:2 + - diamond:1 + - '{p=0.2}diamond:1' + currency: 35 + xp: 35 + ironfarm: + name: '&9Iron Farm' + description: Build an iron-farm. + type: onIsland + displayItem: iron_block + lockedDisplayItem: blue_stained_glass_pane + radius: 50 + requiredChallenges: + - monsterfarm + - applecollector + requiredItems: + - oak_door:30 + requiredEntities: + - Villager:10 + - IRON_GOLEM:1 + reward: + text: 2 gold blocks, 1 diamond block + items: + - gold_block:2 + - diamond_block:1 + currency: 500 + xp: 400 + netherfortress: + name: '&9Nether Fortress' + description: Build a netherfortress. + type: onIsland + radius: 50 + requiredItems: + - nether_bricks:512 + - nether_brick_slab:64 + - nether_brick_fence:64 + - nether_brick_stairs:64 + - soul_sand:32 + displayItem: nether_brick_fence + lockedDisplayItem: blue_stained_glass_pane + reward: + text: a wither-skull and a blaze rod and a chance of diamonds + items: + - wither_skeleton_skull:1 + - blaze_rod:1 + - '{p=0.05}diamond:3' + - '{p=0.10}diamond:2' + - '{p=0.15}diamond:1' + currency: 40 + xp: 40 + # =============================================== + Tier4: + name: '&cMaster' + displayItem: orange_terracotta + resetInHours: 20 + requires: + + # disabled + rankLeeway: 99 + challenges: + - expertbuilder + challenges: + glassmaker: + name: '&3Glassmaker' + description: Collect every color of stained glass. + type: onPlayer + displayItem: white_stained_glass + lockedDisplayItem: cyan_stained_glass_pane + requiredItems: + - white_stained_glass:8;+2 + - orange_stained_glass:8;+2 + - magenta_stained_glass:8;+2 + - light_blue_stained_glass:8;+2 + - yellow_stained_glass:8;+2 + - lime_stained_glass:8;+2 + - pink_stained_glass:8;+2 + - gray_stained_glass:8;+2 + - light_gray_stained_glass:8;+2 + - cyan_stained_glass:8;+2 + - purple_stained_glass:8;+2 + - blue_stained_glass:8;+2 + - brown_stained_glass:8;+2 + - green_stained_glass:8;+2 + - red_stained_glass:8;+2 + - black_stained_glass:8;+2 + reward: + text: 2 diamonds, 2 disks, 1 emeralds + items: + - diamond:2 + - music_disc_ward:1 + - music_disc_11:1 + - emerald:1 + - sunflower:1 + - lilac:1 + permission: usb.biome.flower_forest + currency: 70 + xp: 70 + repeatReward: + text: 30% chance on 1 or 2 disks, 1 emeralds + items: + - '{p=0.3}music_disc_ward:1' + - '{p=0.3}music_disc_11:1' + - emerald:1 + - sunflower:1 + - lilac:1 + currency: 35 + xp: 35 + carpenter: + name: '&3Carpenter' + description: Collect all types of wood items. + type: onPlayer + requiredItems: + - jungle_stairs:16;+8 + - spruce_door:1;+1 + - dark_oak_fence_gate:2;+2 + - acacia_fence:30;+15 + - ladder:30;+15 + - oak_trapdoor:4;+2 + - oak_pressure_plate:2;+1 + requiredChallenges: + - toolmaker + - lumberjack:2 + displayItem: crafting_table + lockedDisplayItem: cyan_stained_glass_pane + reward: + text: a parrot and a jukebox with some redstone and iron + items: + - parrot_spawn_egg:1 + - jukebox:1 + - redstone_ore:4 + - iron_ore:4 + currency: 30 + xp: 30 + repeatReward: + text: redstone, iron and a slim chance at a parrot + items: + - redstone_ore:1 + - iron_ore:1 + - '{p=0.25}jukebox:1' + - '{p=0.05}parrot_spawn_egg:1' + currency: 15 + xp: 15 + cookielover: + name: '&eCookie Lover' + description: Make cookies and a bucket of milk. + type: onPlayer + requiredChallenges: + - expertfarmer + requiredItems: + - cookie:128;+4 + - milk_bucket:1 + displayItem: cookie + lockedDisplayItem: yellow_stained_glass_pane + reward: + text: 4 redstone ore, clay a bucket and a diamond + items: + - redstone_ore:4 + - clay:5 + - diamond:1 + - bucket:1 + currency: 80 + xp: 80 + repeatReward: + text: iron ore and some clay + items: + - iron_ore:1 + - clay:5 + - bucket:1 + - '{p=0.4}clay:3' + - '{p=0.3}iron_ore:2' + currency: 40 + xp: 40 + deepseafisherman: + name: '&5Deep Sea Fishing' + description: Farm the deep sea + type: onPlayer + requiredItems: + - cod:10;+5 + - salmon:10;+5 + - pufferfish:5;+3 + - tropical_fish:3;+2 + - prismarine_crystals:16;+8 + - prismarine_shard:16;+8 + - dried_kelp_block:64;+32 + - nautilus_shell:1 + displayItem: heart_of_the_sea + lockedDisplayItem: purple_stained_glass_pane + reward: + text: heart-of-the-sea, nautilus shell and a turtle + items: + - nautilus_shell:1 + - heart_of_the_sea:1 + - turtle_spawn_egg:1 + - '{p=0.1}nautilus_shell:1' + permission: usb.biome.deep_ocean + currency: 50 + xp: 50 + repeatReward: + text: nautilus shell, turtle and a chance of a heart + items: + - nautilus_shell:1 + - turtle_spawn_egg:1 + - '{p=0.05}heart_of_the_sea:1' + - '{p=0.1}nautilus_shell:1' + currency: 25 + xp: 25 + horsingaround: + name: '&6Horsing Around' + description: Get hay bales for the horses. + type: onPlayer + requiredItems: + - hay_block:32;+4 + - lead:8;+2 + - carrot_on_a_stick:1 + - shears:1 + requirecChallenges: + - wheatfarmer + displayItem: hay_block + lockedDisplayItem: brown_stained_glass_pane + reward: + text: 1 horse, 1 iron horse armor, 5% chance on diamond horse armor + items: + - iron_horse_armor:1 + - horse_spawn_egg:1 + - '{p=0.05}diamond_horse_armor:1' + currency: 50 + xp: 50 + repeatReward: + text: 1 redstone ore, 1 emerald + items: + - redstone:1 + - emerald:1 + currency: 25 + xp: 25 + slimefarmer: + name: '&5Slime Farmer' + description: Collect slimeballs from slimes. + type: onPlayer + requiredItems: + - slime_ball:64;+4 + displayItem: slime_ball + lockedDisplayItem: purple_stained_glass_pane + reward: + text: 1 diamond, 1 emeralds, swampland + items: + - diamond:1 + - emerald:1 + permission: usb.biome.swamp + currency: 70 + xp: 70 + repeatReward: + text: 1 redstone ore, 1 emeralds + items: + - redstone_ore:1 + - emerald:1 + currency: 35 + xp: 35 + animalfarm: + name: '&9Animal Farm' + description: Create an animal farm. + type: onIsland + displayItem: oak_fence + radius: 40 + requiredChallenges: + - woolcollector + - potatofarmer + - carrotfarmer + requiredEntities: + - Cow:8 + - Pig:8 + - Chicken:16 + - Sheep:{"Color":"WHITE"} + - Sheep:{"Color":"ORANGE"} + - Sheep:{"Color":"MAGENTA"} + - Sheep:{"Color":"LIGHT_BLUE"} + - Sheep:{"Color":"YELLOW"} + - Sheep:{"Color":"LIME"} + - Sheep:{"Color":"PINK"} + - Sheep:{"Color":"GRAY"} + - Sheep:{"Color":"LIGHT_GRAY"} + - Sheep:{"Color":"CYAN"} + - Sheep:{"Color":"PURPLE"} + - Sheep:{"Color":"BLUE"} + - Sheep:{"Color":"BROWN"} + - Sheep:{"Color":"GREEN"} + - Sheep:{"Color":"RED"} + - Sheep:{"Color":"BLACK"} + reward: + text: 1 horse, 1 iron horse armor, 5% chance horse armor + items: + - iron_horse_armor:1 + - horse_spawn_egg:1 + - '{p=0.05}iron_horse_armor:1' + - '{p=0.05}golden_horse_armor:1' + - '{p=0.05}diamond_horse_armor:1' + currency: 50 + xp: 50 + repeatReward: + text: 1 redstone ore, 1 emerald, 5% chance horse armor + items: + - redstone:1 + - emerald:1 + - '{p=0.05}iron_horse_armor:1' + - '{p=0.05}golden_horse_armor:1' + - '{p=0.05}diamond_horse_armor:1' + currency: 25 + xp: 25 + witherhunter: + name: '&5Wither Hunter' + description: Collect some Wither Skeleton skulls. + type: onPlayer + requiredChallenges: + - netherfortress + requiredItems: + - wither_skeleton_skull:10;+1 + displayItem: wither_skeleton_skull + lockedDisplayItem: purple_stained_glass_pane + offset: -1 + reward: + text: 5 diamonds, 5% chance of nether star + items: + - diamond:5 + - '{p=0.05}nether_star:1' + currency: 400 + xp: 400 + repeatReward: + text: 2 gold ore, 5% chance of nether star + items: + - gold_ore:2 + - '{p=0.05}nether_star:1' + currency: 20 + xp: 20 + pearlcollector: + name: '&5Pearl Collector' + description: Collect enderpearls from endermen. + type: onPlayer + requiredItems: + - ender_pearl:10;+4 + displayItem: ender_pearl + lockedDisplayItem: purple_stained_glass_pane + reward: + text: 5 gold ore, 1 blaze rod, 1 emerald + items: + - gold_ore:5 + - blaze_rod:1 + - emerald:1 + currency: 70 + xp: 70 + repeatReward: + text: 1 gold ore, 1 blaze rod + items: + - gold_ore:1 + - blaze_rod:1 + currency: 35 + xp: 35 + + # =============================================== + Tier5: + name: '&4Sky Lord' + displayItem: red_terracotta + resetInHours: 48 + requires: + challenges: + - masterbuilder + challenges: + technician: + name: '&3Technician' + description: Collect some of every type of redstone equipment. + type: onPlayer + requiredItems: + - redstone:64;+16 + - redstone_torch:32;+4 + - repeater:5;+1 + - comparator:3;+1 + - piston:2;+1 + - sticky_piston:2;+1 + - lever:1;+1 + - stone_button:1;+1 + - stone_pressure_plate:1;+1 + - hopper:1;+1 + - dispenser:1;+1 + - dropper:1;+1 + - daylight_detector:1;+1 + displayItem: redstone + lockedDisplayItem: cyan_stained_glass_pane + reward: + text: some snow, ice and packed ice + items: + - snow_block:4 + - ice:8 + - packed_ice:8 + - redstone_block:64 + currency: 70 + xp: 70 + repeatReward: + text: a stack of redstone blocks and some ice + items: + - redstone_block:64 + - ice:4 + - packed_ice:1 + currency: 35 + xp: 35 + emeraldcollector: + name: '&5Emerald Collector' + description: Collect emeralds. + type: onPlayer + requiredItems: + - emerald:60;+10 + displayItem: emerald + lockedDisplayItem: purple_stained_glass_pane + reward: + text: a full set of diamond armor + items: + - diamond_helmet:1 + - diamond_chestplate:1 + - diamond_leggings:1 + - diamond_boots:1 + currency: 70 + xp: 70 + repeatReward: + text: full diamond armor + items: + - diamond_helmet:1 + - diamond_chestplate:1 + - diamond_leggings:1 + - diamond_boots:1 + currency: 35 + xp: 35 + topchef: + name: '&eTop Chef' + description: Collect every kind of edible food. + type: onPlayer + requiredItems: + - baked_potato:1 + - bread:1 + - cake:1 + - cooked_chicken:1 + - cooked_cod:1 + - cooked_salmon:1 + - tropical_fish:1 + - cooked_porkchop:1 + - cookie:1 + - golden_apple:1 + - golden_carrot:1 + - mushroom_stew:1 + - pumpkin_pie:1 + - cooked_beef:1 + - melon:1 + - carrot:1 + requiredChallenges: + - expertfarmer + - fisherman + - cookielover + displayItem: carrot + lockedDisplayItem: yellow_stained_glass_pane + reward: + text: 2 diamond, 1 mooshroom + items: + - diamond:2 + - mooshroom_spawn_egg:1 + currency: 80 + xp: 80 + repeatReward: + text: 1 diamond, 1 mooshroom + items: + - diamond:1 + - mooshroom_spawn_egg:1 + currency: 40 + xp: 40 + tajmahal: + name: '&9Taj Mahal' + description: Build a temple of quartz + type: onIsland + radius: 30 + displayItem: quartz_block + lockedDisplayItem: blue_stained_glass_pane + requiredChallenges: + - nethermining + requiredItems: + - quartz_block:512 + - chiseled_quartz_block:64 + - quartz_pillar:128 + - quartz_stairs:64 + - quartz_slab:192 + reward: + text: 3 end-portal frames, red sand + items: + - end_portal_frame:3 + - red_sand:64 + currency: 1000 + xp: 500 + greatpyramid: + name: '&9Great Pyramid' + description: Build a pyramid of sandstone + type: onIsland + radius: 30 + displayItem: sandstone + lockedDisplayItem: blue_stained_glass_pane + requiredItems: + - sandstone:512 + - chiseled_sandstone:64 + - smooth_sandstone:128 + - sandstone_stairs:64 + - sandstone_slab:192 + - red_sandstone:16 + reward: + text: 3 end-portal frames, 3 diamonds, bow + items: + - end_portal_frame:3 + - diamond:3 + - 'bow[enchantments={levels:{infinity:1,power:5,unbreaking:3}}]:1' + currency: 1000 + xp: 500 + poseidonshalls: + name: '&9Poseidon''s Halls' + description: Build the halls of Poseidon + type: onIsland + radius: 50 + displayItem: prismarine + lockedDisplayItem: blue_stained_glass_pane + requiredChallenges: + - deepseafisherman + requiredItems: + - prismarine:512 + - prismarine_bricks:128 + - dark_prismarine:64 + - sea_lantern:32 + - conduit:1 + - flower_pot:16 + - vine:128 + - poppy:10 + - blue_orchid:10 + - allium:10 + - azure_bluet:10 + - red_tulip:10 + - orange_tulip:10 + - white_tulip:10 + - pink_tulip:10 + - oxeye_daisy:10 + - sunflower:10 + - lilac:10 + - rose_bush:10 + - peony:10 + reward: + text: 3 end-portal frames, 5 diamonds + items: + - end_portal_frame:3 + - diamond:5 + currency: 1000 + xp: 500 + beaconator: + name: '&9Beaconator' + description: Build a very expensive beacon. + type: onIsland + radius: 15 + displayItem: beacon + lockedDisplayItem: blue_stained_glass_pane + requiredChallenges: + - netherfortress + - ironfarm + - maestro + - witherhunter + requiredItems: + - beacon:1 + - diamond_block:1 + - emerald_block:8 + - gold_block:25 + - iron_block:49 + reward: + items: + - end_portal_frame:3 + - 'diamond_sword[enchantments={levels:{fire_aspect:1,sharpness:5,unbreaking:3}}]:1' + text: some end-portal-pieces and a magic diamond sword + currency: 4000 + xp: 1000 + endportal: + name: '&9End Portal' + description: Build and activate an end-portal. + type: onIsland + radius: 15 + displayItem: end_portal_frame + lockedDisplayItem: blue_stained_glass_pane + requiredChallenges: + - beaconator + - tajmahal + - poseidonshalls + - greatpyramid + requiredItems: + - end_portal_frame:12 + - end_portal:9 + reward: + text: 5 diamonds and some nice boots + items: + - diamond:5 + - 'diamond_boots[enchantments={levels:{blast_protection:4,feather_falling:4,protection:4}}]:1' + currency: 4000 + xp: 1000 + + # =============================================== + Tier6: + name: '&aWorld Foods' + displayItem: light_gray_terracotta + resetInHours: 20 + requires: + challenges: + - masterbuilder + - topchef + challenges: + fishandchips: + name: '&eFish & Chips' + description: Create the famous English Fish & Chips. + type: onPlayer + requiredItems: + - cooked_cod:32;+8 + - baked_potato:32;+8 + displayItem: cooked_cod + lockedDisplayItem: yellow_stained_glass_pane + reward: + text: 16 baked potatos, 1 disk, 4 emerald + items: + - baked_potato:16 + - music_disc_13:1 + - emerald:4 + currency: 60 + xp: 60 + commands: + - 'console: msg {player} &9https://en.wikipedia.org/wiki/Fish_and_chips + &2Click the link for more info' + repeatReward: + text: 30% chance on 1 disk, 1 emerald + items: + - emerald:1 + - '{p=0.3}music_disc_13:1' + currency: 30 + xp: 30 + commands: + - 'console: msg {player} &9https://en.wikipedia.org/wiki/Fish_and_chips + &2Click the link for more info' + smorrebrod: + name: '&eSmørrebrød' + description: Create the famous Danish smørrebrød. + type: onPlayer + requiredItems: + - bread:16;+4 + - cooked_salmon:8;+4 + - tropical_fish:2;+1 + - cooked_porkchop:8;+4 + - cooked_beef:8;+4 + displayItem: cooked_cod + lockedDisplayItem: yellow_stained_glass_pane + reward: + text: 1 rod, 2 name tags, 2 ocelots + items: + - fishing_rod:1 + - name_tag:2 + - ocelot_spawn_egg:2 + currency: 60 + xp: 60 + commands: + - 'console: msg {player} &9https://en.wikipedia.org/wiki/Danish_cuisine + &2Click the link for more info' + repeatReward: + text: 1 emerald + items: + - emerald:1 + currency: 30 + xp: 30 + commands: + - 'console: msg {player} &9https://en.wikipedia.org/wiki/Danish_cuisine + &2Click the link for more info' + hutspot: + name: '&eHutspot' + description: Create the famous Dutch hutspot. + type: onPlayer + requiredItems: + - potato:64;+8 + - carrot:64;+8 + lockedDisplayItem: yellow_stained_glass_pane + displayItem: golden_carrot + reward: + text: 1 golden carrot, 1 disk, 4 emerald + items: + - golden_carrot:1 + - music_disc_cat:1 + - emerald:4 + currency: 60 + xp: 60 + commands: + - 'console: msg {player} &9https://en.wikipedia.org/wiki/Hutspot &2Click + the link for more info' + repeatReward: + text: 30% chance on 1 disk, 1 emerald + items: + - emerald:1 + - '{p=0.3}music_disc_cat:1' + currency: 30 + xp: 30 + commands: + - 'console: msg {player} &9https://en.wikipedia.org/wiki/Hutspot &2Click + the link for more info' + apfelstrudel: + name: '&eApfelstrudele' + description: Create the famous German apfelstrudel. + type: onPlayer + requiredItems: + - apple:8;+5 + - wheat:16;+5 + - sugar:16;+5 + - milk_bucket:1 + displayItem: golden_apple + lockedDisplayItem: yellow_stained_glass_pane + reward: + text: 1 bucket, 1 disk, 4 emerald + items: + - bucket:1 + - emerald:4 + - music_disc_blocks:1 + currency: 60 + xp: 60 + commands: + - 'console: msg {player} &9https://en.wikipedia.org/wiki/Apple_strudel &2Click + the link for more info' + repeatReward: + text: 1 bucket, 30% chance on 1 disk, 1 emerald + items: + - bucket:1 + - emerald:1 + - '{p=0.3}music_disc_blocks:1' + currency: 30 + xp: 30 + commands: + - 'console: msg {player} &9https://en.wikipedia.org/wiki/Apple_strudel &2Click + the link for more info' + brownies: + name: '&eBrownies' + description: Create the famous American brownies. + type: onPlayer + requiredItems: + - wheat:16;+5 + - sugar:16;+5 + - egg:16;+5 + - ink_sac:16;+5 + - milk_bucket:1 + displayItem: dark_oak_slab + lockedDisplayItem: yellow_stained_glass_pane + reward: + text: 1 bucket, 1 disk, 4 emerald + items: + - bucket:1 + - emerald:4 + - music_disc_far:1 + currency: 60 + xp: 60 + commands: + - 'console: msg {player} &9https://en.wikipedia.org/wiki/Chocolate_brownie + &2Click the link for more info' + repeatReward: + text: 1 bucket, 30% chance on 1 disk, 1 emerald + items: + - bucket:1 + - emerald:1 + - '{p=0.3}music_disc_far:1' + currency: 30 + xp: 30 + commands: + - 'console: msg {player} &9https://en.wikipedia.org/wiki/Chocolate_brownie + &2Click the link for more info' + pastafunghi: + name: '&ePasta Funghi' + description: Create the famous Italian Pasta Funghi. + type: onPlayer + requiredItems: + - wheat:32;+8 + - egg:32;+8 + - brown_mushroom:16;+4 + - red_mushroom:16;+4 + - milk_bucket:1 + displayItem: red_mushroom_block + lockedDisplayItem: yellow_stained_glass_pane + reward: + text: 2 rabbits, 1 bucket + items: + - bucket:1 + - rabbit_spawn_egg:2 + currency: 60 + xp: 60 + commands: + - console:msg {player} &9https://en.wikipedia.org/wiki/Pasta &2Click the + link for more info + repeatReward: + text: 30% chance on 1 disk, 1 emerald + items: + - emerald:1 + - '{p=0.3}music_disc_13:1' + currency: 30 + xp: 30 + commands: + - console:msg {player} &9https://en.wikipedia.org/wiki/Pasta &2Click the + link for more info + chocolate: + name: '&eBelgian chocolate' + description: Create the famous belgian chocolate. + type: onPlayer + requiredItems: + - sugar:16;+5 + - ink_sac:16;+5 + - milk_bucket:1 + displayItem: pig_spawn_egg + lockedDisplayItem: yellow_stained_glass_pane + reward: + text: 1 bucket, 1 disk, 4 emeralds + items: + - bucket:1 + - emerald:4 + - music_disc_mall:1 + currency: 60 + xp: 60 + commands: + - 'console: msg {player} &9https://en.wikipedia.org/wiki/Belgian_chocolate + &2Click the link for more info' + repeatReward: + text: 1 bucket, 30% chance on 1 disk, 1 emerald + items: + - bucket:1 + - emerald:1 + - '{p=0.3}music_disc_mall:1' + currency: 30 + xp: 30 + commands: + - 'console: msg {player} &9https://en.wikipedia.org/wiki/Belgian_chocolate + &2Click the link for more info' + baker: + name: '&ePâtisserie' + description: Bake cakes, pumpkin pies, and cookies. + type: onPlayer + requiredItems: + - cake:5;+1 + - pumpkin_pie:5;+1 + - cookie:128;+4 + displayItem: cake + lockedDisplayItem: yellow_stained_glass_pane + reward: + text: 5 gold ore, 1 diamond + items: + - gold_ore:5 + - diamond:1 + currency: 80 + xp: 80 + repeatReward: + text: 2 iron ore, 1 gold ore + items: + - iron_ore:2 + - gold_ore:1 + currency: 40 + xp: 40 + +# DO NOT CHANGE THE VERSION! You will break the conversion and unexpected things will happen! +version: 107 diff --git a/uSkyBlock-Core/src/test/resources/us/talabrek/ultimateskyblock/imports/old-config-expected.yml b/uSkyBlock-Core/src/test/resources/us/talabrek/ultimateskyblock/imports/old-config-expected.yml new file mode 100644 index 000000000..873f75319 --- /dev/null +++ b/uSkyBlock-Core/src/test/resources/us/talabrek/ultimateskyblock/imports/old-config-expected.yml @@ -0,0 +1,476 @@ +# The language used in the plugin (en is default). +language: en +options: + general: + + # [integer] The max number of players allowed in a single party. (including the leader) + maxPartySize: 4 + + # [integer] The time in seconds before a player can use the /island info command again. (note: cooldowns are reset when the plugin is reloaded) + cooldownInfo: 20 + + # [integer] The time in seconds before a player can use the /island restart command again. + cooldownRestart: 30 + + # [integer] The time in seconds before a player can use the /island biome command again. + biomeChange: 60 + + # [string] The default biome assigned to new islands + defaultBiome: OCEAN + + # [integer] The number of milliseconds between the same notification is sent to the player. + # This is used when events are triggered heavily - i.e. item-pickup-prevention, damage-prevention etc. + maxSpam: 2000 + + # [string] The name of the skyblock world, will be automatically generated if it doesn't exist. + worldName: skyworld + + # [integer] Area around 0,0 where islands will not be created to protect spawn. + spawnSize: 64 + island: + + # [integer] The y-coordinate (height) where islands are spawned. + height: 150 + + # [integer] The number of blocks between islands. + distance: 128 + + # [integer] The size of the protective region for each island. Can't be higher than 'distance' + # and MUST be divisible by 32 if you intend to use nether. + protectionRange: 128 + + # [filename] The schematic to use for island generation. + # Put your schematic in the 'uSkyBlock/schematics' folder, you don't need to add the '.schematic' part below. + schematicName: default + + # [true/false] If true, remove all hostile mobs when a player teleports back to their island. + removeCreaturesByTeleport: false + + # [item list] The list of items to place in the chest when a player starts a new island. ITEM_ID:HOW_MANY. + # default is 2 ice, 1 watermelon, 1 cactus, 1 lava bucket, 1 red & brown mushroom, 1 pumpkin seed, 1 sugar cane, 1 sign. + chestItems: + - ice:2 + - melon_slice:1 + - cactus:1 + - lava_bucket:1 + - red_mushroom:1 + - brown_mushroom:1 + - pumpkin_seeds:1 + - sugar_cane:1 + - oak_sign:1 + addExtraItems: true + islandTeleportDelay: 2 + allowPvP: deny + allowIslandLock: true + useIslandLevel: true + useTopTen: true + topTenTimeout: 20 + autoRefreshScore: 0 + topTenShowMembers: true + fixFlatland: false + chat-format: '&9SKY &r{DISPLAYNAME} &f>&d {MESSAGE}' + log-size: 10 + spawn-limits: + enabled: true + animals: 64 + monsters: 50 + villagers: 16 + golems: 5 + block-limits: + enabled: true + hopper: 50 + spawner: 10 + extraPermissions: + smallbonus: + - cobblestone:16 + - cooked_porkchop:5 + mediumbonus: + - torch:16 + - lava_bucket:1 + largebonus: + - dirt:5 + - sand:5 + giantbonus: + - grass_block:1 + - mycelium:1 + extremebonus: + - bone:8 + - coal:4 + donorbonus: + - bow:1 + - arrow:32 + - stone_sword:1 + netheraccess: + - obsidian:14 + - flint_and_steel:1 + extras: + + # [true/false] If true, return players that don't have an island (this includes players removed from a party while offline), to the server spawn when they login. + # NOTE: Requires EssentialsSpawn or another plugin with the "/spawn" command + sendToSpawn: false + + # [true/false] If true, when a player respawns they will respawn on their island. Will first attempt to respawn them at the closest safe location to their island home, then their island spawn, then the server spawn. + # NOTE: If this is true, sendToSpawn is ignored unless the player has no island or no viable island home or spawn location. + respawnAtIsland: true + + # [true/false] If true, a player can right-click on a block of obsidian on their island while holding an empty bucket to remove the obsidian and fill the bucket with lava. This is useful for people that accidentally + # turn their lava into obsidian with a bad cobblestone generator design. Will only work on the player's island and if there are no other obsidian blocks nearby (so can't be used on portals). + obsidianToLava: true + + # Contains flags for enabling PROTECTION of various mechanics. + protection: + + # Whether or not, items dropped on the ground should be limited to party-members. + item-drops: true + + # If true, only creepers targeting party-members will explode + creepers: true + + # If true, Withers will be limited to harming island-members/island blocks. + withers: true + + # Whether or not the plugin will try to protect the player from accidentally extinguishing lava + protect-lava: true + + # Whether or not portalling to the nether roof should be blocked. + nether-roof: true + + # Whether or not anyone can trade i skyblock + # default: false - since it goes against the core of skyblock + villager-trading-enabled: false + + # Generally protections against griefers + # If the flag is true, it generally means the protection is active + # (meaning the action is blocked) + visitors: + + # Protect visitors from trampling your crop + trampling: true + + # Protect against visitors attacked animals + kill-animals: true + + # Protect against visitors attacking monsters + kill-monsters: true + + # Protect from shearing + shearing: true + + # Protect from begin bombarded with eggs that hatch + hatching: true + + # Protect from villager-trading + villager-trading: true + + # Whether or not visitors are protected from fall damage + fall: true + + # Whether or not visitors are protected from fire damage (incl. lava) + fire-damage: true + + # Whether or not visitors are protected from monster damage + monster-damage: false + + # Whether or not visitors should be allowed to drop items + item-drops: true + + # Warns online members when a player visits the island. + warn-on-warp: true + + # Whether or not to actively block banned players from entering an island (by walking/flying). + block-banned-entry: true + + # Whether or not visitors can use portals (default: false) + use-portals: false + + # Wheter or not visitors can mount vehicles + vehicle-enter: false + + # Wheter or not visitors can break vehicles + vehicle-damage: false + + # Contains flags for controlling monster spawning on islands. + spawning: + # Controls if Phantoms are allowed to spawn naturally. Non-natural spawns, e.g. from a spawnegg, are not + # managed by us. + phantoms: + # [true/false] If natural Phantom spawns are allowed in the overworld. True enables Phantom spawning. + overworld: true + # [true/false] If natural Phantom spawns are allowed in the nether. True enables Phantom spawning. + nether: false + + party: + + # The number of ms before an invite timeouts (1000 ms per second) + invite-timeout: 30000 + + # The format used in /partytalk chat messages + chat-format: '&9PARTY &r{DISPLAYNAME} &f>&b {MESSAGE}' + + # Extra commands to execute when players join an island + join-commands: + - op:playsound block.enderchest.open block @p + + # Extra commands to execute when players leave an island + leave-commands: + - op:playsound block.enderchest.close block @p + + # This section provide some performance tweaking configs + advanced: + + # If true, display-name is looked up (might be performance intensive). + useDisplayNames: false + + # [number] The threshold for purging islands. + # any island with a level above this, is spared. + purgeLevel: 10 + + # [seconds] The number of seconds for confirming a command by + # re-executing it (/is leave, /is restart). + confirmTimeout: 10 + + # [number] The number of chunks to regenerate per server tick. Might be decreased or increased based on available + # server resources. Default value: 4. + chunkRegenSpeed: 4 + + # If false, the world spawn will be ignored. You should take care of placing the world spawn location + # with /mv setspawn and managing the spawn building. + manageSpawn: true + + # Controls advanced behaviour reg. the internal playerdb + playerdb: + + # valid values are: yml, memory, bukkit + storage: bukkit + + # Section about restarting your island (or accepting an invite). + restart: + + # Clears the player's inventory on island create/restart + clearInventory: true + + # Clears the player's armor on island create/restart + clearArmor: true + + # Clears the player's enderchest on island create/restart + clearEnderChest: true + + # Clears the permissions the player has been granted from rewards + clearPerms: true + + # Clears the users balance (set to 0) + clearCurrency: false + + # [ms] The number of ms to wait, before porting the player back + # on /is restart or /is create (default: 1000) + teleportDelay: 1000 + + # [true/false] Whether or not the player should be auto teleported to the island when it's ready + teleportWhenReady: true + + # list of commands to execute after island-creation + # i.e. + # - me Jumps with &ajoy + extra-commands: [] + +# In this section it's possible to assign additional perks to permission-nodes +donor-perks: + # the permission node to give to someone + 'usb.donor.small': + extraItems: [bone_meal:1] + maxPartySize: 5 + animals: 84 + monsters: 60 + villagers: 20 + golems: 6 + # 10% more XP + rewardBonus: 0.1 + # 10% less hunger + hungerReduction: 0.1 + +# Whether or not to show the UI for create/restart +island-schemes-enabled: true + +# List of selections for /is create and /is restart +# the nodes under island-schemes must match the schematic-names from the schematics folder. +island-schemes: + + # name of the schematic + default: + + # permission needed to use island + permission: usb.island.create + + # small discription of the island + description: The default uSkyBlock island + + # item to display in the GUI + displayItem: oak_sapling + + # optional, default true (true enabled in GUI, false disabled in GUI) + enabled: true + + # optional, must be listed in ascending order + index: 2 + + # optional extra's that can be given per island + extraItems: '' + maxPartySize: 4 + animals: 64 + monsters: 50 + villagers: 16 + golems: 5 + + # Get 100% of the normal score + scoreMultiply: 1.0 + + # But start with no offset + scoreOffset: 0 + skySMP: + permission: usb.schematic.skysmp + description: The original SkySMP island + displayItem: oak_leaves + enabled: false + index: 3 + extraItems: + - obsidian:14 + - flint_and_steel:1 + maxPartySize: 4 + animals: 64 + monsters: 50 + villagers: 16 + golems: 5 + scoreMultiply: 0.9 + scoreOffset: 40 + spawn: + description: The default spawn schematic + enabled: false +confirmation: + + # [true/false] Whether to require confirmation (i.e. repeating the command twice). + is leave: true + + # [true/false] Whether to require confirmation (i.e. repeating the command twice). + is restart: true +asyncworldedit: + + # Supports disabling the detection of AWE + enabled: true + + # Show progress to the user every 5 seconds + progressEveryMs: 5000 + + # Or 20pct (what-ever comes first) + progressEveryPct: 20 + watchDog: + + # The maximum time to wait for AWE paste to complete (2m, 3m20s, etc.) + timeout: 15s + + # The number of ms between each heartbeat + heartBeatMs: 2000 +worldguard: + entry-message: true + exit-message: true +nether: + enabled: true + height: 75 + lava_level: 7 + activate-at: + level: 100 + schematicName: uSkyBlockNether + terraform-enabled: true + + # The distance to search for valid terra-form location. + terraform-distance: 7 + + # In what range of pitch (vertical aim) will terraforming be enabled + # -90 is looking directly upwards + # 90 is looking directly down + terraform-min-pitch: -70.0 + terraform-max-pitch: 90.0 + + # The probability of forming blocks + terraform: + NETHERRACK: + - '{p=0.30}NETHERRACK' + - '{p=0.15}NETHERRACK' + - '{p=0.05}NETHER_QUARTZ_ORE' + - '{p=0.05}SOUL_SAND' + NETHER_QUARTZ_ORE: + - '{p=0.30}NETHER_QUARTZ_ORE' + - '{p=0.10}NETHER_QUARTZ_ORE' + SOUL_SAND: + - '{p=0.25}SOUL_SAND' + - '{p=0.07}SOUL_SAND' + - '{p=0.05}GRAVEL' + GRAVEL: + - '{p=0.15}GRAVEL' + - '{p=0.05}GRAVEL' + - '{p=0.05}SOUL_SAND' + GLOWSTONE: + - '{p=0.80}GLOWSTONE' + - '{p=0.15}GLOWSTONE' + + # Weights that is applied to the above terraform chances depending on the tool used + terraform-weight: + WOOD: 0 + STONE: 1.0 + IRON: 0.9 + GOLD: 1.5 + DIAMOND: 0.2 + NETHERITE: 1.2 + + # The chances of changing a pigzombie when spawned on a netherbrick + spawn-chances: + enabled: true + wither: 0.2 + skeleton: 0.1 + blaze: 0.2 +tool-menu: + enabled: true + tool: oak_sapling + commands: + CHEST: island + CRAFTING_TABLE: challenges + BEDROCK: island spawn + +plugin-updates: + # Should we check for updates and log a message if an update is available + check: true + # Possible options: RELEASE or STAGING + branch: RELEASE + +# Placeholders - enable these to get placeholder substitution +# usb_version +# usb_island_level, usb_island_level_int +# usb_island_rank, usb_island_partysize_max, usb_island_partysize +# usb_island_leader, usb_island_bans, usb_island_members, usb_island_trustees +# usb_island_biome, usb_island_schematic +# usb_island_location, usb_island_location_x, usb_island_location_y, usb_island_location_z +# usb_island_golems_max, usb_island_monsters_max, usb_island_animals_max, usb_island_villagers_max, +# usb_island_golems, usb_island_monsters, usb_island_animals, usb_island_villagers +placeholder: + + # Hooks into MVdWPlaceholderAPI + mvdwplaceholderapi: false + + # uSkyBlock native placeholders for chat messages and format + chatplaceholder: false + + # uSkyBlock native placeholders for server-commands + servercommandplaceholder: false + +# DO NOT TOUCH THE FIELDS BELOW +version: 109 +force-replace: + options.party.invite-timeout: 100 + options.island.islandTeleportDelay: 5 + options.island.useOldIslands: false + options.island.schematicName: yourschematichere +move-nodes: + options.restart.confirmation: confirmation.is restart + options.party.leave.confirmation: confirmation.is leave + options.island.hopperlimit: options.island.block-limits.hopper + options.island.spawnerlimit: options.island.block-limits.mob_spawner + options.island.block-limits.mob_spawner: options.island.block-limits.spawner diff --git a/uSkyBlock-Core/src/test/resources/us/talabrek/ultimateskyblock/imports/old-config.yml b/uSkyBlock-Core/src/test/resources/us/talabrek/ultimateskyblock/imports/old-config.yml new file mode 100644 index 000000000..ebf8f7db1 --- /dev/null +++ b/uSkyBlock-Core/src/test/resources/us/talabrek/ultimateskyblock/imports/old-config.yml @@ -0,0 +1,476 @@ +# The language used in the plugin (en is default). +language: en +options: + general: + + # [integer] The max number of players allowed in a single party. (including the leader) + maxPartySize: 4 + + # [integer] The time in seconds before a player can use the /island info command again. (note: cooldowns are reset when the plugin is reloaded) + cooldownInfo: 20 + + # [integer] The time in seconds before a player can use the /island restart command again. + cooldownRestart: 30 + + # [integer] The time in seconds before a player can use the /island biome command again. + biomeChange: 60 + + # [string] The default biome assigned to new islands + defaultBiome: OCEAN + + # [integer] The number of milliseconds between the same notification is sent to the player. + # This is used when events are triggered heavily - i.e. item-pickup-prevention, damage-prevention etc. + maxSpam: 2000 + + # [string] The name of the skyblock world, will be automatically generated if it doesn't exist. + worldName: skyworld + + # [integer] Area around 0,0 where islands will not be created to protect spawn. + spawnSize: 64 + island: + + # [integer] The y-coordinate (height) where islands are spawned. + height: 150 + + # [integer] The number of blocks between islands. + distance: 128 + + # [integer] The size of the protective region for each island. Can't be higher than 'distance' + # and MUST be divisible by 32 if you intend to use nether. + protectionRange: 128 + + # [filename] The schematic to use for island generation. + # Put your schematic in the 'uSkyBlock/schematics' folder, you don't need to add the '.schematic' part below. + schematicName: default + + # [true/false] If true, remove all hostile mobs when a player teleports back to their island. + removeCreaturesByTeleport: false + + # [item list] The list of items to place in the chest when a player starts a new island. ITEM_ID:HOW_MANY. + # default is 2 ice, 1 watermelon, 1 cactus, 1 lava bucket, 1 red & brown mushroom, 1 pumpkin seed, 1 sugar cane, 1 sign. + chestItems: + - ICE:2 + - MELON_SLICE:1 + - CACTUS:1 + - LAVA_BUCKET:1 + - RED_MUSHROOM:1 + - BROWN_MUSHROOM:1 + - PUMPKIN_SEEDS:1 + - SUGAR_CANE:1 + - SIGN:1 + addExtraItems: true + islandTeleportDelay: 2 + allowPvP: deny + allowIslandLock: true + useIslandLevel: true + useTopTen: true + topTenTimeout: 20 + autoRefreshScore: 0 + topTenShowMembers: true + fixFlatland: false + chat-format: '&9SKY &r{DISPLAYNAME} &f>&d {MESSAGE}' + log-size: 10 + spawn-limits: + enabled: true + animals: 64 + monsters: 50 + villagers: 16 + golems: 5 + block-limits: + enabled: true + hopper: 50 + spawner: 10 + extraPermissions: + smallbonus: + - COBBLESTONE:16 + - COOKED_PORKCHOP:5 + mediumbonus: + - TORCH:16 + - LAVA_BUCKET:1 + largebonus: + - DIRT:5 + - SAND:5 + giantbonus: + - GRASS:1 + - MYCELIUM:1 + extremebonus: + - BONE:8 + - COAL:4 + donorbonus: + - BOW:1 + - ARROW:32 + - STONE_SWORD:1 + netheraccess: + - OBSIDIAN:14 + - FLINT_AND_STEEL:1 + extras: + + # [true/false] If true, return players that don't have an island (this includes players removed from a party while offline), to the server spawn when they login. + # NOTE: Requires EssentialsSpawn or another plugin with the "/spawn" command + sendToSpawn: false + + # [true/false] If true, when a player respawns they will respawn on their island. Will first attempt to respawn them at the closest safe location to their island home, then their island spawn, then the server spawn. + # NOTE: If this is true, sendToSpawn is ignored unless the player has no island or no viable island home or spawn location. + respawnAtIsland: true + + # [true/false] If true, a player can right-click on a block of obsidian on their island while holding an empty bucket to remove the obsidian and fill the bucket with lava. This is useful for people that accidentally + # turn their lava into obsidian with a bad cobblestone generator design. Will only work on the player's island and if there are no other obsidian blocks nearby (so can't be used on portals). + obsidianToLava: true + + # Contains flags for enabling PROTECTION of various mechanics. + protection: + + # Whether or not, items dropped on the ground should be limited to party-members. + item-drops: true + + # If true, only creepers targeting party-members will explode + creepers: true + + # If true, Withers will be limited to harming island-members/island blocks. + withers: true + + # Whether or not the plugin will try to protect the player from accidentally extinguishing lava + protect-lava: true + + # Whether or not portalling to the nether roof should be blocked. + nether-roof: true + + # Whether or not anyone can trade i skyblock + # default: false - since it goes against the core of skyblock + villager-trading-enabled: false + + # Generally protections against griefers + # If the flag is true, it generally means the protection is active + # (meaning the action is blocked) + visitors: + + # Protect visitors from trampling your crop + trampling: true + + # Protect against visitors attacked animals + kill-animals: true + + # Protect against visitors attacking monsters + kill-monsters: true + + # Protect from shearing + shearing: true + + # Protect from begin bombarded with eggs that hatch + hatching: true + + # Protect from villager-trading + villager-trading: true + + # Whether or not visitors are protected from fall damage + fall: true + + # Whether or not visitors are protected from fire damage (incl. lava) + fire-damage: true + + # Whether or not visitors are protected from monster damage + monster-damage: false + + # Whether or not visitors should be allowed to drop items + item-drops: true + + # Warns online members when a player visits the island. + warn-on-warp: true + + # Whether or not to actively block banned players from entering an island (by walking/flying). + block-banned-entry: true + + # Whether or not visitors can use portals (default: false) + use-portals: false + + # Wheter or not visitors can mount vehicles + vehicle-enter: false + + # Wheter or not visitors can break vehicles + vehicle-damage: false + + # Contains flags for controlling monster spawning on islands. + spawning: + # Controls if Phantoms are allowed to spawn naturally. Non-natural spawns, e.g. from a spawnegg, are not + # managed by us. + phantoms: + # [true/false] If natural Phantom spawns are allowed in the overworld. True enables Phantom spawning. + overworld: true + # [true/false] If natural Phantom spawns are allowed in the nether. True enables Phantom spawning. + nether: false + + party: + + # The number of ms before an invite timeouts (1000 ms per second) + invite-timeout: 30000 + + # The format used in /partytalk chat messages + chat-format: '&9PARTY &r{DISPLAYNAME} &f>&b {MESSAGE}' + + # Extra commands to execute when players join an island + join-commands: + - op:playsound block.enderchest.open block @p + + # Extra commands to execute when players leave an island + leave-commands: + - op:playsound block.enderchest.close block @p + + # This section provide some performance tweaking configs + advanced: + + # If true, display-name is looked up (might be performance intensive). + useDisplayNames: false + + # [number] The threshold for purging islands. + # any island with a level above this, is spared. + purgeLevel: 10 + + # [seconds] The number of seconds for confirming a command by + # re-executing it (/is leave, /is restart). + confirmTimeout: 10 + + # [number] The number of chunks to regenerate per server tick. Might be decreased or increased based on available + # server resources. Default value: 4. + chunkRegenSpeed: 4 + + # If false, the world spawn will be ignored. You should take care of placing the world spawn location + # with /mv setspawn and managing the spawn building. + manageSpawn: true + + # Controls advanced behaviour reg. the internal playerdb + playerdb: + + # valid values are: yml, memory, bukkit + storage: bukkit + + # Section about restarting your island (or accepting an invite). + restart: + + # Clears the player's inventory on island create/restart + clearInventory: true + + # Clears the player's armor on island create/restart + clearArmor: true + + # Clears the player's enderchest on island create/restart + clearEnderChest: true + + # Clears the permissions the player has been granted from rewards + clearPerms: true + + # Clears the users balance (set to 0) + clearCurrency: false + + # [ms] The number of ms to wait, before porting the player back + # on /is restart or /is create (default: 1000) + teleportDelay: 1000 + + # [true/false] Whether or not the player should be auto teleported to the island when it's ready + teleportWhenReady: true + + # list of commands to execute after island-creation + # i.e. + # - me Jumps with &ajoy + extra-commands: [] + +# In this section it's possible to assign additional perks to permission-nodes +donor-perks: + # the permission node to give to someone + 'usb.donor.small': + extraItems: [BONE_MEAL:1] + maxPartySize: 5 + animals: 84 + monsters: 60 + villagers: 20 + golems: 6 + # 10% more XP + rewardBonus: 0.1 + # 10% less hunger + hungerReduction: 0.1 + +# Whether or not to show the UI for create/restart +island-schemes-enabled: true + +# List of selections for /is create and /is restart +# the nodes under island-schemes must match the schematic-names from the schematics folder. +island-schemes: + + # name of the schematic + default: + + # permission needed to use island + permission: usb.island.create + + # small discription of the island + description: The default uSkyBlock island + + # item to display in the GUI + displayItem: SAPLING + + # optional, default true (true enabled in GUI, false disabled in GUI) + enabled: true + + # optional, must be listed in ascending order + index: 2 + + # optional extra's that can be given per island + extraItems: '' + maxPartySize: 4 + animals: 64 + monsters: 50 + villagers: 16 + golems: 5 + + # Get 100% of the normal score + scoreMultiply: 1.0 + + # But start with no offset + scoreOffset: 0 + skySMP: + permission: usb.schematic.skysmp + description: The original SkySMP island + displayItem: OAK_LEAVES + enabled: false + index: 3 + extraItems: + - OBSIDIAN:14 + - FLINT_AND_STEEL:1 + maxPartySize: 4 + animals: 64 + monsters: 50 + villagers: 16 + golems: 5 + scoreMultiply: 0.9 + scoreOffset: 40 + spawn: + description: The default spawn schematic + enabled: false +confirmation: + + # [true/false] Whether to require confirmation (i.e. repeating the command twice). + is leave: true + + # [true/false] Whether to require confirmation (i.e. repeating the command twice). + is restart: true +asyncworldedit: + + # Supports disabling the detection of AWE + enabled: true + + # Show progress to the user every 5 seconds + progressEveryMs: 5000 + + # Or 20pct (what-ever comes first) + progressEveryPct: 20 + watchDog: + + # The maximum time to wait for AWE paste to complete (2m, 3m20s, etc.) + timeout: 15s + + # The number of ms between each heartbeat + heartBeatMs: 2000 +worldguard: + entry-message: true + exit-message: true +nether: + enabled: true + height: 75 + lava_level: 7 + activate-at: + level: 100 + schematicName: uSkyBlockNether + terraform-enabled: true + + # The distance to search for valid terra-form location. + terraform-distance: 7 + + # In what range of pitch (vertical aim) will terraforming be enabled + # -90 is looking directly upwards + # 90 is looking directly down + terraform-min-pitch: -70.0 + terraform-max-pitch: 90.0 + + # The probability of forming blocks + terraform: + NETHERRACK: + - '{p=0.30}NETHERRACK' + - '{p=0.15}NETHERRACK' + - '{p=0.05}NETHER_QUARTZ_ORE' + - '{p=0.05}SOUL_SAND' + NETHER_QUARTZ_ORE: + - '{p=0.30}NETHER_QUARTZ_ORE' + - '{p=0.10}NETHER_QUARTZ_ORE' + SOUL_SAND: + - '{p=0.25}SOUL_SAND' + - '{p=0.07}SOUL_SAND' + - '{p=0.05}GRAVEL' + GRAVEL: + - '{p=0.15}GRAVEL' + - '{p=0.05}GRAVEL' + - '{p=0.05}SOUL_SAND' + GLOWSTONE: + - '{p=0.80}GLOWSTONE' + - '{p=0.15}GLOWSTONE' + + # Weights that is applied to the above terraform chances depending on the tool used + terraform-weight: + WOOD: 0 + STONE: 1.0 + IRON: 0.9 + GOLD: 1.5 + DIAMOND: 0.2 + NETHERITE: 1.2 + + # The chances of changing a pigzombie when spawned on a netherbrick + spawn-chances: + enabled: true + wither: 0.2 + skeleton: 0.1 + blaze: 0.2 +tool-menu: + enabled: true + tool: SAPLING + commands: + CHEST: island + CRAFTING_TABLE: challenges + BEDROCK: island spawn + +plugin-updates: + # Should we check for updates and log a message if an update is available + check: true + # Possible options: RELEASE or STAGING + branch: RELEASE + +# Placeholders - enable these to get placeholder substitution +# usb_version +# usb_island_level, usb_island_level_int +# usb_island_rank, usb_island_partysize_max, usb_island_partysize +# usb_island_leader, usb_island_bans, usb_island_members, usb_island_trustees +# usb_island_biome, usb_island_schematic +# usb_island_location, usb_island_location_x, usb_island_location_y, usb_island_location_z +# usb_island_golems_max, usb_island_monsters_max, usb_island_animals_max, usb_island_villagers_max, +# usb_island_golems, usb_island_monsters, usb_island_animals, usb_island_villagers +placeholder: + + # Hooks into MVdWPlaceholderAPI + mvdwplaceholderapi: false + + # uSkyBlock native placeholders for chat messages and format + chatplaceholder: false + + # uSkyBlock native placeholders for server-commands + servercommandplaceholder: false + +# DO NOT TOUCH THE FIELDS BELOW +version: 108 +force-replace: + options.party.invite-timeout: 100 + options.island.islandTeleportDelay: 5 + options.island.useOldIslands: false + options.island.schematicName: yourschematichere +move-nodes: + options.restart.confirmation: confirmation.is restart + options.party.leave.confirmation: confirmation.is leave + options.island.hopperlimit: options.island.block-limits.hopper + options.island.spawnerlimit: options.island.block-limits.mob_spawner + options.island.block-limits.mob_spawner: options.island.block-limits.spawner diff --git a/uSkyBlock-Core/src/test/resources/us/talabrek/ultimateskyblock/imports/old-default-challenges-expected.yml b/uSkyBlock-Core/src/test/resources/us/talabrek/ultimateskyblock/imports/old-default-challenges-expected.yml new file mode 100644 index 000000000..535ca2f3c --- /dev/null +++ b/uSkyBlock-Core/src/test/resources/us/talabrek/ultimateskyblock/imports/old-default-challenges-expected.yml @@ -0,0 +1,1795 @@ +# ====================================================================================================================== +# +# Here follows the format and explanation for the challenge group and challenges setup. The single commented (#) are the +# most used settings while the double commented (##) are optional if you like to use those settings. +# +# Item type format +# Item types are defined as in the minecraft /give command, i.e. with their minecraft key and possible components in +# square brackets. For example, 'minecraft:diamond_sword[damage=42]'. Refer to the Minecraft wiki for more information: +# https://minecraft.wiki/w/Data_component_format. You can also use the command '/usb iteminfo' to get the information. +# +# This item type is supplemented with additional information depending on where it is used: +# +# display-item: +# An item to be displayed in a GUI. It only defines the item type as above, without any additional information. +# For example: 'cobblestone', 'minecraft:stone', 'diamond_sword[damage=42]'. +# +# item-requirement: :[;+] +# An item requirement for a challenge. The amount is the number of items required. The optional + is the number +# of items to add to the required amount for each repeat of the challenge. For example, 'cobblestone:64;+16' would +# require 64 cobblestone for the first completion and 80 cobblestone for the second completion. Other options are +# -, *, and / for subtraction, multiplication, and division, respectively. For example, 'cobblestone:64;*2' would +# require 64 cobblestone for the first completion, 128 cobblestone for the second completion, and 256 cobblestone for +# the third completion. +# +# item-reward: [{p=}]: +# An item reward for a challenge. The amount is the number of items to give. The optional {p=} is the +# probability of the item being given. For example, 'cobblestone:64' would give 64 cobblestone every time the challenge +# is completed, while '{p=0.1}cobblestone:64' would give 64 cobblestone 10% of the time. +# +# ====================================================================================================================== +# All challenges are defined in the ranks section. Each rank is a tier of challenges that players can complete. +# +# ranks: +# # [text] name of the challenge Rank. +# TierX: +# # [text] The name of the challenge rank that shows when you do /challenges (supports capitals and color codes). +# name: '&aCustom Challenges rank name' +# # [display-item] The item to be displayed in the challenge menu for completed challenges. +# displayItem: 'cyan_terracotta' +# # [integer] The time in hours before required items reset to default (this overwrites the main reset time) +# resetInHours: 20 +# # These requirements controls when a challenge group will be available to a player. +# requires: +# # [integer] The number of tasks per rank that can be left uncompleted to advance to the next rank. For example, +# if you have 4 challenges with a rankLeeway of 1, a player would only need to complete 3 to advance to +# the next rank. A rankLeeway of 0 would require them all. +# rankLeeway: 8 +# # [List[text]] Challenges that have to be completed before this group will available to a player. +# challenges: +# - a challenge name +# ====================================================================================================================== +# challenges: +# # [text] The name of the challenge. All challenge names should be lower-case. +# defaultchallenge: +# # [text] The name of the challenge that shows in /challenges (this supports capitals and color codes). +# name: '&a Default Challenge' +# # [text] The descriptions players see when they do /challenges +# description: +# # [onIsland/onPlayer/islandLevel] This defines whether the required blocks/items should be in the player's +# # inventory or on their island. When using onIsland, the player must within 10 blocks from the required blocks +# # on his island. When using islandLevel, the 'requiredItems' field should be the island level required. The +# # player must use /island level first to update their level. +# type: onPlayer +# ## type: islandLevel +# ## type: onIsland +# # [integer] Overrides the default radius of 10 blocks when using onIsland. +# ## radius: 20 +# # List[item-requirement] The items required to complete the challenge. +# requiredItems: +# - stone:64;+16 +# - cobblestone:64;+16 +# # List[block-requirement] The blocks required to complete the challenge. +# requiredBlocks: +# - stone:64 +# - cobblestone:64 +# # [true/false] If the challenge can be repeated or not. +# ## repeatable: true +# # [integer] The maximum number of times the challenge can be completed. Overrides the default repeatLimit. +# # A value of 0 means unlimited repeats. +# ## repeatLimit: 5 +# # [display-item] The item to be displayed in the challenge menu for completed challenges. +# displayItem: cobblestone +# # [integer] The time in hours before required items reset to default (overwrites the main and rank defaults). +# ## resetInHours: 4 +# # [true/false] Take required items on completing a challenge. +# ## takeItems: true +# # The rewards players get for completing the challenge +# reward: +# # [text] Description of the reward. +# text: 'Mossy cobblestone and an iron pickaxe with unbreaking 1' +# # [List[item-requirement]] A list of items given to the player for completing the challenge. +# items: +# - mossy_cobblestone:16 +# - iron_pickaxe[enchantments={levels:{unbreaking:1}}]:1 +# # [permission node] A permission granted for completion. Multiple permissions are space-separated. +# ## permission: 'test.permission' +# # [integer] How much currency to give for completion. (requires an economy plugin) +# ## currency: 0 +# # [integer] How much xp to give to the player for completion. +# ## xp: 0 +# # [List[Text]] Executes the given command upon completion. Prepend with "op" or "console" to run the commands +# as OP or from the Console. Examples: +# # Possible command arguments are: +# # {player} - The name of the player +# # {playerName} - The display name of the player +# # {challenge} - The name of the challenge +# # {position} - The position of the player +# # {party} - Execute the command once for each member of the party (substituting the name) +# ## commands: +# ## - 'op: me are the GOD of things' +# ## - 'console: give {party} aweseomestuff 32' +# # reward section to reward the player for completing a repeated challenge (any time after the first). The +# # structure is identical to the 'reward' section. +# repeatReward: +# text: 'Mossy cobblestone' +# items: +# - mossy_cobblestone:16 +# +# ====================================================================================================================== + +# [true/false] Enable the use of the challenges command. +allowChallenges: true + +# [island/player] Whether challenges are tracked per player, or per island +challengeSharing: island + +# [true/false] If true, first time challenge completions are broadcast to the whole server. +broadcastCompletion: true + +# [text] The color/formatting of the broadcast text when showing first time completions. +broadcastText: '&6' + +# [true/false] If true, challenges in higher level ranks require challenges in lower level ranks to be completed. +requirePreviousRank: true + +# [integer] The number of tasks per rank that can be left uncompleted to advance to the next rank. For example, if you have 4 easy challenges +# with a rankLeeway of 1, a player would only need to complete 3 to advance to the next rank. +# A rankLeeway of 0 would require them all. +rankLeeway: 12 + +# [integer] The time in hours before required items reset to default. (only if not specified in the challenges below) +defaultResetInHours: 20 + +# [color code] The color to use for uncompleted challenges in the list. +challengeColor: '&e' + +# [color code] The color to use for completed challenges in the list. (non-repeatable) +finishedColor: '&2' + +# [color code] The color to use for completed challenges in the list. (repeatable) +repeatableColor: '&a' + +# [true/false] If true, enables vault to handle currency rewards. +enableEconomyPlugin: true + +# [true/false] If false, challenges are not reset on island creation (or restart) +resetChallengesOnCreate: true + +# Material to show for locked challenges (i.e. STAINED_GLASS_PANE:14 for a red glass-pane, or 160:14) +lockedDisplayItem: red_stained_glass_pane + +# Material to show for onIsland challenges when locked +ISLAND: + lockedDisplayItem: blue_stained_glass_pane + +# Material to show for islandLevel challenges when locked +ISLAND_LEVEL: + lockedDisplayItem: black_stained_glass_pane + +# Whether to show the name of locked challenges +showLockedChallengeName: true + +# When creating your own challenges you will have +# to uncomment the section below or the default challenges +# will be re-added on every server restart. When altering +# the default challenges this should be fine to leave. +# merge-ignore: +# - 'ranks' +# +# =============================================== +# An explanation to setup your own challenges +# can be found at the bottom of this file. +# =============================================== +ranks: + Tier1: + name: '&7Novice' + displayItem: cyan_terracotta + resetInHours: 20 + challenges: + cobblestonegenerator: + name: '&7Cobble Stone Generator' + description: Mine from a cobblestone generator. + type: onPlayer + requiredItems: + - cobblestone:64;+2 + displayItem: cobblestone + lockedDisplayItem: gray_stained_glass_pane + resetInHours: 12 + reward: + text: 3 leather, 20% chance to get a book + items: + - leather:3 + - '{p=0.2}book:1' + currency: 10 + xp: 10 + commands: + - op:effect give {player} regeneration + repeatReward: + text: 1 leather, 10% chance to get a book + items: + - leather:1 + - '{p=0.1}book:1' + currency: 5 + xp: 5 + applecollector: + name: '&6Apple Collector' + description: Collect apples from trees. + type: onPlayer + requiredItems: + - apple:2;+1 + displayItem: apple + lockedDisplayItem: brown_stained_glass_pane + resetInHours: 6 + reward: + text: 1 of each sapling, (4 dark oak) + items: + - oak_sapling:1 + - spruce_sapling:1 + - birch_sapling:1 + - jungle_sapling:1 + - acacia_sapling:1 + - dark_oak_sapling:4 + - '{p=0.2}jungle_sapling:3' + currency: 20 + xp: 20 + repeatReward: + text: 1 of each sapling (4 dark oak) + items: + - oak_sapling:1 + - spruce_sapling:1 + - birch_sapling:1 + - jungle_sapling:1 + - acacia_sapling:1 + - dark_oak_sapling:4 + - '{p=0.1}jungle_sapling:3' + currency: 10 + xp: 10 + sugarplanter: + name: '&9Sugar Planter' + type: onIsland + displayItem: sugar_cane + lockedDisplayItem: brown_stained_glass_pane + requiredItems: + - sugar_cane:4 + reward: + text: 4 dirt + items: + - dirt:4 + currency: 20 + xp: 20 + sugarfarmer: + name: '&6Sugar Farmer' + description: Harvest sugarcane from a farm. + type: onPlayer + requiredItems: + - sugar_cane:64;+16 + offset: -1 + displayItem: sugar_cane + lockedDisplayItem: brown_stained_glass_pane + reward: + text: 8 dirt + items: + - dirt:8 + - '{p=0.1}bone:1' + currency: 20 + xp: 20 + repeatReward: + text: 4 dirt + items: + - dirt:4 + - '{p=0.05}dirt:4' + currency: 10 + xp: 10 + melonfarmer: + name: '&6Melon Farmer' + description: Harvest slices of melon from a farm. + type: onPlayer + requiredItems: + - melon_slice:128;+8 + displayItem: melon + lockedDisplayItem: brown_stained_glass_pane + reward: + text: 8 dirt + items: + - dirt:8 + currency: 20 + xp: 20 + repeatReward: + text: 4 dirt + items: + - dirt:4 + currency: 10 + xp: 10 + cactusfarmer: + name: '&6Cactus Farmer' + description: Harvest cacti from a farm. + type: onPlayer + requiredItems: + - cactus:64;+16 + displayItem: cactus + lockedDisplayItem: brown_stained_glass_pane + reward: + text: 8 sand, 20% chance to get a bone + items: + - sand:8 + - '{p=0.2}bone:1' + - '{p=0.1}wooden_hoe:1' + currency: 20 + xp: 20 + repeatReward: + text: 4 sand + items: + - sand:4 + - '{p=0.1}bone:1' + currency: 10 + xp: 10 + pumpkinfarmer: + name: '&6Pumpkin Farmer' + description: Harvest pumpkins from a farm. + type: onPlayer + requiredItems: + - pumpkin:64;+4 + displayItem: pumpkin + lockedDisplayItem: brown_stained_glass_pane + reward: + text: 8 dirt + items: + - dirt:8 + - '{p=0.3}jack_o_lantern:1' + currency: 20 + xp: 20 + repeatReward: + text: 4 dirt + items: + - dirt:4 + - '{p=0.05}jack_o_lantern:1' + currency: 10 + xp: 10 + stonebrickmaker: + name: '&aStone Brick Maker' + description: Make 64 Stone Bricks. + type: onPlayer + requiredItems: + - stone_bricks:64;+8 + - stone_brick_slab:30;+6 + - chiseled_stone_bricks:30;+6 + - stone_brick_stairs:16;+4 + displayItem: stone_bricks + lockedDisplayItem: gray_stained_glass_pane + reward: + text: 4 redstone ore, 4 iron ore, 1 chicken + items: + - redstone_ore:4 + - iron_ore:4 + - chicken_spawn_egg:1 + currency: 30 + xp: 30 + repeatReward: + text: 1 redstone ore, 1 iron ore + items: + - redstone_ore:1 + - iron_ore:1 + currency: 15 + xp: 15 + novicebuilder: + name: '&7Novice Builder' + description: Reach island level 20. + type: islandLevel + requiredLevel: 20 + displayItem: coal_ore + reward: + text: 8 dirt, 8 sand, 5 emeralds + items: + - dirt:8 + - sand:8 + - emerald:5 + - diamond:1 + currency: 20 + xp: 20 + commands: + - op:effect give {party} regeneration + adeptbuilder: + name: '&aAdept Builder' + description: Reach island level 50. + type: islandLevel + requiredLevel: 50 + displayItem: iron_ore + offset: -1 + reward: + text: 10 obsidian, 2 diamonds, 5 emeralds + items: + - obsidian:10 + - emerald:5 + - diamond:2 + currency: 100 + xp: 100 + expertbuilder: + name: '&eExpert Builder' + description: Reach island level 100. + type: islandLevel + requiredLevel: 100 + displayItem: gold_ore + offset: -1 + reward: + text: 16 dirt, 16 sand, 3 diamonds, 5 emeralds + items: + - dirt:16 + - sand:16 + - emerald:5 + - diamond:3 + currency: 250 + xp: 250 + masterbuilder: + name: '&cMaster Builder' + description: Reach island level 250. + type: islandLevel + requiredLevel: 250 + displayItem: diamond_ore + offset: -1 + reward: + text: 32 dirt, 32 sand, 4 diamonds, 5 emeralds + items: + - dirt:32 + - sand:32 + - emerald:5 + - diamond:4 + currency: 500 + xp: 500 + skylord: + name: '&4Sky Lord' + description: Reach island level 500. + type: islandLevel + requiredLevel: 500 + displayItem: emerald_ore + offset: -1 + reward: + text: 64 dirt, 64 sand, 5 diamond, 5 emeralds + items: + - dirt:64 + - sand:64 + - diamond:5 + - emerald:5 + currency: 1000 + xp: 1000 + Tier2: + name: '&aAdept' + displayItem: lime_terracotta + resetInHours: 20 + requires: + + # means disabled in effect + rankLeeway: 99 + challenges: + - cobblestonegenerator + - novicebuilder + challenges: + lumberjack: + name: '&3Lumberjack' + description: Collect all types of wood logs. + type: onPlayer + requiredItems: + - oak_log:16;+2 + - spruce_log:16;+2 + - birch_log:16;+2 + - jungle_log:16;+2 + - acacia_log:16;+2 + - dark_oak_log:16;+2 + displayItem: oak_log + lockedDisplayItem: cyan_stained_glass_pane + reward: + text: 4 redstone ore, 4 iron ore, 1 wolf + items: + - redstone_ore:4 + - iron_ore:4 + - wolf_spawn_egg:1 + currency: 30 + xp: 30 + repeatReward: + text: 1 redstone ore, 1 iron ore + items: + - redstone_ore:1 + - iron_ore:1 + currency: 15 + xp: 15 + shroompicker: + name: '&6Shroom Picker' + description: Collect red and brown mushrooms. + type: onPlayer + requiredItems: + - brown_mushroom:64;+4 + - red_mushroom:64;+4 + displayItem: red_mushroom + lockedDisplayItem: brown_stained_glass_pane + reward: + text: 8 mycelium, 4 podzol + items: + - mycelium:8 + - podzol:4 + currency: 30 + xp: 30 + repeatReward: + text: 4 mycelium + items: + - mycelium:4 + - '{p=0.02}mycelium:4' + - '{p=0.02}podzol:2' + currency: 15 + xp: 15 + potatofarmer: + name: '&6Potato Farmer' + description: Harvest potato's from a farm. + type: onPlayer + requiredItems: + - potato:64;+16 + displayItem: potato + lockedDisplayItem: brown_stained_glass_pane + reward: + text: 1 carrot, 4 dirt + items: + - carrot:1 + - dirt:4 + - '{p=0.10}beetroot_seeds:1' + - '{p=0.05}red_sand:1' + - '{p=0.01}diamond:1' + currency: 50 + xp: 50 + repeatReward: + text: 4 dirt and a baked potato + items: + - dirt:4 + - baked_potato:1 + - '{p=0.10}beetroot_seeds:1' + - '{p=0.05}red_sand:1' + - '{p=0.01}diamond:1' + currency: 25 + xp: 25 + carrotfarmer: + name: '&6Carrot Farmer' + description: Harvest carrot's from a farm. + type: onPlayer + requiredItems: + - carrot:64;+16 + displayItem: carrot + lockedDisplayItem: brown_stained_glass_pane + reward: + text: a pig with saddle and a potato + items: + - saddle:1 + - potato:1 + - pig_spawn_egg:1 + - '{p=0.05}red_sand:1' + - '{p=0.01}diamond:1' + currency: 50 + xp: 50 + repeatReward: + text: 1 golden carrot + items: + - golden_carrot:1 + - '{p=0.05}sand:1' + - '{p=0.01}diamond:1' + currency: 25 + xp: 25 + wheatfarmer: + name: '&6Wheat Farmer' + description: Harvest wheat from a farm. + type: onPlayer + requiredItems: + - wheat:64;+16 + displayItem: wheat + lockedDisplayItem: brown_stained_glass_pane + reward: + text: 8 dirt + items: + - dirt:8 + currency: 20 + xp: 20 + repeatReward: + text: 4 dirt + items: + - dirt:4 + - '{p=0.2}bone:1' + currency: 10 + xp: 10 + monsterfarm: + name: '&5Monster Farm' + description: Build a mob farm and collect mob loot. + type: onPlayer + requiredItems: + - rotten_flesh:64;+4 + - string:32;+2 + - arrow:32;+2 + - bone:32;+2 + - gunpowder:16;+1 + - spider_eye:5 + displayItem: rotten_flesh + lockedDisplayItem: purple_stained_glass_pane + reward: + text: 4 redstone ore, 4 iron ore and 1 flint + items: + - redstone_ore:4 + - iron_ore:4 + - flint:1 + - '{p=0.10}potato:1' + - '{p=0.10}carrot:1' + currency: 30 + xp: 30 + repeatReward: + text: 1 redstone ore, 1 iron ore, 1 flint + items: + - redstone_ore:1 + - iron_ore:4 + - flint:1 + - '{p=0.05}potato:1' + - '{p=0.05}carrot:1' + currency: 15 + xp: 15 + homeowner: + name: '&9Home Owner' + description: Build a house with furnishings. + type: onIsland + requiredChallenges: + - stonebrickmaker + requiredItems: + - red_bed:1 + - crafting_table:1 + - glass:1 + - oak_door:1 + - furnace:1 + - bookshelf:1 + - torch:1 + lockedDisplayItem: blue_stained_glass_pane + displayItem: oak_door + reward: + text: 4 redstone ore, 5 inksac, 4 iron ore and some seeds + items: + - redstone_ore:4 + - iron_ore:4 + - ink_sac:5 + - beetroot_seeds:1 + currency: 40 + xp: 40 + netherportal: + name: '&9Nether Portal' + description: Build a nether portal on your island. + type: onIsland + requiredChallenges: + - adeptbuilder + - homeowner + requiredItems: + - obsidian:10 + - nether_portal:1 + displayItem: obsidian + lockedDisplayItem: blue_stained_glass_pane + reward: + text: 1 iron pickaxe, 1 iron shovel + items: + - iron_shovel:1 + - iron_pickaxe:1 + currency: 40 + xp: 40 + nethermining: + name: '&7Nether Mining' + description: Mine from your Nether island. + type: onPlayer + displayItem: netherrack + lockedDisplayItem: gray_stained_glass_pane + offset: -1 + requiredItems: + - netherrack:64;+2 + - soul_sand:16;+2 + - gravel:16;+2 + - quartz:32;+2 + - glowstone:16;+2 + - coarse_dirt:4;+2 + reward: + text: 1 ghast tear, 1 magic bow + items: + - ghast_tear:1 + - 'bow[enchantments={levels:{infinity:1,unbreaking:3}}]:1' + currency: 40 + xp: 40 + repeatReward: + text: a blaze-rod and a chance of ghast tear + items: + - blaze_rod:1 + - '{p=0.10}ghast_tear:1' + currency: 20 + xp: 20 + Tier3: + name: '&eExpert' + displayItem: yellow_terracotta + resetInHours: 20 + requires: + challenges: + - adeptbuilder + challenges: + toolmaker: + name: '&3Tool Maker' + description: Make all stone tools + type: onPlayer + requiredChallenges: + - lumberjack + requiredItems: + - stone_shovel:1 + - stone_pickaxe:1 + - stone_axe:1 + - stone_hoe:1 + displayItem: stone_axe + lockedDisplayItem: cyan_stained_glass_pane + reward: + text: 4 redstone ore, 4 iron, 1 pig + items: + - redstone_ore:4 + - iron_ore:4 + - pig_spawn_egg:1 + currency: 30 + xp: 30 + sawmill: + name: '&3Saw Mill' + description: Deliver a collection of processed wood + type: onPlayer + offset: -1 + requiredChallenges: + - toolmaker + requiredItems: + - stripped_oak_log:8;+2 + - stripped_spruce_log:8;+2 + - stripped_birch_log:8;+2 + - stripped_jungle_log:8;+2 + - stripped_acacia_log:8;+2 + - stripped_dark_oak_log:8;+2 + displayItem: iron_axe + lockedDisplayItem: cyan_stained_glass_pane + reward: + text: 4 redstone ore, 4 iron, cocoa and a mule + items: + - cocoa_beans:1 + - redstone_ore:4 + - iron_ore:4 + - mule_spawn_egg:1 + currency: 30 + xp: 30 + repeatReward: + text: 1 redstone ore, 1 iron ore + items: + - redstone_ore:1 + - iron_ore:1 + - '{p=0.05}gold_ore:1' + currency: 15 + xp: 15 + torchmaker: + name: '&3Torch Maker' + description: Make 128 torches. + type: onPlayer + requiredChallenges: + - lumberjack + requiredItems: + - torch:128;+32 + displayItem: torch + lockedDisplayItem: cyan_stained_glass_pane + reward: + text: 4 redstone ore, 4 iron ore + items: + - redstone_ore:4 + - iron_ore:4 + currency: 30 + xp: 30 + repeatReward: + text: 1 redstone ore, 1 iron ore + items: + - redstone_ore:1 + - iron_ore:1 + currency: 15 + xp: 15 + expertfarmer: + name: '&6Expert Farmer' + description: Harvest many different farming resources. + type: onPlayer + displayItem: iron_hoe + lockedDisplayItem: brown_stained_glass_pane + requiredChallenges: + - applecollector + - melonfarmer + - pumpkinfarmer + - wheatfarmer + - carrotfarmer + - potatofarmer + - cactusfarmer + - sugarfarmer + - shroompicker + requiredItems: + - melon_slice:256;+2 + - sugar_cane:128;+2 + - wheat:128;+2 + - potato:128;+2 + - carrot:128;+2 + - pumpkin:128;+2 + - cactus:128;+2 + - beetroot:128;+2 + reward: + text: a bucket, cocoa and a cow + items: + - bucket:1 + - cocoa_beans:1 + - cow_spawn_egg:1 + currency: 50 + xp: 50 + repeatReward: + text: a cow + items: + - cow_spawn_egg:1 + currency: 25 + xp: 25 + fisherman: + name: '&5Fisherman' + description: Catch different types of fish. + type: onPlayer + requiredItems: + - cod:5;+1 + - salmon:5;+1 + - pufferfish:3;+1 + - tropical_fish:1;+0 + - ink_sac:5;+2 + displayItem: cod + lockedDisplayItem: purple_stained_glass_pane + reward: + text: 10 lapis blocks, 20 prismarine, kelp, deep-ocean + items: + - lapis_block:10 + - prismarine:20 + - kelp:1 + - '{p=0.20}prismarine_crystals:5' + permission: usb.biome.deep_ocean + currency: 50 + xp: 50 + repeatReward: + text: 2 lapis, 5 prismarine, kelp and a chance at prismarine crystals + items: + - lapis_lazuli:2 + - prismarine:5 + - kelp:1 + - '{p=0.20}prismarine_crystals:5' + currency: 25 + xp: 25 + woolcollector: + name: '&5Wool Collector' + description: Collect every color of wool. + type: onPlayer + displayItem: white_wool + lockedDisplayItem: purple_stained_glass_pane + requiredChallenges: + - monsterfarmer + requiredItems: + - white_wool:2;+4 + - orange_wool:2;+4 + - magenta_wool:2;+4 + - light_blue_wool:2;+4 + - yellow_wool:2;+4 + - lime_wool:2;+4 + - pink_wool:2;+4 + - gray_wool:2;+4 + - light_gray_wool:2;+4 + - cyan_wool:2;+4 + - purple_wool:2;+4 + - blue_wool:2;+4 + - brown_wool:2;+4 + - green_wool:2;+4 + - red_wool:2;+4 + - black_wool:2;+4 + reward: + text: 2 diamonds, sheep, emerald, flowers + items: + - diamond:2 + - emerald:1 + - sheep_spawn_egg:1 + - rose_bush:1 + - peony:1 + permission: usb.biome.flower_forest + currency: 70 + xp: 70 + repeatReward: + text: emerald, sheep, flowers + items: + - emerald:1 + - sheep_spawn_egg:1 + - rose_bush:1 + - peony:1 + currency: 35 + xp: 35 + maestro: + name: '&5Maestro' + description: Make a jukebox and collect all music discs. + type: onPlayer + requiredItems: + - music_disc_13:1 + - music_disc_cat:1 + - music_disc_blocks:1 + - music_disc_chirp:1 + - music_disc_far:1 + - music_disc_mall:1 + - music_disc_mellohi:1 + - music_disc_stal:1 + - music_disc_strad:1 + - music_disc_ward:1 + - music_disc_11:1 + - music_disc_wait:1 + - jukebox:1 + displayItem: jukebox + lockedDisplayItem: purple_stained_glass_pane + reward: + text: 3 diamonds, 1 gold block, 10 emeralds + items: + - diamond:3 + - emerald:10 + - gold_block:1 + currency: 70 + xp: 70 + repeatReward: + text: 2 gold ore, 1 diamond + items: + - gold_ore:2 + - diamond:1 + - '{p=0.2}diamond:1' + currency: 35 + xp: 35 + ironfarm: + name: '&9Iron Farm' + description: Build an iron-farm. + type: onIsland + displayItem: iron_block + lockedDisplayItem: blue_stained_glass_pane + radius: 50 + requiredChallenges: + - monsterfarm + - applecollector + requiredItems: + - oak_door:30 + requiredEntities: + - Villager:10 + - IRON_GOLEM:1 + reward: + text: 2 gold blocks, 1 diamond block + items: + - gold_block:2 + - diamond_block:1 + currency: 500 + xp: 400 + netherfortress: + name: '&9Nether Fortress' + description: Build a netherfortress. + type: onIsland + radius: 50 + requiredItems: + - nether_bricks:512 + - nether_brick_slab:64 + - nether_brick_fence:64 + - nether_brick_stairs:64 + - soul_sand:32 + displayItem: nether_brick_fence + lockedDisplayItem: blue_stained_glass_pane + reward: + text: a wither-skull and a blaze rod and a chance of diamonds + items: + - wither_skeleton_skull:1 + - blaze_rod:1 + - '{p=0.05}diamond:3' + - '{p=0.10}diamond:2' + - '{p=0.15}diamond:1' + currency: 40 + xp: 40 + # =============================================== + Tier4: + name: '&cMaster' + displayItem: orange_terracotta + resetInHours: 20 + requires: + + # disabled + rankLeeway: 99 + challenges: + - expertbuilder + challenges: + glassmaker: + name: '&3Glassmaker' + description: Collect every color of stained glass. + type: onPlayer + displayItem: white_stained_glass + lockedDisplayItem: cyan_stained_glass_pane + requiredItems: + - white_stained_glass:8;+2 + - orange_stained_glass:8;+2 + - magenta_stained_glass:8;+2 + - light_blue_stained_glass:8;+2 + - yellow_stained_glass:8;+2 + - lime_stained_glass:8;+2 + - pink_stained_glass:8;+2 + - gray_stained_glass:8;+2 + - light_gray_stained_glass:8;+2 + - cyan_stained_glass:8;+2 + - purple_stained_glass:8;+2 + - blue_stained_glass:8;+2 + - brown_stained_glass:8;+2 + - green_stained_glass:8;+2 + - red_stained_glass:8;+2 + - black_stained_glass:8;+2 + reward: + text: 2 diamonds, 2 disks, 1 emeralds + items: + - diamond:2 + - music_disc_ward:1 + - music_disc_11:1 + - emerald:1 + - sunflower:1 + - lilac:1 + permission: usb.biome.flower_forest + currency: 70 + xp: 70 + repeatReward: + text: 30% chance on 1 or 2 disks, 1 emeralds + items: + - '{p=0.3}music_disc_ward:1' + - '{p=0.3}music_disc_11:1' + - emerald:1 + - sunflower:1 + - lilac:1 + currency: 35 + xp: 35 + carpenter: + name: '&3Carpenter' + description: Collect all types of wood items. + type: onPlayer + requiredItems: + - jungle_stairs:16;+8 + - spruce_door:1;+1 + - dark_oak_fence_gate:2;+2 + - acacia_fence:30;+15 + - ladder:30;+15 + - oak_trapdoor:4;+2 + - oak_pressure_plate:2;+1 + requiredChallenges: + - toolmaker + - lumberjack:2 + displayItem: crafting_table + lockedDisplayItem: cyan_stained_glass_pane + reward: + text: a parrot and a jukebox with some redstone and iron + items: + - parrot_spawn_egg:1 + - jukebox:1 + - redstone_ore:4 + - iron_ore:4 + currency: 30 + xp: 30 + repeatReward: + text: redstone, iron and a slim chance at a parrot + items: + - redstone_ore:1 + - iron_ore:1 + - '{p=0.25}jukebox:1' + - '{p=0.05}parrot_spawn_egg:1' + currency: 15 + xp: 15 + cookielover: + name: '&eCookie Lover' + description: Make cookies and a bucket of milk. + type: onPlayer + requiredChallenges: + - expertfarmer + requiredItems: + - cookie:128;+4 + - milk_bucket:1 + displayItem: cookie + lockedDisplayItem: yellow_stained_glass_pane + reward: + text: 4 redstone ore, clay a bucket and a diamond + items: + - redstone_ore:4 + - clay:5 + - diamond:1 + - bucket:1 + currency: 80 + xp: 80 + repeatReward: + text: iron ore and some clay + items: + - iron_ore:1 + - clay:5 + - bucket:1 + - '{p=0.4}clay:3' + - '{p=0.3}iron_ore:2' + currency: 40 + xp: 40 + deepseafisherman: + name: '&5Deep Sea Fishing' + description: Farm the deep sea + type: onPlayer + requiredItems: + - cod:10;+5 + - salmon:10;+5 + - pufferfish:5;+3 + - tropical_fish:3;+2 + - prismarine_crystals:16;+8 + - prismarine_shard:16;+8 + - dried_kelp_block:64;+32 + - nautilus_shell:1 + displayItem: heart_of_the_sea + lockedDisplayItem: purple_stained_glass_pane + reward: + text: heart-of-the-sea, nautilus shell and a turtle + items: + - nautilus_shell:1 + - heart_of_the_sea:1 + - turtle_spawn_egg:1 + - '{p=0.1}nautilus_shell:1' + permission: usb.biome.deep_ocean + currency: 50 + xp: 50 + repeatReward: + text: nautilus shell, turtle and a chance of a heart + items: + - nautilus_shell:1 + - turtle_spawn_egg:1 + - '{p=0.05}heart_of_the_sea:1' + - '{p=0.1}nautilus_shell:1' + currency: 25 + xp: 25 + horsingaround: + name: '&6Horsing Around' + description: Get hay bales for the horses. + type: onPlayer + requiredItems: + - hay_block:32;+4 + - lead:8;+2 + - carrot_on_a_stick:1 + - shears:1 + requirecChallenges: + - wheatfarmer + displayItem: hay_block + lockedDisplayItem: brown_stained_glass_pane + reward: + text: 1 horse, 1 iron horse armor, 5% chance on diamond horse armor + items: + - iron_horse_armor:1 + - horse_spawn_egg:1 + - '{p=0.05}diamond_horse_armor:1' + currency: 50 + xp: 50 + repeatReward: + text: 1 redstone ore, 1 emerald + items: + - redstone:1 + - emerald:1 + currency: 25 + xp: 25 + slimefarmer: + name: '&5Slime Farmer' + description: Collect slimeballs from slimes. + type: onPlayer + requiredItems: + - slime_ball:64;+4 + displayItem: slime_ball + lockedDisplayItem: purple_stained_glass_pane + reward: + text: 1 diamond, 1 emeralds, swampland + items: + - diamond:1 + - emerald:1 + permission: usb.biome.swamp + currency: 70 + xp: 70 + repeatReward: + text: 1 redstone ore, 1 emeralds + items: + - redstone_ore:1 + - emerald:1 + currency: 35 + xp: 35 + animalfarm: + name: '&9Animal Farm' + description: Create an animal farm. + type: onIsland + displayItem: oak_fence + radius: 40 + requiredChallenges: + - woolcollector + - potatofarmer + - carrotfarmer + requiredEntities: + - Cow:8 + - Pig:8 + - Chicken:16 + - Sheep:{"Color":"WHITE"} + - Sheep:{"Color":"ORANGE"} + - Sheep:{"Color":"MAGENTA"} + - Sheep:{"Color":"LIGHT_BLUE"} + - Sheep:{"Color":"YELLOW"} + - Sheep:{"Color":"LIME"} + - Sheep:{"Color":"PINK"} + - Sheep:{"Color":"GRAY"} + - Sheep:{"Color":"LIGHT_GRAY"} + - Sheep:{"Color":"CYAN"} + - Sheep:{"Color":"PURPLE"} + - Sheep:{"Color":"BLUE"} + - Sheep:{"Color":"BROWN"} + - Sheep:{"Color":"GREEN"} + - Sheep:{"Color":"RED"} + - Sheep:{"Color":"BLACK"} + reward: + text: 1 horse, 1 iron horse armor, 5% chance horse armor + items: + - iron_horse_armor:1 + - horse_spawn_egg:1 + - '{p=0.05}iron_horse_armor:1' + - '{p=0.05}golden_horse_armor:1' + - '{p=0.05}diamond_horse_armor:1' + currency: 50 + xp: 50 + repeatReward: + text: 1 redstone ore, 1 emerald, 5% chance horse armor + items: + - redstone:1 + - emerald:1 + - '{p=0.05}iron_horse_armor:1' + - '{p=0.05}golden_horse_armor:1' + - '{p=0.05}diamond_horse_armor:1' + currency: 25 + xp: 25 + witherhunter: + name: '&5Wither Hunter' + description: Collect some Wither Skeleton skulls. + type: onPlayer + requiredChallenges: + - netherfortress + requiredItems: + - wither_skeleton_skull:10;+1 + displayItem: wither_skeleton_skull + lockedDisplayItem: purple_stained_glass_pane + offset: -1 + reward: + text: 5 diamonds, 5% chance of nether star + items: + - diamond:5 + - '{p=0.05}nether_star:1' + currency: 400 + xp: 400 + repeatReward: + text: 2 gold ore, 5% chance of nether star + items: + - gold_ore:2 + - '{p=0.05}nether_star:1' + currency: 20 + xp: 20 + pearlcollector: + name: '&5Pearl Collector' + description: Collect enderpearls from endermen. + type: onPlayer + requiredItems: + - ender_pearl:10;+4 + displayItem: ender_pearl + lockedDisplayItem: purple_stained_glass_pane + reward: + text: 5 gold ore, 1 blaze rod, 1 emerald + items: + - gold_ore:5 + - blaze_rod:1 + - emerald:1 + currency: 70 + xp: 70 + repeatReward: + text: 1 gold ore, 1 blaze rod + items: + - gold_ore:1 + - blaze_rod:1 + currency: 35 + xp: 35 + + # =============================================== + Tier5: + name: '&4Sky Lord' + displayItem: red_terracotta + resetInHours: 48 + requires: + challenges: + - masterbuilder + challenges: + technician: + name: '&3Technician' + description: Collect some of every type of redstone equipment. + type: onPlayer + requiredItems: + - redstone:64;+16 + - redstone_torch:32;+4 + - repeater:5;+1 + - comparator:3;+1 + - piston:2;+1 + - sticky_piston:2;+1 + - lever:1;+1 + - stone_button:1;+1 + - stone_pressure_plate:1;+1 + - hopper:1;+1 + - dispenser:1;+1 + - dropper:1;+1 + - daylight_detector:1;+1 + displayItem: redstone + lockedDisplayItem: cyan_stained_glass_pane + reward: + text: some snow, ice and packed ice + items: + - snow_block:4 + - ice:8 + - packed_ice:8 + - redstone_block:64 + currency: 70 + xp: 70 + repeatReward: + text: a stack of redstone blocks and some ice + items: + - redstone_block:64 + - ice:4 + - packed_ice:1 + currency: 35 + xp: 35 + emeraldcollector: + name: '&5Emerald Collector' + description: Collect emeralds. + type: onPlayer + requiredItems: + - emerald:60;+10 + displayItem: emerald + lockedDisplayItem: purple_stained_glass_pane + reward: + text: a full set of diamond armor + items: + - diamond_helmet:1 + - diamond_chestplate:1 + - diamond_leggings:1 + - diamond_boots:1 + currency: 70 + xp: 70 + repeatReward: + text: full diamond armor + items: + - diamond_helmet:1 + - diamond_chestplate:1 + - diamond_leggings:1 + - diamond_boots:1 + currency: 35 + xp: 35 + topchef: + name: '&eTop Chef' + description: Collect every kind of edible food. + type: onPlayer + requiredItems: + - baked_potato:1 + - bread:1 + - cake:1 + - cooked_chicken:1 + - cooked_cod:1 + - cooked_salmon:1 + - tropical_fish:1 + - cooked_porkchop:1 + - cookie:1 + - golden_apple:1 + - golden_carrot:1 + - mushroom_stew:1 + - pumpkin_pie:1 + - cooked_beef:1 + - melon:1 + - carrot:1 + requiredChallenges: + - expertfarmer + - fisherman + - cookielover + displayItem: carrot + lockedDisplayItem: yellow_stained_glass_pane + reward: + text: 2 diamond, 1 mooshroom + items: + - diamond:2 + - mooshroom_spawn_egg:1 + currency: 80 + xp: 80 + repeatReward: + text: 1 diamond, 1 mooshroom + items: + - diamond:1 + - mooshroom_spawn_egg:1 + currency: 40 + xp: 40 + tajmahal: + name: '&9Taj Mahal' + description: Build a temple of quartz + type: onIsland + radius: 30 + displayItem: quartz_block + lockedDisplayItem: blue_stained_glass_pane + requiredChallenges: + - nethermining + requiredItems: + - quartz_block:512 + - chiseled_quartz_block:64 + - quartz_pillar:128 + - quartz_stairs:64 + - quartz_slab:192 + reward: + text: 3 end-portal frames, red sand + items: + - end_portal_frame:3 + - red_sand:64 + currency: 1000 + xp: 500 + greatpyramid: + name: '&9Great Pyramid' + description: Build a pyramid of sandstone + type: onIsland + radius: 30 + displayItem: sandstone + lockedDisplayItem: blue_stained_glass_pane + requiredItems: + - sandstone:512 + - chiseled_sandstone:64 + - smooth_sandstone:128 + - sandstone_stairs:64 + - sandstone_slab:192 + - red_sandstone:16 + reward: + text: 3 end-portal frames, 3 diamonds, bow + items: + - end_portal_frame:3 + - diamond:3 + - 'bow[enchantments={levels:{infinity:1,power:5,unbreaking:3}}]:1' + currency: 1000 + xp: 500 + poseidonshalls: + name: '&9Poseidon''s Halls' + description: Build the halls of Poseidon + type: onIsland + radius: 50 + displayItem: prismarine + lockedDisplayItem: blue_stained_glass_pane + requiredChallenges: + - deepseafisherman + requiredItems: + - prismarine:512 + - prismarine_bricks:128 + - dark_prismarine:64 + - sea_lantern:32 + - conduit:1 + - flower_pot:16 + - vine:128 + - poppy:10 + - blue_orchid:10 + - allium:10 + - azure_bluet:10 + - red_tulip:10 + - orange_tulip:10 + - white_tulip:10 + - pink_tulip:10 + - oxeye_daisy:10 + - sunflower:10 + - lilac:10 + - rose_bush:10 + - peony:10 + reward: + text: 3 end-portal frames, 5 diamonds + items: + - end_portal_frame:3 + - diamond:5 + currency: 1000 + xp: 500 + beaconator: + name: '&9Beaconator' + description: Build a very expensive beacon. + type: onIsland + radius: 15 + displayItem: beacon + lockedDisplayItem: blue_stained_glass_pane + requiredChallenges: + - netherfortress + - ironfarm + - maestro + - witherhunter + requiredItems: + - beacon:1 + - diamond_block:1 + - emerald_block:8 + - gold_block:25 + - iron_block:49 + reward: + items: + - end_portal_frame:3 + - 'diamond_sword[enchantments={levels:{fire_aspect:1,sharpness:5,unbreaking:3}}]:1' + text: some end-portal-pieces and a magic diamond sword + currency: 4000 + xp: 1000 + endportal: + name: '&9End Portal' + description: Build and activate an end-portal. + type: onIsland + radius: 15 + displayItem: end_portal_frame + lockedDisplayItem: blue_stained_glass_pane + requiredChallenges: + - beaconator + - tajmahal + - poseidonshalls + - greatpyramid + requiredItems: + - end_portal_frame:12 + - end_portal:9 + reward: + text: 5 diamonds and some nice boots + items: + - diamond:5 + - 'diamond_boots[enchantments={levels:{blast_protection:4,feather_falling:4,protection:4}}]:1' + currency: 4000 + xp: 1000 + + # =============================================== + Tier6: + name: '&aWorld Foods' + displayItem: light_gray_terracotta + resetInHours: 20 + requires: + challenges: + - masterbuilder + - topchef + challenges: + fishandchips: + name: '&eFish & Chips' + description: Create the famous English Fish & Chips. + type: onPlayer + requiredItems: + - cooked_cod:32;+8 + - baked_potato:32;+8 + displayItem: cooked_cod + lockedDisplayItem: yellow_stained_glass_pane + reward: + text: 16 baked potatos, 1 disk, 4 emerald + items: + - baked_potato:16 + - music_disc_13:1 + - emerald:4 + currency: 60 + xp: 60 + commands: + - 'console: msg {player} &9https://en.wikipedia.org/wiki/Fish_and_chips + &2Click the link for more info' + repeatReward: + text: 30% chance on 1 disk, 1 emerald + items: + - emerald:1 + - '{p=0.3}music_disc_13:1' + currency: 30 + xp: 30 + commands: + - 'console: msg {player} &9https://en.wikipedia.org/wiki/Fish_and_chips + &2Click the link for more info' + smorrebrod: + name: '&eSmørrebrød' + description: Create the famous Danish smørrebrød. + type: onPlayer + requiredItems: + - bread:16;+4 + - cooked_salmon:8;+4 + - tropical_fish:2;+1 + - cooked_porkchop:8;+4 + - cooked_beef:8;+4 + displayItem: cooked_cod + lockedDisplayItem: yellow_stained_glass_pane + reward: + text: 1 rod, 2 name tags, 2 ocelots + items: + - fishing_rod:1 + - name_tag:2 + - ocelot_spawn_egg:2 + currency: 60 + xp: 60 + commands: + - 'console: msg {player} &9https://en.wikipedia.org/wiki/Danish_cuisine + &2Click the link for more info' + repeatReward: + text: 1 emerald + items: + - emerald:1 + currency: 30 + xp: 30 + commands: + - 'console: msg {player} &9https://en.wikipedia.org/wiki/Danish_cuisine + &2Click the link for more info' + hutspot: + name: '&eHutspot' + description: Create the famous Dutch hutspot. + type: onPlayer + requiredItems: + - potato:64;+8 + - carrot:64;+8 + lockedDisplayItem: yellow_stained_glass_pane + displayItem: golden_carrot + reward: + text: 1 golden carrot, 1 disk, 4 emerald + items: + - golden_carrot:1 + - music_disc_cat:1 + - emerald:4 + currency: 60 + xp: 60 + commands: + - 'console: msg {player} &9https://en.wikipedia.org/wiki/Hutspot &2Click + the link for more info' + repeatReward: + text: 30% chance on 1 disk, 1 emerald + items: + - emerald:1 + - '{p=0.3}music_disc_cat:1' + currency: 30 + xp: 30 + commands: + - 'console: msg {player} &9https://en.wikipedia.org/wiki/Hutspot &2Click + the link for more info' + apfelstrudel: + name: '&eApfelstrudele' + description: Create the famous German apfelstrudel. + type: onPlayer + requiredItems: + - apple:8;+5 + - wheat:16;+5 + - sugar:16;+5 + - milk_bucket:1 + displayItem: golden_apple + lockedDisplayItem: yellow_stained_glass_pane + reward: + text: 1 bucket, 1 disk, 4 emerald + items: + - bucket:1 + - emerald:4 + - music_disc_blocks:1 + currency: 60 + xp: 60 + commands: + - 'console: msg {player} &9https://en.wikipedia.org/wiki/Apple_strudel &2Click + the link for more info' + repeatReward: + text: 1 bucket, 30% chance on 1 disk, 1 emerald + items: + - bucket:1 + - emerald:1 + - '{p=0.3}music_disc_blocks:1' + currency: 30 + xp: 30 + commands: + - 'console: msg {player} &9https://en.wikipedia.org/wiki/Apple_strudel &2Click + the link for more info' + brownies: + name: '&eBrownies' + description: Create the famous American brownies. + type: onPlayer + requiredItems: + - wheat:16;+5 + - sugar:16;+5 + - egg:16;+5 + - ink_sac:16;+5 + - milk_bucket:1 + displayItem: dark_oak_slab + lockedDisplayItem: yellow_stained_glass_pane + reward: + text: 1 bucket, 1 disk, 4 emerald + items: + - bucket:1 + - emerald:4 + - music_disc_far:1 + currency: 60 + xp: 60 + commands: + - 'console: msg {player} &9https://en.wikipedia.org/wiki/Chocolate_brownie + &2Click the link for more info' + repeatReward: + text: 1 bucket, 30% chance on 1 disk, 1 emerald + items: + - bucket:1 + - emerald:1 + - '{p=0.3}music_disc_far:1' + currency: 30 + xp: 30 + commands: + - 'console: msg {player} &9https://en.wikipedia.org/wiki/Chocolate_brownie + &2Click the link for more info' + pastafunghi: + name: '&ePasta Funghi' + description: Create the famous Italian Pasta Funghi. + type: onPlayer + requiredItems: + - wheat:32;+8 + - egg:32;+8 + - brown_mushroom:16;+4 + - red_mushroom:16;+4 + - milk_bucket:1 + displayItem: red_mushroom_block + lockedDisplayItem: yellow_stained_glass_pane + reward: + text: 2 rabbits, 1 bucket + items: + - bucket:1 + - rabbit_spawn_egg:2 + currency: 60 + xp: 60 + commands: + - console:msg {player} &9https://en.wikipedia.org/wiki/Pasta &2Click the + link for more info + repeatReward: + text: 30% chance on 1 disk, 1 emerald + items: + - emerald:1 + - '{p=0.3}music_disc_13:1' + currency: 30 + xp: 30 + commands: + - console:msg {player} &9https://en.wikipedia.org/wiki/Pasta &2Click the + link for more info + chocolate: + name: '&eBelgian chocolate' + description: Create the famous belgian chocolate. + type: onPlayer + requiredItems: + - sugar:16;+5 + - ink_sac:16;+5 + - milk_bucket:1 + displayItem: pig_spawn_egg + lockedDisplayItem: yellow_stained_glass_pane + reward: + text: 1 bucket, 1 disk, 4 emeralds + items: + - bucket:1 + - emerald:4 + - music_disc_mall:1 + currency: 60 + xp: 60 + commands: + - 'console: msg {player} &9https://en.wikipedia.org/wiki/Belgian_chocolate + &2Click the link for more info' + repeatReward: + text: 1 bucket, 30% chance on 1 disk, 1 emerald + items: + - bucket:1 + - emerald:1 + - '{p=0.3}music_disc_mall:1' + currency: 30 + xp: 30 + commands: + - 'console: msg {player} &9https://en.wikipedia.org/wiki/Belgian_chocolate + &2Click the link for more info' + baker: + name: '&ePâtisserie' + description: Bake cakes, pumpkin pies, and cookies. + type: onPlayer + requiredItems: + - cake:5;+1 + - pumpkin_pie:5;+1 + - cookie:128;+4 + displayItem: cake + lockedDisplayItem: yellow_stained_glass_pane + reward: + text: 5 gold ore, 1 diamond + items: + - gold_ore:5 + - diamond:1 + currency: 80 + xp: 80 + repeatReward: + text: 2 iron ore, 1 gold ore + items: + - iron_ore:2 + - gold_ore:1 + currency: 40 + xp: 40 + +# This file has been updated to version 107. Please check the changes made in this version. +# Changes in this version: +# - Items are now specified in the new component format. +# - Refer to the config header for the new format. +# - NBT tags are not automatically converted. They have been moved to the comments, please check them manually. +# You can use the following converter to convert the old item specifications to the new format: +# https://docs.papermc.io/misc/tools/item-command-converter +# DO NOT CHANGE THE VERSION! You will break the conversion and unexpected things will happen! +version: 107 diff --git a/uSkyBlock-Core/src/test/resources/us/talabrek/ultimateskyblock/imports/old-default-challenges.yml b/uSkyBlock-Core/src/test/resources/us/talabrek/ultimateskyblock/imports/old-default-challenges.yml new file mode 100644 index 000000000..2f6d9f62e --- /dev/null +++ b/uSkyBlock-Core/src/test/resources/us/talabrek/ultimateskyblock/imports/old-default-challenges.yml @@ -0,0 +1,1767 @@ +# [true/false] Enable the use of the challenges command. +allowChallenges: true + +# [island/player] Whether challenges are tracked per player, or per island +challengeSharing: island + +# [true/false] If true, first time challenge completions are broadcast to the whole server. +broadcastCompletion: true + +# [text] The color/formatting of the broadcast text when showing first time completions. +broadcastText: '&6' + +# [true/false] If true, challenges in higher level ranks require challenges in lower level ranks to be completed. +requirePreviousRank: true + +# [integer] The number of tasks per rank that can be left uncompleted to advance to the next rank. For example, if you have 4 easy challenges +# with a rankLeeway of 1, a player would only need to complete 3 to advance to the next rank. +# A rankLeeway of 0 would require them all. +rankLeeway: 12 + +#[integer] The time in hours before required items reset to default. (only if not specified in the challenges below) +defaultResetInHours: 20 + +#[color code] The color to use for uncompleted challenges in the list. +challengeColor: '&e' + +#[color code] The color to use for completed challenges in the list. (non-repeatable) +finishedColor: '&2' + +#[color code] The color to use for completed challenges in the list. (repeatable) +repeatableColor: '&a' + +#[true/false] If true, enables vault to handle currency rewards. +enableEconomyPlugin: true + +# [true/false] If false, challenges are not reset on island creation (or restart) +resetChallengesOnCreate: true + +# Material to show for locked challenges (i.e. STAINED_GLASS_PANE:14 for a red glass-pane, or 160:14) +lockedDisplayItem: RED_STAINED_GLASS_PANE + +# Material to show for onIsland challenges when locked +ISLAND: + lockedDisplayItem: BLUE_STAINED_GLASS_PANE + +# Material to show for islandLevel challenges when locked +ISLAND_LEVEL: + lockedDisplayItem: BLACK_STAINED_GLASS_PANE + +# Whether to show the name of locked challenges +showLockedChallengeName: true + +# When creating your own challenges you will have +# to uncomment the section below or the default challenges +# will be re-added on every server restart. When altering +# the default challenges this should be fine to leave. +#merge-ignore: +# - 'ranks' +# +#=============================================== +# An explanation to setup your own challenges +# can be found at the bottom of this file. +#=============================================== +ranks: + Tier1: + name: '&7Novice' + displayItem: CYAN_TERRACOTTA + resetInHours: 20 + challenges: + cobblestonegenerator: + name: '&7Cobble Stone Generator' + description: Mine from a cobblestone generator. + type: onPlayer + requiredItems: + - COBBLESTONE:64;+2 + displayItem: COBBLESTONE + lockedDisplayItem: GRAY_STAINED_GLASS_PANE + resetInHours: 12 + reward: + text: 3 leather, 20% chance to get a book + items: + - LEATHER:3 + - '{p=0.2}BOOK:1' + currency: 10 + xp: 10 + commands: + - op:effect give {player} regeneration + repeatReward: + text: 1 leather, 10% chance to get a book + items: + - LEATHER:1 + - '{p=0.1}BOOK:1' + currency: 5 + xp: 5 + applecollector: + name: '&6Apple Collector' + description: Collect apples from trees. + type: onPlayer + requiredItems: + - APPLE:2;+1 + displayItem: APPLE + lockedDisplayItem: BROWN_STAINED_GLASS_PANE + resetInHours: 6 + reward: + text: 1 of each sapling, (4 dark oak) + items: + - OAK_SAPLING:1 + - SPRUCE_SAPLING:1 + - BIRCH_SAPLING:1 + - JUNGLE_SAPLING:1 + - ACACIA_SAPLING:1 + - DARK_OAK_SAPLING:4 + - '{p=0.2}JUNGLE_SAPLING:3' + currency: 20 + xp: 20 + repeatReward: + text: 1 of each sapling (4 dark oak) + items: + - OAK_SAPLING:1 + - SPRUCE_SAPLING:1 + - BIRCH_SAPLING:1 + - JUNGLE_SAPLING:1 + - ACACIA_SAPLING:1 + - DARK_OAK_SAPLING:4 + - '{p=0.1}JUNGLE_SAPLING:3' + currency: 10 + xp: 10 + sugarplanter: + name: '&9Sugar Planter' + type: onIsland + displayItem: SUGAR_CANE + lockedDisplayItem: BROWN_STAINED_GLASS_PANE + requiredItems: + - SUGAR_CANE:4 + reward: + text: 4 dirt + items: + - DIRT:4 + currency: 20 + xp: 20 + sugarfarmer: + name: '&6Sugar Farmer' + description: Harvest sugarcane from a farm. + type: onPlayer + requiredItems: + - SUGAR_CANE:64;+16 + offset: -1 + displayItem: SUGAR_CANE + lockedDisplayItem: BROWN_STAINED_GLASS_PANE + reward: + text: 8 dirt + items: + - DIRT:8 + - '{p=0.1}BONE:1' + currency: 20 + xp: 20 + repeatReward: + text: 4 dirt + items: + - DIRT:4 + - '{p=0.05}DIRT:4' + currency: 10 + xp: 10 + melonfarmer: + name: '&6Melon Farmer' + description: Harvest slices of melon from a farm. + type: onPlayer + requiredItems: + - MELON_SLICE:128;+8 + displayItem: MELON + lockedDisplayItem: BROWN_STAINED_GLASS_PANE + reward: + text: 8 dirt + items: + - DIRT:8 + currency: 20 + xp: 20 + repeatReward: + text: 4 dirt + items: + - DIRT:4 + currency: 10 + xp: 10 + cactusfarmer: + name: '&6Cactus Farmer' + description: Harvest cacti from a farm. + type: onPlayer + requiredItems: + - CACTUS:64;+16 + displayItem: CACTUS + lockedDisplayItem: BROWN_STAINED_GLASS_PANE + reward: + text: 8 sand, 20% chance to get a bone + items: + - SAND:8 + - '{p=0.2}BONE:1' + - '{p=0.1}WOODEN_HOE:1' + currency: 20 + xp: 20 + repeatReward: + text: 4 sand + items: + - SAND:4 + - '{p=0.1}BONE:1' + currency: 10 + xp: 10 + pumpkinfarmer: + name: '&6Pumpkin Farmer' + description: Harvest pumpkins from a farm. + type: onPlayer + requiredItems: + - PUMPKIN:64;+4 + displayItem: PUMPKIN + lockedDisplayItem: BROWN_STAINED_GLASS_PANE + reward: + text: 8 dirt + items: + - DIRT:8 + - '{p=0.3}JACK_O_LANTERN:1' + currency: 20 + xp: 20 + repeatReward: + text: 4 dirt + items: + - DIRT:4 + - '{p=0.05}JACK_O_LANTERN:1' + currency: 10 + xp: 10 + stonebrickmaker: + name: '&aStone Brick Maker' + description: Make 64 Stone Bricks. + type: onPlayer + requiredItems: + - STONE_BRICKS:64;+8 + - STONE_BRICK_SLAB:30;+6 + - CHISELED_STONE_BRICKS:30;+6 + - STONE_BRICK_STAIRS:16;+4 + displayItem: STONE_BRICKS + lockedDisplayItem: GRAY_STAINED_GLASS_PANE + reward: + text: 4 redstone ore, 4 iron ore, 1 chicken + items: + - REDSTONE_ORE:4 + - IRON_ORE:4 + - CHICKEN_SPAWN_EGG:1 + currency: 30 + xp: 30 + repeatReward: + text: 1 redstone ore, 1 iron ore + items: + - REDSTONE_ORE:1 + - IRON_ORE:1 + currency: 15 + xp: 15 + novicebuilder: + name: '&7Novice Builder' + description: Reach island level 20. + type: islandLevel + requiredLevel: 20 + displayItem: COAL_ORE + reward: + text: 8 dirt, 8 sand, 5 emeralds + items: + - DIRT:8 + - SAND:8 + - EMERALD:5 + - DIAMOND:1 + currency: 20 + xp: 20 + commands: + - op:effect give {party} regeneration + adeptbuilder: + name: '&aAdept Builder' + description: Reach island level 50. + type: islandLevel + requiredLevel: 50 + displayItem: IRON_ORE + offset: -1 + reward: + text: 10 obsidian, 2 diamonds, 5 emeralds + items: + - OBSIDIAN:10 + - EMERALD:5 + - DIAMOND:2 + currency: 100 + xp: 100 + expertbuilder: + name: '&eExpert Builder' + description: Reach island level 100. + type: islandLevel + requiredLevel: 100 + displayItem: GOLD_ORE + offset: -1 + reward: + text: 16 dirt, 16 sand, 3 diamonds, 5 emeralds + items: + - DIRT:16 + - SAND:16 + - EMERALD:5 + - DIAMOND:3 + currency: 250 + xp: 250 + masterbuilder: + name: '&cMaster Builder' + description: Reach island level 250. + type: islandLevel + requiredLevel: 250 + displayItem: DIAMOND_ORE + offset: -1 + reward: + text: 32 dirt, 32 sand, 4 diamonds, 5 emeralds + items: + - DIRT:32 + - SAND:32 + - EMERALD:5 + - DIAMOND:4 + currency: 500 + xp: 500 + skylord: + name: '&4Sky Lord' + description: Reach island level 500. + type: islandLevel + requiredLevel: 500 + displayItem: EMERALD_ORE + offset: -1 + reward: + text: 64 dirt, 64 sand, 5 diamond, 5 emeralds + items: + - DIRT:64 + - SAND:64 + - DIAMOND:5 + - EMERALD:5 + currency: 1000 + xp: 1000 + Tier2: + name: '&aAdept' + displayItem: LIME_TERRACOTTA + resetInHours: 20 + requires: + + # means disabled in effect + rankLeeway: 99 + challenges: + - cobblestonegenerator + - novicebuilder + challenges: + lumberjack: + name: '&3Lumberjack' + description: Collect all types of wood logs. + type: onPlayer + requiredItems: + - OAK_LOG:16;+2 + - SPRUCE_LOG:16;+2 + - BIRCH_LOG:16;+2 + - JUNGLE_LOG:16;+2 + - ACACIA_LOG:16;+2 + - DARK_OAK_LOG:16;+2 + displayItem: OAK_LOG + lockedDisplayItem: CYAN_STAINED_GLASS_PANE + reward: + text: 4 redstone ore, 4 iron ore, 1 wolf + items: + - REDSTONE_ORE:4 + - IRON_ORE:4 + - WOLF_SPAWN_EGG:1 + currency: 30 + xp: 30 + repeatReward: + text: 1 redstone ore, 1 iron ore + items: + - REDSTONE_ORE:1 + - IRON_ORE:1 + currency: 15 + xp: 15 + shroompicker: + name: '&6Shroom Picker' + description: Collect red and brown mushrooms. + type: onPlayer + requiredItems: + - BROWN_MUSHROOM:64;+4 + - RED_MUSHROOM:64;+4 + displayItem: RED_MUSHROOM + lockedDisplayItem: BROWN_STAINED_GLASS_PANE + reward: + text: 8 mycelium, 4 podzol + items: + - MYCELIUM:8 + - PODZOL:4 + currency: 30 + xp: 30 + repeatReward: + text: 4 mycelium + items: + - MYCELIUM:4 + - '{p=0.02}MYCELIUM:4' + - '{p=0.02}PODZOL:2' + currency: 15 + xp: 15 + potatofarmer: + name: '&6Potato Farmer' + description: Harvest potato's from a farm. + type: onPlayer + requiredItems: + - POTATO:64;+16 + displayItem: POTATO + lockedDisplayItem: BROWN_STAINED_GLASS_PANE + reward: + text: 1 carrot, 4 dirt + items: + - CARROT:1 + - DIRT:4 + - '{p=0.10}BEETROOT_SEEDS:1' + - '{p=0.05}RED_SAND:1' + - '{p=0.01}DIAMOND:1' + currency: 50 + xp: 50 + repeatReward: + text: 4 dirt and a baked potato + items: + - DIRT:4 + - BAKED_POTATO:1 + - '{p=0.10}BEETROOT_SEEDS:1' + - '{p=0.05}RED_SAND:1' + - '{p=0.01}DIAMOND:1' + currency: 25 + xp: 25 + carrotfarmer: + name: '&6Carrot Farmer' + description: Harvest carrot's from a farm. + type: onPlayer + requiredItems: + - CARROT:64;+16 + displayItem: CARROT + lockedDisplayItem: BROWN_STAINED_GLASS_PANE + reward: + text: a pig with saddle and a potato + items: + - SADDLE:1 + - POTATO:1 + - PIG_SPAWN_EGG:1 + - '{p=0.05}RED_SAND:1' + - '{p=0.01}DIAMOND:1' + currency: 50 + xp: 50 + repeatReward: + text: 1 golden carrot + items: + - GOLDEN_CARROT:1 + - '{p=0.05}SAND:1' + - '{p=0.01}DIAMOND:1' + currency: 25 + xp: 25 + wheatfarmer: + name: '&6Wheat Farmer' + description: Harvest wheat from a farm. + type: onPlayer + requiredItems: + - WHEAT:64;+16 + displayItem: WHEAT + lockedDisplayItem: BROWN_STAINED_GLASS_PANE + reward: + text: 8 dirt + items: + - DIRT:8 + currency: 20 + xp: 20 + repeatReward: + text: 4 dirt + items: + - DIRT:4 + - '{p=0.2}BONE:1' + currency: 10 + xp: 10 + monsterfarm: + name: '&5Monster Farm' + description: Build a mob farm and collect mob loot. + type: onPlayer + requiredItems: + - ROTTEN_FLESH:64;+4 + - STRING:32;+2 + - ARROW:32;+2 + - BONE:32;+2 + - GUNPOWDER:16;+1 + - SPIDER_EYE:5 + displayItem: ROTTEN_FLESH + lockedDisplayItem: PURPLE_STAINED_GLASS_PANE + reward: + text: 4 redstone ore, 4 iron ore and 1 flint + items: + - REDSTONE_ORE:4 + - IRON_ORE:4 + - FLINT:1 + - '{p=0.10}POTATO:1' + - '{p=0.10}CARROT:1' + currency: 30 + xp: 30 + repeatReward: + text: 1 redstone ore, 1 iron ore, 1 flint + items: + - REDSTONE_ORE:1 + - IRON_ORE:4 + - FLINT:1 + - '{p=0.05}POTATO:1' + - '{p=0.05}CARROT:1' + currency: 15 + xp: 15 + homeowner: + name: '&9Home Owner' + description: Build a house with furnishings. + type: onIsland + requiredChallenges: + - stonebrickmaker + requiredItems: + - RED_BED:1 + - CRAFTING_TABLE:1 + - GLASS:1 + - OAK_DOOR:1 + - FURNACE:1 + - BOOKSHELF:1 + - TORCH:1 + lockedDisplayItem: BLUE_STAINED_GLASS_PANE + displayItem: OAK_DOOR + reward: + text: 4 redstone ore, 5 inksac, 4 iron ore and some seeds + items: + - REDSTONE_ORE:4 + - IRON_ORE:4 + - INK_SAC:5 + - BEETROOT_SEEDS:1 + currency: 40 + xp: 40 + netherportal: + name: '&9Nether Portal' + description: Build a nether portal on your island. + type: onIsland + requiredChallenges: + - adeptbuilder + - homeowner + requiredItems: + - OBSIDIAN:10 + - NETHER_PORTAL:1 + displayItem: OBSIDIAN + lockedDisplayItem: BLUE_STAINED_GLASS_PANE + reward: + text: 1 iron pickaxe, 1 iron shovel + items: + - IRON_SHOVEL:1 + - IRON_PICKAXE:1 + currency: 40 + xp: 40 + nethermining: + name: '&7Nether Mining' + description: Mine from your Nether island. + type: onPlayer + displayItem: NETHERRACK + lockedDisplayItem: GRAY_STAINED_GLASS_PANE + offset: -1 + requiredItems: + - NETHERRACK:64;+2 + - SOUL_SAND:16;+2 + - GRAVEL:16;+2 + - QUARTZ:32;+2 + - GLOWSTONE:16;+2 + - COARSE_DIRT:4;+2 + reward: + text: 1 ghast tear, 1 magic bow + items: + - GHAST_TEAR:1 + - 'BOW:1 {Enchantments:[{id:"minecraft:infinity",lvl:1},{id:"minecraft:unbreaking",lvl:3}]}' + currency: 40 + xp: 40 + repeatReward: + text: a blaze-rod and a chance of ghast tear + items: + - BLAZE_ROD:1 + - '{p=0.10}GHAST_TEAR:1' + currency: 20 + xp: 20 + Tier3: + name: '&eExpert' + displayItem: YELLOW_TERRACOTTA + resetInHours: 20 + requires: + challenges: + - adeptbuilder + challenges: + toolmaker: + name: '&3Tool Maker' + description: Make all stone tools + type: onPlayer + requiredChallenges: + - lumberjack + requiredItems: + - STONE_SHOVEL:1 + - STONE_PICKAXE:1 + - STONE_AXE:1 + - STONE_HOE:1 + displayItem: STONE_AXE + lockedDisplayItem: CYAN_STAINED_GLASS_PANE + reward: + text: 4 redstone ore, 4 iron, 1 pig + items: + - REDSTONE_ORE:4 + - IRON_ORE:4 + - PIG_SPAWN_EGG:1 + currency: 30 + xp: 30 + sawmill: + name: '&3Saw Mill' + description: Deliver a collection of processed wood + type: onPlayer + offset: -1 + requiredChallenges: + - toolmaker + requiredItems: + - STRIPPED_OAK_LOG:8;+2 + - STRIPPED_SPRUCE_LOG:8;+2 + - STRIPPED_BIRCH_LOG:8;+2 + - STRIPPED_JUNGLE_LOG:8;+2 + - STRIPPED_ACACIA_LOG:8;+2 + - STRIPPED_DARK_OAK_LOG:8;+2 + displayItem: IRON_AXE + lockedDisplayItem: CYAN_STAINED_GLASS_PANE + reward: + text: 4 redstone ore, 4 iron, cocoa and a mule + items: + - COCOA_BEANS:1 + - REDSTONE_ORE:4 + - IRON_ORE:4 + - MULE_SPAWN_EGG:1 + currency: 30 + xp: 30 + repeatReward: + text: 1 redstone ore, 1 iron ore + items: + - REDSTONE_ORE:1 + - IRON_ORE:1 + - '{p=0.05}GOLD_ORE:1' + currency: 15 + xp: 15 + torchmaker: + name: '&3Torch Maker' + description: Make 128 torches. + type: onPlayer + requiredChallenges: + - lumberjack + requiredItems: + - TORCH:128;+32 + displayItem: TORCH + lockedDisplayItem: CYAN_STAINED_GLASS_PANE + reward: + text: 4 redstone ore, 4 iron ore + items: + - REDSTONE_ORE:4 + - IRON_ORE:4 + currency: 30 + xp: 30 + repeatReward: + text: 1 redstone ore, 1 iron ore + items: + - REDSTONE_ORE:1 + - IRON_ORE:1 + currency: 15 + xp: 15 + expertfarmer: + name: '&6Expert Farmer' + description: Harvest many different farming resources. + type: onPlayer + displayItem: IRON_HOE + lockedDisplayItem: BROWN_STAINED_GLASS_PANE + requiredChallenges: + - applecollector + - melonfarmer + - pumpkinfarmer + - wheatfarmer + - carrotfarmer + - potatofarmer + - cactusfarmer + - sugarfarmer + - shroompicker + requiredItems: + - MELON_SLICE:256;+2 + - SUGAR_CANE:128;+2 + - WHEAT:128;+2 + - POTATO:128;+2 + - CARROT:128;+2 + - PUMPKIN:128;+2 + - CACTUS:128;+2 + - BEETROOT:128;+2 + reward: + text: a bucket, cocoa and a cow + items: + - BUCKET:1 + - COCOA_BEANS:1 + - COW_SPAWN_EGG:1 + currency: 50 + xp: 50 + repeatReward: + text: a cow + items: + - COW_SPAWN_EGG:1 + currency: 25 + xp: 25 + fisherman: + name: '&5Fisherman' + description: Catch different types of fish. + type: onPlayer + requiredItems: + - COD:5;+1 + - SALMON:5;+1 + - PUFFERFISH:3;+1 + - TROPICAL_FISH:1;+0 + - INK_SAC:5;+2 + displayItem: COD + lockedDisplayItem: PURPLE_STAINED_GLASS_PANE + reward: + text: 10 lapis blocks, 20 prismarine, kelp, deep-ocean + items: + - LAPIS_BLOCK:10 + - PRISMARINE:20 + - KELP:1 + - '{p=0.20}PRISMARINE_CRYSTALS:5' + permission: usb.biome.deep_ocean + currency: 50 + xp: 50 + repeatReward: + text: 2 lapis, 5 prismarine, kelp and a chance at prismarine crystals + items: + - LAPIS_LAZULI:2 + - PRISMARINE:5 + - KELP:1 + - '{p=0.20}PRISMARINE_CRYSTALS:5' + currency: 25 + xp: 25 + woolcollector: + name: '&5Wool Collector' + description: Collect every color of wool. + type: onPlayer + displayItem: WHITE_WOOL + lockedDisplayItem: PURPLE_STAINED_GLASS_PANE + requiredChallenges: + - monsterfarmer + requiredItems: + - WHITE_WOOL:2;+4 + - ORANGE_WOOL:2;+4 + - MAGENTA_WOOL:2;+4 + - LIGHT_BLUE_WOOL:2;+4 + - YELLOW_WOOL:2;+4 + - LIME_WOOL:2;+4 + - PINK_WOOL:2;+4 + - GRAY_WOOL:2;+4 + - LIGHT_GRAY_WOOL:2;+4 + - CYAN_WOOL:2;+4 + - PURPLE_WOOL:2;+4 + - BLUE_WOOL:2;+4 + - BROWN_WOOL:2;+4 + - GREEN_WOOL:2;+4 + - RED_WOOL:2;+4 + - BLACK_WOOL:2;+4 + reward: + text: 2 diamonds, sheep, emerald, flowers + items: + - DIAMOND:2 + - EMERALD:1 + - SHEEP_SPAWN_EGG:1 + - ROSE_BUSH:1 + - PEONY:1 + permission: usb.biome.flower_forest + currency: 70 + xp: 70 + repeatReward: + text: emerald, sheep, flowers + items: + - EMERALD:1 + - SHEEP_SPAWN_EGG:1 + - ROSE_BUSH:1 + - PEONY:1 + currency: 35 + xp: 35 + maestro: + name: '&5Maestro' + description: Make a jukebox and collect all music discs. + type: onPlayer + requiredItems: + - MUSIC_DISC_13:1 + - MUSIC_DISC_CAT:1 + - MUSIC_DISC_BLOCKS:1 + - MUSIC_DISC_CHIRP:1 + - MUSIC_DISC_FAR:1 + - MUSIC_DISC_MALL:1 + - MUSIC_DISC_MELLOHI:1 + - MUSIC_DISC_STAL:1 + - MUSIC_DISC_STRAD:1 + - MUSIC_DISC_WARD:1 + - MUSIC_DISC_11:1 + - MUSIC_DISC_WAIT:1 + - JUKEBOX:1 + displayItem: JUKEBOX + lockedDisplayItem: PURPLE_STAINED_GLASS_PANE + reward: + text: 3 diamonds, 1 gold block, 10 emeralds + items: + - DIAMOND:3 + - EMERALD:10 + - GOLD_BLOCK:1 + currency: 70 + xp: 70 + repeatReward: + text: 2 gold ore, 1 diamond + items: + - GOLD_ORE:2 + - DIAMOND:1 + - '{p=0.2}DIAMOND:1' + currency: 35 + xp: 35 + ironfarm: + name: '&9Iron Farm' + description: Build an iron-farm. + type: onIsland + displayItem: IRON_BLOCK + lockedDisplayItem: BLUE_STAINED_GLASS_PANE + radius: 50 + requiredChallenges: + - monsterfarm + - applecollector + requiredItems: + - OAK_DOOR:30 + requiredEntities: + - Villager:10 + - IRON_GOLEM:1 + reward: + text: 2 gold blocks, 1 diamond block + items: + - GOLD_BLOCK:2 + - DIAMOND_BLOCK:1 + currency: 500 + xp: 400 + netherfortress: + name: '&9Nether Fortress' + description: Build a netherfortress. + type: onIsland + radius: 50 + requiredItems: + - NETHER_BRICKS:512 + - NETHER_BRICK_SLAB:64 + - NETHER_BRICK_FENCE:64 + - NETHER_BRICK_STAIRS:64 + - SOUL_SAND:32 + displayItem: NETHER_BRICK_FENCE + lockedDisplayItem: BLUE_STAINED_GLASS_PANE + reward: + text: a wither-skull and a blaze rod and a chance of diamonds + items: + - WITHER_SKELETON_SKULL:1 + - BLAZE_ROD:1 + - '{p=0.05}DIAMOND:3' + - '{p=0.10}DIAMOND:2' + - '{p=0.15}DIAMOND:1' + currency: 40 + xp: 40 + #=============================================== + Tier4: + name: '&cMaster' + displayItem: ORANGE_TERRACOTTA + resetInHours: 20 + requires: + + # disabled + rankLeeway: 99 + challenges: + - expertbuilder + challenges: + glassmaker: + name: '&3Glassmaker' + description: Collect every color of stained glass. + type: onPlayer + displayItem: WHITE_STAINED_GLASS + lockedDisplayItem: CYAN_STAINED_GLASS_PANE + requiredItems: + - WHITE_STAINED_GLASS:8;+2 + - ORANGE_STAINED_GLASS:8;+2 + - MAGENTA_STAINED_GLASS:8;+2 + - LIGHT_BLUE_STAINED_GLASS:8;+2 + - YELLOW_STAINED_GLASS:8;+2 + - LIME_STAINED_GLASS:8;+2 + - PINK_STAINED_GLASS:8;+2 + - GRAY_STAINED_GLASS:8;+2 + - LIGHT_GRAY_STAINED_GLASS:8;+2 + - CYAN_STAINED_GLASS:8;+2 + - PURPLE_STAINED_GLASS:8;+2 + - BLUE_STAINED_GLASS:8;+2 + - BROWN_STAINED_GLASS:8;+2 + - GREEN_STAINED_GLASS:8;+2 + - RED_STAINED_GLASS:8;+2 + - BLACK_STAINED_GLASS:8;+2 + reward: + text: 2 diamonds, 2 disks, 1 emeralds + items: + - DIAMOND:2 + - MUSIC_DISC_WARD:1 + - MUSIC_DISC_11:1 + - EMERALD:1 + - SUNFLOWER:1 + - LILAC:1 + permission: usb.biome.flower_forest + currency: 70 + xp: 70 + repeatReward: + text: 30% chance on 1 or 2 disks, 1 emeralds + items: + - '{p=0.3}MUSIC_DISC_WARD:1' + - '{p=0.3}MUSIC_DISC_11:1' + - EMERALD:1 + - SUNFLOWER:1 + - LILAC:1 + currency: 35 + xp: 35 + carpenter: + name: '&3Carpenter' + description: Collect all types of wood items. + type: onPlayer + requiredItems: + - JUNGLE_STAIRS:16;+8 + - SPRUCE_DOOR:1;+1 + - DARK_OAK_FENCE_GATE:2;+2 + - ACACIA_FENCE:30;+15 + - LADDER:30;+15 + - OAK_TRAPDOOR:4;+2 + - OAK_PRESSURE_PLATE:2;+1 + requiredChallenges: + - toolmaker + - lumberjack:2 + displayItem: CRAFTING_TABLE + lockedDisplayItem: CYAN_STAINED_GLASS_PANE + reward: + text: a parrot and a jukebox with some redstone and iron + items: + - PARROT_SPAWN_EGG:1 + - JUKEBOX:1 + - REDSTONE_ORE:4 + - IRON_ORE:4 + currency: 30 + xp: 30 + repeatReward: + text: redstone, iron and a slim chance at a parrot + items: + - REDSTONE_ORE:1 + - IRON_ORE:1 + - '{p=0.25}JUKEBOX:1' + - '{p=0.05}PARROT_SPAWN_EGG:1' + currency: 15 + xp: 15 + cookielover: + name: '&eCookie Lover' + description: Make cookies and a bucket of milk. + type: onPlayer + requiredChallenges: + - expertfarmer + requiredItems: + - COOKIE:128;+4 + - MILK_BUCKET:1 + displayItem: COOKIE + lockedDisplayItem: YELLOW_STAINED_GLASS_PANE + reward: + text: 4 redstone ore, clay a bucket and a diamond + items: + - REDSTONE_ORE:4 + - CLAY:5 + - DIAMOND:1 + - BUCKET:1 + currency: 80 + xp: 80 + repeatReward: + text: iron ore and some clay + items: + - IRON_ORE:1 + - CLAY:5 + - BUCKET:1 + - '{p=0.4}CLAY:3' + - '{p=0.3}IRON_ORE:2' + currency: 40 + xp: 40 + deepseafisherman: + name: '&5Deep Sea Fishing' + description: Farm the deep sea + type: onPlayer + requiredItems: + - COD:10;+5 + - SALMON:10;+5 + - PUFFERFISH:5;+3 + - TROPICAL_FISH:3;+2 + - PRISMARINE_CRYSTALS:16;+8 + - PRISMARINE_SHARD:16;+8 + - DRIED_KELP_BLOCK:64;+32 + - NAUTILUS_SHELL:1 + displayItem: HEART_OF_THE_SEA + lockedDisplayItem: PURPLE_STAINED_GLASS_PANE + reward: + text: heart-of-the-sea, nautilus shell and a turtle + items: + - NAUTILUS_SHELL:1 + - HEART_OF_THE_SEA:1 + - TURTLE_SPAWN_EGG:1 + - '{p=0.1}NAUTILUS_SHELL:1' + permission: usb.biome.deep_ocean + currency: 50 + xp: 50 + repeatReward: + text: nautilus shell, turtle and a chance of a heart + items: + - NAUTILUS_SHELL:1 + - TURTLE_SPAWN_EGG:1 + - '{p=0.05}HEART_OF_THE_SEA:1' + - '{p=0.1}NAUTILUS_SHELL:1' + currency: 25 + xp: 25 + horsingaround: + name: '&6Horsing Around' + description: Get hay bales for the horses. + type: onPlayer + requiredItems: + - HAY_BLOCK:32;+4 + - LEAD:8;+2 + - CARROT_ON_A_STICK:1 + - SHEARS:1 + requirecChallenges: + - wheatfarmer + displayItem: HAY_BLOCK + lockedDisplayItem: BROWN_STAINED_GLASS_PANE + reward: + text: 1 horse, 1 iron horse armor, 5% chance on diamond horse armor + items: + - IRON_HORSE_ARMOR:1 + - HORSE_SPAWN_EGG:1 + - '{p=0.05}DIAMOND_HORSE_ARMOR:1' + currency: 50 + xp: 50 + repeatReward: + text: 1 redstone ore, 1 emerald + items: + - REDSTONE:1 + - EMERALD:1 + currency: 25 + xp: 25 + slimefarmer: + name: '&5Slime Farmer' + description: Collect slimeballs from slimes. + type: onPlayer + requiredItems: + - SLIME_BALL:64;+4 + displayItem: SLIME_BALL + lockedDisplayItem: PURPLE_STAINED_GLASS_PANE + reward: + text: 1 diamond, 1 emeralds, swampland + items: + - DIAMOND:1 + - EMERALD:1 + permission: usb.biome.swamp + currency: 70 + xp: 70 + repeatReward: + text: 1 redstone ore, 1 emeralds + items: + - REDSTONE_ORE:1 + - EMERALD:1 + currency: 35 + xp: 35 + animalfarm: + name: '&9Animal Farm' + description: Create an animal farm. + type: onIsland + displayItem: OAK_FENCE + radius: 40 + requiredChallenges: + - woolcollector + - potatofarmer + - carrotfarmer + requiredEntities: + - Cow:8 + - Pig:8 + - Chicken:16 + - Sheep:{"Color":"WHITE"} + - Sheep:{"Color":"ORANGE"} + - Sheep:{"Color":"MAGENTA"} + - Sheep:{"Color":"LIGHT_BLUE"} + - Sheep:{"Color":"YELLOW"} + - Sheep:{"Color":"LIME"} + - Sheep:{"Color":"PINK"} + - Sheep:{"Color":"GRAY"} + - Sheep:{"Color":"LIGHT_GRAY"} + - Sheep:{"Color":"CYAN"} + - Sheep:{"Color":"PURPLE"} + - Sheep:{"Color":"BLUE"} + - Sheep:{"Color":"BROWN"} + - Sheep:{"Color":"GREEN"} + - Sheep:{"Color":"RED"} + - Sheep:{"Color":"BLACK"} + reward: + text: 1 horse, 1 iron horse armor, 5% chance horse armor + items: + - IRON_HORSE_ARMOR:1 + - HORSE_SPAWN_EGG:1 + - '{p=0.05}IRON_HORSE_ARMOR:1' + - '{p=0.05}GOLDEN_HORSE_ARMOR:1' + - '{p=0.05}DIAMOND_HORSE_ARMOR:1' + currency: 50 + xp: 50 + repeatReward: + text: 1 redstone ore, 1 emerald, 5% chance horse armor + items: + - REDSTONE:1 + - EMERALD:1 + - '{p=0.05}IRON_HORSE_ARMOR:1' + - '{p=0.05}GOLDEN_HORSE_ARMOR:1' + - '{p=0.05}DIAMOND_HORSE_ARMOR:1' + currency: 25 + xp: 25 + witherhunter: + name: '&5Wither Hunter' + description: Collect some Wither Skeleton skulls. + type: onPlayer + requiredChallenges: + - netherfortress + requiredItems: + - WITHER_SKELETON_SKULL:10;+1 + displayItem: WITHER_SKELETON_SKULL + lockedDisplayItem: PURPLE_STAINED_GLASS_PANE + offset: -1 + reward: + text: 5 diamonds, 5% chance of nether star + items: + - DIAMOND:5 + - '{p=0.05}NETHER_STAR:1' + currency: 400 + xp: 400 + repeatReward: + text: 2 gold ore, 5% chance of nether star + items: + - GOLD_ORE:2 + - '{p=0.05}NETHER_STAR:1' + currency: 20 + xp: 20 + pearlcollector: + name: '&5Pearl Collector' + description: Collect enderpearls from endermen. + type: onPlayer + requiredItems: + - ENDER_PEARL:10;+4 + displayItem: ENDER_PEARL + lockedDisplayItem: PURPLE_STAINED_GLASS_PANE + reward: + text: 5 gold ore, 1 blaze rod, 1 emerald + items: + - GOLD_ORE:5 + - BLAZE_ROD:1 + - EMERALD:1 + currency: 70 + xp: 70 + repeatReward: + text: 1 gold ore, 1 blaze rod + items: + - GOLD_ORE:1 + - BLAZE_ROD:1 + currency: 35 + xp: 35 + + #=============================================== + Tier5: + name: '&4Sky Lord' + displayItem: RED_TERRACOTTA + resetInHours: 48 + requires: + challenges: + - masterbuilder + challenges: + technician: + name: '&3Technician' + description: Collect some of every type of redstone equipment. + type: onPlayer + requiredItems: + - REDSTONE:64;+16 + - REDSTONE_TORCH:32;+4 + - REPEATER:5;+1 + - COMPARATOR:3;+1 + - PISTON:2;+1 + - STICKY_PISTON:2;+1 + - LEVER:1;+1 + - STONE_BUTTON:1;+1 + - STONE_PRESSURE_PLATE:1;+1 + - HOPPER:1;+1 + - DISPENSER:1;+1 + - DROPPER:1;+1 + - DAYLIGHT_DETECTOR:1;+1 + displayItem: REDSTONE + lockedDisplayItem: CYAN_STAINED_GLASS_PANE + reward: + text: some snow, ice and packed ice + items: + - SNOW_BLOCK:4 + - ICE:8 + - PACKED_ICE:8 + - REDSTONE_BLOCK:64 + currency: 70 + xp: 70 + repeatReward: + text: a stack of redstone blocks and some ice + items: + - REDSTONE_BLOCK:64 + - ICE:4 + - PACKED_ICE:1 + currency: 35 + xp: 35 + emeraldcollector: + name: '&5Emerald Collector' + description: Collect emeralds. + type: onPlayer + requiredItems: + - EMERALD:60;+10 + displayItem: EMERALD + lockedDisplayItem: PURPLE_STAINED_GLASS_PANE + reward: + text: a full set of diamond armor + items: + - DIAMOND_HELMET:1 + - DIAMOND_CHESTPLATE:1 + - DIAMOND_LEGGINGS:1 + - DIAMOND_BOOTS:1 + currency: 70 + xp: 70 + repeatReward: + text: full diamond armor + items: + - DIAMOND_HELMET:1 + - DIAMOND_CHESTPLATE:1 + - DIAMOND_LEGGINGS:1 + - DIAMOND_BOOTS:1 + currency: 35 + xp: 35 + topchef: + name: '&eTop Chef' + description: Collect every kind of edible food. + type: onPlayer + requiredItems: + - BAKED_POTATO:1 + - BREAD:1 + - CAKE:1 + - COOKED_CHICKEN:1 + - COOKED_COD:1 + - COOKED_SALMON:1 + - TROPICAL_FISH:1 + - COOKED_PORKCHOP:1 + - COOKIE:1 + - GOLDEN_APPLE:1 + - GOLDEN_CARROT:1 + - MUSHROOM_STEW:1 + - PUMPKIN_PIE:1 + - COOKED_BEEF:1 + - MELON:1 + - CARROT:1 + requiredChallenges: + - expertfarmer + - fisherman + - cookielover + displayItem: CARROT + lockedDisplayItem: YELLOW_STAINED_GLASS_PANE + reward: + text: 2 diamond, 1 mooshroom + items: + - DIAMOND:2 + - MOOSHROOM_SPAWN_EGG:1 + currency: 80 + xp: 80 + repeatReward: + text: 1 diamond, 1 mooshroom + items: + - DIAMOND:1 + - MOOSHROOM_SPAWN_EGG:1 + currency: 40 + xp: 40 + tajmahal: + name: '&9Taj Mahal' + description: Build a temple of quartz + type: onIsland + radius: 30 + displayItem: QUARTZ_BLOCK + lockedDisplayItem: BLUE_STAINED_GLASS_PANE + requiredChallenges: + - nethermining + requiredItems: + - QUARTZ_BLOCK:512 + - CHISELED_QUARTZ_BLOCK:64 + - QUARTZ_PILLAR:128 + - QUARTZ_STAIRS:64 + - QUARTZ_SLAB:192 + reward: + text: 3 end-portal frames, red sand + items: + - END_PORTAL_FRAME:3 + - RED_SAND:64 + currency: 1000 + xp: 500 + greatpyramid: + name: '&9Great Pyramid' + description: Build a pyramid of sandstone + type: onIsland + radius: 30 + displayItem: SANDSTONE + lockedDisplayItem: BLUE_STAINED_GLASS_PANE + requiredItems: + - SANDSTONE:512 + - CHISELED_SANDSTONE:64 + - SMOOTH_SANDSTONE:128 + - SANDSTONE_STAIRS:64 + - SANDSTONE_SLAB:192 + - RED_SANDSTONE:16 + reward: + text: 3 end-portal frames, 3 diamonds, bow + items: + - END_PORTAL_FRAME:3 + - DIAMOND:3 + - BOW:1 {Enchantments:[{id:"minecraft:infinity",lvl:1},{id:"minecraft:unbreaking",lvl:3},{id:"minecraft:power",lvl:5}]} + currency: 1000 + xp: 500 + poseidonshalls: + name: '&9Poseidon''s Halls' + description: Build the halls of Poseidon + type: onIsland + radius: 50 + displayItem: PRISMARINE + lockedDisplayItem: BLUE_STAINED_GLASS_PANE + requiredChallenges: + - deepseafisherman + requiredItems: + - PRISMARINE:512 + - PRISMARINE_BRICKS:128 + - DARK_PRISMARINE:64 + - SEA_LANTERN:32 + - CONDUIT:1 + - FLOWER_POT:16 + - VINE:128 + - POPPY:10 + - BLUE_ORCHID:10 + - ALLIUM:10 + - AZURE_BLUET:10 + - RED_TULIP:10 + - ORANGE_TULIP:10 + - WHITE_TULIP:10 + - PINK_TULIP:10 + - OXEYE_DAISY:10 + - SUNFLOWER:10 + - LILAC:10 + - ROSE_BUSH:10 + - PEONY:10 + reward: + text: 3 end-portal frames, 5 diamonds + items: + - END_PORTAL_FRAME:3 + - DIAMOND:5 + currency: 1000 + xp: 500 + beaconator: + name: '&9Beaconator' + description: Build a very expensive beacon. + type: onIsland + radius: 15 + displayItem: BEACON + lockedDisplayItem: BLUE_STAINED_GLASS_PANE + requiredChallenges: + - netherfortress + - ironfarm + - maestro + - witherhunter + requiredItems: + - BEACON:1 + - DIAMOND_BLOCK:1 + - EMERALD_BLOCK:8 + - GOLD_BLOCK:25 + - IRON_BLOCK:49 + reward: + items: + - END_PORTAL_FRAME:3 + - DIAMOND_SWORD:1 {Enchantments:[{id:"minecraft:sharpness",lvl:5},{id:"minecraft:unbreaking",lvl:3},{id:"minecraft:fire",lvl:1}]} + text: some end-portal-pieces and a magic diamond sword + currency: 4000 + xp: 1000 + endportal: + name: '&9End Portal' + description: Build and activate an end-portal. + type: onIsland + radius: 15 + displayItem: END_PORTAL_FRAME + lockedDisplayItem: BLUE_STAINED_GLASS_PANE + requiredChallenges: + - beaconator + - tajmahal + - poseidonshalls + - greatpyramid + requiredItems: + - END_PORTAL_FRAME:12 + - END_PORTAL:9 + reward: + text: 5 diamonds and some nice boots + items: + - DIAMOND:5 + - DIAMOND_BOOTS:1 {Enchantments:[{id:"minecraft:feather_falling",lvl:4},{id:"minecraft:protection",lvl:4},{id:"minecraft:blast_protection",lvl:4}]} + currency: 4000 + xp: 1000 + + #=============================================== + Tier6: + name: '&aWorld Foods' + displayItem: LIGHT_GRAY_TERRACOTTA + resetInHours: 20 + requires: + challenges: + - masterbuilder + - topchef + challenges: + fishandchips: + name: '&eFish & Chips' + description: Create the famous English Fish & Chips. + type: onPlayer + requiredItems: + - COOKED_COD:32;+8 + - BAKED_POTATO:32;+8 + displayItem: COOKED_COD + lockedDisplayItem: YELLOW_STAINED_GLASS_PANE + reward: + text: 16 baked potatos, 1 disk, 4 emerald + items: + - BAKED_POTATO:16 + - MUSIC_DISC_13:1 + - EMERALD:4 + currency: 60 + xp: 60 + commands: + - 'console: msg {player} &9https://en.wikipedia.org/wiki/Fish_and_chips + &2Click the link for more info' + repeatReward: + text: 30% chance on 1 disk, 1 emerald + items: + - EMERALD:1 + - '{p=0.3}MUSIC_DISC_13:1' + currency: 30 + xp: 30 + commands: + - 'console: msg {player} &9https://en.wikipedia.org/wiki/Fish_and_chips + &2Click the link for more info' + smorrebrod: + name: '&eSmørrebrød' + description: Create the famous Danish smørrebrød. + type: onPlayer + requiredItems: + - BREAD:16;+4 + - COOKED_SALMON:8;+4 + - TROPICAL_FISH:2;+1 + - COOKED_PORKCHOP:8;+4 + - COOKED_BEEF:8;+4 + displayItem: COOKED_COD + lockedDisplayItem: YELLOW_STAINED_GLASS_PANE + reward: + text: 1 rod, 2 name tags, 2 ocelots + items: + - FISHING_ROD:1 + - NAME_TAG:2 + - OCELOT_SPAWN_EGG:2 + currency: 60 + xp: 60 + commands: + - 'console: msg {player} &9https://en.wikipedia.org/wiki/Danish_cuisine + &2Click the link for more info' + repeatReward: + text: 1 emerald + items: + - EMERALD:1 + currency: 30 + xp: 30 + commands: + - 'console: msg {player} &9https://en.wikipedia.org/wiki/Danish_cuisine + &2Click the link for more info' + hutspot: + name: '&eHutspot' + description: Create the famous Dutch hutspot. + type: onPlayer + requiredItems: + - POTATO:64;+8 + - CARROT:64;+8 + lockedDisplayItem: YELLOW_STAINED_GLASS_PANE + displayItem: GOLDEN_CARROT + reward: + text: 1 golden carrot, 1 disk, 4 emerald + items: + - GOLDEN_CARROT:1 + - MUSIC_DISC_CAT:1 + - EMERALD:4 + currency: 60 + xp: 60 + commands: + - 'console: msg {player} &9https://en.wikipedia.org/wiki/Hutspot &2Click + the link for more info' + repeatReward: + text: 30% chance on 1 disk, 1 emerald + items: + - EMERALD:1 + - '{p=0.3}MUSIC_DISC_CAT:1' + currency: 30 + xp: 30 + commands: + - 'console: msg {player} &9https://en.wikipedia.org/wiki/Hutspot &2Click + the link for more info' + apfelstrudel: + name: '&eApfelstrudele' + description: Create the famous German apfelstrudel. + type: onPlayer + requiredItems: + - APPLE:8;+5 + - WHEAT:16;+5 + - SUGAR:16;+5 + - MILK_BUCKET:1 + displayItem: GOLDEN_APPLE + lockedDisplayItem: YELLOW_STAINED_GLASS_PANE + reward: + text: 1 bucket, 1 disk, 4 emerald + items: + - BUCKET:1 + - EMERALD:4 + - MUSIC_DISC_BLOCKS:1 + currency: 60 + xp: 60 + commands: + - 'console: msg {player} &9https://en.wikipedia.org/wiki/Apple_strudel &2Click + the link for more info' + repeatReward: + text: 1 bucket, 30% chance on 1 disk, 1 emerald + items: + - BUCKET:1 + - EMERALD:1 + - '{p=0.3}MUSIC_DISC_BLOCKS:1' + currency: 30 + xp: 30 + commands: + - 'console: msg {player} &9https://en.wikipedia.org/wiki/Apple_strudel &2Click + the link for more info' + brownies: + name: '&eBrownies' + description: Create the famous American brownies. + type: onPlayer + requiredItems: + - WHEAT:16;+5 + - SUGAR:16;+5 + - EGG:16;+5 + - INK_SAC:16;+5 + - MILK_BUCKET:1 + displayItem: DARK_OAK_SLAB + lockedDisplayItem: YELLOW_STAINED_GLASS_PANE + reward: + text: 1 bucket, 1 disk, 4 emerald + items: + - BUCKET:1 + - EMERALD:4 + - MUSIC_DISC_FAR:1 + currency: 60 + xp: 60 + commands: + - 'console: msg {player} &9https://en.wikipedia.org/wiki/Chocolate_brownie + &2Click the link for more info' + repeatReward: + text: 1 bucket, 30% chance on 1 disk, 1 emerald + items: + - BUCKET:1 + - EMERALD:1 + - '{p=0.3}MUSIC_DISC_FAR:1' + currency: 30 + xp: 30 + commands: + - 'console: msg {player} &9https://en.wikipedia.org/wiki/Chocolate_brownie + &2Click the link for more info' + pastafunghi: + name: '&ePasta Funghi' + description: Create the famous Italian Pasta Funghi. + type: onPlayer + requiredItems: + - WHEAT:32;+8 + - EGG:32;+8 + - BROWN_MUSHROOM:16;+4 + - RED_MUSHROOM:16;+4 + - MILK_BUCKET:1 + displayItem: RED_MUSHROOM_BLOCK + lockedDisplayItem: YELLOW_STAINED_GLASS_PANE + reward: + text: 2 rabbits, 1 bucket + items: + - BUCKET:1 + - RABBIT_SPAWN_EGG:2 + currency: 60 + xp: 60 + commands: + - console:msg {player} &9https://en.wikipedia.org/wiki/Pasta &2Click the + link for more info + repeatReward: + text: 30% chance on 1 disk, 1 emerald + items: + - EMERALD:1 + - '{p=0.3}MUSIC_DISC_13:1' + currency: 30 + xp: 30 + commands: + - console:msg {player} &9https://en.wikipedia.org/wiki/Pasta &2Click the + link for more info + chocolate: + name: '&eBelgian chocolate' + description: Create the famous belgian chocolate. + type: onPlayer + requiredItems: + - SUGAR:16;+5 + - INK_SAC:16;+5 + - MILK_BUCKET:1 + displayItem: PIG_SPAWN_EGG + lockedDisplayItem: YELLOW_STAINED_GLASS_PANE + reward: + text: 1 bucket, 1 disk, 4 emeralds + items: + - BUCKET:1 + - EMERALD:4 + - MUSIC_DISC_MALL:1 + currency: 60 + xp: 60 + commands: + - 'console: msg {player} &9https://en.wikipedia.org/wiki/Belgian_chocolate + &2Click the link for more info' + repeatReward: + text: 1 bucket, 30% chance on 1 disk, 1 emerald + items: + - BUCKET:1 + - EMERALD:1 + - '{p=0.3}MUSIC_DISC_MALL:1' + currency: 30 + xp: 30 + commands: + - 'console: msg {player} &9https://en.wikipedia.org/wiki/Belgian_chocolate + &2Click the link for more info' + baker: + name: '&ePâtisserie' + description: Bake cakes, pumpkin pies, and cookies. + type: onPlayer + requiredItems: + - CAKE:5;+1 + - PUMPKIN_PIE:5;+1 + - COOKIE:128;+4 + displayItem: CAKE + lockedDisplayItem: YELLOW_STAINED_GLASS_PANE + reward: + text: 5 gold ore, 1 diamond + items: + - GOLD_ORE:5 + - DIAMOND:1 + currency: 80 + xp: 80 + repeatReward: + text: 2 iron ore, 1 gold ore + items: + - IRON_ORE:2 + - GOLD_ORE:1 + currency: 40 + xp: 40 + +#=============================================== +# +# Here follows the format and explanation for the challenge group and challenges setup. The single commented (#) are the most used settings +# while the double commented (##) are optionnal if you like to use those settings. +# +#ranks: +# # [text] name of the challenge Rank. +# TierX: +# #[text] The name of the challenge rank that shows up when you do /challenges (this supports capitals and colorcodes). +# name: '&aCuston Challenges rank name' +# # [itemid] The itemid of the item to be displayed in the challenge menu for complete challenges. +# displayItem: 3 +# # [integer] The time in hours before required items reset to default (tis overwrites the Main reset time) +# resetInHours: 20 +# # The requierments will allow when a challenge groups will be available to see and complete for players. +# requires: +# # [integer] The number of tasks per rank that can be left uncompleted to advance to the next rank. For example, if you have 4 challenges +# # with a rankLeeway of 1, a player would only need to complete 3 to advance to the next rank. +# # A rankLeeway of 0 would require them all. +# rankLeeway: 8 +# # [text] Challenges that has to be completed before this group will show in the Challenges GUI +# challenges: +# - a challenge name +#=============================================== +# challenges: +# #[text] The name of the challenge. All challenge names should be lower case. +# defaultchallengename: +# #[text] The name of the challenge that shows up when you do /challenges (this supports capitals and colorcodes). +# name: '&a' +# # [text] What the player sees when they do /challenges +# description: +# # [onIsland/onPlayer/islandLevel] This tells whether the required blocks/items should be in the player's inventory or on their island +# # When using onIsland, the player must be 10 blocks away from the required blocks on his island. +# # When using onIsland, the default radius distance is set to 10, all items requierd must be within this distance. +# # When using islandLevel, the 'requiredItems' field should be the island level required. The player must use /island level first to update his level. +# type: onPlayer +## type: islandLevel +## type: onIsland +# # To override the default radius when using onIsland, uncommend the "radius: " and fill in your own value. +## radius: +# # [itemid list] The itemid:count of the items required for the challenge. +# requiredItems: '' +# # [true/false] If the challenge can repeated or not. +# # [itemid] The itemid of the item to be displayed in the challenge menu for complete challenges. +# displayItem: +## tool: +# # [integer] The time in hours before required items reset to default (this overwrites the Main and Ranks reset times). +## resetInHours: +# # [true/false] Take required items on completing a challenge. +# # # reward section to reward the player for completing the challenge. +# reward: +# # [text] Description of the reward. If omitted §4Unknown will be shown. +# text: +# # [itemid list] The itemid::count of the reward to give the player for completing the challenge. +# items: +## # [permission node] A permission granted for completion. Multiple permissions are space-separated, i.e. - "test.1 test.2" (use none to not give a permission) +# # # [integer] How much currency to give for the first time completion. (requires an economy plugin) +# currency: 0 +# # [integer] How much xp to give to the player for the first time completion. +# xp: 0 +# # Executes the given command upon completion. Prepend with "op" or "console" to run the commands as OP or from the Console. Examples: +# # commands: +# # - 'op: me are the GOD of things' +# # - 'console: give {party} aweseomestuff 32' +# # Possible command arguments are: +# # {player} - The name of the player +# # {playerName} - The display name of the player +# # {challenge} - The name of the challenge +# # {position} - The position of the player +# # {party} - Execute the command once for each member of the party (substituting the name) +## commands: +## - op: +## - console: +# # reward section to reward the player for completing a repeated challenge. +# repeatReward: +# # [text] Description of the reward. If omitted §4Unknown will be shown. +# text: +# # [itemid list] The itemid::count of the reward to give the player for completing the challenge. +# items: +## # [integer] How much currency to give for the first time completion. (requires an economy plugin) +# currency: 0 +# # [integer] How much xp to give to the player for the first time completion. +# xp: 0 +# # Executes the given command upon completion. Prepend with "op" or "console" to run the commands as OP or from the Console. Examples: +# # commands: +# # - 'op: me are the GOD of things' +# # - 'console: give {party} aweseomestuff 32' +# # Possible command arguments are: +# # {player} - The name of the player +# # {playerName} - The display name of the player +# # {challenge} - The name of the challenge +# # {position} - The position of the player +# # {party} - Execute the command once for each member of the party (substituting the name) +## commands: +## - op: +## - console: +# +# All commented settings (#) in the above challenges (not the explanation) can be removed to clean up the test-challenges.yml. +# +# DO NOT CHANGE! +version: 106 diff --git a/uSkyBlock-Core/src/test/resources/us/talabrek/ultimateskyblock/imports/test-challenges-expected.yml b/uSkyBlock-Core/src/test/resources/us/talabrek/ultimateskyblock/imports/test-challenges-expected.yml new file mode 100644 index 000000000..968e4a745 --- /dev/null +++ b/uSkyBlock-Core/src/test/resources/us/talabrek/ultimateskyblock/imports/test-challenges-expected.yml @@ -0,0 +1,185 @@ +# ====================================================================================================================== +# +# Here follows the format and explanation for the challenge group and challenges setup. The single commented (#) are the +# most used settings while the double commented (##) are optional if you like to use those settings. +# +# Item type format +# Item types are defined as in the minecraft /give command, i.e. with their minecraft key and possible components in +# square brackets. For example, 'minecraft:diamond_sword[damage=42]'. Refer to the Minecraft wiki for more information: +# https://minecraft.wiki/w/Data_component_format. You can also use the command '/usb iteminfo' to get the information. +# +# This item type is supplemented with additional information depending on where it is used: +# +# display-item: +# An item to be displayed in a GUI. It only defines the item type as above, without any additional information. +# For example: 'cobblestone', 'minecraft:stone', 'diamond_sword[damage=42]'. +# +# item-requirement: :[;+] +# An item requirement for a challenge. The amount is the number of items required. The optional + is the number +# of items to add to the required amount for each repeat of the challenge. For example, 'cobblestone:64;+16' would +# require 64 cobblestone for the first completion and 80 cobblestone for the second completion. Other options are +# -, *, and / for subtraction, multiplication, and division, respectively. For example, 'cobblestone:64;*2' would +# require 64 cobblestone for the first completion, 128 cobblestone for the second completion, and 256 cobblestone for +# the third completion. +# +# item-reward: [{p=}]: +# An item reward for a challenge. The amount is the number of items to give. The optional {p=} is the +# probability of the item being given. For example, 'cobblestone:64' would give 64 cobblestone every time the challenge +# is completed, while '{p=0.1}cobblestone:64' would give 64 cobblestone 10% of the time. +# +# ====================================================================================================================== +# All challenges are defined in the ranks section. Each rank is a tier of challenges that players can complete. +# +# ranks: +# # [text] name of the challenge Rank. +# TierX: +# # [text] The name of the challenge rank that shows when you do /challenges (supports capitals and color codes). +# name: '&aCustom Challenges rank name' +# # [display-item] The item to be displayed in the challenge menu for completed challenges. +# displayItem: 'cyan_terracotta' +# # [integer] The time in hours before required items reset to default (this overwrites the main reset time) +# resetInHours: 20 +# # These requirements controls when a challenge group will be available to a player. +# requires: +# # [integer] The number of tasks per rank that can be left uncompleted to advance to the next rank. For example, +# if you have 4 challenges with a rankLeeway of 1, a player would only need to complete 3 to advance to +# the next rank. A rankLeeway of 0 would require them all. +# rankLeeway: 8 +# # [List[text]] Challenges that have to be completed before this group will available to a player. +# challenges: +# - a challenge name +# ====================================================================================================================== +# challenges: +# # [text] The name of the challenge. All challenge names should be lower-case. +# defaultchallenge: +# # [text] The name of the challenge that shows in /challenges (this supports capitals and color codes). +# name: '&a Default Challenge' +# # [text] The descriptions players see when they do /challenges +# description: +# # [onIsland/onPlayer/islandLevel] This defines whether the required blocks/items should be in the player's +# # inventory or on their island. When using onIsland, the player must within 10 blocks from the required blocks +# # on his island. When using islandLevel, the 'requiredItems' field should be the island level required. The +# # player must use /island level first to update their level. +# type: onPlayer +# ## type: islandLevel +# ## type: onIsland +# # [integer] Overrides the default radius of 10 blocks when using onIsland. +# ## radius: 20 +# # List[item-requirement] The items required to complete the challenge. +# requiredItems: +# - stone:64;+16 +# - cobblestone:64;+16 +# # List[block-requirement] The blocks required to complete the challenge. +# requiredBlocks: +# - stone:64 +# - cobblestone:64 +# # [true/false] If the challenge can be repeated or not. +# ## repeatable: true +# # [integer] The maximum number of times the challenge can be completed. Overrides the default repeatLimit. +# # A value of 0 means unlimited repeats. +# ## repeatLimit: 5 +# # [display-item] The item to be displayed in the challenge menu for completed challenges. +# displayItem: cobblestone +# # [integer] The time in hours before required items reset to default (overwrites the main and rank defaults). +# ## resetInHours: 4 +# # [true/false] Take required items on completing a challenge. +# ## takeItems: true +# # The rewards players get for completing the challenge +# reward: +# # [text] Description of the reward. +# text: 'Mossy cobblestone and an iron pickaxe with unbreaking 1' +# # [List[item-requirement]] A list of items given to the player for completing the challenge. +# items: +# - mossy_cobblestone:16 +# - iron_pickaxe[enchantments={levels:{unbreaking:1}}]:1 +# # [permission node] A permission granted for completion. Multiple permissions are space-separated. +# ## permission: 'test.permission' +# # [integer] How much currency to give for completion. (requires an economy plugin) +# ## currency: 0 +# # [integer] How much xp to give to the player for completion. +# ## xp: 0 +# # [List[Text]] Executes the given command upon completion. Prepend with "op" or "console" to run the commands +# as OP or from the Console. Examples: +# # Possible command arguments are: +# # {player} - The name of the player +# # {playerName} - The display name of the player +# # {challenge} - The name of the challenge +# # {position} - The position of the player +# # {party} - Execute the command once for each member of the party (substituting the name) +# ## commands: +# ## - 'op: me are the GOD of things' +# ## - 'console: give {party} aweseomestuff 32' +# # reward section to reward the player for completing a repeated challenge (any time after the first). The +# # structure is identical to the 'reward' section. +# repeatReward: +# text: 'Mossy cobblestone' +# items: +# - mossy_cobblestone:16 +# +# ====================================================================================================================== + +# Unrelated items - should be preserved +allowChallenges: true + +challengeColor: '&e' + +# Unrelated item that should be converted +ISLAND: + # Comment should be preserved + lockedDisplayItem: blue_stained_glass_pane # Ideally inline comments are also preserved + +# Challenge items - items should be converted + +path: + to: + name: '&7Novice' + # Simple display item + displayItem: cyan_terracotta + resetInHours: 20 + challenges: + testchallenge: + name: '&7Only for testing' + description: Not a real challenge. + type: onPlayer + # Mix of required items + # enchanted_book{StoredEnchantments:[{id:34,lvl:3}]} + requiredItems: + - cobblestone:64;+2 + - iron_ingot:100 + - enchanted_book[]:1 + - white_wool:32;-1 + - diamond:1;*1 + - invalid_item:10 + displayItem: cobblestone + # Complex display item with meta + # enchanted_book{StoredEnchantments:[{id:34,lvl:3}]} + lockedDisplayItem: enchanted_book[] + resetInHours: 12 + reward: + text: Reward to test the converter + # enchanted_book{StoredEnchantments:[{id:34,lvl:3}]} + items: + - leather:3 + - '{p=0.2}book:1' + - '{p=0.99}enchanted_book[]:3' + currency: 10 + xp: 10 + commands: + - op:test command + repeatReward: + text: This should work the same + items: + - leather:1 + - '{p=0.1}book:1' + currency: 5 + xp: 5 + +# This file has been updated to version 107. Please check the changes made in this version. +# Changes in this version: +# - Items are now specified in the new component format. +# - Refer to the config header for the new format. +# - NBT tags are not automatically converted. They have been moved to the comments, please check them manually. +# You can use the following converter to convert the old item specifications to the new format: +# https://docs.papermc.io/misc/tools/item-command-converter +# DO NOT CHANGE THE VERSION! You will break the conversion and unexpected things will happen! +version: 107 diff --git a/uSkyBlock-Core/src/test/resources/us/talabrek/ultimateskyblock/imports/test-challenges.yml b/uSkyBlock-Core/src/test/resources/us/talabrek/ultimateskyblock/imports/test-challenges.yml new file mode 100644 index 000000000..b59a71ed8 --- /dev/null +++ b/uSkyBlock-Core/src/test/resources/us/talabrek/ultimateskyblock/imports/test-challenges.yml @@ -0,0 +1,58 @@ +# Header Comments +# Make sure these are preserved + +# Unrelated items - should be preserved +allowChallenges: true + +challengeColor: '&e' + +# Unrelated item that should be converted +ISLAND: + # Comment should be preserved + lockedDisplayItem: BLUE_STAINED_GLASS_PANE # Ideally inline comments are also preserved + +# Challenge items - items should be converted + +path: + to: + name: '&7Novice' + # Simple display item + displayItem: CYAN_TERRACOTTA + resetInHours: 20 + challenges: + testchallenge: + name: '&7Only for testing' + description: Not a real challenge. + type: onPlayer + # Mix of required items + requiredItems: + - COBBLESTONE:64;+2 + - IRON_INGOT:100 + - ENCHANTED_BOOK{StoredEnchantments:[{id:34,lvl:3}]}:1 + - WHITE_WOOL:32;-1 + - DIAMOND:1;*1 + - INVALID_ITEM:10 + displayItem: COBBLESTONE + # Complex display item with meta + lockedDisplayItem: 'ENCHANTED_BOOK {StoredEnchantments:[{id:34,lvl:3}]}' + resetInHours: 12 + reward: + text: Reward to test the converter + items: + - LEATHER:3 + - '{p=0.2}BOOK:1' + - '{p=0.99}ENCHANTED_BOOK:3 {StoredEnchantments:[{id:34,lvl:3}]}' + currency: 10 + xp: 10 + commands: + - op:test command + repeatReward: + text: This should work the same + items: + - LEATHER:1 + - '{p=0.1}BOOK:1' + currency: 5 + xp: 5 + +# Here used to be the old config description. It should be removed and added as header. +version: 106 diff --git a/uSkyBlock-FAWE/build.gradle.kts b/uSkyBlock-FAWE/build.gradle.kts new file mode 100644 index 000000000..5d97a5a1d --- /dev/null +++ b/uSkyBlock-FAWE/build.gradle.kts @@ -0,0 +1,16 @@ +/* + * This file was generated by the Gradle 'init' task. + */ + + +dependencies { + implementation(project(":bukkit-utils")) + implementation(project(":uSkyBlock-API")) + implementation(project(":uSkyBlock-Core")) + compileOnly("com.fastasyncworldedit:FastAsyncWorldEdit-Core:2.13.0") + compileOnly("com.fastasyncworldedit:FastAsyncWorldEdit-Bukkit:2.13.0") + compileOnly("org.spigotmc:spigot-api:1.20.6-R0.1-SNAPSHOT") + compileOnly("com.sk89q.worldedit:worldedit-bukkit:7.2.19") +} + +description = "uSkyBlock-FAWE" diff --git a/uSkyBlock-FAWE/pom.xml b/uSkyBlock-FAWE/pom.xml deleted file mode 100644 index 32171f6f0..000000000 --- a/uSkyBlock-FAWE/pom.xml +++ /dev/null @@ -1,102 +0,0 @@ - - - - uSkyBlock - uSkyBlock - 2.8.8-SNAPSHOT - - 4.0.0 - uSkyBlock-FAWE - - - false - - - - - fawe-jenkins - https://ci.athion.net/job/FastAsyncWorldEdit-Breaking/ws/mvn - - - - - - com.boydti - fawe-api - latest - provided - - - uSkyBlock - uSkyBlock-Core - - - org.bukkit - bukkit - 1.13.2-R0.1-SNAPSHOT - true - compile - - - - - - internal.repo - Temporary Staging Repository - file://${project.build.directory}/mvn-repo - - - - - - - org.apache.maven.plugins - maven-shade-plugin - 3.2.1 - - - package - - shade - - - false - true - - - dk.lockfuglsang.minecraft:po-utils - - - - - dk.lockfuglsang.minecraft - us.talabrek.ultimateskyblock.utils - - - - - - - - org.apache.maven.plugins - maven-javadoc-plugin - 3.1.0 - - private - false - none - - - - - javadoc - - deploy - - - - - - \ No newline at end of file diff --git a/uSkyBlock-FAWE/src/main/java/us/talabrek/ultimateskyblock/handler/asyncworldedit/FAWEAdaptor.java b/uSkyBlock-FAWE/src/main/java/us/talabrek/ultimateskyblock/handler/asyncworldedit/FAWEAdaptor.java index 566c18af0..fb495b684 100644 --- a/uSkyBlock-FAWE/src/main/java/us/talabrek/ultimateskyblock/handler/asyncworldedit/FAWEAdaptor.java +++ b/uSkyBlock-FAWE/src/main/java/us/talabrek/ultimateskyblock/handler/asyncworldedit/FAWEAdaptor.java @@ -1,18 +1,18 @@ package us.talabrek.ultimateskyblock.handler.asyncworldedit; -import com.boydti.fawe.util.EditSessionBuilder; import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.bukkit.BukkitWorld; +import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormat; import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormats; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.world.World; import org.bukkit.Location; import org.bukkit.entity.Player; -import org.bukkit.plugin.Plugin; import us.talabrek.ultimateskyblock.player.PlayerPerk; import us.talabrek.ultimateskyblock.uSkyBlock; -import us.talabrek.ultimateskyblock.util.LogUtil; +import us.talabrek.ultimateskyblock.util.Scheduler; import java.io.File; import java.io.IOException; @@ -24,17 +24,16 @@ */ public class FAWEAdaptor implements AWEAdaptor { private static final Logger log = Logger.getLogger(FAWEAdaptor.class.getName()); - private uSkyBlock plugin; + private Scheduler scheduler; @Override - public void onEnable(Plugin plugin) { - this.plugin = (uSkyBlock) plugin; - log.finer("- FAWE debugging: Location of WorldEdit EditSession: " + EditSession.class.getResource('/' + EditSession.class.getName().replace('.', '/') + ".class")); + public void onEnable(uSkyBlock plugin) { + this.scheduler = plugin.getScheduler(); } @Override - public void onDisable(Plugin plugin) { - this.plugin = null; + public void onDisable(uSkyBlock plugin) { + this.scheduler = null; } @Override @@ -43,21 +42,25 @@ public synchronized void registerCompletion(Player player) { @Override public void loadIslandSchematic(final File file, final Location origin, final PlayerPerk playerPerk) { - plugin.async(() -> { - log.finer("Trying to load schematic " + file); + scheduler.async(() -> { if (file == null || !file.exists() || !file.canRead()) { - LogUtil.log(Level.WARNING, "Unable to load schematic " + file); + log.log(Level.WARNING, "Unable to load schematic {}", file); + return; } + + ClipboardFormat format = ClipboardFormats.findByFile(file); + if (format == null) { + log.log(Level.SEVERE, "Unable to find schematic format for file {}", file); + return; + } + BlockVector3 to = BlockVector3.at(origin.getBlockX(), origin.getBlockY(), origin.getBlockZ()); EditSession editSession = getEditSession(playerPerk, origin); - try { - ClipboardFormats - .findByFile(file) - .load(file) - .paste(editSession, to, false); + try(var schematic = format.load(file)) { + schematic.paste(editSession, to, false); editSession.flushQueue(); - } catch (IOException e) { - log.log(Level.INFO, "Unable to paste schematic " + file, e); + } catch (IOException ex) { + log.log(Level.INFO, "Unable to paste schematic " + file, ex); } }); } @@ -67,15 +70,14 @@ private synchronized EditSession getEditSession(PlayerPerk playerPerk, Location } public EditSession createEditSession(World bukkitWorld, int maxBlocks) { - return new EditSessionBuilder(bukkitWorld).fastmode(true).build(); + return WorldEdit.getInstance().newEditSessionBuilder().world(bukkitWorld).fastMode(true).build(); } @Override public void regenerate(final Region region, final Runnable onCompletion) { // NOTE: Running this asynchronous MIGHT be a bit dangereous! Since pasting could interfere - plugin.async(() -> { - try { - EditSession editSession = createEditSession(region.getWorld(), -1); + scheduler.async(() -> { + try(var editSession = createEditSession(region.getWorld(), -1)) { editSession.regenerate(region); editSession.flushQueue(); } finally { diff --git a/uSkyBlock-Plugin/build.gradle.kts b/uSkyBlock-Plugin/build.gradle.kts new file mode 100644 index 000000000..57d2e20fa --- /dev/null +++ b/uSkyBlock-Plugin/build.gradle.kts @@ -0,0 +1,36 @@ +import java.time.LocalDate +import java.time.format.DateTimeFormatter + +/* + * This file was generated by the Gradle 'init' task. + */ + + +plugins { + java + `maven-publish` + id("com.gradleup.shadow") version "8.3.6" +} + +val version = "cong-" + LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")) // 替换为实际版本号 + + +tasks { + register("buildPlugin") { + group = "plugin" + from(sourceSets.main.map { it.output }) + archiveClassifier.set("") + configurations = listOf(project.configurations.runtimeClasspath.get()) + doLast { + println(archiveFile.get()) + } + } +} + +dependencies { + implementation(project(":uSkyBlock-API")) + implementation(project(":uSkyBlock-Core")) + implementation(project(":uSkyBlock-FAWE")) +} + +description = "uSkyBlock-Plugin" diff --git a/uSkyBlock-Plugin/pom.xml b/uSkyBlock-Plugin/pom.xml deleted file mode 100644 index 924a784ea..000000000 --- a/uSkyBlock-Plugin/pom.xml +++ /dev/null @@ -1,63 +0,0 @@ - - - - uSkyBlock - uSkyBlock - 2.8.8-SNAPSHOT - - 4.0.0 - jar - uSkyBlock-Plugin - - - github - true - - - - uSkyBlock-${project.version} - - - maven-assembly-plugin - 3.1.1 - - - distro-assembly - compile - - single - - - - src/assembly/plugin.xml - - - - - - - maven-jar-plugin - - ${project.build.directory}/uSkyBlock-${project.version}-Plugin - - - - - - - - com.github.rlf - uSkyBlock-API - - - uSkyBlock - uSkyBlock-Core - - - uSkyBlock - uSkyBlock-FAWE - - - \ No newline at end of file diff --git a/uSkyBlock-Plugin/src/assembly/plugin.xml b/uSkyBlock-Plugin/src/assembly/plugin.xml index 531cc37a4..c152c895a 100644 --- a/uSkyBlock-Plugin/src/assembly/plugin.xml +++ b/uSkyBlock-Plugin/src/assembly/plugin.xml @@ -10,10 +10,11 @@ com.github.rlf:uSkyBlock-API - uSkyBlock:uSkyBlock-Core - uSkyBlock:uSkyBlock-FAWE + ovh.uskyblock:uSkyBlock-APIv2 + ovh.uskyblock:uSkyBlock-Core + ovh.uskyblock:uSkyBlock-FAWE true - \ No newline at end of file + diff --git a/uSkyBlock-Plugin/src/main/resources/version.json b/uSkyBlock-Plugin/src/main/resources/version.json new file mode 100644 index 000000000..28fc1049e --- /dev/null +++ b/uSkyBlock-Plugin/src/main/resources/version.json @@ -0,0 +1 @@ +{"version": "cong", "minecraft-version": "1.21.4-R0.1-SNAPSHOT"}