Compare commits

..
3 Commits
Author SHA1 Message Date
Eric Stokes 3db789fb17 Added stuff 2012-10-18 18:36:25 -06:00
Eric Stokes bf4119c6ee Create gh-pages branch via GitHub 2012-10-18 17:16:43 -07:00
Eric Stokes 6a056a0c31 Created gh-pages branch via GitHub 2012-01-02 08:47:29 -08:00
409 changed files with 739 additions and 40187 deletions

No files matched your search

-1
View File
@@ -1 +0,0 @@
.gitea/CODEOWNERS linguist-detectable=false
-3
View File
@@ -1,3 +0,0 @@
# Pull requests request review from the eligible staff team(s).
# One approval from either an Admin or Developer satisfies branch protection.
* @trueog/Admins @trueog/Developers
-2
View File
@@ -1,2 +0,0 @@
github: Multiverse
open_collective: multiverse-plugins
-77
View File
@@ -1,77 +0,0 @@
name: 🐛 Report a Bug
description: Report a problem with Multiverse-Core.
title: "[Bug]: "
labels: ["Bug: Unconfirmed"]
body:
- type: markdown
attributes:
value: |
Thanks for taking the time to fill out this bug report!
If you are having issues with other submodules of Multiverse, please go to their respective git repo:
Multiverse-Portals: https://github.com/Multiverse/Multiverse-Portals/issues/new/choose
Multiverse-NetherPortals: https://github.com/Multiverse/Multiverse-NetherPortals/issues/new/choose
Multiverse-Inventories: https://github.com/Multiverse/Multiverse-Inventories/issues/new/choose
Multiverse-SignPortals: https://github.com/Multiverse/Multiverse-SignPortals/issues/new/choose
Not sure if this is a bug with Multiverse-Core? Feel free to ask on our Discord server: https://discord.gg/NZtfKky
When completing the report, please ensure all fields are filled with the correct details requested. Failure to do so will result in your issue being closed.
- type: input
id: multiverse-version-info
attributes:
label: '`/mv dumps` output'
description: Run `/mv dumps` in the console, then copy and paste the link from the output of the command into this box. If `/mv dumps` command is not available, update multiverse to v5.
validations:
required: true
- type: input
id: server-logs
attributes:
label: Server logs
description: >
Include full startup log and console error (if any). upload all relevant logs to either https://pastes.dev
or https://gist.github.com, save and the paste the link in this box.
validations:
required: true
- type: textarea
id: server-version
attributes:
label: Server Version
description: Output of `/version` command.
placeholder: server version info
render: shell
validations:
required: true
- type: textarea
id: bug-description
attributes:
label: Bug Description
description: Describe in detail what the bug is about. Include the expected behaviour and actual behaviour.
placeholder: "Example: When running `/mv` it cause the server to crash..."
validations:
required: true
- type: textarea
id: reproduce-steps
attributes:
label: Steps to reproduce
description: Provide detailed instructions to trigger the bug.
placeholder: |
Example:
1. Start a server with only multiverse installed.
2. Run `/mv` command.
3. Players all get kicked with a "server closed" message.
...
validations:
required: true
- type: checkboxes
id: agreements
attributes:
label: Agreements
description: Ensure you have done the following being submitting this issue.
options:
- label: I have searched for and ensured there isn't already an open or resolved issue(s) regarding this.
required: true
- label: >
I was able to reproduce my issue on a freshly setup and up-to-date server with the latest
version of Multiverse plugins with no other plugins and with no kinds of other server or client mods.
required: true
-5
View File
@@ -1,5 +0,0 @@
blank_issues_enabled: false
contact_links:
- name: 💬 Multiverse Discord Server
url: https://discord.gg/NZtfKky
about: Need help with using Multiverse-Core or just want to chat with the devs? Join the Multiverse Discord Server for help!
@@ -1,41 +0,0 @@
name: 💡 Request a Feature
description: Suggest a feature you want to see in Multiverse-Core!
title: "[Idea]: "
labels: ["Type: Idea"]
body:
- type: markdown
attributes:
value: |
Thanks for taking the time to share your ideas!
If you are look for other submodules of Multiverse, go to their respective git repo:
Multiverse-Portals: https://github.com/Multiverse/Multiverse-Portals/issues/new/choose
Multiverse-NetherPortals: https://github.com/Multiverse/Multiverse-NetherPortals/issues/new/choose
Multiverse-Inventories: https://github.com/Multiverse/Multiverse-Inventories/issues/new/choose
Multiverse-SignPortals: https://github.com/Multiverse/Multiverse-SignPortals/issues/new/choose
Not sure if your feature fits in Multiverse-Core? Feel free to ask on our Discord server: https://discord.gg/NZtfKky
- type: textarea
id: feature-description
attributes:
label: Describe the feature
description: What feature are you suggesting?
placeholder: "Example: I hope multiverse can implement random teleport feature..."
validations:
required: true
- type: textarea
id: feature-reasoning
attributes:
label: How is the feature useful to all Multiverse users?
description: How is the feature useful to all players, server owners and/or developers?
placeholder: "Example: Random teleporting can be integrated into many multiverse modules such as mv-portals..."
validations:
required: true
- type: checkboxes
id: agreements
attributes:
label: Agreements
description: Ensure you have done the following being submitting this issue.
options:
- label: I have searched for and ensured there isn't already an open issue regarding this.
required: true
-34
View File
@@ -1,34 +0,0 @@
name: 'Call: GitHub Release'
on:
workflow_call:
inputs:
release_mode:
description: 'Release mode'
required: true
type: string
version_bump:
description: 'Version bump'
required: false
type: string
promote_from:
description: 'Promote from'
required: false
type: string
outputs:
release_created:
description: 'Release created'
value: ${{ jobs.github_release.outputs.release_created }}
tag_name:
description: 'Tag name'
value: ${{ jobs.github_release.outputs.tag_name }}
jobs:
github_release:
uses: ./.github/workflows/generic.github_release.yml
secrets: inherit
with:
plugin_name: multiverse-core
release_mode: ${{ inputs.release_mode }}
version_bump: ${{ inputs.version_bump }}
promote_from: ${{ inputs.promote_from }}
@@ -1,83 +0,0 @@
name: 'Call: Platform Uploads'
on:
workflow_call:
inputs:
target_tag:
description: 'Version to upload'
required: true
type: string
upload_modrinth:
description: 'Upload to modrinth.com'
required: true
type: string
upload_dbo:
description: 'Upload to dev.bukkit.org'
required: true
type: string
upload_hangar:
description: 'Upload to hangar.papermc.io'
required: true
type: string
secrets:
MODRINTH_TOKEN:
required: true
DBO_UPLOAD_API_TOKEN:
required: true
HANGAR_UPLOAD_TOKEN:
required: true
jobs:
platform_uploads:
uses: ./.github/workflows/generic.platform_uploads.yml
secrets: inherit
with:
plugin_name: multiverse-core
modrinth_project_id: 3wmN97b8
modrinth_dependencies: >
[
{"project_id": "qvdtDX3s", "dependency_type": "optional"},
{"project_id": "8VMk6P0I", "dependency_type": "optional"},
{"project_id": "vtawPsTo", "dependency_type": "optional"},
{"project_id": "WuErDeI1", "dependency_type": "optional"}
]
dbo_project_id: 30765
dbo_project_relations: >
[
{"slug": "multiverse-inventories", "type": "optionalDependency"},
{"slug": "multiverse-portals", "type": "optionalDependency"},
{"slug": "multiverse-netherportals", "type": "optionalDependency"},
{"slug": "multiverse-signportals", "type": "optionalDependency"},
{"slug": "vault", "type": "optionalDependency"}
]
hangar_slug: Multiverse-Core
hangar_plugin_dependencies: >
{ "PAPER": [
{
"name": "Multiverse-Inventories",
"required": false,
"platforms": ["PAPER"]
},
{
"name": "Multiverse-Portals",
"required": false,
"platforms": ["PAPER"]
},
{
"name": "Multiverse-NetherPortals",
"required": false,
"platforms": ["PAPER"]
},
{
"name": "Multiverse-SignPortals",
"required": false,
"platforms": ["PAPER"]
}
]}
target_tag: ${{ inputs.target_tag }}
upload_modrinth: ${{ inputs.upload_modrinth }}
upload_dbo: ${{ inputs.upload_dbo }}
upload_hangar: ${{ inputs.upload_hangar }}
@@ -1,39 +0,0 @@
name: 'Dispatch: Create Release'
on:
workflow_dispatch:
inputs:
release_type:
description: 'Stable or pre-release'
required: true
type: choice
options:
- prerelease
- release
version_bump:
description: 'Version bump type'
required: true
type: choice
options:
- major
- minor
- patch
jobs:
github_release:
uses: ./.github/workflows/call.github_release.yml
secrets: inherit
with:
release_mode: ${{ inputs.release_type }}
version_bump: ${{ inputs.version_bump }}
platform_uploads:
needs: github_release
if: needs.github_release.outputs.release_created == 'true'
uses: ./.github/workflows/call.platform_uploads.yml
secrets: inherit
with:
target_tag: ${{ needs.github_release.outputs.tag_name }}
upload_modrinth: 'true'
upload_dbo: 'false'
upload_hangar: 'false'
-55
View File
@@ -1,55 +0,0 @@
name: 'Dispatch: Deploy Javadoc'
on:
workflow_dispatch:
inputs:
target_tag:
description: 'Version to generate javadoc'
required: true
replace_latest:
description: 'Replace the latest folder'
type: boolean
jobs:
javadocs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
ref: ${{ inputs.target_tag }}
- uses: actions/setup-java@v4
with:
java-version: '21'
distribution: 'adopt'
cache: gradle
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4
- name: build javadoc
run: ./gradlew javadoc
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_VERSION: ${{ inputs.target_tag }}
- name: Copy javadoc to deploy folder
run: |
mkdir -p build/docs/javadoc-deploy/${{ inputs.target_tag }}
cp -r build/docs/javadoc/* build/docs/javadoc-deploy/${{ inputs.target_tag }}
- name: Copy javadoc to latest folder
if: inputs.replace_latest
run: |
mkdir -p build/docs/javadoc-deploy/latest
cp -r build/docs/javadoc/* build/docs/javadoc-deploy/latest
- name: Deploy javadoc to gh pages
uses: JamesIves/github-pages-deploy-action@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}
folder: build/docs/javadoc-deploy
branch: javadoc
target-folder: javadoc
clean: false
commit-message: Deploy javadoc for ${{ inputs.target_tag }}
@@ -1,31 +0,0 @@
name: 'Dispatch: Platform Uploads'
on:
workflow_dispatch:
inputs:
target_tag:
description: 'Version to upload'
required: true
type: string
upload_modrinth:
description: 'Upload to modrinth.com'
required: true
type: boolean
upload_dbo:
description: 'Upload to dev.bukkit.org'
required: true
type: boolean
upload_hangar:
description: 'Upload to hangar.papermc.io'
required: true
type: boolean
jobs:
dispatch_platform_uploads:
uses: ./.github/workflows/call.platform_uploads.yml
secrets: inherit
with:
target_tag: ${{ github.event.inputs.target_tag }}
upload_modrinth: ${{ github.event.inputs.upload_modrinth }}
upload_dbo: ${{ github.event.inputs.upload_dbo }}
upload_hangar: ${{ github.event.inputs.upload_hangar }}
@@ -1,38 +0,0 @@
name: 'Dispatch: Promote Release'
on:
workflow_dispatch:
inputs:
target_tag:
description: 'Version to promote'
required: true
jobs:
check_version:
runs-on: ubuntu-latest
steps:
- name: Verify input version is prerelease
run: |
if [[ "${{ github.event.inputs.target_tag }}" != *"pre"* ]]; then
echo "Version must be a prerelease"
exit 1
fi
github_release:
needs: check_version
uses: ./.github/workflows/call.github_release.yml
secrets: inherit
with:
release_mode: promote
promote_from: ${{ github.event.inputs.target_tag }}
platform_uploads:
needs: github_release
if: needs.github_release.outputs.release_created == 'true'
uses: ./.github/workflows/call.platform_uploads.yml
secrets: inherit
with:
target_tag: ${{ needs.github_release.outputs.tag_name }}
upload_modrinth: 'true'
upload_dbo: 'true'
upload_hangar: 'true'
-22
View File
@@ -1,22 +0,0 @@
name: 'Generic: Checkstyle'
on:
workflow_call:
jobs:
checkstyle:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
continue-on-error: true
steps:
- uses: actions/checkout@v5
- uses: dbelyaev/action-checkstyle@v3
continue-on-error: true
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
checkstyle_version: 10.12.2
checkstyle_config: ./config/mv_checks.xml
level: warning
@@ -1,131 +0,0 @@
name: 'Generic: GitHub Release'
on:
workflow_call:
inputs:
# Plugin specific
plugin_name:
description: 'Plugin name'
required: true
type: string
# Common params
release_mode:
description: 'Release mode'
required: true
type: string
version_bump:
description: 'Version bump'
required: false
type: string
promote_from:
description: 'Promote from'
required: false
type: string
outputs:
release_created:
description: 'Release created'
value: ${{ jobs.github_release.outputs.release_created }}
tag_name:
description: 'Tag name'
value: ${{ jobs.github_release.outputs.tag_name }}
jobs:
github_release:
runs-on: ubuntu-latest
outputs:
release_created: ${{ steps.release.outputs.release_created }}
tag_name: ${{ steps.release.outputs.tag_name }}
steps:
- name: Echo inputs
run: |
echo "release_mode: ${{ inputs.release_mode }}"
echo "version_bump: ${{ inputs.version_bump }}"
echo "promote_from: ${{ inputs.promote_from }}"
- uses: actions/checkout@v4
with:
ref: ${{ inputs.promote_from }}
- uses: actions/setup-java@v4
with:
java-version: '21'
distribution: 'adopt'
cache: gradle
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4
- name: Build to test
id: test
run: ./gradlew clean build -x assemble -x shadowJar
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Create release
id: release
if: steps.test.outcome == 'success'
uses: benwoo1110/semantic-release-action@v1
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
release_mode: ${{ inputs.release_mode }}
version_bump: ${{ inputs.version_bump }}
promote_from: ${{ inputs.promote_from }}
- name: Publish package
if: steps.release.outputs.release_created == 'true'
run: >
./gradlew publish -x test
-PmultiverseUsername=${{ secrets.REPOSILITE_REPO_USERNAME }}
-PmultiversePassword=${{ secrets.REPOSILITE_REPO_PASSWORD }}
env:
GITHUB_VERSION: ${{ steps.release.outputs.publish_version }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Build for upload
id: build
if: steps.release.outputs.release_created == 'true'
run: ./gradlew build -x test
env:
GITHUB_VERSION: ${{ steps.release.outputs.tag_name }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Upload release artifact
if: steps.build.outcome == 'success'
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: build/libs/${{ inputs.plugin_name }}-${{ steps.release.outputs.tag_name }}.jar
asset_name: ${{ inputs.plugin_name }}-${{ steps.release.outputs.tag_name }}.jar
tag: ${{ steps.release.outputs.tag_name }}
- name: Generate javadoc
id: javadoc
if: steps.build.outcome == 'success'
run: ./gradlew javadoc -x test
env:
GITHUB_VERSION: ${{ steps.release.outputs.publish_version }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Upload javadoc
if: steps.javadoc.outcome == 'success'
run: |
mkdir -p build/docs/javadoc-deploy/${{ steps.release.outputs.publish_version }}
cp -r build/docs/javadoc/* build/docs/javadoc-deploy/${{ steps.release.outputs.publish_version }}
- name: Override latest javadoc for stable release
if: steps.javadoc.outcome == 'success' && steps.release.outputs.release_type == 'release'
run: |
mkdir -p build/docs/javadoc-deploy/latest
cp -r build/docs/javadoc/* build/docs/javadoc-deploy/latest
- name: Deploy javadoc to gh pages
if: steps.javadoc.outcome == 'success'
uses: JamesIves/github-pages-deploy-action@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}
folder: build/docs/javadoc-deploy
branch: javadoc
target-folder: javadoc
clean: false
commit-message: Deploy javadoc for ${{ steps.release.outputs.tag_name }}
@@ -1,140 +0,0 @@
name: 'Generic: Platform Uploads'
on:
workflow_call:
inputs:
# Plugin specific params
plugin_name:
description: 'Plugin name'
required: true
type: string
modrinth_project_id:
description: 'modrinth.com project ID'
required: true
type: string
modrinth_dependencies:
description: 'modrinth.com project dependencies'
required: false
type: string
default: '[]'
dbo_project_id:
description: 'dev.bukkit.org project ID'
required: true
type: string
dbo_project_relations:
description: 'dev.bukkit.org project relations'
required: false
type: string
default: '[]'
hangar_slug:
description: 'hangar.papermc.io project slug'
required: true
type: string
hangar_plugin_dependencies:
description: 'hangar.papermc.io project dependencies'
required: false
type: string
default: '{}'
# Common params
target_tag:
description: 'Version to upload'
required: true
type: string
upload_modrinth:
description: 'Upload to modrinth.com'
required: true
type: string
upload_dbo:
description: 'Upload to dev.bukkit.org'
required: true
type: string
upload_hangar:
description: 'Upload to hangar.papermc.io'
required: true
type: string
secrets:
MODRINTH_TOKEN:
required: true
DBO_UPLOAD_API_TOKEN:
required: true
HANGAR_UPLOAD_TOKEN:
required: true
jobs:
platform_uploads:
runs-on: ubuntu-latest
steps:
- name: Get release info
id: release-info
uses: cardinalby/git-get-release-action@1.2.4
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag: ${{ inputs.target_tag }}
- name: Download release artifact
id: release-artifact
uses: dsaltares/fetch-gh-release-asset@1.1.1
with:
token: ${{ secrets.GITHUB_TOKEN }}
version: tags/${{ steps.release-info.outputs.tag_name }}
file: ${{ inputs.plugin_name }}-${{ steps.release-info.outputs.tag_name }}.jar
- name: Parse release type
id: parse-release-type
run: |
if [[ "${{ steps.release-info.outputs.prerelease }}" == "true" ]]; then
echo Setting release_type to Beta
echo "release_type=Beta" >> $GITHUB_OUTPUT
else
echo Setting release_type to Release
echo "release_type=Release" >> $GITHUB_OUTPUT
fi
- name: Upload to Modrinth
if: ${{ !cancelled() && inputs.upload_modrinth == 'true' }}
uses: benwoo1110/modrinth-upload-action@v1
with:
api_token: ${{ secrets.MODRINTH_TOKEN }}
project_id: ${{ inputs.modrinth_project_id }}
version_number: ${{ steps.release-info.outputs.tag_name }}
files: '["${{ github.workspace }}/${{ inputs.plugin_name }}-${{ steps.release-info.outputs.tag_name }}.jar"]'
name: ${{ steps.release-info.outputs.tag_name }}
changelog: ${{ steps.release-artifact.outputs.body }}
game_versions: 1.21.11, 1.21.10, 1.21.9, 1.21.8, 1.21.7, 1.21.6, 1.21.5, 1.21.4, 1.21.3, 1.21.2, 1.21.1, 1.21, 1.20.6, 1.20.5, 1.20.4, 1.20.3, 1.20.2, 1.20.1, 1.20, 1.19.4, 1.19.3, 1.19.2, 1.19.1, 1.19, 1.18.2
version_type: ${{ steps.parse-release-type.outputs.release_type }}
loaders: bukkit, spigot, paper, purpur
dependencies: ${{ inputs.modrinth_dependencies }}
- name: Upload to DBO
if: ${{ !cancelled() && inputs.upload_dbo == 'true' }}
uses: benwoo1110/dbo-upload-action@v1
with:
api_token: ${{ secrets.DBO_UPLOAD_API_TOKEN }}
project_id: ${{ inputs.dbo_project_id }}
changelog: ${{ steps.release-artifact.outputs.body }}
changelog_type: markdown
display_name: ${{ steps.release-info.outputs.tag_name }}
game_versions: 1.21.11, 1.21.10, 1.21.9, 1.21.8, 1.21.7, 1.21.6, 1.21.5, 1.21.4, 1.21.3, 1.21.2, 1.21.1, 1.21, 1.20.6, 1.20.5, 1.20.4, 1.20.3, 1.20.2, 1.20.1, 1.20, 1.19.4, 1.19.3, 1.19.2, 1.19.1, 1.19, 1.18.2
release_type: ${{ steps.parse-release-type.outputs.release_type }}
project_relations: ${{ inputs.dbo_project_relations }}
file_path: ${{ github.workspace }}/${{ inputs.plugin_name }}-${{ steps.release-info.outputs.tag_name }}.jar
- name: Upload to Hangar
if: ${{ !cancelled() && inputs.upload_hangar == 'true' }}
uses: benwoo1110/hangar-upload-action@v1
with:
api_token: ${{ secrets.HANGAR_UPLOAD_TOKEN }}
slug: ${{ inputs.hangar_slug }}
version: ${{ steps.release-info.outputs.tag_name }}
channel: ${{ steps.parse-release-type.outputs.release_type }}
files: '[{"path": "${{ github.workspace }}/${{ inputs.plugin_name }}-${{ steps.release-info.outputs.tag_name }}.jar", "platforms": ["PAPER"]}]'
description: ${{ steps.release-artifact.outputs.body }}
platform_dependencies: '{"PAPER": ["1.18.2-1.21.11"]}'
plugin_dependencies: ${{ inputs.hangar_plugin_dependencies }}
-48
View File
@@ -1,48 +0,0 @@
name: 'Generic: Test'
on:
workflow_call:
inputs:
plugin_name:
description: 'Plugin name'
required: true
type: string
jobs:
test-and-artifact:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
java-version: '21'
distribution: 'adopt'
cache: gradle
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4
- name: Run unit tests
run: ./gradlew build
env:
GITHUB_VERSION: pr${{ github.event.pull_request.number }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Artifact output
uses: actions/upload-artifact@v4
with:
name: ${{ inputs.plugin_name }}-pr${{ github.event.pull_request.number }}
path: build/libs/${{ inputs.plugin_name }}-pr${{ github.event.pull_request.number }}.jar
comment-artifact:
needs: test-and-artifact
runs-on: ubuntu-latest
permissions:
pull-requests: write
steps:
- uses: benwoo1110/artifact-comment-action@v1
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
-24
View File
@@ -1,24 +0,0 @@
name: 'Main: Prerelease'
on:
push:
branches: [main]
jobs:
github_release_on_push:
uses: ./.github/workflows/call.github_release.yml
secrets: inherit
with:
release_mode: prerelease
version_bump: prlabel
platform_uploads_on_push:
needs: github_release_on_push
if: needs.github_release_on_push.outputs.release_created == 'true'
uses: ./.github/workflows/call.platform_uploads.yml
secrets: inherit
with:
target_tag: ${{ needs.github_release_on_push.outputs.tag_name }}
upload_modrinth: 'true'
upload_dbo: 'false'
upload_hangar: 'false'
-9
View File
@@ -1,9 +0,0 @@
name: 'PR: Checkstyle'
on:
pull_request:
types: [opened, synchronize]
jobs:
checkstyle:
uses: ./.github/workflows/generic.checkstyle.yml
-18
View File
@@ -1,18 +0,0 @@
name: 'PR: Require Label'
on:
pull_request:
types: [opened, labeled, unlabeled, synchronize]
branches: [main]
jobs:
require_label:
runs-on: ubuntu-latest
steps:
- uses: mheap/github-action-required-labels@v2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
mode: exactly
count: 1
labels: "release:major, release:minor, release:patch, no release"
-11
View File
@@ -1,11 +0,0 @@
name: 'PR: Test'
on:
pull_request:
types: [opened, synchronize]
jobs:
test:
uses: ./.github/workflows/generic.test.yml
with:
plugin_name: multiverse-core
-48
View File
@@ -1,48 +0,0 @@
# Eclipse stuff
/.classpath
/.project
/.settings
/.checkstyle
# netbeans
/nbproject
# we use maven!
/build.xml
# maven
/target
dependency-reduced-pom.xml
# vim
.*.sw[a-p]
# various other potential build files
/build
/bin
/dist
/manifest.mf
/world
# Mac filesystem dust
*.DS_Store
# intellij
*.iml
*.ipr
*.iws
.idea/
# Fern's utils
uploadtoserver.sh
# Testing files:
debug.log
# Doxygen
/docs/html
debug.txt
# Gradle
.gradle
-4
View File
@@ -1,4 +0,0 @@
kotlin version: 2.0.21
error message: The daemon has terminated unexpectedly on startup attempt #1 with error code: 0. The daemon process output:
1. Kotlin compile daemon is ready
-6
View File
@@ -1,6 +0,0 @@
Copyright (c) 2011, The Multiverse Team All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of the The Multiverse Team nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-51
View File
@@ -1,51 +0,0 @@
<p align="center">
<img src="config/multiverse-banner.png" alt="Multiverse Logo">
</p>
[![Modrinth](https://cdn.jsdelivr.net/npm/@intergrav/devins-badges@3/assets/cozy/available/modrinth_vector.svg)](https://modrinth.com/plugin/multiverse-core)
[![Hangar](https://cdn.jsdelivr.net/npm/@intergrav/devins-badges@3/assets/cozy/available/hangar_vector.svg)](https://hangar.papermc.io/Multiverse/Multiverse-Core)
[![Bukkit](https://raw.githubusercontent.com/intergrav/devins-badges/refs/heads/v3/assets/cozy/available/bukkit_vector.svg)](https://dev.bukkit.org/projects/multiverse-core)
[![Spigot](https://raw.githubusercontent.com/intergrav/devins-badges/refs/heads/v3/assets/cozy/available/spigot_vector.svg)](https://www.spigotmc.org/resources/multiverse-core.390/)
[![Release](https://img.shields.io/github/v/release/multiverse/multiverse-core)](https://github.com/Multiverse/Multiverse-Core/releases/latest)
[![Pre-Release](https://img.shields.io/github/v/release/multiverse/multiverse-core?include_prereleases&label=Pre-release)](https://github.com/Multiverse/Multiverse-Core/releases)
[![Discord](https://img.shields.io/discord/325459248047980545?label=Discord&logo=discord)](https://discord.gg/NZtfKky)
[![Donate on Github Sponsor](https://img.shields.io/badge/Github%20Sponsor-Donate-pink?logo=githubsponsors)](https://github.com/sponsors/Multiverse)
[![Donate on Open Collective](https://img.shields.io/badge/Open%20Collective-Donate-blue?style=flat&logo=opencollective)](https://opencollective.com/multiverse-plugins)
# About
[Multiverse](https://modrinth.com/plugin/multiverse-core) was created at the dawn of Bukkit multiworld support. It has since then grown into a **complete world management solution!** Multiverse provides the easiest to use world management solution for your Minecraft server, big or small, and with great addons like [Portals](https://dev.bukkit.org/projects/multiverse-portals) and [NetherPortals](https://dev.bukkit.org/projects/multiverse-netherportals/), what's not to love!
Now it's time to create your very own Multiverse server, do check out our [Wiki](https://github.com/Multiverse/Multiverse-Core/wiki) and [Usage Guide](https://github.com/Multiverse/Multiverse-Core/wiki/Basics) to get started. Feel free to hop onto our [Discord](https://discord.gg/NZtfKky) if you have any question or just want to have a chat with us!
## Amazing sub-modules available:
* [Multiverse-NetherPortals](https://github.com/Multiverse/Multiverse-NetherPortals) -> Have separate nether and end worlds for each of your overworlds!
* [Multiverse-Portals](https://github.com/Multiverse/Multiverse-Portals) -> Make custom portals to go to any destination!
* [Multiverse-Inventories](https://github.com/Multiverse/Multiverse-Inventories) -> Have separated players stats and inventories per world or per group of worlds.
* [Multiverse-SignPortals](https://github.com/Multiverse/Multiverse-SignPortals) -> Signs as teleporters!
## Usage Guide
We have a cool new website hosting our Wiki: https://mvplugins.org
## Building
Simply build the source with Gradle:
```
./gradlew build
```
## Contributing
**Want to help improve Multiverse?** There are several ways you can support and contribute to the project.
* Take a look at our "Bug: Unconfirmed" issues, where you can find issues that need extra testing and investigation.
* Want others to love Multiverse too? You can join the [Multiverse Discord community](https://discord.gg/NZtfKky) and help others with issues and setup!
* A Multiverse guru? You can update our [Wiki](https://github.com/Multiverse/multiverse-web) with your latest tip, tricks and guides! The wiki open for all to edit and improve.
* Love coding? You could look at ["State: Open to PR"](https://github.com/Multiverse/Multiverse-Core/labels/State%3A%20Open%20to%20PR) and ["Resolution: Accepted"](https://github.com/Multiverse/Multiverse-Core/labels/Resolution%3A%20Accepted) issues. We're always happy to receive bug fixes and feature additions as [pull requests](https://www.freecodecamp.org/news/how-to-make-your-first-pull-request-on-github-3/).
* If you'd like to make a financial contribution to the project, do consider donating to our [Github Sponsors](https://github.com/sponsors/Multiverse) or [Open Collective](https://opencollective.com/multiverse-plugins)!
Additionally, we would like to give a big thanks to everyone that has supported Multiverse over the years, as well as those in the years to come. Thank you!
## License
Multiverse-Core is licensed under BSD-3-Clause License. Please see [LICENSE.md](LICENSE.md) for more info.
-364
View File
@@ -1,364 +0,0 @@
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
import groovy.util.Node
import groovy.util.NodeList
import org.apache.tools.ant.filters.ReplaceTokens
import org.gradle.api.artifacts.Configuration
import org.gradle.api.credentials.PasswordCredentials
import org.gradle.api.publish.maven.MavenPublication
import org.gradle.api.tasks.SourceSetContainer
import org.gradle.api.tasks.Sync
import org.gradle.api.tasks.compile.JavaCompile
import org.gradle.api.tasks.javadoc.Javadoc
import org.gradle.api.tasks.testing.Test
import org.gradle.api.tasks.testing.logging.TestExceptionFormat
import org.gradle.external.javadoc.StandardJavadocDocletOptions
import org.gradle.jvm.tasks.Jar
import org.gradle.language.jvm.tasks.ProcessResources
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
`java-library`
eclipse
`maven-publish`
id("com.gradleup.shadow") version "8.3.9"
kotlin("jvm") version "2.0.21"
}
group = "org.mvplugins.multiverse.core"
version = System.getenv("GITHUB_VERSION") ?: "5.3.4.17"
description = "Multiverse-Core"
val sourceSets = extensions.getByType<SourceSetContainer>()
val serverApi: Configuration by configurations.creating
val externalPlugin: Configuration by configurations.creating
val shadowed: Configuration by configurations.creating
repositories {
mavenLocal()
mavenCentral()
maven {
url = uri("https://hub.spigotmc.org/nexus/content/repositories/snapshots/")
}
maven {
url = uri("https://repo.onarandombox.com/content/groups/public")
}
maven {
url = uri("https://hub.spigotmc.org/nexus/content/groups/public/")
}
maven {
url = uri("https://jitpack.io")
}
maven {
url = uri("https://repo.minebench.de/")
}
maven {
url = uri("https://repo.maven.apache.org/maven2/")
}
maven {
name = "aikar repo"
url = uri("https://repo.aikar.co/content/groups/aikar/")
}
maven {
name = "glaremasters repo"
url = uri("https://repo.glaremasters.me/repository/towny/")
}
maven {
name = "helpchatRepoReleases"
url = uri("https://repo.helpch.at/releases/")
}
maven {
name = "papermc repo"
url = uri("https://repo.papermc.io/repository/maven-public/")
}
// Purpur API
maven("https://repo.purpurmc.org/snapshots")
}
// Configuration wiring like in the Groovy build
configurations["compileOnly"].extendsFrom(serverApi)
configurations["runtimeClasspath"].extendsFrom(serverApi)
configurations["implementation"].extendsFrom(externalPlugin)
configurations["compileClasspath"].extendsFrom(shadowed)
configurations["testCompileClasspath"].extendsFrom(shadowed)
configurations["testRuntimeClasspath"].extendsFrom(shadowed)
dependencies {
// Server API: Purpur 1.19.4
serverApi("org.purpurmc.purpur:purpur-api:1.19.4-R0.1-SNAPSHOT")
// Economy
externalPlugin("com.github.MilkBowl:VaultAPI:1.7.1") {
exclude(group = "org.bukkit", module = "bukkit")
}
// PlaceholderAPI
externalPlugin("me.clip:placeholderapi:2.11.6")
// Command Framework
shadowed("co.aikar:acf-paper:0.5.1-SNAPSHOT")
// Config
shadowed("io.github.townyadvanced.commentedconfiguration:CommentedConfiguration:1.0.1") {
exclude(group = "org.spigotmc", module = "spigot-api")
}
// Utils
shadowed("io.vavr:vavr:0.10.4")
shadowed("org.glassfish.hk2:hk2-locator:3.0.3")
shadowed("org.glassfish.hk2:hk2-inhabitant-generator:3.0.3") {
exclude(group = "org.apache.maven", module = "maven-core")
}
shadowed("com.dumptruckman.minecraft:Logging:1.1.1") {
exclude(group = "junit", module = "junit")
}
shadowed("de.themoep.idconverter:mappings:1.2-SNAPSHOT")
shadowed("org.bstats:bstats-bukkit:3.1.0") {
exclude(group = "org.bukkit", module = "bukkit")
}
shadowed("net.minidev:json-smart:2.4.9")
shadowed("org.jetbrains:annotations:22.0.0")
shadowed("io.papermc:paperlib:1.0.8")
// Tests
testImplementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.0.21")
testImplementation("com.github.seeseemelk:MockBukkit-v1.19:3.1.0")
testImplementation("com.googlecode.json-simple:json-simple:1.1.1") {
exclude(group = "junit", module = "junit")
}
testImplementation("org.jetbrains.kotlin:kotlin-test")
testImplementation("com.natpryce:hamkrest:1.8.0.1")
testImplementation("org.mockito.kotlin:mockito-kotlin:4.1.0")
// Annotation Processors
annotationProcessor("org.glassfish.hk2:hk2-metadata-generator:3.0.3")
testAnnotationProcessor("org.glassfish.hk2:hk2-metadata-generator:3.0.3")
}
java {
// Plugin is compiled for Java 17 (GraalVM 17-friendly)
toolchain.languageVersion.set(JavaLanguageVersion.of(17))
withSourcesJar()
withJavadocJar()
}
val bitlyAccessToken: String = System.getenv("BITLY_ACCESS_TOKEN") ?: "bitly-access-token"
// === Token-filtered source generation (prepareSource) ===
val prepareSource by tasks.registering(Sync::class) {
inputs.property("bitlyAccessToken", bitlyAccessToken)
from(sourceSets["main"].java)
// avoid deprecated buildDir getter
into(layout.buildDirectory.dir("src"))
filter<ReplaceTokens>(
"tokens" to mapOf(
"bitly-access-token" to bitlyAccessToken,
),
)
}
// Use ONLY the filtered sources for compileJava (fixes duplicate classes)
tasks.named<JavaCompile>("compileJava") {
dependsOn(prepareSource)
source = prepareSource.get().outputs.files.asFileTree
sourceCompatibility = JavaVersion.VERSION_17.toString()
targetCompatibility = JavaVersion.VERSION_17.toString()
}
tasks.named<JavaCompile>("compileTestJava") {
sourceCompatibility = JavaVersion.VERSION_17.toString()
targetCompatibility = JavaVersion.VERSION_17.toString()
}
tasks.withType<JavaCompile>().configureEach {
options.encoding = "UTF-8"
options.compilerArgs.add(
"-Aorg.glassfish.hk2.metadata.location=META-INF/hk2-locator/Multiverse-Core",
)
}
tasks.withType<Javadoc>().configureEach {
(options as StandardJavadocDocletOptions).encoding = "UTF-8"
}
tasks.named<KotlinCompile>("compileTestKotlin") {
compilerOptions {
jvmTarget.set(JvmTarget.JVM_17)
javaParameters.set(true)
}
}
// We're not using Kotlin in the plugin itself, just tests!
tasks.named<KotlinCompile>("compileKotlin") {
enabled = false
}
configurations
.filterNot { it.name.startsWith("test") }
.forEach {
it.exclude(group = "org.jetbrains.kotlin", module = "kotlin-stdlib-jdk8")
}
tasks.named<ProcessResources>("processResources") {
val props = mapOf("version" to project.version.toString())
inputs.properties(props)
filteringCharset = "UTF-8"
filesMatching("plugin.yml") {
expand(props)
}
// Never up-to-date: tests depend on fresh version expansion
outputs.upToDateWhen { false }
}
tasks.named<Javadoc>("javadoc") {
source = sourceSets["main"].allJava
classpath = configurations["compileClasspath"]
}
// Removed: configurations["api"].isCanBeResolved = true (deprecated in Gradle 8/9)
val jarTask = tasks.named<Jar>("jar")
val shadowJarTask = tasks.named<ShadowJar>("shadowJar")
listOf("apiElements", "runtimeElements").forEach { configurationName ->
configurations.named(configurationName) {
outgoing.artifacts.removeIf { artifact ->
artifact.buildDependencies.getDependencies(null).contains(jarTask.get())
}
outgoing.artifact(shadowJarTask)
}
}
tasks.named<ShadowJar>("shadowJar") {
relocate("co.aikar", "org.mvplugins.multiverse.external.acf")
relocate("com.dumptruckman.minecraft.util.Logging", "org.mvplugins.multiverse.core.utils.CoreLogging")
relocate("com.dumptruckman.minecraft.util.DebugLog", "org.mvplugins.multiverse.core.utils.DebugFileLogger")
relocate("de.themoep.idconverter", "org.mvplugins.multiverse.external.idconverter")
relocate("io.github.townyadvanced.commentedconfiguration", "org.mvplugins.multiverse.external.commentedconfiguration")
relocate("me.main__.util", "org.mvplugins.multiverse.external.serializationconfig")
relocate("org.bstats", "org.mvplugins.multiverse.external.bstats")
relocate("com.sun", "org.mvplugins.multiverse.external.sun")
relocate("net.minidev", "org.mvplugins.multiverse.external.minidev")
relocate("org.objectweb", "org.mvplugins.multiverse.external.objectweb")
relocate("io.vavr", "org.mvplugins.multiverse.external.vavr")
relocate("jakarta", "org.mvplugins.multiverse.external.jakarta")
relocate("javassist", "org.mvplugins.multiverse.external.javassist")
relocate("org.aopalliance", "org.mvplugins.multiverse.external.aopalliance")
relocate("org.glassfish", "org.mvplugins.multiverse.external.glassfish")
relocate("org.jvnet", "org.mvplugins.multiverse.external.jvnet")
relocate("org.intellij", "org.mvplugins.multiverse.external.intellij")
relocate("org.jetbrains", "org.mvplugins.multiverse.external.jetbrains")
relocate("io.papermc.lib", "org.mvplugins.multiverse.external.paperlib")
configurations = listOf(project.configurations["shadowed"])
archiveClassifier.set("")
dependencies {
exclude { dependency ->
dependency.moduleGroup == "org.jetbrains.kotlin"
}
}
}
tasks.named("build") {
dependsOn("shadowJar")
}
tasks.named<Jar>("jar") {
enabled = false
}
tasks.named<Test>("test") {
useJUnitPlatform()
testLogging {
exceptionFormat = TestExceptionFormat.FULL
}
}
publishing {
publications {
create<MavenPublication>("maven") {
from(components["java"])
pom.withXml {
val pomNode = asNode()
val dependenciesNode = (
(pomNode.get("dependencies") as? NodeList)
?.firstOrNull() as? Node
) ?: (pomNode.appendNode("dependencies") as Node)
val dependencyNodes = dependenciesNode.children().filterIsInstance<Node>().toList()
// Remove Kotlin dependency
dependencyNodes
.filter { dependencyNode ->
val groupIdNodes = dependencyNode.get("groupId") as? NodeList ?: return@filter false
val groupId = (groupIdNodes.firstOrNull() as? Node)?.text()
groupId == "org.jetbrains.kotlin"
}
.forEach { dependencyNode ->
dependenciesNode.remove(dependencyNode)
}
// Switch runtime deps to provided
dependencyNodes.forEach { dependencyNode ->
val scopeNodes = dependencyNode.get("scope") as? NodeList ?: return@forEach
val scopeNode = scopeNodes.firstOrNull() as? Node ?: return@forEach
if (scopeNode.text() == "runtime") {
scopeNode.setValue("provided")
}
}
// Add server API (Purpur) to pom as provided
configurations["serverApi"].allDependencies.forEach { dependency ->
val depNode = dependenciesNode.appendNode("dependency")
depNode.appendNode("groupId", dependency.group)
depNode.appendNode("artifactId", dependency.name)
depNode.appendNode("version", dependency.version)
depNode.appendNode("scope", "provided")
}
}
}
}
repositories {
maven {
name = "GitHubPackages"
url = uri("https://maven.pkg.github.com/Multiverse/Multiverse-Core")
credentials {
username = System.getenv("GITHUB_ACTOR")
password = System.getenv("GITHUB_TOKEN")
}
}
maven {
name = "multiverse"
val releasesRepoUrl = uri("https://repo.dumptruckman.com/multiverse-releases")
val snapshotsRepoUrl = uri("https://repo.dumptruckman.com/multiverse-snapshots")
url = if (version.toString().endsWith("SNAPSHOT")) snapshotsRepoUrl else releasesRepoUrl
credentials(PasswordCredentials::class)
}
maven {
// todo: remove before mv5 release
name = "multiverseBeta"
url = uri("https://repo.c0ding.party/multiverse-beta")
credentials(PasswordCredentials::class)
}
}
}
-1
View File
@@ -1 +0,0 @@
out.txt
-1813
View File
File diff suppressed because it is too large. Load diff
-20
View File
@@ -1,20 +0,0 @@
filepath = "../src/main/resources/multiverse-core_en.properties"
enumList = []
with open(filepath) as f:
for line in f:
line = line.strip()
if len(line) == 0:
enumList.append("")
elif line.startswith("#"):
enumList.append(line.replace("#", "//"))
else:
parts = line.split("=")
if (len(parts) < 2):
continue
parts = parts[0].split(".")[1:]
enumList.append("_".join([p.upper() for p in parts]) + ",")
with open("out.txt", "w") as f:
f.write("\n".join(enumList))
Binary file not shown.

Before

Width:  |  Height:  |  Size: 166 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

-665
View File
@@ -1,665 +0,0 @@
<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~ Multiverse 2 Copyright (c) the Multiverse Team 2011. ~
~ Multiverse 2 is licensed under the BSD License. ~
~ For more information please check the README.md file included ~
~ with this project. ~
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
<!DOCTYPE module PUBLIC
"-//Checkstyle//DTD Checkstyle Configuration 1.3//EN"
"https://checkstyle.org/dtds/configuration_1_3.dtd">
<!-- Future reference: valid severity values are 'ignore', 'info', 'warning', 'error' -->
<module name="Checker">
<property name="charset" value="UTF-8"/>
<property name="fileExtensions" value="java, properties, xml"/>
<property name="severity" value="warning"/>
<!-- Javadoc Comments -->
<module name="JavadocPackage"/><!-- require package-info.java file -->
<!-- Miscellaneous -->
<module name="NewlineAtEndOfFile" />
<module name="OrderedProperties"/>
<module name="Translation"/>
<module name="UniqueProperties"/>
<!-- Regexp -->
<module name="RegexpSingleline">
<property name="message" value="Line has trailing spaces."/>
<property name="format" value="\s+$"/>
</module>
<!-- Size Violations -->
<module name="FileLength">
<property name="max" value="2000"/>
</module>
<module name="LineLength">
<property name="fileExtensions" value="java"/>
<property name="max" value="120"/>
<property name="ignorePattern" value="^package.*|^import.*|a href|href|http://|https://|ftp://"/>
</module>
<!-- Whitespace -->
<module name="FileTabCharacter">
<property name="eachLine" value="true"/>
</module>
<module name="TreeWalker">
<!-- Make suppression possible -->
<module name="SuppressWithNearbyCommentFilter">
<property name="commentFormat" value="SUPPRESS CHECKSTYLE: (\w+)"/>
<property name="checkFormat" value="$1"/>
</module>
<module name="SuppressionCommentFilter">
<property name="offCommentFormat" value="BEGIN CHECKSTYLE-SUPPRESSION\: (\w+)"/>
<property name="onCommentFormat" value="END CHECKSTYLE-SUPPRESSION\: (\w+)"/>
<property name="checkFormat" value="$1"/>
</module>
<module name="SuppressionCommentFilter">
<property name="offCommentFormat" value="BEGIN CHECKSTYLE-SUPPRESSION\: ALL"/>
<property name="onCommentFormat" value="END CHECKSTYLE-SUPPRESSION\: ALL"/>
</module>
<!-- Actual checks -->
<!-- Annotations -->
<module name="AnnotationLocation">
<property name="id" value="AnnotationLocationMostCases"/>
<property name="tokens"
value="CLASS_DEF, INTERFACE_DEF, PACKAGE_DEF, ENUM_CONSTANT_DEF,
ENUM_DEF, METHOD_DEF, CTOR_DEF, RECORD_DEF, COMPACT_CTOR_DEF"/>
</module>
<module name="AnnotationLocation">
<property name="id" value="AnnotationLocationVariables"/>
<property name="tokens" value="VARIABLE_DEF"/>
<property name="allowSamelineMultipleAnnotations" value="true"/>
</module>
<module name="AnnotationLocation"/>
<module name="AnnotationUseStyle"/>
<module name="MissingDeprecated"/>
<module name="MissingOverride"/>
<!-- Block Checks -->
<module name="AvoidNestedBlocks"/>
<module name="EmptyBlock">
<property name="tokens"
value="LITERAL_WHILE, LITERAL_TRY, LITERAL_FINALLY, LITERAL_DO, LITERAL_IF,
LITERAL_ELSE, LITERAL_FOR, INSTANCE_INIT, STATIC_INIT, LITERAL_SWITCH,
LITERAL_SYNCHRONIZED"/>
</module>
<module name="EmptyCatchBlock">
<property name="exceptionVariableName" value="(ignore|expect)"/>
</module>
<module name="LeftCurly">
<property name="ignoreEnums" value="false"/>
<property name="tokens"
value="ANNOTATION_DEF, CLASS_DEF, CTOR_DEF, ENUM_CONSTANT_DEF, ENUM_DEF,
INTERFACE_DEF, LAMBDA, LITERAL_CASE, LITERAL_CATCH, LITERAL_DEFAULT,
LITERAL_DO, LITERAL_ELSE, LITERAL_FINALLY, LITERAL_FOR, LITERAL_IF,
LITERAL_SWITCH, LITERAL_SYNCHRONIZED, LITERAL_TRY, LITERAL_WHILE, METHOD_DEF,
OBJBLOCK, STATIC_INIT, RECORD_DEF, COMPACT_CTOR_DEF"/>
</module>
<module name="NeedBraces">
<property name="id" value="SingleLineIf"/>
<property name="tokens" value="LITERAL_IF"/>
<property name="allowSingleLineStatement" value="true"/>
</module>
<module name="NeedBraces">
<property name="id" value="NoSingleLineControl"/>
<property name="tokens" value="LITERAL_DO, LITERAL_ELSE, LITERAL_FOR, LITERAL_WHILE"/>
</module>
<module name="RightCurly">
<property name="id" value="RightCurlySame"/>
<property name="tokens"
value="LITERAL_TRY, LITERAL_CATCH, LITERAL_FINALLY, LITERAL_IF, LITERAL_ELSE,
LITERAL_DO"/>
</module>
<module name="RightCurly">
<property name="id" value="RightCurlyAlone"/>
<property name="option" value="alone"/>
<property name="tokens"
value="CLASS_DEF, METHOD_DEF, CTOR_DEF, LITERAL_FOR, LITERAL_WHILE, STATIC_INIT,
INSTANCE_INIT, ANNOTATION_DEF, ENUM_DEF, INTERFACE_DEF, RECORD_DEF,
COMPACT_CTOR_DEF, LITERAL_SWITCH"/>
</module>
<!-- Class Design -->
<module name="DesignForExtension">
<property name="severity" value="info"/>
</module>
<module name="FinalClass"/>
<module name="HideUtilityClassConstructor"/>
<module name="InnerTypeLast"/>
<module name="InterfaceIsType"/>
<module name="MutableException"/>
<module name="OneTopLevelClass"/>
<module name="ThrowsCount">
<property name="max" value="2"/>
</module>
<module name="VisibilityModifier">
<property name="protectedAllowed" value="true"/>
</module>
<!-- Coding -->
<module name="ArrayTrailingComma"/>
<module name="AvoidDoubleBraceInitialization"/>
<module name="AvoidNoArgumentSuperConstructorCall"/>
<module name="CovariantEquals"/>
<module name="DeclarationOrder">
<!-- Doesn't respect static methods at top -->
<property name="severity" value="ignore"/>
</module>
<module name="DefaultComesLast"/>
<module name="EmptyStatement"/>
<module name="EqualsAvoidNull"/>
<module name="EqualsHashCode"/>
<module name="FallThrough">
<property name="checkLastCaseGroup" value="true"/>
</module>
<module name="HiddenField">
<property name="ignoreConstructorParameter" value="true"/>
<property name="ignoreSetter" value="true"/>
<property name="setterCanReturnItsClass" value="true"/>
<property name="ignoreAbstractMethods" value="true"/>
<!-- Not smart enough -->
<property name="severity" value="ignore"/>
</module>
<module name="IllegalCatch"/>
<module name="IllegalThrows"/>
<module name="IllegalToken"/>
<module name="IllegalTokenText">
<property name="tokens" value="STRING_LITERAL, CHAR_LITERAL"/>
<property name="format"
value="\\u00(09|0(a|A)|0(c|C)|0(d|D)|22|27|5(C|c))|\\(0(10|11|12|14|15|42|47)|134)"/>
<property name="message"
value="Consider using special escape sequence instead of octal value or Unicode escaped value."/>
</module>
<module name="IllegalType"/>
<module name="InnerAssignment"/>
<module name="MagicNumber">
<property name="ignoreHashCodeMethod" value="true"/>
<property name="ignoreAnnotationElementDefaults" value="true"/>
</module>
<module name="MissingSwitchDefault"/>
<module name="ModifiedControlVariable"/>
<module name="MultipleStringLiterals">
</module>
<module name="MultipleVariableDeclarations"/>
<module name="NestedForDepth">
<property name="severity" value="warning"/>
</module>
<module name="NestedIfDepth"/>
<module name="NestedTryDepth"/>
<module name="NoClone"/>
<module name="NoFinalizer"/>
<module name="OneStatementPerLine"/>
<module name="PackageDeclaration"/>
<module name="ParameterAssignment"/>
<module name="RequireThis"/>
<module name="ReturnCount">
<property name="severity" value="info"/>
</module>
<module name="SimplifyBooleanExpression"/>
<module name="SimplifyBooleanReturn"/>
<module name="StringLiteralEquality"/>
<module name="SuperClone"/>
<module name="SuperFinalize"/>
<module name="UnnecessaryParentheses"/>
<module name="UnnecessarySemicolonAfterOuterTypeDeclaration"/>
<module name="UnnecessarySemicolonAfterTypeMemberDeclaration"/>
<module name="UnnecessarySemicolonInEnumeration"/>
<module name="UnnecessarySemicolonInTryWithResources"/>
<module name="UnusedLocalVariable"/>
<module name="VariableDeclarationUsageDistance">
<property name="allowedDistance" value="3"/>
<property name="validateBetweenScopes" value="false"/>
</module>
<module name="VariableDeclarationUsageDistance">
<property name="allowedDistance" value="5"/>
<property name="validateBetweenScopes" value="true"/>
</module>
<!-- Imports -->
<module name="AvoidStarImport"/>
<module name="IllegalImport">
<property name="illegalPkgs" value="sun"/>
</module>
<module name="ImportOrder">
<property name="option" value="bottom"/>
<property name="ordered" value="true"/>
<property name="separated" value="true"/>
<property name="separatedStaticGroups" value="true"/>
<property name="caseSensitive" value="true"/>
<property name="sortStaticImportsAlphabetically" value="true"/>
<property name="groups"
value="java,javax,*,org.mvplugins"/>
<property name="staticGroups"
value="/^.*$/"/>
</module>
<module name="RedundantImport"/>
<module name="UnusedImports"/>
<!-- Javadoc Comments -->
<module name="AtclauseOrder">
<property name="target"
value="CLASS_DEF, COMPACT_CTOR_DEF, CTOR_DEF, ENUM_DEF,
INTERFACE_DEF, METHOD_DEF, RECORD_DEF, VARIABLE_DEF"/>
</module>
<module name="InvalidJavadocPosition"/>
<module name="JavadocBlockTagLocation"/>
<module name="JavadocContentLocationCheck"/>
<module name="JavadocMethod">
<property name="accessModifiers" value="public"/>
<property name="allowMissingParamTags" value="true"/>
<property name="allowMissingReturnTag" value="true"/>
<property name="allowedAnnotations" value="Override, Test"/>
<property name="tokens" value="METHOD_DEF, CTOR_DEF, ANNOTATION_FIELD_DEF, COMPACT_CTOR_DEF"/>
</module>
<module name="JavadocMethod">
<property name="accessModifiers" value="public"/>
</module>
<module name="JavadocMissingLeadingAsterisk"/>
<module name="JavadocMissingWhitespaceAfterAsterisk"/>
<module name="JavadocParagraph">
<property name="allowNewlineParagraph" value="true"/>
</module>
<module name="JavadocStyle">
<property name="scope" value="package"/>
</module>
<module name="JavadocTagContinuationIndentation"/>
<module name="JavadocType"/>
<module name="JavadocVariable">
<property name="scope" value="package"/>
</module>
<module name="MissingJavadocMethod">
<property name="scope" value="public"/>
<property name="allowMissingPropertyJavadoc" value="true"/>
</module>
<module name="MissingJavadocMethod">
<property name="scope" value="protected"/>
<property name="excludeScope" value="public"/>
<property name="allowMissingPropertyJavadoc" value="true"/>
<property name="minLineCount" value="2"/>
</module>
<module name="MissingJavadocType">
<property name="scope" value="protected"/>
<property name="tokens"
value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF,
RECORD_DEF, ANNOTATION_DEF"/>
<property name="excludeScope" value="nothing"/>
</module>
<module name="NonEmptyAtclauseDescription"/>
<module name="RequireEmptyLineBeforeBlockTagGroup"/>
<module name="SingleLineJavadoc"/>
<module name="SummaryJavadoc">
<property name="forbiddenSummaryFragments"
value="^@return the *|^This method returns |^A [{]@code [a-zA-Z0-9]+[}]( is a )"/>
</module>
<module name="WriteTag">
<property name="tag" value="@author"/>
<property name="tagFormat" value="\S"/>
<property name="severity" value="ignore"/>
<property name="tagSeverity" value="warning"/>
</module>
<!-- Metrics -->
<module name="BooleanExpressionComplexity"/>
<module name="ClassDataAbstractionCoupling">
<property name="max" value="7"/>
<property name="excludedClasses"
value="ArrayIndexOutOfBoundsException, ArrayList, Boolean, Byte, Character,
Class, Collection, Deprecated, Deque, Double, DoubleStream, EnumSet, Exception,
Float, FunctionalInterface, HashMap, HashSet, IllegalArgumentException,
IllegalStateException, IndexOutOfBoundsException, IntStream, Integer,
LinkedHashMap, LinkedHashSet, LinkedList, List, Long, LongStream, Map,
NullPointerException, Object, Optional, OptionalDouble, OptionalInt,
OptionalLong, Override, Queue, RuntimeException, SafeVarargs, SecurityException,
Set, Short, SortedMap, SortedSet, Stream, String, StringBuffer, StringBuilder,
SuppressWarnings, Throwable, TreeMap, TreeSet, UnsupportedOperationException,
Void, boolean, byte, char, double, float, int, long, short, var, void"/>
</module>
<module name="ClassFanOutComplexity">
<property name="id" value="FanOutComplexityGettingTooHigh"/>
<property name="max" value="20"/>
<property name="excludedClasses"
value="ArrayIndexOutOfBoundsException, ArrayList, Boolean, Byte, Character,
Class, Collection, Deprecated, Deque, Double, DoubleStream, EnumSet, Exception,
Float, FunctionalInterface, HashMap, HashSet, IllegalArgumentException,
IllegalStateException, IndexOutOfBoundsException, IntStream, Integer,
LinkedHashMap, LinkedHashSet, LinkedList, List, Long, LongStream, Map,
NullPointerException, Object, Optional, OptionalDouble, OptionalInt,
OptionalLong, Override, Queue, RuntimeException, SafeVarargs, SecurityException,
Set, Short, SortedMap, SortedSet, Stream, String, StringBuffer, StringBuilder,
SuppressWarnings, Throwable, TreeMap, TreeSet, UnsupportedOperationException,
Void, boolean, byte, char, double, float, int, long, short, var, void"/>
<property name="severity" value="info"/>
</module>
<module name="ClassFanOutComplexity">
<property name="id" value="FanOutComplexityProbablyTooHigh"/>
<property name="max" value="35"/>
<property name="excludedClasses"
value="ArrayIndexOutOfBoundsException, ArrayList, Boolean, Byte, Character,
Class, Collection, Deprecated, Deque, Double, DoubleStream, EnumSet, Exception,
Float, FunctionalInterface, HashMap, HashSet, IllegalArgumentException,
IllegalStateException, IndexOutOfBoundsException, IntStream, Integer,
LinkedHashMap, LinkedHashSet, LinkedList, List, Long, LongStream, Map,
NullPointerException, Object, Optional, OptionalDouble, OptionalInt,
OptionalLong, Override, Queue, RuntimeException, SafeVarargs, SecurityException,
Set, Short, SortedMap, SortedSet, Stream, String, StringBuffer, StringBuilder,
SuppressWarnings, Throwable, TreeMap, TreeSet, UnsupportedOperationException,
Void, boolean, byte, char, double, float, int, long, short, var, void"/>
</module>
<module name="CyclomaticComplexity">
<property name="max" value="7"/>
<property name="severity" value="warning"/>
</module>
<module name="JavaNCSS">
<property name="methodMaximum" value="50"/>
<property name="classMaximum" value="1500"/>
<property name="fileMaximum" value="2000"/>
<property name="recordMaximum" value="150"/>
</module>
<module name="NPathComplexity">
<property name="max" value="200"/>
</module>
<!-- Miscellaneous -->
<module name="ArrayTypeStyle"/>
<module name="AvoidEscapedUnicodeCharacters">
<property name="allowEscapesForControlCharacters" value="true"/>
<property name="allowNonPrintableEscapes" value="true"/>
</module>
<module name="CommentsIndentation">
<property name="tokens" value="SINGLE_LINE_COMMENT, BLOCK_COMMENT_BEGIN"/>
</module>
<module name="Indentation">
<property name="basicOffset" value="4"/>
<property name="braceAdjustment" value="4"/>
<property name="caseIndent" value="4"/>
<property name="throwsIndent" value="8"/>
<property name="arrayInitIndent" value="4"/>
<property name="lineWrappingIndentation" value="8"/>
<property name="forceStrictCondition" value="false"/>
</module>
<module name="NoCodeInFile"/>
<module name="OuterTypeFilename"/>
<module name="TodoComment">
<property name="format" value="TODO"/>
<!-- TODO change back to warning after MV5 -->
<property name="severity" value="info"/>
</module>
<module name="TrailingComment">
<property name="legalComment" value="^\sSUPPRESS CHECKSTYLE:"/>
</module>
<module name="UncommentedMain"/>
<module name="UpperEll"/>
<!-- Modifiers -->
<module name="ModifierOrder"/>
<module name="RedundantModifier"/>
<!-- Naming Conventions -->
<module name="AbbreviationAsWordInName">
<property name="ignoreFinal" value="false"/>
<property name="allowedAbbreviationLength" value="0"/>
<property name="allowedAbbreviations" value="MV"/>
<property name="tokens"
value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, ANNOTATION_DEF, ANNOTATION_FIELD_DEF,
PARAMETER_DEF, VARIABLE_DEF, METHOD_DEF, PATTERN_VARIABLE_DEF, RECORD_DEF,
RECORD_COMPONENT_DEF"/>
</module>
<module name="AbstractClassName">
<property name="ignoreName" value="true"/>
<message key="name.invalidPattern"
value="Abstract class name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="CatchParameterName">
<property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/>
<message key="name.invalidPattern"
value="Catch parameter name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="ClassTypeParameterName">
<property name="format" value="^[A-Z]{1,3}$"/>
<message key="name.invalidPattern"
value="Class type name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="ConstantName">
<message key="name.invalidPattern"
value="Constant name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="InterfaceTypeParameterName">
<property name="format" value="^[A-Z]{1,3}$"/>
<message key="name.invalidPattern"
value="Interface type name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="LambdaParameterName">
<property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/>
<message key="name.invalidPattern"
value="Lambda parameter name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="LocalFinalVariableName">
<property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/>
<message key="name.invalidPattern"
value="Local final variable name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="LocalVariableName">
<property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/>
<message key="name.invalidPattern"
value="Local variable name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="MemberName">
<property name="format" value="^[a-z][a-z0-9][a-zA-Z0-9]*$|^[A-Z][A-Z0-9_]*$"/>
<message key="name.invalidPattern"
value="Member name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="MethodName">
<message key="name.invalidPattern"
value="Method name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="MethodTypeParameterName">
<property name="format" value="^[A-Z]{1,3}$"/>
<message key="name.invalidPattern"
value="Method type name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="PackageName">
<property name="severity" value="info"/>
<property name="format" value="^[a-z]+(\.[a-z][a-z0-9]*)*$"/>
<message key="name.invalidPattern"
value="Package name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="ParameterName">
<property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/>
<message key="name.invalidPattern"
value="Parameter name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="PatternVariableName">
<property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/>
<message key="name.invalidPattern"
value="Pattern variable name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="RecordComponentName">
<property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/>
<message key="name.invalidPattern"
value="Record component name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="RecordTypeParameterName">
<property name="format" value="^[A-Z]{1,3}$"/>
<message key="name.invalidPattern"
value="Record type name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="StaticVariableName">
<message key="name.invalidPattern"
value="Static variable name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="TypeName">
<property name="tokens" value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF,
ANNOTATION_DEF, RECORD_DEF"/>
<message key="name.invalidPattern"
value="Type name ''{0}'' must match pattern ''{1}''."/>
</module>
<!-- Size Violations -->
<module name="AnonInnerLength">
<property name="max" value="20"/>
</module>
<module name="ExecutableStatementCount">
<property name="max" value="30"/>
</module>
<module name="LambdaBodyLength">
<property name="id" value="LambdaGettingTooLong"/>
<property name="max" value="10"/>
<property name="severity" value="info"/>
</module>
<module name="LambdaBodyLength">
<property name="id" value="LambdaProbablyTooLong"/>
<property name="max" value="25"/>
</module>
<module name="MethodCount">
<property name="maxTotal" value="100"/>
<property name="maxPrivate" value="100"/>
<property name="maxPackage" value="100"/>
<property name="maxProtected" value="100"/>
<property name="maxPublic" value="100"/>
</module>
<module name="MethodLength">
<property name="max" value="50"/>
<property name="severity" value="warning"/>
</module>
<module name="OuterTypeNumber">
<property name="max" value="1"/>
</module>
<module name="ParameterNumber">
<property name="id" value="ConsiderLessMethodParameters"/>
<property name="max" value="4"/>
<property name="ignoreOverriddenMethods" value="true"/>
<property name="severity" value="info"/>
<property name="tokens" value="METHOD_DEF"/>
</module>
<module name="ParameterNumber">
<property name="id" value="ProbablyTooManyMethodParameters"/>
<property name="max" value="10"/>
<property name="ignoreOverriddenMethods" value="true"/>
<property name="tokens" value="METHOD_DEF"/>
</module>
<module name="ParameterNumber">
<property name="id" value="ConsiderLessConstructorParameters"/>
<property name="max" value="7"/>
<property name="ignoreOverriddenMethods" value="true"/>
<property name="severity" value="info"/>
<property name="tokens" value="CTOR_DEF"/>
</module>
<module name="ParameterNumber">
<property name="id" value="ProbablyTooManyConstructorParameters"/>
<property name="max" value="12"/>
<property name="ignoreOverriddenMethods" value="true"/>
<property name="tokens" value="CTOR_DEF"/>
</module>
<module name="RecordComponentNumber">
<property name="max" value="8"/>
</module>
<!-- Whitespace -->
<module name="EmptyForInitializerPad"/>
<module name="EmptyForIteratorPad"/>
<module name="EmptyLineSeparator">
<property name="allowNoEmptyLineBetweenFields" value="true"/>
<property name="allowMultipleEmptyLines" value="false"/>
<property name="allowMultipleEmptyLinesInsideClassMembers" value="false"/>
<property name="tokens"
value="PACKAGE_DEF, IMPORT, STATIC_IMPORT, CLASS_DEF, INTERFACE_DEF, ENUM_DEF,
STATIC_INIT, INSTANCE_INIT, METHOD_DEF, CTOR_DEF, VARIABLE_DEF, RECORD_DEF,
COMPACT_CTOR_DEF"/>
</module>
<module name="GenericWhitespace">
<message key="ws.followed"
value="GenericWhitespace ''{0}'' is followed by whitespace."/>
<message key="ws.preceded"
value="GenericWhitespace ''{0}'' is preceded with whitespace."/>
<message key="ws.illegalFollow"
value="GenericWhitespace ''{0}'' should followed by whitespace."/>
<message key="ws.notPreceded"
value="GenericWhitespace ''{0}'' is not preceded with whitespace."/>
</module>
<module name="MethodParamPad">
<property name="tokens"
value="CTOR_DEF, LITERAL_NEW, METHOD_CALL, METHOD_DEF,
SUPER_CTOR_CALL, ENUM_CONSTANT_DEF, RECORD_DEF"/>
</module>
<module name="NoLineWrap">
<property name="tokens" value="PACKAGE_DEF, IMPORT, STATIC_IMPORT"/>
</module>
<module name="NoWhitespaceAfter">
<property name="tokens"
value="AT, INC, DEC, UNARY_MINUS,UNARY_PLUS,BNOT,LNOT,DOT,ARRAY_DECLARATOR,INDEX_OP,LITERAL_SYNCHRONIZED,METHOD_REF"/>
</module>
<module name="NoWhitespaceBefore">
<property name="id" value="AllowedWhitespaceBeforeWithoutLinebreak"/>
<property name="tokens"
value="COMMA, SEMI, POST_INC, POST_DEC, ELLIPSIS, LABELED_STAT"/>
</module>
<module name="NoWhitespaceBefore">
<property name="id" value="AllowedWhitespaceBeforeWithLinebreak"/>
<property name="tokens" value="DOT, METHOD_REF"/>
<property name="allowLineBreaks" value="true"/>
</module>
<module name="NoWhitespaceBeforeCaseDefaultColon"/>
<module name="OperatorWrap">
<property name="tokens"
value="QUESTION, COLON, EQUAL, NOT_EQUAL, DIV, PLUS, MINUS, STAR, MOD,
SR, BSR, GE, GT, SL, LE, LT, BXOR, BOR, LOR, BAND, LAND, LITERAL_INSTANCEOF,
TYPE_EXTENSION_AND, ASSIGN, DIV_ASSIGN, PLUS_ASSIGN, MINUS_ASSIGN,
STAR_ASSIGN, MOD_ASSIGN, SR_ASSIGN, BSR_ASSIGN, SL_ASSIGN, BXOR_ASSIGN,
BOR_ASSIGN, BAND_ASSIGN, METHOD_REF"/>
</module>
<module name="ParenPad">
<property name="tokens"
value="ANNOTATION, ANNOTATION_FIELD_DEF, CTOR_CALL, CTOR_DEF, DOT, ENUM_CONSTANT_DEF,
EXPR, LITERAL_CATCH, LITERAL_DO, LITERAL_FOR, LITERAL_IF, LITERAL_NEW,
LITERAL_SWITCH, LITERAL_SYNCHRONIZED, LITERAL_WHILE, METHOD_CALL,
METHOD_DEF, QUESTION, RESOURCE_SPECIFICATION, SUPER_CTOR_CALL, LAMBDA,
RECORD_DEF"/>
</module>
<module name="SeparatorWrap">
<property name="id" value="SeparatorWrapNl"/>
<property name="option" value="nl"/>
<property name="tokens" value="DOT, AT, METHOD_REF"/>
</module>
<module name="SeparatorWrap">
<property name="id" value="SeparatorWrapEol"/>
<property name="option" value="eol"/>
<property name="tokens"
value="COMMA, SEMI, ELLIPSIS, RPAREN, ARRAY_DECLARATOR, RBRACK"/>
</module>
<module name="SingleSpaceSeparator"/>
<module name="TypecastParenPad"/>
<module name="WhitespaceAfter">
<property name="tokens"
value="COMMA, SEMI, TYPECAST, LITERAL_IF, LITERAL_ELSE, LITERAL_RETURN,
LITERAL_WHILE, LITERAL_DO, LITERAL_FOR, LITERAL_FINALLY, DO_WHILE, ELLIPSIS,
LITERAL_SWITCH, LITERAL_SYNCHRONIZED, LITERAL_TRY, LITERAL_CATCH, LAMBDA,
LITERAL_YIELD, LITERAL_CASE"/>
</module>
<module name="WhitespaceAround">
<property name="allowEmptyConstructors" value="true"/>
<property name="allowEmptyLambdas" value="true"/>
<property name="allowEmptyMethods" value="true"/>
<property name="allowEmptyTypes" value="true"/>
<property name="allowEmptyLoops" value="true"/>
<property name="ignoreEnhancedForColon" value="false"/>
<property name="tokens"
value="ASSIGN, BAND, BAND_ASSIGN, BOR, BOR_ASSIGN, BSR, BSR_ASSIGN, BXOR,
BXOR_ASSIGN, COLON, DIV, DIV_ASSIGN, DO_WHILE, EQUAL, GE, GT, LAMBDA, LAND,
LCURLY, LE, LITERAL_CATCH, LITERAL_DO, LITERAL_ELSE, LITERAL_FINALLY,
LITERAL_FOR, LITERAL_IF, LITERAL_RETURN, LITERAL_SWITCH, LITERAL_SYNCHRONIZED,
LITERAL_TRY, LITERAL_WHILE, LOR, LT, MINUS, MINUS_ASSIGN, MOD, MOD_ASSIGN,
NOT_EQUAL, PLUS, PLUS_ASSIGN, QUESTION, RCURLY, SL, SLIST, SL_ASSIGN, SR,
SR_ASSIGN, STAR, STAR_ASSIGN, LITERAL_ASSERT, TYPE_EXTENSION_AND"/>
<message key="ws.notFollowed"
value="WhitespaceAround: ''{0}'' is not followed by whitespace. Empty blocks
may only be represented as '{}' when not part of a multi-block statement"/>
<message key="ws.notPreceded"
value="WhitespaceAround: ''{0}'' is not preceded with whitespace."/>
</module>
</module>
</module>
View File
Whitespace-only changes.
Binary file not shown.
-7
View File
@@ -1,7 +0,0 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Vendored
-252
View File
@@ -1,252 +0,0 @@
#!/bin/sh
#
# Copyright © 2015-2021 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# SPDX-License-Identifier: Apache-2.0
#
##############################################################################
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done
# This is normally unused
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
' "$PWD" ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
warn () {
echo "$*"
} >&2
die () {
echo
echo "$*"
echo
exit 1
} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "$( uname )" in #(
CYGWIN* ) cygwin=true ;; #(
Darwin* ) darwin=true ;; #(
MSYS* | MINGW* ) msys=true ;; #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD=$JAVA_HOME/jre/sh/java
else
JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD=java
if ! command -v java >/dev/null 2>&1
then
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
fi
# Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
die "xargs is not available"
fi
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@"
Vendored
-94
View File
@@ -1,94 +0,0 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@rem SPDX-License-Identifier: Apache-2.0
@rem
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=.
@rem This is normally unused
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute
echo. 1>&2
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo. 1>&2
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if %ERRORLEVEL% equ 0 goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

+71
View File
@@ -0,0 +1,71 @@
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8'>
<meta http-equiv="X-UA-Compatible" content="chrome=1">
<link href='https://fonts.googleapis.com/css?family=Chivo:900' rel='stylesheet' type='text/css'>
<link rel="stylesheet" type="text/css" href="stylesheets/stylesheet.css" media="screen" />
<link rel="stylesheet" type="text/css" href="stylesheets/pygment_trac.css" media="screen" />
<link rel="stylesheet" type="text/css" href="stylesheets/print.css" media="print" />
<!--[if lt IE 9]>
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<title>Multiverse-Core by Multiverse</title>
</head>
<body>
<div id="container">
<div class="inner">
<header>
<h1>Multiverse-core</h1>
<h2>The original Bukkit Multi-World Plugin! </h2>
</header>
<section id="downloads" class="clearfix">
<a href="https://github.com/Multiverse/Multiverse-Core/zipball/master" id="download-zip" class="button"><span>Download .zip</span></a>
<a href="https://github.com/Multiverse/Multiverse-Core/tarball/master" id="download-tar-gz" class="button"><span>Download .tar.gz</span></a>
<a href="https://github.com/Multiverse/Multiverse-Core" id="view-on-github" class="button"><span>View on GitHub</span></a>
</section>
<hr>
<section id="main_content">
<h3>Welcome to GitHub Pages.</h3>
<p>This automatic page generator is the easiest way to create beautiful pages for all of your projects. Author your page content here using GitHub Flavored Markdown, select a template crafted by a designer, and publish. After your page is generated, you can check out the new branch:</p>
<pre><code>$ cd your_repo_root/repo_name
$ git fetch origin
$ git checkout gh-pages
</code></pre>
<p>If you're using the GitHub for Mac, simply sync your repository and you'll see the new branch.</p>
<h3>Designer Templates</h3>
<p>We've crafted some handsome templates for you to use. Go ahead and continue to layouts to browse through them. You can easily go back to edit your page before publishing. After publishing your page, you can revisit the page generator and switch to another theme. Your Page content will be preserved if it remained markdown format.</p>
<h3>Rather Drive Stick?</h3>
<p>If you prefer to not use the automatic generator, push a branch named <code>gh-pages</code> to your repository to create a page manually. In addition to supporting regular HTML content, GitHub Pages support Jekyll, a simple, blog aware static site generator written by our own Tom Preston-Werner. Jekyll makes it easy to create site-wide headers and footers without having to copy them across every page. It also offers intelligent blog support and other advanced templating features.</p>
<h3>Authors and Contributors</h3>
<p>You can <a href="https://github.com/blog/821" class="user-mention">@mention</a> a GitHub username to generate a link to their profile. The resulting <code>&lt;a&gt;</code> element will link to the contributor's GitHub Profile. For example: In 2007, Chris Wanstrath (<a href="https://github.com/defunkt" class="user-mention">@defunkt</a>), PJ Hyett (<a href="https://github.com/pjhyett" class="user-mention">@pjhyett</a>), and Tom Preston-Werner (<a href="https://github.com/mojombo" class="user-mention">@mojombo</a>) founded GitHub.</p>
<h3>Support or Contact</h3>
<p>Having trouble with Pages? Check out the documentation at <a href="http://help.github.com/pages">http://help.github.com/pages</a> or contact <a href="mailto:support@github.com">support@github.com</a> and we’ll help you sort it out.</p>
</section>
<footer>
Multiverse-core is maintained by <a href="https://github.com/Multiverse">Multiverse</a><br>
This page was generated by <a href="http://pages.github.com">GitHub Pages</a>. Tactile theme by <a href="http://twitter.com/jasonlong">Jason Long</a>.
</footer>
</div>
</div>
</body>
</html>
+1
View File
@@ -0,0 +1 @@
console.log('This would be the main JS file.');
+1
View File
@@ -0,0 +1 @@
{"tagline":"The original Bukkit Multi-World Plugin! ","body":"### Welcome to GitHub Pages.\r\nThis automatic page generator is the easiest way to create beautiful pages for all of your projects. Author your page content here using GitHub Flavored Markdown, select a template crafted by a designer, and publish. After your page is generated, you can check out the new branch:\r\n\r\n```\r\n$ cd your_repo_root/repo_name\r\n$ git fetch origin\r\n$ git checkout gh-pages\r\n```\r\n\r\nIf you're using the GitHub for Mac, simply sync your repository and you'll see the new branch.\r\n\r\n### Designer Templates\r\nWe've crafted some handsome templates for you to use. Go ahead and continue to layouts to browse through them. You can easily go back to edit your page before publishing. After publishing your page, you can revisit the page generator and switch to another theme. Your Page content will be preserved if it remained markdown format.\r\n\r\n### Rather Drive Stick?\r\nIf you prefer to not use the automatic generator, push a branch named `gh-pages` to your repository to create a page manually. In addition to supporting regular HTML content, GitHub Pages support Jekyll, a simple, blog aware static site generator written by our own Tom Preston-Werner. Jekyll makes it easy to create site-wide headers and footers without having to copy them across every page. It also offers intelligent blog support and other advanced templating features.\r\n\r\n### Authors and Contributors\r\nYou can @mention a GitHub username to generate a link to their profile. The resulting `<a>` element will link to the contributor's GitHub Profile. For example: In 2007, Chris Wanstrath (@defunkt), PJ Hyett (@pjhyett), and Tom Preston-Werner (@mojombo) founded GitHub.\r\n\r\n### Support or Contact\r\nHaving trouble with Pages? Check out the documentation at http://help.github.com/pages or contact support@github.com and we’ll help you sort it out.","note":"Don't delete this file! It's used internally to help with page regeneration.","google":"","name":"Multiverse-core"}
-11
View File
@@ -1,11 +0,0 @@
pluginManagement {
repositories {
gradlePluginPortal()
maven {
url = uri("https://repo.onarandombox.com/multiverse-releases")
}
}
}
rootProject.name = "Multiverse-Core"
@@ -1,108 +0,0 @@
package org.mvplugins.multiverse.core;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.function.Consumer;
import com.dumptruckman.minecraft.util.Logging;
import jakarta.annotation.PostConstruct;
import jakarta.inject.Inject;
import org.apache.commons.lang3.text.WordUtils;
import org.bstats.bukkit.Metrics;
import org.bstats.charts.AdvancedPie;
import org.bstats.charts.SingleLineChart;
import org.bukkit.World;
import org.jvnet.hk2.annotations.Service;
import org.mvplugins.multiverse.core.utils.REPatterns;
import org.mvplugins.multiverse.core.world.MultiverseWorld;
import org.mvplugins.multiverse.core.world.WorldManager;
/**
* Helper class that registers and sends metrics to <a href="https://bstats.org">bstats website</a>.
*/
@Service
final class BstatsMetricsConfigurator {
private static final int PLUGIN_ID = 7765;
private static final String NO_GENERATOR_NAME = "N/A";
private final WorldManager worldManager;
private final Metrics metrics;
@Inject
private BstatsMetricsConfigurator(MultiverseCore plugin, WorldManager worldManager) {
this.worldManager = worldManager;
this.metrics = new Metrics(plugin, PLUGIN_ID);
}
@PostConstruct
private void initMetrics() {
try {
addCustomGeneratorsMetric();
addEnvironmentsMetric();
addWorldCountMetric();
Logging.fine("Metrics enabled.");
} catch (Exception e) {
Logging.warning("There was an issue while enabling metrics:");
e.printStackTrace();
}
}
private void addCustomGeneratorsMetric() {
addAdvancedPieMetric("custom_generators", map -> {
for (MultiverseWorld w : worldManager.getLoadedWorlds()) {
incrementCount(map, getGeneratorName(w));
}
});
}
private String getGeneratorName(MultiverseWorld world) {
String gen = world.getGenerator();
return (gen != null && !gen.equalsIgnoreCase("null"))
? REPatterns.COLON.split(gen)[0]
: NO_GENERATOR_NAME;
}
private void addEnvironmentsMetric() {
addAdvancedPieMetric("environments", map -> {
for (MultiverseWorld w : worldManager.getLoadedWorlds()) {
incrementCount(map, titleCaseEnv(w.getEnvironment()));
}
});
}
private String titleCaseEnv(World.Environment env) {
String envName = env.name().replaceAll("_+", " ");
return WordUtils.capitalizeFully(envName);
}
private void addWorldCountMetric() {
addLineMetric("worlds", () -> worldManager.getWorlds().size());
}
private void addAdvancedPieMetric(String chartId, Consumer<Map<String, Integer>> metricsFunc) {
metrics.addCustomChart(createAdvancedPieChart(chartId, metricsFunc));
}
private void addLineMetric(String chartId, Callable<Integer> metricsFunc) {
metrics.addCustomChart(createLineChart(chartId, metricsFunc));
}
private void incrementCount(Map<String, Integer> map, String key) {
Integer count = map.getOrDefault(key, 0);
map.put(key, count + 1);
}
private AdvancedPie createAdvancedPieChart(String chartId, Consumer<Map<String, Integer>> metricsFunc) {
Map<String, Integer> map = new HashMap<>();
metricsFunc.accept(map);
return new AdvancedPie(chartId, () -> map);
}
private SingleLineChart createLineChart(String chartId, Callable<Integer> metricsFunc) {
return new SingleLineChart(chartId, metricsFunc);
}
}
@@ -1,299 +0,0 @@
/******************************************************************************
* Multiverse 2 Copyright (c) the Multiverse Team 2011. *
* Multiverse 2 is licensed under the BSD License. *
* For more information please check the README.md file included *
* with this project. *
******************************************************************************/
package org.mvplugins.multiverse.core;
import java.util.logging.Logger;
import com.dumptruckman.minecraft.util.Logging;
import io.vavr.control.Try;
import jakarta.inject.Inject;
import jakarta.inject.Provider;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.serialization.ConfigurationSerialization;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Service;
import org.mvplugins.multiverse.core.anchor.AnchorManager;
import org.mvplugins.multiverse.core.destination.Destination;
import org.mvplugins.multiverse.core.destination.DestinationsProvider;
import org.mvplugins.multiverse.core.commands.CoreCommand;
import org.mvplugins.multiverse.core.config.CoreConfig;
import org.mvplugins.multiverse.core.economy.MVEconomist;
import org.mvplugins.multiverse.core.listeners.CoreListener;
import org.mvplugins.multiverse.core.inject.PluginServiceLocatorFactory;
import org.mvplugins.multiverse.core.module.MultiverseModule;
import org.mvplugins.multiverse.core.utils.StringFormatter;
import org.mvplugins.multiverse.core.world.WorldManager;
import org.mvplugins.multiverse.core.world.location.NullSpawnLocation;
import org.mvplugins.multiverse.core.world.location.SpawnLocation;
import org.mvplugins.multiverse.core.world.location.UnloadedWorldLocation;
/**
* The start of the Multiverse-Core plugin
*/
@Service
public class MultiverseCore extends MultiverseModule {
@Inject
private Provider<CoreConfig> configProvider;
@Inject
private Provider<WorldManager> worldManagerProvider;
@Inject
private Provider<AnchorManager> anchorManagerProvider;
@Inject
private Provider<DestinationsProvider> destinationsProviderProvider;
@Inject
private Provider<BstatsMetricsConfigurator> metricsConfiguratorProvider;
@Inject
private Provider<MVEconomist> economistProvider;
/**
* This is the constructor for the MultiverseCore.
*/
public MultiverseCore() {
super();
}
@Override
public void onLoad() {
// Setup our Logging
Logging.init(this);
// Create our DataFolder
if (!getDataFolder().exists() && !getDataFolder().mkdirs()) {
Logging.severe("Failed to create data folder!");
getServer().getPluginManager().disablePlugin(this);
return;
}
// Register our config classes
ConfigurationSerialization.registerClass(NullSpawnLocation.class);
ConfigurationSerialization.registerClass(SpawnLocation.class);
ConfigurationSerialization.registerClass(UnloadedWorldLocation.class);
}
/**
* {@inheritDoc}
*/
@Override
public void onEnable() {
super.onEnable();
initializeDependencyInjection(new MultiverseCorePluginBinder(this));
// Load our configs first as we need them for everything else.
var config = configProvider.get();
var loadSuccess = config.load().andThenTry(config::save).isSuccess();
if (!loadSuccess || !config.isLoaded()) {
Logging.severe("Your configs were not loaded.");
Logging.severe("Please check your configs and restart the server.");
this.getServer().getPluginManager().disablePlugin(this);
return;
}
Logging.setShowingConfig(shouldShowConfig());
// Initialize the worlds
worldManagerProvider.get().initAllWorlds().andThenTry(() -> {
loadEconomist(); // Setup economy here so vault is loaded
loadAnchors();
registerDynamicListeners(CoreListener.class);
setUpLocales();
registerCommands(CoreCommand.class);
registerDestinations();
setupMetrics();
loadPlaceholderApiIntegration();
loadApiService();
saveAllConfigs();
logEnableMessage();
}).onFailure(e -> {
Logging.severe("Failed to multiverse core! Disabling...");
e.printStackTrace();
getServer().getPluginManager().disablePlugin(this);
});
}
/**
* {@inheritDoc}
*/
@Override
public void onDisable() {
super.onDisable();
MultiverseCoreApi.shutdown();
shutdownDependencyInjection();
PluginServiceLocatorFactory.get().shutdown();
Logging.shutdown();
}
private boolean shouldShowConfig() {
return !configProvider.get().getSilentStart();
}
private void loadEconomist() {
Try.run(() -> economistProvider.get())
.onFailure(e -> {
Logging.severe("Failed to load economy integration");
e.printStackTrace();
});
}
private void loadAnchors() {
Try.of(() -> anchorManagerProvider.get())
.flatMap(AnchorManager::loadAnchors)
.onFailure(e -> {
Logging.severe("Failed to load anchors");
e.printStackTrace();
});
}
/**
* Register all the destinations.
*/
private void registerDestinations() {
Try.of(() -> destinationsProviderProvider.get())
.andThenTry(destinationsProvider -> {
serviceLocator.getAllServices(Destination.class)
.forEach(destinationsProvider::registerDestination);
})
.onFailure(e -> {
Logging.severe("Failed to register destinations");
e.printStackTrace();
});
}
/**
* Setup bstats Metrics.
*/
private void setupMetrics() {
if (TestingMode.isDisabled()) {
// Load metrics
Try.of(() -> metricsConfiguratorProvider.get())
.onFailure(e -> {
Logging.severe("Failed to setup metrics");
e.printStackTrace();
});
} else {
Logging.info("Metrics are disabled in testing mode.");
}
}
/**
* Setup placeholder api hook
*/
private void loadPlaceholderApiIntegration() {
if (configProvider.get().isRegisterPapiHook()
&& getServer().getPluginManager().getPlugin("PlaceholderAPI") != null) {
Try.run(() -> serviceLocator.getService(PlaceholderExpansionHook.class))
.onFailure(e -> {
Logging.severe("Failed to load PlaceholderAPI integration.");
e.printStackTrace();
});
}
}
/**
* Setup the api service for {@link MultiverseCoreApi}
*/
private void loadApiService() {
Try.run(() -> MultiverseCoreApi.init(this))
.onSuccess(ignore -> Logging.info("API service loaded!"))
.onFailure(e -> {
Logging.severe("Failed to load API service!");
e.printStackTrace();
});
}
/**
* Save config.yml, worlds.yml, and anchors.yml.
*
* @return {@link Try#isSuccess()} true if all configs were successfully saved
*/
private Try<Void> saveAllConfigs() {
return configProvider.get().save()
.andThenTry(() -> worldManagerProvider.get().saveWorldsConfig())
.andThenTry(() -> anchorManagerProvider.get().saveAllAnchors())
.onFailure(e ->
Logging.severe("Failed to save all configs, things may not work as expected. %s",
e.getLocalizedMessage()));
}
/**
* Logs the enable message.
*/
private void logEnableMessage() {
Logging.config("\u001B[32mVersion %s (API v%s) Enabled - By %s\u001B[39m",
this.getDescription().getVersion(), getVersionAsNumber(), StringFormatter.joinAnd(getDescription().getAuthors()));
if (configProvider.get().isShowingDonateMessage()) {
Logging.config("\u001B[32mLoving Multiverse-Core? Please consider supporting the project with a small donation: https://github.com/sponsors/Multiverse\u001B[39m");
}
}
/**
* Gets the MultiverseCoreApi
*
* @return The MultiverseCoreApi
*
* @deprecated Use {@link MultiverseCoreApi#get()} directly.
*/
@Deprecated(since = "5.1", forRemoval = true)
@ApiStatus.ScheduledForRemoval(inVersion = "6.0")
public MultiverseCoreApi getApi() {
return MultiverseCoreApi.get();
}
/**
* {@inheritDoc}
*/
@Override
public double getTargetCoreVersion() {
return getVersionAsNumber();
}
/**
* {@inheritDoc}
*/
@NotNull
@Override
public Logger getLogger() {
return Logging.getLogger();
}
/**
* {@inheritDoc}
*/
@Override
public @NotNull FileConfiguration getConfig() {
CoreConfig coreConfig = this.configProvider.get();
var config = coreConfig.getConfig();
if (config != null && coreConfig.isLoaded()) {
return config;
}
var loadSuccess = coreConfig.load().isSuccess();
if (!loadSuccess || !coreConfig.isLoaded()) {
throw new RuntimeException("Failed to load configs");
}
return coreConfig.getConfig();
}
/**
* {@inheritDoc}
*/
@Override
public void reloadConfig() {
this.configProvider.get().load();
}
/**
* {@inheritDoc}
*/
@Override
public void saveConfig() {
this.configProvider.get().save();
}
}
@@ -1,205 +0,0 @@
package org.mvplugins.multiverse.core;
import org.bukkit.Bukkit;
import org.bukkit.plugin.ServicePriority;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.mvplugins.multiverse.core.anchor.AnchorManager;
import org.mvplugins.multiverse.core.config.CoreConfig;
import org.mvplugins.multiverse.core.destination.DestinationsProvider;
import org.mvplugins.multiverse.core.economy.MVEconomist;
import org.mvplugins.multiverse.core.inject.PluginServiceLocator;
import org.mvplugins.multiverse.core.teleportation.AsyncSafetyTeleporter;
import org.mvplugins.multiverse.core.teleportation.BlockSafety;
import org.mvplugins.multiverse.core.teleportation.LocationManipulation;
import org.mvplugins.multiverse.core.world.WorldManager;
import org.mvplugins.multiverse.core.world.biomeprovider.BiomeProviderFactory;
import org.mvplugins.multiverse.core.world.generators.GeneratorProvider;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.function.Consumer;
/**
* Provides access to the MultiverseCore API.
*/
public class MultiverseCoreApi {
private static MultiverseCoreApi instance;
private static final List<Consumer<MultiverseCoreApi>> whenLoadedCallbacks = new ArrayList<>();
static void init(@NotNull MultiverseCore multiverseCore) {
if (instance != null) {
throw new IllegalStateException("MultiverseCoreApi has already been initialized!");
}
instance = new MultiverseCoreApi(multiverseCore.getServiceLocator());
Bukkit.getServicesManager().register(MultiverseCoreApi.class, instance, multiverseCore, ServicePriority.Normal);
whenLoadedCallbacks.forEach(c -> c.accept(instance));
whenLoadedCallbacks.clear();
}
static void shutdown() {
Bukkit.getServicesManager().unregister(instance);
instance = null;
}
/**
* Hook your plugin into the MultiverseCoreApi here to ensure you only start using the API after it has been initialized.
* Use this if you know your plugin may load before Multiverse-Core is fully initialized.
* <br/>
* This handy method removes the need for you to check with plugin manager or listen to plugin enable event.
* <br/>
* Callback will be called immediately if the MultiverseCoreApi has already been initialized.
*
* @param consumer The callback to execute when the MultiverseCoreApi has been initialized.
*
* @since 5.1
*/
@ApiStatus.AvailableSince("5.1")
public static void whenLoaded(@NotNull Consumer<MultiverseCoreApi> consumer) {
if (instance != null) {
consumer.accept(instance);
} else {
whenLoadedCallbacks.add(consumer);
}
}
/**
* Checks if the MultiverseCoreApi has been initialized.
*
* @return True if the MultiverseCoreApi has been initialized, false otherwise
*
* @since 5.1
*/
@ApiStatus.AvailableSince("5.1")
public static boolean isLoaded() {
return instance != null;
}
/**
* Gets the MultiverseCoreApi. This will throw an exception if the Multiverse-Core has not been initialized.
* <br/>
* You can check if the MultiverseCoreApi has been initialized with {@link #isLoaded()} before using this method.
* <br/>
* Alternatively, you can use {@link #whenLoaded(Consumer)} to hook into the MultiverseCoreApi if your plugin may
* load before Multiverse-Core is fully initialized.
*
* @return The MultiverseCoreApi
* @throws IllegalStateException if the MultiverseCoreApi has not been initialized
*/
public static @NotNull MultiverseCoreApi get() {
if (instance == null) {
throw new IllegalStateException("MultiverseCoreApi has not been initialized!");
}
return instance;
}
private final PluginServiceLocator serviceLocator;
private MultiverseCoreApi(@NotNull PluginServiceLocator serviceProvider) {
this.serviceLocator = serviceProvider;
}
/**
* Gets the instance of AnchorManager.
*
* @return The AnchorManager instance
*/
public @NotNull AnchorManager getAnchorManager() {
return Objects.requireNonNull(serviceLocator.getActiveService(AnchorManager.class));
}
/**
* Gets the instance of BiomeProviderFactory.
*
* @return The BiomeProviderFactory instance
*/
public @NotNull BiomeProviderFactory getBiomeProviderFactory() {
return Objects.requireNonNull(serviceLocator.getActiveService(BiomeProviderFactory.class));
}
/**
* Gets the instance of BlockSafety.
*
* @return The BlockSafety instance
*/
public @NotNull BlockSafety getBlockSafety() {
return Objects.requireNonNull(serviceLocator.getActiveService(BlockSafety.class));
}
/**
* Gets the instance of CoreConfig.
*
* @return The CoreConfig instance
*/
public @NotNull CoreConfig getCoreConfig() {
return Objects.requireNonNull(serviceLocator.getActiveService(CoreConfig.class));
}
/**
* Gets the instance of DestinationsProvider.
*
* @return The DestinationsProvider instance
*/
public @NotNull DestinationsProvider getDestinationsProvider() {
return Objects.requireNonNull(serviceLocator.getActiveService(DestinationsProvider.class));
}
/**
* Gets the instance of GeneratorProvider.
*
* @return The GeneratorProvider instance
*/
public @NotNull GeneratorProvider getGeneratorProvider() {
return Objects.requireNonNull(serviceLocator.getActiveService(GeneratorProvider.class));
}
/**
* Gets the instance of LocationManipulation.
*
* @return The LocationManipulation instance
*/
public @NotNull LocationManipulation getLocationManipulation() {
return Objects.requireNonNull(serviceLocator.getActiveService(LocationManipulation.class));
}
/**
* Gets the instance of MVEconomist.
*
* @return The MVEconomist instance
*/
public @NotNull MVEconomist getMVEconomist() {
return Objects.requireNonNull(serviceLocator.getActiveService(MVEconomist.class));
}
/**
* Gets the instance of SafetyTeleporter.
*
* @return The SafetyTeleporter instance
*/
public @NotNull AsyncSafetyTeleporter getSafetyTeleporter() {
return Objects.requireNonNull(serviceLocator.getActiveService(AsyncSafetyTeleporter.class));
}
/**
* Gets the instance of WorldManager.
*
* @return The WorldManager instance
*/
public @NotNull WorldManager getWorldManager() {
return Objects.requireNonNull(serviceLocator.getActiveService(WorldManager.class));
}
/**
* Gets the instance of Multiverse-Core's PluginServiceLocator.
* <br/>
* You can use this to hook into the hk2 dependency injection system used by Multiverse-Core.
*
* @return The Multiverse-Core's PluginServiceLocator
*/
public @NotNull PluginServiceLocator getServiceLocator() {
return serviceLocator;
}
}
@@ -1,19 +0,0 @@
package org.mvplugins.multiverse.core;
import org.glassfish.hk2.utilities.binding.ScopedBindingBuilder;
import org.jetbrains.annotations.NotNull;
import org.mvplugins.multiverse.core.module.MultiverseModuleBinder;
final class MultiverseCorePluginBinder extends MultiverseModuleBinder<MultiverseCore> {
MultiverseCorePluginBinder(@NotNull MultiverseCore plugin) {
super(plugin);
}
@Override
protected ScopedBindingBuilder<MultiverseCore> bindPluginClass(
ScopedBindingBuilder<MultiverseCore> bindingBuilder) {
return super.bindPluginClass(bindingBuilder).to(MultiverseCore.class);
}
}
@@ -1,215 +0,0 @@
package org.mvplugins.multiverse.core;
import com.google.common.collect.Lists;
import jakarta.annotation.PostConstruct;
import jakarta.inject.Inject;
import me.clip.placeholderapi.expansion.PlaceholderExpansion;
import org.bukkit.OfflinePlayer;
import org.bukkit.World;
import org.bukkit.entity.Player;
import org.bukkit.entity.SpawnCategory;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jvnet.hk2.annotations.Service;
import org.mvplugins.multiverse.core.economy.MVEconomist;
import org.mvplugins.multiverse.core.utils.MinecraftTimeFormatter;
import org.mvplugins.multiverse.core.utils.REPatterns;
import org.mvplugins.multiverse.core.utils.StringFormatter;
import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld;
import org.mvplugins.multiverse.core.world.WorldManager;
import java.util.List;
import java.util.Locale;
@Service
final class PlaceholderExpansionHook extends PlaceholderExpansion {
private final MultiverseCore plugin;
private final WorldManager worldManager;
private final MVEconomist economist;
@Inject
public PlaceholderExpansionHook(MultiverseCore plugin, WorldManager worldManager, MVEconomist economist) {
this.plugin = plugin;
this.worldManager = worldManager;
this.economist = economist;
}
@PostConstruct
@Override
public boolean register() {
return super.register();
}
@Override
public @NotNull String getIdentifier() {
return "multiverse-core";
}
@Override
public @NotNull String getAuthor() {
return StringFormatter.joinAnd(plugin.getDescription().getAuthors());
}
@Override
public @NotNull String getVersion() {
return plugin.getDescription().getVersion();
}
@Override
public boolean persist() {
return true;
}
/**
* Placeholder implementation, format: %multiverse-core_&lt;placeholder&gt;_[world]% world is optional.
*
* @param offlinePlayer Player to get the placeholder for
* @param params Placeholder to get
* @return Placeholder value
*/
@Override
public @Nullable String onRequest(OfflinePlayer offlinePlayer, @NotNull String params) {
// Split string in to an Array with underscores
List<String> paramsArray = Lists.newArrayList(REPatterns.UNDERSCORE.split(params));
// No placeholder defined
if (paramsArray.isEmpty()) {
warning("No placeholder key defined");
return null;
}
final var placeholder = paramsArray.remove(0);
String worldName = parseWorldName(offlinePlayer, paramsArray);
if (worldName == null) return null;
return worldManager.getLoadedWorld(worldName)
.onEmpty(() -> warning("Multiverse World not found: " + worldName))
.map(world -> getWorldPlaceHolderValue(placeholder, paramsArray, world))
.getOrNull();
}
private @Nullable String parseWorldName(OfflinePlayer offlinePlayer, List<String> paramsArray) {
// No world defined, get from player
if (paramsArray.isEmpty()) {
if (offlinePlayer instanceof Player player) {
return player.getWorld().getName();
} else {
warning("You must specify a world name for non-player placeholders");
return null;
}
}
// Try get from params
String paramWorldName = paramsArray.get(paramsArray.size() - 1);
if (worldManager.isLoadedWorld(paramWorldName)) {
paramsArray.remove(paramsArray.size() - 1);
return paramWorldName;
}
// Param not a world, fallback to player
if (offlinePlayer instanceof Player player) {
return player.getWorld().getName();
}
warning("Multiverse World not found: " + paramWorldName);
return null;
}
private @Nullable String getWorldPlaceHolderValue(@NotNull String placeholder,
@NotNull List<String> placeholderParams,
@NotNull LoadedMultiverseWorld world) {
// Switch to find what specific placeholder we want
switch (placeholder.toLowerCase(Locale.ENGLISH)) {
case "alias" -> {
return world.getAliasOrName();
}
case "animalspawn" -> {
return String.valueOf(world.getEntitySpawnConfig().getSpawnCategoryConfig(SpawnCategory.ANIMAL).isSpawn());
}
case "autoheal" -> {
return String.valueOf(world.getAutoHeal());
}
case "blacklist" -> {
return String.join(", ", world.getWorldBlacklist());
}
case "currency" -> {
return String.valueOf(world.getCurrency());
}
case "difficulty" -> {
return world.getDifficulty().toString();
}
case "entryfee" -> {
return economist.formatPrice(world.getPrice(), world.getCurrency());
}
case "environment" -> {
return world.getEnvironment().toString().toLowerCase();
}
case "flight" -> {
return String.valueOf(world.isAllowFlight());
}
case "gamemode" -> {
return world.getGameMode().toString().toLowerCase();
}
case "generator" -> {
return world.getGenerator();
}
case "hunger" -> {
return String.valueOf(world.isHunger());
}
case "monstersspawn" -> {
return String.valueOf(world.getEntitySpawnConfig().getSpawnCategoryConfig(SpawnCategory.MONSTER).isSpawn());
}
case "name" -> {
return world.getName();
}
case "playercount" -> {
return String.valueOf(world.getBukkitWorld()
.map(World::getPlayers)
.map(List::size)
.getOrElse(-1));
}
case "playerlimit" -> {
return String.valueOf(world.getPlayerLimit());
}
case "price" -> {
return String.valueOf(world.getPrice());
}
case "pvp" -> {
return String.valueOf(world.getPvp());
}
case "seed" -> {
return String.valueOf(world.getSeed());
}
case "time" -> {
String timeFormat = !placeholderParams.isEmpty() ? placeholderParams.get(0) : "";
long time = world.getBukkitWorld().map(World::getTime).getOrElse(0L);
switch (timeFormat) {
case "" -> {
return String.valueOf(time);
}
case "12h" -> {
return MinecraftTimeFormatter.format12h(time);
}
case "24h" -> {
return MinecraftTimeFormatter.format24h(time);
}
default -> {
return MinecraftTimeFormatter.formatTime(time, timeFormat);
}
}
}
case "type" -> {
return world.getBukkitWorld().map(World::getWorldType).map(Enum::name).getOrElse("null");
}
case "weather" -> {
return String.valueOf(world.isAllowWeather());
}
default -> {
warning("Unknown placeholder: " + placeholder);
return null;
}
}
}
}
@@ -1,22 +0,0 @@
package org.mvplugins.multiverse.core;
/**
* A utility class that enables automated tests to flag Multiverse for testing. This allows Multiverse to not perform
* certain behaviors such as enabled stats uploads.
*/
final class TestingMode {
private static boolean enabled = false;
private TestingMode() {
// No instantiation
}
static void enable() {
enabled = true;
}
static boolean isDisabled() {
return !enabled;
}
}
@@ -1,205 +0,0 @@
/******************************************************************************
* Multiverse 2 Copyright (c) the Multiverse Team 2011. *
* Multiverse 2 is licensed under the BSD License. *
* For more information please check the README.md file included *
* with this project. *
******************************************************************************/
package org.mvplugins.multiverse.core.anchor;
import java.io.File;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.dumptruckman.minecraft.util.Logging;
import io.vavr.control.Option;
import io.vavr.control.Try;
import jakarta.inject.Inject;
import org.bukkit.Location;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jvnet.hk2.annotations.Service;
import org.mvplugins.multiverse.core.MultiverseCore;
import org.mvplugins.multiverse.core.teleportation.LocationManipulation;
import org.mvplugins.multiverse.core.utils.result.Result;
import org.mvplugins.multiverse.core.world.WorldManager;
import org.mvplugins.multiverse.core.world.entrycheck.WorldEntryCheckerProvider;
import org.mvplugins.multiverse.core.world.location.UnloadedWorldLocation;
/**
* Manages anchors and it's location.
*/
@Service
public final class AnchorManager {
private static final String ANCHORS_FILE = "anchors.yml";
private static final String ANCHORS_CONFIG_SECTION = "anchors";
private final Map<String, MultiverseAnchor> anchors;
private FileConfiguration anchorConfig;
private final Plugin plugin;
private final LocationManipulation locationManipulation;
private final WorldEntryCheckerProvider entryCheckerProvider;
private final WorldManager worldManager;
@Inject
AnchorManager(
MultiverseCore plugin,
LocationManipulation locationManipulation,
WorldEntryCheckerProvider entryCheckerProvider,
WorldManager worldManager
) {
this.plugin = plugin;
this.locationManipulation = locationManipulation;
this.entryCheckerProvider = entryCheckerProvider;
this.worldManager = worldManager;
this.anchors = new HashMap<>();
}
/**
* Loads all anchors.
*/
public Try<Void> loadAnchors() {
anchors.clear();
return Try.run(() -> {
anchorConfig = YamlConfiguration.loadConfiguration(new File(plugin.getDataFolder(), ANCHORS_FILE));
parseAnchorsFromConfig();
});
}
private void parseAnchorsFromConfig() {
var anchorsSection = getAnchorsConfigSection();
Set<String> anchorKeys = anchorsSection.getKeys(false);
for (String key : anchorKeys) {
//world:x,y,z:pitch:yaw
Location anchorLocation = locationManipulation.stringToLocation(anchorsSection.getString(key, ""));
if (anchorLocation != null) {
Logging.config("Loading anchor: '%s'...", key);
anchors.put(key, new MultiverseAnchor(key, UnloadedWorldLocation.fromLocation(anchorLocation)));
} else {
Logging.warning("The location for anchor '%s' is INVALID.", key);
}
}
}
private ConfigurationSection getAnchorsConfigSection() {
var anchorsConfigSection = anchorConfig.getConfigurationSection(ANCHORS_CONFIG_SECTION);
if (anchorsConfigSection == null) {
anchorsConfigSection = anchorConfig.createSection(ANCHORS_CONFIG_SECTION);
}
return anchorsConfigSection;
}
/**
* Saves all anchors.
*
* @return True if all anchors were successfully saved.
*/
public Try<Void> saveAllAnchors() {
return Try.run(() -> anchorConfig.save(new File(plugin.getDataFolder(), ANCHORS_FILE)))
.onFailure(failure ->
Logging.severe("Failed to save anchors.yml. Please check your file permissions."));
}
/**
* Gets the {@link Location} associated with an anchor.
*
* @param anchorName The name of the anchor.
* @return The {@link Location}.
*/
public Option<MultiverseAnchor> getAnchor(String anchorName) {
return Option.of(anchors.get(anchorName));
}
/**
* Sets a new location for an anchor. Creates a new anchor if it doesn't exist.
*
* @param anchorName The name of the anchor.
* @param location The location of the anchor as string (world:x,y,z:pitch:yaw).
* @return True if the anchor was successfully saved.
*/
public Try<Void> setAnchor(@NotNull String anchorName, @NotNull String location) {
Location parsed = locationManipulation.stringToLocation(location);
if (parsed == null) {
return Try.failure(new IllegalArgumentException("The location for anchor '" + anchorName + "' is INVALID."));
}
return setAnchor(anchorName, parsed);
}
/**
* Sets a new location for an anchor. Creates a new anchor if it doesn't exist.
*
* @param anchorName The name of the anchor.
* @param location The {@link Location} of the anchor.
* @return True if the anchor was successfully saved.
*/
public Try<Void> setAnchor(@NotNull String anchorName, @NotNull Location location) {
Option.of(anchors.get(anchorName))
.peek(anchor -> anchor.setLocation(UnloadedWorldLocation.fromLocation(location)))
.onEmpty(() -> anchors.put(anchorName, new MultiverseAnchor(anchorName, UnloadedWorldLocation.fromLocation(location))));
getAnchorsConfigSection().set(anchorName, locationManipulation.locationToString(location));
return saveAllAnchors();
}
/**
* Gets all anchors.
*
* @return An unmodifiable {@link List} containing all anchors.
*/
public List<MultiverseAnchor> getAllAnchors() {
return anchors.values().stream().toList();
}
/**
* Gets all anchors that the specified {@link Player} can access.
*
* @param player The {@link Player}.
* @return An unmodifiable {@link List} containing all anchors the specified {@link Player} can access.
*/
public List<MultiverseAnchor> getAnchors(@Nullable Player player) {
if (player == null) {
return getAllAnchors();
} else {
return getAnchorsForPlayer(player);
}
}
private List<MultiverseAnchor> getAnchorsForPlayer(@NotNull Player player) {
return anchors.values().stream()
.filter(anchor -> shouldIncludeAnchorForPlayer(anchor, player))
.toList();
}
private boolean shouldIncludeAnchorForPlayer(MultiverseAnchor anchor, Player player) {
return worldManager.getWorld(anchor.getLocationWorld())
.map(mvWorld -> entryCheckerProvider.forSender(player).canAccessWorld(mvWorld))
.peek(result -> Logging.finer("Result for %s can access anchor %s: %s", player.getName(), anchor.getName(), result))
.map(Result::isSuccess)
.getOrElse(true);
}
/**
* Deletes the specified anchor.
*
* @param anchor The anchor to delete.
* @return True if the anchor was successfully deleted.
*/
public Try<Void> deleteAnchor(@NotNull MultiverseAnchor anchor) {
if (anchors.containsKey(anchor.getName())) {
anchors.remove(anchor.getName());
getAnchorsConfigSection().set(anchor.getName(), null);
return saveAllAnchors();
}
return Try.failure(new IllegalArgumentException("Anchor does not exist"));
}
}
@@ -1,54 +0,0 @@
package org.mvplugins.multiverse.core.anchor;
import org.bukkit.Location;
import org.bukkit.World;
import org.jetbrains.annotations.Nullable;
import org.mvplugins.multiverse.core.world.location.UnloadedWorldLocation;
/**
* Represents a single anchor location.
*/
public final class MultiverseAnchor {
private final String name;
private UnloadedWorldLocation location;
MultiverseAnchor(String name, UnloadedWorldLocation location) {
this.name = name;
this.location = location;
}
/**
* Gets the name of the anchor.
*
* @return The name.
*/
public String getName() {
return name;
}
/**
* Gets a copy of the anchor's location.
*
* @return The location.
*/
public Location getLocation() {
return location.toBukkitLocation();
}
void setLocation(Location location) {
this.location = new UnloadedWorldLocation(location);
}
/**
* Gets the world of the anchor's location.
*
* @return The world.
*/
@Nullable World getLocationWorld() {
if (location == null) {
return null;
}
return location.getWorld();
}
}
@@ -1,4 +0,0 @@
/**
* this package contains the classes that are used for the anchor system.
*/
package org.mvplugins.multiverse.core.anchor;
@@ -1,7 +0,0 @@
package org.mvplugins.multiverse.core.command;
/**
* Extends a command class to add legacy alias annotations.
*/
public interface LegacyAliasCommand {
}
@@ -1,364 +0,0 @@
package org.mvplugins.multiverse.core.command;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.stream.Collectors;
import co.aikar.commands.BukkitCommandCompletionContext;
import co.aikar.commands.CommandIssuer;
import co.aikar.commands.PaperCommandCompletions;
import co.aikar.commands.RegisteredCommand;
import co.aikar.commands.RootCommand;
import com.dumptruckman.minecraft.util.Logging;
import io.vavr.control.Option;
import io.vavr.control.Try;
import jakarta.inject.Inject;
import org.apache.commons.lang3.Validate;
import org.bukkit.Bukkit;
import org.bukkit.Difficulty;
import org.bukkit.GameMode;
import org.bukkit.GameRule;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.entity.SpawnCategory;
import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Service;
import org.mvplugins.multiverse.core.anchor.AnchorManager;
import org.mvplugins.multiverse.core.anchor.MultiverseAnchor;
import org.mvplugins.multiverse.core.command.context.issueraware.IssuerAwareValue;
import org.mvplugins.multiverse.core.command.context.issueraware.MultiverseWorldValue;
import org.mvplugins.multiverse.core.command.context.issueraware.PlayerArrayValue;
import org.mvplugins.multiverse.core.config.CoreConfig;
import org.mvplugins.multiverse.core.config.node.functions.DefaultSuggesterProvider;
import org.mvplugins.multiverse.core.config.handle.PropertyModifyAction;
import org.mvplugins.multiverse.core.destination.DestinationInstance;
import org.mvplugins.multiverse.core.destination.DestinationSuggestionPacket;
import org.mvplugins.multiverse.core.destination.DestinationsProvider;
import org.mvplugins.multiverse.core.destination.core.WorldDestination;
import org.mvplugins.multiverse.core.permissions.CorePermissionsChecker;
import org.mvplugins.multiverse.core.utils.REPatterns;
import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld;
import org.mvplugins.multiverse.core.world.MultiverseWorld;
import org.mvplugins.multiverse.core.world.WorldManager;
import org.mvplugins.multiverse.core.world.generators.GeneratorPlugin;
import org.mvplugins.multiverse.core.world.generators.GeneratorProvider;
import static org.mvplugins.multiverse.core.utils.StringFormatter.addonToCommaSeperated;
@Service
public class MVCommandCompletions extends PaperCommandCompletions {
private final MVCommandManager commandManager;
private final WorldManager worldManager;
private final DestinationsProvider destinationsProvider;
private final CoreConfig config;
private final CorePermissionsChecker corePermissionsChecker;
private final AnchorManager anchorManager;
private final GeneratorProvider generatorProvider;
@Inject
MVCommandCompletions(
@NotNull MVCommandManager mvCommandManager,
@NotNull WorldManager worldManager,
@NotNull DestinationsProvider destinationsProvider,
@NotNull CoreConfig config,
@NotNull CorePermissionsChecker corePermissionsChecker,
@NotNull AnchorManager anchorManager,
@NotNull GeneratorProvider generatorProvider
) {
super(mvCommandManager);
this.commandManager = mvCommandManager;
this.worldManager = worldManager;
this.destinationsProvider = destinationsProvider;
this.config = config;
this.corePermissionsChecker = corePermissionsChecker;
this.anchorManager = anchorManager;
this.generatorProvider = generatorProvider;
registerAsyncCompletion("anchornames", this::suggestAnchorNames);
registerAsyncCompletion("commands", this::suggestCommands);
registerAsyncCompletion("destinations", this::suggestDestinations);
registerStaticCompletion("difficulties", suggestEnums(Difficulty.class));
registerStaticCompletion("environments", List.of("normal", "nether", "the_end")); // Don't tab complete the "custom" environment
registerAsyncCompletion("flags", this::suggestFlags);
registerStaticCompletion("gamemodes", suggestEnums(GameMode.class));
registerStaticCompletion("gamerules", this::suggestGamerules);
registerAsyncCompletion("gamerulesvalues", this::suggestGamerulesValues);
registerAsyncCompletion("generatorplugins", this::suggestGeneratorPlugins);
registerStaticCompletion("materials", suggestEnums(Material.class));
registerStaticCompletion("mvconfigs", config.getStringPropertyHandle().getAllPropertyNames());
registerAsyncCompletion("mvconfigvalues", this::suggestMVConfigValues);
registerAsyncCompletion("mvworlds", this::suggestMVWorlds);
registerAsyncCompletion("mvworldpropsname", this::suggestMVWorldPropsName);
registerAsyncCompletion("mvworldpropsvalue", this::suggestMVWorldPropsValue);
registerCompletion("playersarray", this::suggestPlayersArray); // getting online players cannot be async
registerStaticCompletion("propsmodifyaction", suggestEnums(PropertyModifyAction.class));
registerStaticCompletion("spawncategories", suggestEnums(SpawnCategory.class));
registerAsyncCompletion("spawncategorypropsname", this::suggestSpawnCategoryPropsName);
registerAsyncCompletion("spawncategorypropsvalue", this::suggestSpawnCategoryPropsValue);
setDefaultCompletion("destinations", DestinationInstance.class);
setDefaultCompletion("difficulties", Difficulty.class);
setDefaultCompletion("environments", World.Environment.class);
setDefaultCompletion("flags", String[].class);
setDefaultCompletion("gamemodes", GameMode.class);
setDefaultCompletion("gamerules", GameRule.class);
setDefaultCompletion("mvworlds", LoadedMultiverseWorld.class);
}
@Override
public CommandCompletionHandler registerCompletion(String id, CommandCompletionHandler<BukkitCommandCompletionContext> handler) {
return super.registerCompletion(id, context ->
completeWithPreconditions(context, handler));
}
@Override
public CommandCompletionHandler registerAsyncCompletion(String id, AsyncCommandCompletionHandler<BukkitCommandCompletionContext> handler) {
return super.registerAsyncCompletion(id, context ->
completeWithPreconditions(context, handler));
}
private Collection<String> completeWithPreconditions(
BukkitCommandCompletionContext context,
CommandCompletionHandler<BukkitCommandCompletionContext> handler) {
if (context.hasConfig("playerOnly") && !context.getIssuer().isPlayer()) {
return Collections.emptyList();
}
if (context.hasConfig("byIssuerForArg")) {
Boolean byIssuerForArg = Try.of(() -> context.getContextValueByName(IssuerAwareValue.class, context.getConfig("byIssuerForArg")))
.map(IssuerAwareValue::isByIssuer)
.getOrElse(false);
if (!byIssuerForArg) {
return Collections.emptyList();
}
}
if (context.hasConfig("notByIssuerForArg")) {
Boolean byIssuerForArg = Try.of(() -> context.getContextValueByName(IssuerAwareValue.class, context.getConfig("notByIssuerForArg")))
.map(IssuerAwareValue::isByIssuer)
.getOrElse(false);
if (byIssuerForArg) {
return Collections.emptyList();
}
}
if (context.hasConfig("resolveUntil")) {
if (!Try.run(() -> context.getContextValueByName(Object.class, context.getConfig("resolveUntil"))).isSuccess()) {
return Collections.emptyList();
}
}
if (context.hasConfig("checkPermissions")) {
for (String permission : REPatterns.SEMICOLON.split(context.getConfig("checkPermissions"))) {
if (!commandManager.getCommandPermissions().hasPermission(context.getIssuer(), permission)) {
return Collections.emptyList();
}
}
}
if (context.hasConfig("multiple")) {
return addonToCommaSeperated(context.getInput(), handler.getCompletions(context));
}
return handler.getCompletions(context);
}
/**
* Shortcut to suggest enums values
*
* @param enumClass The enum class with values
* @return A collection of possible string values
* @param <T> The enum type
*/
public <T extends Enum<T>> Collection<String> suggestEnums(Class<T> enumClass) {
return EnumSet.allOf(enumClass).stream()
.map(Enum::name)
.map(name -> name.toLowerCase(Locale.ENGLISH))
.toList();
}
private Collection<String> suggestAnchorNames(BukkitCommandCompletionContext context) {
return anchorManager.getAnchors(context.getPlayer()).stream()
.map(MultiverseAnchor::getName)
.toList();
}
private Collection<String> suggestCommands(BukkitCommandCompletionContext context) {
String rootCmdName = context.getConfig();
if (rootCmdName == null) {
return Collections.emptyList();
}
RootCommand rootCommand = this.commandManager.getRegisteredRootCommands().stream()
.unordered()
.filter(c -> c.getCommandName().equals(rootCmdName))
.findFirst()
.orElse(null);
if (rootCommand == null) {
return Collections.emptyList();
}
return rootCommand.getSubCommands().entries().stream()
.filter(entry -> checkPerms(context.getIssuer(), entry.getValue()))
.map(Map.Entry::getKey)
.filter(cmdName -> !cmdName.startsWith("__"))
.collect(Collectors.toList());
}
private boolean checkPerms(CommandIssuer issuer, RegisteredCommand<?> command) {
return this.commandManager.hasPermission(issuer, command.getRequiredPermissions());
}
private Collection<String> suggestDestinations(BukkitCommandCompletionContext context) {
return Try.of(() -> context.getContextValue(PlayerArrayValue.class))
.map(PlayerArrayValue::value)
.recover(IllegalStateException.class, e -> context.getContextValue(Player[].class))
.recover(IllegalStateException.class, e -> new Player[]{context.getContextValue(Player.class)})
.map(players -> {
if (players.length == 0) {
// Most likely console did not specify a player
return Collections.<String>emptyList();
}
CommandSender sender = context.getIssuer().getIssuer();
if (context.hasConfig("othersOnly") && (players.length == 1 && players[0].equals(sender))) {
return Collections.<String>emptyList();
}
return suggestDestinationsWithPerms(sender, Arrays.asList(players), context.getInput());
})
.getOrElse(Collections.emptyList());
}
private Collection<String> suggestDestinationsWithPerms(CommandSender teleporter, List<Entity> teleportees, String deststring) {
return destinationsProvider.suggestDestinations(teleporter, deststring).stream()
.filter(packet -> !config.getSimplifiedDestinationTabCompletion()
|| packet.destination() instanceof WorldDestination
|| deststring.startsWith(packet.destination().getIdentifier() + ":"))
.filter(packet -> corePermissionsChecker
.checkDestinationPacketPermission(teleporter, teleportees, packet))
.map(DestinationSuggestionPacket::parsableString)
.toList();
}
private Collection<String> suggestFlags(@NotNull BukkitCommandCompletionContext context) {
String groupName = context.getConfig("groupName", "");
return Try.of(() -> context.getContextValue(String[].class))
.map(flags -> commandManager.getFlagsManager().suggest(groupName, flags))
.getOrElse(Collections.emptyList());
}
private Collection<String> suggestGamerules() {
return Arrays.stream(GameRule.values()).map(GameRule::getName).collect(Collectors.toList());
}
private Collection<String> suggestGamerulesValues(BukkitCommandCompletionContext context) {
return Try.of(() -> context.getContextValue(GameRule.class))
// Just use our suggester from configuration lib since gamerules are only boolean or int
.mapTry(gamerule -> Option.of(DefaultSuggesterProvider.getDefaultSuggester(gamerule.getType()))
.map(s -> s.suggest(context.getInput())).getOrElse(Collections.emptyList()))
.getOrElse(Collections.emptyList());
}
private Collection<String> suggestGeneratorPlugins(BukkitCommandCompletionContext context) {
return generatorProvider.getRegisteredGeneratorPlugins().stream()
.map(GeneratorPlugin::getPluginName)
.toList();
}
private Collection<String> suggestMVConfigValues(BukkitCommandCompletionContext context) {
return Try.of(() -> context.getContextValue(String.class))
.map(propertyName -> config.getStringPropertyHandle()
.getSuggestedPropertyValue(propertyName, context.getInput(), PropertyModifyAction.SET, context.getSender()))
.getOrElse(Collections.emptyList());
}
private Collection<String> suggestMVWorlds(BukkitCommandCompletionContext context) {
String scope = context.getConfig("scope", "loaded");
switch (scope) {
case "both" -> {
return worldManager.getWorlds().stream()
.map(MultiverseWorld::getTabCompleteName)
.toList();
}
case "loaded" -> {
return worldManager.getLoadedWorlds().stream()
.map(MultiverseWorld::getTabCompleteName)
.toList();
}
case "unloaded" -> {
return worldManager.getUnloadedWorlds().stream()
.map(MultiverseWorld::getTabCompleteName)
.toList();
}
case "potential" -> {
return worldManager.getPotentialWorlds();
}
}
Logging.severe("Invalid MVWorld scope: " + scope);
return Collections.emptyList();
}
private Collection<String> suggestMVWorldPropsName(BukkitCommandCompletionContext context) {
return Try.of(() -> {
MultiverseWorld world = context.getContextValue(MultiverseWorldValue.class).value();
PropertyModifyAction action = context.getContextValue(PropertyModifyAction.class);
return world.getStringPropertyHandle().getModifiablePropertyNames(action);
}).getOrElse(Collections.emptyList());
}
private Collection<String> suggestMVWorldPropsValue(BukkitCommandCompletionContext context) {
return Try.of(() -> {
MultiverseWorld world = context.getContextValue(MultiverseWorldValue.class).value();
PropertyModifyAction action = context.getContextValue(PropertyModifyAction.class);
String propertyName = context.getContextValue(String.class);
return world.getStringPropertyHandle().getSuggestedPropertyValue(propertyName, context.getInput(), action, context.getSender());
}).getOrElse(Collections.emptyList());
}
private Collection<String> suggestPlayersArray(BukkitCommandCompletionContext context) {
CommandSender sender = context.getSender();
Validate.notNull(sender, "Sender cannot be null");
Player senderPlayer = sender instanceof Player ? (Player)sender : null;
List<String> matchedPlayers = new ArrayList<>();
for(Player player : Bukkit.getOnlinePlayers()) {
String name = player.getName();
if ((senderPlayer == null || senderPlayer.canSee(player))
&& (!context.hasConfig("excludeSelf") || !player.equals(senderPlayer))) {
matchedPlayers.add(name);
}
}
return addonToCommaSeperated(context.getInput(), matchedPlayers);
}
private Collection<String> suggestSpawnCategoryPropsName(BukkitCommandCompletionContext context) {
return Try.of(() -> {
MultiverseWorld world = context.getContextValue(MultiverseWorld.class);
SpawnCategory spawnCategory = context.getContextValue(SpawnCategory.class);
PropertyModifyAction action = context.getContextValue(PropertyModifyAction.class);
return world.getEntitySpawnConfig()
.getSpawnCategoryConfig(spawnCategory)
.getStringPropertyHandle()
.getModifiablePropertyNames(action);
}).getOrElse(Collections.emptyList());
}
private Collection<String> suggestSpawnCategoryPropsValue(BukkitCommandCompletionContext context) {
return Try.of(() -> {
MultiverseWorld world = context.getContextValue(MultiverseWorld.class);
SpawnCategory spawnCategory = context.getContextValue(SpawnCategory.class);
PropertyModifyAction action = context.getContextValue(PropertyModifyAction.class);
String propertyName = context.getContextValue(String.class);
return world.getEntitySpawnConfig()
.getSpawnCategoryConfig(spawnCategory)
.getStringPropertyHandle()
.getSuggestedPropertyValue(propertyName, context.getInput(), action, context.getSender());
}).getOrElse(Collections.emptyList());
}
}
@@ -1,88 +0,0 @@
package org.mvplugins.multiverse.core.command;
import co.aikar.commands.BukkitCommandExecutionContext;
import co.aikar.commands.BukkitCommandIssuer;
import co.aikar.commands.BukkitConditionContext;
import co.aikar.commands.CommandConditions;
import co.aikar.commands.ConditionContext;
import co.aikar.commands.ConditionFailedException;
import org.jetbrains.annotations.NotNull;
import org.mvplugins.multiverse.core.world.WorldManager;
import org.mvplugins.multiverse.core.world.helpers.WorldNameChecker;
public class MVCommandConditions {
static void load(
@NotNull MVCommandManager commandManager,
@NotNull WorldManager worldManager,
@NotNull WorldNameChecker worldNameChecker) {
MVCommandConditions mvCommandConditions = new MVCommandConditions(commandManager, worldManager, worldNameChecker);
mvCommandConditions.registerConditions();
}
private final WorldManager worldManager;
private final MVCommandManager commandManager;
private final WorldNameChecker worldNameChecker;
private MVCommandConditions(
@NotNull MVCommandManager commandManager,
@NotNull WorldManager worldManager,
@NotNull WorldNameChecker worldNameChecker) {
this.worldManager = worldManager;
this.commandManager = commandManager;
this.worldNameChecker = worldNameChecker;
}
private void registerConditions() {
CommandConditions<BukkitCommandIssuer, BukkitCommandExecutionContext, BukkitConditionContext> conditions
= commandManager.getCommandConditions();
conditions.addCondition(String.class, "worldname", this::checkWorldname);
}
private void checkWorldname(
ConditionContext<BukkitCommandIssuer> context,
BukkitCommandExecutionContext executionContext,
String worldName) {
String scope = context.getConfigValue("scope", "loaded");
switch (scope) {
// Worlds that are loaded
case "loaded":
if (!this.worldManager.isLoadedWorld(worldName)) {
throw new ConditionFailedException("World with name '" + worldName + "' does not exist or is not loaded!");
}
break;
// Worlds that are unloaded
case "unloaded":
if (!this.worldManager.isUnloadedWorld(worldName)) {
if (this.worldManager.isLoadedWorld(worldName)) {
throw new ConditionFailedException("World with name '" + worldName + "' is loaded already!");
}
throw new ConditionFailedException("World with name '" + worldName + "' does not exist!");
}
break;
// World that are loaded or unloaded
case "both":
if (!this.worldManager.isWorld(worldName)) {
throw new ConditionFailedException("World with name '" + worldName + "' does not exist!");
}
break;
// World that are does not exist
case "new":
if (this.worldManager.isWorld(worldName)) {
throw new ConditionFailedException("World with name '" + worldName + "' already exists!");
}
switch (worldNameChecker.checkName(worldName)) {
case INVALID_CHARS ->
throw new ConditionFailedException("World name '" + worldName + "' contains invalid characters!");
case BLACKLISTED ->
throw new ConditionFailedException("World name '" + worldName + "' is used for critical server operations and is blacklisted!");
}
break;
// Probably a typo happened here
default:
throw new ConditionFailedException("Unknown scope '" + scope + "'!");
}
}
}
@@ -1,396 +0,0 @@
package org.mvplugins.multiverse.core.command;
import java.util.HashSet;
import java.util.Set;
import co.aikar.commands.ACFUtil;
import co.aikar.commands.BukkitCommandExecutionContext;
import co.aikar.commands.BukkitCommandIssuer;
import co.aikar.commands.InvalidCommandArgument;
import co.aikar.commands.MinecraftMessageKeys;
import co.aikar.commands.PaperCommandContexts;
import co.aikar.commands.contexts.ContextResolver;
import com.google.common.base.Strings;
import io.vavr.control.Option;
import io.vavr.control.Try;
import jakarta.inject.Inject;
import org.bukkit.Bukkit;
import org.bukkit.GameRule;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.command.BlockCommandSender;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.entity.SpawnCategory;
import org.jetbrains.annotations.Nullable;
import org.jvnet.hk2.annotations.Service;
import org.mvplugins.multiverse.core.anchor.AnchorManager;
import org.mvplugins.multiverse.core.anchor.MultiverseAnchor;
import org.mvplugins.multiverse.core.command.context.GameRuleValue;
import org.mvplugins.multiverse.core.command.context.issueraware.IssuerAwareContextBuilder;
import org.mvplugins.multiverse.core.command.context.PlayerLocation;
import org.mvplugins.multiverse.core.command.context.issueraware.MultiverseWorldValue;
import org.mvplugins.multiverse.core.command.context.issueraware.PlayerArrayValue;
import org.mvplugins.multiverse.core.config.CoreConfig;
import org.mvplugins.multiverse.core.destination.DestinationInstance;
import org.mvplugins.multiverse.core.destination.DestinationsProvider;
import org.mvplugins.multiverse.core.display.filters.ContentFilter;
import org.mvplugins.multiverse.core.display.filters.DefaultContentFilter;
import org.mvplugins.multiverse.core.display.filters.RegexContentFilter;
import org.mvplugins.multiverse.core.exceptions.command.MVInvalidCommandArgument;
import org.mvplugins.multiverse.core.locale.message.Message;
import org.mvplugins.multiverse.core.utils.PlayerFinder;
import org.mvplugins.multiverse.core.utils.REPatterns;
import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld;
import org.mvplugins.multiverse.core.world.MultiverseWorld;
import org.mvplugins.multiverse.core.world.WorldManager;
import org.mvplugins.multiverse.core.world.generators.GeneratorPlugin;
import org.mvplugins.multiverse.core.world.generators.GeneratorProvider;
@Service
public class MVCommandContexts extends PaperCommandContexts {
private final MVCommandManager mvCommandManager;
private final DestinationsProvider destinationsProvider;
private final WorldManager worldManager;
private final CoreConfig config;
private final AnchorManager anchorManager;
private final GeneratorProvider generatorProvider;
@Inject
MVCommandContexts(
MVCommandManager mvCommandManager,
DestinationsProvider destinationsProvider,
WorldManager worldManager,
CoreConfig config,
AnchorManager anchorManager,
GeneratorProvider generatorProvider
) {
super(mvCommandManager);
this.mvCommandManager = mvCommandManager;
this.destinationsProvider = destinationsProvider;
this.worldManager = worldManager;
this.config = config;
this.anchorManager = anchorManager;
this.generatorProvider = generatorProvider;
registerIssuerOnlyContext(BukkitCommandIssuer.class, BukkitCommandExecutionContext::getIssuer);
registerIssuerOnlyContext(MVCommandIssuer.class, this::parseMVCommandIssuer);
registerOptionalContext(ContentFilter.class, this::parseContentFilter);
registerContext(DestinationInstance.class, this::parseDestination);
registerContext(GameRule.class, this::parseGameRule);
registerContext(GameRuleValue.class, this::parseGameRuleValue);
registerContext(GeneratorPlugin.class, this::parseGeneratorPlugin);
registerIssuerAwareContext(LoadedMultiverseWorld.class, loadedMultiverseWorldContextBuilder().generateContext());
registerIssuerAwareContext(LoadedMultiverseWorld[].class, loadedMultiverseWorldArrayContextBuilder().generateContext());
registerIssuerAwareContext(MultiverseWorld.class, multiverseWorldContextBuilder().generateContext());
registerIssuerAwareContext(MultiverseWorldValue.class, multiverseWorldContextBuilder().generateContext(MultiverseWorldValue::new));
registerIssuerAwareContext(MultiverseWorld[].class, multiverseWorldArrayContextBuilder().generateContext());
registerContext(MultiverseAnchor.class, this::parseMultiverseAnchor);
registerIssuerAwareContext(Player.class, playerContextBuilder().generateContext());
registerIssuerAwareContext(Player[].class, playerArrayContextBuilder().generateContext());
registerIssuerAwareContext(PlayerArrayValue.class, playerArrayContextBuilder().generateContext(PlayerArrayValue::new));
registerIssuerAwareContext(PlayerLocation.class, this::parsePlayerLocation);
registerContext(SpawnCategory[].class, this::parseSpawnCategories);
}
private MVCommandIssuer parseMVCommandIssuer(BukkitCommandExecutionContext context) {
if (context.getIssuer() instanceof MVCommandIssuer) {
return (MVCommandIssuer) context.getIssuer();
}
return mvCommandManager.getCommandIssuer(context.getSender());
}
private ContentFilter parseContentFilter(BukkitCommandExecutionContext context) {
if (Strings.isNullOrEmpty(context.getFirstArg())) {
return DefaultContentFilter.get();
}
String filterString = context.popFirstArg();
return RegexContentFilter.fromString(filterString);
}
private DestinationInstance<?, ?> parseDestination(BukkitCommandExecutionContext context) {
String destination = context.popFirstArg();
if (Strings.isNullOrEmpty(destination)) {
throw new InvalidCommandArgument("No destination specified.");
}
return destinationsProvider.parseDestination(context.getSender(), destination)
.getOrThrow(failure ->
new InvalidCommandArgument(failure.getFailureMessage().formatted(context.getIssuer())));
}
private GameRule<?> parseGameRule(BukkitCommandExecutionContext context) {
String gameRuleName = context.popFirstArg();
if (Strings.isNullOrEmpty(gameRuleName)) {
throw new InvalidCommandArgument("No game rule specified.");
}
GameRule<?> gameRule = GameRule.getByName(gameRuleName);
if (gameRule == null) {
throw new InvalidCommandArgument("The game rule " + gameRuleName + " is not valid.");
}
return gameRule;
}
private GameRuleValue parseGameRuleValue(BukkitCommandExecutionContext context) {
GameRule<?> gameRule = (GameRule<?>) context.getResolvedArg(GameRule.class);
if (gameRule == null) {
throw new InvalidCommandArgument("No game rule specified.");
}
String valueString = context.getFirstArg();
if (Strings.isNullOrEmpty(valueString)) {
throw new InvalidCommandArgument("No game rule value specified.");
}
ContextResolver<?, BukkitCommandExecutionContext> resolver = getResolver(gameRule.getType());
if (resolver == null) {
return new GameRuleValue(valueString);
}
Object resolvedValue = resolver.getContext(context);
if (resolvedValue == null) {
throw new InvalidCommandArgument("The game rule value " + valueString + " is not valid for game rule " + gameRule.getName() + ".");
}
return new GameRuleValue(resolvedValue);
}
private GeneratorPlugin parseGeneratorPlugin(BukkitCommandExecutionContext context) {
GeneratorPlugin generatorPlugin = generatorProvider.getGeneratorPlugin(context.popFirstArg());
if (generatorPlugin == null) {
throw new InvalidCommandArgument("Invalid generator plugin: " + context.getFirstArg());
}
return generatorPlugin;
}
private IssuerAwareContextBuilder<LoadedMultiverseWorld> loadedMultiverseWorldContextBuilder() {
return new IssuerAwareContextBuilder<LoadedMultiverseWorld>()
.fromPlayer((context, player) -> worldManager.getLoadedWorld(player.getWorld()).getOrNull())
.fromInput((context, input) -> getLoadedMultiverseWorld(input))
.issuerOnlyFailMessage((context) -> Message.of("This command can only be used by a player in a loaded Multiverse World."))
.issuerAwarePlayerFailMessage((context, player) -> Message.of("You are not in a loaded multiverse world. Either specify a multiverse world name or use this command in a loaded multiverse world."))
.issuerAwareInputFailMessage((context, input) -> Message.of("World '" + input + "' is not a loaded multiverse world. Remember to specify the world name when using this command in console."))
.inputOnlyFailMessage((context, input) -> Message.of("World " + input + " is not a loaded multiverse world."));
}
private IssuerAwareContextBuilder<LoadedMultiverseWorld[]> loadedMultiverseWorldArrayContextBuilder() {
return new IssuerAwareContextBuilder<LoadedMultiverseWorld[]>()
.fromPlayer((context, player) -> worldManager.getLoadedWorld(player.getWorld())
.map(world -> new LoadedMultiverseWorld[]{world})
.getOrNull())
.fromInput((context, input) -> {
String[] worldNames = input == null ? new String[0] : REPatterns.COMMA.split(input);
Set<LoadedMultiverseWorld> worlds = new HashSet<>(worldNames.length);
for (String worldName : worldNames) {
if ("*".equals(worldName)) {
worlds.addAll(worldManager.getLoadedWorlds());
break;
}
LoadedMultiverseWorld world = getLoadedMultiverseWorld(worldName);
if (world == null) {
throw new InvalidCommandArgument("World " + worldName + " is not a loaded multiverse world.");
}
worlds.add(world);
}
return worlds.isEmpty() ? null : worlds.toArray(new LoadedMultiverseWorld[0]);
})
.issuerOnlyFailMessage((context) -> Message.of("This command can only be used by a player in a loaded Multiverse World."))
.issuerAwarePlayerFailMessage((context, player) -> Message.of("You are not in a loaded multiverse world. Either specify a multiverse world name or use this command in a loaded multiverse world."))
.issuerAwareInputFailMessage((context, input) -> Message.of("World '" + input + "' is not a loaded multiverse world. Remember to specify the world name when using this command in console."))
.inputOnlyFailMessage((context, input) -> Message.of("World " + input + " is not a loaded multiverse world."));
}
@Nullable
private LoadedMultiverseWorld getLoadedMultiverseWorld(String worldName) {
return config.getResolveAliasName()
? worldManager.getLoadedWorldByNameOrAlias(worldName).getOrNull()
: worldManager.getLoadedWorld(worldName).getOrNull();
}
private IssuerAwareContextBuilder<MultiverseWorld> multiverseWorldContextBuilder() {
return new IssuerAwareContextBuilder<MultiverseWorld>()
.fromPlayer((context, player) -> worldManager.getWorld(player.getWorld()).getOrNull())
.fromInput((context, input) -> getMultiverseWorld(input))
.issuerOnlyFailMessage((context) -> Message.of("This command can only be used by a player in a Multiverse World."))
.issuerAwarePlayerFailMessage((context, player) -> Message.of("You are not in a multiverse world. Either specify a multiverse world name or use this command in a multiverse world."))
.issuerAwareInputFailMessage((context, input) -> Message.of("World '" + input + "' is not a multiverse world. Remember to specify the world name when using this command in console."))
.inputOnlyFailMessage((context, input) -> Message.of("World " + input + " is not a multiverse world."));
}
private IssuerAwareContextBuilder<MultiverseWorld[]> multiverseWorldArrayContextBuilder() {
return new IssuerAwareContextBuilder<MultiverseWorld[]>()
.fromPlayer((context, player) -> worldManager.getWorld(player.getWorld())
.map(world -> new MultiverseWorld[]{world})
.getOrNull())
.fromInput((context, input) -> {
String[] worldNames = input == null ? new String[0] : REPatterns.COMMA.split(input);
Set<MultiverseWorld> worlds = new HashSet<>(worldNames.length);
for (String worldName : worldNames) {
if ("*".equals(worldName)) {
worlds.addAll(worldManager.getWorlds());
break;
}
MultiverseWorld world = getMultiverseWorld(worldName);
if (world == null) {
throw new InvalidCommandArgument("World " + worldName + " is not a multiverse world.");
}
worlds.add(world);
}
return worlds.isEmpty() ? null : worlds.toArray(new MultiverseWorld[0]);
})
.issuerOnlyFailMessage((context) -> Message.of("This command can only be used by a player in a Multiverse World."))
.issuerAwarePlayerFailMessage((context, player) -> Message.of("You are not in a multiverse world. Either specify a multiverse world name or use this command in a multiverse world."))
.issuerAwareInputFailMessage((context, input) -> Message.of("World '" + input + "' is not a multiverse world. Remember to specify the world name when using this command in console."))
.inputOnlyFailMessage((context, input) -> Message.of("World " + input + " is not a multiverse world."));
}
@Nullable
private MultiverseWorld getMultiverseWorld(String worldName) {
return config.getResolveAliasName()
? worldManager.getWorldByNameOrAlias(worldName).getOrNull()
: worldManager.getWorld(worldName).getOrNull();
}
private MultiverseAnchor parseMultiverseAnchor(BukkitCommandExecutionContext context) {
String anchorName = context.popFirstArg();
return anchorManager.getAnchor(anchorName)
.getOrElseThrow(() -> new InvalidCommandArgument("The anchor '" +anchorName + "' does not exist."));
}
private IssuerAwareContextBuilder<Player> playerContextBuilder() {
return new IssuerAwareContextBuilder<Player>()
.fromPlayer((context, player) -> player)
.fromInput((context, input) -> PlayerFinder.get(input, context.getSender()))
.issuerOnlyFailMessage((context) -> Message.of("This command can only be used by a player."))
.issuerAwareInputFailMessage((context, input) -> Message.of("Invalid player: " + input + ". Either specify an online player or use this command as a player."))
.inputOnlyFailMessage((context, input) -> Message.of("Player " + input + " not found."));
}
private IssuerAwareContextBuilder<Player[]> playerArrayContextBuilder() {
return new IssuerAwareContextBuilder<Player[]>()
.fromPlayer((context, player) -> new Player[]{player})
.fromInput((context, input) -> PlayerFinder
.tryGetMulti(input, context.getSender())
.map(list -> list.toArray(new Player[0]))
.map(arr -> (arr.length == 0) ? null : arr)
.getOrElseThrow(failure -> {
if (failure instanceof MVInvalidCommandArgument mvFailure) {
throw mvFailure;
}
throw new InvalidCommandArgument(failure.getLocalizedMessage() + " "
+ Option.of(failure.getCause()).map(Throwable::getLocalizedMessage).getOrElse(""));
}))
.issuerOnlyFailMessage((context) -> Message.of("This command can only be used by a player."))
.issuerAwareInputFailMessage((context, input) -> Message.of("Invalid player: " + input + ". Either specify an online player or use this command as a player."))
.inputOnlyFailMessage((context, input) -> {
if (PlayerFinder.isSelector(input)) {
return Message.of("No player(s) matched selector: " + input + ".");
}
return Message.of("Player(s) " + input + " not found.");
});
}
private PlayerLocation parsePlayerLocation(BukkitCommandExecutionContext context) {
Try<Location> location = Try.of(() -> parseLocationFromInput(context.getFirstArg(), context.getSender()));
if (location.isSuccess()) {
context.popFirstArg();
return new PlayerLocation(location.get());
}
if (context.getPlayer() != null) {
return new PlayerLocation(context.getPlayer().getLocation());
}
if (context.getFirstArg() == null) {
throw new InvalidCommandArgument("You must specify a world location when using this command in console.");
}
if (location.getCause() instanceof InvalidCommandArgument) {
throw (InvalidCommandArgument)location.getCause();
}
throw new RuntimeException(location.getCause());
}
// copied from ACF
private Location parseLocationFromInput(String input, CommandSender sender) {
String[] split = REPatterns.COLON.split(input, 2);
if (split.length == 0) {
throw new InvalidCommandArgument(true);
} else if (split.length < 2 && !(sender instanceof Player) && !(sender instanceof BlockCommandSender)) {
throw new InvalidCommandArgument(MinecraftMessageKeys.LOCATION_PLEASE_SPECIFY_WORLD, new String[0]);
} else {
Location sourceLoc = null;
String world;
String rest;
if (split.length == 2) {
world = split[0];
rest = split[1];
} else if (sender instanceof Player) {
sourceLoc = ((Player)sender).getLocation();
world = sourceLoc.getWorld().getName();
rest = split[0];
} else {
if (!(sender instanceof BlockCommandSender)) {
throw new InvalidCommandArgument(true);
}
sourceLoc = ((BlockCommandSender)sender).getBlock().getLocation();
world = sourceLoc.getWorld().getName();
rest = split[0];
}
boolean rel = rest.startsWith("~");
split = REPatterns.COMMA.split(rel ? rest.substring(1) : rest);
if (split.length < 3) {
throw new InvalidCommandArgument(MinecraftMessageKeys.LOCATION_PLEASE_SPECIFY_XYZ, new String[0]);
} else {
Double x = ACFUtil.parseDouble(split[0], rel ? (double)0.0F : null);
Double y = ACFUtil.parseDouble(split[1], rel ? (double)0.0F : null);
Double z = ACFUtil.parseDouble(split[2], rel ? (double)0.0F : null);
if (sourceLoc != null && rel) {
x = x + sourceLoc.getX();
y = y + sourceLoc.getY();
z = z + sourceLoc.getZ();
} else if (rel) {
throw new InvalidCommandArgument(MinecraftMessageKeys.LOCATION_CONSOLE_NOT_RELATIVE, new String[0]);
}
if (x != null && y != null && z != null) {
World worldObj = Bukkit.getWorld(world);
if (worldObj == null) {
throw new InvalidCommandArgument(MinecraftMessageKeys.INVALID_WORLD, new String[0]);
} else if (split.length >= 5) {
Float yaw = ACFUtil.parseFloat(split[3]);
Float pitch = ACFUtil.parseFloat(split[4]);
if (pitch != null && yaw != null) {
return new Location(worldObj, x, y, z, yaw, pitch);
} else {
throw new InvalidCommandArgument(MinecraftMessageKeys.LOCATION_PLEASE_SPECIFY_XYZ, new String[0]);
}
} else {
return new Location(worldObj, x, y, z);
}
} else {
throw new InvalidCommandArgument(MinecraftMessageKeys.LOCATION_PLEASE_SPECIFY_XYZ, new String[0]);
}
}
}
}
private SpawnCategory[] parseSpawnCategories(BukkitCommandExecutionContext context) {
if (context.isOptional() && context.getArgs().isEmpty()) {
return new SpawnCategory[0];
}
Set<SpawnCategory> categories = new HashSet<>();
String[] split = REPatterns.COMMA.split(context.popFirstArg());
for (String category : split) {
SpawnCategory spawnCategory = ACFUtil.simpleMatch(SpawnCategory.class, category);
if (spawnCategory != null) {
categories.add(spawnCategory);
}
}
return categories.toArray(new SpawnCategory[0]);
}
}
@@ -1,121 +0,0 @@
package org.mvplugins.multiverse.core.command;
import co.aikar.commands.BukkitCommandIssuer;
import co.aikar.commands.MessageType;
import co.aikar.locales.MessageKeyProvider;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
import org.mvplugins.multiverse.core.locale.message.Message;
import org.mvplugins.multiverse.core.locale.message.MessageReplacement;
import org.mvplugins.multiverse.core.utils.text.ChatTextFormatter;
public class MVCommandIssuer extends BukkitCommandIssuer {
private final MVCommandManager commandManager;
MVCommandIssuer(@NotNull MVCommandManager commandManager, @NotNull CommandSender sender) {
super(commandManager, sender);
this.commandManager = commandManager;
}
@Override
public MVCommandManager getManager() {
return commandManager;
}
@Override
public void sendMessageInternal(String message) {
ChatTextFormatter.sendFormattedMessage(getIssuer(), message);
}
public void sendError(String message, MessageReplacement... replacements) {
sendMessage(MessageType.ERROR, message, replacements);
}
public void sendSyntax(String message, MessageReplacement... replacements) {
sendMessage(MessageType.SYNTAX, message, replacements);
}
public void sendInfo(String message, MessageReplacement... replacements) {
sendMessage(MessageType.INFO, message, replacements);
}
public void sendMessage(String message, MessageReplacement... replacements) {
sendMessage(null, Message.of(message, replacements));
}
public void sendMessage(MessageType type, String message, MessageReplacement... replacements) {
sendMessage(type, Message.of(message, replacements));
}
public void sendMessage(MessageType messageType, String message) {
var formatter = getManager().getFormat(messageType);
if (formatter != null) {
sendMessage(formatter.format(message));
} else {
sendMessage(message);
}
}
public void sendError(MessageKeyProvider key) {
sendMessage(MessageType.ERROR, key, new String[0]);
}
public void sendSyntax(MessageKeyProvider key) {
sendMessage(MessageType.SYNTAX, key, new String[0]);
}
public void sendInfo(MessageKeyProvider key) {
sendMessage(MessageType.INFO, key, new String[0]);
}
public void sendError(MessageKeyProvider key, MessageReplacement... replacements) {
sendMessage(MessageType.ERROR, key, replacements);
}
public void sendSyntax(MessageKeyProvider key, MessageReplacement... replacements) {
sendMessage(MessageType.SYNTAX, key, replacements);
}
public void sendInfo(MessageKeyProvider key, MessageReplacement... replacements) {
sendMessage(MessageType.INFO, key, replacements);
}
public void sendMessage(MessageKeyProvider key, MessageReplacement... replacements) {
sendMessage(null, Message.of(key, replacements));
}
public void sendMessage(MessageType messageType, MessageKeyProvider key, MessageReplacement... replacements) {
sendMessage(messageType, Message.of(key, replacements));
}
public void sendError(Message message) {
sendMessage(MessageType.ERROR, message);
}
public void sendSyntax(Message message) {
sendMessage(MessageType.SYNTAX, message);
}
public void sendInfo(Message message) {
sendMessage(MessageType.INFO, message);
}
public void sendMessage(Message message) {
sendMessage(null, message);
}
public void sendMessage(MessageType messageType, Message message) {
if (message instanceof MessageKeyProvider) {
sendMessage(messageType, (MessageKeyProvider) message,
message.getReplacements(getManager().getLocales(), this));
} else {
var formatter = getManager().getFormat(messageType);
if (formatter != null) {
sendMessage(formatter.format(message.formatted(getManager().getLocales(), this)));
} else {
sendMessage(message.formatted(getManager().getLocales(), this));
}
}
}
}
@@ -1,202 +0,0 @@
package org.mvplugins.multiverse.core.command;
import java.util.List;
import co.aikar.commands.BaseCommand;
import co.aikar.commands.CommandCompletions;
import co.aikar.commands.CommandContexts;
import co.aikar.commands.CommandHelp;
import co.aikar.commands.CommandIssuer;
import co.aikar.commands.HelpEntry;
import co.aikar.commands.LogLevel;
import co.aikar.commands.PaperCommandManager;
import co.aikar.commands.RootCommand;
import com.dumptruckman.minecraft.util.Logging;
import jakarta.inject.Inject;
import jakarta.inject.Provider;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Service;
import org.mvplugins.multiverse.core.MultiverseCore;
import org.mvplugins.multiverse.core.command.flag.CommandFlagsManager;
import org.mvplugins.multiverse.core.command.queue.CommandQueueManager;
import org.mvplugins.multiverse.core.config.CoreConfig;
import org.mvplugins.multiverse.core.locale.PluginLocales;
import org.mvplugins.multiverse.core.world.WorldManager;
import org.mvplugins.multiverse.core.world.helpers.WorldNameChecker;
/**
* Main class to manage permissions.
*/
@Service
public class MVCommandManager extends PaperCommandManager {
private final CommandFlagsManager flagsManager;
private final CommandQueueManager commandQueueManager;
private final Provider<MVCommandContexts> commandContextsProvider;
private final Provider<MVCommandCompletions> commandCompletionsProvider;
private final MVCommandPermissions commandPermissions;
private final CoreConfig config;
private final PluginLocales pluginLocales;
@Inject
MVCommandManager(
@NotNull MultiverseCore plugin,
@NotNull CommandFlagsManager flagsManager,
@NotNull CommandQueueManager commandQueueManager,
@NotNull Provider<MVCommandContexts> commandContextsProvider,
@NotNull Provider<MVCommandCompletions> commandCompletionsProvider,
@NotNull WorldManager worldManager,
@NotNull WorldNameChecker worldNameChecker,
@NotNull MVCommandPermissions commandPermissions,
@NotNull CoreConfig config
) {
super(plugin);
this.flagsManager = flagsManager;
this.commandQueueManager = commandQueueManager;
this.commandContextsProvider = commandContextsProvider;
this.commandCompletionsProvider = commandCompletionsProvider;
this.commandPermissions = commandPermissions;
this.config = config;
this.pluginLocales = new PluginLocales(this);
this.locales = this.pluginLocales;
this.pluginLocales.loadLanguages();
MVCommandConditions.load(this, worldManager, worldNameChecker);
this.enableUnstableAPI("help");
this.setDefaultExceptionHandler(new MVDefaultExceptionHandler());
}
/**
* Registers a list of commands and handles {@link LegacyAliasCommand} based on config option.
* @param commands The commands to register
* @param <T> The type of the commands
*/
public <T extends BaseCommand> void registerAllCommands(List<T> commands) {
List<T> noLegacy = commands.stream().filter(command -> !(command instanceof LegacyAliasCommand)).toList();
if (!config.getShowLegacyAliases()) {
noLegacy.forEach(this::registerCommand);
return;
}
List<T> legacy = commands.stream().filter(command -> command instanceof LegacyAliasCommand).toList();
List<T> noLegacyInheritance = noLegacy.stream()
.filter(command -> legacy.stream().noneMatch(
legacyAliasCommand -> command.getClass().isAssignableFrom(legacyAliasCommand.getClass())))
.toList();
legacy.forEach(this::registerCommand);
noLegacyInheritance.forEach(this::registerCommand);
}
@Override
public void registerCommand(BaseCommand command, boolean force) {
if (command instanceof LegacyAliasCommand && !config.getShowLegacyAliases()) {
return;
}
super.registerCommand(command, force);
}
/**
* {@inheritDoc}
*/
@Override
public PluginLocales getLocales() {
return this.pluginLocales;
}
/**
* Gets class responsible for flag handling.
*
* @return A not-null {@link CommandFlagsManager}.
*/
public synchronized @NotNull CommandFlagsManager getFlagsManager() {
return flagsManager;
}
/**
* Manager for command that requires /mv confirm before execution.
*
* @return A non-null {@link CommandQueueManager}.
*/
public synchronized @NotNull CommandQueueManager getCommandQueueManager() {
return commandQueueManager;
}
public synchronized @NotNull MVCommandPermissions getCommandPermissions() {
return commandPermissions;
}
/**
* Gets class responsible for parsing string args into objects.
*
* @return A not-null {@link CommandContexts}.
*/
@Override
public synchronized @NotNull MVCommandContexts getCommandContexts() {
if (this.contexts == null) {
this.contexts = commandContextsProvider.get();
}
return (MVCommandContexts) this.contexts;
}
/**
* Gets class responsible for tab-completion handling.
*
* @return A not-null {@link CommandCompletions}.
*/
@Override
public synchronized @NotNull MVCommandCompletions getCommandCompletions() {
if (this.completions == null) {
this.completions = commandCompletionsProvider.get();
}
return (MVCommandCompletions) this.completions;
}
@Override
public boolean hasPermission(CommandIssuer issuer, String permission) {
return commandPermissions.hasPermission(issuer, permission);
}
/**
* Standardise usage command formatting for all mv modules.
*
* @param help The target {@link CommandHelp}.
*/
public void showUsage(@NotNull CommandHelp help) {
List<HelpEntry> entries = help.getHelpEntries();
if (entries.size() == 1) {
getHelpFormatter().showDetailedHelp(help, entries.get(0));
return;
}
help.showHelp();
}
public @NotNull MVCommandIssuer getConsoleCommandIssuer() {
return getCommandIssuer(Bukkit.getConsoleSender());
}
@Override
public @NotNull MVCommandIssuer getCommandIssuer(Object issuer) {
if (!(issuer instanceof CommandSender)) {
throw new IllegalArgumentException(issuer.getClass().getName() + " is not a Command Issuer.");
} else {
return new MVCommandIssuer(this, (CommandSender)issuer);
}
}
@Override
public RootCommand createRootCommand(String cmd) {
return new MVRootCommand(this, cmd);
}
@Override
public void log(LogLevel level, String message, Throwable throwable) {
Logging.finer("[%s] %s", level, message);
if (throwable != null) {
throwable.printStackTrace();
}
}
}
@@ -1,62 +0,0 @@
package org.mvplugins.multiverse.core.command;
import co.aikar.commands.CommandIssuer;
import co.aikar.commands.annotation.CommandPermission;
import io.vavr.control.Option;
import jakarta.inject.Inject;
import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Service;
import org.mvplugins.multiverse.core.permissions.CorePermissionsChecker;
import org.mvplugins.multiverse.core.permissions.PermissionUtils;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.function.Predicate;
import static org.mvplugins.multiverse.core.permissions.CorePermissionsChecker.*;
/**
* Maps permission checking to custom logic for commands, to allow more complex permission checking.
*/
@Service
public class MVCommandPermissions {
private final Map<String, Predicate<CommandIssuer>> permissionsCheckMap;
@Inject
MVCommandPermissions(@NotNull CorePermissionsChecker permissionsChecker) {
this.permissionsCheckMap = new HashMap<>();
registerPermissionChecker("mvteleport", issuer -> permissionsChecker.hasAnyTeleportPermission(issuer.getIssuer()));
registerPermissionChecker("mvteleportother", issuer -> permissionsChecker.hasAnyTeleportPermission(issuer.getIssuer(), Scope.OTHER));
registerPermissionChecker("mvspawn", issuer -> permissionsChecker.hasAnySpawnPermission(issuer.getIssuer()));
registerPermissionChecker("mvspawnother", issuer -> permissionsChecker.hasAnySpawnPermission(issuer.getIssuer(), Scope.OTHER));
}
/**
* Registers a custom permission checker callback. Use `@id-name` in {@link CommandPermission} decorator to use
* the callback instead of the default permission string checking.
*
* @param id The permission id
* @param checker The permission checker callback
*/
public void registerPermissionChecker(String id, Predicate<CommandIssuer> checker) {
permissionsCheckMap.put(prepareId(id), checker);
}
private static @NotNull String prepareId(String id) {
return (id.startsWith("@") ? "" : "@") + id.toLowerCase(Locale.ENGLISH);
}
/**
* Check if the issuer has the given permission.
* @param issuer The issuer
* @param permission The permission to check
* @return True if the issuer has the permission
*/
boolean hasPermission(CommandIssuer issuer, String permission) {
return Option.of(permissionsCheckMap.get(permission))
.map(checker -> checker.test(issuer))
.getOrElse(() -> PermissionUtils.hasPermission(issuer.getIssuer(), permission));
}
}
@@ -1,22 +0,0 @@
package org.mvplugins.multiverse.core.command;
import co.aikar.commands.BaseCommand;
import co.aikar.commands.CommandIssuer;
import co.aikar.commands.ExceptionHandler;
import co.aikar.commands.RegisteredCommand;
import com.dumptruckman.minecraft.util.Logging;
import java.util.List;
/**
* Default handler that will print the stack trace when the command throws an exception.
*/
public class MVDefaultExceptionHandler implements ExceptionHandler {
@Override
public boolean execute(BaseCommand command, RegisteredCommand registeredCommand, CommandIssuer sender, List<String> args, Throwable t) {
Logging.severe("Failed to execute command /%s %s %s",
registeredCommand.getCommand(), registeredCommand.getPrefSubCommand(), String.join(" ", args));
t.printStackTrace();
return false;
}
}
@@ -1,30 +0,0 @@
package org.mvplugins.multiverse.core.command;
import co.aikar.commands.BaseCommand;
import co.aikar.commands.BukkitCommandManager;
import co.aikar.commands.BukkitRootCommand;
import co.aikar.commands.CommandIssuer;
import org.mvplugins.multiverse.core.utils.StringFormatter;
import java.util.List;
public class MVRootCommand extends BukkitRootCommand {
protected MVRootCommand(BukkitCommandManager manager, String name) {
super(manager, name);
}
@Override
public BaseCommand execute(CommandIssuer sender, String commandLabel, String[] args) {
String[] quoteFormatedArgs = StringFormatter.parseQuotesInArgs(args).toArray(String[]::new);
return super.execute(sender, commandLabel, quoteFormatedArgs);
}
@Override
public List<String> getTabCompletions(CommandIssuer sender, String alias, String[] args, boolean commandsOnly, boolean isAsync) {
String[] quoteFormatedArgs = StringFormatter.parseQuotesInArgs(args).toArray(String[]::new);
return super.getTabCompletions(sender, alias, quoteFormatedArgs, commandsOnly, isAsync)
.stream()
.map(StringFormatter::quoteMultiWordString)
.toList();
}
}
@@ -1,19 +0,0 @@
package org.mvplugins.multiverse.core.command;
import co.aikar.commands.BaseCommand;
import com.dumptruckman.minecraft.util.Logging;
import jakarta.annotation.PostConstruct;
import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Contract;
import org.mvplugins.multiverse.core.command.flag.CommandFlag;
import org.mvplugins.multiverse.core.command.flag.CommandFlagGroup;
import org.mvplugins.multiverse.core.command.flag.CommandFlagsManager;
import org.mvplugins.multiverse.core.command.flag.ParsedCommandFlags;
/**
* Base class for all Multiverse commands.
*/
@Contract
public abstract class MultiverseCommand extends BaseCommand {
}
@@ -1,7 +0,0 @@
package org.mvplugins.multiverse.core.command.context;
/**
* Simple wrapper for game rule value, as they may be different types.
*/
public record GameRuleValue(Object value) {
}
@@ -1,16 +0,0 @@
package org.mvplugins.multiverse.core.command.context;
import org.bukkit.Location;
import org.jetbrains.annotations.ApiStatus;
/**
* Wrapper for context that get location from player if executed by player,
* else requires user input of location coordinates.
*
* @param value The location
*
* @since 5.1
*/
@ApiStatus.AvailableSince("5.1")
public record PlayerLocation(Location value) {
}
@@ -1,203 +0,0 @@
package org.mvplugins.multiverse.core.command.context.issueraware;
import co.aikar.commands.BukkitCommandExecutionContext;
import co.aikar.commands.BukkitCommandIssuer;
import co.aikar.commands.InvalidCommandArgument;
import co.aikar.commands.contexts.IssuerAwareContextResolver;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.ApiStatus;
import org.mvplugins.multiverse.core.locale.message.Message;
import java.util.Objects;
import java.util.function.BiFunction;
import java.util.function.Function;
/**
* Reusable logic of issuer only and issuer aware context resolvers
*
* @since 5.1
*/
@ApiStatus.AvailableSince("5.1")
public final class IssuerAwareContextBuilder<T> {
private BiFunction<BukkitCommandExecutionContext, Player, T> fromPlayer;
private BiFunction<BukkitCommandExecutionContext, String, T> fromInput;
private Function<BukkitCommandExecutionContext, Message> issuerOnlyFailMessage = context -> Message.of("This command can only be used by a player.");
private BiFunction<BukkitCommandExecutionContext, Player, Message> issuerAwarePlayerFailMessage = (context, player) -> Message.of("Unable to resolve context for player '" + player.getName() + "'.");
private BiFunction<BukkitCommandExecutionContext, String, Message> issuerAwareInputFailMessage = (context, input) -> Message.of("Unable to resolve context for input '" + input + "'.");
private BiFunction<BukkitCommandExecutionContext, String, Message> inputOnlyFailMessage = (context, input) -> Message.of("Unable to resolve context for input '" + input + "'.");
public IssuerAwareContextBuilder() {
}
/**
* Parse value from player itself.
*
* @param fromInput the parsing function
* @return The same builder for chaining
*
* @since 5.1
*/
@ApiStatus.AvailableSince("5.1")
public IssuerAwareContextBuilder<T> fromPlayer(BiFunction<BukkitCommandExecutionContext, Player, T> fromInput) {
this.fromPlayer = fromInput;
return this;
}
/**
* Parse value from command input string.
*
* @param fromInput the parsing function
* @return The same builder for chaining
*
* @since 5.1
*/
@ApiStatus.AvailableSince("5.1")
public IssuerAwareContextBuilder<T> fromInput(BiFunction<BukkitCommandExecutionContext, String, T> fromInput) {
this.fromInput = fromInput;
return this;
}
/**
* When getting value fails as issuer is not a player.
*
* @param issuerOnlyFailMessage The message
* @return The same builder for chaining
*
* @since 5.1
*/
@ApiStatus.AvailableSince("5.1")
public IssuerAwareContextBuilder<T> issuerOnlyFailMessage(Function<BukkitCommandExecutionContext, Message> issuerOnlyFailMessage) {
this.issuerOnlyFailMessage = issuerOnlyFailMessage;
return this;
}
/**
* When getting value fails as player cannot parse the value.
*
* @param issuerAwarePlayerFailMessage The message
* @return The same builder for chaining
*
* @since 5.1
*/
@ApiStatus.AvailableSince("5.1")
public IssuerAwareContextBuilder<T> issuerAwarePlayerFailMessage(BiFunction<BukkitCommandExecutionContext, Player, Message> issuerAwarePlayerFailMessage) {
this.issuerAwarePlayerFailMessage = issuerAwarePlayerFailMessage;
return this;
}
/**
* When getting value fails as input cannot parse the value.
*
* @param issuerAwareInputFailMessage The message
* @return The same builder for chaining
*
* @since 5.1
*/
@ApiStatus.AvailableSince("5.1")
public IssuerAwareContextBuilder<T> issuerAwareInputFailMessage(BiFunction<BukkitCommandExecutionContext, String, Message> issuerAwareInputFailMessage) {
this.issuerAwareInputFailMessage = issuerAwareInputFailMessage;
return this;
}
/**
* When getting value fails as input cannot parse the value.
*
* @param inputOnlyFailMessage The message
* @return The same builder for chaining
*
* @since 5.1
*/
@ApiStatus.AvailableSince("5.1")
public IssuerAwareContextBuilder<T> inputOnlyFailMessage(BiFunction<BukkitCommandExecutionContext, String, Message> inputOnlyFailMessage) {
this.inputOnlyFailMessage = inputOnlyFailMessage;
return this;
}
/**
* Creates the issuer aware context resolver logic.
*
* @return The generated context resolver
*
* @since 5.1
*/
@ApiStatus.AvailableSince("5.1")
public IssuerAwareContextResolver<T, BukkitCommandExecutionContext> generateContext() {
validateRequiredVariables();
return context -> resolveValue(context, GenericIssuerAwareValue::new).value;
}
/**
* Creates the issuer aware context resolver logic, with marking of whether the value was resolved from player or input.
*
* @param createValue The function to create the {@link IssuerAwareValue} value
* @param <I> The type of the value
* @return The generated context resolver
*
* @since 5.1
*/
@ApiStatus.AvailableSince("5.1")
public <I extends IssuerAwareValue> IssuerAwareContextResolver<I, BukkitCommandExecutionContext> generateContext(BiFunction<Boolean, T, I> createValue) {
validateRequiredVariables();
return context -> resolveValue(context, createValue);
}
private void validateRequiredVariables() {
Objects.requireNonNull(fromPlayer);
Objects.requireNonNull(fromInput);
Objects.requireNonNull(issuerOnlyFailMessage);
Objects.requireNonNull(issuerAwarePlayerFailMessage);
Objects.requireNonNull(issuerAwareInputFailMessage);
Objects.requireNonNull(inputOnlyFailMessage);
}
private <I extends IssuerAwareValue> I resolveValue(BukkitCommandExecutionContext context, BiFunction<Boolean, T, I> createValue) {
BukkitCommandIssuer issuer = context.getIssuer();
String resolve = context.getFlagValue("resolve", "");
if (resolve.equals("issuerOnly")) {
if (issuer.isPlayer()) {
T result = fromPlayer.apply(context, issuer.getPlayer());
if (result != null) {
return createValue.apply(true, result);
}
}
throw new InvalidCommandArgument(issuerOnlyFailMessage.apply(context).formatted(issuer));
}
String input = context.getFirstArg();
T result = fromInput.apply(context, input);
if (result != null) {
context.popFirstArg();
return createValue.apply(false, result);
}
int maxArgForAware = context.getFlagValue("maxArgForAware", Integer.MAX_VALUE);
long argLengthWithoutFlags = context.getArgs().stream()
.takeWhile(value -> !value.startsWith("-") && !value.isEmpty()) // ignore the flags
.count();
if (resolve.equals("issuerAware") && argLengthWithoutFlags <= maxArgForAware) {
if (issuer.isPlayer()) {
Player player = issuer.getPlayer();
result = fromPlayer.apply(context, player);
if (result != null) {
return createValue.apply(true, result);
}
throw new InvalidCommandArgument(issuerAwarePlayerFailMessage.apply(context, player).formatted(issuer));
}
throw new InvalidCommandArgument(issuerAwareInputFailMessage.apply(context, input).formatted(issuer));
}
throw new InvalidCommandArgument(inputOnlyFailMessage.apply(context, input).formatted(issuer));
}
private static class GenericIssuerAwareValue<T> extends IssuerAwareValue {
private final T value;
public GenericIssuerAwareValue(boolean byIssuer, T value) {
super(byIssuer);
this.value = value;
}
}
}
@@ -1,35 +0,0 @@
package org.mvplugins.multiverse.core.command.context.issueraware;
import org.jetbrains.annotations.ApiStatus;
/**
* Base class for issuer aware values
*
* @since 5.1
*/
@ApiStatus.AvailableSince("5.1")
public abstract class IssuerAwareValue {
protected final boolean byIssuer;
/**
* Constructor to create an issuer aware value
*
* @since 5.1
*/
@ApiStatus.AvailableSince("5.1")
protected IssuerAwareValue(boolean byIssuer) {
this.byIssuer = byIssuer;
}
/**
* Gets whether the value is by issuer or input
*
* @return true if by issuer, false if by input
*
* @since 5.1
*/
@ApiStatus.AvailableSince("5.1")
public boolean isByIssuer() {
return byIssuer;
}
}
@@ -1,38 +0,0 @@
package org.mvplugins.multiverse.core.command.context.issueraware;
import org.jetbrains.annotations.ApiStatus;
import org.mvplugins.multiverse.core.world.MultiverseWorld;
/**
* Issuer aware value wrapper for {@link MultiverseWorld}
*
* @since 5.1
*/
@ApiStatus.AvailableSince("5.1")
public final class MultiverseWorldValue extends IssuerAwareValue {
private final MultiverseWorld world;
/**
* Constructor for issuer aware value
*
* @since 5.1
*/
@ApiStatus.AvailableSince("5.1")
public MultiverseWorldValue(boolean byIssuer, MultiverseWorld world) {
super(byIssuer);
this.world = world;
}
/**
* The containing world wrapped
*
* @return wrapped world
*
* @since 5.1
*/
@ApiStatus.AvailableSince("5.1")
public MultiverseWorld value() {
return world;
}
}
@@ -1,36 +0,0 @@
package org.mvplugins.multiverse.core.command.context.issueraware;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.ApiStatus;
/**
* Issuer aware value wrapper for {@link Player} array
*
* @since 5.1
*/
@ApiStatus.AvailableSince("5.1")
public final class PlayerArrayValue extends IssuerAwareValue {
private final Player[] players;
/**
* @since 5.1
*/
@ApiStatus.AvailableSince("5.1")
public PlayerArrayValue(boolean byIssuer, Player[] players) {
super(byIssuer);
this.players = players;
}
/**
* The containing player array wrapped
*
* @return wrapped player array
*
* @since 5.1
*/
@ApiStatus.AvailableSince("5.1")
public Player[] value() {
return players;
}
}
@@ -1 +0,0 @@
package org.mvplugins.multiverse.core.command.context;
@@ -1,105 +0,0 @@
package org.mvplugins.multiverse.core.command.flag;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.jetbrains.annotations.NotNull;
/**
* Represents a flag.
*/
public class CommandFlag {
/**
* A builder for a flag.
*
* @param key The key for the new flag.
* @return The builder.
*/
public static @NotNull Builder<?> builder(@NotNull String key){
return new Builder<>(key);
}
private final String key;
private final List<String> aliases;
/**
* Creates a new flag.
*
* @param key The key for the new flag.
* @param aliases The aliases that also refer to this flag.
*/
protected CommandFlag(@NotNull String key, @NotNull List<String> aliases) {
this.key = key;
this.aliases = aliases;
}
/**
* Get the key of this flag.
*
* @return The key of this flag.
*/
public @NotNull String getKey() {
return key;
}
/**
* Get the aliases of this flag.
*
* @return The aliases of this flag.
*/
public @NotNull List<String> getAliases() {
return aliases;
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
return "Builder{"
+ "key='" + key + '\''
+ ", aliases=" + aliases
+ '}';
}
/**
* A builder for a flag.
*
* @param <S> The type of the builder.
*/
public static class Builder<S extends Builder<?>> {
protected final String key;
protected final List<String> aliases;
/**
* Create a new builder.
*
* @param key The key for the new flag.
*/
public Builder(@NotNull String key) {
this.key = key;
aliases = new ArrayList<>();
}
/**
* Add one or more alias to the flag.
*
* @param alias The alias to add.
* @return The builder.
*/
public @NotNull S addAlias(@NotNull String...alias){
Collections.addAll(this.aliases, alias);
return (S) this;
}
/**
* Build the flag.
*
* @return The flag.
*/
public @NotNull CommandFlag build(){
return new CommandFlag(key, aliases);
}
}
}
@@ -1,129 +0,0 @@
package org.mvplugins.multiverse.core.command.flag;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* A group of flags.
*/
public class CommandFlagGroup {
/**
* A builder for a flag group.
*
* @param name The name of the group.
* @return The builder.
*/
public static @NotNull Builder builder(@NotNull String name) {
return new Builder(name);
}
private final String name;
private final List<String> keys;
private final Map<String, CommandFlag> keysFlagMap;
/**
* Creates a new flag group.
*
* @param builder The builder.
*/
protected CommandFlagGroup(@NotNull Builder builder) {
name = builder.name;
keys = builder.keys;
keysFlagMap = builder.keysFlagMap;
}
/**
* Get the name of this group.
*
* @return The name of this group.
*/
public @NotNull String getName() {
return name;
}
/**
* Check if this group contains a flag with the given key. Works with alias keys.
*
* @param key The key to check.
* @return True if the group contains a flag with the given key, false otherwise.
*/
public boolean hasKey(@Nullable String key) {
return keysFlagMap.containsKey(key);
}
/**
* Get the remaining keys after the given flags have been removed. Works with alias keys.
*
* @param flags The flags to remove.
* @return The remaining keys.
*/
public @NotNull Set<String> getRemainingKeys(@NotNull String[] flags) {
Set<String> keysRemaining = new HashSet<>(this.keys);
for (String flag : flags) {
CommandFlag mvFlag = this.getFlagByKey(flag);
if (mvFlag != null) {
keysRemaining.remove(mvFlag.getKey());
}
}
return keysRemaining;
}
/**
* Get a flag by its key. Alias keys are supported as well.
*
* @param key The key of the flag.
* @return The flag if found, null otherwise.
*/
public @Nullable CommandFlag getFlagByKey(String key) {
return keysFlagMap.get(key);
}
/**
* A builder for {@link CommandFlagGroup}.
*/
public static class Builder {
private final String name;
private final List<String> keys;
private final Map<String, CommandFlag> keysFlagMap;
/**
* Creates a new builder.
*
* @param name The name of the flag group.
*/
public Builder(@NotNull String name) {
this.name = name;
this.keys = new ArrayList<>();
this.keysFlagMap = new HashMap<>();
}
/**
* Adds a flag to the group.
*
* @param flag The flag to add.
* @return The builder.
*/
public @NotNull Builder add(CommandFlag flag) {
keys.add(flag.getKey());
keysFlagMap.put(flag.getKey(), flag);
flag.getAliases().forEach((alias) -> keysFlagMap.put(alias, flag));
return this;
}
/**
* Builds the flag group.
*
* @return The flag group.
*/
public @NotNull CommandFlagGroup build() {
return new CommandFlagGroup(this);
}
}
}
@@ -1,96 +0,0 @@
package org.mvplugins.multiverse.core.command.flag;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import co.aikar.commands.InvalidCommandArgument;
import com.dumptruckman.minecraft.util.Logging;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jvnet.hk2.annotations.Service;
/**
* Manages all the flag groups and parsing.
*/
@Service
public class CommandFlagsManager {
private final Map<String, CommandFlagGroup> flagGroupMap;
/**
* Creates a new FlagsManager.
*/
public CommandFlagsManager() {
flagGroupMap = new HashMap<>();
}
/**
* Registers a flag group.
*
* @param flagGroup The target flag group to register.
*/
public void registerFlagGroup(@NotNull CommandFlagGroup flagGroup) {
flagGroupMap.put(flagGroup.getName(), flagGroup);
}
/**
* Get a flag group by name.
*
* @param groupName The target flag group name.
* @return The flag group if found, null otherwise.
*/
public @Nullable CommandFlagGroup getFlagGroup(@Nullable String groupName) {
return this.flagGroupMap.get(groupName);
}
/**
* Autocompletes suggestions for flags.
*
* @param groupName The target flag group name.
* @param flags The current flags so far.
* @return The list of suggestions.
*/
public @NotNull Collection<String> suggest(@Nullable String groupName, @NotNull String[] flags) {
CommandFlagGroup flagGroup = this.getFlagGroup(groupName);
if (flagGroup == null) {
Logging.warning("Unknown flag group: " + groupName);
return Collections.emptyList();
}
Collection<String> suggestions = new ArrayList<>();
CommandFlag currentFlag = (flags.length <= 1) ? null : flagGroup.getFlagByKey(flags[flags.length - 2]);
if (currentFlag instanceof CommandValueFlag<?> valueFlag) {
if (valueFlag.getCompletion() != null) {
suggestions.addAll(valueFlag.getCompletion().apply(flags[flags.length - 1]));
}
if (valueFlag.isOptional()) {
suggestions.addAll(flagGroup.getRemainingKeys(flags));
}
} else {
suggestions.addAll(flagGroup.getRemainingKeys(flags));
}
return suggestions;
}
/**
* Parses the flags.
*
* @param groupName The target flag group name.
* @param flags The flags to parse.
* @return The parsed flags.
*
* @throws InvalidCommandArgument If the flags are invalid.
*/
public @NotNull ParsedCommandFlags parse(@Nullable String groupName, @NotNull String[] flags) {
CommandFlagGroup flagGroup = this.getFlagGroup(groupName);
if (flagGroup == null) {
return ParsedCommandFlags.EMPTY;
}
return new CommandFlagsParser(this.getFlagGroup(groupName), flags).parse();
}
}
@@ -1,116 +0,0 @@
package org.mvplugins.multiverse.core.command.flag;
import co.aikar.commands.InvalidCommandArgument;
/**
* Parses flags from a string array. Each parser should only be used once.
*/
public class CommandFlagsParser {
private final CommandFlagGroup flagGroup;
private final String[] flags;
private ParsedCommandFlags parsedFlags;
private boolean nextArgMayBeKey;
private boolean nextArgMayBeValue;
private CommandFlag currentFlag;
/**
* Creates a new CommandFlagsParser.
*
* @param flagGroup The flag group to parse flags for.
* @param flags The flags to parse.
*/
public CommandFlagsParser(CommandFlagGroup flagGroup, String[] flags) {
this.flagGroup = flagGroup;
this.flags = flags;
}
/**
* Parses the flags.
*
* @return The parsed flags.
*/
public ParsedCommandFlags parse() {
parsedFlags = new ParsedCommandFlags();
// First argument is always a key
this.nextArgMayBeKey = true;
this.nextArgMayBeValue = false;
for (String flag : flags) {
if (this.nextArgMayBeKey) {
if (parseKey(flag)) continue;
}
if (this.nextArgMayBeValue) {
if (parseValue(flag)) continue;
}
throw new InvalidCommandArgument(flag + " is not a valid flag.");
}
if (!this.nextArgMayBeKey && this.nextArgMayBeValue) {
throw new InvalidCommandArgument(currentFlag.getKey() + " requires a value!");
}
return parsedFlags;
}
/**
* Parses a key.
*
* @param flag The flag to parse.
* @return True if the flag was parsed as a key, false otherwise.
*/
private boolean parseKey(String flag) {
CommandFlag potentialFlag = flagGroup.getFlagByKey(flag);
if (potentialFlag == null) {
return false;
}
this.currentFlag = potentialFlag;
if (this.currentFlag instanceof CommandValueFlag<?> valueFlag) {
if (valueFlag.isOptional()) {
parsedFlags.addFlagResult(valueFlag.getKey(), valueFlag.getDefaultValue());
this.nextArgMayBeKey = true;
this.nextArgMayBeValue = true;
return true;
}
this.nextArgMayBeKey = false;
this.nextArgMayBeValue = true;
return true;
}
parsedFlags.addFlagResult(this.currentFlag.getKey(), null);
this.nextArgMayBeKey = true;
this.nextArgMayBeValue = false;
return true;
}
/**
* Parses a value.
*
* @param flag The flag to parse.
* @return True if the flag was parsed as a value, false otherwise.
*/
private boolean parseValue(String flag) {
if (this.currentFlag == null) {
throw new InvalidCommandArgument("Some flag logic error occurred at '" + flag + "'!");
}
if (flagGroup.hasKey(flag)) {
throw new InvalidCommandArgument(currentFlag.getKey() + " requires a value!");
}
Object flagValue;
CommandValueFlag<?> valueFlag = (CommandValueFlag<?>) this.currentFlag;
flagValue = valueFlag.getContext() != null ? valueFlag.getContext().apply(flag) : flag;
parsedFlags.addFlagResult(valueFlag.getKey(), flagValue);
// After a value, the next argument must be a key
this.nextArgMayBeKey = true;
this.nextArgMayBeValue = false;
this.currentFlag = null;
return true;
}
}
@@ -1,270 +0,0 @@
package org.mvplugins.multiverse.core.command.flag;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Locale;
import java.util.function.Function;
import co.aikar.commands.InvalidCommandArgument;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Represents a flag with a value.
*
* @param <T> The type of the value.
*/
public class CommandValueFlag<T> extends CommandFlag {
/**
* A builder for a flag.
*
* @param key The key for the new flag.
* @param type The type of the value.
* @return The builder.
*/
public static @NotNull <T> Builder<T, ?> builder(@NotNull String key, @NotNull Class<T> type) {
return new Builder<>(key, type);
}
/**
* A builder for a flag with enum value.
*
* @param key The key for the new flag.
* @param type The type of the value, must be enum.
* @return The builder.
*/
public static @NotNull <T extends Enum<T>> EnumBuilder<T, ?> enumBuilder(@NotNull String key, @NotNull Class<T> type) {
return new EnumBuilder<>(key, type);
}
private final Class<T> type;
private final boolean optional;
private final T defaultValue;
private final Function<String, T> context;
private final Function<String, Collection<String>> completion;
/**
* Creates a new flag.
*
* @param key The key for the new flag.
* @param aliases The aliases that also refer to this flag.
* @param type The type of the value.
* @param optional Allow for flag without value.
* @param defaultValue The default value if optional is true and user does not specify a value.
* @param context Function to parse string into value type.
* @param completion Function to get completion for this flag.
*/
protected CommandValueFlag(
@NotNull String key,
@NotNull List<String> aliases,
@NotNull Class<T> type,
boolean optional,
@Nullable T defaultValue,
@Nullable Function<String, T> context,
@Nullable Function<String, Collection<String>> completion
) {
super(key, aliases);
this.type = type;
this.optional = optional;
this.defaultValue = defaultValue;
this.context = context;
this.completion = completion;
}
/**
* Get the type of the value.
*
* @return The type of the value.
*/
public @NotNull Class<T> getType() {
return type;
}
/**
* Check if it is optional for users to specify a value.
*
* @return True if the value is optional, false otherwise.
*/
public boolean isOptional() {
return optional;
}
/**
* Get the default value. May be null.
*
* @return The default value.
*/
public @Nullable T getDefaultValue() {
return defaultValue;
}
/**
* Get the context. May be null for {@link String} value type.
*
* @return The context.
*/
public @Nullable Function<String, T> getContext() {
return context;
}
/**
* Get the completion. May be null.
*
* @return The completion.
*/
public @Nullable Function<String, Collection<String>> getCompletion() {
return completion;
}
/**
* A builder for a flag.
*
* @param <T> The type of the value.
* @param <S> The type of the builder.
*/
public static class Builder<T, S extends Builder<T, S>> extends CommandFlag.Builder<S> {
protected final Class<T> type;
protected boolean optional = false;
protected T defaultValue = null;
protected Function<String, T> context = null;
protected Function<String, Collection<String>> completion = null;
/**
* Create a new builder.
*
* @param key The key for the new flag.
* @param type The type of the value.
*/
public Builder(@NotNull String key, @NotNull Class<T> type) {
super(key);
this.type = type;
}
/**
* Set the flag as optional for users to specify a value.
*
* @return The builder.
*/
public @NotNull S optional() {
this.optional = true;
return (S) this;
}
/**
* Set the default value. Used if optional is true and user does not specify a value.
*
* @param defaultValue The default value.
* @return The builder.
*/
public @NotNull S defaultValue(@NotNull T defaultValue) {
this.defaultValue = defaultValue;
return (S) this;
}
/**
* Set the context callback for parsing string into value type.
*
* @param context The context.
* @return The builder.
*/
public @NotNull S context(@NotNull Function<String, T> context) {
this.context = context;
return (S) this;
}
/**
* Set the completion callback for autocomplete.
*
* @param completion The completion. Input is the current input string, and output is the list of suggestions.
* @return The builder.
*/
public @NotNull S completion(@NotNull Function<String, Collection<String>> completion) {
this.completion = completion;
return (S) this;
}
/**
* Build the flag.
*
* @return The flag.
*/
@Override
public @NotNull CommandValueFlag<T> build() {
if (context == null && !String.class.equals(type)) {
throw new IllegalStateException("Context is required for non-string value flags");
}
return new CommandValueFlag<>(key, aliases, type, optional, defaultValue, context, completion);
}
}
/**
* Specific builder for a flag with enum value.
*
* @param <T> The type of the value.
* @param <S> The type of the builder.
*/
public static class EnumBuilder<T extends Enum<T>, S extends EnumBuilder<T, S>> extends CommandFlag.Builder<S> {
protected final Class<T> type;
protected boolean optional = false;
protected T defaultValue = null;
protected Function<String, T> context = null;
protected Function<String, Collection<String>> completion = null;
public EnumBuilder(@NotNull String key, @NotNull Class<T> type) {
super(key);
this.type = type;
setEnumContext();
setEnumCompletion();
}
private void setEnumContext() {
this.context = (String value) -> {
try {
return Enum.valueOf(type, value.toUpperCase(Locale.ENGLISH));
} catch (IllegalArgumentException e) {
throw new InvalidCommandArgument("Invalid value for argument " + key + ": " + value);
}
};
}
private void setEnumCompletion() {
List<String> types = Arrays.stream(type.getEnumConstants())
.map(typeClass -> typeClass.name().toLowerCase(Locale.ENGLISH))
.toList();
this.completion = (input) -> types;
}
/**
* Set the flag as optional for users to specify a value.
*
* @return The builder.
*/
public @NotNull S optional() {
this.optional = true;
return (S) this;
}
/**
* Set the default value. Used if optional is true and user does not specify a value.
*
* @param defaultValue The default value.
* @return The builder.
*/
public @NotNull S defaultValue(@NotNull T defaultValue) {
this.defaultValue = defaultValue;
return (S) this;
}
/**
* Build the flag.
*
* @return The flag.
*/
@Override
public @NotNull CommandValueFlag<T> build() {
return new CommandValueFlag<>(key, aliases, type, optional, defaultValue, context, completion);
}
}
}
@@ -1,61 +0,0 @@
package org.mvplugins.multiverse.core.command.flag;
import com.dumptruckman.minecraft.util.Logging;
import jakarta.annotation.PostConstruct;
import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Contract;
import java.util.ArrayList;
import java.util.List;
/**
* Builds and registers a flag group on initialization. Flags should be final fields that calls {@link #flag(CommandFlag)}.
*/
@Contract
public abstract class FlagBuilder {
private final String name;
private final CommandFlagsManager flagsManager;
private final List<CommandFlag> flags;
protected FlagBuilder(@NotNull String name, @NotNull CommandFlagsManager flagsManager) {
this.name = name;
this.flagsManager = flagsManager;
this.flags = new ArrayList<>();
}
@PostConstruct
private void postConstruct() {
CommandFlagGroup.Builder flagGroupBuilder = CommandFlagGroup.builder(name);
flags.forEach(flagGroupBuilder::add);
registerFlagGroup(flagGroupBuilder.build());
}
private void registerFlagGroup(@NotNull CommandFlagGroup flagGroup) {
flagsManager.registerFlagGroup(flagGroup);
Logging.finer("Registered flag group: " + flagGroup.getName());
}
/**
* Add a new flag to the flag builder.
*
* @param flag The flag to add.
* @param <T> The type of the flag.
* @return The flag.
*/
protected <T extends CommandFlag> T flag(T flag) {
flags.add(flag);
Logging.finer("Added flag: " + flag);
return flag;
}
/**
* Parses flags.
*
* @param flags The raw string array to parse into flags.
* @return The parsed flags.
*/
public @NotNull ParsedCommandFlags parse(@NotNull String[] flags) {
return flagsManager.parse(name, flags);
}
}
@@ -1,122 +0,0 @@
package org.mvplugins.multiverse.core.command.flag;
import java.util.HashMap;
import java.util.Map;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Object to contain the results of the flags present and its values.
*/
public class ParsedCommandFlags
{
public static final ParsedCommandFlags EMPTY = new ParsedCommandFlags();
private final Map<String, Object> flagValues;
ParsedCommandFlags() {
flagValues = new HashMap<>();
}
/**
* Add a flag result to the parsed flags.
*
* @param key The key of the flag.
* @param value The value of the flag.
*/
void addFlagResult(@NotNull String key, @Nullable Object value) {
flagValues.put(key, value);
}
/**
* Check if a flag is present.
*
* @param flag The flag to check.
* @return True if the flag is present, false otherwise.
*/
public boolean hasFlag(@NotNull CommandFlag flag) {
return hasFlag(flag.getKey());
}
/**
* Check if a flag is present.
*
* @param key The key of the flag.
* @return True if the flag is present, false otherwise.
*/
public boolean hasFlag(@Nullable String key) {
return this.flagValues.containsKey(key);
}
/**
* Check if a flag is present and has a value.
*
* @param key The key of the flag.
* @return True if the flag is present and has a value, false otherwise.
*/
public boolean hasFlagValue(@Nullable String key) {
return flagValue(key, Object.class) != null;
}
/**
* Get the value of a flag.
*
* @param <T> The type of the value.
* @param flag The flag to get the value of.
* @param type The type of the value.
* @return The value of the flag, null if flag does not exist or no value.
*/
public @Nullable <T> T flagValue(@NotNull CommandFlag flag, @NotNull Class<T> type) {
return flagValue(flag.getKey(), type);
}
/**
* Get the value of a flag.
*
* @param key The key of the flag to get the value of.
* @param type The type of the value.
* @return The value of the flag, null if flag does not exist or no value.
*/
public @Nullable <T> T flagValue(@Nullable String key, @NotNull Class<T> type) {
Object value = this.flagValues.get(key);
return (T) value;
}
/**
* Get the value of a flag.
*
* @param <T> The type of the value.
* @param flag The flag to get the value of.
* @return The value of the flag, default value if flag does not exist or no value.
*/
public @Nullable <T> T flagValue(@NotNull CommandValueFlag<T> flag) {
return flagValue(flag.getKey(), flag.getType());
}
/**
* Get the value of a flag.
*
* @param <T> The type of the value.
* @param flag The flag to get the value of.
* @param defaultValue The default value if flag does not exist or no value.
* @return The value of the flag, default value if flag does not exist or no value.
*/
public @NotNull <T> T flagValue(@NotNull CommandValueFlag<T> flag, @NotNull T defaultValue) {
return flagValue(flag.getKey(), defaultValue, flag.getType());
}
/**
* Get the value of a flag.
*
* @param <T> The type of the value.
* @param key The key of the flag to get the value of.
* @param defaultValue The default value if flag does not exist or no value.
* @param type The type of the value.
* @return The value of the flag, default value if flag does not exist or no value.
*/
public @NotNull <T> T flagValue(@Nullable String key, @NotNull T defaultValue, @NotNull Class<T> type) {
T value = flagValue(key, type);
return value != null ? value : defaultValue;
}
}
@@ -1,4 +0,0 @@
/**
* This package contains classes related to parsing command flags.
*/
package org.mvplugins.multiverse.core.command.flag;
@@ -1,41 +0,0 @@
package org.mvplugins.multiverse.core.command.flags;
import java.util.List;
import java.util.function.Function;
import co.aikar.commands.InvalidCommandArgument;
import org.jetbrains.annotations.NotNull;
import org.mvplugins.multiverse.core.command.flag.CommandValueFlag;
import org.mvplugins.multiverse.core.display.filters.ContentFilter;
import org.mvplugins.multiverse.core.display.filters.RegexContentFilter;
/**
* A command flag for regex filtering.
* <br/>
* Parses the value of the --filter (or -f) flag as a {@link ContentFilter}.
*/
public final class FilterCommandFlag extends CommandValueFlag<ContentFilter> {
private static final Function<String, ContentFilter> VALUE_PARSER = value -> {
try {
return RegexContentFilter.fromString(value);
} catch (IllegalArgumentException e) {
throw new InvalidCommandArgument("Invalid filter: " + value);
}
};
/**
* Creates a new instance of this flag.
*
* @return The new instance.
*/
@NotNull
public static FilterCommandFlag create() {
return new FilterCommandFlag();
}
private FilterCommandFlag() {
super("--filter", List.of("-f"), ContentFilter.class, false, null, VALUE_PARSER, null);
}
}
@@ -1,38 +0,0 @@
package org.mvplugins.multiverse.core.command.flags;
import java.util.List;
import java.util.function.Function;
import org.jetbrains.annotations.NotNull;
import org.mvplugins.multiverse.core.command.flag.CommandValueFlag;
/**
* A command flag for page number.
* <br/>
* Parses the value of the --page (or -p) flag as an integer.
*/
public final class PageCommandFlag extends CommandValueFlag<Integer> {
private static final Function<String, Integer> VALUE_PARSER = value -> {
try {
return Integer.parseInt(value);
} catch (NumberFormatException e) {
throw new IllegalArgumentException("Invalid page number: " + value);
}
};
/**
* Creates a new instance of this flag.
*
* @return The new instance.
*/
@NotNull
public static PageCommandFlag create() {
return new PageCommandFlag();
}
private PageCommandFlag() {
super("--page", List.of("-p"), Integer.class, false, null, VALUE_PARSER, null);
}
}
@@ -1,28 +0,0 @@
package org.mvplugins.multiverse.core.command.flags;
import jakarta.inject.Inject;
import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Service;
import org.mvplugins.multiverse.core.command.flag.CommandFlagsManager;
import org.mvplugins.multiverse.core.command.flag.CommandValueFlag;
import org.mvplugins.multiverse.core.command.flag.FlagBuilder;
import org.mvplugins.multiverse.core.display.filters.ContentFilter;
@Service
public class PageFilterFlags extends FlagBuilder {
public static final String NAME = "pagefilter";
protected PageFilterFlags(@NotNull String name, @NotNull CommandFlagsManager flagsManager) {
super(name, flagsManager);
}
@Inject
protected PageFilterFlags(@NotNull CommandFlagsManager flagsManager) {
super(NAME, flagsManager);
}
public final CommandValueFlag<Integer> page = flag(PageCommandFlag.create());
public final CommandValueFlag<ContentFilter> filter = flag(FilterCommandFlag.create());
}
@@ -1,27 +0,0 @@
package org.mvplugins.multiverse.core.command.flags;
import jakarta.inject.Inject;
import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Service;
import org.mvplugins.multiverse.core.command.flag.CommandFlag;
import org.mvplugins.multiverse.core.command.flag.CommandFlagsManager;
import org.mvplugins.multiverse.core.command.flag.FlagBuilder;
@Service
public class RemovePlayerFlags extends FlagBuilder {
public static final String NAME = "removeplayer";
protected RemovePlayerFlags(@NotNull String name, @NotNull CommandFlagsManager flagsManager) {
super(name, flagsManager);
}
@Inject
protected RemovePlayerFlags(@NotNull CommandFlagsManager flagsManager) {
super(NAME, flagsManager);
}
public final CommandFlag removePlayers = flag(CommandFlag.builder("--remove-players")
.addAlias("-r")
.build());
}
@@ -1,27 +0,0 @@
package org.mvplugins.multiverse.core.command.flags;
import jakarta.inject.Inject;
import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Service;
import org.mvplugins.multiverse.core.command.flag.CommandFlag;
import org.mvplugins.multiverse.core.command.flag.CommandFlagsManager;
import org.mvplugins.multiverse.core.command.flag.FlagBuilder;
@Service
public class UnsafeFlags extends FlagBuilder {
public static final String NAME = "unsafe";
protected UnsafeFlags(@NotNull String name, @NotNull CommandFlagsManager flagsManager) {
super(name, flagsManager);
}
@Inject
protected UnsafeFlags(@NotNull CommandFlagsManager flagsManager) {
super(NAME, flagsManager);
}
public final CommandFlag unsafe = flag(CommandFlag.builder("--unsafe")
.addAlias("-u")
.build());
}
@@ -1,4 +0,0 @@
/**
* This package contains predefined flags for commands.
*/
package org.mvplugins.multiverse.core.command.flags;
@@ -1,4 +0,0 @@
/**
* This package contains the classes that manages command handling.
*/
package org.mvplugins.multiverse.core.command;
@@ -1,192 +0,0 @@
/******************************************************************************
* Multiverse 2 Copyright (c) the Multiverse Team 2020. *
* Multiverse 2 is licensed under the BSD License. *
* For more information please check the README.md file included *
* with this project. *
******************************************************************************/
package org.mvplugins.multiverse.core.command.queue;
import java.util.Map;
import java.util.Objects;
import java.util.WeakHashMap;
import com.dumptruckman.minecraft.util.Logging;
import io.vavr.control.Option;
import jakarta.inject.Inject;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.block.data.type.CommandBlock;
import org.bukkit.command.BlockCommandSender;
import org.bukkit.command.CommandSender;
import org.bukkit.command.ConsoleCommandSender;
import org.bukkit.plugin.Plugin;
import org.bukkit.scheduler.BukkitTask;
import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Service;
import org.mvplugins.multiverse.core.MultiverseCore;
import org.mvplugins.multiverse.core.command.MVCommandIssuer;
import org.mvplugins.multiverse.core.config.CoreConfig;
import org.mvplugins.multiverse.core.utils.result.Attempt;
import static org.mvplugins.multiverse.core.locale.message.MessageReplacement.*;
/**
* <p>Manages the queuing of dangerous commands that require {@code /mv confirm} before executing.</p>
*
* <p>Each sender can only have one command in queue at any given time. When a queued command is added
* for a sender that already has a command in queue, it will replace the old queued command.</p>
*/
@Service
public class CommandQueueManager {
private static final String CONSOLE_NAME = "@console";
private static final String COMMAND_BLOCK_NAME = "@commandblock";
private static final long TICKS_PER_SECOND = 20;
private final Plugin plugin;
private final CoreConfig config;
private final Map<String, CommandQueuePayload> queuedCommandMap;
@Inject
CommandQueueManager(@NotNull MultiverseCore plugin, @NotNull CoreConfig config) {
this.plugin = plugin;
this.config = config;
this.queuedCommandMap = new WeakHashMap<>();
}
/**
* Adds a {@link CommandQueuePayload} into queue.
*
* @param payload The command queue payload to add.
*/
public void addToQueue(CommandQueuePayload payload) {
String senderName = parseSenderName(payload.issuer());
if (canRunImmediately(senderName)) {
payload.action().run();
return;
}
this.removeFromQueue(senderName);
Logging.finer("Add new command to queue for sender %s.", senderName);
this.queuedCommandMap.put(senderName, payload);
payload.expireTask(runExpireLater(senderName, config.getConfirmTimeout()));
payload.issuer().sendInfo(payload.prompt());
var confirmCommand = "/mv confirm";
if (config.getUseConfirmOtp()) {
confirmCommand += " " + payload.otp();
}
payload.issuer().sendMessage(String.format("Run %s%s %sto continue. This will expire in %s seconds.",
ChatColor.GREEN, confirmCommand, ChatColor.WHITE, config.getConfirmTimeout()));
}
/**
* Check if sender does not need to queue before running.
*
* @param senderName The name of sender.
* @return True if sender does not need to queue before running.
*/
private boolean canRunImmediately(@NotNull String senderName) {
return switch (config.getConfirmMode()) {
case ENABLE -> false;
case PLAYER_ONLY -> senderName.equals(CONSOLE_NAME) || senderName.equals(COMMAND_BLOCK_NAME);
case DISABLE_COMMAND_BLOCKS -> senderName.equals(COMMAND_BLOCK_NAME);
case DISABLE_CONSOLE -> senderName.equals(CONSOLE_NAME);
case DISABLE -> true;
};
}
/**
* Expire task that removes a {@link CommandQueuePayload} from queue after valid duration defined.
*
* @param senderName The name of the sender.
* @return The expire {@link BukkitTask}.
*/
@NotNull
private BukkitTask runExpireLater(@NotNull String senderName, int validDuration) {
return Bukkit.getScheduler().runTaskLater(
this.plugin,
expireRunnable(senderName),
validDuration * TICKS_PER_SECOND);
}
/**
* Runnable responsible for expiring the queued command.
*
* @param senderName The name of the sender.
* @return The expire runnable.
*/
@NotNull
private Runnable expireRunnable(@NotNull String senderName) {
return () -> {
CommandQueuePayload payload = this.queuedCommandMap.remove(senderName);
if (payload == null) {
// Payload already removed
return;
}
payload.issuer().sendMessage("Your queued command has expired.");
};
}
/**
* Runs the command in queue for the given sender, if any.
*
* @param issuer Sender that confirmed the command.
* @return True if queued command ran successfully, else false.
*/
public Attempt<Void, RunQueuedFailedReason> runQueuedCommand(@NotNull MVCommandIssuer issuer, String otpInput) {
String senderName = parseSenderName(issuer);
return Option.of(this.queuedCommandMap.get(senderName)).fold(
() -> Attempt.failure(RunQueuedFailedReason.NO_COMMAND_IN_QUEUE),
payload -> runPayload(senderName, otpInput, payload));
}
private Attempt<Void, RunQueuedFailedReason> runPayload(String senderName, String otpInput, CommandQueuePayload payload) {
if (config.getUseConfirmOtp() && !Objects.equals(payload.otp(), otpInput)) {
return Attempt.failure(RunQueuedFailedReason.INVALID_OTP, replace("{otp}").with(otpInput));
}
this.removeFromQueue(senderName);
Logging.finer("Running queued command...");
payload.action().run();
return Attempt.success(null);
}
/**
* Since only one command is stored in queue per sender, we remove the old one.
*
* @param senderName The sender that executed the command.
*/
public void removeFromQueue(@NotNull String senderName) {
CommandQueuePayload payload = this.queuedCommandMap.remove(senderName);
if (payload == null) {
Logging.finer("No queue command to remove for sender %s.", senderName);
return;
}
Option.of(payload.expireTask()).peek(BukkitTask::cancel);
Logging.finer("Removed queue command for sender %s.", senderName);
}
private String parseSenderName(MVCommandIssuer issuer) {
CommandSender sender = issuer.getIssuer();
if (isCommandBlock(sender)) {
return COMMAND_BLOCK_NAME;
} else if (sender instanceof ConsoleCommandSender) {
return CONSOLE_NAME;
}
return sender.getName();
}
/**
* Checks if the sender is a command block.
*
* @param sender The sender to check.
* @return True if sender is a command block, else false.
*/
private boolean isCommandBlock(@NotNull CommandSender sender) {
return sender instanceof BlockCommandSender
&& ((BlockCommandSender) sender).getBlock().getBlockData() instanceof CommandBlock;
}
}
@@ -1,116 +0,0 @@
package org.mvplugins.multiverse.core.command.queue;
import co.aikar.commands.ACFUtil;
import org.bukkit.scheduler.BukkitTask;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.mvplugins.multiverse.core.command.MVCommandIssuer;
import org.mvplugins.multiverse.core.locale.message.Message;
/**
* Represents a single command used in {@link CommandQueueManager} for confirming before running potentially
* dangerous action.
*/
public class CommandQueuePayload {
/**
* Creates a new {@link CommandQueuePayload}
*
* @param issuer The issuer of the command
* @return The new {@link CommandQueuePayload}
*/
public static CommandQueuePayload issuer(@NotNull MVCommandIssuer issuer) {
return new CommandQueuePayload(issuer);
}
private static final String DEFAULT_PROMPT_MESSAGE = "The command you are trying to run is deemed dangerous."; // todo: localize
private final MVCommandIssuer issuer;
private String otp;
private Runnable action = () -> {};
private Message prompt = Message.of(DEFAULT_PROMPT_MESSAGE);
private BukkitTask expireTask;
protected CommandQueuePayload(@NotNull MVCommandIssuer issuer) {
this.otp = String.valueOf(ACFUtil.rand(100, 999));
this.issuer = issuer;
}
/**
* Gets the issuer of the command.
* @return The issuer
*/
@NotNull
public MVCommandIssuer issuer() {
return issuer;
}
/**
* Sets the logic to execute upon confirming.
* @param action The logic to execute
* @return The same {@link CommandQueuePayload} for method chaining
*/
public CommandQueuePayload action(@NotNull Runnable action) {
this.action = action;
return this;
}
/**
* Gets the logic to execute upon confirming.
*
* @return The logic to execute
*/
@NotNull
public Runnable action() {
return action;
}
/**
* Sets the OTP number for validation.
*
* @param otp The OTP number
* @return The same {@link CommandQueuePayload} for method chaining
*/
public CommandQueuePayload otp(String otp) {
this.otp = otp;
return this;
}
/**
* Gets the OTP number for validation.
*
* @return The OTP number
*/
public String otp() {
return otp;
}
/**
* Sets the question to ask sender to confirm.
*
* @param prompt The prompt message.
* @return The same {@link CommandQueuePayload} for method chaining.
*/
public CommandQueuePayload prompt(Message prompt) {
this.prompt = prompt;
return this;
}
/**
* Gets the question to ask sender to confirm.
*
* @return The prompt message.
*/
public Message prompt() {
return prompt;
}
void expireTask(BukkitTask expireTask) {
this.expireTask = expireTask;
}
@Nullable
BukkitTask expireTask() {
return expireTask;
}
}
@@ -1,31 +0,0 @@
package org.mvplugins.multiverse.core.command.queue;
/**
* Sets whether the command executor needs to confirm before executing a command.
*/
public enum ConfirmMode {
/**
* Every command executor needs to confirm before executing a command.
*/
ENABLE,
/**
* Only players need to confirm before executing a command. Command blocks and console will not need to confirm.
*/
PLAYER_ONLY,
/**
* Everyone except command blocks will not need to confirm before executing a command.
*/
DISABLE_COMMAND_BLOCKS,
/**
* Everyone except console will not need to confirm before executing a command.
*/
DISABLE_CONSOLE,
/**
* Everyone will not need to confirm before executing a command.
*/
DISABLE,
}
@@ -1,27 +0,0 @@
package org.mvplugins.multiverse.core.command.queue;
import co.aikar.locales.MessageKey;
import co.aikar.locales.MessageKeyProvider;
import org.mvplugins.multiverse.core.locale.MVCorei18n;
import org.mvplugins.multiverse.core.utils.result.FailureReason;
public enum RunQueuedFailedReason implements FailureReason {
NO_COMMAND_IN_QUEUE(MVCorei18n.QUEUECOMMAND_NOCOMMANDINQUEUE),
INVALID_OTP(MVCorei18n.QUEUECOMMAND_INVALIDOTP),
COMMAND_EXECUTION_ERROR(MVCorei18n.QUEUECOMMAND_COMMANDEXECUTIONERROR),
;
private final MessageKeyProvider message;
RunQueuedFailedReason(MessageKeyProvider message) {
this.message = message;
}
/**
* {@inheritDoc}
*/
@Override
public MessageKey getMessageKey() {
return message.getMessageKey();
}
}
@@ -1,4 +0,0 @@
/**
* Manager queuing of dangerous commands in need of confirmation.
*/
package org.mvplugins.multiverse.core.command.queue;
@@ -1,43 +0,0 @@
package org.mvplugins.multiverse.core.commands;
import co.aikar.commands.annotation.CommandCompletion;
import co.aikar.commands.annotation.CommandPermission;
import co.aikar.commands.annotation.Description;
import co.aikar.commands.annotation.Subcommand;
import co.aikar.commands.annotation.Syntax;
import jakarta.inject.Inject;
import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Service;
import org.mvplugins.multiverse.core.anchor.AnchorManager;
import org.mvplugins.multiverse.core.anchor.MultiverseAnchor;
import org.mvplugins.multiverse.core.command.MVCommandIssuer;
@Service
final class AnchorDeleteCommand extends CoreCommand {
private final AnchorManager anchorManager;
@Inject
AnchorDeleteCommand(@NotNull AnchorManager anchorManager) {
this.anchorManager = anchorManager;
}
@Subcommand("anchor delete")
@CommandPermission("multiverse.core.anchor.delete")
@CommandCompletion("@anchornames")
@Syntax("<name>")
@Description("")
void onAnchorDeleteCommand(
MVCommandIssuer issuer,
@Syntax("<name>")
@Description("")
MultiverseAnchor anchor) {
if (anchorManager.deleteAnchor(anchor).isSuccess()) {
issuer.sendMessage("&aAnchor &f" + anchor.getName() + "&a deleted.");
} else {
issuer.sendMessage("&cFailed to delete anchor.");
}
}
}
@@ -1,75 +0,0 @@
package org.mvplugins.multiverse.core.commands;
import java.util.List;
import co.aikar.commands.annotation.CommandCompletion;
import co.aikar.commands.annotation.CommandPermission;
import co.aikar.commands.annotation.Description;
import co.aikar.commands.annotation.Optional;
import co.aikar.commands.annotation.Subcommand;
import co.aikar.commands.annotation.Syntax;
import jakarta.inject.Inject;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Service;
import org.mvplugins.multiverse.core.anchor.AnchorManager;
import org.mvplugins.multiverse.core.command.MVCommandIssuer;
import org.mvplugins.multiverse.core.command.MVCommandManager;
import org.mvplugins.multiverse.core.command.flag.ParsedCommandFlags;
import org.mvplugins.multiverse.core.command.flags.PageFilterFlags;
import org.mvplugins.multiverse.core.display.ContentDisplay;
import org.mvplugins.multiverse.core.display.filters.DefaultContentFilter;
import org.mvplugins.multiverse.core.display.handlers.PagedSendHandler;
import org.mvplugins.multiverse.core.display.parsers.ListContentProvider;
import org.mvplugins.multiverse.core.teleportation.LocationManipulation;
@Service
final class AnchorListCommand extends CoreCommand {
private final AnchorManager anchorManager;
private final LocationManipulation locationManipulation;
private final PageFilterFlags flags;
@Inject
AnchorListCommand(
@NotNull AnchorManager anchorManager,
@NotNull LocationManipulation locationManipulation,
@NotNull PageFilterFlags flags
) {
this.anchorManager = anchorManager;
this.locationManipulation = locationManipulation;
this.flags = flags;
}
@Subcommand("anchor list")
@CommandPermission("multiverse.core.anchor.list")
@CommandCompletion("@flags:groupName=" + PageFilterFlags.NAME)
@Syntax("[--page <page>] [--filter <filter>]")
@Description("")
void onAnchorListCommand(
MVCommandIssuer issuer,
@Optional
@Syntax("[--page <page>] [--filter <filter>]")
@Description("")
String[] flagArray) {
ParsedCommandFlags parsedFlags = flags.parse(flagArray);
ContentDisplay.create()
.addContent(ListContentProvider.forContent(getAnchors(issuer.getPlayer())))
.withSendHandler(PagedSendHandler.create()
.withHeader("&3==== [ Multiverse Anchors ] ====")
.doPagination(true)
.withTargetPage(parsedFlags.flagValue(flags.page, 1))
.withFilter(parsedFlags.flagValue(flags.filter, DefaultContentFilter.get())))
.send(issuer);
}
private List<String> getAnchors(Player player) {
return anchorManager.getAnchors(player).stream()
.map(anchor ->
"&a%s&7 - &f%s".formatted(
anchor.getName(), locationManipulation.locationToString(anchor.getLocation())))
.toList();
}
}
@@ -1,89 +0,0 @@
package org.mvplugins.multiverse.core.commands;
import co.aikar.commands.annotation.CommandCompletion;
import co.aikar.commands.annotation.CommandPermission;
import co.aikar.commands.annotation.Description;
import co.aikar.commands.annotation.Flags;
import co.aikar.commands.annotation.Single;
import co.aikar.commands.annotation.Subcommand;
import co.aikar.commands.annotation.Syntax;
import jakarta.inject.Inject;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Service;
import org.mvplugins.multiverse.core.anchor.AnchorManager;
import org.mvplugins.multiverse.core.command.MVCommandIssuer;
import org.mvplugins.multiverse.core.teleportation.LocationManipulation;
@Service
final class AnchorSetCommand extends CoreCommand {
private final AnchorManager anchorManager;
private final LocationManipulation locationManipulation;
@Inject
AnchorSetCommand(
@NotNull AnchorManager anchorManager,
@NotNull LocationManipulation locationManipulation
) {
this.anchorManager = anchorManager;
this.locationManipulation = locationManipulation;
}
@Subcommand("anchor set")
@CommandPermission("multiverse.core.anchor.create")
@CommandCompletion("@empty @locations")
@Syntax("<name> [location]")
@Description("")
void onAnchorSetCommand(
MVCommandIssuer issuer,
@Flags("resolve=issuerOnly")
Player player,
@Single
@Syntax("<name>")
@Description("")
//TODO: Check if anchor name is valid or exists already.
String anchorName) {
Location anchorLocation = player.getLocation();
if (anchorManager.setAnchor(anchorName, anchorLocation).isSuccess()) {
sendAnchorSetSuccessMessage(issuer, anchorName, locationManipulation.locationToString(anchorLocation));
} else {
sendAnchorSetFailedMessage(issuer, anchorName);
}
}
@Subcommand("anchor set")
@CommandPermission("multiverse.core.anchor.create")
@CommandCompletion("@anchornames @locations")
@Syntax("<name> [location]")
@Description("")
void onAnchorSetCommand(
MVCommandIssuer issuer,
@Syntax("<name>")
@Description("")
String anchorName,
@Single
@Syntax("[location]")
@Description("")
String locationString) {
if (anchorManager.setAnchor(anchorName, locationString).isSuccess()) {
sendAnchorSetSuccessMessage(issuer, anchorName, locationString);
} else {
sendAnchorSetFailedMessage(issuer, anchorName);
}
}
private void sendAnchorSetSuccessMessage(MVCommandIssuer issuer, String anchorName, String locationString) {
issuer.sendMessage("&aAnchor &f" + anchorName + "&a set to &f" + locationString);
}
private void sendAnchorSetFailedMessage(MVCommandIssuer issuer, String anchorName) {
issuer.sendMessage("&cFailed to set anchor &f" + anchorName + ".");
}
}
@@ -1,82 +0,0 @@
package org.mvplugins.multiverse.core.commands;
import co.aikar.commands.annotation.CommandAlias;
import co.aikar.commands.annotation.CommandCompletion;
import co.aikar.commands.annotation.CommandPermission;
import co.aikar.commands.annotation.Description;
import co.aikar.commands.annotation.Subcommand;
import co.aikar.commands.annotation.Syntax;
import jakarta.inject.Inject;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Service;
import org.mvplugins.multiverse.core.command.LegacyAliasCommand;
import org.mvplugins.multiverse.core.command.MVCommandIssuer;
import org.mvplugins.multiverse.core.destination.DestinationInstance;
import org.mvplugins.multiverse.core.locale.MVCorei18n;
import org.mvplugins.multiverse.core.locale.message.Message;
import org.mvplugins.multiverse.core.locale.message.MessageReplacement.Replace;
import org.mvplugins.multiverse.core.permissions.CorePermissionsChecker;
import org.mvplugins.multiverse.core.teleportation.LocationManipulation;
import static org.mvplugins.multiverse.core.locale.message.MessageReplacement.replace;
@Service
class CheckCommand extends CoreCommand {
private final CorePermissionsChecker corePermissionsChecker;
private final LocationManipulation locationManipulation;
@Inject
CheckCommand(
@NotNull CorePermissionsChecker corePermissionsChecker,
@NotNull LocationManipulation locationManipulation
) {
this.corePermissionsChecker = corePermissionsChecker;
this.locationManipulation = locationManipulation;
}
@Subcommand("check")
@CommandPermission("multiverse.core.check")
@CommandCompletion("@players @destinations")
@Syntax("<player> <destination>")
@Description("{@@mv-core.check.description}")
void onCheckCommand(
MVCommandIssuer issuer,
@Syntax("<player>")
@Description("{@@mv-core.check.player.description}")
Player player,
@Syntax("<destination>")
@Description("{@@mv-core.check.destination.description}")
DestinationInstance<?, ?> destination) {
issuer.sendInfo(this.corePermissionsChecker.checkTeleportPermission(player, player, destination)
? MVCorei18n.CHECK_HASPERMISSION
: MVCorei18n.CHECK_NOPERMISSION,
Replace.PLAYER.with(player.getName()),
Replace.DESTINATION.with(destination));
issuer.sendInfo(MVCorei18n.CHECK_LOCATION,
replace("{location}").with(destination.getLocation(player)
.map(locationManipulation::locationToString)
.map(Message::of)
.getOrElse(() -> Message.of(MVCorei18n.GENERIC_NULL, "Null!"))));
// TODO: Show permission required for this particular destination
}
@Service
private final static class LegacyAlias extends CheckCommand implements LegacyAliasCommand {
@Inject
LegacyAlias(@NotNull CorePermissionsChecker corePermissionsChecker, @NotNull LocationManipulation locationManipulation) {
super(corePermissionsChecker, locationManipulation);
}
@Override
@CommandAlias("mvcheck")
void onCheckCommand(MVCommandIssuer issuer, Player player, DestinationInstance<?, ?> destination) {
super.onCheckCommand(issuer, player, destination);
}
}
}
Loaded 100 of 409 files, more files were not shown because too many files have changed in this diff. Show more