diff --git a/.github/ISSUE_TEMPLATE/bug-report.yaml b/.github/ISSUE_TEMPLATE/bug-report.yaml index b09a82e1cc..e1e0d1d518 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.yaml +++ b/.github/ISSUE_TEMPLATE/bug-report.yaml @@ -101,8 +101,6 @@ body: options: - Spigot - Paper - - Tuinity - - Yatopia - Purpur - Airplane - Other (please specify in your description) @@ -115,10 +113,11 @@ body: label: '🎮 Minecraft Version' description: 'Please select the Minecraft version of the server' options: + - 1.20.x + - 1.19.x + - 1.18.x - 1.17.x - 1.16.x - - 1.15.x - - 1.14.x - (Older versions are not supported) - id: slimefun-version diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 552758aa29..0ebdee6426 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -17,6 +17,7 @@ - [ ] I have fully tested the proposed changes and promise that they will not break everything into chaos. - [ ] I have also tested the proposed changes in combination with various popular addons and can confirm my changes do not break them. +- [ ] I have made sure that the proposed changes do not break compatibility across the supported Minecraft versions (1.16.* - 1.20.*). - [ ] I followed the existing code standards and didn't mess up the formatting. - [ ] I did my best to add documentation to any public classes or methods I added. - [ ] I have added `Nonnull` and `Nullable` annotations to my methods to indicate their behaviour for null values diff --git a/.github/workflows/auto-approve.yml b/.github/workflows/auto-approve.yml index 3dd7a4b507..4d44968a97 100644 --- a/.github/workflows/auto-approve.yml +++ b/.github/workflows/auto-approve.yml @@ -2,18 +2,25 @@ name: Auto approve on: pull_request +permissions: + contents: read + jobs: auto-approve: name: Auto approve Pull Request runs-on: ubuntu-latest - ## Only run this on the main repo + # for hmarr/auto-approve-action to approve PRs + permissions: + pull-requests: write + + # Only run this on the main repo if: github.event.pull_request.head.repo.full_name == 'Slimefun/Slimefun4' steps: - name: Approve via actions - uses: hmarr/auto-approve-action@v2.1.0 + uses: hmarr/auto-approve-action@v3.2.1 if: github.actor == 'TheBusyBot' || github.actor == 'renovate[bot]' with: github-token: "${{ secrets.GITHUB_TOKEN }}" diff --git a/.github/workflows/auto-squash.yml b/.github/workflows/auto-squash.yml index a310b8930d..050acaec2e 100644 --- a/.github/workflows/auto-squash.yml +++ b/.github/workflows/auto-squash.yml @@ -1,4 +1,4 @@ -name: Auto squash Crowdin updates +name: Auto squash pull requests on: pull_request_review: @@ -12,9 +12,9 @@ on: status: {} jobs: - autosquash: + autosquash-crowdin: - name: Auto squash + name: Auto squash (Crowdin) runs-on: ubuntu-latest ## Only run this on the main repo @@ -22,7 +22,7 @@ jobs: steps: - name: Auto squash - uses: pascalgn/automerge-action@v0.14.3 + uses: pascalgn/automerge-action@v0.15.6 env: GITHUB_TOKEN: ${{ secrets.ACCESS_TOKEN }} UPDATE_RETRIES: 0 @@ -31,3 +31,23 @@ jobs: MERGE_DELETE_BRANCH: true MERGE_LABELS: '📄 Translations Update' MERGE_COMMIT_MESSAGE: '[CI skip] New locale updates from Crowdin' + + autosquash-renovate: + + name: Auto squash (Renovate) + runs-on: ubuntu-latest + + ## Only run this on the main repo + if: github.event.pull_request.head.repo.full_name == 'Slimefun/Slimefun4' + + steps: + - name: Auto squash + uses: pascalgn/automerge-action@v0.15.6 + env: + GITHUB_TOKEN: ${{ secrets.ACCESS_TOKEN }} + UPDATE_RETRIES: 0 + MERGE_METHOD: squash + MERGE_FORKS: false + MERGE_DELETE_BRANCH: true + MERGE_LABELS: '🚨 Dependency Update' + MERGE_COMMIT_MESSAGE: '[CI skip] ${{ github.event.pull_request.title }}' diff --git a/.github/workflows/closed-issues-reason.yml b/.github/workflows/closed-issues-reason.yml deleted file mode 100644 index f20726e9ef..0000000000 --- a/.github/workflows/closed-issues-reason.yml +++ /dev/null @@ -1,60 +0,0 @@ -name: Respond to closed Issue - -on: - issues: - types: [closed] - -jobs: - comment: - - name: Comment on Issue - runs-on: ubuntu-latest - if: contains(github.event.issue.labels.*.name, '🐞 Bug Report') - - steps: - - name: Query recent commits - uses: TheBusyBiscuit/recently-closed-issues@1.1.0 - id: resolved - with: - token: ${{ secrets.ACCESS_TOKEN }} - max_commits: 20 - - name: Add label - if: contains(steps.resolved.outputs.issues, github.event.issue.number) - uses: maxkomarychev/octions/octions/issues/add-labels@master - with: - token: ${{ secrets.ACCESS_TOKEN }} - issue_number: ${{ github.event.issue.number }} - labels: '✔ Resolved' - - uses: maxkomarychev/octions/octions/issues/create-comment@master - id: comment - if: contains(steps.resolved.outputs.issues, github.event.issue.number) == false - with: - token: ${{ secrets.ACCESS_TOKEN }} - issue_number: ${{ github.event.issue.number }} - body: |- - Your issue has been closed by an admin, it may fall under one or more of the following categories. - **Please wait for an admin to tick off the points that apply.** -
- - * [ ] You did not follow our template. Please follow the Issue template to help us identify your issue more effectively. - * [ ] You did not provide any information about your versions (We absolutely need the exact version numbers that are installed on your Server, \"latest\" is not helpful) - * [ ] You did not provide a proper description to the problem. Try to write at least 4-6 sentences and/or provide screenshots or videos on how to reproduce this. - * [ ] We were unable to reproduce issue, if you think your issue still persists then please comment down below and give a better description on how to reproduce it. - * [ ] Your issue was posted in a foreign language, we only accept english issues on here. - * [ ] Your issue is not a bug, it is intended to work this way. - * [ ] Your issue is not really a bug, it is a limitation or simply something we have no control over. - * [ ] Your issue is not a bug, please only use this issue tracker to report bugs. Any other kind of communication should happen on discord. - * [ ] Your issue has already been reported before, it is a duplicate. Check the other issues first before posting! - * [ ] You posted an error without using pastebin. Please always post errors via pastebin otherwise they become nearly unreadable. - * [ ] You seem to be reporting multiple bugs at once. Please make a separate issue for each bug you encountered, so we can properly handle them individually. - * [ ] Your issue has already been fixed in a later version of Slimefun, you should update. - * [ ] You are using an outdated and unsupported version of Slimefun, again, you should update. - * [ ] You are using an unofficially modified build of Slimefun. We only support official versions of Slimefun - for obvious reasons. - * [ ] You are using an unsupported version of Minecraft. We only provide support for the Minecraft versions Slimefun was developed for, older versions are not supported anymore. - * [ ] You are using a \"stable\" version of Slimefun (prefixed with \"RC - \"), your issue may have been fixed in a development build, so we only accept bug reports from those. - * [ ] You are on the wrong issue tracker. We would like to remind you that this Issue Tracker is **only for Slimefun**. To report bugs on any addons, head to the corresponding issue tracker of that addon. -
- - Please respond below, if you have any further questions. - Do **not** open a new Issue unless explicitly told otherwise, comment below or edit your post instead.
- Make sure to check out our article on [How to report bugs](https://github.com/Slimefun/Slimefun4/wiki/How-to-report-bugs) for even more information. diff --git a/.github/workflows/discord-webhook.yml b/.github/workflows/discord-webhook.yml index 5134fbb6db..9288b679d0 100644 --- a/.github/workflows/discord-webhook.yml +++ b/.github/workflows/discord-webhook.yml @@ -7,6 +7,9 @@ on: - '!src/main/resources/languages/**' - 'pom.xml' +permissions: + contents: read + jobs: report: @@ -18,18 +21,18 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v2.3.4 + uses: actions/checkout@v3.5.3 - - name: Set up Java JDK 11 - uses: actions/setup-java@v2.3.0 + - name: Set up Java JDK 17 + uses: actions/setup-java@v3.11.0 with: distribution: 'adopt' - java-version: '11' + java-version: '17' java-package: jdk architecture: x64 - name: Cache Maven packages - uses: actions/cache@v2 + uses: actions/cache@v3 with: path: ~/.m2 key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} diff --git a/.github/workflows/duplicates.yml b/.github/workflows/duplicates.yml index a8883a4087..7cb94d0dcb 100644 --- a/.github/workflows/duplicates.yml +++ b/.github/workflows/duplicates.yml @@ -4,6 +4,10 @@ on: issue_comment: types: [created] +permissions: + contents: read + issues: write + jobs: comment: diff --git a/.github/workflows/json-validator.yml b/.github/workflows/json-validator.yml index de56bec679..9d8364ceda 100644 --- a/.github/workflows/json-validator.yml +++ b/.github/workflows/json-validator.yml @@ -8,6 +8,9 @@ on: paths: - 'src/main/resources/wiki.json' +permissions: + contents: read + jobs: validate: @@ -16,8 +19,8 @@ jobs: steps: - name: Checkout Repository - uses: actions/checkout@v2 - - name: Validate JSON + uses: actions/checkout@v3 + - name: Validate wiki.json uses: docker://orrosenblatt/validate-json-action:latest@sha256:02370758b8b199e0477da11ecfdd498c75c561685056b5c31b925a4ab95df7f4 env: INPUT_SCHEMA: '.github/configs/wiki-schema.json' diff --git a/.github/workflows/label-resolved-issues.yml b/.github/workflows/label-resolved-issues.yml new file mode 100644 index 0000000000..5a54fb3144 --- /dev/null +++ b/.github/workflows/label-resolved-issues.yml @@ -0,0 +1,32 @@ +name: Label resolved issues + +on: + issues: + types: [closed] + +permissions: + contents: read + issues: write + +jobs: + label: + + name: Label Issue + runs-on: ubuntu-latest + if: contains(github.event.issue.labels.*.name, '🐞 Bug Report') + + steps: + - name: Query recent commits + uses: TheBusyBiscuit/recently-closed-issues@1.1.0 + id: resolved + with: + token: ${{ secrets.ACCESS_TOKEN }} + max_commits: 20 + + - name: Add label + if: contains(steps.resolved.outputs.issues, github.event.issue.number) + uses: maxkomarychev/octions/octions/issues/add-labels@master + with: + token: ${{ secrets.ACCESS_TOKEN }} + issue_number: ${{ github.event.issue.number }} + labels: '✔ Resolved' diff --git a/.github/workflows/maven-compiler.yml b/.github/workflows/maven-compiler.yml index a3dd44618b..bd3763ff75 100644 --- a/.github/workflows/maven-compiler.yml +++ b/.github/workflows/maven-compiler.yml @@ -12,6 +12,10 @@ on: - '.github/workflows/**' - 'src/**' - 'pom.xml' + - 'CHANGELOG.md' + +permissions: + contents: read jobs: build: @@ -21,16 +25,18 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v2 + uses: actions/checkout@v3 - - name: Set up JDK 1.8 - uses: actions/setup-java@v2.3.0 + - name: Set up JDK 17 + uses: actions/setup-java@v3.11.0 with: distribution: 'adopt' - java-version: '8' + java-version: '17' + java-package: jdk + architecture: x64 - name: Cache Maven packages - uses: actions/cache@v2 + uses: actions/cache@v3 with: path: ~/.m2 key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} diff --git a/.github/workflows/merge-conflicts.yml b/.github/workflows/merge-conflicts.yml index 71b8d025f0..2f5f304c16 100644 --- a/.github/workflows/merge-conflicts.yml +++ b/.github/workflows/merge-conflicts.yml @@ -5,6 +5,10 @@ on: branches: - master +permissions: + contents: read + pull-requests: write + jobs: validate: diff --git a/.github/workflows/pr-labels.yml b/.github/workflows/pr-labels.yml index d2638bbf06..d6e0720bb2 100644 --- a/.github/workflows/pr-labels.yml +++ b/.github/workflows/pr-labels.yml @@ -1,10 +1,14 @@ name: Pull Request Labels on: - pull_request: + pull_request_target: types: - opened +permissions: + contents: read + pull-requests: write + jobs: pr-labeler: @@ -27,7 +31,7 @@ jobs: api: '🔧 API' compatibility: '🤝 Compatibility' - - uses: thollander/actions-comment-pull-request@1.0.3 + - uses: thollander/actions-comment-pull-request@v2.4.0 name: Leave a comment about the applied label if: ${{ steps.labeller.outputs.applied != 0 }} with: @@ -36,7 +40,7 @@ jobs: Your Pull Request was automatically labelled as: "${{ steps.labeller.outputs.applied }}" Thank you for contributing to this project! ❤️ - - uses: thollander/actions-comment-pull-request@1.0.3 + - uses: thollander/actions-comment-pull-request@v2.4.0 name: Leave a comment about our branch naming convention if: ${{ steps.labeller.outputs.applied == 0 }} with: diff --git a/.github/workflows/preview-builds.yml b/.github/workflows/preview-builds.yml new file mode 100644 index 0000000000..947ded7153 --- /dev/null +++ b/.github/workflows/preview-builds.yml @@ -0,0 +1,82 @@ +name: Preview builds + +on: + workflow_run: + workflows: ["Pull Request"] + types: + - completed + +permissions: + contents: read + pull-requests: write + +jobs: + preview: + if: ${{ github.repository_owner == 'Slimefun' && github.event.workflow_run.conclusion == 'success' }} + name: Build and Publish the jar + runs-on: ubuntu-latest + + steps: + # Kinda jank way to grab the PR and commit hash and then download the artifact + # TODO: Move this code to our own mini-action + - name: Grab PR & run ID and download the artifact + uses: actions/github-script@v6 + with: + script: | + const allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({ + owner: context.repo.owner, + repo: context.repo.repo, + run_id: context.payload.workflow_run.id, + }); + + for (const artifact of allArtifacts.data.artifacts) { + // Extract the PR number and commit hash from the artifact name + const match = /^slimefun-(\d+)-([a-f0-9]{8})$/.exec(artifact.name); + if (match) { + require("fs").appendFileSync( + process.env.GITHUB_ENV, + `\nPR_NUMBER=${match[1]}` + + `\nCOMMIT_HASH=${match[2]}` + ); + + const download = await github.rest.actions.downloadArtifact({ + owner: context.repo.owner, + repo: context.repo.repo, + artifact_id: artifact.id, + archive_format: 'zip', + }); + require('fs').writeFileSync(`${process.env.GITHUB_WORKSPACE}/preview.zip`, Buffer.from(download.data)) + + break; + } + } + + # Unzip the artifact + - name: Unzip + run: | + unzip preview.zip + rm preview.zip + mv 'Slimefun vPreview Build #${{ env.PR_NUMBER }}-${{ env.COMMIT_HASH }}.jar' preview.jar + + - name: Upload to preview service + run: | + curl -X POST \ + -H 'Authorization: ${{ secrets.PUBLISH_TOKEN }}' \ + -H "X-Checksum: $(sha256sum 'preview.jar' | awk '{print $1}')" \ + --data-binary '@preview.jar' \ + https://preview-builds.walshy.dev/upload/Slimefun/${{ env.PR_NUMBER }}/${{ env.COMMIT_HASH }} + + - name: Post comment + uses: marocchino/sticky-pull-request-comment@v2 + with: + number: ${{ env.PR_NUMBER }} + message: | + ### Slimefun preview build + + A Slimefun preview build is available for testing! + Commit: ${{ env.COMMIT_HASH }} + + https://preview-builds.walshy.dev/download/Slimefun/${{ env.PR_NUMBER }}/${{ env.COMMIT_HASH }} + + > **Note**: This is not a supported build and is only here for the purposes of testing. + > Do not run this on a live server and do not report bugs anywhere but this PR! diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml new file mode 100644 index 0000000000..2c3a31ab8c --- /dev/null +++ b/.github/workflows/pull-request.yml @@ -0,0 +1,52 @@ +name: Pull Request + +on: + pull_request: + paths: + - '.github/workflows/**' + - 'src/**' + - 'pom.xml' + +permissions: + contents: read + +jobs: + setup-preview-build: + name: Preview build + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Set up JDK 17 + uses: actions/setup-java@v3.11.0 + with: + distribution: 'adopt' + java-version: '17' + java-package: jdk + architecture: x64 + + - name: Cache Maven packages + uses: actions/cache@v3 + with: + path: ~/.m2 + key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} + restore-keys: ${{ runner.os }}-m2 + + # Setup for the preview build + - run: | + SHORT_COMMIT_HASH=$(git rev-parse --short=8 ${{ github.sha }}) + JAR_VERSION="Preview Build #${{ github.event.number }}-$SHORT_COMMIT_HASH" + echo "SHORT_COMMIT_HASH=$SHORT_COMMIT_HASH" >> "$GITHUB_ENV" + echo "JAR_VERSION=$JAR_VERSION" >> "$GITHUB_ENV" + sed -i "s/4.9-UNOFFICIAL<\/version>/$JAR_VERSION<\/version>/g" pom.xml + + - name: Build with Maven + run: mvn package + + - name: Upload the artifact + uses: actions/upload-artifact@v3 + with: + name: slimefun-${{ github.event.number }}-${{ env.SHORT_COMMIT_HASH }} + path: 'target/Slimefun v${{ env.JAR_VERSION }}.jar' diff --git a/.github/workflows/release-candidates.yml b/.github/workflows/release-candidates.yml index 6dfa374329..1c279e172e 100644 --- a/.github/workflows/release-candidates.yml +++ b/.github/workflows/release-candidates.yml @@ -19,7 +19,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: ref: 'stable' diff --git a/.github/workflows/sonarcloud.yml b/.github/workflows/sonarcloud.yml index 802816ae75..6d499901e9 100644 --- a/.github/workflows/sonarcloud.yml +++ b/.github/workflows/sonarcloud.yml @@ -4,6 +4,9 @@ on: pull_request: types: [opened, synchronize, reopened] +permissions: + contents: read + jobs: scan: @@ -16,25 +19,27 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v2.3.4 + uses: actions/checkout@v3.5.3 with: fetch-depth: 0 - - name: Set up JDK 11 - uses: actions/setup-java@v2.3.0 + - name: Set up JDK 17 + uses: actions/setup-java@v3.11.0 with: distribution: 'adopt' - java-version: '11' + java-version: '17' + java-package: jdk + architecture: x64 - name: Cache SonarCloud packages - uses: actions/cache@v2 + uses: actions/cache@v3 with: path: ~/.sonar/cache key: ${{ runner.os }}-sonar restore-keys: ${{ runner.os }}-sonar - name: Cache Maven packages - uses: actions/cache@v2 + uses: actions/cache@v3 with: path: ~/.m2 key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} diff --git a/.github/workflows/translator-webhook.yml b/.github/workflows/translator-webhook.yml index f93a2aeff7..ec8328f899 100644 --- a/.github/workflows/translator-webhook.yml +++ b/.github/workflows/translator-webhook.yml @@ -7,6 +7,9 @@ on: paths: - 'src/main/resources/languages/en/**.yml' +permissions: + contents: read + jobs: notify: diff --git a/.github/workflows/yaml-linter.yml b/.github/workflows/yaml-linter.yml index 8f5ebd8b3b..3d87edf772 100644 --- a/.github/workflows/yaml-linter.yml +++ b/.github/workflows/yaml-linter.yml @@ -8,6 +8,9 @@ on: branches: - master +permissions: + contents: read + jobs: linter: @@ -16,8 +19,8 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: YAML Linter - uses: ibiqlik/action-yamllint@v3.0.4 + uses: ibiqlik/action-yamllint@v3.1.1 with: config_file: '.github/configs/yaml-linter.yml' diff --git a/.gitignore b/.gitignore index 7f69a29594..fda2f4a052 100644 --- a/.gitignore +++ b/.gitignore @@ -12,4 +12,5 @@ dependency-reduced-pom.xml .factorypath .project *.iml +*.bak .DS_Store \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index a455e6c9a4..ecd01fbb11 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Table of contents -- [Release Candidate 29 (TBD)](#release-candidate-29-tbd) +- [Release Candidate 36 (TBD)](#release-candidate-36-tbd) +- [Release Candidate 35 (7 Jul 2023)](#release-candidate-35-7-jul-2023) +- [Release Candidate 34 (20 Jun 2023)](#release-candidate-34-20-jun-2023) +- [Release Candidate 33 (07 Jan 2023)](#release-candidate-33-07-jan-2023) +- [Release Candidate 32 (26 Jun 2022)](#release-candidate-32-26-jun-2022) +- [Release Candidate 31 (14 Mar 2022)](#release-candidate-31-14-mar-2022) +- [Release Candidate 30 (31 Dec 2021)](#release-candidate-30-31-dec-2021) +- [Release Candidate 29 (07 Nov 2021)](#release-candidate-29-07-nov-2021) - [Release Candidate 28 (06 Sep 2021)](#release-candidate-28-06-sep-2021) - [Release Candidate 27 (03 Sep 2021)](#release-candidate-27-03-sep-2021) - [Release Candidate 26 (20 Jul 2021)](#release-candidate-26-20-jul-2021) @@ -29,23 +36,228 @@ - [Release Candidate 2 (29 Sep 2019)](#release-candidate-2-29-sep-2019) - [Release Candidate 1 (26 Sep 2019)](#release-candidate-1-26-sep-2019) -## Release Candidate 29 (TBD) +## Release Candidate 36 (TBD) #### Additions #### Changes +#### Fixes + +## Release Candidate 35 (7 Jul 2023) + +#### Additions +* Added `sounds.yml` file to configure sound effects for Slimefun +* Added preview builds to the repo, PRs will now have a build which testers can use +* (API) Added SlimefunBlockBreakEvent and SlimefunBlockPlaceEvent events for plugins/addons to implement +* (API) Added an efficient way to clear BlockStorage within a chunk - BlockStorage.clearAllBlockInfoAtChunk +* (API) Added DistinctiveItem, a way to distinguish your item with more than just ID +* (API) Added ExternallyInteractable, a way for addons to define "interactions" for blocks + +#### Changes +* Moved all sound effects to the new sound system + +#### Fixes +* Fixed recipe shift in multiblocks when items are disabled (#3286) +* Fixed backpack dupe within cargo (#3379) + +## Release Candidate 34 (20 Jun 2023) +https://thebusybiscuit.github.io/builds/TheBusyBiscuit/Slimefun4/stable/#34 + +#### Additions +* Added "Cobbled Deepslate -> Gravel" recipe to the Grind Stone +* Added "Cobbled Deepslate -> Sand" recipe to the Ore Crusher +* (API) Added EnergyNet#getGenerators() +* (API) Added EnergyNet#getCapacitors() +* (API) Added EnergyNet#getConsumers() +* Added Bamboo as a fuel type for Tier 1 Androids +* Added "Basalt -> Blackstone" recipe to the Grind Stone +* Added a way to automate salt with the Ore Washer +* Added compatibility for Minecraft 1.20 + +#### Changes +* Removed 1.14.* and 1.15.* support +* The Climbing Pick now also works on: + * Calcite + * Deepslate + * Dripstone blocks + * Smooth Basalt + * Tuff + * Clay + * Skulk +* Lumber Axe no longer works when shifting + +#### Fixes +* Fixed #3741 +* Fixed #3724 +* Fixed #3462 +* Fixed #3758 +* Fixed #3701 +* Fixed #3361 +* Fixed #3254 +* Fixed #3443 +* Fixed #3511 +* Fixed #3524 +* Fixed #3657 +* Fixed #3768 +* Fixed #3414 + +## Release Candidate 33 (07 Jan 2023) +https://thebusybiscuit.github.io/builds/TheBusyBiscuit/Slimefun4/stable/#33 + +#### Additions +* (API) Added Tinted Glass to "GLASS_BLOCKS" tag +* (API) Added "WOOL_CARPETS" tag (for compatibility across MC 1.19/1.18 tags) +* Added a new language: Persian +* Added a new language: Romanian +* (API) Added a method for item groups to allow addons to choose if they want to allow items from other addons +* Added a new option to Eletric Gold Pans: "override-output-limit" +* Added "Mud -> Clay" recipe to the Auto Drier +* Added a third tier for Freezers +* Added Glow Berry Juice + +#### Changes +* Tree Growth Accelerators can now actually cause the Tree to fully grow (1.17+ only) +* Slimefun now requires Java 16 +* "Connected / Not connected" messages for cargo nodes are now sent via the actionbar +* "/sf stats" can no longer be used if researching is disabled +* "/sf research" can no longer be used if researching is disabled +* Removed the Hercules Pickaxe from Slimefun +* If CS-CoreLib is present, Slimefun will disable itself (previously it would just error) + +#### Fixes +* Fixed #3597 +* Fixed an issue related to "Bee Wings" +* Fixed #3573 +* Fixed "round-robin" mode for cargo networks being very unreliable +* Fixed #3664 +* Fixed #3651 +* Fixed #3677 +* Fixed #3705 +* Fixed BlockPlacer being able to place disabled items + +## Release Candidate 32 (26 Jun 2022) +https://thebusybiscuit.github.io/builds/TheBusyBiscuit/Slimefun4/stable/#32 + +#### Additions +* Added Organic Food for Seagrass +* Added Organic Fertilizer for Seagrass +* Added compatibility for Minecraft 1.19 + +#### Changes +* Removed support for ChestTerminal + +#### Fixes +* Fixed #3445 +* Fixed #3504 +* Fixed #3534 +* Fixed #3538 +* Fixed #3548 +* Fixed an issue with machines being placed below y=0 + +## Release Candidate 31 (14 Mar 2022) +https://thebusybiscuit.github.io/builds/TheBusyBiscuit/Slimefun4/stable/#31 + +#### Additions +* Added Armored Jetpack +* Added Cocoa Beans as a fuel type for the Bio-Generator +* Added Beetroots and Beetroot seeds as fuel types for the Bio-Generator +* Added small and big dripleaves as fuel types for the Bio-Generator +* Added Glow Berries as a fuel type for the Bio-Generator +* Added Glow Lichen as a fuel type for the Bio-Generator +* Added Spore Blossom as a fuel type for the Bio-Generator +* Added a new item setting for Freezers to allow them to use a 9:1 "vanilla" ratio instead of 1:1 (1:1 by default, like before) +* (API) Added `PlayerProfile#hasUnlockedEverything()` to check if a player has unlocked all researches +* (API) Added `Research#getUnlocalizedName()` +* Added support for the plugin "HuskTowns" +* Added support for Minecraft 1.18.2 +* You can now pick up Slimefun blocks in creative mode using the middle mouse button +* `/sf search` no longer shows items in hidden item groups (can be overidden by a config setting) +* Fluid Pumps can now fill bottles with water +* (API) Added Shulker boxes to `ColoredMaterial` enum + +#### Changes +* (API) `BiomeMapParser` is now `public` +* (API) `BiomeMap.fromJson` now allows you to specify if you want the BiomeMap to be parsed leniently +* Some translation updates + +#### Fixes +* Fixed #3390 +* Fixed research issues for vanilla items, e.g. Trident or Totem of Undying +* Fixed #3368 +* Fixed #1315 +* Fixed #3400 +* Fixed rare issue where Slimefun would not load at all +* Fixed #3429 +* Fixed "LogBlock" integration +* Fixed "Lands" integration +* Fixed #3133 +* Fixed #3483 +* Fixed #3469 +* Fixed #3476 +* Fixed #3487 +* Fixed #3336 (again) + +## Release Candidate 30 (31 Dec 2021) +https://thebusybiscuit.github.io/builds/TheBusyBiscuit/Slimefun4/stable/#30 + +#### Additions +* Added a ton of wiki links to the guide +* (API) Added "GRAVITY_AFFECTED_BLOCKS" tag +* (API) Added "Biome-Maps" for more in-depth GEO resource configuration (developers only for now) +* (API) Added some utility methods for Biome-Maps +* Added support for 1.18 +* Added Talisman of Farmer + +#### Changes +* GEO resource distributions have been slightly adjusted +* Salt can now also generate in the Nether (as a GEO resource) + +#### Fixes +* Crimson and Warped Pressure Plates are now properly recognized as pressure plates +* Fixed #3336 +* (API) Fixed `Parachute` constructor parameter being ignored +* Fixed #3385 +* Fixed (Easter) Apple Pie recipe yielding (Christmas) Apple Pies + +## Release Candidate 29 (07 Nov 2021) +https://thebusybiscuit.github.io/builds/TheBusyBiscuit/Slimefun4/stable/#29 + +#### Additions +* Added support for deepslate ores and copper with the Hercules' Pickaxe +* The Electric Crucible now also accepts Netherrack +* The Electric Crucible now also accepts Stone +* Added the ability to shift-click in the Cheat Sheet menu +* Added the ability to break blocks normally with a Lumber Axe when sneaking +* Added an option to allow Solar Generators to operate in "night-mode" in other dimensions +* Added `/sf debug ` (This allows server owners to get more in-depth logging which they can forward to developers for better bug/lag investigations) +* Added an option to disable data backups on disable + +#### Changes +* Massive performance improvements for Cargo networks +* (API) `SolarGenerator` has a new constructor to accept capacity + #### Fixes * Fixed #3218 * Fixed #3241 +* Fixed #3248 +* Fixed #3273 +* Fixed an exploit regarding the Smithing Table +* Fixed #3265 +* Fixed #3264 +* Fixed extreme knockback caused by the Explosive Bow +* Fixed #3313 +* Fixed smithing table issue on 1.15 and lower ## Release Candidate 28 (06 Sep 2021) +https://thebusybiscuit.github.io/builds/TheBusyBiscuit/Slimefun4/stable/#28 #### Fixes * Fixed Metrics * Fixed some naming conventions and localization keys for RC-27 ## Release Candidate 27 (03 Sep 2021) +https://thebusybiscuit.github.io/builds/TheBusyBiscuit/Slimefun4/stable/#27 ### **Breaking Changes (API)** This RC brings a lot of breaking changes to the API. For more info on why we did this and what happened [please refer to our PSA](https://github.com/Slimefun/Slimefun4/pull/3139) diff --git a/README.md b/README.md index 9ed369ef11..72a597409f 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,8 @@ Here is a full summary of the differences between the two different versions of | | development (latest) | "stable" | | ------------------ | -------- | -------- | -| **Minecraft version(s)** | :video_game: **1.14.\* - 1.17.\*** | :video_game: **1.14.\* - 1.17.\*** | +| **Minecraft version(s)** | :video_game: **1.16.\* - 1.20.\*** | :video_game: **1.16.\* - 1.20.\*** | +| **Java version** | :computer: **Java 16 (or higher)** | :computer: **Java 16 (or higher)** | | **automatic updates** | :heavy_check_mark: | :heavy_check_mark: | | **frequent updates** | :heavy_check_mark: | :x: | | **latest content** | :heavy_check_mark: | :x: | @@ -56,7 +57,7 @@ While "stable" builds most definitely contain more bugs than development builds ## :framed_picture: Screenshots So what does Slimefun look like?
-Well, we asked some users on our [Discord server](#discord) to send us some screenshots, so see for yourself: +Well, we asked some users on our [Discord server](#headphones-discord) to send us some screenshots, so see for yourself: | Reactors and electricity | Awesome factories | Magic and Altars | | :-------------------------------------------: | :--------------------------------------: | :----------------------------------------: | | ![](https://raw.githubusercontent.com/Slimefun/Slimefun-Wiki/master/images/showcase1.png) | ![](https://raw.githubusercontent.com/Slimefun/Slimefun-Wiki/master/images/showcase6.png) | ![](https://raw.githubusercontent.com/Slimefun/Slimefun-Wiki/master/images/showcase5.png) | @@ -65,7 +66,7 @@ Well, we asked some users on our [Discord server](#discord) to send us some scre | *Screenshot provided by GalaxyKat11#3816* | *Screenshot provided by TamThan#7987* | *Screenshot provided by Kilaruna#4981* | ## :headphones: Discord -You can find Slimefun's community on Discord and connect with **over 6000** users of this plugin from all over the world.
+You can find Slimefun's community on Discord and connect with **over 7000** users of this plugin from all over the world.
Click the badge down below to join the server for suggestions/questions or other discussions about this plugin.
We are also hosting a community event every so often, join us to find out more.
**Important: We don't accept bug reports on discord, please use our [Issue Tracker](https://github.com/Slimefun/Slimefun4/issues) to submit bug reports!** diff --git a/jitpack.yml b/jitpack.yml new file mode 100644 index 0000000000..3dcd4f7383 --- /dev/null +++ b/jitpack.yml @@ -0,0 +1,6 @@ +before_install: + - sdk install java 17.0.1-open + - sdk use java 17.0.1-open + +jdk: + - openjdk17 diff --git a/pom.xml b/pom.xml index aa814880e5..41eefa9674 100644 --- a/pom.xml +++ b/pom.xml @@ -21,12 +21,12 @@ UTF-8 - - 1.8 - 1.8 + + 16 + 16 - 1.17 + 1.20 https://hub.spigotmc.org/javadocs/spigot/ @@ -63,7 +63,7 @@ paper-repo - https://papermc.io/repo/repository/maven-public + https://repo.papermc.io/repository/maven-public/ @@ -73,7 +73,7 @@ worldedit-repo - https://maven.sk89q.com/repo + https://maven.enginehub.org/repo/ @@ -92,8 +92,8 @@ - imprex-repo - https://imprex.ingrim4.me/repository/maven-public/ + codemc-repo + https://repo.codemc.io/repository/maven-public/ @@ -115,7 +115,7 @@ org.apache.maven.plugins maven-compiler-plugin - 3.8.1 + 3.11.0 @@ -130,7 +130,7 @@ org.apache.maven.plugins maven-source-plugin - 3.2.1 + 3.3.0 @@ -146,7 +146,7 @@ org.apache.maven.plugins maven-surefire-plugin - 3.0.0-M5 + 3.1.2 org.junit.jupiter:junit-jupiter @@ -158,14 +158,14 @@ org.sonarsource.scanner.maven sonar-maven-plugin - 3.9.0.2155 + 3.9.1.2184 org.jacoco jacoco-maven-plugin - 0.8.7 + 0.8.10 @@ -191,7 +191,7 @@ org.apache.maven.plugins maven-shade-plugin - 3.2.4 + 3.5.0 @@ -208,6 +208,10 @@ kong.unirest io.github.thebusybiscuit.slimefun4.libraries.unirest + + org.apache.commons.lang + io.github.thebusybiscuit.slimefun4.libraries.commons.lang + @@ -235,7 +239,7 @@ org.apache.maven.plugins maven-javadoc-plugin - 3.3.1 + 3.5.0 ${project.basedir} @@ -309,7 +313,9 @@ wiki.json languages/translators.json + tags/*.json + biome-maps/*.json languages/**/*.yml @@ -345,22 +351,23 @@ + - io.github.baked-libs + com.github.baked-libs.dough dough-api - 1.0.3 + 39856a32c4 compile io.papermc paperlib - 1.0.6 + 1.0.8 compile com.konghq unirest-java - 3.12.0 + 3.14.5 compile @@ -376,19 +383,25 @@ org.junit.jupiter junit-jupiter - 5.7.2 + 5.9.3 test org.mockito mockito-core - 3.12.4 + 5.4.0 + test + + + org.slf4j + slf4j-simple + 2.0.7 test com.github.seeseemelk - MockBukkit-v1.16 - 1.5.0 + MockBukkit-v1.18 + 2.0.0 test @@ -405,7 +418,7 @@ com.sk89q.worldedit worldedit-core - 7.2.6 + 7.2.15 provided @@ -419,7 +432,7 @@ com.sk89q.worldedit worldedit-bukkit - 7.2.6 + 7.2.15 provided @@ -433,7 +446,7 @@ com.gmail.nossr50.mcMMO mcMMO - 2.1.201 + 2.1.221 provided @@ -447,7 +460,7 @@ me.clip placeholderapi - 2.10.10 + 2.11.3 provided @@ -475,7 +488,7 @@ com.github.LoneDev6 itemsadder-api - 2.4.7 + 3.5.0b provided @@ -489,7 +502,7 @@ net.imprex orebfuscator-api - 1.0.0 + 5.3.3 provided @@ -500,5 +513,12 @@ + + + commons-lang + commons-lang + 2.6 + compile + - \ No newline at end of file + diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/api/MinecraftVersion.java b/src/main/java/io/github/thebusybiscuit/slimefun4/api/MinecraftVersion.java index 6c52e9fc85..f4154e554a 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/api/MinecraftVersion.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/api/MinecraftVersion.java @@ -19,18 +19,6 @@ */ public enum MinecraftVersion { - /** - * This constant represents Minecraft (Java Edition) Version 1.14 - * (The "Village & Pillage" Update) - */ - MINECRAFT_1_14(14, "1.14.x"), - - /** - * This constant represents Minecraft (Java Edition) Version 1.15 - * (The "Buzzy Bees" Update) - */ - MINECRAFT_1_15(15, "1.15.x"), - /** * This constant represents Minecraft (Java Edition) Version 1.16 * (The "Nether Update") @@ -40,10 +28,27 @@ public enum MinecraftVersion { /** * This constant represents Minecraft (Java Edition) Version 1.17 * (The "Caves and Cliffs: Part I" Update) - * */ MINECRAFT_1_17(17, "1.17.x"), + /** + * This constant represents Minecraft (Java Edition) Version 1.18 + * (The "Caves and Cliffs: Part II" Update) + */ + MINECRAFT_1_18(18, "1.18.x"), + + /** + * This constant represents Minecraft (Java Edition) Version 1.19 + * ("The Wild Update") + */ + MINECRAFT_1_19(19, "1.19.x"), + + /** + * This constant represents Minecraft (Java Edition) Version 1.20 + * ("The Trails & Tales Update") + */ + MINECRAFT_1_20(20, "1.20.x"), + /** * This constant represents an exceptional state in which we were unable * to identify the Minecraft Version we are using @@ -150,6 +155,21 @@ public boolean isAtLeast(@Nonnull MinecraftVersion version) { return false; } + /** + * Unit-Test only code. + * Running #isAtLeast(...) should always be meaningful. + * If the provided version equals the lowest supported version, then + * this will essentially always return true and result in a tautology. + * This is most definitely an oversight from us and should be fixed, therefore + * we will trigger an exception. + * + * In order to not disrupt server operations, this exception is only thrown during + * unit tests since the oversight itself will be harmless. + */ + if (this == UNIT_TEST && version.ordinal() == 0) { + throw new IllegalArgumentException("Version " + version + " is the lowest supported version already!"); + } + return this.ordinal() >= version.ordinal(); } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/api/events/SlimefunBlockBreakEvent.java b/src/main/java/io/github/thebusybiscuit/slimefun4/api/events/SlimefunBlockBreakEvent.java new file mode 100644 index 0000000000..8554a69499 --- /dev/null +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/api/events/SlimefunBlockBreakEvent.java @@ -0,0 +1,106 @@ +package io.github.thebusybiscuit.slimefun4.api.events; + +import javax.annotation.Nonnull; +import javax.annotation.ParametersAreNonnullByDefault; + +import org.bukkit.block.Block; +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; +import org.bukkit.event.block.BlockEvent; +import org.bukkit.inventory.ItemStack; + +import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItem; + +/** + * This {@link Event} is fired whenever a {@link SlimefunItem} placed as a {@link Block} in the world is broken. + * + * @author J3fftw1 + */ +public class SlimefunBlockBreakEvent extends Event implements Cancellable { + + private static final HandlerList handlers = new HandlerList(); + + private final Block blockBroken; + private final SlimefunItem slimefunItem; + private final ItemStack heldItem; + private final Player player; + + private boolean cancelled = false; + + /** + * @param player + * The {@link Player} who broke this {@link SlimefunItem} + * @param heldItem + * The {@link ItemStack} held by the {@link Player} + * @param blockBroken + * The {@link Block} broken by the {@link Player} + * @param slimefunItem + * The {@link SlimefunItem} within the {@link ItemStack} + */ + @ParametersAreNonnullByDefault + public SlimefunBlockBreakEvent(Player player, ItemStack heldItem, Block blockBroken, SlimefunItem slimefunItem) { + super(); + + this.player = player; + this.heldItem = heldItem; + this.blockBroken = blockBroken; + this.slimefunItem = slimefunItem; + } + + /** + * This gets the broken {@link Block} + * + * @return The broken {@link Block} + */ + public @Nonnull Block getBlockBroken() { + return blockBroken; + } + + /** + * This gets the {@link SlimefunItem} being broken + * + * @return The {@link SlimefunItem} being broken + */ + public @Nonnull SlimefunItem getSlimefunItem() { + return slimefunItem; + } + + /** + * The {@link ItemStack} held by the {@link Player} + * + * @return The held {@link ItemStack} + */ + public @Nonnull ItemStack getHeldItem() { + return heldItem; + } + + /** + * This gets the {@link Player} + * + * @return The {@link Player} + */ + public @Nonnull Player getPlayer() { + return player; + } + + @Override + public boolean isCancelled() { + return cancelled; + } + + @Override + public void setCancelled(boolean cancelled) { + this.cancelled = cancelled; + } + + public static @Nonnull HandlerList getHandlerList() { + return handlers; + } + + @Override + public @Nonnull HandlerList getHandlers() { + return getHandlerList(); + } +} diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/api/events/SlimefunBlockPlaceEvent.java b/src/main/java/io/github/thebusybiscuit/slimefun4/api/events/SlimefunBlockPlaceEvent.java new file mode 100644 index 0000000000..2978b9d77a --- /dev/null +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/api/events/SlimefunBlockPlaceEvent.java @@ -0,0 +1,105 @@ +package io.github.thebusybiscuit.slimefun4.api.events; + +import javax.annotation.Nonnull; +import javax.annotation.ParametersAreNonnullByDefault; + +import org.bukkit.block.Block; +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; +import org.bukkit.inventory.ItemStack; + +import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItem; + +/** + * This {@link Event} is fired whenever a {@link SlimefunItem} is placed as a {@link Block} in the world. + * + * @author J3fftw1 + */ +public class SlimefunBlockPlaceEvent extends Event implements Cancellable { + + private static final HandlerList handlers = new HandlerList(); + + private final Block blockPlaced; + private final SlimefunItem slimefunItem; + private final ItemStack placedItem; + private final Player player; + + private boolean cancelled = false; + + /** + * @param player + * The {@link Player} who placed this {@link SlimefunItem} + * @param placedItem + * The {@link ItemStack} held by the {@link Player} + * @param blockPlaced + * The {@link Block} placed by the {@link Player} + * @param slimefunItem + * The {@link SlimefunItem} within the {@link ItemStack} + */ + @ParametersAreNonnullByDefault + public SlimefunBlockPlaceEvent(Player player, ItemStack placedItem, Block blockPlaced, SlimefunItem slimefunItem) { + super(); + + this.player = player; + this.placedItem = placedItem; + this.blockPlaced = blockPlaced; + this.slimefunItem = slimefunItem; + } + + /** + * This gets the placed {@link Block} + * + * @return The placed {@link Block} + */ + public @Nonnull Block getBlockPlaced() { + return blockPlaced; + } + + /** + * This gets the {@link SlimefunItem} being placed + * + * @return The {@link SlimefunItem} being placed + */ + public @Nonnull SlimefunItem getSlimefunItem() { + return slimefunItem; + } + + /** + * This gets the placed {@link ItemStack}. + * + * @return The placed {@link ItemStack} + */ + public @Nonnull ItemStack getItemStack() { + return placedItem; + } + + /** + * This gets the {@link Player} + * + * @return The {@link Player} + */ + public @Nonnull Player getPlayer() { + return player; + } + + @Override + public boolean isCancelled() { + return cancelled; + } + + @Override + public void setCancelled(boolean cancelled) { + this.cancelled = cancelled; + } + + public static @Nonnull HandlerList getHandlerList() { + return handlers; + } + + @Override + public @Nonnull HandlerList getHandlers() { + return getHandlerList(); + } +} diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/api/exceptions/BiomeMapException.java b/src/main/java/io/github/thebusybiscuit/slimefun4/api/exceptions/BiomeMapException.java new file mode 100644 index 0000000000..8a116e283c --- /dev/null +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/api/exceptions/BiomeMapException.java @@ -0,0 +1,47 @@ +package io.github.thebusybiscuit.slimefun4.api.exceptions; + +import javax.annotation.ParametersAreNonnullByDefault; + +import org.bukkit.NamespacedKey; + +import io.github.thebusybiscuit.slimefun4.utils.biomes.BiomeMap; + +/** + * A {@link BiomeMapException} is thrown whenever a {@link BiomeMap} + * contains illegal, invalid or unknown values. + * + * @author TheBusyBiscuit + * + */ +public class BiomeMapException extends Exception { + + private static final long serialVersionUID = -1894334121194788527L; + + /** + * This constructs a new {@link BiomeMapException} for the given + * {@link BiomeMap}'s {@link NamespacedKey} with the provided context. + * + * @param key + * The {@link NamespacedKey} of our {@link BiomeMap} + * @param message + * The message to display + */ + @ParametersAreNonnullByDefault + public BiomeMapException(NamespacedKey key, String message) { + super("Biome Map '" + key + "' has been misconfigured: " + message); + } + + /** + * This constructs a new {@link BiomeMapException} for the given + * {@link BiomeMap}'s {@link NamespacedKey} with the provided context. + * + * @param key + * The {@link NamespacedKey} of our {@link BiomeMap} + * @param cause + * The {@link Throwable} which has caused this to happen + */ + @ParametersAreNonnullByDefault + public BiomeMapException(NamespacedKey key, Throwable cause) { + super("Biome Map '" + key + "' has been misconfigured (" + cause.getMessage() + ')', cause); + } +} diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/api/geo/ResourceManager.java b/src/main/java/io/github/thebusybiscuit/slimefun4/api/geo/ResourceManager.java index 184529b1dc..d15024440d 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/api/geo/ResourceManager.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/api/geo/ResourceManager.java @@ -104,8 +104,7 @@ void register(@Nonnull GEOResource resource) { * * @return An {@link OptionalInt}, either empty or containing the amount of the given {@link GEOResource} */ - @Nonnull - public OptionalInt getSupplies(@Nonnull GEOResource resource, @Nonnull World world, int x, int z) { + public @Nonnull OptionalInt getSupplies(@Nonnull GEOResource resource, @Nonnull World world, int x, int z) { Validate.notNull(resource, "Cannot get supplies for null"); Validate.notNull(world, "World must not be null"); @@ -159,12 +158,12 @@ public void setSupplies(@Nonnull GEOResource resource, @Nonnull World world, int * * @return The new supply value */ - private int generate(@Nonnull GEOResource resource, @Nonnull World world, int x, int z) { + private int generate(@Nonnull GEOResource resource, @Nonnull World world, int x, int y, int z) { Validate.notNull(resource, "Cannot generate resources for null"); Validate.notNull(world, "World cannot be null"); // Get the corresponding Block (and Biome) - Block block = world.getBlockAt(x << 4, 72, z << 4); + Block block = world.getBlockAt(x << 4, y, z << 4); Biome biome = block.getBiome(); /* @@ -237,7 +236,7 @@ public void scan(@Nonnull Player p, @Nonnull Block block, int page) { for (int i = page * 28; i < resources.size() && i < (page + 1) * 28; i++) { GEOResource resource = resources.get(i); OptionalInt optional = getSupplies(resource, block.getWorld(), x, z); - int supplies = optional.isPresent() ? optional.getAsInt() : generate(resource, block.getWorld(), x, z); + int supplies = optional.isPresent() ? optional.getAsInt() : generate(resource, block.getWorld(), x, block.getY(), z); String suffix = Slimefun.getLocalization().getResourceString(p, supplies == 1 ? "tooltips.unit" : "tooltips.units"); ItemStack item = new CustomItemStack(resource.getItem(), "&f" + resource.getName(p), "&8\u21E8 &e" + supplies + ' ' + suffix); diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/api/gps/GPSNetwork.java b/src/main/java/io/github/thebusybiscuit/slimefun4/api/gps/GPSNetwork.java index 81fc0d14c8..d2d9a3c1ee 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/api/gps/GPSNetwork.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/api/gps/GPSNetwork.java @@ -8,13 +8,13 @@ import java.util.UUID; import javax.annotation.Nonnull; +import javax.annotation.ParametersAreNonnullByDefault; import org.apache.commons.lang.Validate; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.Location; import org.bukkit.Server; -import org.bukkit.Sound; import org.bukkit.World; import org.bukkit.World.Environment; import org.bukkit.entity.Player; @@ -28,6 +28,7 @@ import io.github.thebusybiscuit.slimefun4.api.geo.ResourceManager; import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItem; import io.github.thebusybiscuit.slimefun4.api.player.PlayerProfile; +import io.github.thebusybiscuit.slimefun4.core.services.sounds.SoundEffect; import io.github.thebusybiscuit.slimefun4.implementation.Slimefun; import io.github.thebusybiscuit.slimefun4.implementation.SlimefunItems; import io.github.thebusybiscuit.slimefun4.implementation.items.gps.GPSTransmitter; @@ -57,6 +58,7 @@ public class GPSNetwork { private final Map> transmitters = new HashMap<>(); private final TeleportationManager teleportation = new TeleportationManager(); + private final ResourceManager resourceManager; /** @@ -111,8 +113,8 @@ public int getNetworkComplexity(@Nonnull UUID uuid) { for (Location l : locations) { SlimefunItem item = BlockStorage.check(l); - if (item instanceof GPSTransmitter) { - level += ((GPSTransmitter) item).getMultiplier(l.getBlockY()); + if (item instanceof GPSTransmitter transmitter) { + level += transmitter.getMultiplier(Math.max(l.getBlockY(), 0)); } } @@ -155,7 +157,7 @@ public void openTransmitterControlPanel(@Nonnull Player p) { menu.addMenuClickHandler(2, ChestMenuUtils.getEmptyClickHandler()); int complexity = getNetworkComplexity(p.getUniqueId()); - menu.addItem(4, new CustomItemStack(SlimefunItems.GPS_CONTROL_PANEL, "&7Network Info", "", "&8\u21E8 &7Status: " + (complexity > 0 ? "&2&lONLINE" : "&4&lOFFLINE"), "&8\u21E8 &7Complexity: &f" + complexity)); + menu.addItem(4, new CustomItemStack(SlimefunItems.GPS_CONTROL_PANEL, "&7Network Info", "", "&8\u21E8 &7Status: " + getStatusText(p, complexity), "&8\u21E8 &7Complexity: &f" + complexity)); menu.addMenuClickHandler(4, ChestMenuUtils.getEmptyClickHandler()); menu.addItem(6, new CustomItemStack(HeadTexture.GLOBE_OVERWORLD.getAsItemStack(), "&7" + Slimefun.getLocalization().getMessage(p, "machines.GPS_CONTROL_PANEL.waypoints"), "", ChatColor.GRAY + "\u21E8 " + Slimefun.getLocalization().getMessage(p, "guide.tooltips.open-itemgroup"))); @@ -172,10 +174,10 @@ public void openTransmitterControlPanel(@Nonnull Player p) { SlimefunItem sfi = BlockStorage.check(l); - if (sfi instanceof GPSTransmitter) { + if (sfi instanceof GPSTransmitter transmitter) { int slot = inventory[index]; - menu.addItem(slot, new CustomItemStack(SlimefunItems.GPS_TRANSMITTER, "&bGPS Transmitter", "&8\u21E8 &7World: &f" + l.getWorld().getName(), "&8\u21E8 &7X: &f" + l.getX(), "&8\u21E8 &7Y: &f" + l.getY(), "&8\u21E8 &7Z: &f" + l.getZ(), "", "&8\u21E8 &7Signal Strength: &f" + ((GPSTransmitter) sfi).getMultiplier(l.getBlockY()), "&8\u21E8 &7Ping: &f" + NumberUtils.roundDecimalNumber(1000D / l.getY()) + "ms")); + menu.addItem(slot, new CustomItemStack(SlimefunItems.GPS_TRANSMITTER, "&bGPS Transmitter", "&8\u21E8 &7World: &f" + l.getWorld().getName(), "&8\u21E8 &7X: &f" + l.getX(), "&8\u21E8 &7Y: &f" + l.getY(), "&8\u21E8 &7Z: &f" + l.getZ(), "", "&8\u21E8 &7Signal Strength: &f" + transmitter.getMultiplier(l.getBlockY()), "&8\u21E8 &7Ping: &f" + NumberUtils.roundDecimalNumber(1000D / l.getY()) + "ms")); menu.addMenuClickHandler(slot, ChestMenuUtils.getEmptyClickHandler()); index++; @@ -200,8 +202,8 @@ public void openTransmitterControlPanel(@Nonnull Player p) { * * @return An icon for this waypoint */ - @Nonnull - public ItemStack getIcon(@Nonnull String name, @Nonnull Environment environment) { + @ParametersAreNonnullByDefault + public @Nonnull ItemStack getIcon(String name, Environment environment) { if (name.startsWith("player:death ")) { return HeadTexture.DEATHPOINT.getAsItemStack(); } else if (environment == Environment.NETHER) { @@ -213,6 +215,15 @@ public ItemStack getIcon(@Nonnull String name, @Nonnull Environment environment) } } + @ParametersAreNonnullByDefault + private @Nonnull String getStatusText(Player player, int complexity) { + if (complexity > 0) { + return "&2&l" + Slimefun.getLocalization().getMessage(player, "gps.status-online"); + } else { + return "&4&l" + Slimefun.getLocalization().getMessage(player, "gps.status-offline"); + } + } + public void openWaypointControlPanel(@Nonnull Player p) { PlayerProfile.get(p, profile -> { ChestMenu menu = new ChestMenu(ChatColor.BLUE + Slimefun.getLocalization().getMessage(p, "machines.GPS_CONTROL_PANEL.title")); @@ -246,7 +257,7 @@ public void openWaypointControlPanel(@Nonnull Player p) { menu.addItem(slot, new CustomItemStack(waypoint.getIcon(), waypoint.getName().replace("player:death ", ""), "&8\u21E8 &7World: &f" + l.getWorld().getName(), "&8\u21E8 &7X: &f" + l.getX(), "&8\u21E8 &7Y: &f" + l.getY(), "&8\u21E8 &7Z: &f" + l.getZ(), "", "&8\u21E8 &cClick to delete")); menu.addMenuClickHandler(slot, (pl, s, item, action) -> { profile.removeWaypoint(waypoint); - pl.playSound(pl.getLocation(), Sound.UI_BUTTON_CLICK, 1F, 1F); + SoundEffect.GPS_NETWORK_OPEN_PANEL_SOUND.playFor(p); openWaypointControlPanel(pl); return false; @@ -279,7 +290,7 @@ public void createWaypoint(@Nonnull Player p, @Nonnull Location l) { } Slimefun.getLocalization().sendMessage(p, "gps.waypoint.new", true); - p.playSound(p.getLocation(), Sound.BLOCK_NOTE_BLOCK_PLING, 0.5F, 1F); + SoundEffect.GPS_NETWORK_CREATE_WAYPOINT.playFor(p); ChatInput.waitForPlayer(Slimefun.instance(), p, message -> addWaypoint(p, message, l)); }); @@ -322,7 +333,7 @@ public void addWaypoint(@Nonnull Player p, @Nonnull String name, @Nonnull Locati profile.addWaypoint(new Waypoint(profile, id, event.getLocation(), event.getName())); - p.playSound(p.getLocation(), Sound.BLOCK_NOTE_BLOCK_PLING, 1F, 1F); + SoundEffect.GPS_NETWORK_ADD_WAYPOINT.playFor(p); Slimefun.getLocalization().sendMessage(p, "gps.waypoint.added", true); } }); diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/api/gps/TeleportationManager.java b/src/main/java/io/github/thebusybiscuit/slimefun4/api/gps/TeleportationManager.java index 39dbda09f4..19646ed4c0 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/api/gps/TeleportationManager.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/api/gps/TeleportationManager.java @@ -13,7 +13,6 @@ import org.bukkit.ChatColor; import org.bukkit.Location; import org.bukkit.Particle; -import org.bukkit.Sound; import org.bukkit.block.Block; import org.bukkit.entity.Player; import org.bukkit.potion.PotionEffect; @@ -22,6 +21,7 @@ import io.github.bakedlibs.dough.common.ChatColors; import io.github.bakedlibs.dough.items.CustomItemStack; import io.github.thebusybiscuit.slimefun4.api.player.PlayerProfile; +import io.github.thebusybiscuit.slimefun4.core.services.sounds.SoundEffect; import io.github.thebusybiscuit.slimefun4.implementation.Slimefun; import io.github.thebusybiscuit.slimefun4.implementation.items.teleporter.Teleporter; import io.github.thebusybiscuit.slimefun4.utils.ChestMenuUtils; @@ -70,8 +70,7 @@ public void openTeleporterGUI(Player p, UUID ownerUUID, Block b) { @ParametersAreNonnullByDefault public void openTeleporterGUI(Player p, UUID ownerUUID, Block b, int complexity) { if (teleporterUsers.add(p.getUniqueId())) { - p.playSound(p.getLocation(), Sound.UI_BUTTON_CLICK, 1F, 1F); - + SoundEffect.TELEPORTATION_MANAGER_OPEN_GUI.playFor(p); PlayerProfile.fromUUID(ownerUUID, profile -> { ChestMenu menu = new ChestMenu("&3Teleporter"); menu.addMenuCloseHandler(pl -> teleporterUsers.remove(pl.getUniqueId())); @@ -128,7 +127,8 @@ public void teleport(UUID uuid, int complexity, Location source, Location destin teleporterUsers.add(uuid); int time = getTeleportationTime(complexity, source, destination); - updateProgress(uuid, Math.max(1, 100 / time), 0, source, destination, resistance); + int speed = Math.max(1, 100 / time); + updateProgress(uuid, speed, 0, source, destination, resistance); } /** @@ -162,7 +162,10 @@ public int getTeleportationTime(int complexity, @Nonnull Location source, @Nonnu } int speed = 50_000 + complexity * complexity; - return 1 + Math.min(4 * distanceSquared(source, destination) / speed, 40); + int unsafeTime = Math.min(4 * distanceSquared(source, destination) / speed, 40); + + // Fixes #3573 - Using Math.max is a safer way to ensure values > 0 than relying on addition. + return Math.max(1, unsafeTime); } @ParametersAreNonnullByDefault @@ -199,8 +202,7 @@ private void updateProgress(UUID uuid, int speed, int progress, Location source, p.sendTitle(ChatColors.color(Slimefun.getLocalization().getMessage(p, "machines.TELEPORTER.teleporting")), ChatColors.color("&b" + progress + "%"), 0, 60, 0); source.getWorld().spawnParticle(Particle.PORTAL, source, progress * 2, 0.2F, 0.8F, 0.2F); - source.getWorld().playSound(source, Sound.BLOCK_BEACON_AMBIENT, 1F, 0.6F); - + SoundEffect.TELEPORT_UPDATE_SOUND.playFor(p); Slimefun.runSync(() -> updateProgress(uuid, speed, progress + speed, source, destination, resistance), 10L); } } else { @@ -225,7 +227,7 @@ private void onTeleport(Player p, Location destination, boolean success, boolean // Spawn some particles for aesthetic reasons. Location loc = new Location(destination.getWorld(), destination.getX(), destination.getY() + 1, destination.getZ()); destination.getWorld().spawnParticle(Particle.PORTAL, loc, 200, 0.2F, 0.8F, 0.2F); - destination.getWorld().playSound(destination, Sound.BLOCK_BEACON_ACTIVATE, 1F, 1F); + SoundEffect.TELEPORT_SOUND.playFor(p); teleporterUsers.remove(p.getUniqueId()); } else { /* diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/api/items/HashedArmorpiece.java b/src/main/java/io/github/thebusybiscuit/slimefun4/api/items/HashedArmorpiece.java index a58d0b139a..92a66e5a2f 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/api/items/HashedArmorpiece.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/api/items/HashedArmorpiece.java @@ -12,7 +12,7 @@ import org.bukkit.inventory.meta.ItemMeta; import io.github.thebusybiscuit.slimefun4.implementation.items.armor.SlimefunArmorPiece; -import io.github.thebusybiscuit.slimefun4.implementation.tasks.ArmorTask; +import io.github.thebusybiscuit.slimefun4.implementation.tasks.armor.SlimefunArmorTask; /** * This class serves as a way of checking whether a {@link Player} has changed their armor @@ -25,7 +25,7 @@ * @author TheBusyBiscuit * * @see SlimefunArmorPiece - * @see ArmorTask + * @see SlimefunArmorTask */ public final class HashedArmorpiece { @@ -61,8 +61,8 @@ public void update(@Nullable ItemStack stack, @Nullable SlimefunItem item) { this.hash = copy.hashCode(); } - if (item instanceof SlimefunArmorPiece) { - this.item = Optional.of((SlimefunArmorPiece) item); + if (item instanceof SlimefunArmorPiece armorPiece) { + this.item = Optional.of(armorPiece); } else { this.item = Optional.empty(); } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/api/items/ItemGroup.java b/src/main/java/io/github/thebusybiscuit/slimefun4/api/items/ItemGroup.java index c4d89d64f5..0f70a46e6e 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/api/items/ItemGroup.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/api/items/ItemGroup.java @@ -44,6 +44,7 @@ public class ItemGroup implements Keyed { protected final NamespacedKey key; protected final ItemStack item; protected int tier; + protected boolean crossAddonItemGroup = false; /** * Constructs a new {@link ItemGroup} with the given {@link NamespacedKey} as an identifier @@ -90,8 +91,7 @@ public ItemGroup(NamespacedKey key, ItemStack item, int tier) { } @Override - @Nonnull - public final NamespacedKey getKey() { + public final @Nonnull NamespacedKey getKey() { return key; } @@ -117,6 +117,16 @@ public void register(@Nonnull SlimefunAddon addon) { sortCategoriesByTier(); } + /** + * This method returns whether this {@link ItemGroup} has been registered yet. + * More specifically: Whether {@link #register(SlimefunAddon)} was called or not. + * + * @return Whether this {@link ItemGroup} has been registered + */ + public boolean isRegistered() { + return this.addon != null && Slimefun.getRegistry().getAllItemGroups().contains(this); + } + /** * Returns the tier of this {@link ItemGroup}. * The tier determines the position of this {@link ItemGroup} in the {@link SlimefunGuide}. @@ -157,8 +167,7 @@ private void sortCategoriesByTier() { * * @return The {@link SlimefunAddon} or null if unregistered */ - @Nullable - public final SlimefunAddon getAddon() { + public final @Nullable SlimefunAddon getAddon() { return addon; } @@ -176,6 +185,10 @@ public void add(@Nonnull SlimefunItem item) { return; } + if (isRegistered() && !isCrossAddonItemGroup() && !item.getAddon().getName().equals(this.addon.getName())) { + item.warn("This item does not belong into ItemGroup " + this + " as that group belongs to " + this.addon.getName()); + } + items.add(item); } @@ -199,8 +212,7 @@ public void remove(@Nonnull SlimefunItem item) { * * @return A localized display item for this {@link ItemGroup} */ - @Nonnull - public ItemStack getItem(@Nonnull Player p) { + public @Nonnull ItemStack getItem(@Nonnull Player p) { return new CustomItemStack(item, meta -> { String name = Slimefun.getLocalization().getItemGroupName(p, getKey()); @@ -224,8 +236,7 @@ public ItemStack getItem(@Nonnull Player p) { * * @return The unlocalized name of this {@link ItemGroup} */ - @Nonnull - public String getUnlocalizedName() { + public @Nonnull String getUnlocalizedName() { return ChatColor.stripColor(item.getItemMeta().getDisplayName()); } @@ -238,8 +249,7 @@ public String getUnlocalizedName() { * * @return The localized name of this {@link ItemGroup} */ - @Nonnull - public String getDisplayName(@Nonnull Player p) { + public @Nonnull String getDisplayName(@Nonnull Player p) { String localized = Slimefun.getLocalization().getItemGroupName(p, getKey()); if (localized != null) { @@ -254,8 +264,7 @@ public String getDisplayName(@Nonnull Player p) { * * @return the list of SlimefunItems bound to this {@link ItemGroup} */ - @Nonnull - public List getItems() { + public @Nonnull List getItems() { return items; } @@ -271,10 +280,79 @@ public boolean contains(@Nullable SlimefunItem item) { return item != null && items.contains(item); } + /** + * This method returns whether this {@link ItemGroup} can be accessed + * by the given {@link Player}. If an {@link ItemGroup} is not accessible, + * it will not show up in the {@link SlimefunGuide} nor will items from this + * {@link ItemGroup} show up in the guide search. + * + * @param p + * The {@link Player} to check for + * + * @return Whether this {@link ItemGroup} is accessible by the given {@link Player} + */ + public boolean isAccessible(@Nonnull Player p) { + return true; + } + + /** + * This method returns whether this {@link ItemGroup} can be viewed + * by the given {@link Player}. Empty {@link ItemGroup ItemGroups} will not + * be visible. This includes {@link ItemGroup ItemGroups} where every {@link SlimefunItem} + * is disabled. If an {@link ItemGroup} is not accessible by the {@link Player}, + * see {@link #isAccessible(Player)}, this method will also return false. + * + * @param p + * The {@link Player} to check for + * + * @return Whether this {@link ItemGroup} is visible to the given {@link Player} + */ + public boolean isVisible(@Nonnull Player p) { + if (items.isEmpty() || !isAccessible(p)) { + return false; + } + + for (SlimefunItem slimefunItem : getItems()) { + /* + * If any item for this item group is visible, + * the item group itself is also visible. + * Empty item groups are not displayed. + */ + if (!slimefunItem.isHidden() && !slimefunItem.isDisabledIn(p.getWorld())) { + return true; + } + } + + return false; + } + + /** + * Returns if items from other addons are allowed to be + * added to this {@link ItemGroup}. + * + * @return true if items from other addons are allowed to be added to this {@link ItemGroup}. + */ + public boolean isCrossAddonItemGroup() { + return crossAddonItemGroup; + } + + /** + * This method will set if this {@link ItemGroup} will + * allow {@link SlimefunItem}s from other addons to + * be added, without a warning, into the group. False by default. + * If set to true, Slimefun will not warn about items being added. + * + * @param crossAddonItemGroup + * Whether items from another addon are allowable + */ + public void setCrossAddonItemGroup(boolean crossAddonItemGroup) { + this.crossAddonItemGroup = crossAddonItemGroup; + } + @Override public final boolean equals(Object obj) { - if (obj instanceof ItemGroup) { - return ((ItemGroup) obj).getKey().equals(getKey()); + if (obj instanceof ItemGroup group) { + return group.getKey().equals(this.getKey()); } else { return false; } @@ -301,24 +379,9 @@ public String toString() { * * @return Whether this {@link ItemGroup} will be hidden to the given {@link Player} */ + @Deprecated public boolean isHidden(@Nonnull Player p) { - for (SlimefunItem slimefunItem : getItems()) { - if (!slimefunItem.isHidden() && !slimefunItem.isDisabledIn(p.getWorld())) { - return false; - } - } - - return true; - } - - /** - * This method returns whether this {@link ItemGroup} has been registered yet. - * More specifically: Whether {@link #register(SlimefunAddon)} was called or not. - * - * @return Whether this {@link ItemGroup} has been registered - */ - public boolean isRegistered() { - return Slimefun.getRegistry().getAllItemGroups().contains(this); + return !isVisible(p); } } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/api/items/ItemSetting.java b/src/main/java/io/github/thebusybiscuit/slimefun4/api/items/ItemSetting.java index b47a71fff1..4a49b89ec8 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/api/items/ItemSetting.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/api/items/ItemSetting.java @@ -106,8 +106,21 @@ public void update(@Nonnull T newValue) { */ public @Nonnull T getValue() { if (value != null) { + /** + * If the value has been initialized, return it immediately. + */ return value; + } else if (Slimefun.instance().isUnitTest()) { + /* + * In Unit Tests, we want the test to fail. So we know there is + * something that needs to be fixed. + */ + throw new IllegalStateException("ItemSetting '" + key + "' was invoked but was not initialized yet."); } else { + /* + * In a normal environment, we can mitigate the issue + * easily and just print a warning instead. + */ item.warn("ItemSetting '" + key + "' was invoked but was not initialized yet."); return defaultValue; } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/api/items/ItemSpawnReason.java b/src/main/java/io/github/thebusybiscuit/slimefun4/api/items/ItemSpawnReason.java index 49f1e1f859..1d1589afd0 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/api/items/ItemSpawnReason.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/api/items/ItemSpawnReason.java @@ -4,8 +4,8 @@ import org.bukkit.inventory.ItemStack; import io.github.thebusybiscuit.slimefun4.api.events.SlimefunItemSpawnEvent; -import io.github.thebusybiscuit.slimefun4.core.networks.cargo.CargoNet; import io.github.thebusybiscuit.slimefun4.core.multiblocks.MultiBlockMachine; +import io.github.thebusybiscuit.slimefun4.core.networks.cargo.CargoNet; import io.github.thebusybiscuit.slimefun4.implementation.items.altar.AncientPedestal; import io.github.thebusybiscuit.slimefun4.implementation.items.seasonal.ChristmasPresent; import io.github.thebusybiscuit.slimefun4.implementation.items.seasonal.EasterEgg; diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/api/items/SlimefunItem.java b/src/main/java/io/github/thebusybiscuit/slimefun4/api/items/SlimefunItem.java index edfd06ebd0..0d4b1f12fa 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/api/items/SlimefunItem.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/api/items/SlimefunItem.java @@ -221,6 +221,11 @@ protected SlimefunItem(ItemGroup itemGroup, ItemStack item, String id, RecipeTyp return itemGroup; } + /** + * Retrieve the recipe for this {@link SlimefunItem}. + * + * @return An {@link ItemStack} array of 9 which represents the recipe for this {@link SlimefunItem} + */ public @Nonnull ItemStack[] getRecipe() { return recipe; } @@ -477,8 +482,8 @@ public void register(@Nonnull SlimefunAddon addon) { } // Lock the SlimefunItemStack from any accidental manipulations - if (itemStackTemplate instanceof SlimefunItemStack && isItemStackImmutable()) { - ((SlimefunItemStack) itemStackTemplate).lock(); + if (itemStackTemplate instanceof SlimefunItemStack stack && isItemStackImmutable()) { + stack.lock(); } postRegister(); @@ -655,7 +660,13 @@ public void setResearch(@Nullable Research research) { this.research = research; } - public void setRecipe(ItemStack[] recipe) { + /** + * Sets the recipe for this {@link SlimefunItem}. + * + * @param recipe + * The recipe for this {@link ItemStack} + */ + public void setRecipe(@Nonnull ItemStack[] recipe) { if (recipe == null || recipe.length != 9) { throw new IllegalArgumentException("Recipes must be of length 9"); } @@ -663,6 +674,12 @@ public void setRecipe(ItemStack[] recipe) { this.recipe = recipe; } + /** + * Sets the {@link RecipeType} for this {@link SlimefunItem}. + * + * @param type + * The {@link RecipeType} for this {@link SlimefunItem} + */ public void setRecipeType(@Nonnull RecipeType type) { Validate.notNull(type, "The RecipeType is not allowed to be null!"); this.recipeType = type; @@ -738,8 +755,8 @@ public boolean isItem(@Nullable ItemStack item) { } // If the given item is a SlimefunitemStack, simply compare the id - if (item instanceof SlimefunItemStack) { - return getId().equals(((SlimefunItemStack) item).getItemId()); + if (item instanceof SlimefunItemStack stack) { + return getId().equals(stack.getItemId()); } if (item.hasItemMeta()) { @@ -790,10 +807,10 @@ public final void addItemHandler(ItemHandler... handlers) { itemhandlers.put(handler.getIdentifier(), handler); // Tickers are a special case (at the moment at least) - if (handler instanceof BlockTicker) { + if (handler instanceof BlockTicker ticker) { ticking = true; Slimefun.getRegistry().getTickerBlocks().add(getId()); - blockTicker = (BlockTicker) handler; + blockTicker = ticker; } } } @@ -968,7 +985,8 @@ public String toString() { * @param message * The message to send */ - public void info(@Nonnull String message) { + @ParametersAreNonnullByDefault + public void info(String message) { Validate.notNull(addon, "Cannot log a message for an unregistered item!"); String msg = toString() + ": " + message; @@ -983,7 +1001,8 @@ public void info(@Nonnull String message) { * @param message * The message to send */ - public void warn(@Nonnull String message) { + @ParametersAreNonnullByDefault + public void warn(String message) { Validate.notNull(addon, "Cannot send a warning for an unregistered item!"); String msg = toString() + ": " + message; @@ -1004,7 +1023,8 @@ public void warn(@Nonnull String message) { * @param throwable * The {@link Throwable} to throw as a stacktrace. */ - public void error(@Nonnull String message, @Nonnull Throwable throwable) { + @ParametersAreNonnullByDefault + public void error(String message, Throwable throwable) { Validate.notNull(addon, "Cannot send an error for an unregistered item!"); addon.getLogger().log(Level.SEVERE, "Item \"{0}\" from {1} v{2} has caused an Error!", new Object[] { id, addon.getName(), addon.getPluginVersion() }); @@ -1016,11 +1036,24 @@ public void error(@Nonnull String message, @Nonnull Throwable throwable) { addon.getLogger().log(Level.SEVERE, message, throwable); // We definitely want to re-throw them during Unit Tests - if (throwable instanceof RuntimeException && Slimefun.getMinecraftVersion() == MinecraftVersion.UNIT_TEST) { - throw (RuntimeException) throwable; + if (throwable instanceof RuntimeException e && Slimefun.getMinecraftVersion() == MinecraftVersion.UNIT_TEST) { + throw e; } } + /** + * This method informs the given {@link Player} that this {@link SlimefunItem} + * will be removed soon. + * + * @param player + * The {@link Player} to inform. + */ + @ParametersAreNonnullByDefault + public void sendDeprecationWarning(Player player) { + Validate.notNull(player, "The Player must not be null."); + Slimefun.getLocalization().sendMessage(player, "messages.deprecated-item"); + } + /** * This method checks if the given {@link Player} is able to use this {@link SlimefunItem}. * A {@link Player} can use it if the following conditions apply: @@ -1105,8 +1138,8 @@ public boolean canUse(@Nonnull Player p, boolean sendMessage) { @Override public final boolean equals(Object obj) { - if (obj instanceof SlimefunItem) { - return ((SlimefunItem) obj).getId().equals(getId()); + if (obj instanceof SlimefunItem item) { + return item.getId().equals(this.getId()); } else { return false; } @@ -1117,17 +1150,31 @@ public final int hashCode() { return getId().hashCode(); } + /** + * Retrieve a {@link SlimefunItem} by its id. + * + * @param id + * The id of the {@link SlimefunItem} + * @return The {@link SlimefunItem} associated with that id. Null if non-existent + */ public static @Nullable SlimefunItem getById(@Nonnull String id) { return Slimefun.getRegistry().getSlimefunItemIds().get(id); } + /** + * Retrieve a {@link SlimefunItem} from an {@link ItemStack}. + * + * @param item + * The {@link ItemStack} to check + * @return The {@link SlimefunItem} associated with this {@link ItemStack} if present, otherwise null + */ public static @Nullable SlimefunItem getByItem(@Nullable ItemStack item) { if (item == null || item.getType() == Material.AIR) { return null; } - if (item instanceof SlimefunItemStack) { - return getById(((SlimefunItemStack) item).getItemId()); + if (item instanceof SlimefunItemStack stack) { + return getById(stack.getItemId()); } Optional itemID = Slimefun.getItemDataService().getItemData(item); diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/api/items/SlimefunItemStack.java b/src/main/java/io/github/thebusybiscuit/slimefun4/api/items/SlimefunItemStack.java index 024972196a..5604cdcfa8 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/api/items/SlimefunItemStack.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/api/items/SlimefunItemStack.java @@ -128,12 +128,12 @@ public SlimefunItemStack(@Nonnull String id, @Nonnull Material type, @Nonnull Co im.setLore(lines); } - if (im instanceof LeatherArmorMeta) { - ((LeatherArmorMeta) im).setColor(color); + if (im instanceof LeatherArmorMeta leatherArmorMeta) { + leatherArmorMeta.setColor(color); } - if (im instanceof PotionMeta) { - ((PotionMeta) im).setColor(color); + if (im instanceof PotionMeta potionMeta) { + potionMeta.setColor(color); } }); } @@ -154,9 +154,9 @@ public SlimefunItemStack(@Nonnull String id, @Nonnull Color color, @Nonnull Poti im.setLore(lines); } - if (im instanceof PotionMeta) { - ((PotionMeta) im).setColor(color); - ((PotionMeta) im).addCustomEffect(effect, true); + if (im instanceof PotionMeta potionMeta) { + potionMeta.setColor(color); + potionMeta.addCustomEffect(effect, true); if (effect.getType().equals(PotionEffectType.SATURATION)) { im.addItemFlags(ItemFlag.HIDE_POTION_EFFECTS); diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/api/items/groups/FlexItemGroup.java b/src/main/java/io/github/thebusybiscuit/slimefun4/api/items/groups/FlexItemGroup.java index 56d065b8b7..92f137dcfc 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/api/items/groups/FlexItemGroup.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/api/items/groups/FlexItemGroup.java @@ -36,6 +36,16 @@ protected FlexItemGroup(NamespacedKey key, ItemStack item, int tier) { super(key, item, tier); } + @Override + public final boolean isVisible(@Nonnull Player p) { + /* + * We can stop this method right here. + * We provide a custom method with more parameters for this. + * See isVisible(...) + */ + return true; + } + /** * This method returns whether this {@link FlexItemGroup} is visible under the given context. * Implementing this method gives full flexibility over who can see the ItemGroup when and where. @@ -66,16 +76,6 @@ protected FlexItemGroup(NamespacedKey key, ItemStack item, int tier) { */ public abstract void open(Player p, PlayerProfile profile, SlimefunGuideMode layout); - @Override - public final boolean isHidden(@Nonnull Player p) { - /* - * We can stop this method right here. - * We provide a custom method with more parameters for this. - * See isVisible(...) - */ - return false; - } - @Override public final void add(@Nonnull SlimefunItem item) { throw new UnsupportedOperationException("You cannot add items to a FlexItemGroup!"); diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/api/items/groups/NestedItemGroup.java b/src/main/java/io/github/thebusybiscuit/slimefun4/api/items/groups/NestedItemGroup.java index e8908be95e..9a928c8be1 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/api/items/groups/NestedItemGroup.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/api/items/groups/NestedItemGroup.java @@ -17,6 +17,7 @@ import io.github.thebusybiscuit.slimefun4.core.guide.GuideHistory; import io.github.thebusybiscuit.slimefun4.core.guide.SlimefunGuide; import io.github.thebusybiscuit.slimefun4.core.guide.SlimefunGuideMode; +import io.github.thebusybiscuit.slimefun4.core.services.sounds.SoundEffect; import io.github.thebusybiscuit.slimefun4.implementation.Slimefun; import io.github.thebusybiscuit.slimefun4.implementation.guide.SurvivalSlimefunGuide; import io.github.thebusybiscuit.slimefun4.utils.ChestMenuUtils; @@ -86,7 +87,7 @@ private void openGuide(Player p, PlayerProfile profile, SlimefunGuideMode mode, SurvivalSlimefunGuide guide = (SurvivalSlimefunGuide) Slimefun.getRegistry().getSlimefunGuide(mode); menu.setEmptySlotsClickable(false); - menu.addMenuOpeningHandler(pl -> pl.playSound(pl.getLocation(), guide.getSound(), 1, 1)); + menu.addMenuOpeningHandler(SoundEffect.GUIDE_BUTTON_CLICK_SOUND::playFor); guide.createHeader(p, profile, menu); menu.addItem(1, new CustomItemStack(ChestMenuUtils.getBackButton(p, "", ChatColor.GRAY + Slimefun.getLocalization().getMessage(p, "guide.back.guide")))); @@ -103,6 +104,10 @@ private void openGuide(Player p, PlayerProfile profile, SlimefunGuideMode mode, target++; SubItemGroup itemGroup = subGroups.get(target); + if (!itemGroup.isVisibleInNested(p)) { + continue; + } + menu.addItem(index, itemGroup.getItem(p)); menu.addMenuClickHandler(index, (pl, slot, item, action) -> { SlimefunGuide.openItemGroup(profile, itemGroup, mode, 1); diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/api/items/groups/SeasonalItemGroup.java b/src/main/java/io/github/thebusybiscuit/slimefun4/api/items/groups/SeasonalItemGroup.java index be726b119d..c61212c265 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/api/items/groups/SeasonalItemGroup.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/api/items/groups/SeasonalItemGroup.java @@ -56,12 +56,12 @@ public SeasonalItemGroup(NamespacedKey key, Month month, int tier, ItemStack ite } @Override - public boolean isHidden(@Nonnull Player p) { - // Hide this ItemGroup if the month differs + public boolean isAccessible(@Nonnull Player p) { + // Block this ItemGroup if the month differs if (month != LocalDate.now().getMonth()) { - return true; + return false; } - return super.isHidden(p); + return super.isAccessible(p); } } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/api/items/groups/SubItemGroup.java b/src/main/java/io/github/thebusybiscuit/slimefun4/api/items/groups/SubItemGroup.java index 0505123228..35b5aef3d1 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/api/items/groups/SubItemGroup.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/api/items/groups/SubItemGroup.java @@ -10,13 +10,14 @@ import io.github.thebusybiscuit.slimefun4.api.SlimefunAddon; import io.github.thebusybiscuit.slimefun4.api.items.ItemGroup; +import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItem; /** * The {@link SubItemGroup} is a child {@link ItemGroup} of the * {@link NestedItemGroup}. - * + * * @author TheBusyBiscuit - * + * * @see NestedItemGroup * */ @@ -40,14 +41,47 @@ public SubItemGroup(NamespacedKey key, NestedItemGroup parent, ItemStack item, i } @Override - public final boolean isHidden(@Nonnull Player p) { + public final boolean isVisible(@Nonnull Player p) { /* * Sub Categories are always hidden, * they won't show up in the normal guide view. */ + return false; + } + + @Override + public final boolean isAccessible(@Nonnull Player p) { + /* + * Sub Categories are accessible, they are invisible + * but their items are available to the guide search. + */ return true; } + /** + * This method returns whether this {@link SubItemGroup} can be viewed + * by the given {@link Player} in a {@link NestedItemGroup}. + * Empty {@link ItemGroup ItemGroups} will not be visible. + * This includes {@link ItemGroup ItemGroups} where every {@link SlimefunItem} + * is disabled. If an {@link ItemGroup} is not accessible by the {@link Player}, + * see {@link #isAccessible(Player)}, this method will also return false. + * + * @param p + * The {@link Player} to check for + * + * @return Whether this {@link SubItemGroup} is visible to the given {@link Player} + * in the {@link NestedItemGroup} + */ + public final boolean isVisibleInNested(@Nonnull Player p) { + return super.isVisible(p); + } + + /** + * This method returns the parent {@link NestedItemGroup} which this + * {@link SubItemGroup} belongs to. + * + * @return The parent {@link NestedItemGroup} + */ public final @Nonnull NestedItemGroup getParent() { return parentItemGroup; } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/api/items/package-info.java b/src/main/java/io/github/thebusybiscuit/slimefun4/api/items/package-info.java index 4fab4fc76e..fd1bd1c8d6 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/api/items/package-info.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/api/items/package-info.java @@ -1,6 +1,6 @@ /** * This package contains a few classes that revolve around the API for - * {@link me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem}, such as + * {@link io.github.thebusybiscuit.slimefun4.api.items.SlimefunItem}, such as * {@link io.github.thebusybiscuit.slimefun4.api.items.ItemSetting} */ package io.github.thebusybiscuit.slimefun4.api.items; \ No newline at end of file diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/api/items/settings/MaterialTagSetting.java b/src/main/java/io/github/thebusybiscuit/slimefun4/api/items/settings/MaterialTagSetting.java index b518e351ac..047f08d4d5 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/api/items/settings/MaterialTagSetting.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/api/items/settings/MaterialTagSetting.java @@ -40,14 +40,12 @@ public MaterialTagSetting(SlimefunItem item, String key, Tag defaultTa * * @return The default {@link Tag} */ - @Nonnull - public Tag getDefaultTag() { + public @Nonnull Tag getDefaultTag() { return defaultTag; } - @Nonnull @Override - protected String getErrorMessage() { + protected @Nonnull String getErrorMessage() { return "This List can only contain Materials in the format of e.g. REDSTONE_BLOCK"; } @@ -74,10 +72,10 @@ public boolean validateInput(List input) { * * @param tag * Our {@link Tag} + * * @return The {@link String} {@link List} */ - @Nonnull - private static List getAsStringList(@Nonnull Tag tag) { + private static @Nonnull List getAsStringList(@Nonnull Tag tag) { return tag.getValues().stream().map(Material::name).collect(Collectors.toList()); } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/api/player/PlayerBackpack.java b/src/main/java/io/github/thebusybiscuit/slimefun4/api/player/PlayerBackpack.java index 5d942114f7..4960a7c9b3 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/api/player/PlayerBackpack.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/api/player/PlayerBackpack.java @@ -1,10 +1,14 @@ package io.github.thebusybiscuit.slimefun4.api.player; import java.io.File; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.concurrent.CompletableFuture; import javax.annotation.Nonnull; import org.bukkit.Bukkit; +import org.bukkit.entity.HumanEntity; import org.bukkit.entity.Player; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.ItemStack; @@ -132,6 +136,30 @@ public void open(Player... players) { }); } + /** + * This will close the {@link Inventory} of this backpack for every {@link Player} + * that has opened it. + * + * This process has to run on the main server thread. + * + * @return A {@link CompletableFuture}. + */ + public CompletableFuture closeForAll() { + CompletableFuture future = new CompletableFuture<>(); + + Slimefun.runSync(() -> { + Iterator iterator = new ArrayList<>(inventory.getViewers()).iterator(); + + while (iterator.hasNext()) { + iterator.next().closeInventory(); + } + + future.complete(null); + }); + + return future; + } + /** * This will change the current size of this Backpack to the specified size. * diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/api/player/PlayerProfile.java b/src/main/java/io/github/thebusybiscuit/slimefun4/api/player/PlayerProfile.java index 49e3748518..de807c1d9b 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/api/player/PlayerProfile.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/api/player/PlayerProfile.java @@ -192,6 +192,7 @@ public void setResearched(@Nonnull Research research, boolean unlock) { * * @param research * The {@link Research} that is being queried + * * @return Whether this {@link Research} has been unlocked */ public boolean hasUnlocked(@Nullable Research research) { @@ -203,6 +204,23 @@ public boolean hasUnlocked(@Nullable Research research) { return !research.isEnabled() || researches.contains(research); } + /** + * This method returns whether this {@link Player} has unlocked all {@link Research Researches}. + * + * @return Whether they unlocked every {@link Research} + */ + public boolean hasUnlockedEverything() { + for (Research research : Slimefun.getRegistry().getResearches()) { + // If there is a single Research not unlocked: They haven't unlocked everything. + if (!hasUnlocked(research)) { + return false; + } + } + + // Player has everything unlocked - Hooray! + return true; + } + /** * This Method will return all Researches that this {@link Player} has unlocked * @@ -469,9 +487,7 @@ public boolean hasFullProtectionAgainst(@Nonnull ProtectionType type) { if (!armorPiece.isPresent()) { setId = null; - } else if (armorPiece.get() instanceof ProtectiveArmor) { - ProtectiveArmor protectedArmor = (ProtectiveArmor) armorPiece.get(); - + } else if (armorPiece.get() instanceof ProtectiveArmor protectedArmor) { if (setId == null && protectedArmor.isFullSetRequired()) { setId = protectedArmor.getArmorSetId(); } @@ -499,7 +515,7 @@ public int hashCode() { @Override public boolean equals(Object obj) { - return obj instanceof PlayerProfile && uuid.equals(((PlayerProfile) obj).uuid); + return obj instanceof PlayerProfile profile && uuid.equals(profile.uuid); } @Override diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/api/recipes/RecipeType.java b/src/main/java/io/github/thebusybiscuit/slimefun4/api/recipes/RecipeType.java index 828c78ddb3..e22c947b67 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/api/recipes/RecipeType.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/api/recipes/RecipeType.java @@ -23,7 +23,6 @@ import io.github.bakedlibs.dough.items.CustomItemStack; import io.github.bakedlibs.dough.recipes.MinecraftRecipe; -import io.github.thebusybiscuit.slimefun4.api.MinecraftVersion; import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItem; import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItemStack; import io.github.thebusybiscuit.slimefun4.core.multiblocks.MultiBlockMachine; @@ -56,6 +55,7 @@ public class RecipeType implements Keyed { public static final RecipeType MOB_DROP = new RecipeType(new NamespacedKey(Slimefun.instance(), "mob_drop"), new CustomItemStack(Material.IRON_SWORD, "&bMob Drop"), RecipeType::registerMobDrop, "", "&rKill the specified Mob to obtain this Item"); public static final RecipeType BARTER_DROP = new RecipeType(new NamespacedKey(Slimefun.instance(), "barter_drop"), new CustomItemStack(Material.GOLD_INGOT, "&bBarter Drop"), RecipeType::registerBarterDrop, "&aBarter with piglins for a chance", "&ato obtain this item"); + public static final RecipeType INTERACT = new RecipeType(new NamespacedKey(Slimefun.instance(), "interact"), new CustomItemStack(Material.PLAYER_HEAD, "&bInteract", "", "&a&oRight click with this item")); public static final RecipeType HEATED_PRESSURE_CHAMBER = new RecipeType(new NamespacedKey(Slimefun.instance(), "heated_pressure_chamber"), SlimefunItems.HEATED_PRESSURE_CHAMBER); public static final RecipeType FOOD_FABRICATOR = new RecipeType(new NamespacedKey(Slimefun.instance(), "food_fabricator"), SlimefunItems.FOOD_FABRICATOR); @@ -99,8 +99,8 @@ public RecipeType(NamespacedKey key, ItemStack item, BiConsumer recipe) { @@ -124,8 +124,8 @@ public void register(ItemStack[] recipe, ItemStack result) { } else { SlimefunItem slimefunItem = SlimefunItem.getById(this.machine); - if (slimefunItem instanceof MultiBlockMachine) { - ((MultiBlockMachine) slimefunItem).addRecipe(recipe, result); + if (slimefunItem instanceof MultiBlockMachine mbm) { + mbm.addRecipe(recipe, result); } } } @@ -149,8 +149,8 @@ public SlimefunItem getMachine() { @Override public final boolean equals(Object obj) { - if (obj instanceof RecipeType) { - return ((RecipeType) obj).getKey().equals(this.getKey()); + if (obj instanceof RecipeType recipeType) { + return recipeType.getKey().equals(this.getKey()); } else { return false; } @@ -163,9 +163,7 @@ public final int hashCode() { @ParametersAreNonnullByDefault private static void registerBarterDrop(ItemStack[] recipe, ItemStack output) { - if (Slimefun.getMinecraftVersion().isAtLeast(MinecraftVersion.MINECRAFT_1_16)) { - Slimefun.getRegistry().getBarteringDrops().add(output); - } + Slimefun.getRegistry().getBarteringDrops().add(output); } @ParametersAreNonnullByDefault diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/api/researches/PlayerResearchTask.java b/src/main/java/io/github/thebusybiscuit/slimefun4/api/researches/PlayerResearchTask.java index de395c4357..010f258f6c 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/api/researches/PlayerResearchTask.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/api/researches/PlayerResearchTask.java @@ -7,12 +7,12 @@ import org.apache.commons.lang.Validate; import org.bukkit.Bukkit; -import org.bukkit.Sound; import org.bukkit.entity.Player; import io.github.thebusybiscuit.slimefun4.api.events.ResearchUnlockEvent; import io.github.thebusybiscuit.slimefun4.api.player.PlayerProfile; import io.github.thebusybiscuit.slimefun4.core.guide.options.SlimefunGuideSettings; +import io.github.thebusybiscuit.slimefun4.core.services.sounds.SoundEffect; import io.github.thebusybiscuit.slimefun4.implementation.Slimefun; import io.github.thebusybiscuit.slimefun4.utils.FireworkUtils; @@ -64,7 +64,7 @@ public void accept(PlayerProfile profile) { if (!isInstant) { Slimefun.runSync(() -> { - p.playSound(p.getLocation(), Sound.ENTITY_BAT_TAKEOFF, 0.7F, 1F); + SoundEffect.PLAYER_RESEARCHING_SOUND.playFor(p); Slimefun.getLocalization().sendMessage(p, "messages.research.progress", true, msg -> msg.replace(PLACEHOLDER, research.getName(p)).replace("%progress%", "0%")); }, 5L); } @@ -93,7 +93,7 @@ private void sendUpdateMessage(@Nonnull Player p) { int index = i; Slimefun.runSync(() -> { - p.playSound(p.getLocation(), Sound.ENTITY_BAT_TAKEOFF, 0.7F, 1); + SoundEffect.PLAYER_RESEARCHING_SOUND.playFor(p); Slimefun.getLocalization().sendMessage(p, "messages.research.progress", true, msg -> { String progress = RESEARCH_PROGRESS[index - 1] + "%"; @@ -125,5 +125,4 @@ private void onFinish(@Nonnull Player p) { callback.accept(p); } } - } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/api/researches/Research.java b/src/main/java/io/github/thebusybiscuit/slimefun4/api/researches/Research.java index 6c013f9c37..6a4ee6b528 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/api/researches/Research.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/api/researches/Research.java @@ -12,6 +12,7 @@ import org.apache.commons.lang.Validate; import org.bukkit.Bukkit; +import org.bukkit.ChatColor; import org.bukkit.GameMode; import org.bukkit.Keyed; import org.bukkit.NamespacedKey; @@ -119,6 +120,15 @@ public int getID() { return localized != null ? localized : name; } + /** + * Retrieve the name of this {@link Research} without any localization nor coloring. + * + * @return The unlocalized, decolorized name for this {@link Research} + */ + public @Nonnull String getUnlocalizedName() { + return ChatColor.stripColor(name); + } + /** * Gets the cost in XP levels to unlock this {@link Research}. * diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/api/researches/package-info.java b/src/main/java/io/github/thebusybiscuit/slimefun4/api/researches/package-info.java index 14ae9e8729..ba6cd3df54 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/api/researches/package-info.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/api/researches/package-info.java @@ -1,5 +1,5 @@ /** - * This package holds everything connected to the {@link io.github.thebusybiscuit.slimefun4.core.researching.Research} + * This package holds everything connected to the {@link io.github.thebusybiscuit.slimefun4.api.researches.Research} * class. */ package io.github.thebusybiscuit.slimefun4.api.researches; \ No newline at end of file diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/SlimefunRegistry.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/SlimefunRegistry.java index bf896ecac6..a02bd420c6 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/SlimefunRegistry.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/SlimefunRegistry.java @@ -103,7 +103,8 @@ public void load(@Nonnull Slimefun plugin, @Nonnull Config cfg) { guideKey = new NamespacedKey(plugin, "slimefun_guide_mode"); boolean showVanillaRecipes = cfg.getBoolean("guide.show-vanilla-recipes"); - guides.put(SlimefunGuideMode.SURVIVAL_MODE, new SurvivalSlimefunGuide(showVanillaRecipes)); + boolean showHiddenItemGroupsInSearch = cfg.getBoolean("guide.show-hidden-item-groups-in-search"); + guides.put(SlimefunGuideMode.SURVIVAL_MODE, new SurvivalSlimefunGuide(showVanillaRecipes, showHiddenItemGroupsInSearch)); guides.put(SlimefunGuideMode.CHEAT_MODE, new CheatSheetSlimefunGuide()); researchRanks.addAll(cfg.getStringList("research-ranks")); diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/attributes/DamageableItem.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/attributes/DamageableItem.java index 7a766315f6..05694f3e64 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/attributes/DamageableItem.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/attributes/DamageableItem.java @@ -59,10 +59,11 @@ default void damageItem(@Nonnull Player p, @Nullable ItemStack item) { ItemMeta meta = item.getItemMeta(); - if (!meta.isUnbreakable()) { + if (meta != null && !meta.isUnbreakable()) { Damageable damageable = (Damageable) meta; if (damageable.getDamage() >= item.getType().getMaxDurability()) { + // No need for a SoundEffect equivalent here since this is supposed to be a vanilla sound. p.playSound(p.getEyeLocation(), Sound.ENTITY_ITEM_BREAK, 1, 1); item.setAmount(0); } else { diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/attributes/DistinctiveItem.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/attributes/DistinctiveItem.java new file mode 100644 index 0000000000..150bef8319 --- /dev/null +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/attributes/DistinctiveItem.java @@ -0,0 +1,33 @@ +package io.github.thebusybiscuit.slimefun4.core.attributes; + +import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItemStack; +import io.github.thebusybiscuit.slimefun4.utils.SlimefunUtils; +import org.bukkit.inventory.meta.ItemMeta; + +import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItem; + +import javax.annotation.Nonnull; + +/** + * Implement this interface for any {@link SlimefunItem} to prevent + * cargo using only its ID when comparing. #canStack is used when + * comparing stacks + * + * @author Sefiraat + */ +public interface DistinctiveItem extends ItemAttribute { + + /** + * This method is called by {@link SlimefunUtils#isItemSimilar} when two {@link SlimefunItemStack} + * IDs match on a DistinctiveItem and should return if the two items can stack + * with one another. + * + * @param itemMetaOne + * The {@link ItemMeta} of the first stack being compared. + * @param itemMetaTwo + * The {@link ItemMeta} of the second stack being compared. + * + * @return Whether the two {@link ItemMeta}s are stackable + */ + boolean canStack(@Nonnull ItemMeta itemMetaOne, @Nonnull ItemMeta itemMetaTwo); +} diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/attributes/ExternallyInteractable.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/attributes/ExternallyInteractable.java new file mode 100644 index 0000000000..8204409886 --- /dev/null +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/attributes/ExternallyInteractable.java @@ -0,0 +1,30 @@ +package io.github.thebusybiscuit.slimefun4.core.attributes; + +import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItem; +import io.github.thebusybiscuit.slimefun4.core.attributes.interactions.InteractionResult; +import org.bukkit.Location; + +import javax.annotation.Nonnull; + +/** + * Implement this interface for any {@link SlimefunItem} to provide methods for + * external code to 'interact' with the item when placed as a block in the world. + * + * @author Sefiraat + */ +@FunctionalInterface +public interface ExternallyInteractable { + + /** + * This method should be used by the implementing class to fulfill the actions needed + * when being interacted with returning the result of the interaction. + * + * @param location + * The {@link Location} of the Block being targeted for the interaction. + * + * @return The {@link InteractionResult} denoting the result of the interaction. + */ + @Nonnull + InteractionResult onInteract(@Nonnull Location location); + +} diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/attributes/RadiationSymptom.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/attributes/RadiationSymptom.java new file mode 100644 index 0000000000..2c703de40b --- /dev/null +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/attributes/RadiationSymptom.java @@ -0,0 +1,69 @@ +package io.github.thebusybiscuit.slimefun4.core.attributes; + +import javax.annotation.Nonnull; + +import com.google.common.base.Preconditions; + +import org.bukkit.entity.Player; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; + +import io.github.thebusybiscuit.slimefun4.implementation.Slimefun; +import io.github.thebusybiscuit.slimefun4.utils.RadiationUtils; + +/** + * An enum of potential radiation symptoms. + * A symptom will be applied when the minExposure + * threshold is reached on the {@link Player}'s + * exposure level. + * When the {@link Player} gets above the minExposure threshold + * the {@link PotionEffect} will be applied. + * + * @author Semisol + * + * @see RadiationUtils + */ +public enum RadiationSymptom { + + SLOW(10, PotionEffectType.SLOW, 3), + WITHER_LOW(25, PotionEffectType.WITHER, 0), + BLINDNESS(50, PotionEffectType.BLINDNESS, 4), + WITHER_HIGH(75, PotionEffectType.WITHER, 3), + IMMINENT_DEATH(100, PotionEffectType.HARM, 49); + + private final int minExposure; + private final PotionEffect potionEffect; + + RadiationSymptom(int minExposure, @Nonnull PotionEffectType type, int level) { + Preconditions.checkNotNull(type, "The effect type cannot be null"); + Preconditions.checkArgument(minExposure > 0, "The minimum exposure must be greater than 0."); + Preconditions.checkArgument(level >= 0, "The status effect level must be non-negative."); + + this.minExposure = minExposure; + this.potionEffect = new PotionEffect(type, Slimefun.getCfg().getOrSetDefault("options.radiation-update-interval", 1) * 20 + 20, level); + } + + /** + * This method applies the symptom to a player. + * + * @param p + * The player + */ + public void apply(@Nonnull Player p) { + Preconditions.checkNotNull(p, "The player cannot be null"); + p.addPotionEffect(potionEffect); + } + + /** + * This method returns if this symptom + * should be applied. + * + * @param exposure + * Exposure level + * + * @return If the symptom should be applied + */ + public boolean shouldApply(int exposure) { + return exposure >= minExposure; + } +} diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/attributes/Radioactivity.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/attributes/Radioactivity.java index 3e4bd9f129..1025e1e073 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/attributes/Radioactivity.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/attributes/Radioactivity.java @@ -2,6 +2,8 @@ import javax.annotation.Nonnull; +import io.github.thebusybiscuit.slimefun4.implementation.tasks.armor.RadiationTask; + import org.bukkit.ChatColor; import org.bukkit.entity.Player; @@ -20,41 +22,53 @@ public enum Radioactivity { * This represents a low level of radiation. * It will still cause damage but will take a while before it becomes deadly. */ - LOW(ChatColor.YELLOW), + LOW(ChatColor.YELLOW, 1), /** * This represents a medium level of radiation. * This can be considered the default. */ - MODERATE(ChatColor.YELLOW), + MODERATE(ChatColor.YELLOW, 2), /** * This is a high level of radiation. * It will cause death if the {@link Player} does not act quickly. */ - HIGH(ChatColor.DARK_GREEN), + HIGH(ChatColor.GOLD, 3), /** * A very high level of radiation will be deadly. * The {@link Player} should not take this too lightly... */ - VERY_HIGH(ChatColor.RED), + VERY_HIGH(ChatColor.RED, 5), /** - * This is the deadlies level of radiation. + * This is the deadliest level of radiation. * The {@link Player} has basically no chance to protect themselves in time. * It will cause certain death. */ - VERY_DEADLY(ChatColor.DARK_RED); + VERY_DEADLY(ChatColor.DARK_RED, 10); private final ChatColor color; + private final int exposureModifier; - Radioactivity(@Nonnull ChatColor color) { + Radioactivity(@Nonnull ChatColor color, int exposureModifier) { this.color = color; + this.exposureModifier = exposureModifier; + } + + /** + * This method returns the amount of exposure applied + * to a player every run of the {@link RadiationTask} + * for this radiation level. + * + * @return The exposure amount applied per run. + */ + public int getExposureModifier() { + return exposureModifier; } - @Nonnull - public String getLore() { + public @Nonnull String getLore() { return ChatColor.GREEN + "\u2622" + ChatColor.GRAY + " Radiation level: " + color + toString().replace('_', ' '); } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/attributes/interactions/InteractionResult.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/attributes/interactions/InteractionResult.java new file mode 100644 index 0000000000..d0b36ae606 --- /dev/null +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/attributes/interactions/InteractionResult.java @@ -0,0 +1,61 @@ +package io.github.thebusybiscuit.slimefun4.core.attributes.interactions; + +import io.github.thebusybiscuit.slimefun4.core.attributes.ExternallyInteractable; + +import javax.annotation.Nullable; +import javax.annotation.ParametersAreNonnullByDefault; + +/** + * This class represents the result of an interaction on an {@link ExternallyInteractable} item. + */ +public class InteractionResult { + + private final boolean interactionSuccessful; + private @Nullable String resultMessage; + + /** + * Creates a new InteractionResult. + * + * @param successful Whether the interaction was successful or not. + */ + @ParametersAreNonnullByDefault + public InteractionResult(boolean successful) { + this.interactionSuccessful = successful; + } + + /** + * Returns whether the interaction was successful or not. + * + * @return boolean denoting whether the interaction was successful or not. + */ + public boolean isInteractionSuccessful() { + return interactionSuccessful; + } + + /** + * Sets a custom result message for this interaction. + * + * @param resultMessage The message to be sent with the Result + */ + public void setResultMessage(@Nullable String resultMessage) { + this.resultMessage = resultMessage; + } + + /** + * Returns whether this result has a result message or is null. + * + * @return true if a result message is present + */ + public boolean hasResultMessage() { + return this.resultMessage != null; + } + + /** + * Returns the custom result message for this result or null if not set. + * + * @return A String of the provided custom result message. + */ + public @Nullable String getResultMessage() { + return resultMessage; + } +} diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/attributes/interactions/ItemInteractionResult.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/attributes/interactions/ItemInteractionResult.java new file mode 100644 index 0000000000..3d93ffaf01 --- /dev/null +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/attributes/interactions/ItemInteractionResult.java @@ -0,0 +1,64 @@ +package io.github.thebusybiscuit.slimefun4.core.attributes.interactions; + +import io.github.thebusybiscuit.slimefun4.core.attributes.ExternallyInteractable; +import org.bukkit.inventory.ItemStack; + +import javax.annotation.Nonnull; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +/** + * This class represents the result of an interaction on an {@link ExternallyInteractable} item. + */ +public class ItemInteractionResult extends InteractionResult { + + private final @Nonnull Set resultItems = new HashSet<>(); + + /** + * Creates a new InteractionResult. + * + * @param successful Whether the interaction was successful or not. + */ + public ItemInteractionResult(boolean successful) { + super(successful); + } + + /** + * Creates a new InteractionResult. + * + * @param successful Whether the interaction was successful or not. + * @param itemStacks The {@link ItemStack}(s) that would be returned due to this interaction. + */ + public ItemInteractionResult(boolean successful, ItemStack... itemStacks) { + super(successful); + addResultItems(itemStacks); + } + + /** + * Adds an or several {@link ItemStack}'s into the result. + * + * @param itemStacks The {@link ItemStack}(s) that would be returned due to this interaction. + */ + public void addResultItems(ItemStack... itemStacks) { + Collections.addAll(resultItems, itemStacks); + } + + /** + * This returned whether items are included as part of the result. + * + * @return True if items are included in the result. + */ + public boolean resultedInItems() { + return !this.resultItems.isEmpty(); + } + + /** + * Returns the {@link ItemStack}(s) produced as a result of this interaction, if any. + * + * @return An unmodifiable {@link Set} of {@link ItemStack}(s) created due to the interaction. + */ + public @Nonnull Set getResultItems() { + return Collections.unmodifiableSet(resultItems); + } +} diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/attributes/interactions/package-info.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/attributes/interactions/package-info.java new file mode 100644 index 0000000000..c8d0bf92b1 --- /dev/null +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/attributes/interactions/package-info.java @@ -0,0 +1,5 @@ +/** + * This package contains the various possible {@link io.github.thebusybiscuit.slimefun4.core.attributes.interactions.InteractionResult}s + * that can be returned by an {@link io.github.thebusybiscuit.slimefun4.core.attributes.ExternallyInteractable} object. + */ +package io.github.thebusybiscuit.slimefun4.core.attributes.interactions; \ No newline at end of file diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/attributes/package-info.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/attributes/package-info.java index 4913fcf00f..9a2c8c5699 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/attributes/package-info.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/attributes/package-info.java @@ -1,5 +1,5 @@ /** * This package contains all variations of {@link io.github.thebusybiscuit.slimefun4.core.attributes.ItemAttribute} that - * can be assigned to a {@link me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem} + * can be assigned to a {@link io.github.thebusybiscuit.slimefun4.api.items.SlimefunItem} */ package io.github.thebusybiscuit.slimefun4.core.attributes; \ No newline at end of file diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/commands/SubCommand.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/commands/SubCommand.java index 56350d931e..4b5717a7a6 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/commands/SubCommand.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/commands/SubCommand.java @@ -80,10 +80,9 @@ protected String getDescription() { * * @return A possibly localized description of this {@link SubCommand} */ - @Nonnull - public String getDescription(@Nonnull CommandSender sender) { - if (sender instanceof Player) { - return Slimefun.getLocalization().getMessage((Player) sender, getDescription()); + public @Nonnull String getDescription(@Nonnull CommandSender sender) { + if (sender instanceof Player player) { + return Slimefun.getLocalization().getMessage(player, getDescription()); } else { return Slimefun.getLocalization().getMessage(getDescription()); } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/commands/subcommands/BackpackCommand.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/commands/subcommands/BackpackCommand.java index 29aaca7400..431d47cb66 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/commands/subcommands/BackpackCommand.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/commands/subcommands/BackpackCommand.java @@ -40,7 +40,7 @@ protected String getDescription() { @Override public void onExecute(CommandSender sender, String[] args) { - if (sender instanceof Player) { + if (sender instanceof Player player) { if (sender.hasPermission("slimefun.command.backpack")) { if (args.length != 3) { Slimefun.getLocalization().sendMessage(sender, "messages.usage", true, msg -> msg.replace("%usage%", "/sf backpack ")); @@ -71,7 +71,7 @@ public void onExecute(CommandSender sender, String[] args) { Slimefun.runSync(() -> { ItemStack item = SlimefunItems.RESTORED_BACKPACK.clone(); Slimefun.getBackpackListener().setBackpackId(backpackOwner, item, 2, id); - ((Player) sender).getInventory().addItem(item); + player.getInventory().addItem(item); Slimefun.getLocalization().sendMessage(sender, "commands.backpack.restored-backpack-given"); }); }); diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/commands/subcommands/ChargeCommand.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/commands/subcommands/ChargeCommand.java index 6bfbf147ab..119091e1e6 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/commands/subcommands/ChargeCommand.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/commands/subcommands/ChargeCommand.java @@ -33,14 +33,12 @@ protected String getDescription() { @Override public void onExecute(CommandSender sender, String[] args) { - if (sender instanceof Player) { + if (sender instanceof Player player) { if (sender.hasPermission("slimefun.command.charge")) { - Player p = (Player) sender; - ItemStack item = p.getInventory().getItemInMainHand(); + ItemStack item = player.getInventory().getItemInMainHand(); SlimefunItem slimefunItem = SlimefunItem.getByItem(item); - if (slimefunItem instanceof Rechargeable) { - Rechargeable rechargeableItem = (Rechargeable) slimefunItem; + if (slimefunItem instanceof Rechargeable rechargeableItem) { rechargeableItem.setItemCharge(item, rechargeableItem.getMaxItemCharge(item)); Slimefun.getLocalization().sendMessage(sender, "commands.charge.charge-success", true); } else { diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/commands/subcommands/CheatCommand.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/commands/subcommands/CheatCommand.java index f3fcd49872..973ed1ee39 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/commands/subcommands/CheatCommand.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/commands/subcommands/CheatCommand.java @@ -19,9 +19,9 @@ class CheatCommand extends SubCommand { @Override public void onExecute(CommandSender sender, String[] args) { - if (sender instanceof Player) { + if (sender instanceof Player player) { if (sender.hasPermission("slimefun.cheat.items")) { - SlimefunGuide.openCheatMenu((Player) sender); + SlimefunGuide.openCheatMenu(player); } else { Slimefun.getLocalization().sendMessage(sender, "messages.no-permission", true); } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/commands/subcommands/DebugCommand.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/commands/subcommands/DebugCommand.java new file mode 100644 index 0000000000..12c567cb14 --- /dev/null +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/commands/subcommands/DebugCommand.java @@ -0,0 +1,56 @@ +package io.github.thebusybiscuit.slimefun4.core.commands.subcommands; + +import javax.annotation.Nonnull; + +import org.bukkit.command.CommandSender; + +import io.github.thebusybiscuit.slimefun4.core.commands.SlimefunCommand; +import io.github.thebusybiscuit.slimefun4.core.commands.SubCommand; +import io.github.thebusybiscuit.slimefun4.core.debug.Debug; +import io.github.thebusybiscuit.slimefun4.implementation.Slimefun; + +/** + * The debug command will allow server owners to get information for us developers. + * We can put debug messages in the code and they can trigger it for us to see what exactly is going on. + * + * @author WalshyDev + */ +public class DebugCommand extends SubCommand { + + protected DebugCommand(@Nonnull Slimefun plugin, @Nonnull SlimefunCommand cmd) { + super(plugin, cmd, "debug", true); + } + + @Override + protected @Nonnull String getDescription() { + return "commands.debug.description"; + } + + @Override + public void onExecute(@Nonnull CommandSender sender, @Nonnull String[] args) { + if (!sender.hasPermission("slimefun.command.debug")) { + Slimefun.getLocalization().sendMessage(sender, "messages.no-permission", true); + return; + } + + if (args.length == 1) { + String currentCase = Debug.getTestCase(); + if (currentCase != null) { + Slimefun.getLocalization().sendMessage(sender, "commands.debug.current", true, msg -> msg.replace("%test_case%", currentCase)); + } else { + Slimefun.getLocalization().sendMessage(sender, "commands.debug.none-running", true); + } + return; + } + + String test = args[1]; + + if (test.equalsIgnoreCase("disable") || test.equalsIgnoreCase("off")) { + Debug.setTestCase(null); + Slimefun.getLocalization().sendMessage(sender, "commands.debug.disabled"); + } else { + Debug.setTestCase(test); + Slimefun.getLocalization().sendMessage(sender, "commands.debug.running", msg -> msg.replace("%test%", test)); + } + } +} diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/commands/subcommands/DebugFishCommand.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/commands/subcommands/DebugFishCommand.java index 2214360e37..cc79b113e6 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/commands/subcommands/DebugFishCommand.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/commands/subcommands/DebugFishCommand.java @@ -7,8 +7,8 @@ import io.github.thebusybiscuit.slimefun4.core.commands.SlimefunCommand; import io.github.thebusybiscuit.slimefun4.core.commands.SubCommand; -import io.github.thebusybiscuit.slimefun4.implementation.SlimefunItems; import io.github.thebusybiscuit.slimefun4.implementation.Slimefun; +import io.github.thebusybiscuit.slimefun4.implementation.SlimefunItems; class DebugFishCommand extends SubCommand { @@ -19,8 +19,8 @@ class DebugFishCommand extends SubCommand { @Override public void onExecute(CommandSender sender, String[] args) { - if (sender instanceof Player && sender.hasPermission("slimefun.debugging")) { - ((Player) sender).getInventory().addItem(SlimefunItems.DEBUG_FISH.clone()); + if (sender instanceof Player player && sender.hasPermission("slimefun.debugging")) { + player.getInventory().addItem(SlimefunItems.DEBUG_FISH.clone()); } else { Slimefun.getLocalization().sendMessage(sender, "messages.no-permission", true); } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/commands/subcommands/GuideCommand.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/commands/subcommands/GuideCommand.java index 7849d49e4b..006744d6c7 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/commands/subcommands/GuideCommand.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/commands/subcommands/GuideCommand.java @@ -20,10 +20,10 @@ class GuideCommand extends SubCommand { @Override public void onExecute(CommandSender sender, String[] args) { - if (sender instanceof Player) { + if (sender instanceof Player player) { if (sender.hasPermission("slimefun.command.guide")) { SlimefunGuideMode design = SlimefunGuide.getDefaultMode(); - ((Player) sender).getInventory().addItem(SlimefunGuide.getItem(design).clone()); + player.getInventory().addItem(SlimefunGuide.getItem(design).clone()); } else { Slimefun.getLocalization().sendMessage(sender, "messages.no-permission", true); } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/commands/subcommands/OpenGuideCommand.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/commands/subcommands/OpenGuideCommand.java index 2d89f09b03..83db842925 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/commands/subcommands/OpenGuideCommand.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/commands/subcommands/OpenGuideCommand.java @@ -21,9 +21,9 @@ class OpenGuideCommand extends SubCommand { @Override @ParametersAreNonnullByDefault public void onExecute(CommandSender sender, String[] args) { - if (sender instanceof Player) { + if (sender instanceof Player player) { if (sender.hasPermission("slimefun.command.open_guide")) { - SlimefunGuide.openGuide((Player) sender, SlimefunGuideMode.SURVIVAL_MODE); + SlimefunGuide.openGuide(player, SlimefunGuideMode.SURVIVAL_MODE); } else { Slimefun.getLocalization().sendMessage(sender, "messages.no-permission", true); } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/commands/subcommands/ResearchCommand.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/commands/subcommands/ResearchCommand.java index caf6f6d254..8b81c87b53 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/commands/subcommands/ResearchCommand.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/commands/subcommands/ResearchCommand.java @@ -33,6 +33,12 @@ protected String getDescription() { @Override public void onExecute(CommandSender sender, String[] args) { + // Check if researching is even enabled + if (!Slimefun.getRegistry().isResearchingEnabled()) { + Slimefun.getLocalization().sendMessage(sender, "messages.researching-is-disabled"); + return; + } + if (args.length == 3) { if (!(sender instanceof Player) || sender.hasPermission("slimefun.cheat.researches")) { Optional player = PlayerList.findByName(args[1]); diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/commands/subcommands/SearchCommand.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/commands/subcommands/SearchCommand.java index 3e87fb40b7..9783518b17 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/commands/subcommands/SearchCommand.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/commands/subcommands/SearchCommand.java @@ -23,11 +23,11 @@ class SearchCommand extends SubCommand { @Override public void onExecute(CommandSender sender, String[] args) { - if (sender instanceof Player) { + if (sender instanceof Player player) { if (sender.hasPermission("slimefun.command.search")) { if (args.length > 1) { String query = String.join(" ", Arrays.copyOfRange(args, 1, args.length)); - PlayerProfile.get((Player) sender, profile -> SlimefunGuide.openSearch(profile, query, SlimefunGuideMode.SURVIVAL_MODE, true)); + PlayerProfile.get(player, profile -> SlimefunGuide.openSearch(profile, query, SlimefunGuideMode.SURVIVAL_MODE, true)); } else { Slimefun.getLocalization().sendMessage(sender, "messages.usage", true, msg -> msg.replace("%usage%", "/sf search ")); } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/commands/subcommands/SlimefunSubCommands.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/commands/subcommands/SlimefunSubCommands.java index e13dc3c5d6..17d70bce3e 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/commands/subcommands/SlimefunSubCommands.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/commands/subcommands/SlimefunSubCommands.java @@ -41,6 +41,7 @@ public static Collection getAllCommands(@Nonnull SlimefunCommand cmd commands.add(new DebugFishCommand(plugin, cmd)); commands.add(new BackpackCommand(plugin, cmd)); commands.add(new ChargeCommand(plugin, cmd)); + commands.add(new DebugCommand(plugin, cmd)); return commands; } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/commands/subcommands/StatsCommand.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/commands/subcommands/StatsCommand.java index 6e08f6e01f..cb9069eb6a 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/commands/subcommands/StatsCommand.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/commands/subcommands/StatsCommand.java @@ -23,6 +23,12 @@ class StatsCommand extends SubCommand { @Override public void onExecute(CommandSender sender, String[] args) { + // Check if researching is even enabled + if (!Slimefun.getRegistry().isResearchingEnabled()) { + Slimefun.getLocalization().sendMessage(sender, "messages.researching-is-disabled"); + return; + } + if (args.length > 1) { if (sender.hasPermission("slimefun.stats.others") || sender instanceof ConsoleCommandSender) { Optional player = PlayerList.findByName(args[1]); @@ -35,8 +41,8 @@ public void onExecute(CommandSender sender, String[] args) { } else { Slimefun.getLocalization().sendMessage(sender, "messages.no-permission", true); } - } else if (sender instanceof Player) { - PlayerProfile.get((Player) sender, profile -> profile.sendStats(sender)); + } else if (sender instanceof Player player) { + PlayerProfile.get(player, profile -> profile.sendStats(sender)); } else { Slimefun.getLocalization().sendMessage(sender, "messages.only-players", true); } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/commands/subcommands/TeleporterCommand.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/commands/subcommands/TeleporterCommand.java index 8401e3b5db..891ba85059 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/commands/subcommands/TeleporterCommand.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/commands/subcommands/TeleporterCommand.java @@ -21,18 +21,17 @@ class TeleporterCommand extends SubCommand { @Override public void onExecute(CommandSender sender, String[] args) { - if (sender instanceof Player) { + if (sender instanceof Player player) { if (sender.hasPermission("slimefun.command.teleporter")) { if (args.length == 1) { - Player p = (Player) sender; - Slimefun.getGPSNetwork().getTeleportationManager().openTeleporterGUI(p, p.getUniqueId(), p.getLocation().getBlock().getRelative(BlockFace.DOWN), 999999999); + Slimefun.getGPSNetwork().getTeleportationManager().openTeleporterGUI(player, player.getUniqueId(), player.getLocation().getBlock().getRelative(BlockFace.DOWN), 999999999); } else if (args.length == 2) { @SuppressWarnings("deprecation") - OfflinePlayer player = Bukkit.getOfflinePlayer(args[1]); + OfflinePlayer targetPlayer = Bukkit.getOfflinePlayer(args[1]); - if (player.getName() != null) { - Slimefun.getGPSNetwork().getTeleportationManager().openTeleporterGUI((Player) sender, player.getUniqueId(), ((Player) sender).getLocation().getBlock().getRelative(BlockFace.DOWN), 999999999); + if (targetPlayer.getName() != null) { + Slimefun.getGPSNetwork().getTeleportationManager().openTeleporterGUI(player, targetPlayer.getUniqueId(), player.getLocation().getBlock().getRelative(BlockFace.DOWN), 999999999); } else { Slimefun.getLocalization().sendMessage(sender, "messages.unknown-player", msg -> msg.replace("%player%", args[1])); } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/commands/subcommands/TimingsCommand.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/commands/subcommands/TimingsCommand.java index a22d2683f9..fe20360411 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/commands/subcommands/TimingsCommand.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/commands/subcommands/TimingsCommand.java @@ -98,9 +98,9 @@ private boolean hasFlag(String[] args, String flag) { @Nonnull @ParametersAreNonnullByDefault - private PerformanceInspector inspectorOf(CommandSender sender, boolean verbose, SummaryOrderType orderType) { - if (sender instanceof Player) { - return new PlayerPerformanceInspector((Player) sender, orderType); + private PerformanceInspector inspectorOf((@Nonnull CommandSender sender, boolean verbose, SummaryOrderType orderType) { + if (sender instanceof Player player) { + return new PlayerPerformanceInspector(player); } else { return new ConsolePerformanceInspector(sender, verbose, orderType); } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/commands/subcommands/VersionsCommand.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/commands/subcommands/VersionsCommand.java index f01b19112a..415d75555b 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/commands/subcommands/VersionsCommand.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/commands/subcommands/VersionsCommand.java @@ -146,7 +146,7 @@ private void addPluginVersions(@Nonnull ComponentBuilder builder) { secondaryColor = ChatColor.DARK_GREEN; String authors = String.join(", ", plugin.getDescription().getAuthors()); - if (plugin instanceof SlimefunAddon && ((SlimefunAddon) plugin).getBugTrackerURL() != null) { + if (plugin instanceof SlimefunAddon addon && addon.getBugTrackerURL() != null) { // @formatter:off hoverEvent = new HoverEvent(HoverEvent.Action.SHOW_TEXT, new Text(new ComponentBuilder() .append("Author(s): ") @@ -158,7 +158,7 @@ private void addPluginVersions(@Nonnull ComponentBuilder builder) { )); // @formatter:on - clickEvent = new ClickEvent(ClickEvent.Action.OPEN_URL, ((SlimefunAddon) plugin).getBugTrackerURL()); + clickEvent = new ClickEvent(ClickEvent.Action.OPEN_URL, addon.getBugTrackerURL()); } else { // @formatter:off hoverEvent = new HoverEvent(HoverEvent.Action.SHOW_TEXT, new Text(new ComponentBuilder() @@ -173,7 +173,7 @@ private void addPluginVersions(@Nonnull ComponentBuilder builder) { primaryColor = ChatColor.RED; secondaryColor = ChatColor.DARK_RED; - if (plugin instanceof SlimefunAddon && ((SlimefunAddon) plugin).getBugTrackerURL() != null) { + if (plugin instanceof SlimefunAddon addon && addon.getBugTrackerURL() != null) { // @formatter:off hoverEvent = new HoverEvent(HoverEvent.Action.SHOW_TEXT, new Text(new ComponentBuilder() .append("This plugin is disabled.\nCheck the console for an error message.") @@ -184,8 +184,6 @@ private void addPluginVersions(@Nonnull ComponentBuilder builder) { )); // @formatter:on - SlimefunAddon addon = (SlimefunAddon) plugin; - if (addon.getBugTrackerURL() != null) { clickEvent = new ClickEvent(ClickEvent.Action.OPEN_URL, addon.getBugTrackerURL()); } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/debug/Debug.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/debug/Debug.java new file mode 100644 index 0000000000..433ec5e1cd --- /dev/null +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/debug/Debug.java @@ -0,0 +1,134 @@ +package io.github.thebusybiscuit.slimefun4.core.debug; + +import java.util.logging.Level; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import io.github.thebusybiscuit.slimefun4.implementation.Slimefun; + +/** + * This class is responsible for debug logging. + * Server owners can enable testing specific cases and have debug logs for those cases. + * + * Note: We don't have validates in here because we want it to be quick and it's mainly for us internal devs. + * + * @author WalshyDev + */ +public final class Debug { + + private static String testCase = null; + + private Debug() {} + + /** + * Log a message if the {@link TestCase} is currently enabled. + * + * @param testCase + * The {@link TestCase} to use + * @param msg + * The message to log + */ + public static void log(@Nonnull TestCase testCase, @Nonnull String msg) { + log(testCase.toString(), msg, new Object[0]); + } + + /** + * Log a variable message if the {@link TestCase} is currently enabled. + * + * @param testCase + * The {@link TestCase} to use + * @param msg + * The message to log + * @param vars + * The variables to replace, use "{}" in the message and have it replaced with a specified thing + */ + public static void log(@Nonnull TestCase testCase, @Nonnull String msg, @Nonnull Object... vars) { + log(testCase.toString(), msg, vars); + } + + /** + * Log a message if the test case is currently enabled. + * + * @param test + * The test case to use + * @param msg + * The message to log + */ + public static void log(@Nonnull String test, @Nonnull String msg) { + log(test, msg, new Object[0]); + } + + /** + * Log a message if the test case is currently enabled. + * + * @param test + * The test case to use + * @param msg + * The message to log + * @param vars + * The variables to replace, use "{}" in the message and have it replaced with a specified thing + */ + public static void log(@Nonnull String test, @Nonnull String msg, @Nonnull Object... vars) { + if (testCase == null || !testCase.equals(test)) { + return; + } + + if (vars.length > 0) { + String formatted = formatMessage(msg, vars); + Slimefun.logger().log(Level.INFO, "[DEBUG {0}] {1}", new Object[] { test, formatted }); + } else { + Slimefun.logger().log(Level.INFO, "[DEBUG {0}] {1}", new Object[] { test, msg }); + } + } + + /** + * Format the message. Replace "{}" with the supplied variable. This is quick and works great. + * + * + * Benchmark Mode Cnt Score Error Units + * MyBenchmark.loopAllChars thrpt 5 2336518.563 ± 24129.488 ops/s + * MyBenchmark.whileFindChars thrpt 5 3319022.018 ± 45663.898 ops/s + * + * + * @param msg + * The message to send. For variables, you can pass "{}" + * @param vars + * A varargs of the variables you wish to use + * + * @return The resulting String + */ + private static @Nonnull String formatMessage(@Nonnull String msg, @Nonnull Object... vars) { + int i = 0; + int idx = 0; + + // Find an opening curly brace `{` and validate the next char is a closing one `}` + while ((i = msg.indexOf('{', i)) != -1 && msg.charAt(i + 1) == '}') { + // Substring up to the opening brace `{`, add the variable for this and add the rest of the message + msg = msg.substring(0, i) + vars[idx] + msg.substring(i + 2); + idx++; + } + + return msg; + } + + /** + * Set the current test case for this server. + * This will enable debug logging for this specific case which can be helpful by Slimefun or addon developers. + * + * @param test + * The test case to enable or null to disable it + */ + public static void setTestCase(@Nullable String test) { + testCase = test; + } + + /** + * Get the current test case for this server or null if disabled + * + * @return The current test case to enable or null if disabled + */ + public static @Nullable String getTestCase() { + return testCase; + } +} diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/debug/TestCase.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/debug/TestCase.java new file mode 100644 index 0000000000..00b3bbf70c --- /dev/null +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/debug/TestCase.java @@ -0,0 +1,28 @@ +package io.github.thebusybiscuit.slimefun4.core.debug; + +import java.util.Locale; + +import javax.annotation.Nonnull; + +/** + * Test cases in Slimefun. These are very useful for debugging why behavior is happening. + * Server owners can enable these with {@code /sf debug } + * + * @author WalshyDev + */ +public enum TestCase { + + /** + * Cargo input testing. This will log what is going on with CARGO_INPUT_NODEs so that we can see what items are. + * being checked and why it is comparing IDs or meta. + * This is helpful for us to check into why input nodes are taking a while for servers. + */ + CARGO_INPUT_TESTING; + + TestCase() {} + + @Override + public @Nonnull String toString() { + return "slimefun_" + name().toLowerCase(Locale.ROOT); + } +} diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/debug/package-info.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/debug/package-info.java new file mode 100644 index 0000000000..57ef3809c0 --- /dev/null +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/debug/package-info.java @@ -0,0 +1,5 @@ +/** + * This package holds the debug functionality of Slimefun, these can be used as addons but are mostly just + * going to be used by Slimefun itself. + */ +package io.github.thebusybiscuit.slimefun4.core.debug; \ No newline at end of file diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/guide/GuideHistory.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/guide/GuideHistory.java index ca9245c10f..d84adcfee2 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/guide/GuideHistory.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/guide/GuideHistory.java @@ -188,14 +188,14 @@ public void goBack(@Nonnull SlimefunGuideImplementation guide) { private void open(@Nonnull SlimefunGuideImplementation guide, @Nullable GuideEntry entry) { if (entry == null) { guide.openMainMenu(profile, mainMenuPage); - } else if (entry.getIndexedObject() instanceof ItemGroup) { - guide.openItemGroup(profile, (ItemGroup) entry.getIndexedObject(), entry.getPage()); - } else if (entry.getIndexedObject() instanceof SlimefunItem) { - guide.displayItem(profile, (SlimefunItem) entry.getIndexedObject(), false); - } else if (entry.getIndexedObject() instanceof ItemStack) { - guide.displayItem(profile, (ItemStack) entry.getIndexedObject(), entry.getPage(), false); - } else if (entry.getIndexedObject() instanceof String) { - guide.openSearch(profile, (String) entry.getIndexedObject(), false); + } else if (entry.getIndexedObject() instanceof ItemGroup group) { + guide.openItemGroup(profile, group, entry.getPage()); + } else if (entry.getIndexedObject() instanceof SlimefunItem item) { + guide.displayItem(profile, item, false); + } else if (entry.getIndexedObject() instanceof ItemStack stack) { + guide.displayItem(profile, stack, entry.getPage(), false); + } else if (entry.getIndexedObject() instanceof String query) { + guide.openSearch(profile, query, false); } else { throw new IllegalStateException("Unknown GuideHistory entry: " + entry.getIndexedObject()); } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/guide/SlimefunGuideMode.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/guide/SlimefunGuideMode.java index 25c6776b45..5b269f96e4 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/guide/SlimefunGuideMode.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/guide/SlimefunGuideMode.java @@ -2,8 +2,6 @@ import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItem; -import me.mrCookieSlime.CSCoreLibPlugin.general.Inventory.ChestMenu; - /** * This enum holds the different designs a {@link SlimefunGuide} can have. * Each constant corresponds to a {@link SlimefunGuideImplementation}. @@ -17,7 +15,7 @@ public enum SlimefunGuideMode { /** - * This design is the standard layout, it uses a {@link ChestMenu} + * This design is the standard layout used in survival mode. */ SURVIVAL_MODE, diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/guide/options/ContributorsMenu.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/guide/options/ContributorsMenu.java index 951a9e064f..93e8d89a35 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/guide/options/ContributorsMenu.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/guide/options/ContributorsMenu.java @@ -6,7 +6,6 @@ import java.util.List; import java.util.Map; -import org.bukkit.Sound; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.SkullMeta; @@ -15,6 +14,7 @@ import io.github.bakedlibs.dough.common.CommonPatterns; import io.github.bakedlibs.dough.items.CustomItemStack; import io.github.thebusybiscuit.slimefun4.core.services.github.Contributor; +import io.github.thebusybiscuit.slimefun4.core.services.sounds.SoundEffect; import io.github.thebusybiscuit.slimefun4.implementation.Slimefun; import io.github.thebusybiscuit.slimefun4.utils.ChatUtils; import io.github.thebusybiscuit.slimefun4.utils.ChestMenuUtils; @@ -36,7 +36,7 @@ public static void open(Player p, int page) { ChestMenu menu = new ChestMenu(Slimefun.getLocalization().getMessage(p, "guide.title.credits")); menu.setEmptySlotsClickable(false); - menu.addMenuOpeningHandler(pl -> pl.playSound(pl.getLocation(), Sound.BLOCK_NOTE_BLOCK_HARP, 0.7F, 0.7F)); + menu.addMenuOpeningHandler(SoundEffect.GUIDE_CONTRIBUTORS_OPEN_SOUND::playFor); ChestMenuUtils.drawBackground(menu, 0, 2, 3, 4, 5, 6, 7, 8, 45, 47, 48, 49, 50, 51, 53); diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/guide/options/FireworksOption.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/guide/options/FireworksOption.java index 85fa76612f..07b757018a 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/guide/options/FireworksOption.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/guide/options/FireworksOption.java @@ -10,6 +10,7 @@ import io.github.bakedlibs.dough.data.persistent.PersistentDataAPI; import io.github.bakedlibs.dough.items.CustomItemStack; import io.github.thebusybiscuit.slimefun4.api.SlimefunAddon; +import io.github.thebusybiscuit.slimefun4.core.SlimefunRegistry; import io.github.thebusybiscuit.slimefun4.implementation.Slimefun; class FireworksOption implements SlimefunGuideOption { @@ -26,7 +27,9 @@ public NamespacedKey getKey() { @Override public Optional getDisplayItem(Player p, ItemStack guide) { - if (Slimefun.getRegistry().isResearchFireworkEnabled()) { + SlimefunRegistry registry = Slimefun.getRegistry(); + + if (registry.isResearchingEnabled() && registry.isResearchFireworkEnabled()) { boolean enabled = getSelectedOption(p, guide).orElse(true); ItemStack item = new CustomItemStack(Material.FIREWORK_ROCKET, "&bFireworks: &" + (enabled ? "aYes" : "4No"), "", "&7You can now toggle whether you", "&7will be presented with a big firework", "&7upon researching an item.", "", "&7\u21E8 &eClick to " + (enabled ? "disable" : "enable") + " your fireworks"); return Optional.of(item); diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/guide/options/LearningAnimationOption.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/guide/options/LearningAnimationOption.java index 11040f2c3f..5d694c896c 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/guide/options/LearningAnimationOption.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/guide/options/LearningAnimationOption.java @@ -13,6 +13,7 @@ import io.github.bakedlibs.dough.data.persistent.PersistentDataAPI; import io.github.bakedlibs.dough.items.CustomItemStack; import io.github.thebusybiscuit.slimefun4.api.SlimefunAddon; +import io.github.thebusybiscuit.slimefun4.core.SlimefunRegistry; import io.github.thebusybiscuit.slimefun4.implementation.Slimefun; /** @@ -39,7 +40,9 @@ public NamespacedKey getKey() { @Nonnull @Override public Optional getDisplayItem(@Nonnull Player p, @Nonnull ItemStack guide) { - if (Slimefun.getRegistry().isLearningAnimationDisabled()) { + SlimefunRegistry registry = Slimefun.getRegistry(); + + if (!registry.isResearchingEnabled() || registry.isLearningAnimationDisabled()) { return Optional.empty(); } else { boolean enabled = getSelectedOption(p, guide).orElse(true); diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/guide/options/PlayerLanguageOption.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/guide/options/PlayerLanguageOption.java index a6fe624586..7a4779ff59 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/guide/options/PlayerLanguageOption.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/guide/options/PlayerLanguageOption.java @@ -6,7 +6,6 @@ import org.bukkit.ChatColor; import org.bukkit.NamespacedKey; -import org.bukkit.Sound; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; @@ -15,6 +14,7 @@ import io.github.thebusybiscuit.slimefun4.api.SlimefunAddon; import io.github.thebusybiscuit.slimefun4.api.events.PlayerLanguageChangeEvent; import io.github.thebusybiscuit.slimefun4.core.services.localization.Language; +import io.github.thebusybiscuit.slimefun4.core.services.sounds.SoundEffect; import io.github.thebusybiscuit.slimefun4.implementation.Slimefun; import io.github.thebusybiscuit.slimefun4.utils.ChatUtils; import io.github.thebusybiscuit.slimefun4.utils.ChestMenuUtils; @@ -79,7 +79,7 @@ private void openLanguageSelection(Player p, ItemStack guide) { ChestMenu menu = new ChestMenu(Slimefun.getLocalization().getMessage(p, "guide.title.languages")); menu.setEmptySlotsClickable(false); - menu.addMenuOpeningHandler(pl -> pl.playSound(pl.getLocation(), Sound.BLOCK_NOTE_BLOCK_HARP, 0.7F, 0.7F)); + menu.addMenuOpeningHandler(SoundEffect.GUIDE_LANGUAGE_OPEN_SOUND::playFor); for (int i = 0; i < 9; i++) { if (i == 1) { diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/guide/options/SlimefunGuideOption.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/guide/options/SlimefunGuideOption.java index a1318fedfb..1caeb02826 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/guide/options/SlimefunGuideOption.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/guide/options/SlimefunGuideOption.java @@ -2,6 +2,8 @@ import java.util.Optional; +import javax.annotation.Nonnull; + import org.bukkit.Keyed; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; @@ -24,6 +26,7 @@ public interface SlimefunGuideOption extends Keyed { * * @return The registering {@link SlimefunAddon} */ + @Nonnull SlimefunAddon getAddon(); Optional getDisplayItem(Player p, ItemStack guide); diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/guide/options/SlimefunGuideSettings.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/guide/options/SlimefunGuideSettings.java index 48b7ac5a50..6761a9c19c 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/guide/options/SlimefunGuideSettings.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/guide/options/SlimefunGuideSettings.java @@ -10,7 +10,6 @@ import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.Material; -import org.bukkit.Sound; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; @@ -21,6 +20,7 @@ import io.github.thebusybiscuit.slimefun4.core.services.LocalizationService; import io.github.thebusybiscuit.slimefun4.core.services.github.GitHubService; import io.github.thebusybiscuit.slimefun4.core.services.localization.Language; +import io.github.thebusybiscuit.slimefun4.core.services.sounds.SoundEffect; import io.github.thebusybiscuit.slimefun4.implementation.Slimefun; import io.github.thebusybiscuit.slimefun4.utils.ChatUtils; import io.github.thebusybiscuit.slimefun4.utils.ChestMenuUtils; @@ -63,7 +63,7 @@ public static void openSettings(Player p, ItemStack guide) { ChestMenu menu = new ChestMenu(Slimefun.getLocalization().getMessage(p, "guide.title.settings")); menu.setEmptySlotsClickable(false); - menu.addMenuOpeningHandler(pl -> pl.playSound(pl.getLocation(), Sound.BLOCK_NOTE_BLOCK_HARP, 0.7F, 0.7F)); + menu.addMenuOpeningHandler(SoundEffect.GUIDE_OPEN_SETTING_SOUND::playFor); ChestMenuUtils.drawBackground(menu, BACKGROUND_SLOTS); diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/handlers/RainbowTickHandler.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/handlers/RainbowTickHandler.java index 17e01dde0f..38900d8524 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/handlers/RainbowTickHandler.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/handlers/RainbowTickHandler.java @@ -1,137 +1,134 @@ -package io.github.thebusybiscuit.slimefun4.core.handlers; - -import java.util.Arrays; -import java.util.List; - -import javax.annotation.Nonnull; - -import org.apache.commons.lang.Validate; -import org.bukkit.Material; -import org.bukkit.block.Block; -import org.bukkit.block.BlockFace; -import org.bukkit.block.data.BlockData; -import org.bukkit.block.data.type.GlassPane; - -import io.github.bakedlibs.dough.collections.LoopIterator; -import io.github.thebusybiscuit.slimefun4.api.MinecraftVersion; -import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItem; -import io.github.thebusybiscuit.slimefun4.implementation.Slimefun; -import io.github.thebusybiscuit.slimefun4.implementation.items.blocks.RainbowBlock; -import io.github.thebusybiscuit.slimefun4.utils.ColoredMaterial; - -import me.mrCookieSlime.CSCoreLibPlugin.Configuration.Config; -import me.mrCookieSlime.Slimefun.Objects.handlers.BlockTicker; - -/** - * This is a {@link BlockTicker} that is exclusively used for Rainbow blocks. - * On every tick it cycles through the {@link LoopIterator} and chooses the next {@link Material} - * and sets itself to that. - * - * @author TheBusyBiscuit - * - * @see RainbowBlock - * - */ -public class RainbowTickHandler extends BlockTicker { - - private final LoopIterator iterator; - private final boolean glassPanes; - private Material material; - - public RainbowTickHandler(@Nonnull List materials) { - Validate.noNullElements(materials, "A RainbowTicker cannot have a Material that is null!"); - - if (materials.isEmpty()) { - throw new IllegalArgumentException("A RainbowTicker must have at least one Material associated with it!"); - } - - glassPanes = containsGlassPanes(materials); - iterator = new LoopIterator<>(materials); - material = iterator.next(); - } - - public RainbowTickHandler(@Nonnull Material... materials) { - this(Arrays.asList(materials)); - } - - public RainbowTickHandler(@Nonnull ColoredMaterial material) { - this(material.asList()); - } - - /** - * This method checks whether a given {@link Material} array contains any {@link Material} - * that would result in a {@link GlassPane} {@link BlockData}. - * This is done to save performance, so we don't have to validate {@link BlockData} at - * runtime. - * - * @param materials - * The {@link Material} Array to check - * - * @return Whether the array contained any {@link GlassPane} materials - */ - private boolean containsGlassPanes(@Nonnull List materials) { - if (Slimefun.getMinecraftVersion() == MinecraftVersion.UNIT_TEST) { - // BlockData is not available to us during Unit Tests :/ - return false; - } - - for (Material type : materials) { - /** - * This BlockData is purely virtual and only created on startup, it should have - * no impact on performance, in fact it should save performance as it preloads - * the data but also saves heavy calls for other Materials - */ - if (type.createBlockData() instanceof GlassPane) { - return true; - } - } - - return false; - } - - @Override - public void tick(Block b, SlimefunItem item, Config data) { - if (b.getType() == Material.AIR) { - /** - * The block was broken, setting the Material now would result in a - * duplication glitch - */ - return; - } - - if (glassPanes) { - BlockData blockData = b.getBlockData(); - - if (blockData instanceof GlassPane) { - BlockData block = material.createBlockData(bd -> { - if (bd instanceof GlassPane) { - GlassPane previousData = (GlassPane) blockData; - GlassPane nextData = (GlassPane) bd; - - nextData.setWaterlogged(previousData.isWaterlogged()); - - for (BlockFace face : previousData.getAllowedFaces()) { - nextData.setFace(face, previousData.hasFace(face)); - } - } - }); - - b.setBlockData(block, false); - return; - } - } - - b.setType(material, false); - } - - @Override - public void uniqueTick() { - material = iterator.next(); - } - - @Override - public boolean isSynchronized() { - return true; - } - -} +package io.github.thebusybiscuit.slimefun4.core.handlers; + +import java.util.Arrays; +import java.util.List; + +import javax.annotation.Nonnull; + +import org.apache.commons.lang.Validate; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.block.data.BlockData; +import org.bukkit.block.data.type.GlassPane; + +import io.github.bakedlibs.dough.collections.LoopIterator; +import io.github.thebusybiscuit.slimefun4.api.MinecraftVersion; +import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItem; +import io.github.thebusybiscuit.slimefun4.implementation.Slimefun; +import io.github.thebusybiscuit.slimefun4.implementation.items.blocks.RainbowBlock; +import io.github.thebusybiscuit.slimefun4.utils.ColoredMaterial; + +import me.mrCookieSlime.CSCoreLibPlugin.Configuration.Config; +import me.mrCookieSlime.Slimefun.Objects.handlers.BlockTicker; + +/** + * This is a {@link BlockTicker} that is exclusively used for Rainbow blocks. + * On every tick it cycles through the {@link LoopIterator} and chooses the next {@link Material} + * and sets itself to that. + * + * @author TheBusyBiscuit + * + * @see RainbowBlock + * + */ +public class RainbowTickHandler extends BlockTicker { + + private final LoopIterator iterator; + private final boolean glassPanes; + private Material material; + + public RainbowTickHandler(@Nonnull List materials) { + Validate.noNullElements(materials, "A RainbowTicker cannot have a Material that is null!"); + + if (materials.isEmpty()) { + throw new IllegalArgumentException("A RainbowTicker must have at least one Material associated with it!"); + } + + glassPanes = containsGlassPanes(materials); + iterator = new LoopIterator<>(materials); + material = iterator.next(); + } + + public RainbowTickHandler(@Nonnull Material... materials) { + this(Arrays.asList(materials)); + } + + public RainbowTickHandler(@Nonnull ColoredMaterial material) { + this(material.asList()); + } + + /** + * This method checks whether a given {@link Material} array contains any {@link Material} + * that would result in a {@link GlassPane} {@link BlockData}. + * This is done to save performance, so we don't have to validate {@link BlockData} at + * runtime. + * + * @param materials + * The {@link Material} Array to check + * + * @return Whether the array contained any {@link GlassPane} materials + */ + private boolean containsGlassPanes(@Nonnull List materials) { + if (Slimefun.getMinecraftVersion() == MinecraftVersion.UNIT_TEST) { + // BlockData is not available to us during Unit Tests :/ + return false; + } + + for (Material type : materials) { + /* + This BlockData is purely virtual and only created on startup, it should have + no impact on performance, in fact it should save performance as it preloads + the data but also saves heavy calls for other Materials + */ + if (type.createBlockData() instanceof GlassPane) { + return true; + } + } + + return false; + } + + @Override + public void tick(Block b, SlimefunItem item, Config data) { + if (b.getType().isAir()) { + /* + The block was broken, setting the Material now would result in a + duplication glitch + */ + return; + } + + if (glassPanes) { + BlockData blockData = b.getBlockData(); + + if (blockData instanceof GlassPane previousData) { + BlockData block = material.createBlockData(bd -> { + if (bd instanceof GlassPane nextData) { + nextData.setWaterlogged(previousData.isWaterlogged()); + + for (BlockFace face : previousData.getAllowedFaces()) { + nextData.setFace(face, previousData.hasFace(face)); + } + } + }); + + b.setBlockData(block, false); + return; + } + } + + b.setType(material, false); + } + + @Override + public void uniqueTick() { + material = iterator.next(); + } + + @Override + public boolean isSynchronized() { + return true; + } + +} diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/handlers/package-info.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/handlers/package-info.java index 2dc20269ac..d3c439ec1e 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/handlers/package-info.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/handlers/package-info.java @@ -1,5 +1,5 @@ /** - * This package contains all variations of {@link me.mrCookieSlime.Slimefun.Objects.handlers.ItemHandler} that - * can be assigned to a {@link me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem} + * This package contains all variations of {@link io.github.thebusybiscuit.slimefun4.api.items.ItemHandler} that + * can be assigned to a {@link io.github.thebusybiscuit.slimefun4.api.items.SlimefunItem} */ package io.github.thebusybiscuit.slimefun4.core.handlers; \ No newline at end of file diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/machines/MachineProcessor.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/machines/MachineProcessor.java index 71aa91b8a5..33ccb4111d 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/machines/MachineProcessor.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/machines/MachineProcessor.java @@ -251,7 +251,11 @@ public void updateProgressBar(@Nonnull BlockMenu inv, int slot, @Nonnull T opera // Update the progress bar in our inventory (if anyone is watching) int remainingTicks = operation.getRemainingTicks(); int totalTicks = operation.getTotalTicks(); - ChestMenuUtils.updateProgressbar(inv, slot, remainingTicks, totalTicks, getProgressBar()); + + // Fixes #3538 - If the operation is finished, we don't need to update the progress bar. + if (remainingTicks > 0 || totalTicks > 0) { + ChestMenuUtils.updateProgressbar(inv, slot, remainingTicks, totalTicks, getProgressBar()); + } } } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/multiblocks/MultiBlock.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/multiblocks/MultiBlock.java index d69e862149..4175b20527 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/multiblocks/MultiBlock.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/multiblocks/MultiBlock.java @@ -14,11 +14,9 @@ import org.bukkit.World; import org.bukkit.block.BlockFace; -import io.github.thebusybiscuit.slimefun4.api.MinecraftVersion; import io.github.thebusybiscuit.slimefun4.api.events.MultiBlockInteractEvent; import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItem; import io.github.thebusybiscuit.slimefun4.core.handlers.MultiBlockInteractionHandler; -import io.github.thebusybiscuit.slimefun4.implementation.Slimefun; /** * A {@link MultiBlock} represents a structure build in a {@link World}. @@ -43,11 +41,7 @@ public class MultiBlock { SUPPORTED_TAGS.add(Tag.WOODEN_TRAPDOORS); SUPPORTED_TAGS.add(Tag.WOODEN_SLABS); SUPPORTED_TAGS.add(Tag.WOODEN_FENCES); - - // Add Soul Fire support on 1.16 - if (Slimefun.getMinecraftVersion().isAtLeast(MinecraftVersion.MINECRAFT_1_16)) { - SUPPORTED_TAGS.add(Tag.FIRE); - } + SUPPORTED_TAGS.add(Tag.FIRE); } @Nonnull @@ -155,7 +149,6 @@ private boolean compareBlocks(Material a, @Nullable Material b) { public boolean isSymmetric() { return isSymmetric; } - @Override public String toString() { return "MultiBlock (" + item.getId() + ") {" + Arrays.toString(blocks) + "}"; diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/multiblocks/MultiBlockMachine.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/multiblocks/MultiBlockMachine.java index b5e28c0535..d18f6ba2cd 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/multiblocks/MultiBlockMachine.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/multiblocks/MultiBlockMachine.java @@ -9,7 +9,10 @@ import javax.annotation.Nullable; import javax.annotation.ParametersAreNonnullByDefault; +import com.google.common.base.Preconditions; + import org.apache.commons.lang.Validate; + import org.bukkit.Material; import org.bukkit.World; import org.bukkit.block.Block; @@ -105,11 +108,21 @@ public void postRegister() { public void load() { super.load(); - for (ItemStack recipeItem : displayRecipes) { - SlimefunItem item = SlimefunItem.getByItem(recipeItem); + Preconditions.checkArgument(displayRecipes.size() % 2 == 0, "This MultiBlockMachine's display recipes were illegally modified!"); + + for (int i = 0; i < displayRecipes.size(); i += 2) { + ItemStack inputStack = displayRecipes.get(i); + ItemStack outputStack = null; + if (displayRecipes.size() >= i + 2) { + outputStack = displayRecipes.get(i + 1); + } - if (item == null || !item.isDisabled()) { - recipes.add(new ItemStack[] { recipeItem }); + SlimefunItem inputItem = SlimefunItem.getByItem(inputStack); + SlimefunItem outputItem = SlimefunItem.getByItem(outputStack); + // If the input/output is not a Slimefun item or it's not disabled then it's valid. + if ((inputItem == null || !inputItem.isDisabled()) && (outputItem == null || !outputItem.isDisabled())) { + recipes.add(new ItemStack[] { inputStack }); + recipes.add(new ItemStack[] { outputStack }); } } } @@ -171,11 +184,11 @@ public void load() { * This method handles an output {@link ItemStack} from the {@link MultiBlockMachine} which has a crafting delay * * @param outputItem - * A crafted {@link ItemStack} from {@link MultiBlockMachine} + * A crafted {@link ItemStack} from {@link MultiBlockMachine} * @param block - * Main {@link Block} of our {@link Container} from {@link MultiBlockMachine} + * Main {@link Block} of our {@link Container} from {@link MultiBlockMachine} * @param blockInv - * The {@link Inventory} of our {@link Container} + * The {@link Inventory} of our {@link Container} * */ @ParametersAreNonnullByDefault diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/cargo/AbstractItemNetwork.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/cargo/AbstractItemNetwork.java index 82666eacb5..2cce2bb796 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/cargo/AbstractItemNetwork.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/cargo/AbstractItemNetwork.java @@ -1,17 +1,9 @@ package io.github.thebusybiscuit.slimefun4.core.networks.cargo; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Optional; -import java.util.Queue; -import java.util.Set; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -21,29 +13,15 @@ import org.bukkit.Material; import org.bukkit.block.Block; import org.bukkit.block.BlockFace; -import org.bukkit.block.BlockState; import org.bukkit.block.data.BlockData; import org.bukkit.block.data.Directional; -import org.bukkit.inventory.Inventory; -import org.bukkit.inventory.InventoryHolder; import org.bukkit.inventory.ItemStack; -import org.bukkit.inventory.meta.ItemMeta; -import io.github.bakedlibs.dough.common.ChatColors; -import io.github.bakedlibs.dough.items.CustomItemStack; -import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItem; import io.github.thebusybiscuit.slimefun4.api.network.Network; import io.github.thebusybiscuit.slimefun4.implementation.Slimefun; -import io.github.thebusybiscuit.slimefun4.utils.ChestMenuUtils; -import io.github.thebusybiscuit.slimefun4.utils.NumberUtils; import io.github.thebusybiscuit.slimefun4.utils.SlimefunUtils; -import io.github.thebusybiscuit.slimefun4.utils.itemstack.ItemStackWrapper; -import io.papermc.lib.PaperLib; -import me.mrCookieSlime.Slimefun.api.BlockStorage; -import me.mrCookieSlime.Slimefun.api.inventory.BlockMenu; import me.mrCookieSlime.Slimefun.api.inventory.DirtyChestMenu; -import me.mrCookieSlime.Slimefun.api.inventory.UniversalBlockMenu; import me.mrCookieSlime.Slimefun.api.item_transport.ItemTransportFlow; /** @@ -55,21 +33,6 @@ */ abstract class AbstractItemNetwork extends Network { - private static final int[] slots = { 19, 20, 21, 28, 29, 30, 37, 38, 39 }; - private static final int[] TERMINAL_SLOTS = { 0, 1, 2, 3, 4, 5, 6, 9, 10, 11, 12, 13, 14, 15, 18, 19, 20, 21, 22, 23, 24, 27, 28, 29, 30, 31, 32, 33, 36, 37, 38, 39, 40, 41, 42 }; - private static final int TERMINAL_OUT_SLOT = 17; - - private final ItemStack terminalPlaceholderItem = new CustomItemStack(Material.BARRIER, "&4No Item cached"); - - protected final Set terminals = new HashSet<>(); - protected final Set imports = new HashSet<>(); - protected final Set exports = new HashSet<>(); - - /** - * This represents a {@link Queue} of requests to handle - */ - private final Queue itemRequests = new LinkedList<>(); - /** * This is a cache for the {@link BlockFace} a node is facing, so we don't need to * request the {@link BlockData} each time we visit a node @@ -81,11 +44,11 @@ abstract class AbstractItemNetwork extends Network { */ protected Map filterCache = new HashMap<>(); - protected AbstractItemNetwork(Location regulator) { + protected AbstractItemNetwork(@Nonnull Location regulator) { super(Slimefun.getNetworkManager(), regulator); } - protected Optional getAttachedBlock(Location l) { + protected Optional getAttachedBlock(@Nonnull Location l) { if (l.getWorld().isChunkLoaded(l.getBlockX() >> 4, l.getBlockZ() >> 4)) { Block block = l.getBlock(); @@ -105,235 +68,6 @@ protected Optional getAttachedBlock(Location l) { return Optional.empty(); } - protected void handleItemRequests(Map inventories, Set providers, Set destinations) { - collectImportRequests(inventories); - collectExportRequests(inventories); - collectTerminalRequests(); - - Iterator iterator = itemRequests.iterator(); - while (iterator.hasNext()) { - ItemRequest request = iterator.next(); - BlockMenu menu = BlockStorage.getInventory(request.getTerminal()); - - if (menu != null) { - switch (request.getDirection()) { - case INSERT: - distributeInsertionRequest(inventories, request, menu, iterator, destinations); - break; - case WITHDRAW: - collectExtractionRequest(inventories, request, menu, iterator, providers); - break; - default: - break; - } - } - } - } - - private void distributeInsertionRequest(Map inventories, ItemRequest request, BlockMenu terminal, Iterator iterator, Set destinations) { - ItemStack item = request.getItem(); - - for (Location l : destinations) { - Optional target = getAttachedBlock(l); - - if (target.isPresent()) { - item = CargoUtils.insert(this, inventories, l.getBlock(), target.get(), false, item, ItemStackWrapper.wrap(item)); - - if (item == null) { - terminal.replaceExistingItem(request.getSlot(), null); - break; - } - } - } - - if (item != null) { - terminal.replaceExistingItem(request.getSlot(), item); - } - - iterator.remove(); - } - - private void collectExtractionRequest(Map inventories, ItemRequest request, BlockMenu terminal, Iterator iterator, Set providers) { - int slot = request.getSlot(); - ItemStack prevStack = terminal.getItemInSlot(slot); - - if (!(prevStack == null || (prevStack.getAmount() + request.getItem().getAmount() <= prevStack.getMaxStackSize() && SlimefunUtils.isItemSimilar(prevStack, request.getItem(), true, false)))) { - iterator.remove(); - return; - } - - ItemStack stack = null; - ItemStack item = request.getItem(); - - for (Location l : providers) { - Optional target = getAttachedBlock(l); - - if (target.isPresent()) { - ItemStack is = CargoUtils.withdraw(this, inventories, l.getBlock(), target.get(), item); - - if (is != null) { - if (stack == null) { - stack = is; - } else { - stack = new CustomItemStack(stack, stack.getAmount() + is.getAmount()); - } - - if (is.getAmount() == item.getAmount()) { - break; - } else { - item = new CustomItemStack(item, item.getAmount() - is.getAmount()); - } - } - } - } - - if (stack != null) { - ItemStack prev = terminal.getItemInSlot(slot); - - if (prev == null) { - terminal.replaceExistingItem(slot, stack); - } else { - terminal.replaceExistingItem(slot, new CustomItemStack(stack, stack.getAmount() + prev.getAmount())); - } - } - - iterator.remove(); - } - - private void collectImportRequests(Map inventories) { - SlimefunItem item = SlimefunItem.getById("CT_IMPORT_BUS"); - - for (Location bus : imports) { - long timestamp = Slimefun.getProfiler().newEntry(); - BlockMenu menu = BlockStorage.getInventory(bus); - - if (menu.getItemInSlot(17) == null) { - Optional target = getAttachedBlock(bus); - - if (target.isPresent()) { - ItemStackAndInteger stack = CargoUtils.withdraw(this, inventories, bus.getBlock(), target.get()); - - if (stack != null) { - menu.replaceExistingItem(17, stack.getItem()); - } - } - } - - if (menu.getItemInSlot(17) != null) { - itemRequests.add(new ItemRequest(bus, 17, menu.getItemInSlot(17), ItemTransportFlow.INSERT)); - } - - Slimefun.getProfiler().closeEntry(bus, item, timestamp); - } - } - - private void collectExportRequests(Map inventories) { - SlimefunItem item = SlimefunItem.getById("CT_EXPORT_BUS"); - - for (Location bus : exports) { - long timestamp = Slimefun.getProfiler().newEntry(); - BlockMenu menu = BlockStorage.getInventory(bus); - - ItemStack itemSlot17 = menu.getItemInSlot(17); - if (itemSlot17 != null) { - Optional target = getAttachedBlock(bus); - target.ifPresent(block -> menu.replaceExistingItem(17, CargoUtils.insert(this, inventories, bus.getBlock(), block, false, itemSlot17, ItemStackWrapper.wrap(itemSlot17)))); - } - - if (menu.getItemInSlot(17) == null) { - List items = new ArrayList<>(); - - for (int slot : slots) { - ItemStack template = menu.getItemInSlot(slot); - - if (template != null) { - items.add(new CustomItemStack(template, 1)); - } - } - - if (!items.isEmpty()) { - int index = Integer.parseInt(BlockStorage.getLocationInfo(bus, "index")); - - index++; - if (index > (items.size() - 1)) { - index = 0; - } - - BlockStorage.addBlockInfo(bus, "index", String.valueOf(index)); - itemRequests.add(new ItemRequest(bus, 17, items.get(index), ItemTransportFlow.WITHDRAW)); - } - } - - Slimefun.getProfiler().closeEntry(bus, item, timestamp); - } - } - - private void collectTerminalRequests() { - for (Location terminal : terminals) { - BlockMenu menu = BlockStorage.getInventory(terminal); - ItemStack sendingItem = menu.getItemInSlot(TERMINAL_OUT_SLOT); - - if (sendingItem != null) { - itemRequests.add(new ItemRequest(terminal, TERMINAL_OUT_SLOT, sendingItem, ItemTransportFlow.INSERT)); - } - } - } - - /** - * This method updates every terminal on the network with {@link ItemStack ItemStacks} - * found in any provider of the network. - * - * @param providers - * A {@link Set} of providers to this {@link AbstractItemNetwork} - * - * @return The time it took to compute this operation - */ - protected long updateTerminals(@Nonnull Set providers) { - if (terminals.isEmpty()) { - // Performance improvement - We don't need to compute items for - // Cargo networks without any Chest Terminals - return 0; - } - - // Timings will be slightly inaccurate here but most often people are not - // gonna use no more than one terminal anyway, so this might be fine - long timestamp = System.nanoTime(); - Location firstTerminal = null; - SlimefunItem item = SlimefunItem.getById("CHEST_TERMINAL"); - List items = findAvailableItems(providers); - - try { - for (Location l : terminals) { - BlockMenu terminal = BlockStorage.getInventory(l); - String data = BlockStorage.getLocationInfo(l, "page"); - int page = data == null ? 1 : Integer.parseInt(data); - - if (!items.isEmpty() && items.size() < (page - 1) * TERMINAL_SLOTS.length + 1) { - page = 1; - BlockStorage.addBlockInfo(l, "page", String.valueOf(1)); - } - - for (int i = 0; i < TERMINAL_SLOTS.length; i++) { - int slot = TERMINAL_SLOTS[i]; - int index = i + (TERMINAL_SLOTS.length * (page - 1)); - updateTerminal(l, terminal, slot, index, items); - } - - if (firstTerminal == null) { - firstTerminal = l; - } - } - } catch (Exception | LinkageError x) { - item.error("An Exception was caused while trying to tick Chest terminals", x); - } - - if (firstTerminal != null) { - return Slimefun.getProfiler().closeEntry(firstTerminal, item, timestamp); - } else { - return System.nanoTime() - timestamp; - } - } - @Override public void markDirty(@Nonnull Location l) { markCargoNodeConfigurationDirty(l); @@ -357,88 +91,6 @@ public void markCargoNodeConfigurationDirty(@Nonnull Location node) { connectorCache.remove(node); } - @ParametersAreNonnullByDefault - private void updateTerminal(Location l, BlockMenu terminal, int slot, int index, List items) { - if (items.size() > index) { - ItemStackAndInteger item = items.get(index); - - ItemStack stack = item.getItem().clone(); - stack.setAmount(1); - ItemMeta im = stack.getItemMeta(); - List lore = new ArrayList<>(); - lore.add(""); - lore.add(ChatColors.color("&7Stored Items: &f" + NumberUtils.getCompactDouble(item.getInt()))); - - if (stack.getMaxStackSize() > 1) { - int amount = item.getInt() > stack.getMaxStackSize() ? stack.getMaxStackSize() : item.getInt(); - lore.add(ChatColors.color("&7")); - } else { - lore.add(ChatColors.color("&7")); - } - - lore.add(""); - - if (im.hasLore()) { - lore.addAll(im.getLore()); - } - - im.setLore(lore); - stack.setItemMeta(im); - terminal.replaceExistingItem(slot, stack); - terminal.addMenuClickHandler(slot, (p, sl, is, action) -> { - int amount = item.getInt() > item.getItem().getMaxStackSize() ? item.getItem().getMaxStackSize() : item.getInt(); - ItemStack requestedItem = new CustomItemStack(item.getItem(), action.isRightClicked() ? amount : 1); - itemRequests.add(new ItemRequest(l, 44, requestedItem, ItemTransportFlow.WITHDRAW)); - return false; - }); - - } else { - terminal.replaceExistingItem(slot, terminalPlaceholderItem); - terminal.addMenuClickHandler(slot, ChestMenuUtils.getEmptyClickHandler()); - } - } - - @Nonnull - private List findAvailableItems(@Nonnull Set providers) { - List items = new LinkedList<>(); - - for (Location l : providers) { - Optional block = getAttachedBlock(l); - - if (block.isPresent()) { - findAllItems(items, l, block.get()); - } - } - - Collections.sort(items, Comparator.comparingInt(item -> -item.getInt())); - return items; - } - - @ParametersAreNonnullByDefault - private void findAllItems(List items, Location l, Block target) { - UniversalBlockMenu menu = BlockStorage.getUniversalInventory(target); - - if (menu != null) { - for (int slot : menu.getPreset().getSlotsAccessedByItemTransport(menu, ItemTransportFlow.WITHDRAW, null)) { - ItemStack is = menu.getItemInSlot(slot); - filter(is, items, l); - } - } else if (BlockStorage.hasInventory(target)) { - BlockMenu blockMenu = BlockStorage.getInventory(target); - handleWithdraw(blockMenu, items, l); - } else if (CargoUtils.hasInventory(target)) { - BlockState state = PaperLib.getBlockState(target, false).getState(); - - if (state instanceof InventoryHolder) { - Inventory inv = ((InventoryHolder) state).getInventory(); - - for (ItemStack is : inv.getContents()) { - filter(is, items, l); - } - } - } - } - @ParametersAreNonnullByDefault private void handleWithdraw(DirtyChestMenu menu, List items, Location l) { for (int slot : menu.getPreset().getSlotsAccessedByItemTransport(menu, ItemTransportFlow.WITHDRAW, null)) { @@ -464,8 +116,7 @@ private void filter(@Nullable ItemStack stack, List items, } } - @Nonnull - protected ItemFilter getItemFilter(@Nonnull Block node) { + protected @Nonnull ItemFilter getItemFilter(@Nonnull Block node) { Location loc = node.getLocation(); ItemFilter filter = filterCache.get(loc); diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/cargo/CargoNet.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/cargo/CargoNet.java index 29b87c18b3..7ef1da5cdb 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/cargo/CargoNet.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/cargo/CargoNet.java @@ -50,13 +50,11 @@ public class CargoNet extends AbstractItemNetwork implements HologramOwner { protected final Map roundRobin = new HashMap<>(); private int tickDelayThreshold = 0; - @Nullable - public static CargoNet getNetworkFromLocation(@Nonnull Location l) { + public static @Nullable CargoNet getNetworkFromLocation(@Nonnull Location l) { return Slimefun.getNetworkManager().getNetworkFromLocation(l, CargoNet.class).orElse(null); } - @Nonnull - public static CargoNet getNetworkFromLocationOrCreate(@Nonnull Location l) { + public static @Nonnull CargoNet getNetworkFromLocationOrCreate(@Nonnull Location l) { Optional cargoNetwork = Slimefun.getNetworkManager().getNetworkFromLocation(l, CargoNet.class); if (cargoNetwork.isPresent()) { @@ -96,21 +94,14 @@ public NetworkComponent classifyLocation(@Nonnull Location l) { return null; } - switch (id) { - case "CARGO_MANAGER": - return NetworkComponent.REGULATOR; - case "CARGO_NODE": - return NetworkComponent.CONNECTOR; - case "CARGO_NODE_INPUT": - case "CARGO_NODE_OUTPUT": - case "CARGO_NODE_OUTPUT_ADVANCED": - case "CT_IMPORT_BUS": - case "CT_EXPORT_BUS": - case "CHEST_TERMINAL": - return NetworkComponent.TERMINUS; - default: - return null; - } + return switch (id) { + case "CARGO_MANAGER" -> NetworkComponent.REGULATOR; + case "CARGO_NODE" -> NetworkComponent.CONNECTOR; + case "CARGO_NODE_INPUT", + "CARGO_NODE_OUTPUT", + "CARGO_NODE_OUTPUT_ADVANCED" -> NetworkComponent.TERMINUS; + default -> null; + }; } @Override @@ -120,32 +111,15 @@ public void onClassificationChange(Location l, NetworkComponent from, NetworkCom if (from == NetworkComponent.TERMINUS) { inputNodes.remove(l); outputNodes.remove(l); - terminals.remove(l); - imports.remove(l); - exports.remove(l); } if (to == NetworkComponent.TERMINUS) { String id = BlockStorage.checkID(l); switch (id) { - case "CARGO_NODE_INPUT": - inputNodes.add(l); - break; - case "CARGO_NODE_OUTPUT": - case "CARGO_NODE_OUTPUT_ADVANCED": - outputNodes.add(l); - break; - case "CHEST_TERMINAL": - terminals.add(l); - break; - case "CT_IMPORT_BUS": - imports.add(l); - break; - case "CT_EXPORT_BUS": - exports.add(l); - break; - default: - break; + case "CARGO_NODE_INPUT" -> inputNodes.add(l); + case "CARGO_NODE_OUTPUT", + "CARGO_NODE_OUTPUT_ADVANCED" -> outputNodes.add(l); + default -> {} } } } @@ -173,34 +147,27 @@ public void tick(@Nonnull Block b) { // Reset the internal threshold, so we can start skipping again tickDelayThreshold = 0; - // Chest Terminal Stuff - Set chestTerminalInputs = new HashSet<>(); - Set chestTerminalOutputs = new HashSet<>(); - - Map inputs = mapInputNodes(chestTerminalInputs); - Map> outputs = mapOutputNodes(chestTerminalOutputs); + Map inputs = mapInputNodes(); + Map> outputs = mapOutputNodes(); if (BlockStorage.getLocationInfo(b.getLocation(), "visualizer") == null) { display(); } - Slimefun.getProfiler().scheduleEntries((terminals.isEmpty() ? 1 : 2) + inputs.size()); + Slimefun.getProfiler().scheduleEntries(inputs.size() + 1); - CargoNetworkTask runnable = new CargoNetworkTask(this, inputs, outputs, chestTerminalInputs, chestTerminalOutputs); + CargoNetworkTask runnable = new CargoNetworkTask(this, inputs, outputs); Slimefun.runSync(runnable); } } - @Nonnull - private Map mapInputNodes(@Nonnull Set chestTerminalNodes) { + private @Nonnull Map mapInputNodes() { Map inputs = new HashMap<>(); for (Location node : inputNodes) { int frequency = getFrequency(node); - if (frequency == 16) { - chestTerminalNodes.add(node); - } else if (frequency >= 0 && frequency < 16) { + if (frequency >= 0 && frequency < 16) { inputs.put(node, frequency); } } @@ -208,8 +175,7 @@ private Map mapInputNodes(@Nonnull Set chestTermina return inputs; } - @Nonnull - private Map> mapOutputNodes(@Nonnull Set chestTerminalOutputs) { + private @Nonnull Map> mapOutputNodes() { Map> output = new HashMap<>(); List list = new LinkedList<>(); @@ -218,11 +184,6 @@ private Map> mapOutputNodes(@Nonnull Set chest for (Location node : outputNodes) { int frequency = getFrequency(node); - if (frequency == 16) { - chestTerminalOutputs.add(node); - continue; - } - if (frequency != lastFrequency && lastFrequency != -1) { output.merge(lastFrequency, list, (prev, next) -> { prev.addAll(next); diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/cargo/CargoNetworkTask.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/cargo/CargoNetworkTask.java index edece7dcbd..35b5fe225f 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/cargo/CargoNetworkTask.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/cargo/CargoNetworkTask.java @@ -9,7 +9,6 @@ import java.util.Map; import java.util.Objects; import java.util.Optional; -import java.util.Set; import java.util.logging.Level; import javax.annotation.Nullable; @@ -55,18 +54,13 @@ class CargoNetworkTask implements Runnable { private final Map inputs; private final Map> outputs; - private final Set chestTerminalInputs; - private final Set chestTerminalOutputs; - @ParametersAreNonnullByDefault - CargoNetworkTask(CargoNet network, Map inputs, Map> outputs, Set chestTerminalInputs, Set chestTerminalOutputs) { + CargoNetworkTask(CargoNet network, Map inputs, Map> outputs) { this.network = network; this.manager = Slimefun.getNetworkManager(); this.inputs = inputs; this.outputs = outputs; - this.chestTerminalInputs = chestTerminalInputs; - this.chestTerminalOutputs = chestTerminalOutputs; } @Override @@ -74,11 +68,6 @@ public void run() { long timestamp = System.nanoTime(); try { - // Chest Terminal Code - if (Slimefun.getIntegrations().isChestTerminalInstalled()) { - network.handleItemRequests(inventories, chestTerminalInputs, chestTerminalOutputs); - } - /** * All operations happen here: Everything gets iterated from the Input Nodes. * (Apart from ChestTerminal Buses) @@ -94,12 +83,6 @@ public void run() { // This will prevent this timings from showing up for the Cargo Manager timestamp += Slimefun.getProfiler().closeEntry(entry.getKey(), inputNode, nodeTimestamp); } - - // Chest Terminal Code - if (Slimefun.getIntegrations().isChestTerminalInstalled()) { - // This will deduct any CT timings and attribute them towards the actual terminal - timestamp += network.updateTerminals(chestTerminalInputs); - } } catch (Exception | LinkageError x) { Slimefun.logger().log(Level.SEVERE, x, () -> "An Exception was caught while ticking a Cargo network @ " + new BlockPosition(network.getRegulator())); } @@ -168,15 +151,19 @@ private ItemStack distributeItem(ItemStack stack, Location inputNode, List destinations; if (roundrobin) { + // The current round-robin index of the (unsorted) outputNodes list, + // or the index at which to start searching for valid output nodes + index = network.roundRobin.getOrDefault(inputNode, 0); // Use an ArrayDeque to perform round-robin sorting // Since the impl for roundRobinSort just does Deque.addLast(Deque#removeFirst) // An ArrayDequeue is preferable as opposed to a LinkedList: // - The number of elements does not change. // - ArrayDequeue has better iterative performance Deque tempDestinations = new ArrayDeque<>(outputNodes); - roundRobinSort(inputNode, tempDestinations); + roundRobinSort(index, tempDestinations); destinations = tempDestinations; } else { // Using an ArrayList here since we won't need to sort the destinations @@ -192,9 +179,14 @@ private ItemStack distributeItem(ItemStack stack, Location inputNode, List outputNodes) { - int index = network.roundRobin.getOrDefault(inputNode, 0); - + private void roundRobinSort(int index, Deque outputNodes) { if (index < outputNodes.size()) { // Not ideal but actually not bad performance-wise over more elegant alternatives for (int i = 0; i < index; i++) { Location temp = outputNodes.removeFirst(); outputNodes.add(temp); } - - index++; - } else { - index = 1; } - - network.roundRobin.put(inputNode, index); } } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/cargo/CargoUtils.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/cargo/CargoUtils.java index c5b3b9b52f..ff8da96d0a 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/cargo/CargoUtils.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/cargo/CargoUtils.java @@ -17,6 +17,8 @@ import org.bukkit.inventory.ItemStack; import io.github.bakedlibs.dough.inventory.InvUtils; +import io.github.thebusybiscuit.slimefun4.core.debug.Debug; +import io.github.thebusybiscuit.slimefun4.core.debug.TestCase; import io.github.thebusybiscuit.slimefun4.implementation.Slimefun; import io.github.thebusybiscuit.slimefun4.utils.SlimefunUtils; import io.github.thebusybiscuit.slimefun4.utils.itemstack.ItemStackWrapper; @@ -67,22 +69,7 @@ static boolean hasInventory(@Nullable Block block) { } Material type = block.getType(); - - switch (type) { - case CHEST: - case TRAPPED_CHEST: - case FURNACE: - case DISPENSER: - case DROPPER: - case HOPPER: - case BREWING_STAND: - case BARREL: - case BLAST_FURNACE: - case SMOKER: - return true; - default: - return SlimefunTag.SHULKER_BOXES.isTagged(type); - } + return SlimefunTag.CARGO_SUPPORTED_STORAGE_BLOCKS.isTagged(type); } @Nonnull @@ -143,8 +130,8 @@ static ItemStack withdraw(AbstractItemNetwork network, Map BlockState state = PaperLib.getBlockState(target, false).getState(); - if (state instanceof InventoryHolder) { - inventory = ((InventoryHolder) state).getInventory(); + if (state instanceof InventoryHolder inventoryHolder) { + inventory = inventoryHolder.getInventory(); inventories.put(target.getLocation(), inventory); return withdrawFromVanillaInventory(network, node, template, inventory); } @@ -228,8 +215,8 @@ static ItemStackAndInteger withdraw(AbstractItemNetwork network, Map inventories, Block node, Block target, boolean smartFill, ItemStack stack, ItemStackWrapper wrapper) { + Debug.log(TestCase.CARGO_INPUT_TESTING, "CargoUtils#insert"); if (!matchesFilter(network, node, stack)) { return stack; } @@ -275,8 +263,8 @@ static ItemStack insert(AbstractItemNetwork network, Map in BlockState state = PaperLib.getBlockState(target, false).getState(); - if (state instanceof InventoryHolder) { - inventory = ((InventoryHolder) state).getInventory(); + if (state instanceof InventoryHolder inventoryHolder) { + inventory = inventoryHolder.getInventory(); inventories.put(target.getLocation(), inventory); return insertIntoVanillaInventory(stack, wrapper, smartFill, inventory); } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/cargo/ItemFilter.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/cargo/ItemFilter.java index dc0bc529fb..1de4b4a4a4 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/cargo/ItemFilter.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/cargo/ItemFilter.java @@ -12,6 +12,8 @@ import org.bukkit.inventory.ItemStack; import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItem; +import io.github.thebusybiscuit.slimefun4.core.debug.Debug; +import io.github.thebusybiscuit.slimefun4.core.debug.TestCase; import io.github.thebusybiscuit.slimefun4.implementation.items.cargo.CargoNode; import io.github.thebusybiscuit.slimefun4.utils.SlimefunUtils; import io.github.thebusybiscuit.slimefun4.utils.itemstack.ItemStackWrapper; @@ -159,7 +161,8 @@ public void markDirty() { @Override public boolean test(@Nonnull ItemStack item) { - /** + Debug.log(TestCase.CARGO_INPUT_TESTING, "ItemFilter#test({})", item); + /* * An empty Filter does not need to be iterated over. * We can just return our default value in this scenario. */ @@ -201,7 +204,7 @@ public boolean test(@Nonnull ItemStack item) { /* * The filter has found a match, we can return the opposite * of our default value. If we exclude items, this is where we - * would return false. Otherwise we return true. + * would return false. Otherwise, we return true. */ return !rejectOnMatch; } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/cargo/ItemRequest.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/cargo/ItemRequest.java deleted file mode 100644 index 2e015f5ba7..0000000000 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/cargo/ItemRequest.java +++ /dev/null @@ -1,55 +0,0 @@ -package io.github.thebusybiscuit.slimefun4.core.networks.cargo; - -import java.util.Objects; - -import org.bukkit.Location; -import org.bukkit.inventory.ItemStack; - -import me.mrCookieSlime.Slimefun.api.item_transport.ItemTransportFlow; - -class ItemRequest { - - private final ItemStack item; - private final ItemTransportFlow flow; - private final Location terminal; - private final int slot; - - ItemRequest(Location terminal, int slot, ItemStack item, ItemTransportFlow flow) { - this.terminal = terminal; - this.item = item; - this.slot = slot; - this.flow = flow; - } - - public Location getTerminal() { - return terminal; - } - - public ItemStack getItem() { - return item; - } - - public ItemTransportFlow getDirection() { - return flow; - } - - public int getSlot() { - return slot; - } - - @Override - public int hashCode() { - return Objects.hash(item, slot, flow, terminal); - } - - @Override - public boolean equals(Object obj) { - if (obj instanceof ItemRequest) { - ItemRequest request = (ItemRequest) obj; - return Objects.equals(item, request.item) && Objects.equals(terminal, request.terminal) && slot == request.slot && flow == request.flow; - } else { - return false; - } - } - -} diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/energy/EnergyNet.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/energy/EnergyNet.java index da488e7d7c..cb7c2c910c 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/energy/EnergyNet.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/energy/EnergyNet.java @@ -1,5 +1,6 @@ package io.github.thebusybiscuit.slimefun4.core.networks.energy; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Map; @@ -22,8 +23,8 @@ import io.github.thebusybiscuit.slimefun4.core.attributes.EnergyNetComponent; import io.github.thebusybiscuit.slimefun4.core.attributes.EnergyNetProvider; import io.github.thebusybiscuit.slimefun4.core.attributes.HologramOwner; -import io.github.thebusybiscuit.slimefun4.implementation.SlimefunItems; import io.github.thebusybiscuit.slimefun4.implementation.Slimefun; +import io.github.thebusybiscuit.slimefun4.implementation.SlimefunItems; import io.github.thebusybiscuit.slimefun4.utils.NumberUtils; import me.mrCookieSlime.CSCoreLibPlugin.Configuration.Config; @@ -31,7 +32,7 @@ /** * The {@link EnergyNet} is an implementation of {@link Network} that deals with - * electrical energy being send from and to nodes. + * electrical energy being sent from and to nodes. * * @author meiamsome * @author TheBusyBiscuit @@ -58,9 +59,36 @@ protected EnergyNet(@Nonnull Location l) { public int getRange() { return RANGE; } + + /** + * This creates an immutable {@link Map} of {@link EnergyNetProvider}s within this {@link EnergyNet} instance. + * + * @return An immutable {@link Map} of generators + */ + public @Nonnull Map getGenerators() { + return Collections.unmodifiableMap(generators); + } + + /** + * This creates an immutable {@link Map} of {@link EnergyNetComponentType#CAPACITOR} {@link EnergyNetComponent}s within this {@link EnergyNet} instance. + * + * @return An immutable {@link Map} of capacitors + */ + public @Nonnull Map getCapacitors() { + return Collections.unmodifiableMap(capacitors); + } + + /** + * This creates an immutable {@link Map} of {@link EnergyNetComponentType#CONSUMER} {@link EnergyNetComponent}s within this {@link EnergyNet} instance. + * + * @return An immutable {@link Map} of consumers + */ + public @Nonnull Map getConsumers() { + return Collections.unmodifiableMap(consumers); + } @Override - public String getId() { + public @Nonnull String getId() { return "ENERGY_NETWORK"; } @@ -75,16 +103,13 @@ public NetworkComponent classifyLocation(@Nonnull Location l) { if (component == null) { return null; } else { - switch (component.getEnergyComponentType()) { - case CONNECTOR: - case CAPACITOR: - return NetworkComponent.CONNECTOR; - case CONSUMER: - case GENERATOR: - return NetworkComponent.TERMINUS; - default: - return null; - } + return switch (component.getEnergyComponentType()) { + case CONNECTOR, + CAPACITOR -> NetworkComponent.CONNECTOR; + case CONSUMER, + GENERATOR -> NetworkComponent.TERMINUS; + default -> null; + }; } } @@ -106,10 +131,10 @@ public void onClassificationChange(Location l, NetworkComponent from, NetworkCom consumers.put(l, component); break; case GENERATOR: - if (component instanceof EnergyNetProvider) { - generators.put(l, (EnergyNetProvider) component); - } else if (component instanceof SlimefunItem) { - ((SlimefunItem) component).warn("This Item is marked as a GENERATOR but does not implement the interface EnergyNetProvider!"); + if (component instanceof EnergyNetProvider provider) { + generators.put(l, provider); + } else if (component instanceof SlimefunItem item) { + item.warn("This Item is marked as a GENERATOR but does not implement the interface EnergyNetProvider!"); } break; default: @@ -132,7 +157,9 @@ public void tick(@Nonnull Block b) { if (connectorNodes.isEmpty() && terminusNodes.isEmpty()) { updateHologram(b, "&4No Energy Network found"); } else { - int supply = tickAllGenerators(timestamp::getAndAdd) + tickAllCapacitors(); + int generatorsSupply = tickAllGenerators(timestamp::getAndAdd); + int capacitorsSupply = tickAllCapacitors(); + int supply = NumberUtils.flowSafeAddition(generatorsSupply, capacitorsSupply); int remainingEnergy = supply; int demand = 0; @@ -144,7 +171,7 @@ public void tick(@Nonnull Block b) { if (charge < capacity) { int availableSpace = capacity - charge; - demand += availableSpace; + demand = NumberUtils.flowSafeAddition(demand, availableSpace); if (remainingEnergy > 0) { if (remainingEnergy > availableSpace) { @@ -220,7 +247,7 @@ private int tickAllGenerators(@Nonnull LongConsumer timings) { int energy = provider.getGeneratedOutput(loc, data); if (provider.isChargeable()) { - energy += provider.getCharge(loc, data); + energy = NumberUtils.flowSafeAddition(energy, provider.getCharge(loc, data)); } if (provider.willExplode(loc, data)) { @@ -232,7 +259,7 @@ private int tickAllGenerators(@Nonnull LongConsumer timings) { loc.getWorld().createExplosion(loc, 0F, false); }); } else { - supply += energy; + supply = NumberUtils.flowSafeAddition(supply, energy); } } catch (Exception | LinkageError throwable) { explodedBlocks.add(loc); @@ -255,7 +282,7 @@ private int tickAllCapacitors() { int supply = 0; for (Map.Entry entry : capacitors.entrySet()) { - supply += entry.getValue().getCharge(entry.getKey()); + supply = NumberUtils.flowSafeAddition(supply, entry.getValue().getCharge(entry.getKey())); } return supply; @@ -275,8 +302,8 @@ private void updateHologram(@Nonnull Block b, double supply, double demand) { private static EnergyNetComponent getComponent(@Nonnull Location l) { SlimefunItem item = BlockStorage.check(l); - if (item instanceof EnergyNetComponent) { - return ((EnergyNetComponent) item); + if (item instanceof EnergyNetComponent component) { + return component; } return null; diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/BlockDataService.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/BlockDataService.java index 2d952c642d..69c9579286 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/BlockDataService.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/BlockDataService.java @@ -6,6 +6,7 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; +import io.github.thebusybiscuit.slimefun4.utils.tags.SlimefunTag; import org.apache.commons.lang.Validate; import org.bukkit.Bukkit; import org.bukkit.Keyed; @@ -72,9 +73,9 @@ public void setBlockData(@Nonnull Block b, @Nonnull String value) { */ BlockState state = b.getState(); - if (state instanceof TileState) { + if (state instanceof TileState tileState) { try { - PersistentDataContainer container = ((TileState) state).getPersistentDataContainer(); + PersistentDataContainer container = tileState.getPersistentDataContainer(); container.set(namespacedKey, PersistentDataType.STRING, value); state.update(); } catch (Exception x) { @@ -111,8 +112,8 @@ public Optional getBlockData(@Nonnull Block b) { @Nullable private PersistentDataContainer getPersistentDataContainer(@Nonnull BlockState state) { - if (state instanceof TileState) { - return ((TileState) state).getPersistentDataContainer(); + if (state instanceof TileState tileState) { + return tileState.getPersistentDataContainer(); } else { return null; } @@ -137,31 +138,7 @@ public boolean isTileEntity(@Nullable Material type) { return false; } - switch (type) { - case PLAYER_HEAD: - case PLAYER_WALL_HEAD: - case CHEST: - case DISPENSER: - case BREWING_STAND: - case DROPPER: - case FURNACE: - case BLAST_FURNACE: - case HOPPER: - case LECTERN: - case JUKEBOX: - case ENDER_CHEST: - case ENCHANTING_TABLE: - case DAYLIGHT_DETECTOR: - case SMOKER: - case BARREL: - case SPAWNER: - case BEACON: - // All of the above Materials are Tile Entities - return true; - default: - // Otherwise we assume they're not Tile Entities - return false; - } + return SlimefunTag.TILE_ENTITIES.isTagged(type); } } \ No newline at end of file diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/CustomItemDataService.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/CustomItemDataService.java index fa5d258627..e9617b7eec 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/CustomItemDataService.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/CustomItemDataService.java @@ -99,8 +99,7 @@ public void setItemData(@Nonnull ItemMeta meta, @Nonnull String id) { * * @return An {@link Optional} describing the result */ - @Nonnull - public Optional getItemData(@Nullable ItemStack item) { + public @Nonnull Optional getItemData(@Nullable ItemStack item) { if (item == null || item.getType() == Material.AIR || !item.hasItemMeta()) { return Optional.empty(); } @@ -117,12 +116,46 @@ public Optional getItemData(@Nullable ItemStack item) { * * @return An {@link Optional} describing the result */ - @Nonnull - public Optional getItemData(@Nonnull ItemMeta meta) { + public @Nonnull Optional getItemData(@Nonnull ItemMeta meta) { Validate.notNull(meta, "Cannot read data from null!"); PersistentDataContainer container = meta.getPersistentDataContainer(); return Optional.ofNullable(container.get(namespacedKey, PersistentDataType.STRING)); } + /** + * This method compares the custom data stored on two {@link ItemMeta} objects. + * This method will only return {@literal true} if both {@link ItemMeta}s contain + * custom data and if both of their data values are equal. + * + * @param meta1 + * The first {@link ItemMeta} + * @param meta2 + * The second {@link ItemMeta} + * + * @return Whether both metas have data on them and its the same. + */ + public boolean hasEqualItemData(@Nonnull ItemMeta meta1, @Nonnull ItemMeta meta2) { + Validate.notNull(meta1, "Cannot read data from null (first arg)"); + Validate.notNull(meta2, "Cannot read data from null (second arg)"); + + Optional data1 = getItemData(meta1); + + // Check if the first data is present + if (data1.isPresent()) { + // Only retrieve the second data where necessary. + Optional data2 = getItemData(meta2); + + /* + * Check if both are present and equal. + * Optional#equals(...) compares their values, so no need + * to call Optional#get() here. + */ + return data2.isPresent() && data1.equals(data2); + } else { + // No value present, we can return immediately. + return false; + } + } + } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/LocalizationService.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/LocalizationService.java index 75c9e56913..39b533335a 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/LocalizationService.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/LocalizationService.java @@ -10,6 +10,7 @@ import java.util.Map; import java.util.Set; import java.util.logging.Level; +import java.util.stream.Collectors; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -18,6 +19,7 @@ import org.apache.commons.lang.Validate; import org.bukkit.NamespacedKey; import org.bukkit.Server; +import org.bukkit.configuration.InvalidConfigurationException; import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.entity.Player; @@ -29,6 +31,7 @@ import io.github.thebusybiscuit.slimefun4.core.services.localization.SlimefunLocalization; import io.github.thebusybiscuit.slimefun4.implementation.Slimefun; import io.github.thebusybiscuit.slimefun4.utils.NumberUtils; +import io.github.thebusybiscuit.slimefun4.utils.PatternUtils; /** * As the name suggests, this Service is responsible for Localization. @@ -97,7 +100,7 @@ public boolean isEnabled() { } @Override - public String getPrefix() { + public String getChatPrefix() { return prefix; } @@ -126,7 +129,7 @@ public boolean hasLanguage(@Nonnull String id) { // Checks if our jar files contains a messages.yml file for that language String file = LanguageFile.MESSAGES.getFilePath(id); - return !streamConfigFile(file, null).getKeys(false).isEmpty(); + return !getConfigurationFromStream(file, null).getKeys(false).isEmpty(); } /** @@ -196,7 +199,7 @@ private void setLanguage(@Nonnull String language, boolean reset) { @ParametersAreNonnullByDefault private void copyToDefaultLanguage(String language, LanguageFile file) { - FileConfiguration config = streamConfigFile(file.getFilePath(language), null); + FileConfiguration config = getConfigurationFromStream(file.getFilePath(language), null); defaultLanguage.setFile(file, config); } @@ -210,7 +213,7 @@ protected void addLanguage(@Nonnull String id, @Nonnull String texture) { for (LanguageFile file : LanguageFile.values()) { FileConfiguration defaults = file == LanguageFile.MESSAGES ? getConfig().getConfiguration() : null; - FileConfiguration config = streamConfigFile(file.getFilePath(language), defaults); + FileConfiguration config = getConfigurationFromStream(file.getFilePath(language), defaults); language.setFile(file, config); } @@ -249,8 +252,7 @@ public double calculateProgress(@Nonnull Language lang) { return Math.min(NumberUtils.reparseDouble(100.0 * (matches / (double) defaultKeys.size())), 100.0); } - @Nonnull - private FileConfiguration streamConfigFile(@Nonnull String file, @Nullable FileConfiguration defaults) { + private @Nonnull FileConfiguration getConfigurationFromStream(@Nonnull String file, @Nullable FileConfiguration defaults) { InputStream inputStream = plugin.getClass().getResourceAsStream(file); if (inputStream == null) { @@ -258,15 +260,24 @@ private FileConfiguration streamConfigFile(@Nonnull String file, @Nullable FileC } try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) { - FileConfiguration config = YamlConfiguration.loadConfiguration(reader); - - if (defaults != null) { - config.setDefaults(defaults); + String content = reader.lines().collect(Collectors.joining("\n")); + YamlConfiguration config = new YamlConfiguration(); + + /* + * Fixes #3400 - Only attempt to load yaml files that contain data. + * This is not a perfect fix but should be sufficient to circumvent this issue. + */ + if (PatternUtils.YAML_ENTRY.matcher(content).find()) { + config.loadFromString(content); + + if (defaults != null) { + config.setDefaults(defaults); + } } return config; - } catch (IOException e) { - Slimefun.logger().log(Level.SEVERE, e, () -> "Failed to load language file into memory: \"" + file + "\""); + } catch (IOException | InvalidConfigurationException e) { + Slimefun.logger().log(Level.WARNING, e, () -> "Failed to load language file into memory: \"" + file + "\""); return new YamlConfiguration(); } } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/MetricsService.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/MetricsService.java index b783b0bf3e..e186004c4e 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/MetricsService.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/MetricsService.java @@ -2,6 +2,7 @@ import java.io.File; import java.io.IOException; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.URL; import java.net.URLClassLoader; @@ -44,7 +45,7 @@ public class MetricsService { * The Name of our repository - Version 2 of this repo (due to big breaking changes) */ private static final String REPO_NAME = "MetricsModule2"; - + /** * The name of the metrics jar file. */ @@ -140,6 +141,8 @@ public void start() { try { start.invoke(null); plugin.getLogger().info("Metrics build #" + version + " started."); + } catch (InvocationTargetException e) { + plugin.getLogger().log(Level.WARNING, "An exception was thrown while starting the metrics module", e.getCause()); } catch (Exception | LinkageError e) { plugin.getLogger().log(Level.WARNING, "Failed to start metrics.", e); } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/MinecraftRecipeService.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/MinecraftRecipeService.java index f959a7493c..c9137ed242 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/MinecraftRecipeService.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/MinecraftRecipeService.java @@ -99,8 +99,7 @@ public void subscribe(@Nonnull Consumer subscription) { * * @return An {@link Optional} describing the furnace output of the given {@link ItemStack} */ - @Nonnull - public Optional getFurnaceOutput(@Nullable ItemStack input) { + public @Nonnull Optional getFurnaceOutput(@Nullable ItemStack input) { if (snapshot == null || input == null) { return Optional.empty(); } @@ -132,18 +131,17 @@ public boolean isSmeltable(@Nullable ItemStack input) { * * @return An Array of {@link RecipeChoice} representing the shape of this {@link Recipe} */ - @Nonnull - public RecipeChoice[] getRecipeShape(@Nonnull Recipe recipe) { + public @Nonnull RecipeChoice[] getRecipeShape(@Nonnull Recipe recipe) { Validate.notNull(recipe, "Recipe must not be null!"); - if (recipe instanceof ShapedRecipe) { + if (recipe instanceof ShapedRecipe shapedRecipe) { List choices = new LinkedList<>(); - for (String row : ((ShapedRecipe) recipe).getShape()) { + for (String row : shapedRecipe.getShape()) { int columns = row.toCharArray().length; for (char key : row.toCharArray()) { - choices.add(((ShapedRecipe) recipe).getChoiceMap().get(key)); + choices.add(shapedRecipe.getChoiceMap().get(key)); } while (columns < 3) { @@ -167,8 +165,7 @@ public RecipeChoice[] getRecipeShape(@Nonnull Recipe recipe) { * * @return An array of {@link Recipe Recipes} to craft the given {@link ItemStack} */ - @Nonnull - public Recipe[] getRecipesFor(@Nullable ItemStack item) { + public @Nonnull Recipe[] getRecipesFor(@Nullable ItemStack item) { if (snapshot == null || item == null) { return new Recipe[0]; } else { @@ -187,8 +184,7 @@ public Recipe[] getRecipesFor(@Nullable ItemStack item) { * * @return The corresponding {@link Recipe} or null */ - @Nullable - public Recipe getRecipe(@Nonnull NamespacedKey key) { + public @Nullable Recipe getRecipe(@Nonnull NamespacedKey key) { Validate.notNull(key, "The NamespacedKey should not be null"); if (snapshot != null) { diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/PermissionsService.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/PermissionsService.java index a9f95dc09d..de92df6d87 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/PermissionsService.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/PermissionsService.java @@ -1,6 +1,7 @@ package io.github.thebusybiscuit.slimefun4.core.services; import java.io.File; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -149,8 +150,7 @@ public void save() { * * @return The configured lore to display */ - @Nonnull - public List getLore(@Nonnull SlimefunItem item) { + public @Nonnull List getLore(@Nonnull SlimefunItem item) { return config.getStringList(item.getId() + ".lore"); } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/UpdaterService.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/UpdaterService.java index a08e65dcfe..bea541fc28 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/UpdaterService.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/UpdaterService.java @@ -9,12 +9,13 @@ import io.github.bakedlibs.dough.config.Config; import io.github.bakedlibs.dough.updater.GitHubBuildsUpdater; +import io.github.bakedlibs.dough.updater.PluginUpdater; import io.github.bakedlibs.dough.versions.PrefixedVersion; import io.github.thebusybiscuit.slimefun4.api.SlimefunBranch; import io.github.thebusybiscuit.slimefun4.implementation.Slimefun; /** - * This Class represents our {@link Updater} Service. + * This Class represents our {@link PluginUpdater} Service. * If enabled, it will automatically connect to https://thebusybiscuit.github.io/builds/ * to check for updates and to download them automatically. * @@ -29,9 +30,9 @@ public class UpdaterService { private final Slimefun plugin; /** - * Our {@link Updater} implementation. + * Our {@link PluginUpdater} implementation. */ - private final GitHubBuildsUpdater updater; + private final PluginUpdater updater; /** * The {@link SlimefunBranch} we are currently on. @@ -126,11 +127,11 @@ public void start() { } /** - * This returns whether the {@link Updater} is enabled or not. + * This returns whether the {@link PluginUpdater} is enabled or not. * This includes the {@link Config} setting but also whether or not we are running an * official or unofficial build. * - * @return Whether the {@link Updater} is enabled + * @return Whether the {@link PluginUpdater} is enabled */ public boolean isEnabled() { return Slimefun.getCfg().getBoolean("options.auto-update") && updater != null; diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/github/ContributionsConnector.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/github/ContributionsConnector.java index 8f1e52542d..7dd4970552 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/github/ContributionsConnector.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/github/ContributionsConnector.java @@ -77,6 +77,7 @@ private void loadConfiguration() { aliases.put("NCBPFluffyBear", "FluffyBear_"); aliases.put("martinbrom", "OneTime97"); aliases.put("LilBC", "Lil_BC"); + aliases.put("st392", "BlueWood"); } /** diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/github/GitHubService.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/github/GitHubService.java index 56e9780286..17d158d27f 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/github/GitHubService.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/github/GitHubService.java @@ -57,7 +57,6 @@ public GitHubService(@Nonnull String repository) { connectors = new HashSet<>(); contributors = new ConcurrentHashMap<>(); - loadConnectors(false); } /** @@ -68,6 +67,8 @@ public GitHubService(@Nonnull String repository) { * Our instance of {@link Slimefun} */ public void start(@Nonnull Slimefun plugin) { + loadConnectors(false); + long period = TimeUnit.HOURS.toMillis(1); GitHubTask task = new GitHubTask(this); diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/github/GitHubTask.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/github/GitHubTask.java index fb3647285a..37a45e4315 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/github/GitHubTask.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/github/GitHubTask.java @@ -82,7 +82,7 @@ private void grabTextures() { } for (GitHubConnector connector : gitHubService.getConnectors()) { - if (connector instanceof ContributionsConnector && !((ContributionsConnector) connector).hasFinished()) { + if (connector instanceof ContributionsConnector contributionsConnector && !contributionsConnector.hasFinished()) { return; } } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/github/TranslatorsReader.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/github/TranslatorsReader.java index 80da4876ec..c6683186c0 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/github/TranslatorsReader.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/github/TranslatorsReader.java @@ -13,9 +13,9 @@ import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; -import com.google.gson.JsonParser; import io.github.thebusybiscuit.slimefun4.implementation.Slimefun; +import io.github.thebusybiscuit.slimefun4.utils.JsonUtils; /** * This class reads all translators of this project. @@ -39,8 +39,7 @@ public void load() { InputStream inputStream = Slimefun.class.getResourceAsStream("/languages/translators.json"); try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) { - JsonParser parser = new JsonParser(); - JsonElement element = parser.parse(reader.lines().collect(Collectors.joining(""))); + JsonElement element = JsonUtils.parseString(reader.lines().collect(Collectors.joining(""))); JsonObject json = element.getAsJsonObject(); for (Map.Entry entry : json.entrySet()) { diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/holograms/Hologram.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/holograms/Hologram.java index 3804d5b00f..b69e71d534 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/holograms/Hologram.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/holograms/Hologram.java @@ -62,9 +62,9 @@ class Hologram { ArmorStand getArmorStand() { Entity n = Bukkit.getEntity(uniqueId); - if (n instanceof ArmorStand && n.isValid()) { + if (n instanceof ArmorStand armorStand && n.isValid()) { this.lastAccess = System.currentTimeMillis(); - return (ArmorStand) n; + return armorStand; } else { this.lastAccess = 0; return null; diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/holograms/HologramsService.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/holograms/HologramsService.java index 6241737a54..2673dd9b91 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/holograms/HologramsService.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/holograms/HologramsService.java @@ -191,11 +191,9 @@ private boolean hasHologramData(PersistentDataContainer container, BlockPosition * @return Whether this could be a hologram */ private boolean isHologram(@Nonnull Entity n) { - if (n instanceof ArmorStand) { - ArmorStand armorstand = (ArmorStand) n; - + if (n instanceof ArmorStand armorStand) { // The absolute minimum requirements to count as a hologram - return !armorstand.isVisible() && armorstand.isSilent() && !armorstand.hasGravity(); + return !armorStand.isVisible() && armorStand.isSilent() && !armorStand.hasGravity(); } else { return false; } @@ -216,22 +214,20 @@ private boolean isHologram(@Nonnull Entity n) { */ @Nullable private Hologram getAsHologram(@Nonnull BlockPosition position, @Nonnull Entity entity, @Nonnull PersistentDataContainer container) { - if (entity instanceof ArmorStand) { - ArmorStand armorstand = (ArmorStand) entity; - - armorstand.setVisible(false); - armorstand.setInvulnerable(true); - armorstand.setSilent(true); - armorstand.setMarker(true); - armorstand.setAI(false); - armorstand.setGravity(false); - armorstand.setRemoveWhenFarAway(false); + if (entity instanceof ArmorStand armorStand) { + armorStand.setVisible(false); + armorStand.setInvulnerable(true); + armorStand.setSilent(true); + armorStand.setMarker(true); + armorStand.setAI(false); + armorStand.setGravity(false); + armorStand.setRemoveWhenFarAway(false); // Set a persistent tag to re-identify the correct hologram later container.set(persistentDataKey, PersistentDataType.LONG, position.getPosition()); // Store in cache for faster access - Hologram hologram = new Hologram(armorstand.getUniqueId()); + Hologram hologram = new Hologram(armorStand.getUniqueId()); cache.put(position, hologram); return hologram; diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/localization/Language.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/localization/Language.java index eeaad65c76..0db0125e2e 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/localization/Language.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/localization/Language.java @@ -59,8 +59,7 @@ public Language(@Nonnull String id, @Nonnull String hash) { * * @return The identifier of this {@link Language} */ - @Nonnull - public String getId() { + public @Nonnull String getId() { return id; } @@ -101,8 +100,7 @@ public void setFile(@Nonnull LanguageFile file, @Nonnull FileConfiguration confi * * @return The {@link ItemStack} used to display this {@link Language} */ - @Nonnull - public ItemStack getItem() { + public @Nonnull ItemStack getItem() { return item; } @@ -114,8 +112,7 @@ public ItemStack getItem() { * The {@link Player} to localize the name for * @return The localized name of this {@link Language} */ - @Nonnull - public String getName(@Nonnull Player p) { + public @Nonnull String getName(@Nonnull Player p) { return Slimefun.getLocalization().getMessage(p, "languages." + id); } @@ -136,7 +133,11 @@ public String toString() { @Nonnull FileConfiguration[] getFiles() { - return Arrays.stream(LanguageFile.valuesCached).map(this::getFile).toArray(FileConfiguration[]::new); + // @formatter:off + return Arrays.stream(LanguageFile.valuesCached) + .map(this::getFile) + .toArray(FileConfiguration[]::new); + // @formatter:on } } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/localization/LanguagePreset.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/localization/LanguagePreset.java index 56b969fb7a..6b5e4bc09d 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/localization/LanguagePreset.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/localization/LanguagePreset.java @@ -17,56 +17,75 @@ */ public enum LanguagePreset { - ENGLISH("en", true, "a1701f21835a898b20759fb30a583a38b994abf60d3912ab4ce9f2311e74f72"), - GERMAN("de", true, "5e7899b4806858697e283f084d9173fe487886453774626b24bd8cfecc77b3f"), - FRENCH("fr", true, "51269a067ee37e63635ca1e723b676f139dc2dbddff96bbfef99d8b35c996bc"), - ITALIAN("it", true, "85ce89223fa42fe06ad65d8d44ca412ae899c831309d68924dfe0d142fdbeea4"), - SPANISH("es", true, "32bd4521983309e0ad76c1ee29874287957ec3d96f8d889324da8c887e485ea8"), - RUSSIAN("ru", true, "16eafef980d6117dabe8982ac4b4509887e2c4621f6a8fe5c9b735a83d775ad"), + ENGLISH("en", "a1701f21835a898b20759fb30a583a38b994abf60d3912ab4ce9f2311e74f72"), + GERMAN("de", "5e7899b4806858697e283f084d9173fe487886453774626b24bd8cfecc77b3f"), + FRENCH("fr", "51269a067ee37e63635ca1e723b676f139dc2dbddff96bbfef99d8b35c996bc"), + ITALIAN("it", "85ce89223fa42fe06ad65d8d44ca412ae899c831309d68924dfe0d142fdbeea4"), + SPANISH("es", "32bd4521983309e0ad76c1ee29874287957ec3d96f8d889324da8c887e485ea8"), + RUSSIAN("ru", "16eafef980d6117dabe8982ac4b4509887e2c4621f6a8fe5c9b735a83d775ad"), POLISH("pl", false, "921b2af8d2322282fce4a1aa4f257a52b68e27eb334f4a181fd976bae6d8eb"), - UKRAINIAN("uk", true, "28b9f52e36aa5c7caaa1e7f26ea97e28f635e8eac9aef74cec97f465f5a6b51"), + UKRAINIAN("uk", "28b9f52e36aa5c7caaa1e7f26ea97e28f635e8eac9aef74cec97f465f5a6b51"), BELARUSIAN("be", false, "8c12eaf0d83e97e2bace652d0d23e74908ee766894361271f00c22ea82d25b02"), - SWEDISH("sv", true, "e910904bff9c86f6ed47688e9429c26e8d9c5d5743bd3ebb8e6f5040be192998"), + SWEDISH("sv", "e910904bff9c86f6ed47688e9429c26e8d9c5d5743bd3ebb8e6f5040be192998"), DUTCH("nl", false, "c23cf210edea396f2f5dfbced69848434f93404eefeabf54b23c073b090adf"), DANISH("da", false, "10c23055c392606f7e531daa2676ebe2e348988810c15f15dc5b3733998232"), FINNISH("fi", false, "59f2349729a7ec8d4b1478adfe5ca8af96479e983fbad238ccbd81409b4ed"), NORWEGIAN("no", false, "e0596e165ec3f389b59cfdda93dd6e363e97d9c6456e7c2e123973fa6c5fda"), - CZECH("cs", true, "48152b7334d7ecf335e47a4f35defbd2eb6957fc7bfe94212642d62f46e61e"), + CZECH("cs", "48152b7334d7ecf335e47a4f35defbd2eb6957fc7bfe94212642d62f46e61e"), ROMANIAN("ro", false, "dceb1708d5404ef326103e7b60559c9178f3dce729007ac9a0b498bdebe46107"), - BULGARIAN("bg", true, "19039e1fd88c78d9d7adc5aad5ab16e356be13464934ed9e2b0cef2051c5b534"), + BULGARIAN("bg", "19039e1fd88c78d9d7adc5aad5ab16e356be13464934ed9e2b0cef2051c5b534"), PORTUGUESE_PORTUGAL("pt", false, "ebd51f4693af174e6fe1979233d23a40bb987398e3891665fafd2ba567b5a53a"), - PORTUGUESE_BRAZIL("pt-BR", true, "9a46475d5dcc815f6c5f2859edbb10611f3e861c0eb14f088161b3c0ccb2b0d9"), - HUNGARIAN("hu", true, "4a9c3c4b6c5031332dd2bfece5e31e999f8deff55474065cc86993d7bdcdbd0"), + PORTUGUESE_BRAZIL("pt-BR", "9a46475d5dcc815f6c5f2859edbb10611f3e861c0eb14f088161b3c0ccb2b0d9"), + HUNGARIAN("hu", "4a9c3c4b6c5031332dd2bfece5e31e999f8deff55474065cc86993d7bdcdbd0"), CROATIAN("hr", false, "b050c04ec8cabce71d7103f3e9ef4bb8819f9f365eb335a9139912bc07ed445"), LATVIAN("lv", false, "f62a4938b59447f996b5ed94101df07429d1ad34776d591ffc6fd75b79473c"), GREEK("el", false, "1514de6dd2b7682b1d3ebcd10291ae1f021e3012b5c8beffeb75b1819eb4259d"), - SLOVAK("sk", true, "6c72a8c115a1fb669a25715c4d15f22136ac4c2452784e4894b3d56bc5b0b9"), - VIETNAMESE("vi", true, "8a57b9d7dd04169478cfdb8d0b6fd0b8c82b6566bb28371ee9a7c7c1671ad0bb"), - INDONESIAN("id", true, "5db2678ccaba7934412cb97ee16d416463a392574c5906352f18dea42895ee"), - CHINESE_CHINA("zh-CN", true, "7f9bc035cdc80f1ab5e1198f29f3ad3fdd2b42d9a69aeb64de990681800b98dc"), - CHINESE_TAIWAN("zh-TW", true, "702a4afb2e1e2e3a1894a8b74272f95cfa994ce53907f9ac140bd3c932f9f"), - JAPANESE("ja", true, "d640ae466162a47d3ee33c4076df1cab96f11860f07edb1f0832c525a9e33323"), - KOREAN("ko", true, "fc1be5f12f45e413eda56f3de94e08d90ede8e339c7b1e8f32797390e9a5f"), - HEBREW("he", true, "1ba086a2cc7272cf5ba49c80248546c22e5ef1bab54120e8a8e5d9e75b6a"), - ARABIC("ar", true, "a4be759a9cf7f0a19a7e8e62f23789ad1d21cebae38af9d9541676a3db001572"), - TURKISH("tr", true, "9852b9aba3482348514c1034d0affe73545c9de679ae4647f99562b5e5f47d09"), - PERSIAN("fa", false, "5cd9badf1972583b663b44b1e027255de8f275aa1e89defcf77782ba6fcc652"), + SLOVAK("sk", "6c72a8c115a1fb669a25715c4d15f22136ac4c2452784e4894b3d56bc5b0b9"), + VIETNAMESE("vi", "8a57b9d7dd04169478cfdb8d0b6fd0b8c82b6566bb28371ee9a7c7c1671ad0bb"), + INDONESIAN("id", "5db2678ccaba7934412cb97ee16d416463a392574c5906352f18dea42895ee"), + CHINESE_CHINA("zh-CN", "7f9bc035cdc80f1ab5e1198f29f3ad3fdd2b42d9a69aeb64de990681800b98dc"), + CHINESE_TAIWAN("zh-TW", "702a4afb2e1e2e3a1894a8b74272f95cfa994ce53907f9ac140bd3c932f9f"), + JAPANESE("ja", "d640ae466162a47d3ee33c4076df1cab96f11860f07edb1f0832c525a9e33323"), + KOREAN("ko", "fc1be5f12f45e413eda56f3de94e08d90ede8e339c7b1e8f32797390e9a5f"), + HEBREW("he", TextDirection.RIGHT_TO_LEFT, "1ba086a2cc7272cf5ba49c80248546c22e5ef1bab54120e8a8e5d9e75b6a"), + ARABIC("ar", TextDirection.RIGHT_TO_LEFT, "a4be759a9cf7f0a19a7e8e62f23789ad1d21cebae38af9d9541676a3db001572"), + TURKISH("tr", "9852b9aba3482348514c1034d0affe73545c9de679ae4647f99562b5e5f47d09"), + PERSIAN("fa", false, TextDirection.RIGHT_TO_LEFT, "5cd9badf1972583b663b44b1e027255de8f275aa1e89defcf77782ba6fcc652"), SERBIAN("sr", false, "5b0483a4f0ddf4fbbc977b127b3d294d7a869f995366e3f50f6b05a70f522510"), AFRIKAANS("af", false, "961a1eacc10524d1f45f23b0e487bb2fc33948d9676b418b19a3da0b109d0e3c"), MALAY("ms", false, "754b9041dea6db6db44750f1385a743adf653767b4b8802cad4c585dd3f5be46"), - THAI("th", true, "2a7916e4a852f7e6f3f3de19c7fb57686a37bce834bd54684a7dbef8d53fb"), - MACEDONIAN("mk", false, "a0e0b0b5d87a855466980a101a757bcdb5f77d9f7287889f3efa998ee0472fc0"), - TAGALOG("tl", true, "9306c0c1ce6a9c61bb42a572c49e6d0ed20e0e6b3d122cc64c339cbf78e9e937"); + THAI("th", "2a7916e4a852f7e6f3f3de19c7fb57686a37bce834bd54684a7dbef8d53fb"), + MACEDONIAN("mk", "a0e0b0b5d87a855466980a101a757bcdb5f77d9f7287889f3efa998ee0472fc0"), + TAGALOG("tl", "9306c0c1ce6a9c61bb42a572c49e6d0ed20e0e6b3d122cc64c339cbf78e9e937"), + SINHALA("si-LK", false, "4b26572c48344ee1fc4b2d89fd0866989a3e256ce19ee05384bc3ca76f2409c5"), + LITHUANIAN("lt", false, "de3d829741976d8330d6947d6eea64ee57aed2f26286ff63844e9089367c11"); private final String id; private final boolean releaseReady; private final String textureHash; + private final TextDirection textDirection; @ParametersAreNonnullByDefault - LanguagePreset(String id, boolean releaseReady, String textureHash) { + LanguagePreset(String id, boolean releaseReady, TextDirection direction, String textureHash) { this.id = id; this.releaseReady = releaseReady; this.textureHash = textureHash; + this.textDirection = direction; + } + + @ParametersAreNonnullByDefault + LanguagePreset(String id, boolean releaseReady, String textureHash) { + this(id, releaseReady, TextDirection.LEFT_TO_RIGHT, textureHash); + } + + @ParametersAreNonnullByDefault + LanguagePreset(String id, TextDirection direction, String textureHash) { + this(id, true, direction, textureHash); + } + + @ParametersAreNonnullByDefault + LanguagePreset(String id, String textureHash) { + this(id, true, textureHash); } /** @@ -96,8 +115,17 @@ boolean isReadyForRelease() { * * @return The texture hash of this language */ - public @Nonnull String getTexture() { return textureHash; } + + /** + * This returns the direction of text for + * this language. + * + * @return The direction of text for this language + */ + public @Nonnull TextDirection getTextDirection() { + return textDirection; + } } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/localization/SlimefunLocalization.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/localization/SlimefunLocalization.java index cbc29189bd..893e3baf21 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/localization/SlimefunLocalization.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/localization/SlimefunLocalization.java @@ -64,7 +64,14 @@ protected void save() { defaultConfig.save(); } - public @Nonnull String getPrefix() { + /** + * This returns the chat prefix for our messages. + * Every message (unless explicitly omitted) will have this + * prefix prepended. + * + * @return The chat prefix + */ + public @Nonnull String getChatPrefix() { return getMessage("prefix"); } @@ -339,10 +346,10 @@ public void sendMessage(@Nonnull CommandSender recipient, @Nonnull String key, b Validate.notNull(recipient, "Recipient cannot be null!"); Validate.notNull(key, "Message key cannot be null!"); - String prefix = addPrefix ? getPrefix() : ""; + String prefix = addPrefix ? getChatPrefix() : ""; - if (recipient instanceof Player) { - recipient.sendMessage(ChatColors.color(prefix + getMessage((Player) recipient, key))); + if (recipient instanceof Player player) { + recipient.sendMessage(ChatColors.color(prefix + getMessage(player, key))); } else { recipient.sendMessage(ChatColor.stripColor(ChatColors.color(prefix + getMessage(key)))); } @@ -352,7 +359,7 @@ public void sendActionbarMessage(@Nonnull Player player, @Nonnull String key, bo Validate.notNull(player, "Player cannot be null!"); Validate.notNull(key, "Message key cannot be null!"); - String prefix = addPrefix ? getPrefix() : ""; + String prefix = addPrefix ? getChatPrefix() : ""; String message = ChatColors.color(prefix + getMessage(player, key)); BaseComponent[] components = TextComponent.fromLegacyText(message); @@ -374,20 +381,20 @@ public void sendMessage(CommandSender recipient, String key, boolean addPrefix, return; } - String prefix = addPrefix ? getPrefix() : ""; + String prefix = addPrefix ? getChatPrefix() : ""; - if (recipient instanceof Player) { - recipient.sendMessage(ChatColors.color(prefix + function.apply(getMessage((Player) recipient, key)))); + if (recipient instanceof Player player) { + recipient.sendMessage(ChatColors.color(prefix + function.apply(getMessage(player, key)))); } else { recipient.sendMessage(ChatColor.stripColor(ChatColors.color(prefix + function.apply(getMessage(key))))); } } public void sendMessages(@Nonnull CommandSender recipient, @Nonnull String key) { - String prefix = getPrefix(); + String prefix = getChatPrefix(); - if (recipient instanceof Player) { - for (String translation : getMessages((Player) recipient, key)) { + if (recipient instanceof Player player) { + for (String translation : getMessages(player, key)) { String message = ChatColors.color(prefix + translation); recipient.sendMessage(message); } @@ -401,10 +408,10 @@ public void sendMessages(@Nonnull CommandSender recipient, @Nonnull String key) @ParametersAreNonnullByDefault public void sendMessages(CommandSender recipient, String key, boolean addPrefix, UnaryOperator function) { - String prefix = addPrefix ? getPrefix() : ""; + String prefix = addPrefix ? getChatPrefix() : ""; - if (recipient instanceof Player) { - for (String translation : getMessages((Player) recipient, key)) { + if (recipient instanceof Player player) { + for (String translation : getMessages(player, key)) { String message = ChatColors.color(prefix + function.apply(translation)); recipient.sendMessage(message); } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/localization/TextDirection.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/localization/TextDirection.java new file mode 100644 index 0000000000..1c1d57bb11 --- /dev/null +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/localization/TextDirection.java @@ -0,0 +1,25 @@ +package io.github.thebusybiscuit.slimefun4.core.services.localization; + +/** + * A simple enum to define the direction of text for a given {@link LanguagePreset} + * + * @author TheBusyBiscuit + * + */ +public enum TextDirection { + + /** + * The text flows from left to right. + * This is true for most western languages, + * such as English, Spanish, German, French and more. + */ + LEFT_TO_RIGHT, + + /** + * The text flows from right to left. + * Most common languages that use this direction + * of text are Hebrew and Arabic. + */ + RIGHT_TO_LEFT + +} diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/profiler/PerformanceSummary.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/profiler/PerformanceSummary.java index 9367659072..d08008c590 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/profiler/PerformanceSummary.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/profiler/PerformanceSummary.java @@ -97,9 +97,9 @@ private void summarizeTimings(int count, String name, PerformanceInspector inspe List> results = inspector.getOrderType().sort(profiler, entrySet); String prefix = count + " " + name + (count != 1 ? 's' : ""); - if (inspector instanceof PlayerPerformanceInspector) { + if (inspector instanceof PlayerPerformanceInspector playerPerformanceInspector) { TextComponent component = summarizeAsTextComponent(count, prefix, results, formatter); - ((PlayerPerformanceInspector) inspector).sendMessage(component); + playerPerformanceInspector.sendMessage(component); } else { String text = summarizeAsString(inspector, count, prefix, results, formatter); inspector.sendMessage(text); diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/profiler/ProfiledBlock.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/profiler/ProfiledBlock.java index 1764fbffba..0bb60625fe 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/profiler/ProfiledBlock.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/profiler/ProfiledBlock.java @@ -143,9 +143,8 @@ public SlimefunAddon getAddon() { @Override public boolean equals(Object obj) { - if (obj instanceof ProfiledBlock) { - ProfiledBlock block = (ProfiledBlock) obj; - return position == block.position && Objects.equals(world, block.world); + if (obj instanceof ProfiledBlock profiledBlock) { + return position == profiledBlock.position && Objects.equals(world, profiledBlock.world); } return false; diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/profiler/package-info.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/profiler/package-info.java index 62b32be856..44f03f890f 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/profiler/package-info.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/profiler/package-info.java @@ -3,7 +3,7 @@ * {@link io.github.thebusybiscuit.slimefun4.core.services.profiler.SlimefunProfiler}. * The {@link io.github.thebusybiscuit.slimefun4.core.services.profiler.SlimefunProfiler} is used to determine * {@link org.bukkit.block.Block Blocks}, {@link org.bukkit.Chunk Chunks} or - * {@link me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem SlimefunItems} that cause lag or performance + * {@link io.github.thebusybiscuit.slimefun4.api.items.SlimefunItem SlimefunItems} that cause lag or performance * drops. */ package io.github.thebusybiscuit.slimefun4.core.services.profiler; \ No newline at end of file diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/sounds/SoundConfiguration.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/sounds/SoundConfiguration.java new file mode 100644 index 0000000000..373e6be3db --- /dev/null +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/sounds/SoundConfiguration.java @@ -0,0 +1,38 @@ +package io.github.thebusybiscuit.slimefun4.core.services.sounds; + +import javax.annotation.Nonnull; + +/** + * This structure class holds configured values for a {@link SoundEffect}. + * + * @author TheBusyBiscuit + * + * @see SoundService + * @see SoundEffect + * + */ +public class SoundConfiguration { + + private final String sound; + private final float volume; + private final float pitch; + + protected SoundConfiguration(@Nonnull String sound, float volume, float pitch) { + this.sound = sound; + this.volume = volume; + this.pitch = pitch; + } + + public @Nonnull String getSoundId() { + return sound; + } + + public float getVolume() { + return volume; + } + + public float getPitch() { + return pitch; + } + +} diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/sounds/SoundEffect.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/sounds/SoundEffect.java new file mode 100644 index 0000000000..d7cd73b2cd --- /dev/null +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/sounds/SoundEffect.java @@ -0,0 +1,218 @@ +package io.github.thebusybiscuit.slimefun4.core.services.sounds; + +import java.util.Locale; +import java.util.logging.Level; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + + +import io.github.thebusybiscuit.slimefun4.implementation.Slimefun; + +import org.bukkit.Location; +import org.bukkit.Sound; +import org.bukkit.SoundCategory; +import org.bukkit.block.Block; +import org.bukkit.entity.Player; + +import com.google.common.base.Preconditions; + +/** + * This enum holds references to all our sounds. + * + * @author TheBusyBiscuit + * @author J3fftw1 + * + * @see SoundService + * @see SoundConfiguration + * + */ +public enum SoundEffect { + + ANCIENT_ALTAR_ITEM_CHECK_SOUND(Sound.ENTITY_ENDERMAN_TELEPORT, 1F, 2F), + ANCIENT_ALTAR_ITEM_DROP_SOUND(Sound.ENTITY_ZOMBIE_ATTACK_IRON_DOOR, 1F, 1F), + ANCIENT_ALTAR_ITEM_PICK_UP_SOUND(Sound.ENTITY_ITEM_PICKUP, 1F, 1F), + ANCIENT_ALTAR_FINISH_SOUND(Sound.ENTITY_ZOMBIE_VILLAGER_CURE, 1F, 1F), + ANCIENT_ALTAR_START_SOUND(Sound.ENTITY_ILLUSIONER_PREPARE_MIRROR, 1F, 1F), + ANCIENT_PEDESTAL_ITEM_PLACE_SOUND(Sound.ENTITY_ITEM_PICKUP, 0.5F, 0.5F), + ARMOR_FORGE_FINISH_SOUND(Sound.BLOCK_ANVIL_USE, 1F, 1F), + ARMOR_FORGE_WORKING_SOUND(Sound.ENTITY_ARROW_HIT_PLAYER, 1F, 1F), + AUTO_CRAFTER_GUI_CLICK_SOUND(Sound.UI_BUTTON_CLICK, 1F, 1F), + AUTO_CRAFTER_UPDATE_RECIPE(Sound.UI_BUTTON_CLICK, 1F, 1F), + AUTOMATED_PANNING_MACHINE_FAIL_SOUND(Sound.ENTITY_ARMOR_STAND_BREAK, 1F, 1F), + AUTOMATED_PANNING_MACHINE_SUCCESS_SOUND(Sound.ENTITY_ARROW_HIT_PLAYER, 1F, 1F), + BEE_BOOTS_FALL_SOUND(Sound.BLOCK_HONEY_BLOCK_FALL, 1F, 1F), + BACKPACK_CLOSE_SOUND(Sound.ENTITY_HORSE_ARMOR, 1F, 1F), + BACKPACK_OPEN_SOUND(Sound.ENTITY_HORSE_ARMOR, 1F, 1F), + COMPOSTER_COMPOST_SOUND(Sound.ENTITY_ARROW_HIT_PLAYER, 1F, 1F), + COMPRESSOR_CRAFT_SOUND(Sound.ENTITY_ARROW_HIT_PLAYER, 1F, 1F), + COMPRESSOR_CRAFT_CONTRACT_SOUND(Sound.BLOCK_PISTON_CONTRACT, 1F, 1F), + COMPRESSOR_CRAFT_EXTEND_SOUND(Sound.BLOCK_PISTON_EXTEND, 1F, 1F), + COOLER_CONSUME_SOUND(Sound.ENTITY_GENERIC_DRINK, 1F, 1F), + CRUCIBLE_ADD_WATER_SOUND(Sound.ENTITY_PLAYER_SPLASH, 1F, 1F), + CRUCIBLE_ADD_LAVA_SOUND(Sound.BLOCK_LAVA_POP, 1F , 1F), + CRUCIBLE_BLOCK_BREAK_SOUND(Sound.BLOCK_METAL_BREAK, 1F, 1F), + CRUCIBLE_GENERATE_LIQUID_SOUND(Sound.BLOCK_LAVA_EXTINGUISH, 1F, 1F), + CRUCIBLE_INTERACT_SOUND(Sound.ENTITY_ARROW_HIT_PLAYER, 1F, 1F), + CRUCIBLE_PLACE_LAVA_SOUND(Sound.BLOCK_LAVA_POP, 1F , 1F), + CRUCIBLE_PLACE_WATER_SOUND(Sound.ENTITY_PLAYER_SPLASH, 1F, 1F), + DEBUG_FISH_CLICK_SOUND(Sound.BLOCK_BAMBOO_PLACE, 1F, 1F), + DIET_COOKIE_CONSUME_SOUND(Sound.ENTITY_GENERIC_EAT, 1F, 1F), + ENCHANTMENT_RUNE_ADD_ENCHANT_SOUND(Sound.ENTITY_ZOMBIE_VILLAGER_CURE, 1F, 1F), + ENDER_BACKPACK_OPEN_SOUND(Sound.ENTITY_ENDERMAN_TELEPORT, 1F, 1F), + ENHANCED_CRAFTING_TABLE_CRAFT_SOUND(Sound.BLOCK_WOODEN_BUTTON_CLICK_ON, 1F, 1F), + ELYTRA_CAP_IMPACT_SOUND(Sound.BLOCK_STONE_HIT, 1F, 1F), + EXPLOSIVE_BOW_HIT_SOUND(Sound.ENTITY_GENERIC_EXPLODE, 1F, 1F), + EXPLOSIVE_TOOL_EXPLODE_SOUND(Sound.ENTITY_GENERIC_EXPLODE, 0.2F, 1F), + FISHERMAN_ANDROID_FISHING_SOUND(Sound.ENTITY_PLAYER_SPLASH, 0.3F, 0.7F), + FLASK_OF_KNOWLEDGE_FILLUP_SOUND(Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 1F, 0.5F), + GUIDE_BUTTON_CLICK_SOUND(Sound.ITEM_BOOK_PAGE_TURN, 1F, 1F), + GUIDE_CONTRIBUTORS_OPEN_SOUND(Sound.BLOCK_NOTE_BLOCK_HARP, 0.7F, 0.7F), + GUIDE_LANGUAGE_OPEN_SOUND(Sound.BLOCK_NOTE_BLOCK_HARP, 0.7F, 0.7F), + GUIDE_OPEN_SETTING_SOUND(Sound.BLOCK_NOTE_BLOCK_HARP, 0.7F, 0.7F), + GRIND_STONE_INTERACT_SOUND(Sound.BLOCK_WOODEN_BUTTON_CLICK_ON, 1F, 1F), + IGNITION_CHAMBER_USE_FLINT_AND_STEEL_SOUND(Sound.ENTITY_ITEM_BREAK, 1F, 1F), + INFUSED_HOPPER_TELEPORT_SOUND(Sound.ENTITY_ENDERMAN_TELEPORT, 0.5F, 2F), + INFUSED_MAGNET_TELEPORT_SOUND(Sound.ENTITY_ENDERMAN_TELEPORT, 0.25F, 0.9F), + IRON_GOLEM_ASSEMBLER_ASSEMBLE_SOUND(Sound.ENTITY_IRON_GOLEM_REPAIR, 0.5F, 1F), + JETBOOTS_THRUST_SOUND(Sound.ENTITY_TNT_PRIMED, 0.25F, 1F), + JETPACK_THRUST_SOUND(Sound.ENTITY_GENERIC_EXPLODE, 0.25F, 1F), + JUICER_USE_SOUND(Sound.ENTITY_PLAYER_SPLASH, 1F, 1F), + LIMITED_USE_ITEM_BREAK_SOUND(Sound.ENTITY_ITEM_BREAK, 1F, 1F), + MAGICAL_EYE_OF_ENDER_USE_SOUND(Sound.ENTITY_ENDERMAN_TELEPORT, 1F, 1F), + MAGIC_SUGAR_CONSUME_SOUND(Sound.ENTITY_GENERIC_EAT, 1F, 1F), + MAGIC_WORKBENCH_FINISH_SOUND(Sound.ENTITY_ARROW_HIT_PLAYER, 1F, 1F), + MAGIC_WORKBENCH_START_ANIMATION_SOUND(Sound.BLOCK_WOODEN_BUTTON_CLICK_ON, 1F, 1F), + MINER_ANDROID_BLOCK_GENERATION_SOUND(Sound.BLOCK_FIRE_EXTINGUISH, 0.075F, 0.8F), + MINING_TASK_SOUND(Sound.ENTITY_ARROW_HIT_PLAYER, 0.2F, 1F), + ORE_WASHER_WASH_SOUND(Sound.ENTITY_PLAYER_SPLASH, 1F, 1F), + PLAYER_RESEARCHING_SOUND(Sound.ENTITY_BAT_TAKEOFF, 0.7F, 1F), + PORTABLE_DUSTBIN_OPEN_SOUND(Sound.BLOCK_ANVIL_LAND, 1F, 1F), + PORTABLE_CRAFTER_OPEN_SOUND(Sound.BLOCK_WOODEN_BUTTON_CLICK_ON, 1F, 1F), + PRESSURE_CHAMBER_FINISH_SOUND(Sound.ENTITY_ARROW_HIT_PLAYER, 1F, 1F), + PRESSURE_CHAMBER_WORKING_SOUND(Sound.ENTITY_TNT_PRIMED, 1F, 1F), + PROGRAMMABLE_ANDROID_SCRIPT_DOWNLOAD_SOUND(Sound.BLOCK_NOTE_BLOCK_HAT, 0.7F, 0.7F), + SLIME_BOOTS_FALL_SOUND(Sound.BLOCK_SLIME_BLOCK_FALL, 1F, 1F), + TELEPORTATION_MANAGER_OPEN_GUI(Sound.UI_BUTTON_CLICK, 1F, 1F), + GPS_NETWORK_ADD_WAYPOINT(Sound.BLOCK_NOTE_BLOCK_PLING, 1F, 1F), + GPS_NETWORK_CREATE_WAYPOINT(Sound.BLOCK_NOTE_BLOCK_PLING, 0.5F, 1F), + GPS_NETWORK_OPEN_PANEL_SOUND(Sound.UI_BUTTON_CLICK, 1F, 1F), + SMELTERY_CRAFT_SOUND(Sound.BLOCK_LAVA_POP, 1F, 1F), + SOULBOUND_RUNE_RITUAL_SOUND(Sound.ENTITY_GENERIC_EXPLODE, 0.3F, 1F), + SPLINT_CONSUME_SOUND(Sound.ENTITY_SKELETON_HURT, 1F, 1F), + STOMPER_BOOTS_STOMP_SOUND(Sound.ENTITY_ZOMBIE_BREAK_WOODEN_DOOR, 1F, 2F), + TAPE_MEASURE_MEASURE_SOUND(Sound.ITEM_BOOK_PUT, 1, 0.7F), + TOME_OF_KNOWLEDGE_USE_SOUND(Sound.ENTITY_PLAYER_LEVELUP, 1F, 1F), + TELEPORT_UPDATE_SOUND(Sound.BLOCK_BEACON_AMBIENT, 1F, 0.6F), + TELEPORT_SOUND(Sound.BLOCK_BEACON_ACTIVATE, 1F, 1F), + VAMPIRE_BLADE_HEALING_SOUND(Sound.ENTITY_ARROW_HIT_PLAYER, 0.7F, 0.7F), + VANILLA_AUTO_CRAFTER_UPDATE_RECIPE_SOUND(Sound.UI_BUTTON_CLICK, 1F, 1F), + VILLAGER_RUNE_TRANSFORM_SOUND(Sound.ENTITY_VILLAGER_CELEBRATE, 1F, 1.4F), + VITAMINS_CONSUME_SOUND(Sound.ENTITY_GENERIC_EAT, 1F, 1F), + WIND_STAFF_USE_SOUND(Sound.ENTITY_TNT_PRIMED, 1F, 1F); + + private final String defaultSound; + private final float defaultVolume; + private final float defaultPitch; + + SoundEffect(@Nonnull String sound, float volume, float pitch) { + Preconditions.checkNotNull(sound, "The Sound id cannot be null!"); + Preconditions.checkArgument(volume >= 0, "The volume cannot be a negative number."); + Preconditions.checkArgument(pitch >= 0.5, "A pitch below 0.5 has no effect on the sound."); + + this.defaultSound = sound; + this.defaultVolume = volume; + this.defaultPitch = pitch; + } + + SoundEffect(@Nonnull Sound sound, float volume, float pitch) { + Preconditions.checkNotNull(sound, "The Sound id cannot be null!"); + Preconditions.checkArgument(volume >= 0, "The volume cannot be a negative number."); + Preconditions.checkArgument(pitch >= 0.5, "A pitch below 0.5 has no effect on the sound."); + + this.defaultSound = sound.getKey().getKey(); + this.defaultVolume = volume; + this.defaultPitch = pitch; + } + + private @Nullable SoundConfiguration getConfiguration() { + SoundConfiguration config = Slimefun.getSoundService().getConfiguration(this); + + if (config == null) { + // This should not happen. But if it does... send a warning + Slimefun.logger().log(Level.WARNING, "Could not find any sound configuration for: {0}", name()); + } + + return config; + } + + /** + * This method will play this {@link SoundEffect} only to the given {@link Player} using the + * eye {@link Location} of the {@link Player} and the {@link SoundCategory} {@code PLAYERS}. + * + * @param player The {@link Player} which to play the {@link Sound} to. + */ + public void playFor(@Nonnull Player player) { + Preconditions.checkNotNull(player, "Cannot play sounds to a Player that is null!"); + SoundConfiguration config = getConfiguration(); + + if (config != null) { + Location loc = player.getEyeLocation(); + player.playSound(loc, config.getSoundId(), SoundCategory.PLAYERS, config.getVolume(), config.getPitch()); + } + } + + /** + * This method will play this {@link SoundEffect} at the given {@link Location} using the + * provided {@link SoundCategory}. + * + * @param loc The {@link Location} at which to play the {@link SoundEffect}. + * @param category The {@link SoundCategory} that should be used. + */ + public void playAt(@Nonnull Location loc, @Nonnull SoundCategory category) { + Preconditions.checkNotNull(loc, "The location should not be null."); + SoundConfiguration config = getConfiguration(); + + if (config != null && loc.getWorld() != null) { + loc.getWorld().playSound(loc, config.getSoundId(), category, config.getVolume(), config.getPitch()); + } + } + + /** + * This method will play this {@link SoundEffect} at the {@link Location} of the given {@link Block}, + * the used {@link SoundCategory} will be {@code BLOCKS}. + * + * @param block The {@link Block} at which to play the {@link SoundEffect} + */ + public void playAt(@Nonnull Block block) { + Preconditions.checkNotNull(block, "The block cannot be null."); + playAt(block.getLocation(), SoundCategory.BLOCKS); + } + + /** + * This returns the default sound id. + * + * @return The default sound id. + */ + public @Nonnull String getDefaultSoundId() { + return defaultSound; + } + + /** + * This returns the default volume. + * + * @return The default volume. + */ + public float getDefaultVolume() { + return defaultVolume; + } + + /** + * This returns the default pitch. + * + * @return The default pitch. + */ + public float getDefaultPitch() { + return defaultPitch; + } +} diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/sounds/SoundService.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/sounds/SoundService.java new file mode 100644 index 0000000000..1c1d1133a8 --- /dev/null +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/sounds/SoundService.java @@ -0,0 +1,109 @@ +package io.github.thebusybiscuit.slimefun4.core.services.sounds; + +import java.util.EnumMap; +import java.util.Map; +import java.util.logging.Level; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import io.github.bakedlibs.dough.config.Config; +import io.github.thebusybiscuit.slimefun4.implementation.Slimefun; + +import com.google.common.base.Preconditions; + +/** + * The {@link SoundService} is responsible for our sound management. + * It allows server owners to fully customize their users' sound experience. + * + * @author TheBusyBiscuit + */ +public class SoundService { + + /** + * Our {@link Config} instance. + */ + private final Config config; + + /** + * In this map we cache the corresponding {@link SoundConfiguration} to each {@link SoundEffect}. + */ + private final Map soundMap = new EnumMap<>(SoundEffect.class); + + public SoundService(@Nonnull Slimefun plugin) { + config = new Config(plugin, "sounds.yml"); + + // @formatter:off + config.getConfiguration().options().header( + "This file is used to assign the sounds which Slimefun will play.\n" + + "You can fully customize any sound you want and even change their pitch\n" + + "and volume. To disable a sound, simply set the volume to zero.\n" + ); + // @formatter:on + + config.getConfiguration().options().copyHeader(); + } + + /** + * This method reloads every {@link SoundConfiguration}. + * + * @param save + * Whether to save the defaults to disk + */ + public void reload(boolean save) { + config.reload(); + + for (SoundEffect sound : SoundEffect.values()) { + try { + reloadSound(sound); + } catch (Exception | LinkageError x) { + Slimefun.logger().log(Level.SEVERE, x, () -> "An exception was thrown while trying to load the configuration data for the following sound:" + sound.name()); + } + } + + if (save) { + config.save(); + } + } + + private void reloadSound(@Nonnull SoundEffect sound) { + // Set up default values + config.setDefaultValue(sound.name() + ".sound", sound.getDefaultSoundId()); + config.setDefaultValue(sound.name() + ".volume", sound.getDefaultVolume()); + config.setDefaultValue(sound.name() + ".pitch", sound.getDefaultPitch()); + + // Read the values + String soundId = config.getString(sound.name() + ".sound"); + float volume = config.getFloat(sound.name() + ".volume"); + float pitch = config.getFloat(sound.name() + ".pitch"); + + // Check whether the volume is at least 0.0 + if (volume < 0) { + Slimefun.logger().log(Level.WARNING, "Invalid value in sounds.yml! Volume for Sound \"{0}\" was {1} (must be at least 0.0)", new Object[] { sound.name(), volume }); + volume = 0; + } + + // Check if the pitch is at least 0.5 + if (pitch < 0.5F) { + Slimefun.logger().log(Level.WARNING, "Invalid value in sounds.yml! Pitch for Sound \"{0}\" was {1} (must be at least 0.5)", new Object[] { sound.name(), pitch }); + pitch = 0.5F; + } + + // Cache this configuration + SoundConfiguration configuration = new SoundConfiguration(soundId, volume, pitch); + soundMap.put(sound, configuration); + } + + /** + * This returns the currently used (immutable) {@link SoundConfiguration} for the given {@link SoundEffect}. + * + * @param sound + * The {@link SoundEffect} + * + * @return The corresponding {@link SoundConfiguration}. This may be null if something went wrong + */ + public @Nullable SoundConfiguration getConfiguration(@Nonnull SoundEffect sound) { + Preconditions.checkNotNull(sound, "The sound must not be null!"); + return soundMap.get(sound); + } +} diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/sounds/package-info.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/sounds/package-info.java new file mode 100644 index 0000000000..c14c511439 --- /dev/null +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/sounds/package-info.java @@ -0,0 +1,7 @@ +/** + * This package holds classes related to the + * {@link io.github.thebusybiscuit.slimefun4.core.services.sounds.SoundService}. + * This service is responsible for our sound management and allowing server owners to fully customize + * their sound experience. + */ +package io.github.thebusybiscuit.slimefun4.core.services.sounds; \ No newline at end of file diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/Slimefun.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/Slimefun.java index 0522e39208..f1fffb2b83 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/Slimefun.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/Slimefun.java @@ -55,6 +55,7 @@ import io.github.thebusybiscuit.slimefun4.core.services.github.GitHubService; import io.github.thebusybiscuit.slimefun4.core.services.holograms.HologramsService; import io.github.thebusybiscuit.slimefun4.core.services.profiler.SlimefunProfiler; +import io.github.thebusybiscuit.slimefun4.core.services.sounds.SoundService; import io.github.thebusybiscuit.slimefun4.implementation.items.altar.AncientAltar; import io.github.thebusybiscuit.slimefun4.implementation.items.altar.AncientPedestal; import io.github.thebusybiscuit.slimefun4.implementation.items.backpacks.Cooler; @@ -81,10 +82,12 @@ import io.github.thebusybiscuit.slimefun4.implementation.listeners.HopperListener; import io.github.thebusybiscuit.slimefun4.implementation.listeners.ItemDropListener; import io.github.thebusybiscuit.slimefun4.implementation.listeners.ItemPickupListener; +import io.github.thebusybiscuit.slimefun4.implementation.listeners.MiddleClickListener; import io.github.thebusybiscuit.slimefun4.implementation.listeners.MiningAndroidListener; import io.github.thebusybiscuit.slimefun4.implementation.listeners.MultiBlockListener; import io.github.thebusybiscuit.slimefun4.implementation.listeners.NetworkListener; import io.github.thebusybiscuit.slimefun4.implementation.listeners.PlayerProfileListener; +import io.github.thebusybiscuit.slimefun4.implementation.listeners.RadioactivityListener; import io.github.thebusybiscuit.slimefun4.implementation.listeners.SeismicAxeListener; import io.github.thebusybiscuit.slimefun4.implementation.listeners.SlimefunBootsListener; import io.github.thebusybiscuit.slimefun4.implementation.listeners.SlimefunBowListener; @@ -101,6 +104,7 @@ import io.github.thebusybiscuit.slimefun4.implementation.listeners.crafting.CauldronListener; import io.github.thebusybiscuit.slimefun4.implementation.listeners.crafting.CraftingTableListener; import io.github.thebusybiscuit.slimefun4.implementation.listeners.crafting.GrindstoneListener; +import io.github.thebusybiscuit.slimefun4.implementation.listeners.crafting.SmithingTableListener; import io.github.thebusybiscuit.slimefun4.implementation.listeners.entity.BeeListener; import io.github.thebusybiscuit.slimefun4.implementation.listeners.entity.EntityInteractionListener; import io.github.thebusybiscuit.slimefun4.implementation.listeners.entity.FireworksListener; @@ -112,7 +116,10 @@ import io.github.thebusybiscuit.slimefun4.implementation.setup.PostSetup; import io.github.thebusybiscuit.slimefun4.implementation.setup.ResearchSetup; import io.github.thebusybiscuit.slimefun4.implementation.setup.SlimefunItemSetup; -import io.github.thebusybiscuit.slimefun4.implementation.tasks.ArmorTask; +import io.github.thebusybiscuit.slimefun4.implementation.tasks.armor.RadiationTask; +import io.github.thebusybiscuit.slimefun4.implementation.tasks.armor.RainbowArmorTask; +import io.github.thebusybiscuit.slimefun4.implementation.tasks.armor.SlimefunArmorTask; +import io.github.thebusybiscuit.slimefun4.implementation.tasks.armor.SolarHelmetTask; import io.github.thebusybiscuit.slimefun4.implementation.tasks.SlimefunStartupTask; import io.github.thebusybiscuit.slimefun4.implementation.tasks.TickerTask; import io.github.thebusybiscuit.slimefun4.integrations.IntegrationsManager; @@ -132,6 +139,13 @@ */ public final class Slimefun extends JavaPlugin implements SlimefunAddon { + /** + * This is the Java version we recommend server owners to use. + * This does not necessarily mean that it's the minimum version + * required to run Slimefun. + */ + private static final int RECOMMENDED_JAVA_VERSION = 17; + /** * Our static instance of {@link Slimefun}. * Make sure to clean this up in {@link #onDisable()}! @@ -166,6 +180,7 @@ public final class Slimefun extends JavaPlugin implements SlimefunAddon { private final PerWorldSettingsService worldSettingsService = new PerWorldSettingsService(this); private final MinecraftRecipeService recipeService = new MinecraftRecipeService(this); private final HologramsService hologramsService = new HologramsService(this); + private final SoundService soundService = new SoundService(this); // Some other things we need private final IntegrationsManager integrations = new IntegrationsManager(this); @@ -241,6 +256,7 @@ private void onUnitTestStart() { command.register(); registry.load(this, config); loadTags(); + soundService.reload(false); } /** @@ -260,11 +276,13 @@ private void onPluginStart() { // Check if CS-CoreLib is installed (it is no longer needed) if (getServer().getPluginManager().getPlugin("CS-CoreLib") != null) { StartupWarnings.discourageCSCoreLib(logger); + getServer().getPluginManager().disablePlugin(this); + return; } - // Encourage Java 16 - if (NumberUtils.getJavaVersion() < 16) { - StartupWarnings.oldJavaVersion(logger); + // Encourage newer Java version + if (NumberUtils.getJavaVersion() < RECOMMENDED_JAVA_VERSION) { + StartupWarnings.oldJavaVersion(logger, RECOMMENDED_JAVA_VERSION); } // If the server has no "data-storage" folder, it's _probably_ a new install. So mark it for metrics. @@ -327,6 +345,7 @@ private void onPluginStart() { runSync(new SlimefunStartupTask(this, () -> { textureService.register(registry.getAllSlimefunItems(), true); permissionsService.register(registry.getAllSlimefunItems(), true); + soundService.reload(true); // This try/catch should prevent buggy Spigot builds from blocking item loading try { @@ -346,8 +365,10 @@ private void onPluginStart() { // Armor Update Task if (config.getBoolean("options.enable-armor-effects")) { - boolean radioactiveFire = config.getBoolean("options.burn-players-when-radioactive"); - getServer().getScheduler().runTaskTimerAsynchronously(this, new ArmorTask(radioactiveFire), 0L, config.getInt("options.armor-update-interval") * 20L); + new SlimefunArmorTask().schedule(this, config.getInt("options.armor-update-interval") * 20L); + new RadiationTask().schedule(this, config.getInt("options.radiation-update-interval") * 20L); + new RainbowArmorTask().schedule(this, config.getInt("options.rainbow-armor-update-interval") * 20L); + new SolarHelmetTask().schedule(this, config.getInt("options.armor-update-interval")); } // Starting our tasks @@ -421,7 +442,9 @@ public void onDisable() { } // Create a new backup zip - backupService.run(); + if (config.getBoolean("options.backup-data")) { + backupService.run(); + } // Close and unload any resources from our Metrics Service metricsService.cleanUp(); @@ -618,21 +641,16 @@ private void registerListeners() { new SoulboundListener(this); new AutoCrafterListener(this); new SlimefunItemHitListener(this); - - // Bees were added in 1.15 - if (minecraftVersion.isAtLeast(MinecraftVersion.MINECRAFT_1_15)) { - new BeeListener(this); - new BeeWingsListener(this, (BeeWings) SlimefunItems.BEE_WINGS.getItem()); - } - - // Piglins were added in 1.16 - if (minecraftVersion.isAtLeast(MinecraftVersion.MINECRAFT_1_16)) { - new PiglinListener(this); - } + new MiddleClickListener(this); + new BeeListener(this); + new BeeWingsListener(this, (BeeWings) SlimefunItems.BEE_WINGS.getItem()); + new PiglinListener(this); + new SmithingTableListener(this); // Item-specific Listeners new CoolerListener(this, (Cooler) SlimefunItems.COOLER.getItem()); new SeismicAxeListener(this, (SeismicAxe) SlimefunItems.SEISMIC_AXE.getItem()); + new RadioactivityListener(this); new AncientAltarListener(this, (AncientAltar) SlimefunItems.ANCIENT_ALTAR.getItem(), (AncientPedestal) SlimefunItems.ANCIENT_PEDESTAL.getItem()); grapplingHookListener.register(this, (GrapplingHook) SlimefunItems.GRAPPLING_HOOK.getItem()); bowListener.register(this); @@ -826,6 +844,17 @@ private static void validateInstance() { return instance.hologramsService; } + /** + * This returns our {@link SoundService} which handles the configuration of all sounds used in Slimefun + * + * @return Our instance of {@link SoundService} + */ + @Nonnull + public static SoundService getSoundService() { + validateInstance(); + return instance.soundService; + } + /** * This returns our instance of {@link IntegrationsManager}. * This is responsible for managing any integrations with third party {@link Plugin plugins}. diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/SlimefunItems.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/SlimefunItems.java index 50e67e25e3..d65a67bd34 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/SlimefunItems.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/SlimefunItems.java @@ -14,7 +14,6 @@ import org.bukkit.potion.PotionEffect; import org.bukkit.potion.PotionEffectType; -import io.github.thebusybiscuit.slimefun4.api.MinecraftVersion; import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItemStack; import io.github.thebusybiscuit.slimefun4.core.attributes.MachineTier; import io.github.thebusybiscuit.slimefun4.core.attributes.MachineType; @@ -87,7 +86,7 @@ private SlimefunItems() {} public static final SlimefunItemStack DAMASCUS_STEEL_JETPACK = new SlimefunItemStack("DAMASCUS_STEEL_JETPACK", Material.LEATHER_CHESTPLATE, Color.SILVER, "&9Electric Jetpack &7- &eV", "", LoreBuilder.material("Damascus Steel"), LoreBuilder.powerCharged(0, 75), "&8\u21E8 &7Thrust: &c0.55", "", LoreBuilder.CROUCH_TO_USE); public static final SlimefunItemStack REINFORCED_ALLOY_JETPACK = new SlimefunItemStack("REINFORCED_ALLOY_JETPACK", Material.LEATHER_CHESTPLATE, Color.SILVER, "&9Electric Jetpack &7- &eVI", "", LoreBuilder.material("Reinforced Alloy"), LoreBuilder.powerCharged(0, 100), "&8\u21E8 &7Thrust: &c0.6", "", LoreBuilder.CROUCH_TO_USE); public static final SlimefunItemStack CARBONADO_JETPACK = new SlimefunItemStack("CARBONADO_JETPACK", Material.LEATHER_CHESTPLATE, Color.BLACK, "&9Electric Jetpack &7- &eVII", "", LoreBuilder.material("Carbonado"), LoreBuilder.powerCharged(0, 150), "&8\u21E8 &7Thrust: &c0.7", "", LoreBuilder.CROUCH_TO_USE); - public static final SlimefunItemStack ARMORED_JETPACK = new SlimefunItemStack("ARMORED_JETPACK", Material.IRON_CHESTPLATE, "&9Armored Jetpack", LoreBuilder.material("Steel"), "", LoreBuilder.powerCharged(0, 50), "&8\u21E8 &7Thrust: &c0.45", "", LoreBuilder.CROUCH_TO_USE); + public static final SlimefunItemStack ARMORED_JETPACK = new SlimefunItemStack("ARMORED_JETPACK", Material.IRON_CHESTPLATE, "&9Armored Jetpack", LoreBuilder.material("Steel"), "", LoreBuilder.powerCharged(0, 50), "&8\u21E8 &7Thrust: &c0.5", "", LoreBuilder.CROUCH_TO_USE); /* Jetboots */ public static final SlimefunItemStack DURALUMIN_JETBOOTS = new SlimefunItemStack("DURALUMIN_JETBOOTS", Material.LEATHER_BOOTS, Color.SILVER, "&9Jet Boots &7- &eI", "", LoreBuilder.material("Duralumin"), LoreBuilder.powerCharged(0, 20), LoreBuilder.speed(0.35F), "&8\u21E8 &7Accuracy: &c50%", "", LoreBuilder.CROUCH_TO_USE); @@ -149,6 +148,7 @@ private SlimefunItems() {} public static final SlimefunItemStack CARROT_JUICE = new SlimefunItemStack("CARROT_JUICE", Color.ORANGE, new PotionEffect(PotionEffectType.SATURATION, 5, 0), "&6Carrot Juice", "", LoreBuilder.hunger(3)); public static final SlimefunItemStack PUMPKIN_JUICE = new SlimefunItemStack("PUMPKIN_JUICE", Color.ORANGE, new PotionEffect(PotionEffectType.SATURATION, 5, 0), "&6Pumpkin Juice", "", LoreBuilder.hunger(3)); public static final SlimefunItemStack SWEET_BERRY_JUICE = new SlimefunItemStack("SWEET_BERRY_JUICE", Color.RED, new PotionEffect(PotionEffectType.SATURATION, 5, 0), "&cSweet Berry Juice", "", LoreBuilder.hunger(3)); + public static final SlimefunItemStack GLOW_BERRY_JUICE = new SlimefunItemStack("GLOW_BERRY_JUICE", Color.ORANGE, new PotionEffect(PotionEffectType.SATURATION, 5, 0), "&6Glow Berry Juice", "", LoreBuilder.hunger(3)); public static final SlimefunItemStack GOLDEN_APPLE_JUICE = new SlimefunItemStack("GOLDEN_APPLE_JUICE", Color.YELLOW, new PotionEffect(PotionEffectType.ABSORPTION, 20 * 20, 0), "&bGolden Apple Juice"); public static final SlimefunItemStack BEEF_JERKY = new SlimefunItemStack("BEEF_JERKY", Material.COOKED_BEEF, "&6Beef Jerky", "", "&fExtra saturating!"); @@ -204,7 +204,6 @@ private SlimefunItems() {} public static final SlimefunItemStack SMELTERS_PICKAXE = new SlimefunItemStack("SMELTERS_PICKAXE", Material.DIAMOND_PICKAXE, "&6Smelter's Pickaxe", "&c&lAuto-Smelting", "", "&9Works with Fortune"); public static final SlimefunItemStack LUMBER_AXE = new SlimefunItemStack("LUMBER_AXE", Material.DIAMOND_AXE, "&6Lumber Axe", "&a&oCuts down the whole Tree..."); public static final SlimefunItemStack PICKAXE_OF_CONTAINMENT = new SlimefunItemStack("PICKAXE_OF_CONTAINMENT", Material.IRON_PICKAXE, "&cPickaxe of Containment", "", "&9Can pickup Spawners"); - public static final SlimefunItemStack HERCULES_PICKAXE = new SlimefunItemStack("HERCULES_PICKAXE", Material.IRON_PICKAXE, "&9Hercules' Pickaxe", "", "&fSo powerful that it", "&fcrushes all mined Ores", "&finto Dust..."); public static final SlimefunItemStack EXPLOSIVE_PICKAXE = new SlimefunItemStack("EXPLOSIVE_PICKAXE", Material.DIAMOND_PICKAXE, "&eExplosive Pickaxe", "", "&fAllows you to mine a good bit", "&fof Blocks at once...", "", "&9Works with Fortune"); public static final SlimefunItemStack EXPLOSIVE_SHOVEL = new SlimefunItemStack("EXPLOSIVE_SHOVEL", Material.DIAMOND_SHOVEL, "&eExplosive Shovel", "", "&fAllows you to mine a good bit", "&fof diggable Blocks at once..."); public static final SlimefunItemStack PICKAXE_OF_THE_SEEKER = new SlimefunItemStack("PICKAXE_OF_THE_SEEKER", Material.DIAMOND_PICKAXE, "&aPickaxe of the Seeker", "&fWill always point you to the nearest Ore", "&fbut might get damaged when doing it", "", "&7&eRight Click&7 to be pointed to the nearest Ore"); @@ -213,9 +212,6 @@ private SlimefunItems() {} public static final SlimefunItemStack CLIMBING_PICK = new SlimefunItemStack("CLIMBING_PICK", Material.IRON_PICKAXE, "&bClimbing Pick", "", "&fAllows you to climb certain surfaces", "&fby right-clicking.", "&fEnchant this pick with Efficiency to", "&fclimb even faster!"); static { - HERCULES_PICKAXE.addUnsafeEnchantment(Enchantment.DURABILITY, 5); - HERCULES_PICKAXE.addUnsafeEnchantment(Enchantment.DIG_SPEED, 3); - COBALT_PICKAXE.addUnsafeEnchantment(Enchantment.DURABILITY, 10); COBALT_PICKAXE.addUnsafeEnchantment(Enchantment.DIG_SPEED, 6); } @@ -225,7 +221,11 @@ private SlimefunItems() {} public static final SlimefunItemStack GLOWSTONE_CHESTPLATE = new SlimefunItemStack("GLOWSTONE_CHESTPLATE", Material.LEATHER_CHESTPLATE, Color.YELLOW, "&e&lGlowstone Chestplate", "", "&a&oShining like the sun!", "", "&9+ Night Vision"); public static final SlimefunItemStack GLOWSTONE_LEGGINGS = new SlimefunItemStack("GLOWSTONE_LEGGINGS", Material.LEATHER_LEGGINGS, Color.YELLOW, "&e&lGlowstone Leggings", "", "&a&oShining like the sun!", "", "&9+ Night Vision"); public static final SlimefunItemStack GLOWSTONE_BOOTS = new SlimefunItemStack("GLOWSTONE_BOOTS", Material.LEATHER_BOOTS, Color.YELLOW, "&e&lGlowstone Boots", "", "&a&oShining like the sun!", "", "&9+ Night Vision"); - + public static final SlimefunItemStack RAINBOW_LEATHER = new SlimefunItemStack("RAINBOW_LEATHER", Material.RABBIT_HIDE, Color.FUCHSIA, "&dRainbow Leather", "", "&fCan be used to craft rainbow armor"); + public static final SlimefunItemStack RAINBOW_HELMET = new SlimefunItemStack("RAINBOW_HELMET", Material.LEATHER_HELMET, Color.FUCHSIA, "&d&lRainbow Helmet", "", LoreBuilder.RAINBOW); + public static final SlimefunItemStack RAINBOW_CHESTPLATE = new SlimefunItemStack("RAINBOW_CHESTPLATE", Material.LEATHER_CHESTPLATE, Color.FUCHSIA, "&d&lRainbow Chestplate", "", LoreBuilder.RAINBOW); + public static final SlimefunItemStack RAINBOW_LEGGINGS = new SlimefunItemStack("RAINBOW_LEGGINGS", Material.LEATHER_LEGGINGS, Color.FUCHSIA, "&d&lRainbow Leggings", "", LoreBuilder.RAINBOW); + public static final SlimefunItemStack RAINBOW_BOOTS = new SlimefunItemStack("RAINBOW_BOOTS", Material.LEATHER_BOOTS, Color.FUCHSIA, "&d&lRainbow Boots", "", LoreBuilder.RAINBOW); public static final SlimefunItemStack ENDER_HELMET = new SlimefunItemStack("ENDER_HELMET", Material.LEATHER_HELMET, Color.fromRGB(28, 25, 112), "&5&lEnder Helmet", "", "&a&oSometimes its here, sometimes there!"); public static final SlimefunItemStack ENDER_CHESTPLATE = new SlimefunItemStack("ENDER_CHESTPLATE", Material.LEATHER_CHESTPLATE, Color.fromRGB(28, 25, 112), "&5&lEnder Chestplate", "", "&a&oSometimes its here, sometimes there!"); public static final SlimefunItemStack ENDER_LEGGINGS = new SlimefunItemStack("ENDER_LEGGINGS", Material.LEATHER_LEGGINGS, Color.fromRGB(28, 25, 112), "&5&lEnder Leggings", "", "&a&oSometimes its here, sometimes there!"); @@ -257,10 +257,7 @@ private SlimefunItems() {} hazmatLore.add(""); hazmatLore.add(ChatColor.GOLD + "Full set effects:"); hazmatLore.add(ChatColor.YELLOW + "- Radiation immunity"); - - if (Slimefun.getMinecraftVersion().isAtLeast(MinecraftVersion.MINECRAFT_1_15)) { - hazmatLore.add(ChatColor.YELLOW + "- Bee Sting protection"); - } + hazmatLore.add(ChatColor.YELLOW + "- Bee Sting protection"); } public static final SlimefunItemStack SCUBA_HELMET = new SlimefunItemStack("SCUBA_HELMET", Material.LEATHER_HELMET, Color.ORANGE, "&cScuba Helmet", "", "&7Allows you to breathe underwater"); @@ -426,14 +423,12 @@ private SlimefunItems() {} public static final SlimefunItemStack CRAFTING_MOTOR = new SlimefunItemStack("CRAFTING_MOTOR", HeadTexture.CRAFTING_MOTOR, "&6Crafting Motor", "", "&7Important component of Auto-Crafters"); /* Rainbow blocks */ - private static final String RAINBOW = "&dCycles through all Colors of the Rainbow!"; - public static final SlimefunItemStack RAINBOW_WOOL = new SlimefunItemStack("RAINBOW_WOOL", Material.WHITE_WOOL, "&5Rainbow Wool", "", RAINBOW); - public static final SlimefunItemStack RAINBOW_GLASS = new SlimefunItemStack("RAINBOW_GLASS", Material.WHITE_STAINED_GLASS, "&5Rainbow Glass", "", RAINBOW); - public static final SlimefunItemStack RAINBOW_CLAY = new SlimefunItemStack("RAINBOW_CLAY", Material.WHITE_TERRACOTTA, "&5Rainbow Clay", "", RAINBOW); - public static final SlimefunItemStack RAINBOW_GLASS_PANE = new SlimefunItemStack("RAINBOW_GLASS_PANE", Material.WHITE_STAINED_GLASS_PANE, "&5Rainbow Glass Pane", "", RAINBOW); - public static final SlimefunItemStack RAINBOW_CONCRETE = new SlimefunItemStack("RAINBOW_CONCRETE", Material.WHITE_CONCRETE, "&5Rainbow Concrete", "", RAINBOW); - public static final SlimefunItemStack RAINBOW_GLAZED_TERRACOTTA = new SlimefunItemStack("RAINBOW_GLAZED_TERRACOTTA", Material.WHITE_GLAZED_TERRACOTTA, "&5Rainbow Glazed Terracotta", "", RAINBOW); - + public static final SlimefunItemStack RAINBOW_WOOL = new SlimefunItemStack("RAINBOW_WOOL", Material.WHITE_WOOL, "&5Rainbow Wool", "", LoreBuilder.RAINBOW); + public static final SlimefunItemStack RAINBOW_GLASS = new SlimefunItemStack("RAINBOW_GLASS", Material.WHITE_STAINED_GLASS, "&5Rainbow Glass", "", LoreBuilder.RAINBOW); + public static final SlimefunItemStack RAINBOW_CLAY = new SlimefunItemStack("RAINBOW_CLAY", Material.WHITE_TERRACOTTA, "&5Rainbow Clay", "", LoreBuilder.RAINBOW); + public static final SlimefunItemStack RAINBOW_GLASS_PANE = new SlimefunItemStack("RAINBOW_GLASS_PANE", Material.WHITE_STAINED_GLASS_PANE, "&5Rainbow Glass Pane", "", LoreBuilder.RAINBOW); + public static final SlimefunItemStack RAINBOW_CONCRETE = new SlimefunItemStack("RAINBOW_CONCRETE", Material.WHITE_CONCRETE, "&5Rainbow Concrete", "", LoreBuilder.RAINBOW); + public static final SlimefunItemStack RAINBOW_GLAZED_TERRACOTTA = new SlimefunItemStack("RAINBOW_GLAZED_TERRACOTTA", Material.WHITE_GLAZED_TERRACOTTA, "&5Rainbow Glazed Terracotta", "", LoreBuilder.RAINBOW); /* Seasonal */ private static final String CHRISTMAS = ChatUtils.christmas("[Christmas Edition]"); public static final SlimefunItemStack RAINBOW_WOOL_XMAS = new SlimefunItemStack("RAINBOW_WOOL_XMAS", Material.WHITE_WOOL, "&5Rainbow Wool &7(Christmas)", "", CHRISTMAS); @@ -496,7 +491,7 @@ private SlimefunItems() {} public static final SlimefunItemStack FERROSILICON = new SlimefunItemStack("FERROSILICON", Material.IRON_INGOT, "&bFerrosilicon"); /* Alloy (Iron + Gold) */ public static final SlimefunItemStack GILDED_IRON = new SlimefunItemStack("GILDED_IRON", Material.GOLD_INGOT, "&6&lGilded Iron"); - /* Alloy (Redston + Ferrosilicon) */ + /* Alloy (Redstone + Ferrosilicon) */ public static final SlimefunItemStack REDSTONE_ALLOY = new SlimefunItemStack("REDSTONE_ALLOY", Material.BRICK, "&cRedstone Alloy Ingot"); /* Alloy (Iron + Copper) */ public static final SlimefunItemStack NICKEL_INGOT = new SlimefunItemStack("NICKEL_INGOT", Material.IRON_INGOT, "&bNickel Ingot"); @@ -549,6 +544,7 @@ private SlimefunItems() {} public static final SlimefunItemStack TALISMAN_ANVIL = new SlimefunItemStack("ANVIL_TALISMAN", Material.EMERALD, "&aTalisman of the Anvil", "", "&fEach Talisman can prevent", "&f1 Tool from breaking, but will then", "&fbe consumed", "", "&4&lWARNING:", "&4This Talisman does not work on", "&4Tools which are too powerful", "&4due to their complexity"); public static final SlimefunItemStack TALISMAN_MINER = new SlimefunItemStack("MINER_TALISMAN", Material.EMERALD, "&aTalisman of the Miner", "", "&fWhile you have this Talisman", "&fin your Inventory it has", "&fa 20% chance of doubling", "&fall Ores you mine"); + public static final SlimefunItemStack TALISMAN_FARMER = new SlimefunItemStack("FARMER_TALISMAN", Material.EMERALD, "&aTalisman of the Farmer", "", "&fWhile you have this Talisman", "&fin your Inventory it has", "&fa 20% chance of doubling", "&fall crops you harvest"); public static final SlimefunItemStack TALISMAN_HUNTER = new SlimefunItemStack("HUNTER_TALISMAN", Material.EMERALD, "&aTalisman of the Hunter", "", "&fWhile you have this Talisman", "&fin your Inventory it has", "&fa 20% chance of doubling", "&fall Drops from Mobs you kill"); public static final SlimefunItemStack TALISMAN_LAVA = new SlimefunItemStack("LAVA_TALISMAN", Material.EMERALD, "&aTalisman of the Lava Walker", "", "&fWhile you have this Talisman", "&fin your Inventory it will", "&fgive you Fire Resistance", "&fas soon as you touch Lava", "&fbut will then be consumed"); public static final SlimefunItemStack TALISMAN_WATER = new SlimefunItemStack("WATER_TALISMAN", Material.EMERALD, "&aTalisman of the Water Breather", "", "&fWhile you have this Talisman", "&fin your Inventory it will", "&fgive you the ability", "&fto breath underwater as", "&fsoon as you start drowning", "&fbut will then be consumed"); @@ -799,6 +795,7 @@ private SlimefunItems() {} public static final SlimefunItemStack SWEET_BERRIES_ORGANIC_FOOD = new SlimefunItemStack("ORGANIC_FOOD_SWEET_BERRIES", HeadTexture.FILLED_CAN, ORGANIC_FOOD.getDisplayName(), "&7Content: &9Sweet Berries"); public static final SlimefunItemStack KELP_ORGANIC_FOOD = new SlimefunItemStack("ORGANIC_FOOD_KELP", HeadTexture.FILLED_CAN, ORGANIC_FOOD.getDisplayName(), "&7Content: &9Dried Kelp"); public static final SlimefunItemStack COCOA_ORGANIC_FOOD = new SlimefunItemStack("ORGANIC_FOOD_COCOA", HeadTexture.FILLED_CAN, ORGANIC_FOOD.getDisplayName(), "&7Content: &9Cocoa Beans"); + public static final SlimefunItemStack SEAGRASS_ORGANIC_FOOD = new SlimefunItemStack("ORGANIC_FOOD_SEAGRASS", HeadTexture.FILLED_CAN, ORGANIC_FOOD.getDisplayName(), "&7Content: &9Seagrass"); public static final SlimefunItemStack FERTILIZER = new SlimefunItemStack("FERTILIZER", HeadTexture.FILLED_CAN, "&aOrganic Fertilizer", "&7Content: &9???"); public static final SlimefunItemStack WHEAT_FERTILIZER = new SlimefunItemStack("FERTILIZER_WHEAT", HeadTexture.FILLED_CAN, FERTILIZER.getDisplayName(), "&7Content: &9Wheat"); @@ -811,6 +808,7 @@ private SlimefunItems() {} public static final SlimefunItemStack SWEET_BERRIES_FERTILIZER = new SlimefunItemStack("FERTILIZER_SWEET_BERRIES", HeadTexture.FILLED_CAN, FERTILIZER.getDisplayName(), "&7Content: &9Sweet Berries"); public static final SlimefunItemStack KELP_FERTILIZER = new SlimefunItemStack("FERTILIZER_KELP", HeadTexture.FILLED_CAN, FERTILIZER.getDisplayName(), "&7Content: &9Dried Kelp"); public static final SlimefunItemStack COCOA_FERTILIZER = new SlimefunItemStack("FERTILIZER_COCOA", HeadTexture.FILLED_CAN, FERTILIZER.getDisplayName(), "&7Content: &9Cocoa beans"); + public static final SlimefunItemStack SEAGRASS_FERTILIZER = new SlimefunItemStack("FERTILIZER_SEAGRASS", HeadTexture.FILLED_CAN, FERTILIZER.getDisplayName(), "&7Content: &9Seagrass"); public static final SlimefunItemStack ANIMAL_GROWTH_ACCELERATOR = new SlimefunItemStack("ANIMAL_GROWTH_ACCELERATOR", Material.HAY_BLOCK, "&bAnimal Growth Accelerator", "", "&fRuns on &aOrganic Food", "", LoreBuilder.machine(MachineTier.END_GAME, MachineType.MACHINE), LoreBuilder.powerBuffer(1024), LoreBuilder.powerPerSecond(28)); public static final SlimefunItemStack CROP_GROWTH_ACCELERATOR = new SlimefunItemStack("CROP_GROWTH_ACCELERATOR", Material.LIME_TERRACOTTA, "&aCrop Growth Accelerator", "", "&fRuns on &aOrganic Fertilizer", "", LoreBuilder.machine(MachineTier.END_GAME, MachineType.MACHINE), "&8\u21E8 &7Radius: 7x7", "&8\u21E8 &7Speed: &a3/time", LoreBuilder.powerBuffer(1024), LoreBuilder.powerPerSecond(50)); @@ -832,6 +830,7 @@ private SlimefunItems() {} public static final SlimefunItemStack FREEZER = new SlimefunItemStack("FREEZER", Material.LIGHT_BLUE_STAINED_GLASS, "&bFreezer", "", LoreBuilder.machine(MachineTier.ADVANCED, MachineType.MACHINE), LoreBuilder.speed(1), LoreBuilder.powerBuffer(256), LoreBuilder.powerPerSecond(18)); public static final SlimefunItemStack FREEZER_2 = new SlimefunItemStack("FREEZER_2", Material.LIGHT_BLUE_STAINED_GLASS, "&bFreezer &7(&eII&7)", "", LoreBuilder.machine(MachineTier.END_GAME, MachineType.MACHINE), LoreBuilder.speed(2), LoreBuilder.powerBuffer(256), LoreBuilder.powerPerSecond(30)); + public static final SlimefunItemStack FREEZER_3 = new SlimefunItemStack("FREEZER_3", Material.LIGHT_GRAY_STAINED_GLASS, "&bFreezer &7(&eIII&7)", "", LoreBuilder.machine(MachineTier.END_GAME, MachineType.MACHINE), LoreBuilder.speed(3), LoreBuilder.powerBuffer(256), LoreBuilder.powerPerSecond(42)); public static final SlimefunItemStack ELECTRIC_GOLD_PAN = new SlimefunItemStack("ELECTRIC_GOLD_PAN", Material.BROWN_TERRACOTTA, "&6Electric Gold Pan", "", LoreBuilder.machine(MachineTier.BASIC, MachineType.MACHINE), LoreBuilder.speed(1), LoreBuilder.powerPerSecond(2)); public static final SlimefunItemStack ELECTRIC_GOLD_PAN_2 = new SlimefunItemStack("ELECTRIC_GOLD_PAN_2", Material.BROWN_TERRACOTTA, "&6Electric Gold Pan &7(&eII&7)", "", LoreBuilder.machine(MachineTier.BASIC, MachineType.MACHINE), LoreBuilder.speed(3), LoreBuilder.powerPerSecond(4)); diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/StartupWarnings.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/StartupWarnings.java index a1d2f0c5f8..77b3a7479a 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/StartupWarnings.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/StartupWarnings.java @@ -24,14 +24,13 @@ private StartupWarnings() {} @ParametersAreNonnullByDefault static void discourageCSCoreLib(Logger logger) { - logger.log(Level.WARNING, BORDER); - logger.log(Level.WARNING, PREFIX + "It looks like you are still using CS-CoreLib."); - logger.log(Level.WARNING, PREFIX); - logger.log(Level.WARNING, PREFIX + "Slimefun no longer requires CS-CoreLib to be"); - logger.log(Level.WARNING, PREFIX + "installed as of January 30th 2021. It is safe"); - logger.log(Level.WARNING, PREFIX + "to remove and we recommend you to uninstall"); - logger.log(Level.WARNING, PREFIX + "CS-CoreLib from your server immediately."); - logger.log(Level.WARNING, BORDER); + logger.log(Level.SEVERE, BORDER); + logger.log(Level.SEVERE, PREFIX + "It looks like you are still using CS-CoreLib."); + logger.log(Level.SEVERE, PREFIX); + logger.log(Level.SEVERE, PREFIX + "Slimefun no longer requires CS-CoreLib to be"); + logger.log(Level.SEVERE, PREFIX + "installed as of January 30th 2021. You need to"); + logger.log(Level.SEVERE, PREFIX + "remove CS-CoreLib for Slimefun to run."); + logger.log(Level.SEVERE, BORDER); } @ParametersAreNonnullByDefault @@ -59,17 +58,17 @@ static void invalidServerSoftware(Logger logger) { } @ParametersAreNonnullByDefault - static void oldJavaVersion(Logger logger) { + static void oldJavaVersion(Logger logger, int recommendedJavaVersion) { int javaVersion = NumberUtils.getJavaVersion(); logger.log(Level.WARNING, BORDER); logger.log(Level.WARNING, PREFIX + "Your Java version (Java {0}) is out of date.", javaVersion); logger.log(Level.WARNING, PREFIX); - logger.log(Level.WARNING, PREFIX + "We recommend you to update to Java 16."); - logger.log(Level.WARNING, PREFIX + "Java 16 is required as of Minecraft 1.17 and"); - logger.log(Level.WARNING, PREFIX + "we would like to utilise all the new features"); + logger.log(Level.WARNING, PREFIX + "We recommend you to update to Java {0}.", recommendedJavaVersion); + logger.log(Level.WARNING, PREFIX + "Java {0} is required for newer versions of Minecraft", recommendedJavaVersion); + logger.log(Level.WARNING, PREFIX + "and we would like to utilise all the new features"); logger.log(Level.WARNING, PREFIX + "that come with it as soon as possible."); - logger.log(Level.WARNING, PREFIX + "Slimefun will also require Java 16 in"); + logger.log(Level.WARNING, PREFIX + "Slimefun will also require Java {0} in", recommendedJavaVersion); logger.log(Level.WARNING, PREFIX + "the foreseeable future, so please update!"); logger.log(Level.WARNING, BORDER); } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/guide/CheatSheetSlimefunGuide.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/guide/CheatSheetSlimefunGuide.java index 99b15f19cb..79ab38c662 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/guide/CheatSheetSlimefunGuide.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/guide/CheatSheetSlimefunGuide.java @@ -34,7 +34,7 @@ public class CheatSheetSlimefunGuide extends SurvivalSlimefunGuide { private final ItemStack item; public CheatSheetSlimefunGuide() { - super(false); + super(false, true); item = new SlimefunGuideItem(this, "&cSlimefun Guide &4(Cheat Sheet)"); } @@ -46,7 +46,7 @@ public CheatSheetSlimefunGuide() { * The {@link Player} who opened his {@link SlimefunGuide} * @param profile * The {@link PlayerProfile} of the {@link Player} - * + * * @return a {@link List} of visible {@link ItemGroup} instances */ @Override @@ -54,7 +54,7 @@ protected List getVisibleItemGroups(@Nonnull Player p, @Nonnull Playe List groups = new LinkedList<>(); for (ItemGroup group : Slimefun.getRegistry().getAllItemGroups()) { - if (!(group instanceof FlexItemGroup) || ((FlexItemGroup) group).isVisible(p, profile, getMode())) { + if (!(group instanceof FlexItemGroup flexItemGroup) || flexItemGroup.isVisible(p, profile, getMode())) { groups.add(group); } } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/guide/SurvivalSlimefunGuide.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/guide/SurvivalSlimefunGuide.java index a25cb5b352..21cee1d006 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/guide/SurvivalSlimefunGuide.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/guide/SurvivalSlimefunGuide.java @@ -14,7 +14,6 @@ import org.apache.commons.lang.Validate; import org.bukkit.ChatColor; import org.bukkit.Material; -import org.bukkit.Sound; import org.bukkit.Tag; import org.bukkit.entity.Player; import org.bukkit.inventory.Inventory; @@ -44,6 +43,7 @@ import io.github.thebusybiscuit.slimefun4.core.guide.options.SlimefunGuideSettings; import io.github.thebusybiscuit.slimefun4.core.multiblocks.MultiBlock; import io.github.thebusybiscuit.slimefun4.core.multiblocks.MultiBlockMachine; +import io.github.thebusybiscuit.slimefun4.core.services.sounds.SoundEffect; import io.github.thebusybiscuit.slimefun4.implementation.Slimefun; import io.github.thebusybiscuit.slimefun4.implementation.tasks.AsyncRecipeChoiceTask; import io.github.thebusybiscuit.slimefun4.utils.ChatUtils; @@ -67,27 +67,18 @@ public class SurvivalSlimefunGuide implements SlimefunGuideImplementation { private static final int MAX_ITEM_GROUPS = 36; - private static final Sound sound = Sound.ITEM_BOOK_PAGE_TURN; private final int[] recipeSlots = { 3, 4, 5, 12, 13, 14, 21, 22, 23 }; private final ItemStack item; private final boolean showVanillaRecipes; + private final boolean showHiddenItemGroupsInSearch; - public SurvivalSlimefunGuide(boolean showVanillaRecipes) { + public SurvivalSlimefunGuide(boolean showVanillaRecipes, boolean showHiddenItemGroupsInSearch) { this.showVanillaRecipes = showVanillaRecipes; + this.showHiddenItemGroupsInSearch = showHiddenItemGroupsInSearch; item = new SlimefunGuideItem(this, "&aSlimefun Guide &7(Chest GUI)"); } - /** - * This returns the {@link Sound} which is played when someone navigates through - * the {@link SlimefunGuide} - * - * @return The {@link Sound} - */ - public @Nonnull Sound getSound() { - return sound; - } - @Override public @Nonnull SlimefunGuideMode getMode() { return SlimefunGuideMode.SURVIVAL_MODE; @@ -117,8 +108,8 @@ protected final boolean isSurvivalMode() { for (ItemGroup group : Slimefun.getRegistry().getAllItemGroups()) { try { - if (group instanceof FlexItemGroup) { - if (((FlexItemGroup) group).isVisible(p, profile, getMode())) { + if (group instanceof FlexItemGroup flexItemGroup) { + if (flexItemGroup.isVisible(p, profile, getMode())) { groups.add(group); } } else if (!group.isHidden(p)) { @@ -231,8 +222,8 @@ public void openItemGroup(PlayerProfile profile, ItemGroup itemGroup, int page) return; } - if (itemGroup instanceof FlexItemGroup) { - ((FlexItemGroup) itemGroup).open(p, profile, getMode()); + if (itemGroup instanceof FlexItemGroup flexItemGroup) { + flexItemGroup.open(p, profile, getMode()); return; } @@ -309,15 +300,28 @@ private void displaySlimefunItem(ChestMenu menu, ItemGroup itemGroup, Player p, try { if (isSurvivalMode()) { displayItem(profile, sfitem, true); - } else { + } else if (pl.hasPermission("slimefun.cheat.items")) { if (sfitem instanceof MultiBlockMachine) { Slimefun.getLocalization().sendMessage(pl, "guide.cheat.no-multiblocks"); } else { - pl.getInventory().addItem(sfitem.getItem().clone()); + ItemStack clonedItem = sfitem.getItem().clone(); + + if (action.isShiftClicked()) { + clonedItem.setAmount(clonedItem.getMaxStackSize()); + } + + pl.getInventory().addItem(clonedItem); } + } else { + /* + * Fixes #3548 - If for whatever reason, + * an unpermitted players gets access to this guide, + * this will be our last line of defense to prevent any exploit. + */ + Slimefun.getLocalization().sendMessage(pl, "messages.no-permission", true); } } catch (Exception | LinkageError x) { - printErrorMessage(pl, x); + printErrorMessage(pl, sfitem, x); } return false; @@ -352,7 +356,7 @@ public void openSearch(PlayerProfile profile, String input, boolean addToHistory break; } - if (!slimefunItem.isHidden() && isSearchFilterApplicable(slimefunItem, searchTerm)) { + if (!slimefunItem.isHidden() && isItemGroupAccessible(p, slimefunItem) && isSearchFilterApplicable(slimefunItem, searchTerm)) { ItemStack itemstack = new CustomItemStack(slimefunItem.getItem(), meta -> { ItemGroup itemGroup = slimefunItem.getItemGroup(); meta.setLore(Arrays.asList("", ChatColor.DARK_GRAY + "\u21E8 " + ChatColor.WHITE + itemGroup.getDisplayName(p))); @@ -368,7 +372,7 @@ public void openSearch(PlayerProfile profile, String input, boolean addToHistory displayItem(profile, slimefunItem, true); } } catch (Exception | LinkageError x) { - printErrorMessage(pl, x); + printErrorMessage(pl, slimefunItem, x); } return false; @@ -381,6 +385,11 @@ public void openSearch(PlayerProfile profile, String input, boolean addToHistory menu.open(p); } + @ParametersAreNonnullByDefault + private boolean isItemGroupAccessible(Player p, SlimefunItem slimefunItem) { + return showHiddenItemGroupsInSearch || slimefunItem.getItemGroup().isAccessible(p); + } + @ParametersAreNonnullByDefault private boolean isSearchFilterApplicable(SlimefunItem slimefunItem, String searchTerm) { String itemName = ChatColor.stripColor(slimefunItem.getItemName()).toLowerCase(Locale.ROOT); @@ -473,19 +482,19 @@ private void showMinecraftRecipe(Recipe[] recipes, int index, ItemStack item, Pl private void showRecipeChoices(T recipe, ItemStack[] recipeItems, AsyncRecipeChoiceTask task) { RecipeChoice[] choices = Slimefun.getMinecraftRecipeService().getRecipeShape(recipe); - if (choices.length == 1 && choices[0] instanceof MaterialChoice) { - recipeItems[4] = new ItemStack(((MaterialChoice) choices[0]).getChoices().get(0)); + if (choices.length == 1 && choices[0] instanceof MaterialChoice materialChoice) { + recipeItems[4] = new ItemStack(materialChoice.getChoices().get(0)); - if (((MaterialChoice) choices[0]).getChoices().size() > 1) { - task.add(recipeSlots[4], (MaterialChoice) choices[0]); + if (materialChoice.getChoices().size() > 1) { + task.add(recipeSlots[4], materialChoice); } } else { for (int i = 0; i < choices.length; i++) { - if (choices[i] instanceof MaterialChoice) { - recipeItems[i] = new ItemStack(((MaterialChoice) choices[i]).getChoices().get(0)); + if (choices[i] instanceof MaterialChoice materialChoice) { + recipeItems[i] = new ItemStack(materialChoice.getChoices().get(0)); - if (((MaterialChoice) choices[i]).getChoices().size() > 1) { - task.add(recipeSlots[i], (MaterialChoice) choices[i]); + if (materialChoice.getChoices().size() > 1) { + task.add(recipeSlots[i], materialChoice); } } } @@ -525,8 +534,8 @@ public void displayItem(PlayerProfile profile, SlimefunItem item, boolean addToH displayItem(menu, profile, p, item, result, recipeType, recipe, task); - if (item instanceof RecipeDisplayItem) { - displayRecipes(p, profile, menu, (RecipeDisplayItem) item, 0); + if (item instanceof RecipeDisplayItem recipeDisplayItem) { + displayRecipes(p, profile, menu, recipeDisplayItem, 0); } menu.open(p); @@ -663,7 +672,7 @@ private void displayRecipes(Player p, PlayerProfile profile, ChestMenu menu, Rec menu.addMenuClickHandler(28, (pl, slot, itemstack, action) -> { if (page > 0) { displayRecipes(pl, profile, menu, sfItem, page - 1); - pl.playSound(pl.getLocation(), sound, 1, 1); + SoundEffect.GUIDE_BUTTON_CLICK_SOUND.playFor(pl); } return false; @@ -673,7 +682,7 @@ private void displayRecipes(Player p, PlayerProfile profile, ChestMenu menu, Rec menu.addMenuClickHandler(34, (pl, slot, itemstack, action) -> { if (recipes.size() > (18 * (page + 1))) { displayRecipes(pl, profile, menu, sfItem, page + 1); - pl.playSound(pl.getLocation(), sound, 1, 1); + SoundEffect.GUIDE_BUTTON_CLICK_SOUND.playFor(pl); } return false; @@ -733,7 +742,7 @@ private static boolean hasPermission(Player p, SlimefunItem item) { ChestMenu menu = new ChestMenu(Slimefun.getLocalization().getMessage(p, "guide.title.main")); menu.setEmptySlotsClickable(false); - menu.addMenuOpeningHandler(pl -> pl.playSound(pl.getLocation(), sound, 1, 1)); + menu.addMenuOpeningHandler(SoundEffect.GUIDE_BUTTON_CLICK_SOUND::playFor); return menu; } @@ -743,4 +752,10 @@ private void printErrorMessage(Player p, Throwable x) { Slimefun.logger().log(Level.SEVERE, "An error has occurred while trying to open a SlimefunItem in the guide!", x); } + @ParametersAreNonnullByDefault + private void printErrorMessage(Player p, SlimefunItem item, Throwable x) { + p.sendMessage(ChatColor.DARK_RED + "An internal server error has occurred. Please inform an admin, check the console for further info."); + item.error("This item has caused an error message to be thrown while viewing it in the Slimefun guide.", x); + } + } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/handlers/VanillaInventoryDropHandler.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/handlers/VanillaInventoryDropHandler.java index a31d718a70..7ead892718 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/handlers/VanillaInventoryDropHandler.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/handlers/VanillaInventoryDropHandler.java @@ -78,8 +78,8 @@ public void onPlayerBreak(BlockBreakEvent e, ItemStack item, List dro @Nonnull protected Inventory getInventory(@Nonnull T inventoryHolder) { - if (inventoryHolder instanceof Chest) { - return ((Chest) inventoryHolder).getBlockInventory(); + if (inventoryHolder instanceof Chest chest) { + return chest.getBlockInventory(); } else { return inventoryHolder.getInventory(); } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/handlers/package-info.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/handlers/package-info.java index a0770977f4..2afaa20dce 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/handlers/package-info.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/handlers/package-info.java @@ -1,5 +1,5 @@ /** - * This package holds simple implementations of {@link me.mrCookieSlime.Slimefun.Objects.handlers.ItemHandler}. + * This package holds simple implementations of {@link io.github.thebusybiscuit.slimefun4.api.items.ItemHandler}. * These are just handlers that can be re-used frequently. */ package io.github.thebusybiscuit.slimefun4.implementation.handlers; \ No newline at end of file diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/EnchantedItem.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/EnchantedItem.java new file mode 100644 index 0000000000..a7525c9971 --- /dev/null +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/EnchantedItem.java @@ -0,0 +1,30 @@ +package io.github.thebusybiscuit.slimefun4.implementation.items; + +import javax.annotation.ParametersAreNonnullByDefault; + +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +import io.github.thebusybiscuit.slimefun4.api.items.ItemGroup; +import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItem; +import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItemStack; +import io.github.thebusybiscuit.slimefun4.api.recipes.RecipeType; +import io.github.thebusybiscuit.slimefun4.core.handlers.ItemUseHandler; +import io.github.thebusybiscuit.slimefun4.implementation.Slimefun; + +/** + * The {@link EnchantedItem} is an enchanted {@link SlimefunItem}. + * By default, this class sets items to be not disenchantable. + * + * @author Fury_Phoenix + * + */ +public class EnchantedItem extends SlimefunItem { + + @ParametersAreNonnullByDefault + public EnchantedItem(ItemGroup itemGroup, SlimefunItemStack item, RecipeType recipeType, ItemStack[] recipe) { + super(itemGroup, item, recipeType, recipe); + disenchantable = false; + } + +} diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/HiddenItem.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/HiddenItem.java new file mode 100644 index 0000000000..dcfaa8a103 --- /dev/null +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/HiddenItem.java @@ -0,0 +1,27 @@ +package io.github.thebusybiscuit.slimefun4.implementation.items; + +import javax.annotation.ParametersAreNonnullByDefault; + +import org.bukkit.inventory.ItemStack; + +import io.github.thebusybiscuit.slimefun4.api.items.ItemGroup; +import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItem; +import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItemStack; +import io.github.thebusybiscuit.slimefun4.api.recipes.RecipeType; +import io.github.thebusybiscuit.slimefun4.core.attributes.NotConfigurable; + +/** + * The {@link HiddenItem} is a {@link NotConfigurable} {@link SlimefunItem} + * that is hidden from the Slimefun guide. + * + * @author char321 + * + */ +public class HiddenItem extends SlimefunItem implements NotConfigurable { + + @ParametersAreNonnullByDefault + public HiddenItem(ItemGroup itemGroup, SlimefunItemStack item, RecipeType recipeType, ItemStack[] recipe) { + super(itemGroup, item, recipeType, recipe); + this.setHidden(true); + } +} diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/LimitedUseItem.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/LimitedUseItem.java index 173fb39813..c4377d4585 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/LimitedUseItem.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/LimitedUseItem.java @@ -9,7 +9,6 @@ import org.apache.commons.lang.Validate; import org.bukkit.Material; import org.bukkit.NamespacedKey; -import org.bukkit.Sound; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.ItemMeta; @@ -22,6 +21,7 @@ import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItemStack; import io.github.thebusybiscuit.slimefun4.api.recipes.RecipeType; import io.github.thebusybiscuit.slimefun4.core.handlers.ItemUseHandler; +import io.github.thebusybiscuit.slimefun4.core.services.sounds.SoundEffect; import io.github.thebusybiscuit.slimefun4.implementation.Slimefun; import io.github.thebusybiscuit.slimefun4.implementation.items.magical.staves.StormStaff; import io.github.thebusybiscuit.slimefun4.utils.LoreBuilder; @@ -66,10 +66,7 @@ public final int getMaxUseCount() { * * @param count * The maximum number of times this item can be used. - * <<<<<<< HEAD - * ======= * - * >>>>>>> branch 'master' of https://github.com/Slimefun/Slimefun4.git * @return The {@link LimitedUseItem} for chaining of setters */ public final @Nonnull LimitedUseItem setMaxUseCount(int count) { @@ -119,7 +116,7 @@ protected void damageItem(Player p, ItemStack item) { int usesLeft = pdc.getOrDefault(key, PersistentDataType.INTEGER, getMaxUseCount()); if (usesLeft == 1) { - p.playSound(p.getLocation(), Sound.ENTITY_ITEM_BREAK, 1, 1); + SoundEffect.LIMITED_USE_ITEM_BREAK_SOUND.playFor(p); item.setAmount(0); item.setType(Material.AIR); } else { diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/VanillaItem.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/VanillaItem.java index 0d557bf680..7d6291b91a 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/VanillaItem.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/VanillaItem.java @@ -30,7 +30,7 @@ public class VanillaItem extends SlimefunItem { * Instantiates a new {@link VanillaItem} with the given arguments. * * @param itemGroup - * the {@Link ItemGroup} to bind this {@link VanillaItem} to + * the {@link ItemGroup} to bind this {@link VanillaItem} to * @param item * the item corresponding to this {@link VanillaItem} * @param id diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/altar/AncientPedestal.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/altar/AncientPedestal.java index a3408efc2f..a95bbbbbe6 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/altar/AncientPedestal.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/altar/AncientPedestal.java @@ -8,7 +8,6 @@ import org.bukkit.GameMode; import org.bukkit.Location; -import org.bukkit.Sound; import org.bukkit.block.Block; import org.bukkit.entity.Entity; import org.bukkit.entity.Item; @@ -26,6 +25,7 @@ import io.github.thebusybiscuit.slimefun4.api.recipes.RecipeType; import io.github.thebusybiscuit.slimefun4.core.handlers.BlockBreakHandler; import io.github.thebusybiscuit.slimefun4.core.handlers.BlockDispenseHandler; +import io.github.thebusybiscuit.slimefun4.core.services.sounds.SoundEffect; import io.github.thebusybiscuit.slimefun4.implementation.Slimefun; import io.github.thebusybiscuit.slimefun4.implementation.handlers.SimpleBlockBreakHandler; import io.github.thebusybiscuit.slimefun4.implementation.items.SimpleSlimefunItem; @@ -86,8 +86,8 @@ public void onBlockBreak(@Nonnull Block b) { Location l = pedestal.getLocation().add(0.5, 1.2, 0.5); for (Entity n : l.getWorld().getNearbyEntities(l, 0.5, 0.5, 0.5, this::testItem)) { - if (n instanceof Item) { - return Optional.of((Item) n); + if (n instanceof Item item) { + return Optional.of(item); } } @@ -95,8 +95,7 @@ public void onBlockBreak(@Nonnull Block b) { } private boolean testItem(@Nullable Entity n) { - if (n instanceof Item && n.isValid()) { - Item item = (Item) n; + if (n instanceof Item item && n.isValid()) { ItemMeta meta = item.getItemStack().getItemMeta(); return meta.hasDisplayName() && meta.getDisplayName().startsWith(ITEM_PREFIX); @@ -141,7 +140,7 @@ public void placeItem(@Nonnull Player p, @Nonnull Block b) { entity.setCustomNameVisible(true); entity.setCustomName(nametag); SlimefunUtils.markAsNoPickup(entity, "altar_item"); - p.playSound(b.getLocation(), Sound.ENTITY_ITEM_PICKUP, 0.3F, 0.3F); + SoundEffect.ANCIENT_PEDESTAL_ITEM_PLACE_SOUND.playAt(b); } } } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/altar/package-info.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/altar/package-info.java index 87e7c4b7ae..5e588c64bb 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/altar/package-info.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/altar/package-info.java @@ -1,5 +1,5 @@ /** - * This package holds the {@link me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem} implementations related to + * This package holds the {@link io.github.thebusybiscuit.slimefun4.api.items.SlimefunItem} implementations related to * the {@link io.github.thebusybiscuit.slimefun4.implementation.items.altar.AncientAltar}. */ package io.github.thebusybiscuit.slimefun4.implementation.items.altar; \ No newline at end of file diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/androids/AndroidType.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/androids/AndroidType.java index fbbbdb108c..c4fa1a0308 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/androids/AndroidType.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/androids/AndroidType.java @@ -46,7 +46,7 @@ public enum AndroidType { FIGHTER, /** - * The {@link FisherAndroid} can catch a fish and other materials. + * The {@link FishermanAndroid} can catch a fish and other materials. */ FISHERMAN, diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/androids/ButcherAndroid.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/androids/ButcherAndroid.java index 3b9aed0a2f..76e43e65fa 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/androids/ButcherAndroid.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/androids/ButcherAndroid.java @@ -2,6 +2,8 @@ import java.util.function.Predicate; +import javax.annotation.ParametersAreNonnullByDefault; + import org.bukkit.block.Block; import org.bukkit.block.BlockFace; import org.bukkit.entity.ArmorStand; @@ -20,6 +22,7 @@ public class ButcherAndroid extends ProgrammableAndroid { private static final String METADATA_KEY = "android_killer"; + @ParametersAreNonnullByDefault public ButcherAndroid(ItemGroup itemGroup, int tier, SlimefunItemStack item, RecipeType recipeType, ItemStack[] recipe) { super(itemGroup, tier, item, recipeType, recipe); } @@ -34,27 +37,17 @@ protected void attack(Block b, BlockFace face, Predicate predicate double damage = getTier() >= 3 ? 20D : 4D * getTier(); double radius = 4.0 + getTier(); - for (Entity n : b.getWorld().getNearbyEntities(b.getLocation(), radius, radius, radius, n -> n instanceof LivingEntity && !(n instanceof ArmorStand) && !(n instanceof Player) && n.isValid() && predicate.test((LivingEntity) n))) { - boolean attack = false; - - switch (face) { - case NORTH: - attack = n.getLocation().getZ() < b.getZ(); - break; - case EAST: - attack = n.getLocation().getX() > b.getX(); - break; - case SOUTH: - attack = n.getLocation().getZ() > b.getZ(); - break; - case WEST: - attack = n.getLocation().getX() < b.getX(); - break; - default: - break; - } - - if (attack) { + for (Entity n : b.getWorld().getNearbyEntities(b.getLocation(), radius, radius, radius, n -> n instanceof LivingEntity livingEntity && !(n instanceof ArmorStand) && !(n instanceof Player) && n.isValid() && predicate.test(livingEntity))) { + // Check if our android is facing this entity. + boolean willAttack = switch (face) { + case NORTH -> n.getLocation().getZ() < b.getZ(); + case EAST -> n.getLocation().getX() > b.getX(); + case SOUTH -> n.getLocation().getZ() > b.getZ(); + case WEST -> n.getLocation().getX() < b.getX(); + default -> false; + }; + + if (willAttack) { if (n.hasMetadata(METADATA_KEY)) { n.removeMetadata(METADATA_KEY, Slimefun.instance()); } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/androids/FarmerAndroid.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/androids/FarmerAndroid.java index 2f5e225580..cd8b72c723 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/androids/FarmerAndroid.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/androids/FarmerAndroid.java @@ -41,8 +41,8 @@ protected void farm(Block b, BlockMenu menu, Block block, boolean isAdvanced) { if (!block.getWorld().getWorldBorder().isInside(block.getLocation())) { return; } - - if (data instanceof Ageable && ((Ageable) data).getAge() >= ((Ageable) data).getMaximumAge()) { + + if (data instanceof Ageable ageable && ageable.getAge() >= ageable.getMaximumAge()) { drop = getDropFromCrop(blockType); } @@ -57,8 +57,8 @@ protected void farm(Block b, BlockMenu menu, Block block, boolean isAdvanced) { if (drop != null && menu.pushItem(drop, getOutputSlots()) == null) { block.getWorld().playEffect(block.getLocation(), Effect.STEP_SOUND, blockType); - if (data instanceof Ageable) { - ((Ageable) data).setAge(0); + if (data instanceof Ageable ageable) { + ageable.setAge(0); block.setBlockData(data); } } @@ -68,24 +68,16 @@ protected void farm(Block b, BlockMenu menu, Block block, boolean isAdvanced) { private ItemStack getDropFromCrop(Material crop) { Random random = ThreadLocalRandom.current(); - switch (crop) { - case WHEAT: - return new ItemStack(Material.WHEAT, random.nextInt(2) + 1); - case POTATOES: - return new ItemStack(Material.POTATO, random.nextInt(3) + 1); - case CARROTS: - return new ItemStack(Material.CARROT, random.nextInt(3) + 1); - case BEETROOTS: - return new ItemStack(Material.BEETROOT, random.nextInt(3) + 1); - case COCOA: - return new ItemStack(Material.COCOA_BEANS, random.nextInt(3) + 1); - case NETHER_WART: - return new ItemStack(Material.NETHER_WART, random.nextInt(3) + 1); - case SWEET_BERRY_BUSH: - return new ItemStack(Material.SWEET_BERRIES, random.nextInt(3) + 1); - default: - return null; - } + return switch (crop) { + case WHEAT -> new ItemStack(Material.WHEAT, random.nextInt(2) + 1); + case POTATOES -> new ItemStack(Material.POTATO, random.nextInt(3) + 1); + case CARROTS -> new ItemStack(Material.CARROT, random.nextInt(3) + 1); + case BEETROOTS -> new ItemStack(Material.BEETROOT, random.nextInt(3) + 1); + case COCOA -> new ItemStack(Material.COCOA_BEANS, random.nextInt(3) + 1); + case NETHER_WART -> new ItemStack(Material.NETHER_WART, random.nextInt(3) + 1); + case SWEET_BERRY_BUSH -> new ItemStack(Material.SWEET_BERRIES, random.nextInt(3) + 1); + default -> null; + }; } } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/androids/FisherAndroid.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/androids/FishermanAndroid.java similarity index 83% rename from src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/androids/FisherAndroid.java rename to src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/androids/FishermanAndroid.java index 1e02d3391d..a6ce80e2fe 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/androids/FisherAndroid.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/androids/FishermanAndroid.java @@ -2,8 +2,9 @@ import java.util.concurrent.ThreadLocalRandom; +import javax.annotation.ParametersAreNonnullByDefault; + import org.bukkit.Material; -import org.bukkit.Sound; import org.bukkit.Tag; import org.bukkit.block.Block; import org.bukkit.block.BlockFace; @@ -13,14 +14,16 @@ import io.github.thebusybiscuit.slimefun4.api.items.ItemGroup; import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItemStack; import io.github.thebusybiscuit.slimefun4.api.recipes.RecipeType; +import io.github.thebusybiscuit.slimefun4.core.services.sounds.SoundEffect; import me.mrCookieSlime.Slimefun.api.inventory.BlockMenu; -public class FisherAndroid extends ProgrammableAndroid { +public class FishermanAndroid extends ProgrammableAndroid { private final RandomizedSet fishingLoot = new RandomizedSet<>(); - public FisherAndroid(ItemGroup itemGroup, int tier, SlimefunItemStack item, RecipeType recipeType, ItemStack[] recipe) { + @ParametersAreNonnullByDefault + public FishermanAndroid(ItemGroup itemGroup, int tier, SlimefunItemStack item, RecipeType recipeType, ItemStack[] recipe) { super(itemGroup, tier, item, recipeType, recipe); // Fish @@ -54,14 +57,12 @@ protected void fish(Block b, BlockMenu menu) { Block water = b.getRelative(BlockFace.DOWN); if (water.getType() == Material.WATER) { - water.getWorld().playSound(water.getLocation(), Sound.ENTITY_PLAYER_SPLASH, 0.3F, 0.7F); + SoundEffect.FISHERMAN_ANDROID_FISHING_SOUND.playAt(water); if (ThreadLocalRandom.current().nextInt(100) < 10 * getTier()) { ItemStack drop = fishingLoot.getRandom(); menu.pushItem(drop.clone(), getOutputSlots()); } - } } - } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/androids/Instruction.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/androids/Instruction.java index 13b55f4c3b..03fba08cff 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/androids/Instruction.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/androids/Instruction.java @@ -182,7 +182,7 @@ public enum Instruction { CHOP_TREE(AndroidType.WOODCUTTER, HeadTexture.SCRIPT_CHOP_TREE), /** - * This {@link Instruction} makes a {@link FisherAndroid} try to catch fish from + * This {@link Instruction} makes a {@link FishermanAndroid} try to catch fish from * the water below. */ CATCH_FISH(AndroidType.FISHERMAN, HeadTexture.SCRIPT_FISH, (android, b, inv, face) -> android.fish(b, inv)), diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/androids/MinerAndroid.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/androids/MinerAndroid.java index ebb0c7e68b..be397c5055 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/androids/MinerAndroid.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/androids/MinerAndroid.java @@ -11,7 +11,6 @@ import org.bukkit.Material; import org.bukkit.OfflinePlayer; import org.bukkit.Particle; -import org.bukkit.Sound; import org.bukkit.block.Block; import org.bukkit.block.BlockFace; import org.bukkit.inventory.ItemStack; @@ -22,6 +21,7 @@ import io.github.thebusybiscuit.slimefun4.api.items.ItemSetting; import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItemStack; import io.github.thebusybiscuit.slimefun4.api.recipes.RecipeType; +import io.github.thebusybiscuit.slimefun4.core.services.sounds.SoundEffect; import io.github.thebusybiscuit.slimefun4.implementation.Slimefun; import io.github.thebusybiscuit.slimefun4.utils.InfiniteBlockGenerator; import io.github.thebusybiscuit.slimefun4.utils.tags.SlimefunTag; @@ -125,13 +125,13 @@ protected void moveAndDig(Block b, BlockMenu menu, BlockFace face, Block block) @ParametersAreNonnullByDefault private void breakBlock(BlockMenu menu, Collection drops, Block block) { - + if (!block.getWorld().getWorldBorder().isInside(block.getLocation())) { return; } - + block.getWorld().playEffect(block.getLocation(), Effect.STEP_SOUND, block.getType()); - + // Push our drops to the inventory for (ItemStack drop : drops) { menu.pushItem(drop, getOutputSlots()); @@ -148,7 +148,7 @@ private void breakBlock(BlockMenu menu, Collection drops, Block block } // "poof" a "new" block was generated - block.getWorld().playSound(block.getLocation(), Sound.BLOCK_FIRE_EXTINGUISH, 0.075F, 0.8F); + SoundEffect.MINER_ANDROID_BLOCK_GENERATION_SOUND.playAt(block); block.getWorld().spawnParticle(Particle.SMOKE_NORMAL, block.getX() + 0.5, block.getY() + 1.25, block.getZ() + 0.5, 8, 0.5, 0.5, 0.5, 0.015); } else { block.setType(Material.AIR); diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/androids/ProgrammableAndroid.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/androids/ProgrammableAndroid.java index 89b3d04737..471551575b 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/androids/ProgrammableAndroid.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/androids/ProgrammableAndroid.java @@ -14,7 +14,6 @@ import org.bukkit.ChatColor; import org.bukkit.Location; import org.bukkit.Material; -import org.bukkit.Sound; import org.bukkit.Tag; import org.bukkit.block.Block; import org.bukkit.block.BlockFace; @@ -45,6 +44,7 @@ import io.github.thebusybiscuit.slimefun4.core.attributes.RecipeDisplayItem; import io.github.thebusybiscuit.slimefun4.core.handlers.BlockBreakHandler; import io.github.thebusybiscuit.slimefun4.core.handlers.BlockPlaceHandler; +import io.github.thebusybiscuit.slimefun4.core.services.sounds.SoundEffect; import io.github.thebusybiscuit.slimefun4.implementation.Slimefun; import io.github.thebusybiscuit.slimefun4.implementation.SlimefunItems; import io.github.thebusybiscuit.slimefun4.utils.ChestMenuUtils; @@ -94,7 +94,7 @@ public void init() { @Override public boolean canOpen(Block b, Player p) { - boolean open = BlockStorage.getLocationInfo(b.getLocation(), "owner").equals(p.getUniqueId().toString()) || p.hasPermission("slimefun.android.bypass"); + boolean open = p.getUniqueId().toString().equals(BlockStorage.getLocationInfo(b.getLocation(), "owner")) || p.hasPermission("slimefun.android.bypass"); if (!open) { Slimefun.getLocalization().sendMessage(p, "inventory.no-access", true); @@ -155,8 +155,8 @@ public void onPlayerPlace(BlockPlaceEvent e) { BlockStorage.addBlockInfo(b, "paused", "true"); BlockData blockData = Material.PLAYER_HEAD.createBlockData(data -> { - if (data instanceof Rotatable) { - ((Rotatable) data).setRotation(p.getFacing()); + if (data instanceof Rotatable rotatable) { + rotatable.setRotation(p.getFacing()); } }); @@ -206,16 +206,12 @@ public AndroidType getAndroidType() { * @return The required type of fuel */ public AndroidFuelSource getFuelSource() { - switch (getTier()) { - case 1: - return AndroidFuelSource.SOLID; - case 2: - return AndroidFuelSource.LIQUID; - case 3: - return AndroidFuelSource.NUCLEAR; - default: - throw new IllegalStateException("Cannot convert the following Android tier to a fuel type: " + getTier()); - } + return switch (getTier()) { + case 1 -> AndroidFuelSource.SOLID; + case 2 -> AndroidFuelSource.LIQUID; + case 3 -> AndroidFuelSource.NUCLEAR; + default -> throw new IllegalStateException("Cannot convert the following Android tier to a fuel type: " + getTier()); + }; } @Override @@ -377,7 +373,7 @@ protected void openScriptDownloader(Player p, Block b, int page) { ChestMenu menu = new ChestMenu("Android Scripts"); menu.setEmptySlotsClickable(false); - menu.addMenuOpeningHandler(pl -> pl.playSound(pl.getLocation(), Sound.BLOCK_NOTE_BLOCK_HAT, 0.7F, 0.7F)); + menu.addMenuOpeningHandler(SoundEffect.PROGRAMMABLE_ANDROID_SCRIPT_DOWNLOAD_SOUND::playFor); List