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