Merge branch 'MV5' into list-command

# Conflicts:
#	src/main/java/com/onarandombox/MultiverseCore/MultiverseCore.java
This commit is contained in:
Ben Woo 2023-08-31 23:34:10 +08:00
commit 55805aa618
No known key found for this signature in database
GPG Key ID: FB2A3645536E12C8
184 changed files with 8385 additions and 2253 deletions

View File

@ -1,22 +0,0 @@
name: Maven CI/CD
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
build_and_test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up JDK 8
uses: actions/setup-java@v1
with:
java-version: 1.8
- name: Build with Maven
run: mvn -B package --file pom.xml

View File

@ -0,0 +1,34 @@
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 }}

View File

@ -0,0 +1,83 @@
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 }}

View File

@ -0,0 +1,31 @@
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 }}

View File

@ -0,0 +1,38 @@
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'

View File

@ -0,0 +1,91 @@
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@v3
with:
ref: ${{ inputs.promote_from }}
- uses: actions/setup-java@v3
with:
java-version: '11'
distribution: 'adopt'
cache: gradle
- name: Validate Gradle wrapper
uses: gradle/wrapper-validation-action@v1
- name: Build and test
uses: gradle/gradle-build-action@v2
with:
arguments: clean build -x assemble -x shadowJar -x checkStyleMain -x checkStyleTest
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Create release
id: release
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'
uses: gradle/gradle-build-action@v2
with:
arguments: publish -x checkStyleMain -x checkStyleTest -x test
env:
GITHUB_VERSION: ${{ steps.release.outputs.publish_version }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Upload release artifact
if: steps.release.outputs.release_created == 'true'
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: build/libs/${{ inputs.plugin_name }}-${{ steps.release.outputs.publish_version }}.jar
asset_name: ${{ inputs.plugin_name }}-${{ steps.release.outputs.tag_name }}.jar
tag: ${{ steps.release.outputs.tag_name }}

View File

@ -0,0 +1,140 @@
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.20.1, 1.20, 1.19.4, 1.19.3, 1.19.2, 1.19.1, 1.19, 1.18.2, 1.18.1, 1.18, 1.17.1, 1.17, 1.16.5, 1.16.4, 1.16.3, 1.16.2, 1.16.1, 1.16, 1.15.2, 1.15.1, 1.15, 1.14.4, 1.14.3, 1.14.2, 1.14.1, 1.14, 1.13.2, 1.13.1, 1.13
version_type: ${{ steps.parse-release-type.outputs.release_type }}
loaders: bukkit, spigot, paper
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.20.1, 1.20, 1.19.4, 1.19.3, 1.19.2, 1.19.1, 1.19, 1.18.2, 1.18.1, 1.18, 1.17, 1.16, 1.15, 1.14, 1.13
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.13-1.20.1"]}'
plugin_dependencies: ${{ inputs.hangar_plugin_dependencies }}

40
.github/workflows/generic.test.yml vendored Normal file
View File

@ -0,0 +1,40 @@
name: 'Generic: Test'
on:
workflow_call:
inputs:
plugin_name:
description: 'Plugin name'
required: true
type: string
jobs:
test:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@v3
- uses: actions/setup-java@v3
with:
java-version: '17'
distribution: 'adopt'
cache: gradle
- name: Validate Gradle wrapper
uses: gradle/wrapper-validation-action@v1
- name: Run unit tests
uses: gradle/gradle-build-action@v2
with:
arguments: build -x checkstyleMain -x checkstyleTest -x javadoc
env:
GITHUB_VERSION: pr${{ github.event.pull_request.number }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Artifact output
uses: actions/upload-artifact@v3
with:
name: ${{ inputs.plugin_name }}-pr${{ github.event.pull_request.number }}
path: build/libs/${{ inputs.plugin_name }}-pr${{ github.event.pull_request.number }}.jar

24
.github/workflows/main.prerelease.yml vendored Normal file
View File

@ -0,0 +1,24 @@
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'

18
.github/workflows/pr.require_label.yml vendored Normal file
View File

@ -0,0 +1,18 @@
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
.github/workflows/pr.test.yml vendored Normal file
View File

@ -0,0 +1,11 @@
name: 'PR: Test'
on:
pull_request:
types: [opened, synchronize]
jobs:
test:
uses: ./.github/workflows/generic.test.yml
with:
plugin_name: multiverse-core

3
.gitignore vendored
View File

@ -43,3 +43,6 @@ debug.log
# Doxygen
/docs/html
debug.txt
# Gradle
.gradle

View File

@ -1,44 +1,50 @@
![Multiverse 2](config/multiverse2-long.png)
<p align="center">
<img src="config/multiverse2-long.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://cdn.jsdelivr.net/npm/@intergrav/devins-badges@3/assets/cozy/supported/bukkit_vector.svg)](https://dev.bukkit.org/projects/multiverse-core)
[![Spigot](https://cdn.jsdelivr.net/npm/@intergrav/devins-badges@3/assets/cozy/supported/spigot_vector.svg)](https://www.spigotmc.org/resources/multiverse-core.390/)
[![Maven CI/CD](https://github.com/Multiverse/Multiverse-Core/actions/workflows/build.yml/badge.svg)](https://github.com/Multiverse/Multiverse-Core/actions/workflows/build.yml)
[![Release](https://img.shields.io/nexus/r/com.onarandombox.multiversecore/Multiverse-Core?label=release&server=https%3A%2F%2Frepo.onarandombox.com%2F)](https://dev.bukkit.org/projects/multiverse-core)
[![Dev builds](https://img.shields.io/nexus/s/com.onarandombox.multiversecore/Multiverse-Core?label=dev%20builds&server=http%3A%2F%2Frepo.onarandombox.com%2F)](https://ci.onarandombox.com/job/Multiverse-Core/)
[![Discord](https://img.shields.io/discord/325459248047980545?label=discord&logo=discord)](https://discord.gg/NZtfKky)
[![Support me on Patreon](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fshieldsio-patreon.vercel.app%2Fapi%3Fusername%3Ddumptruckman%26type%3Dpatrons&style=flat)](https://patreon.com/dumptruckman)
[![Support us on Patreon](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fshieldsio-patreon.vercel.app%2Fapi%3Fusername%3Ddumptruckman%26type%3Dpatrons&style=flat)](https://patreon.com/dumptruckman)
[![License](https://img.shields.io/github/license/Multiverse/Multiverse-Core)](LICENSE.md)
About
========
# About
[Multiverse](https://dev.bukkit.org/projects/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!
<br><br>
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:
## 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 teleprompters!
* [Multiverse-SignPortals](https://github.com/Multiverse/Multiverse-SignPortals) -> Signs as teleporters!
Building
========
Simply build the source with maven:
## Building
Simply build the source with Gradle:
```
$ mvn install
./gradlew build
```
More details are available on the [build instructions wiki page](https://github.com/Multiverse/Multiverse-Core/wiki/Building).
Contributing
=======
## 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-Core/wiki) 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 joining our [patreon](https://www.patreon.com/dumptruckman) or make a one-time donation [here](https://paypal.me/dumptruckman)!
* If you'd like to make a financial contribution to the project, do consider joining our [Patreon](https://www.patreon.com/dumptruckman) or make a one-time donation [here](https://paypal.me/dumptruckman)!
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
=======
## License
Multiverse-Core is licensed under BSD-3-Clause License. Please see [LICENSE.md](LICENSE.md) for more info.

323
build.gradle Normal file
View File

@ -0,0 +1,323 @@
import org.apache.tools.ant.filters.ReplaceTokens
plugins {
id 'java-library'
id 'maven-publish'
id 'checkstyle'
id 'com.github.johnrengelman.shadow' version '7.1.2'
id "org.jetbrains.kotlin.jvm" version "1.8.10"
}
version = System.getenv('GITHUB_VERSION') ?: 'local'
group = 'com.onarandombox.multiversecore'
description = 'Multiverse-Core'
java {
toolchain {
languageVersion.set(JavaLanguageVersion.of(17))
}
}
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 = 'https://repo.glaremasters.me/repository/towny/'
}
maven {
name = 'PlaceholderAPI'
url = 'https://repo.extendedclip.com/content/repositories/placeholderapi/'
}
}
configurations {
// Add configuration for server API
compileOnly.extendsFrom serverApi
runtimeClasspath.extendsFrom serverApi
// Add configuration for external plugins
implementation.extendsFrom externalPlugin
// Add configuration for dependencies that will be included in fat jar
compileClasspath.extendsFrom shadowed
testCompileClasspath.extendsFrom shadowed
testRuntimeClasspath.extendsFrom shadowed
oldTestCompileClasspath.extendsFrom shadowed
oldTestRuntimeClasspath.extendsFrom shadowed
// Add configuration for old tests
oldTestImplementation.extendsFrom implementation
oldTestRuntime.extendsFrom runtime
}
dependencies {
serverApi 'org.spigotmc:spigot-api:1.16.5-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.3'
// Command Framework
shadowed 'co.aikar:acf-paper:0.5.1-SNAPSHOT'
// Config
shadowed('me.main__.util:SerializationConfig:1.7') {
exclude group: 'org.bukkit', module: 'bukkit'
}
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('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:2.2.1'
shadowed 'net.minidev:json-smart:2.4.8'
shadowed 'org.jetbrains:annotations:22.0.0'
shadowed 'io.papermc:paperlib:1.0.8'
// Tests
testImplementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.10'
testImplementation 'com.github.seeseemelk:MockBukkit-v1.19:2.141.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'
// Old Tests
oldTestImplementation 'org.spigotmc:spigot-api:1.19.3-R0.1-SNAPSHOT'
oldTestImplementation('com.googlecode.json-simple:json-simple:1.1.1') {
exclude group: 'junit', module: 'junit'
}
oldTestImplementation 'junit:junit:4.13.1'
oldTestImplementation 'org.mockito:mockito-core:3.11.2'
oldTestImplementation 'commons-io:commons-io:2.7'
// Annotation Processors
annotationProcessor 'org.glassfish.hk2:hk2-metadata-generator:3.0.3'
testAnnotationProcessor 'org.glassfish.hk2:hk2-metadata-generator:3.0.3'
}
java {
withSourcesJar()
withJavadocJar()
}
tasks.withType(JavaCompile) {
options.encoding = 'UTF-8'
}
tasks.withType(Javadoc) {
options.encoding = 'UTF-8'
}
configurations {
[apiElements, runtimeElements].each {
it.outgoing.artifacts.removeIf { it.buildDependencies.getDependencies(null).contains(jar) }
it.outgoing.artifact(shadowJar)
}
}
sourceSets {
oldTest {
java {
compileClasspath += main.output
runtimeClasspath += main.output
srcDir file('src/old-test/java')
}
resources.srcDir file('src/old-test/resources')
}
}
publishing {
publications {
maven(MavenPublication) {
from components.java
pom.withXml {
Node pomNode = asNode()
// Remove Kotlin dependency
pomNode.dependencies.'*'.findAll() {
it.groupId.text() == 'org.jetbrains.kotlin'
}.each() {
it.parent().remove(it)
}
// Switch runtime deps to provided
pomNode.dependencies.'*'.findAll() {
it.scope.text() == 'runtime'
}.each() {
it.scope*.value = 'provided'
}
// Add spigot api to pom
project.configurations.serverApi.allDependencies.each { dependency ->
pomNode.dependencies[0].appendNode("dependency").with {
it.appendNode("groupId", dependency.group)
it.appendNode("artifactId", dependency.name)
it.appendNode("version", dependency.version)
it.appendNode("scope", "provided")
}
}
}
}
}
repositories {
maven {
name = "GitHubPackages"
url = "https://maven.pkg.github.com/Multiverse/Multiverse-Core"
credentials {
username = System.getenv("GITHUB_ACTOR")
password = System.getenv("GITHUB_TOKEN")
}
}
}
}
ext.bitlyAccessToken = System.getenv('BITLY_ACCESS_TOKEN') ?: 'bitly-access-token'
task prepareSource(type: Sync) {
inputs.property 'bitlyAccessToken', bitlyAccessToken
from sourceSets.main.java
into "$buildDir/src"
filter(ReplaceTokens, tokens: [
'bitly-access-token': bitlyAccessToken,
])
}
compileJava {
source = prepareSource.outputs
}
tasks.withType(JavaCompile) {
configure(options) {
options.compilerArgs << '-Aorg.glassfish.hk2.metadata.location=META-INF/hk2-locator/Multiverse-Core'
}
}
compileKotlin {
// We're not using Kotlin in the plugin itself, just tests!
enabled = false
}
configurations.findAll { !it.name.startsWith('test') }.each {
it.exclude group: 'org.jetbrains.kotlin', module: 'kotlin-stdlib-jdk8'
}
processResources {
def props = [version: "${project.version}"]
inputs.properties props
filteringCharset 'UTF-8'
filesMatching('plugin.yml') {
expand props
}
// This task should never be skipped. The tests depend on this having been run but we want the new version number
// that is created after tests are run and before we run again to publish.
outputs.upToDateWhen { false }
}
checkstyle {
toolVersion = '6.1.1'
configFile file('config/mv_checks.xml')
ignoreFailures = true
}
javadoc {
source = sourceSets.main.allJava
classpath = configurations.compileClasspath
}
project.configurations.api.canBeResolved = true
shadowJar {
relocate 'co.aikar', 'com.onarandombox.acf'
relocate 'com.dumptruckman.minecraft.util.Logging', 'com.onarandombox.MultiverseCore.utils.CoreLogging'
relocate 'com.dumptruckman.minecraft.util.DebugLog', 'com.onarandombox.MultiverseCore.utils.DebugFileLogger'
relocate 'de.themoep.idconverter', 'com.onarandombox.idconverter'
relocate 'io.github.townyadvanced.commentedconfiguration', 'com.onarandombox.commentedconfiguration'
relocate 'me.main__.util', 'com.onarandombox.serializationconfig'
relocate 'org.bstats', 'com.onarandombox.bstats'
relocate 'com.sun', 'com.onarandombox.sun'
relocate 'net.minidev', 'com.onarandombox.minidev'
relocate 'org.objectweb', 'com.onarandombox.objectweb'
relocate 'io.vavr', 'com.onarandombox.vavr'
relocate 'jakarta', 'com.onarandombox.jakarta'
relocate 'javassist', 'com.onarandombox.javassist'
relocate 'org.aopalliance', 'com.onarandombox.aopalliance'
relocate 'org.glassfish', 'com.onarandombox.glassfish'
relocate 'org.jvnet', 'com.onarandombox.jvnet'
relocate 'org.intellij', 'com.onarandombox.intellij'
relocate 'org.jetbrains', 'com.onarandombox.jetbrains'
relocate 'io.papermc.lib', 'com.onarandombox.paperlib'
configurations = [project.configurations.shadowed]
archiveFileName = "$baseName-$version.$extension"
dependencies {
exclude(dependency {
it.moduleGroup == 'org.jetbrains.kotlin'
})
}
//classifier = ''
}
build.dependsOn shadowJar
jar.enabled = false
test {
useJUnitPlatform()
testLogging {
exceptionFormat = 'full'
}
}

BIN
gradle/wrapper/gradle-wrapper.jar vendored Normal file

Binary file not shown.

View File

@ -0,0 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip
networkTimeout=10000
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

244
gradlew vendored Executable file
View File

@ -0,0 +1,244 @@
#!/bin/sh
#
# Copyright © 2015-2021 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
##############################################################################
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done
# This is normally unused
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
warn () {
echo "$*"
} >&2
die () {
echo
echo "$*"
echo
exit 1
} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "$( uname )" in #(
CYGWIN* ) cygwin=true ;; #(
Darwin* ) darwin=true ;; #(
MSYS* | MINGW* ) msys=true ;; #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD=$JAVA_HOME/jre/sh/java
else
JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD=java
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=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=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
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
die "xargs is not available"
fi
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@"

92
gradlew.bat vendored Normal file
View File

@ -0,0 +1,92 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=.
@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.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if %ERRORLEVEL% equ 0 goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

377
pom.xml
View File

@ -1,377 +0,0 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.onarandombox.multiversecore</groupId>
<artifactId>Multiverse-Core</artifactId>
<version>5.0.0-SNAPSHOT</version>
<name>Multiverse-Core</name>
<description>World Management Plugin</description>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.build.number>UNKNOWN</project.build.number>
<project.bitly-access-token>bitly-access-token</project.bitly-access-token>
</properties>
<repositories>
<!-- Spigot -->
<repository>
<id>spigot-repo</id>
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
</repository>
<repository>
<id>onarandombox</id>
<url>https://repo.onarandombox.com/content/groups/public</url>
</repository>
<repository>
<id>spigot</id>
<url>https://hub.spigotmc.org/nexus/content/groups/public/</url>
</repository>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
<repository>
<id>minebench-repo</id>
<url>https://repo.minebench.de/</url>
</repository>
<repository>
<id>aikar</id>
<url>https://repo.aikar.co/content/groups/aikar/</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>doodleproject-repo</id>
<name>DoodleProject Maven 2 Repository</name>
<url>https://doodleproject.sourceforge.net/maven2/release</url>
<releases>
<enabled>true</enabled>
</releases>
</pluginRepository>
</pluginRepositories>
<ciManagement>
<system>jenkins</system>
<url>https://ci.onarandombox.com</url>
</ciManagement>
<!-- Profiles are used to detect whether this is a local or Jenkins build
and adjust the build number accordingly -->
<profiles>
<profile>
<id>jenkins</id>
<activation>
<property>
<name>env.BUILD_NUMBER</name>
</property>
</activation>
<properties>
<project.build.number>${env.BUILD_NUMBER}</project.build.number>
</properties>
</profile>
<profile>
<id>bitly</id>
<activation>
<property>
<name>env.BITLY_ACCESS_TOKEN</name>
</property>
</activation>
<properties>
<project.bitly-access-token>${env.BITLY_ACCESS_TOKEN}</project.bitly-access-token>
</properties>
</profile>
</profiles>
<build>
<defaultGoal>clean package</defaultGoal>
<plugins>
<!-- Compiler -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<groupId>com.google.code.maven-replacer-plugin</groupId>
<artifactId>maven-replacer-plugin</artifactId>
<version>1.4.1</version>
<executions>
<execution>
<id>replace-bitly-access-token</id>
<phase>generate-sources</phase>
<goals>
<goal>replace</goal>
</goals>
<configuration>
<basedir>${project.build.sourceDirectory}</basedir>
<includes>
<include>com/onarandombox/MultiverseCore/utils/webpaste/BitlyURLShortener.java</include>
</includes>
<replacements>
<replacement>
<token>bitly-access-token</token>
<value>${project.bitly-access-token}</value>
</replacement>
</replacements>
</configuration>
</execution>
<execution>
<id>replace-maven-version-number</id>
<phase>prepare-package</phase>
<goals>
<goal>replace</goal>
</goals>
<configuration>
<basedir>${project.build.directory}/classes</basedir>
<includes>
<include>plugin.yml</include>
</includes>
<replacements>
<replacement>
<token>maven-version-number</token>
<value>${project.version}-b${project.build.number}</value>
</replacement>
</replacements>
</configuration>
</execution>
</executions>
</plugin>
<!-- Jar Plugin -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.0.2</version>
<configuration>
<archive>
<manifestEntries>
</manifestEntries>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M3</version>
<configuration>
<skipTests>true</skipTests>
<excludes>
<exclude>**/TestCommandSender.java</exclude>
<exclude>**/TestInstanceCreator.java</exclude>
</excludes>
</configuration>
<dependencies>
<dependency>
<groupId>org.apache.maven.surefire</groupId>
<artifactId>surefire-junit47</artifactId>
<version>3.0.0-M3</version>
</dependency>
</dependencies>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<version>2.17</version>
<configuration>
<enableRulesSummary>true</enableRulesSummary>
<configLocation>${project.basedir}/config/mv_checks.xml</configLocation>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.0.1</version>
<executions>
<execution>
<id>attach-sources</id>
<phase>package</phase>
<goals>
<goal>jar-no-fork</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.10.4</version>
<executions>
<execution>
<id>javadoc-jar</id>
<phase>verify</phase>
<goals>
<goal>jar</goal>
</goals>
<configuration>
<failOnError>false</failOnError>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.4</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<minimizeJar>true</minimizeJar>
<relocations>
<relocation>
<pattern>me.main__.util</pattern>
<shadedPattern>com.onarandombox.serializationconfig</shadedPattern>
</relocation>
<relocation>
<pattern>org.bstats</pattern>
<shadedPattern>com.onarandombox.bstats</shadedPattern>
</relocation>
<relocation>
<pattern>com.dumptruckman.minecraft.util.Logging</pattern>
<shadedPattern>com.onarandombox.MultiverseCore.utils.CoreLogging</shadedPattern>
</relocation>
<relocation>
<pattern>com.dumptruckman.minecraft.util.DebugLog</pattern>
<shadedPattern>com.onarandombox.MultiverseCore.utils.DebugFileLogger</shadedPattern>
</relocation>
<relocation>
<pattern>org.codehaus.jettison</pattern>
<shadedPattern>com.onarandombox.jettison</shadedPattern>
</relocation>
<relocation>
<pattern>de.themoep.idconverter</pattern>
<shadedPattern>com.onarandombox.idconverter</shadedPattern>
</relocation>
<relocation>
<pattern>co.aikar.commands</pattern>
<shadedPattern>com.onarandombox.acf</shadedPattern>
</relocation>
<relocation>
<pattern>co.aikar.locales</pattern>
<shadedPattern>com.onarandombox.locales</shadedPattern>
</relocation>
</relocations>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>se.eris</groupId>
<artifactId>notnull-instrumenter-maven-plugin</artifactId>
<version>1.1.1</version>
<executions>
<execution>
<goals>
<goal>instrument</goal>
<goal>tests-instrument</goal>
</goals>
<configuration>
<notNull>
<param>org.jetbrains.annotations.NotNull</param>
<param>javax.validation.constraints.NotNull</param>
</notNull>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>1.16.5-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<!-- SerializationConfig Dependency -->
<dependency>
<groupId>me.main__.util</groupId>
<artifactId>SerializationConfig</artifactId>
<version>1.7</version>
<exclusions>
<exclusion>
<groupId>org.bukkit</groupId>
<artifactId>bukkit</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- End of SerializationConfig Dependency -->
<!-- Start of Economy Dependency -->
<dependency>
<groupId>com.github.MilkBowl</groupId>
<artifactId>VaultAPI</artifactId>
<version>1.7.1</version>
<scope>provided</scope>
</dependency>
<!-- End of Economy Dependency -->
<!-- Start of CommandHandler Dependency -->
<dependency>
<groupId>co.aikar</groupId>
<artifactId>acf-paper</artifactId>
<version>0.5.1-SNAPSHOT</version>
</dependency>
<!-- End of CommandHandler Dependency -->
<dependency>
<groupId>net.minidev</groupId>
<artifactId>json-smart</artifactId>
<version>2.4.8</version>
</dependency>
<dependency>
<groupId>org.bstats</groupId>
<artifactId>bstats-bukkit</artifactId>
<version>2.2.1</version>
</dependency>
<dependency>
<groupId>com.dumptruckman.minecraft</groupId>
<artifactId>Logging</artifactId>
<version>1.1.1</version>
</dependency>
<dependency>
<groupId>org.jetbrains</groupId>
<artifactId>annotations</artifactId>
<version>16.0.2</version>
</dependency>
<dependency>
<groupId>de.themoep.idconverter</groupId>
<artifactId>mappings</artifactId>
<version>1.2-SNAPSHOT</version>
</dependency>
<!-- Start of Test Dependencies -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>3.11.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
<scope>test</scope>
</dependency>
</dependencies>
<distributionManagement>
<repository>
<id>OnARandomBox</id>
<url>https://repo.onarandombox.com/content/repositories/multiverse</url>
</repository>
<snapshotRepository>
<id>OnARandomBox</id>
<url>https://repo.onarandombox.com/content/repositories/multiverse-snapshots/</url>
</snapshotRepository>
</distributionManagement>
</project>

11
settings.gradle Normal file
View File

@ -0,0 +1,11 @@
/*
* This file was generated by the Gradle 'init' task.
*/
pluginManagement {
repositories {
gradlePluginPortal()
}
}
rootProject.name = 'multiverse-core'

View File

@ -0,0 +1,13 @@
package co.aikar.commands;
import org.bukkit.command.CommandSender;
/**
* Exists just so we can extend BukkitCommandIssuer since it has a package-private constructor.
*/
public abstract class OpenBukkitCommandIssuer extends BukkitCommandIssuer {
protected OpenBukkitCommandIssuer(BukkitCommandManager manager, CommandSender sender) {
super(manager, sender);
}
}

View File

@ -7,103 +7,71 @@
package com.onarandombox.MultiverseCore;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.dumptruckman.minecraft.util.Logging;
import com.onarandombox.MultiverseCore.anchor.AnchorManager;
import com.onarandombox.MultiverseCore.api.BlockSafety;
import com.onarandombox.MultiverseCore.api.LocationManipulation;
import com.onarandombox.MultiverseCore.api.MVConfig;
import com.onarandombox.MultiverseCore.api.Destination;
import com.onarandombox.MultiverseCore.api.MVCore;
import com.onarandombox.MultiverseCore.api.MVWorld;
import com.onarandombox.MultiverseCore.api.MVWorldManager;
import com.onarandombox.MultiverseCore.api.SafeTTeleporter;
import com.onarandombox.MultiverseCore.commands.CheckCommand;
import com.onarandombox.MultiverseCore.commands.CloneCommand;
import com.onarandombox.MultiverseCore.commands.ConfirmCommand;
import com.onarandombox.MultiverseCore.commands.CreateCommand;
import com.onarandombox.MultiverseCore.commands.DebugCommand;
import com.onarandombox.MultiverseCore.commands.DeleteCommand;
import com.onarandombox.MultiverseCore.commands.GameruleCommand;
import com.onarandombox.MultiverseCore.commands.ListCommand;
import com.onarandombox.MultiverseCore.commands.LoadCommand;
import com.onarandombox.MultiverseCore.commands.RegenCommand;
import com.onarandombox.MultiverseCore.commands.ReloadCommand;
import com.onarandombox.MultiverseCore.commands.TeleportCommand;
import com.onarandombox.MultiverseCore.commands.UnloadCommand;
import com.onarandombox.MultiverseCore.commandtools.MVCommandManager;
import com.onarandombox.MultiverseCore.commandtools.MultiverseCommand;
import com.onarandombox.MultiverseCore.commandtools.PluginLocales;
import com.onarandombox.MultiverseCore.config.MVCoreConfig;
import com.onarandombox.MultiverseCore.destination.DestinationsProvider;
import com.onarandombox.MultiverseCore.destination.core.AnchorDestination;
import com.onarandombox.MultiverseCore.destination.core.BedDestination;
import com.onarandombox.MultiverseCore.destination.core.CannonDestination;
import com.onarandombox.MultiverseCore.destination.core.ExactDestination;
import com.onarandombox.MultiverseCore.destination.core.PlayerDestination;
import com.onarandombox.MultiverseCore.destination.core.WorldDestination;
import com.onarandombox.MultiverseCore.economy.MVEconomist;
import com.onarandombox.MultiverseCore.event.MVDebugModeEvent;
import com.onarandombox.MultiverseCore.listeners.MVChatListener;
import com.onarandombox.MultiverseCore.listeners.MVEntityListener;
import com.onarandombox.MultiverseCore.listeners.MVPlayerListener;
import com.onarandombox.MultiverseCore.listeners.MVPortalListener;
import com.onarandombox.MultiverseCore.listeners.MVWeatherListener;
import com.onarandombox.MultiverseCore.listeners.MVWorldInitListener;
import com.onarandombox.MultiverseCore.listeners.MVWorldListener;
import com.onarandombox.MultiverseCore.teleportation.SimpleBlockSafety;
import com.onarandombox.MultiverseCore.teleportation.SimpleLocationManipulation;
import com.onarandombox.MultiverseCore.teleportation.SimpleSafeTTeleporter;
import com.onarandombox.MultiverseCore.utils.MVPermissions;
import com.onarandombox.MultiverseCore.inject.InjectableListener;
import com.onarandombox.MultiverseCore.inject.PluginInjection;
import com.onarandombox.MultiverseCore.placeholders.MultiverseCorePlaceholders;
import com.onarandombox.MultiverseCore.utils.TestingMode;
import com.onarandombox.MultiverseCore.utils.UnsafeCallWrapper;
import com.onarandombox.MultiverseCore.utils.metrics.MetricsConfigurator;
import com.onarandombox.MultiverseCore.world.SimpleMVWorldManager;
import com.onarandombox.MultiverseCore.world.WorldProperties;
import com.onarandombox.MultiverseCore.worldnew.WorldManager;
import io.vavr.control.Try;
import jakarta.inject.Inject;
import jakarta.inject.Provider;
import me.main__.util.SerializationConfig.SerializationConfig;
import org.bukkit.configuration.Configuration;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.plugin.PluginDescriptionFile;
import org.bukkit.plugin.PluginManager;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.plugin.java.JavaPluginLoader;
import org.glassfish.hk2.api.MultiException;
import org.glassfish.hk2.api.ServiceHandle;
import org.glassfish.hk2.api.ServiceLocator;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jvnet.hk2.annotations.Service;
import java.io.File;
import java.lang.annotation.Annotation;
import java.util.List;
import java.util.logging.Logger;
import java.util.stream.Collectors;
/**
* The implementation of the Multiverse-{@link MVCore}.
*/
@Service
public class MultiverseCore extends JavaPlugin implements MVCore {
private static final int PROTOCOL = 24;
private static final int PROTOCOL = 50;
// Setup various managers
private final AnchorManager anchorManager = new AnchorManager(this);
private BlockSafety blockSafety = new SimpleBlockSafety(this);
private MVCommandManager commandManager;
private DestinationsProvider destinationsProvider;
private MVEconomist economist;
private LocationManipulation locationManipulation = new SimpleLocationManipulation();
private final MVPermissions mvPermissions = new MVPermissions(this);
private SafeTTeleporter safeTTeleporter = new SimpleSafeTTeleporter(this);
private final UnsafeCallWrapper unsafeCallWrapper = new UnsafeCallWrapper(this);
private final MVWorldManager worldManager = new SimpleMVWorldManager(this);
// Configurations
private FileConfiguration multiverseConfig;
private volatile MultiverseCoreConfiguration config;
// Listeners
private MVChatListener chatListener;
private final MVEntityListener entityListener = new MVEntityListener(this);
private final MVPlayerListener playerListener = new MVPlayerListener(this);
private final MVPortalListener portalListener = new MVPortalListener(this);
private final MVWeatherListener weatherListener = new MVWeatherListener(this);
private final MVWorldListener worldListener = new MVWorldListener(this);
private final MVWorldInitListener worldInitListener = new MVWorldInitListener(this);
private ServiceLocator serviceLocator;
@Inject
private Provider<MVCoreConfig> configProvider;
@Inject
private Provider<MVWorldManager> worldManagerProvider;
@Inject
private Provider<WorldManager> newWorldManagerProvider;
@Inject
private Provider<AnchorManager> anchorManagerProvider;
@Inject
private Provider<MVCommandManager> commandManagerProvider;
@Inject
private Provider<DestinationsProvider> destinationsProviderProvider;
@Inject
private Provider<MetricsConfigurator> metricsConfiguratorProvider;
@Inject
private Provider<MVEconomist> economistProvider;
@Inject
private Provider<PluginLocales> pluginLocalesProvider;
// Counter for the number of plugins that have registered with us
private int pluginCount;
@ -124,7 +92,6 @@ public class MultiverseCore extends JavaPlugin implements MVCore {
Logging.init(this);
// Register our config classes
SerializationConfig.registerAll(MultiverseCoreConfiguration.class);
SerializationConfig.registerAll(WorldProperties.class);
SerializationConfig.initLogging(Logging.getLogger());
}
@ -134,38 +101,48 @@ public class MultiverseCore extends JavaPlugin implements MVCore {
*/
@Override
public void onEnable() {
initializeDependencyInjection();
// Load our configs first as we need them for everything else.
this.loadConfigs();
if (this.multiverseConfig == null) {
var config = configProvider.get();
if (!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(!getMVConfig().getSilentStart());
Logging.setShowingConfig(shouldShowConfig());
this.worldManager.getDefaultWorldGenerators();
this.worldManager.loadDefaultWorlds();
this.worldManager.loadWorlds(true);
var worldManager = worldManagerProvider.get();
worldManager.loadWorldsConfig();
worldManager.getDefaultWorldGenerators();
worldManager.loadDefaultWorlds();
worldManager.loadWorlds(true);
// Now set the firstspawnworld (after the worlds are loaded):
this.worldManager.setFirstSpawnWorld(getMVConfig().getFirstSpawnWorld());
MVWorld firstSpawnWorld = this.worldManager.getFirstSpawnWorld();
worldManager.setFirstSpawnWorld(config.getFirstSpawnLocation());
MVWorld firstSpawnWorld = worldManager.getFirstSpawnWorld();
if (firstSpawnWorld != null) {
getMVConfig().setFirstSpawnWorld(firstSpawnWorld.getName());
config.setFirstSpawnLocation(firstSpawnWorld.getName());
}
var newWorldManager = newWorldManagerProvider.get();
newWorldManager.loadAllWorlds(); // TODO: Possibly move this to constructor of WorldManager
//Setup economy here so vault is loaded
this.economist = new MVEconomist(this);
this.loadEconomist();
// Init all the other stuff
this.anchorManager.loadAnchors();
this.loadAnchors();
this.registerEvents();
this.setUpLocales();
this.registerCommands();
this.registerDestinations();
this.setupMetrics();
this.saveMVConfig();
this.loadPlaceholderAPIIntegration();
this.saveAllConfigs();
this.logEnableMessage();
}
@ -175,55 +152,110 @@ public class MultiverseCore extends JavaPlugin implements MVCore {
@Override
public void onDisable() {
this.saveAllConfigs();
shutdownDependencyInjection();
Logging.shutdown();
}
private void initializeDependencyInjection() {
serviceLocator = PluginInjection.createServiceLocator(new MultiverseCorePluginBinder(this))
.andThenTry(locator -> {
PluginInjection.enable(this, locator);
})
.getOrElseThrow(exception -> {
Logging.severe("Failed to initialize dependency injection");
getServer().getPluginManager().disablePlugin(this);
return new RuntimeException(exception);
});
}
private void shutdownDependencyInjection() {
if (serviceLocator != null) {
PluginInjection.disable(this, serviceLocator);
serviceLocator = null;
}
}
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())
.onSuccess(AnchorManager::loadAnchors)
.onFailure(e -> {
Logging.severe("Failed to load anchors");
e.printStackTrace();
});
}
/**
* Function to Register all the Events needed.
*/
private void registerEvents() {
PluginManager pluginManager = getServer().getPluginManager();
this.chatListener = new MVChatListener(this, this.playerListener);
pluginManager.registerEvents(this.chatListener, this);
pluginManager.registerEvents(this.entityListener, this);
pluginManager.registerEvents(this.playerListener, this);
pluginManager.registerEvents(this.portalListener, this);
pluginManager.registerEvents(this.weatherListener, this);
pluginManager.registerEvents(this.worldListener, this);
pluginManager.registerEvents(this.worldInitListener, this);
var pluginManager = getServer().getPluginManager();
Try.run(() -> serviceLocator.getAllServices(InjectableListener.class)
.forEach(listener -> pluginManager.registerEvents(listener, this)))
.onFailure(e -> {
throw new RuntimeException("Failed to register listeners. Terminating...", e);
});
}
/**
* Register Multiverse-Core commands to Command Manager.
*/
private void registerCommands() {
this.commandManager = new MVCommandManager(this);
this.commandManager.registerCommand(new CheckCommand(this));
this.commandManager.registerCommand(new CloneCommand(this));
this.commandManager.registerCommand(new ConfirmCommand(this));
this.commandManager.registerCommand(new CreateCommand(this));
this.commandManager.registerCommand(new DebugCommand(this));
this.commandManager.registerCommand(new DeleteCommand(this));
this.commandManager.registerCommand(new GameruleCommand(this));
this.commandManager.registerCommand(new ListCommand(this));
this.commandManager.registerCommand(new LoadCommand(this));
this.commandManager.registerCommand(new RegenCommand(this));
this.commandManager.registerCommand(new ReloadCommand(this));
this.commandManager.registerCommand(new TeleportCommand(this));
this.commandManager.registerCommand(new UnloadCommand(this));
Try.of(() -> commandManagerProvider.get())
.andThenTry(commandManager -> {
serviceLocator.getAllServices(MultiverseCommand.class)
.forEach(commandManager::registerCommand);
})
.onFailure(e -> {
Logging.severe("Failed to register commands");
e.printStackTrace();
});
}
/**
* Register locales
*/
private void setUpLocales() {
Try.of(() -> commandManagerProvider.get())
.andThen(commandManager -> {
commandManager.usePerIssuerLocale(true, true);
})
.mapTry(commandManager -> pluginLocalesProvider.get())
.andThen(pluginLocales -> {
pluginLocales.addFileResClassLoader(this);
pluginLocales.addMessageBundles("multiverse-core");
})
.onFailure(e -> {
Logging.severe("Failed to register locales");
e.printStackTrace();
});
}
/**
* Register all the destinations.
*/
private void registerDestinations() {
this.destinationsProvider = new DestinationsProvider(this);
this.destinationsProvider.registerDestination(new AnchorDestination(this));
this.destinationsProvider.registerDestination(new BedDestination());
this.destinationsProvider.registerDestination(new CannonDestination(this));
this.destinationsProvider.registerDestination(new ExactDestination(this));
this.destinationsProvider.registerDestination(new PlayerDestination());
this.destinationsProvider.registerDestination(new WorldDestination(this));
Try.of(() -> destinationsProviderProvider.get())
.andThenTry(destinationsProvider -> {
serviceLocator.getAllServices(Destination.class)
.forEach(destinationsProvider::registerDestination);
})
.onFailure(e -> {
Logging.severe("Failed to register destinations");
e.printStackTrace();
});
}
/**
@ -231,7 +263,14 @@ public class MultiverseCore extends JavaPlugin implements MVCore {
*/
private void setupMetrics() {
if (TestingMode.isDisabled()) {
MetricsConfigurator.configureMetrics(this);
// 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.");
}
}
@ -241,9 +280,20 @@ public class MultiverseCore extends JavaPlugin implements MVCore {
private void logEnableMessage() {
Logging.config("Version %s (API v%s) Enabled - By %s", this.getDescription().getVersion(), PROTOCOL, getAuthors());
if (getMVConfig().isShowingDonateMessage()) {
getLogger().config("Help dumptruckman keep this project alive. Become a patron! https://www.patreon.com/dumptruckman");
getLogger().config("One time donations are also appreciated: https://www.paypal.me/dumptruckman");
if (configProvider.get().isShowingDonateMessage()) {
Logging.config("Help dumptruckman keep this project alive. Become a patron! https://www.patreon.com/dumptruckman");
Logging.config("One time donations are also appreciated: https://www.paypal.me/dumptruckman");
}
}
private void loadPlaceholderAPIIntegration() {
if (configProvider.get().isRegisterPapiHook()
&& getServer().getPluginManager().getPlugin("PlaceholderAPI") != null) {
Try.run(() -> serviceLocator.createAndInitialize(MultiverseCorePlaceholders.class))
.onFailure(e -> {
Logging.severe("Failed to load PlaceholderAPI integration.");
e.printStackTrace();
});
}
}
@ -251,40 +301,16 @@ public class MultiverseCore extends JavaPlugin implements MVCore {
* {@inheritDoc}
*/
@Override
public MultiverseCore getCore() {
public MVCore getCore() {
return this;
}
/**
* {@inheritDoc}
*/
@Override
public void setCore(MultiverseCore core) {
// This method is required by the interface (so core is effectively a plugin of itself) and therefore
// this is never used.
}
/**
* {@inheritDoc}
*/
@Override
public int getProtocolVersion() {
return MultiverseCore.PROTOCOL;
}
/**
* {@inheritDoc}
*/
public MVEconomist getEconomist() {
return economist;
}
/**
* {@inheritDoc}
*/
@Override
public MVPermissions getMVPerms() {
return this.mvPermissions;
return PROTOCOL;
}
/**
@ -311,14 +337,6 @@ public class MultiverseCore extends JavaPlugin implements MVCore {
return authors.toString();
}
/**
* {@inheritDoc}
*/
@Override
public MVCommandManager getMVCommandManager() {
return this.commandManager;
}
/**
* {@inheritDoc}
*/
@ -327,6 +345,12 @@ public class MultiverseCore extends JavaPlugin implements MVCore {
return this.pluginCount;
}
@NotNull
@Override
public Logger getLogger() {
return Logging.getLogger();
}
/**
* {@inheritDoc}
*/
@ -343,267 +367,65 @@ public class MultiverseCore extends JavaPlugin implements MVCore {
this.pluginCount -= 1;
}
/**
* {@inheritDoc}
*/
@Override
public DestinationsProvider getDestinationsProvider() {
return this.destinationsProvider;
}
/**
* {@inheritDoc}
*/
@Override
public MVWorldManager getMVWorldManager() {
return this.worldManager;
}
/**
* {@inheritDoc}
*/
@Override
public void loadConfigs() {
// Now grab the Configuration Files.
this.multiverseConfig = YamlConfiguration.loadConfiguration(new File(getDataFolder(), "config.yml"));
InputStream resourceURL = this.getClass().getResourceAsStream("/defaults/config.yml");
// Read in our default config with UTF-8 now
Configuration coreDefaults;
try {
coreDefaults = YamlConfiguration.loadConfiguration(new BufferedReader(new InputStreamReader(resourceURL, "UTF-8")));
this.multiverseConfig.setDefaults(coreDefaults);
} catch (UnsupportedEncodingException e) {
Logging.severe("Couldn't load default config with UTF-8 encoding. Details follow:");
e.printStackTrace();
Logging.severe("Default configs NOT loaded.");
}
this.multiverseConfig.options().copyDefaults(false);
this.multiverseConfig.options().copyHeader(true);
MultiverseCoreConfiguration wantedConfig = null;
try {
wantedConfig = (MultiverseCoreConfiguration) multiverseConfig.get("multiverse-configuration");
} catch (Exception ignore) {
} finally {
config = ((wantedConfig == null) ? new MultiverseCoreConfiguration() : wantedConfig);
}
this.worldManager.loadWorldConfig(new File(getDataFolder(), "worlds.yml"));
int level = Logging.getDebugLevel();
Logging.setDebugLevel(getMVConfig().getGlobalDebug());
if (level != Logging.getDebugLevel()) {
getServer().getPluginManager().callEvent(new MVDebugModeEvent(level));
}
}
/**
* {@inheritDoc}
*/
@Override
public boolean saveMVConfig() {
try {
this.multiverseConfig.set("multiverse-configuration", getMVConfig());
this.multiverseConfig.save(new File(getDataFolder(), "config.yml"));
return true;
} catch (IOException e) {
Logging.severe("Could not save Multiverse config.yml config. Please check your file permissions.");
return false;
}
}
/**
* {@inheritDoc}
*/
@Override
public boolean saveAllConfigs() {
return this.saveMVConfig() && this.worldManager.saveWorldsConfig();
}
/**
* {@inheritDoc}
*/
@Override
public AnchorManager getAnchorManager() {
return this.anchorManager;
}
/**
* {@inheritDoc}
*/
@Override
public BlockSafety getBlockSafety() {
return blockSafety;
}
/**
* {@inheritDoc}
*/
@Override
public void setBlockSafety(BlockSafety blockSafety) {
if (blockSafety == null) {
throw new NullPointerException("block safety may not be null.");
}
this.blockSafety = blockSafety;
}
/**
* {@inheritDoc}
*/
@Override
public LocationManipulation getLocationManipulation() {
return locationManipulation;
}
/**
* {@inheritDoc}
*/
@Override
public void setLocationManipulation(LocationManipulation locationManipulation) {
if (locationManipulation == null) {
throw new NullPointerException("location manipulation may not be null.");
}
this.locationManipulation = locationManipulation;
}
/**
* {@inheritDoc}
*/
@Override
public SafeTTeleporter getSafeTTeleporter() {
return safeTTeleporter;
}
/**
* {@inheritDoc}
*/
@Override
public void setSafeTTeleporter(SafeTTeleporter safeTTeleporter) {
if (safeTTeleporter == null) {
throw new NullPointerException("safeTTeleporter may not be null.");
}
this.safeTTeleporter = safeTTeleporter;
}
/**
* {@inheritDoc}
*/
@Override
public MVConfig getMVConfig() {
return config;
}
/**
* {@inheritDoc}
*/
@Override
public UnsafeCallWrapper getUnsafeCallWrapper() {
return this.unsafeCallWrapper;
return configProvider.get().save()
&& worldManagerProvider.get().saveWorldsConfig()
&& anchorManagerProvider.get().saveAnchors();
}
//TODO: REMOVE THIS STATIC CRAP - START
private static final Map<String, String> teleportQueue = new HashMap<String, String>();
/**
* This method is used to add a teleportation to the teleportQueue.
* Gets the best service from this plugin that implements the given contract or has the given implementation.
*
* @param teleporter The name of the player that initiated the teleportation.
* @param teleportee The name of the player that was teleported.
* @param contractOrImpl The contract or concrete implementation to get the best instance of
* @param qualifiers The set of qualifiers that must match this service definition
* @return An instance of the contract or impl if it is a service and is already instantiated, null otherwise
* @throws MultiException if there was an error during service lookup
*/
public static void addPlayerToTeleportQueue(String teleporter, String teleportee) {
Logging.finest("Adding mapping '%s' => '%s' to teleport queue", teleporter, teleportee);
teleportQueue.put(teleportee, teleporter);
}
/**
* This method is used to find out who is teleporting a player.
* @param playerName The teleported player (the teleportee).
* @return The player that teleported the other one (the teleporter).
*/
public static String getPlayerTeleporter(String playerName) {
if (teleportQueue.containsKey(playerName)) {
String teleportee = teleportQueue.get(playerName);
teleportQueue.remove(playerName);
return teleportee;
@Nullable
public <T> T getService(@NotNull Class<T> contractOrImpl, Annotation... qualifiers) throws MultiException {
var handle = serviceLocator.getServiceHandle(contractOrImpl, qualifiers);
if (handle != null && handle.isActive()) {
return handle.getService();
}
return null;
}
//TODO: REMOVE THIS STATIC CRAP - END
// For testing purposes only //
private File serverFolder = new File(System.getProperty("user.dir"));
/**
* This is for unit testing.
* Gets all services from this plugin that implement the given contract or have the given implementation and have
* the provided qualifiers.
*
* @param loader The PluginLoader to use.
* @param contractOrImpl The contract or concrete implementation to get the best instance of
* @param qualifiers The set of qualifiers that must match this service definition
* @return A list of services implementing this contract or concrete implementation. May not return null, but may
* return an empty list
* @throws MultiException if there was an error during service lookup
*/
@NotNull
public <T> List<T> getAllServices(
@NotNull Class<T> contractOrImpl,
Annotation... qualifiers
) throws MultiException {
var handles = serviceLocator.getAllServiceHandles(contractOrImpl, qualifiers);
return handles.stream()
.filter(ServiceHandle::isActive)
.map(ServiceHandle::getService)
.collect(Collectors.toList());
}
/**
* This is for unit testing ONLY. Do not use this constructor.
*
* @param loader The PluginLoader to use.
* @param description The Description file to use.
* @param dataFolder The folder that other datafiles can be found in.
* @param file The location of the plugin.
* @param dataFolder The folder that other datafiles can be found in.
* @param file The location of the plugin.
*/
public MultiverseCore(JavaPluginLoader loader, PluginDescriptionFile description, File dataFolder, File file) {
super(loader, description, dataFolder, file);
}
/**
* Gets the {@link MVPlayerListener}.
*
* @return The {@link MVPlayerListener}.
*/
public MVPlayerListener getPlayerListener() {
return this.playerListener;
}
/**
* Gets the {@link MVChatListener}.
*
* @return The {@link MVChatListener}.
*/
public MVChatListener getChatListener() {
return this.chatListener;
}
/**
* Gets the {@link MVEntityListener}.
*
* @return The {@link MVEntityListener}.
*/
public MVEntityListener getEntityListener() {
return this.entityListener;
}
/**
* Gets the {@link MVWeatherListener}.
*
* @return The {@link MVWeatherListener}.
*/
public MVWeatherListener getWeatherListener() {
return this.weatherListener;
}
/**
* Gets the server's root-folder as {@link File}.
*
* @return The server's root-folder
*/
public File getServerFolder() {
return serverFolder;
}
/**
* Sets this server's root-folder.
*
* @param newServerFolder The new server-root
*/
public void setServerFolder(File newServerFolder) {
if (!newServerFolder.isDirectory())
throw new IllegalArgumentException("That's not a folder!");
this.serverFolder = newServerFolder;
}
}

View File

@ -1,316 +0,0 @@
package com.onarandombox.MultiverseCore;
import java.util.Map;
import com.dumptruckman.minecraft.util.Logging;
import com.onarandombox.MultiverseCore.api.MVConfig;
import com.onarandombox.MultiverseCore.event.MVDebugModeEvent;
import me.main__.util.SerializationConfig.NoSuchPropertyException;
import me.main__.util.SerializationConfig.Property;
import me.main__.util.SerializationConfig.SerializationConfig;
import org.bukkit.Bukkit;
/**
* Our configuration.
*/
public class MultiverseCoreConfiguration extends SerializationConfig implements MVConfig {
private static MultiverseCoreConfiguration instance;
/**
* Sets the statically saved instance.
* @param instance The new instance.
*/
public static void setInstance(MultiverseCoreConfiguration instance) {
MultiverseCoreConfiguration.instance = instance;
}
/**
* @return True if the static instance of config is set.
*/
public static boolean isSet() {
return instance != null;
}
/**
* Gets the statically saved instance.
* @return The statically saved instance.
*/
@Deprecated
public static MultiverseCoreConfiguration getInstance() {
if (instance == null)
throw new IllegalStateException("The instance wasn't set!");
return instance;
}
@Property
private volatile boolean enforceaccess;
@Property
private volatile boolean prefixchat;
@Property
private volatile String prefixchatformat;
@Property
private volatile boolean teleportintercept;
@Property
private volatile boolean firstspawnoverride;
@Property
private volatile boolean displaypermerrors;
@Property
private volatile int globaldebug;
@Property
private volatile boolean silentstart;
@Property
private volatile double version;
@Property
private volatile String firstspawnworld;
@Property
private volatile boolean defaultportalsearch;
@Property
private volatile int portalsearchradius;
@Property
private volatile boolean autopurge;
@Property
private volatile boolean idonotwanttodonate;
public MultiverseCoreConfiguration() {
super();
MultiverseCoreConfiguration.setInstance(this);
}
public MultiverseCoreConfiguration(Map<String, Object> values) {
super(values);
MultiverseCoreConfiguration.setInstance(this);
}
/**
* {@inheritDoc}
*/
@Override
protected void setDefaults() {
// BEGIN CHECKSTYLE-SUPPRESSION: MagicNumberCheck
enforceaccess = false;
prefixchat = false;
prefixchatformat = "[%world%]%chat%";
teleportintercept = true;
firstspawnoverride = true;
displaypermerrors = true;
globaldebug = 0;
this.version = 2.9;
silentstart = false;
defaultportalsearch = true;
portalsearchradius = 128;
autopurge = true;
idonotwanttodonate = false;
// END CHECKSTYLE-SUPPRESSION: MagicNumberCheck
}
/**
* {@inheritDoc}
*/
@Override
public boolean setConfigProperty(String property, String value) {
try {
return this.setProperty(property, value, true);
} catch (NoSuchPropertyException e) {
return false;
}
}
// And here we go:
/**
* {@inheritDoc}
*/
@Override
public boolean getEnforceAccess() {
return this.enforceaccess;
}
/**
* {@inheritDoc}
*/
@Override
public void setEnforceAccess(boolean enforceAccess) {
this.enforceaccess = enforceAccess;
}
/**
* {@inheritDoc}
*/
@Override
public boolean getPrefixChat() {
return this.prefixchat;
}
/**
* {@inheritDoc}
*/
@Override
public void setPrefixChat(boolean prefixChat) {
this.prefixchat = prefixChat;
}
/**
* {@inheritDoc}
*/
@Override
public String getPrefixChatFormat() {
return this.prefixchatformat;
}
/**
* {@inheritDoc}
*/
@Override
public void setPrefixChatFormat(String prefixChatFormat) {
this.prefixchatformat = prefixChatFormat;
}
/**
* {@inheritDoc}
*/
@Override
public boolean getTeleportIntercept() {
return this.teleportintercept;
}
/**
* {@inheritDoc}
*/
@Override
public void setTeleportIntercept(boolean teleportIntercept) {
this.teleportintercept = teleportIntercept;
}
/**
* {@inheritDoc}
*/
@Override
public boolean getFirstSpawnOverride() {
return this.firstspawnoverride;
}
/**
* {@inheritDoc}
*/
@Override
public void setFirstSpawnOverride(boolean firstSpawnOverride) {
this.firstspawnoverride = firstSpawnOverride;
}
/**
* {@inheritDoc}
*/
@Override
public boolean getDisplayPermErrors() {
return this.displaypermerrors;
}
/**
* {@inheritDoc}
*/
@Override
public void setDisplayPermErrors(boolean displayPermErrors) {
this.displaypermerrors = displayPermErrors;
}
/**
* {@inheritDoc}
*/
@Override
public int getGlobalDebug() {
return this.globaldebug;
}
/**
* {@inheritDoc}
*/
@Override
public void setGlobalDebug(int globalDebug) {
this.globaldebug = globalDebug;
Logging.setDebugLevel(globalDebug);
Bukkit.getPluginManager().callEvent(new MVDebugModeEvent(globalDebug));
}
/**
* {@inheritDoc}
*/
@Override
public double getVersion() {
return this.version;
}
/**
* {@inheritDoc}
*/
@Override
public void setVersion(int version) {
this.version = version;
}
/**
* {@inheritDoc}
*/
@Override
public String getFirstSpawnWorld() {
return this.firstspawnworld;
}
/**
* {@inheritDoc}
*/
@Override
public void setFirstSpawnWorld(String firstSpawnWorld) {
this.firstspawnworld = firstSpawnWorld;
}
@Override
public void setSilentStart(boolean silentStart) {
Logging.setShowingConfig(!silentStart);
this.silentstart = silentStart;
}
@Override
public boolean getSilentStart() {
return silentstart;
}
@Override
public void setUseDefaultPortalSearch(boolean useDefaultPortalSearch) {
defaultportalsearch = useDefaultPortalSearch;
}
@Override
public boolean isUsingDefaultPortalSearch() {
return defaultportalsearch;
}
@Override
public void setPortalSearchRadius(int searchRadius) {
this.portalsearchradius = searchRadius;
}
@Override
public int getPortalSearchRadius() {
return portalsearchradius;
}
@Override
public boolean isAutoPurgeEnabled() {
return autopurge;
}
@Override
public void setAutoPurgeEnabled(boolean autopurge) {
this.autopurge = autopurge;
}
@Override
public boolean isShowingDonateMessage() {
return !idonotwanttodonate;
}
@Override
public void setShowDonateMessage(boolean showDonateMessage) {
this.idonotwanttodonate = !showDonateMessage;
}
}

View File

@ -0,0 +1,18 @@
package com.onarandombox.MultiverseCore;
import com.onarandombox.MultiverseCore.api.MVCore;
import com.onarandombox.MultiverseCore.inject.binder.JavaPluginBinder;
import org.glassfish.hk2.utilities.binding.ScopedBindingBuilder;
import org.jetbrains.annotations.NotNull;
class MultiverseCorePluginBinder extends JavaPluginBinder<MultiverseCore> {
protected MultiverseCorePluginBinder(@NotNull MultiverseCore plugin) {
super(plugin);
}
@Override
protected ScopedBindingBuilder<MultiverseCore> bindPluginClass(ScopedBindingBuilder<MultiverseCore> bindingBuilder) {
return super.bindPluginClass(bindingBuilder).to(MVCore.class).to(MultiverseCore.class);
}
}

View File

@ -9,11 +9,16 @@ package com.onarandombox.MultiverseCore.anchor;
import com.dumptruckman.minecraft.util.Logging;
import com.onarandombox.MultiverseCore.MultiverseCore;
import com.onarandombox.MultiverseCore.api.LocationManipulation;
import com.onarandombox.MultiverseCore.config.MVCoreConfig;
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.jvnet.hk2.annotations.Service;
import java.io.File;
import java.io.IOException;
@ -22,18 +27,29 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
/**
* Manages anchors.
*/
@Service
public class AnchorManager {
private MultiverseCore plugin;
private Map<String, Location> anchors;
private FileConfiguration anchorConfig;
public AnchorManager(MultiverseCore plugin) {
private final Plugin plugin;
private final LocationManipulation locationManipulation;
private final MVCoreConfig config;
@Inject
public AnchorManager(
MultiverseCore plugin,
LocationManipulation locationManipulation,
MVCoreConfig config
) {
this.plugin = plugin;
this.locationManipulation = locationManipulation;
this.config = config;
this.anchors = new HashMap<String, Location>();
}
@ -48,7 +64,7 @@ public class AnchorManager {
Set<String> anchorKeys = anchorsSection.getKeys(false);
for (String key : anchorKeys) {
//world:x,y,z:pitch:yaw
Location anchorLocation = plugin.getLocationManipulation().stringToLocation(anchorsSection.getString(key, ""));
Location anchorLocation = this.locationManipulation.stringToLocation(anchorsSection.getString(key, ""));
if (anchorLocation != null) {
Logging.config("Loading anchor: '%s'...", key);
this.anchors.put(key, anchorLocation);
@ -98,7 +114,7 @@ public class AnchorManager {
* @return True if the anchor was successfully saved.
*/
public boolean saveAnchorLocation(String anchor, String location) {
Location parsed = plugin.getLocationManipulation().stringToLocation(location);
Location parsed = this.locationManipulation.stringToLocation(location);
return parsed != null && this.saveAnchorLocation(anchor, parsed);
}
@ -112,7 +128,7 @@ public class AnchorManager {
if (l == null) {
return false;
}
this.anchorConfig.set("anchors." + anchor, plugin.getLocationManipulation().locationToString(l));
this.anchorConfig.set("anchors." + anchor, this.locationManipulation.locationToString(l));
this.anchors.put(anchor, l);
return this.saveAnchors();
}
@ -144,8 +160,8 @@ public class AnchorManager {
// Add to the list if we're not enforcing access
// OR
// We are enforcing access and the user has the permission.
if (!this.plugin.getMVConfig().getEnforceAccess() ||
(this.plugin.getMVConfig().getEnforceAccess() && p.hasPermission(worldPerm))) {
if (!config.getEnforceAccess() ||
(config.getEnforceAccess() && p.hasPermission(worldPerm))) {
myAnchors.add(anchor);
} else {
Logging.finer(String.format("Not adding anchor %s to the list, user %s doesn't have the %s " +

View File

@ -1,95 +0,0 @@
package com.onarandombox.MultiverseCore.api;
import java.io.File;
import java.io.IOException;
import java.util.List;
import com.onarandombox.MultiverseCore.MultiverseCore;
import org.bukkit.plugin.java.JavaPlugin;
/**
* Make things easier for MV-Plugins!
*/
public abstract class AbstractMVPlugin extends JavaPlugin implements MVPlugin {
private MultiverseCore core;
/**
* {@inheritDoc}
*
* Note: You can't override this, use {@link #onPluginEnable()} instead!
* @see #onPluginEnable()
*/
@Override
public final void onEnable() {
MultiverseCore theCore = (MultiverseCore) this.getServer().getPluginManager().getPlugin("Multiverse-Core");
if (theCore == null) {
this.getLogger().severe("Core not found! The plugin dev needs to add a dependency!");
this.getLogger().severe("Disabling!");
this.getServer().getPluginManager().disablePlugin(this);
return;
}
if (theCore.getProtocolVersion() < this.getProtocolVersion()) {
this.getLogger().severe("You need a newer version of Multiverse-Core!");
this.getLogger().severe("Disabling!");
this.getServer().getPluginManager().disablePlugin(this);
return;
}
this.setCore(theCore);
this.getServer().getLogger().info(String.format("%s - Version %s enabled - By %s",
this.getDescription().getName(), this.getDescription().getVersion(), getAuthors()));
getDataFolder().mkdirs();
File debugLogFile = new File(getDataFolder(), "debug.log");
try {
debugLogFile.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
this.onPluginEnable();
}
/**
* Parse the Authors Array into a readable String with ',' and 'and'.
*
* @return The readable authors-{@link String}
*/
protected String getAuthors() {
String authors = "";
List<String> auths = this.getDescription().getAuthors();
if (auths.size() == 0) {
return "";
}
if (auths.size() == 1) {
return auths.get(0);
}
for (int i = 0; i < auths.size(); i++) {
if (i == this.getDescription().getAuthors().size() - 1) {
authors += " and " + this.getDescription().getAuthors().get(i);
} else {
authors += ", " + this.getDescription().getAuthors().get(i);
}
}
return authors.substring(2);
}
/**
* Called when the plugin is enabled.
* @see #onEnable()
*/
protected abstract void onPluginEnable();
@Override
public final MultiverseCore getCore() {
if (this.core == null)
throw new IllegalStateException("Core is null!");
return this.core;
}
@Override
public final void setCore(MultiverseCore core) {
this.core = core;
}
}

View File

@ -4,10 +4,12 @@ import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.entity.Minecart;
import org.bukkit.entity.Vehicle;
import org.jvnet.hk2.annotations.Contract;
/**
* Used to get block/location-related information.
*/
@Contract
public interface BlockSafety {
/**
* This function checks whether the block at the given coordinates are above air or not.

View File

@ -5,7 +5,9 @@ import java.util.Collection;
import co.aikar.commands.BukkitCommandIssuer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jvnet.hk2.annotations.Contract;
@Contract
public interface Destination<T extends DestinationInstance> {
/**
* Returns the identifier or prefix that is required for this destination.

View File

@ -3,10 +3,12 @@ package com.onarandombox.MultiverseCore.api;
import org.bukkit.Location;
import org.bukkit.entity.Vehicle;
import org.bukkit.util.Vector;
import org.jvnet.hk2.annotations.Contract;
/**
* Used to manipulate locations.
*/
@Contract
public interface LocationManipulation {
/**
* Convert a Location into a Colon separated string to allow us to store it in text.

View File

@ -1,78 +1,93 @@
package com.onarandombox.MultiverseCore.api;
import org.bukkit.configuration.serialization.ConfigurationSerializable;
import com.onarandombox.MultiverseCore.configuration.node.NodeGroup;
import com.onarandombox.MultiverseCore.placeholders.MultiverseCorePlaceholders;
import io.vavr.control.Try;
import org.jvnet.hk2.annotations.Contract;
/**
* The configuration of MultiverseCore.
*/
public interface MVConfig extends ConfigurationSerializable {
/**
* Sets a property using a {@link String}.
* @param property The name of the property.
* @param value The value.
* @return True on success, false if the operation failed.
*/
boolean setConfigProperty(String property, String value);
@Contract
public interface MVConfig {
/**
* Sets firstSpawnWorld.
* @param firstSpawnWorld The new value.
* Loads the config from disk.
* @return True if the config was loaded successfully.
*/
void setFirstSpawnWorld(String firstSpawnWorld);
boolean load();
/**
* Gets firstSpawnWorld.
* @return firstSpawnWorld.
* Whether the config has been loaded.
* @return True if the config has been loaded.
*/
String getFirstSpawnWorld();
boolean isLoaded();
/**
* Sets version.
* @param version The new value.
* Saves the config to disk.
*/
void setVersion(int version);
boolean save();
/**
* Gets version.
* @return version.
* Gets the nodes for the config.
*
* @return The nodes for the config.
*/
double getVersion();
NodeGroup getNodes();
/**
* Sets globalDebug.
* @param globalDebug The new value.
* Gets a property from the config.
*
* @param name The name of the property.
* @return A {@link Try} with the value of the property, otherwise a {@link Try.Failure} if there is no property by
* that name.
*/
void setGlobalDebug(int globalDebug);
Try<Object> getProperty(String name);
/**
* Gets globalDebug.
* @return globalDebug.
* Sets a property in the config.
*
* @param name The name of the property.
* @param value The value of the property.
* @return An empty {@link Try} if the property was set successfully, otherwise a {@link Try.Failure} with the
* exception explaining why the property could not be set.
*/
int getGlobalDebug();
Try<Void> setProperty(String name, Object value);
/**
* Sets displayPermErrors.
* @param displayPermErrors The new value.
* Sets world access permissions should be enforced.
* @param enforceAccess The new value.
*/
void setDisplayPermErrors(boolean displayPermErrors);
void setEnforceAccess(boolean enforceAccess);
/**
* Gets displayPermErrors.
* @return displayPermErrors.
* Gets enforceAccess.
* @return enforceAccess.
*/
boolean getDisplayPermErrors();
boolean getEnforceAccess();
/**
* Sets firstSpawnOverride.
* @param firstSpawnOverride The new value.
* Sets whether the game mode should be enforced.
* @param enforceGameMode The new value.
*/
void setFirstSpawnOverride(boolean firstSpawnOverride);
void setEnforceGameMode(boolean enforceGameMode);
/**
* Gets firstSpawnOverride.
* @return firstSpawnOverride.
* Gets enforceGameMode value.
* @return True if game mode should be enforced.
*/
boolean getFirstSpawnOverride();
boolean getEnforceGameMode();
/**
* Sets whether or not the automatic purge of entities is enabled.
*
* @param autopurge True if automatic purge should be enabled.
*/
void setAutoPurgeEntities(boolean autopurge);
/**
* Gets whether or not the automatic purge of entities is enabled.
*
* @return True if automatic purge is enabled.
*/
boolean isAutoPurgeEntities();
/**
* Sets teleportIntercept.
@ -86,18 +101,70 @@ public interface MVConfig extends ConfigurationSerializable {
*/
boolean getTeleportIntercept();
/**
* Sets firstSpawnOverride.
* @param firstSpawnOverride The new value.
*/
void setFirstSpawnOverride(boolean firstSpawnOverride);
/**
* Gets firstSpawnOverride.
* @return firstSpawnOverride.
*/
boolean getFirstSpawnOverride();
/**
* Sets firstSpawnWorld.
* @param firstSpawnWorld The new value.
*/
void setFirstSpawnLocation(String firstSpawnWorld);
/**
* Gets firstSpawnWorld.
* @return firstSpawnWorld.
*/
String getFirstSpawnLocation();
/**
* Sets whether or not to let Bukkit determine portal search radius on its own or if Multiverse should give input.
*
* @param useDefaultPortalSearch True to let Bukkit determine portal search radius on its own.
*/
void setUseCustomPortalSearch(boolean useDefaultPortalSearch);
/**
* Gets whether or not Bukkit will be determining portal search radius on its own or if Multiverse should help.
*
* @return True means Bukkit will use its own default values.
*/
boolean isUsingCustomPortalSearch();
/**
* Sets the radius at which vanilla style portals will be searched for to connect to worlds together.
*
* @param searchRadius The portal search radius.
*/
void setCustomPortalSearchRadius(int searchRadius);
/**
* Gets the radius at which vanilla style portals will be searched for to connect to worlds together.
*
* @return The portal search radius.
*/
int getCustomPortalSearchRadius();
/**
* Sets prefixChat.
* @param prefixChat The new value.
*/
void setPrefixChat(boolean prefixChat);
void setEnablePrefixChat(boolean prefixChat);
/**
* Gets prefixChat.
* @return prefixChat.
*/
boolean getPrefixChat();
boolean isEnablePrefixChat();
/**
* Sets prefixChatFormat.
* @param prefixChatFormat The new value.
@ -111,16 +178,28 @@ public interface MVConfig extends ConfigurationSerializable {
String getPrefixChatFormat();
/**
* Sets enforceAccess.
* @param enforceAccess The new value.
* Sets whether to register the {@link MultiverseCorePlaceholders} class with PlaceholderAPI plugin.
* @param registerPapiHook The new value.
*/
void setEnforceAccess(boolean enforceAccess);
void setRegisterPapiHook(boolean registerPapiHook);
/**
* Gets enforceAccess.
* @return enforceAccess.
* Gets whether to register the {@link MultiverseCorePlaceholders} class with PlaceholderAPI plugin.
* @return registerPapiHook.
*/
boolean getEnforceAccess();
boolean isRegisterPapiHook();
/**
* Sets globalDebug.
* @param globalDebug The new value.
*/
void setGlobalDebug(int globalDebug);
/**
* Gets globalDebug.
* @return globalDebug.
*/
int getGlobalDebug();
/**
* Sets whether to suppress startup messages.
@ -137,46 +216,11 @@ public interface MVConfig extends ConfigurationSerializable {
boolean getSilentStart();
/**
* Sets whether or not to let Bukkit determine portal search radius on its own or if Multiverse should give input.
* Sets whether or not the donation/patreon messages are shown.
*
* @param useDefaultPortalSearch True to let Bukkit determine portal search radius on its own.
* @param idonotwanttodonate True if donation/patreon messages should be shown.
*/
void setUseDefaultPortalSearch(boolean useDefaultPortalSearch);
/**
* Gets whether or not Bukkit will be determining portal search radius on its own or if Multiverse should help.
*
* @return True means Bukkit will use its own default values.
*/
boolean isUsingDefaultPortalSearch();
/**
* Sets the radius at which vanilla style portals will be searched for to connect to worlds together.
*
* @param searchRadius The portal search radius.
*/
void setPortalSearchRadius(int searchRadius);
/**
* Gets the radius at which vanilla style portals will be searched for to connect to worlds together.
*
* @return The portal search radius.
*/
int getPortalSearchRadius();
/**
* Gets whether or not the automatic purge of entities is enabled.
*
* @return True if automatic purge is enabled.
*/
boolean isAutoPurgeEnabled();
/**
* Sets whether or not the automatic purge of entities is enabled.
*
* @param autopurge True if automatic purge should be enabled.
*/
void setAutoPurgeEnabled(boolean autopurge);
void setShowDonateMessage(boolean idonotwanttodonate);
/**
* Gets whether or not the donation/patreon messages are shown.
@ -184,11 +228,4 @@ public interface MVConfig extends ConfigurationSerializable {
* @return True if donation/patreon messages should be shown.
*/
boolean isShowingDonateMessage();
/**
* Sets whether or not the donation/patreon messages are shown.
*
* @param idonotwanttodonate True if donation/patreon messages should be shown.
*/
void setShowDonateMessage(boolean idonotwanttodonate);
}

View File

@ -7,80 +7,12 @@
package com.onarandombox.MultiverseCore.api;
import com.onarandombox.MultiverseCore.anchor.AnchorManager;
import com.onarandombox.MultiverseCore.commandtools.MVCommandManager;
import com.onarandombox.MultiverseCore.destination.DestinationsProvider;
import com.onarandombox.MultiverseCore.economy.MVEconomist;
import com.onarandombox.MultiverseCore.teleportation.SimpleBlockSafety;
import com.onarandombox.MultiverseCore.teleportation.SimpleLocationManipulation;
import com.onarandombox.MultiverseCore.teleportation.SimpleSafeTTeleporter;
import com.onarandombox.MultiverseCore.utils.MVPermissions;
import com.onarandombox.MultiverseCore.utils.UnsafeCallWrapper;
/**
* Multiverse 2 Core API
* <p>
* This API contains a bunch of useful things you can get out of Multiverse in general!
*/
public interface MVCore extends MVPlugin {
/**
* Retrieves Multiverse's friendly economist. The economist can be used for dealing with economies without
* worrying about any of the messy details.
*
* @return the economy manager for Multiverse.
*/
MVEconomist getEconomist();
/**
* Reloads the Multiverse Configuration files:
* worlds.yml and config.yml.
*/
void loadConfigs();
/**
* Gets the {@link UnsafeCallWrapper} class.
*
* @return A non-null {@link UnsafeCallWrapper}.
*/
UnsafeCallWrapper getUnsafeCallWrapper();
/**
* Multiverse uses an advanced permissions setup, this object
* simplifies getting/setting permissions.
*
* @return A non-null {@link MVPermissions}.
*/
MVPermissions getMVPerms();
/**
* Multiverse uses {@link MVCommandManager} to make adding and using commands
* a piece of cake.
*
* @return A non-null {@link MVCommandManager}.
*/
MVCommandManager getMVCommandManager();
/**
* Gets the class responsible for loading many different destinations
* on demand.
*
* @return A valid {@link DestinationsProvider}.
*/
DestinationsProvider getDestinationsProvider();
/**
* Gets the primary class responsible for managing Multiverse Worlds.
*
* @return {@link MVWorldManager}.
*/
MVWorldManager getMVWorldManager();
/**
* Saves the Multiverse-Config.
*
* @return Whether the Multiverse-Config was successfully saved
*/
boolean saveMVConfig();
/**
* Saves all configs.
@ -89,13 +21,6 @@ public interface MVCore extends MVPlugin {
*/
boolean saveAllConfigs();
/**
* Gets the {@link AnchorManager}.
*
* @return The {@link AnchorManager}
*/
AnchorManager getAnchorManager();
/**
* Decrements the number of plugins that have specifically hooked into core.
*/
@ -112,65 +37,4 @@ public interface MVCore extends MVPlugin {
* @return The number if plugins that have hooked into core.
*/
int getPluginCount();
/**
* Parse the Authors Array into a readable String with ',' and 'and'.
*
* @return The readable authors-{@link String}
*/
String getAuthors();
/**
* Gets the {@link BlockSafety} this {@link MVCore} is using.
* @return The {@link BlockSafety} this {@link MVCore} is using.
* @see BlockSafety
* @see SimpleBlockSafety
*/
BlockSafety getBlockSafety();
/**
* Sets the {@link BlockSafety} this {@link MVCore} is using.
* @param blockSafety The new {@link BlockSafety}.
* @see BlockSafety
* @see SimpleBlockSafety
*/
void setBlockSafety(BlockSafety blockSafety);
/**
* Gets the {@link LocationManipulation} this {@link MVCore} is using.
* @return The {@link LocationManipulation} this {@link MVCore} is using.
* @see LocationManipulation
* @see SimpleLocationManipulation
*/
LocationManipulation getLocationManipulation();
/**
* Sets the {@link LocationManipulation} this {@link MVCore} is using.
* @param locationManipulation The new {@link LocationManipulation}.
* @see LocationManipulation
* @see SimpleLocationManipulation
*/
void setLocationManipulation(LocationManipulation locationManipulation);
/**
* Gets the {@link SafeTTeleporter} this {@link MVCore} is using.
* @return The {@link SafeTTeleporter} this {@link MVCore} is using.
* @see SafeTTeleporter
* @see SimpleSafeTTeleporter
*/
SafeTTeleporter getSafeTTeleporter();
/**
* Sets the {@link SafeTTeleporter} this {@link MVCore} is using.
* @param safeTTeleporter The new {@link SafeTTeleporter}.
* @see SafeTTeleporter
* @see SimpleSafeTTeleporter
*/
void setSafeTTeleporter(SafeTTeleporter safeTTeleporter);
/**
* Gets the {@link MVConfig}.
* @return The configuration.
*/
MVConfig getMVConfig();
}

View File

@ -7,8 +7,6 @@
package com.onarandombox.MultiverseCore.api;
import com.onarandombox.MultiverseCore.MultiverseCore;
/**
* This interface is implemented by every official Multiverse-plugin.
*/
@ -18,14 +16,7 @@ public interface MVPlugin {
*
* @return A valid {@link com.onarandombox.MultiverseCore}.
*/
MultiverseCore getCore();
/**
* Sets the reference to MultiverseCore.
*
* @param core A valid {@link com.onarandombox.MultiverseCore}.
*/
void setCore(MultiverseCore core);
MVCore getCore();
/**
* Allows Multiverse or a plugin to query another Multiverse plugin to see what version its protocol is. This
@ -35,4 +26,11 @@ public interface MVPlugin {
* @return The Integer protocol version.
*/
int getProtocolVersion();
/**
* Parse the Authors Array into a readable String with ',' and 'and'.
*
* @return The readable authors-{@link String}
*/
String getAuthors();
}

View File

@ -7,16 +7,15 @@
package com.onarandombox.MultiverseCore.api;
import java.io.File;
import java.util.Collection;
import java.util.List;
import com.onarandombox.MultiverseCore.world.SimpleWorldPurger;
import org.bukkit.World;
import org.bukkit.World.Environment;
import org.bukkit.WorldType;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.generator.ChunkGenerator;
import org.jvnet.hk2.annotations.Contract;
/**
* Multiverse 2 World Manager API
@ -24,6 +23,7 @@ import org.bukkit.generator.ChunkGenerator;
* This API contains all of the world managing
* functions that your heart desires!
*/
@Contract
public interface MVWorldManager {
/**
* Add a new World to the Multiverse Setup.
@ -221,15 +221,6 @@ public interface MVWorldManager {
*/
void loadDefaultWorlds();
/**
* Gets the {@link WorldPurger}.
* <p>
* @return The {@link WorldPurger} this {@link MVWorldManager} is using.
* @see WorldPurger
* @see SimpleWorldPurger
*/
WorldPurger getTheWorldPurger();
/**
* Gets the world players will spawn in on first join.
* Currently this always returns worlds.get(0) from Bukkit.
@ -253,10 +244,9 @@ public interface MVWorldManager {
/**
* Load the config from a file.
*
* @param file The file to load.
* @return A loaded configuration.
*/
FileConfiguration loadWorldConfig(File file);
FileConfiguration loadWorldsConfig();
/**
* Saves the world config to disk.

View File

@ -1,15 +1,19 @@
package com.onarandombox.MultiverseCore.api;
import java.util.concurrent.CompletableFuture;
import co.aikar.commands.BukkitCommandIssuer;
import com.onarandombox.MultiverseCore.destination.ParsedDestination;
import com.onarandombox.MultiverseCore.teleportation.TeleportResult;
import org.bukkit.Location;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Entity;
import org.jvnet.hk2.annotations.Contract;
/**
* Used to safely teleport people.
*/
@Contract
public interface SafeTTeleporter extends Teleporter {
/**
@ -37,8 +41,20 @@ public interface SafeTTeleporter extends Teleporter {
* @param destination Destination to teleport them to
* @return true for success, false for failure
*/
@Deprecated
TeleportResult safelyTeleport(BukkitCommandIssuer teleporter, Entity teleportee, ParsedDestination<?> destination);
/**
* Safely teleport the entity to the MVDestination. This will perform checks to see if the place is safe, and if
* it's not, will adjust the final destination accordingly.
*
* @param teleporter Person who performed the teleport command.
* @param teleportee Entity to teleport
* @param destination Destination to teleport them to
* @return true for success, false for failure
*/
CompletableFuture<TeleportResult> safelyTeleportAsync(BukkitCommandIssuer teleporter, Entity teleportee, ParsedDestination<?> destination);
/**
* Safely teleport the entity to the Location. This may perform checks to
* see if the place is safe, and if
@ -68,5 +84,4 @@ public interface SafeTTeleporter extends Teleporter {
* @return The next portal-block's {@link Location}.
*/
Location findPortalBlockNextTo(Location l);
}

View File

@ -1,10 +1,14 @@
package com.onarandombox.MultiverseCore.api;
import java.util.concurrent.CompletableFuture;
import co.aikar.commands.BukkitCommandIssuer;
import com.onarandombox.MultiverseCore.destination.ParsedDestination;
import com.onarandombox.MultiverseCore.teleportation.TeleportResult;
import org.bukkit.entity.Entity;
import org.jvnet.hk2.annotations.Contract;
@Contract
public interface Teleporter {
/**
* Teleport the entity to the Multiverse Destination.
@ -14,5 +18,16 @@ public interface Teleporter {
* @param destination Destination to teleport them to
* @return true for success, false for failure
*/
@Deprecated
TeleportResult teleport(BukkitCommandIssuer teleporter, Entity teleportee, ParsedDestination<?> destination);
/**
* Teleport the entity to the Multiverse Destination.
*
* @param teleporter Person who performed the teleport command.
* @param teleportee Entity to teleport
* @param destination Destination to teleport them to
* @return true for success, false for failure
*/
CompletableFuture<TeleportResult> teleportAsync(BukkitCommandIssuer teleporter, Entity teleportee, ParsedDestination<?> destination);
}

View File

@ -4,10 +4,12 @@ import java.util.List;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Entity;
import org.jvnet.hk2.annotations.Contract;
/**
* Used to remove animals from worlds that don't belong there.
*/
@Contract
public interface WorldPurger {
/**
* Synchronizes the given worlds with their settings.

View File

@ -7,34 +7,47 @@ 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 com.onarandombox.MultiverseCore.MultiverseCore;
import com.onarandombox.MultiverseCore.commandtools.MVCommandManager;
import com.onarandombox.MultiverseCore.commandtools.MultiverseCommand;
import com.onarandombox.MultiverseCore.destination.DestinationsProvider;
import com.onarandombox.MultiverseCore.destination.ParsedDestination;
import com.onarandombox.MultiverseCore.utils.MVCorei18n;
import jakarta.inject.Inject;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Service;
@Service
@CommandAlias("mv")
public class CheckCommand extends MultiverseCoreCommand {
public CheckCommand(@NotNull MultiverseCore plugin) {
super(plugin);
public class CheckCommand extends MultiverseCommand {
private final DestinationsProvider destinationsProvider;
@Inject
public CheckCommand(@NotNull MVCommandManager commandManager, @NotNull DestinationsProvider destinationsProvider) {
super(commandManager);
this.destinationsProvider = destinationsProvider;
}
@Subcommand("check")
@CommandPermission("multiverse.core.check")
@CommandCompletion("@players @destinations|@mvworlds")
@Syntax("<player> <destination>")
@Description("Checks if a player can teleport to a destination.")
@Description("{@@mv-core.check.description}")
public void onCheckCommand(BukkitCommandIssuer issuer,
@Syntax("<player>")
@Description("Player to check destination on.")
@Description("{@@mv-core.check.player.description}")
Player player,
@Syntax("<destination>")
@Description("A destination location, e.g. a world name.")
@Description("{@@mv-core.check.destination.description}")
ParsedDestination<?> destination
) {
issuer.sendMessage("Checking " + player + " to " + destination + "...");
issuer.sendInfo(MVCorei18n.CHECK_CHECKING,
"{player}", player.getName(),
"{destination}", destination.toString());
//TODO More detailed output on permissions required.
this.plugin.getDestinationsProvider().checkTeleportPermissions(issuer, player, destination);
this.destinationsProvider.checkTeleportPermissions(issuer, player, destination);
}
}

View File

@ -9,40 +9,53 @@ import co.aikar.commands.annotation.Description;
import co.aikar.commands.annotation.Single;
import co.aikar.commands.annotation.Subcommand;
import co.aikar.commands.annotation.Syntax;
import com.onarandombox.MultiverseCore.MultiverseCore;
import org.bukkit.ChatColor;
import com.onarandombox.MultiverseCore.api.MVWorldManager;
import com.onarandombox.MultiverseCore.commandtools.MVCommandManager;
import com.onarandombox.MultiverseCore.commandtools.MultiverseCommand;
import com.onarandombox.MultiverseCore.utils.MVCorei18n;
import jakarta.inject.Inject;
import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Service;
@Service
@CommandAlias("mv")
public class CloneCommand extends MultiverseCoreCommand {
public CloneCommand(@NotNull MultiverseCore plugin) {
super(plugin);
public class CloneCommand extends MultiverseCommand {
private final MVWorldManager worldManager;
@Inject
public CloneCommand(@NotNull MVCommandManager commandManager, @NotNull MVWorldManager worldManager) {
super(commandManager);
this.worldManager = worldManager;
}
@Subcommand("clone")
@CommandPermission("multiverse.core.clone")
@CommandCompletion("@mvworlds:scope=both @empty")
@Syntax("<world> <new world name>")
@Description("Clones a world.")
@Description("{@@mv-core.clone.description}")
public void onCloneCommand(CommandIssuer issuer,
@Conditions("validWorldName:scope=both")
@Conditions("worldname:scope=both")
@Syntax("<world>")
@Description("The target world to clone.")
@Description("{@@mv-core.clone.world.description}")
String worldName,
@Single
@Conditions("validWorldName:scope=new")
@Conditions("worldname:scope=new")
@Syntax("<new world name>")
@Description("The new cloned world name.")
@Description("{@@mv-core.clone.newWorld.description}")
String newWorldName
) {
issuer.sendMessage(String.format("Cloning world '%s' to '%s'...", worldName, newWorldName));
issuer.sendInfo(MVCorei18n.CLONE_CLONING,
"{world}", worldName,
"{newWorld}", newWorldName);
if (!this.plugin.getMVWorldManager().cloneWorld(worldName, newWorldName)) {
issuer.sendMessage(String.format("%sWorld could not be cloned! See console for more details.", ChatColor.RED));
if (!this.worldManager.cloneWorld(worldName, newWorldName)) {
issuer.sendError(MVCorei18n.CLONE_FAILED);
return;
}
issuer.sendMessage(String.format("%sCloned world '%s'!", ChatColor.GREEN, newWorldName));
issuer.sendInfo(MVCorei18n.CLONE_SUCCESS,
"{world}", newWorldName);
}
}

View File

@ -0,0 +1,73 @@
package com.onarandombox.MultiverseCore.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.Optional;
import co.aikar.commands.annotation.Single;
import co.aikar.commands.annotation.Subcommand;
import co.aikar.commands.annotation.Syntax;
import com.onarandombox.MultiverseCore.commandtools.MVCommandIssuer;
import com.onarandombox.MultiverseCore.commandtools.MVCommandManager;
import com.onarandombox.MultiverseCore.commandtools.MultiverseCommand;
import com.onarandombox.MultiverseCore.commandtools.context.MVConfigValue;
import com.onarandombox.MultiverseCore.config.MVCoreConfig;
import com.onarandombox.MultiverseCore.exceptions.MultiverseException;
import io.vavr.control.Option;
import jakarta.inject.Inject;
import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Service;
@Service
@CommandAlias("mv")
public class ConfigCommand extends MultiverseCommand {
private final MVCoreConfig config;
@Inject
public ConfigCommand(@NotNull MVCommandManager commandManager, @NotNull MVCoreConfig config) {
super(commandManager);
this.config = config;
}
@Subcommand("config")
@CommandPermission("multiverse.core.config")
@CommandCompletion("@mvconfigs")
@Syntax("<name> [new-value]")
@Description("") //TODO
public void onConfigCommand(MVCommandIssuer issuer,
@Syntax("<name>")
@Description("") //TODO
String name,
@Optional
@Single
@Syntax("[new-value]")
@Description("") //TODO
MVConfigValue value
) {
if (value == null) {
showConfigValue(issuer, name);
return;
}
updateConfigValue(issuer, name, value.getValue());
}
private void showConfigValue(MVCommandIssuer issuer, String name) {
config.getProperty(name)
.onSuccess(value -> issuer.sendMessage(name + "is currently set to " + value))
.onFailure(e -> issuer.sendMessage(e.getMessage()));
}
private void updateConfigValue(MVCommandIssuer issuer, String name, Object value) {
config.setProperty(name, value)
.onSuccess(ignore -> {
config.save();
issuer.sendMessage("Successfully set " + name + " to " + value);
})
.onFailure(ignore -> issuer.sendMessage("Unable to set " + name + " to " + value + "."))
.onFailure(MultiverseException.class, e -> Option.of(e.getMVMessage()).peek(issuer::sendError));
}
}

View File

@ -5,19 +5,25 @@ import co.aikar.commands.annotation.CommandAlias;
import co.aikar.commands.annotation.CommandPermission;
import co.aikar.commands.annotation.Description;
import co.aikar.commands.annotation.Subcommand;
import com.onarandombox.MultiverseCore.MultiverseCore;
import com.onarandombox.MultiverseCore.commandtools.MVCommandManager;
import com.onarandombox.MultiverseCore.commandtools.MultiverseCommand;
import jakarta.inject.Inject;
import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Service;
@Service
@CommandAlias("mv")
public class ConfirmCommand extends MultiverseCoreCommand {
public ConfirmCommand(@NotNull MultiverseCore plugin) {
super(plugin);
public class ConfirmCommand extends MultiverseCommand {
@Inject
public ConfirmCommand(@NotNull MVCommandManager commandManager) {
super(commandManager);
}
@Subcommand("confirm")
@CommandPermission("multiverse.core.confirm")
@Description("Confirms dangerous commands before executing them.")
@Description("{@@mv-core.confirm.description}")
public void onConfirmCommand(@NotNull BukkitCommandIssuer issuer) {
this.plugin.getMVCommandManager().getCommandQueueManager().runQueuedCommand(issuer.getIssuer());
this.commandManager.getCommandQueueManager().runQueuedCommand(issuer.getIssuer());
}
}

View File

@ -17,22 +17,38 @@ 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 com.onarandombox.MultiverseCore.MultiverseCore;
import com.onarandombox.MultiverseCore.api.MVWorldManager;
import com.onarandombox.MultiverseCore.commandtools.MVCommandManager;
import com.onarandombox.MultiverseCore.commandtools.MultiverseCommand;
import com.onarandombox.MultiverseCore.commandtools.flags.CommandFlag;
import com.onarandombox.MultiverseCore.commandtools.flags.CommandFlagGroup;
import com.onarandombox.MultiverseCore.commandtools.flags.CommandValueFlag;
import com.onarandombox.MultiverseCore.commandtools.flags.ParsedCommandFlags;
import com.onarandombox.MultiverseCore.locale.MVCorei18n;
import com.onarandombox.MultiverseCore.utils.MVCorei18n;
import com.onarandombox.MultiverseCore.utils.UnsafeCallWrapper;
import jakarta.inject.Inject;
import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.WorldType;
import org.bukkit.plugin.Plugin;
import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Service;
@Service
@CommandAlias("mv")
public class CreateCommand extends MultiverseCoreCommand {
public CreateCommand(@NotNull MultiverseCore plugin) {
super(plugin);
public class CreateCommand extends MultiverseCommand {
private final MVWorldManager worldManager;
@Inject
public CreateCommand(
@NotNull MVCommandManager commandManager,
@NotNull MVWorldManager worldManager,
@NotNull UnsafeCallWrapper unsafeCallWrapper
) {
super(commandManager);
this.worldManager = worldManager;
registerFlagGroup(CommandFlagGroup.builder("mvcreate")
.add(CommandValueFlag.builder("--seed", String.class)
@ -43,7 +59,7 @@ public class CreateCommand extends MultiverseCoreCommand {
.addAlias("-g")
.completion(() -> Arrays.stream(Bukkit.getServer().getPluginManager().getPlugins())
.filter(Plugin::isEnabled)
.filter(genplugin -> this.plugin.getUnsafeCallWrapper().wrap(
.filter(genplugin -> unsafeCallWrapper.wrap(
() -> genplugin.getDefaultWorldGenerator("world", ""),
genplugin.getName(),
"Get generator"
@ -51,22 +67,8 @@ public class CreateCommand extends MultiverseCoreCommand {
.map(genplugin -> genplugin.getDescription().getName())
.collect(Collectors.toList()))
.build())
.add(CommandValueFlag.builder("--world-type", WorldType.class)
.add(CommandValueFlag.enumBuilder("--world-type", WorldType.class)
.addAlias("-t")
.context((value) -> {
try {
return WorldType.valueOf(value.toUpperCase());
} catch (IllegalArgumentException e) {
throw new InvalidCommandArgument("Invalid world type: " + value);
}
})
.completion(() -> {
List<String> types = new ArrayList<>();
for (WorldType type : WorldType.values()) {
types.add(type.name().toLowerCase());
}
return types;
})
.build())
.add(CommandFlag.builder("--adjust-spawn")
.addAlias("-n")
@ -84,7 +86,7 @@ public class CreateCommand extends MultiverseCoreCommand {
@Description("{@@mv-core.create.description}")
public void onCreateCommand(BukkitCommandIssuer issuer,
@Conditions("validWorldName:scope=new")
@Conditions("worldname:scope=new")
@Syntax("<name>")
@Description("{@@mv-core.create.name.description}")
String worldName,

View File

@ -8,14 +8,24 @@ import co.aikar.commands.annotation.Description;
import co.aikar.commands.annotation.Subcommand;
import co.aikar.commands.annotation.Syntax;
import com.dumptruckman.minecraft.util.Logging;
import com.onarandombox.MultiverseCore.MultiverseCore;
import com.onarandombox.MultiverseCore.locale.MVCorei18n;
import com.onarandombox.MultiverseCore.commandtools.MVCommandManager;
import com.onarandombox.MultiverseCore.commandtools.MultiverseCommand;
import com.onarandombox.MultiverseCore.config.MVCoreConfig;
import com.onarandombox.MultiverseCore.utils.MVCorei18n;
import jakarta.inject.Inject;
import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Service;
@Service
@CommandAlias("mv")
public class DebugCommand extends MultiverseCoreCommand {
public DebugCommand(@NotNull MultiverseCore plugin) {
super(plugin);
public class DebugCommand extends MultiverseCommand {
private final MVCoreConfig config;
@Inject
public DebugCommand(@NotNull MVCommandManager commandManager, @NotNull MVCoreConfig config) {
super(commandManager);
this.config = config;
}
@Subcommand("debug")
@ -36,13 +46,13 @@ public class DebugCommand extends MultiverseCoreCommand {
@Description("{@@mv-core.debug.change.level.description}")
int level) {
this.plugin.getMVConfig().setGlobalDebug(level);
this.plugin.saveAllConfigs();
config.setGlobalDebug(level);
config.save();
this.displayDebugMode(issuer);
}
private void displayDebugMode(BukkitCommandIssuer issuer) {
final int debugLevel = this.plugin.getMVConfig().getGlobalDebug();
final int debugLevel = config.getGlobalDebug();
if (debugLevel == 0) {
issuer.sendInfo(MVCorei18n.DEBUG_INFO_OFF);
return;

View File

@ -1,6 +1,7 @@
package com.onarandombox.MultiverseCore.commands;
import co.aikar.commands.BukkitCommandIssuer;
import co.aikar.commands.MessageType;
import co.aikar.commands.annotation.CommandAlias;
import co.aikar.commands.annotation.CommandCompletion;
import co.aikar.commands.annotation.CommandPermission;
@ -9,41 +10,58 @@ import co.aikar.commands.annotation.Description;
import co.aikar.commands.annotation.Single;
import co.aikar.commands.annotation.Subcommand;
import co.aikar.commands.annotation.Syntax;
import com.onarandombox.MultiverseCore.MultiverseCore;
import com.onarandombox.MultiverseCore.api.MVWorldManager;
import com.onarandombox.MultiverseCore.commandtools.MVCommandManager;
import com.onarandombox.MultiverseCore.commandtools.MultiverseCommand;
import com.onarandombox.MultiverseCore.commandtools.queue.QueuedCommand;
import org.bukkit.ChatColor;
import com.onarandombox.MultiverseCore.utils.MVCorei18n;
import jakarta.inject.Inject;
import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Service;
@Service
@CommandAlias("mv")
public class DeleteCommand extends MultiverseCoreCommand {
public DeleteCommand(@NotNull MultiverseCore plugin) {
super(plugin);
public class DeleteCommand extends MultiverseCommand {
private final MVWorldManager worldManager;
@Inject
public DeleteCommand(@NotNull MVCommandManager commandManager, @NotNull MVWorldManager worldManager) {
super(commandManager);
this.worldManager = worldManager;
}
@Subcommand("delete")
@CommandPermission("multiverse.core.delete")
@CommandCompletion("@mvworlds:scope=both")
@Syntax("<world>")
@Description("Deletes a world on your server PERMANENTLY.")
@Description("{@@mv-core.delete.description}")
public void onDeleteCommand(BukkitCommandIssuer issuer,
@Single
@Conditions("validWorldName:scope=both")
@Conditions("worldname:scope=both")
@Syntax("<world>")
@Description("The world you want to delete.")
String worldName
) {
this.plugin.getMVCommandManager().getCommandQueueManager().addToQueue(new QueuedCommand(
this.commandManager.getCommandQueueManager().addToQueue(new QueuedCommand(
issuer.getIssuer(),
() -> {
issuer.sendMessage(String.format("Deleting world '%s'...", worldName));
if (!this.plugin.getMVWorldManager().deleteWorld(worldName)) {
issuer.sendMessage(String.format("%sThere was an issue deleting '%s'! Please check console for errors.", ChatColor.RED, worldName));
issuer.sendInfo(MVCorei18n.DELETE_DELETING,
"{world}", worldName);
if (!this.worldManager.deleteWorld(worldName)) {
issuer.sendError(MVCorei18n.DELETE_FAILED,
"{world}", worldName);
return;
}
issuer.sendMessage(String.format("%sWorld %s was deleted!", ChatColor.GREEN, worldName));
issuer.sendInfo(MVCorei18n.DELETE_SUCCESS,
"{world}", worldName);
},
"Are you sure you want to delete world '" + worldName + "'?"
this.commandManager.formatMessage(
issuer,
MessageType.INFO,
MVCorei18n.DELETE_PROMPT,
"{world}", worldName)
));
}
}

View File

@ -8,37 +8,43 @@ import co.aikar.commands.annotation.Description;
import co.aikar.commands.annotation.Flags;
import co.aikar.commands.annotation.Subcommand;
import co.aikar.commands.annotation.Syntax;
import com.onarandombox.MultiverseCore.MultiverseCore;
import com.onarandombox.MultiverseCore.api.MVWorld;
import com.onarandombox.MultiverseCore.commandtools.MVCommandManager;
import com.onarandombox.MultiverseCore.commandtools.MultiverseCommand;
import com.onarandombox.MultiverseCore.commandtools.context.GameRuleValue;
import org.bukkit.ChatColor;
import com.onarandombox.MultiverseCore.utils.MVCorei18n;
import jakarta.inject.Inject;
import org.bukkit.GameRule;
import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Service;
@Service
@CommandAlias("mv")
public class GameruleCommand extends MultiverseCoreCommand {
public GameruleCommand(@NotNull MultiverseCore plugin) {
super(plugin);
public class GameruleCommand extends MultiverseCommand {
@Inject
public GameruleCommand(@NotNull MVCommandManager commandManager) {
super(commandManager);
}
@Subcommand("gamerule")
@CommandPermission("multiverse.core.gamerule")
@CommandCompletion("@gamerules true|false|@range:1-10 @mvworlds:multiple|*")
@Syntax("<Gamerule> <Gamerule value> [World or *]")
@Description("Changes a gamerule in one or more worlds")
@Description("{@@mv-core.gamerule.description}")
public void onGameruleCommand(BukkitCommandIssuer issuer,
@Syntax("<Gamerule>")
@Description("Gamerule to set")
@Description("{@@mv-core.gamerule.gamerule.description}")
GameRule gamerule,
@Syntax("<Value>")
@Description("Value of gamerule")
@Description("{@@mv-core.gamerule.value.description}")
GameRuleValue gameRuleValue,
@Flags("resolve=issuerAware")
@Syntax("[World or *]")
@Description("World to apply gamerule to, current world by default")
@Description("{@@mv-core.gamerule.world.description}")
MVWorld[] worlds
) {
Object value = gameRuleValue.getValue();
@ -46,17 +52,27 @@ public class GameruleCommand extends MultiverseCoreCommand {
for(MVWorld world : worlds) {
// Set gamerules and add false to list if it fails
if (!world.getCBWorld().setGameRule(gamerule, value)) {
issuer.sendMessage(ChatColor.RED + "Failed to set gamerule " + gamerule.getName() + " to " + value + " in " + world.getName() + ". It should be a " + gamerule.getType());
issuer.sendError(MVCorei18n.GAMERULE_FAILED,
"{gamerule}", gamerule.getName(),
"{value}", value.toString(),
"{world}", world.getName(),
"{type}", gamerule.getType().getName());
success = false;
}
}
// Tell user if it was successful
if (success) {
if (worlds.length == 1) {
issuer.sendMessage(ChatColor.GREEN + "Successfully set " + gamerule.getName() + " to " + value + " in " + worlds[0].getName());
issuer.sendInfo(MVCorei18n.GAMERULE_SUCCESS_SINGLE,
"{gamerule}", gamerule.getName(),
"{value}", value.toString(),
"{world}", worlds[0].getName());
}
else if (worlds.length > 1) {
issuer.sendMessage(ChatColor.GREEN + "Successfully set " + gamerule.getName() + " to " + value + " in " + worlds.length + " worlds.");
issuer.sendInfo(MVCorei18n.GAMERULE_SUCCESS_MULTIPLE,
"{gamerule}", gamerule.getName(),
"{value}", value.toString(),
"{count}", String.valueOf(worlds.length));
}
}
}

View File

@ -0,0 +1,104 @@
package com.onarandombox.MultiverseCore.commands;
import java.util.Arrays;
import java.util.stream.Collectors;
import co.aikar.commands.BukkitCommandIssuer;
import co.aikar.commands.annotation.CommandAlias;
import co.aikar.commands.annotation.CommandCompletion;
import co.aikar.commands.annotation.CommandPermission;
import co.aikar.commands.annotation.Conditions;
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 com.onarandombox.MultiverseCore.api.MVWorldManager;
import com.onarandombox.MultiverseCore.commandtools.MVCommandManager;
import com.onarandombox.MultiverseCore.commandtools.MultiverseCommand;
import com.onarandombox.MultiverseCore.commandtools.flags.CommandFlag;
import com.onarandombox.MultiverseCore.commandtools.flags.CommandFlagGroup;
import com.onarandombox.MultiverseCore.commandtools.flags.CommandValueFlag;
import com.onarandombox.MultiverseCore.commandtools.flags.ParsedCommandFlags;
import com.onarandombox.MultiverseCore.utils.MVCorei18n;
import com.onarandombox.MultiverseCore.utils.UnsafeCallWrapper;
import jakarta.inject.Inject;
import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.plugin.Plugin;
import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Service;
@Service
@CommandAlias("mv")
public class ImportCommand extends MultiverseCommand {
private final MVWorldManager worldManager;
@Inject
public ImportCommand(
@NotNull MVCommandManager commandManager,
@NotNull MVWorldManager worldManager,
@NotNull UnsafeCallWrapper unsafeCallWrapper
) {
super(commandManager);
this.worldManager = worldManager;
registerFlagGroup(CommandFlagGroup.builder("mvimport")
.add(CommandValueFlag.builder("--generator", String.class)
.addAlias("-g")
.completion(() -> Arrays.stream(Bukkit.getServer().getPluginManager().getPlugins())
.filter(Plugin::isEnabled)
.filter(genplugin -> unsafeCallWrapper.wrap(
() -> genplugin.getDefaultWorldGenerator("world", ""),
genplugin.getName(),
"Get generator"
) != null)
.map(genplugin -> genplugin.getDescription().getName())
.collect(Collectors.toList()))
.build())
.add(CommandFlag.builder("--adjust-spawn")
.addAlias("-a")
.build())
.build());
}
@Subcommand("import")
@CommandPermission("multiverse.core.import")
@CommandCompletion("@mvworlds:scope=potential @flags:groupName=mvimport")
@Syntax("<name> <env> --generator [generator[:id]] --adjust-spawn")
@Description("{@@mv-core.import.description")
public void onImportCommand(BukkitCommandIssuer issuer,
@Conditions("worldname:scope=new")
@Syntax("<name>")
@Description("{@@mv-core.import.name.description}")
String worldName,
@Syntax("<env>")
@Description("{@@mv-core.import.env.description}")
World.Environment environment,
@Optional
@Syntax("--generator [generator[:id]] --adjust-spawn")
@Description("{@@mv-core.import.other.description}")
String[] flags) {
ParsedCommandFlags parsedFlags = parseFlags(flags);
issuer.sendInfo(MVCorei18n.IMPORT_IMPORTING,
"{world}", worldName);
if (!this.worldManager.addWorld(
worldName, environment,
null,
null,
null,
parsedFlags.flagValue("--generator", String.class),
parsedFlags.hasFlag("--adjust-spawn"))
) {
issuer.sendError(MVCorei18n.IMPORT_FAILED);
return;
}
issuer.sendInfo(MVCorei18n.IMPORT_SUCCESS);
}
}

View File

@ -9,34 +9,48 @@ import co.aikar.commands.annotation.Description;
import co.aikar.commands.annotation.Single;
import co.aikar.commands.annotation.Subcommand;
import co.aikar.commands.annotation.Syntax;
import com.onarandombox.MultiverseCore.MultiverseCore;
import com.onarandombox.MultiverseCore.api.MVWorldManager;
import com.onarandombox.MultiverseCore.commandtools.MVCommandManager;
import com.onarandombox.MultiverseCore.commandtools.MultiverseCommand;
import com.onarandombox.MultiverseCore.utils.MVCorei18n;
import jakarta.inject.Inject;
import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Service;
@Service
@CommandAlias("mv")
public class LoadCommand extends MultiverseCoreCommand {
public LoadCommand(@NotNull MultiverseCore plugin) {
super(plugin);
public class LoadCommand extends MultiverseCommand {
private final MVWorldManager worldManager;
@Inject
public LoadCommand(@NotNull MVCommandManager commandManager, @NotNull MVWorldManager worldManager) {
super(commandManager);
this.worldManager = worldManager;
}
@Subcommand("load")
@CommandPermission("multiverse.core.load")
@CommandCompletion("@mvworlds:scope=unloaded")
@Syntax("<world>")
@Description("Loads a world. World must be already in worlds.yml, else please use /mv import.")
@Description("{@@mv-core.load.description}")
public void onLoadCommand(BukkitCommandIssuer issuer,
@Single
@Conditions("validWorldName:scope=unloaded")
@Conditions("worldname:scope=unloaded")
@Syntax("<world>")
@Description("Name of world you want to load.")
@Description("{@@mv-core.load.world.description}")
String worldName
) {
issuer.sendMessage(String.format("Loading world '%s'...", worldName));
issuer.sendInfo(MVCorei18n.LOAD_LOADING,
"{world}", worldName);
if (!this.plugin.getMVWorldManager().loadWorld(worldName)) {
issuer.sendMessage(String.format("Error trying to load world '%s'!", worldName));
if (!this.worldManager.loadWorld(worldName)) {
issuer.sendError(MVCorei18n.LOAD_FAILED,
"{world}", worldName);
return;
}
issuer.sendMessage(String.format("Loaded world '%s'!", worldName));
issuer.sendInfo(MVCorei18n.LOAD_SUCCESS,
"{world}", worldName);
}
}

View File

@ -1,20 +0,0 @@
package com.onarandombox.MultiverseCore.commands;
import com.onarandombox.MultiverseCore.MultiverseCore;
import com.onarandombox.MultiverseCore.api.MVWorldManager;
import com.onarandombox.MultiverseCore.commandtools.MultiverseCommand;
import org.jetbrains.annotations.NotNull;
/**
* A base command for Multiverse.
*/
abstract class MultiverseCoreCommand extends MultiverseCommand {
protected final MultiverseCore plugin;
protected final MVWorldManager worldManager;
protected MultiverseCoreCommand(@NotNull MultiverseCore plugin) {
super(plugin);
this.plugin = plugin;
this.worldManager = plugin.getMVWorldManager();
}
}

View File

@ -4,6 +4,7 @@ import java.util.Collections;
import java.util.Random;
import co.aikar.commands.BukkitCommandIssuer;
import co.aikar.commands.MessageType;
import co.aikar.commands.annotation.CommandAlias;
import co.aikar.commands.annotation.CommandCompletion;
import co.aikar.commands.annotation.CommandPermission;
@ -12,19 +13,29 @@ 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 com.onarandombox.MultiverseCore.MultiverseCore;
import com.onarandombox.MultiverseCore.api.MVWorldManager;
import com.onarandombox.MultiverseCore.commandtools.MVCommandManager;
import com.onarandombox.MultiverseCore.commandtools.MultiverseCommand;
import com.onarandombox.MultiverseCore.commandtools.flags.CommandFlag;
import com.onarandombox.MultiverseCore.commandtools.flags.CommandFlagGroup;
import com.onarandombox.MultiverseCore.commandtools.flags.CommandValueFlag;
import com.onarandombox.MultiverseCore.commandtools.flags.ParsedCommandFlags;
import com.onarandombox.MultiverseCore.commandtools.queue.QueuedCommand;
import org.bukkit.ChatColor;
import com.onarandombox.MultiverseCore.utils.MVCorei18n;
import jakarta.inject.Inject;
import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Service;
@Service
@CommandAlias("mv")
public class RegenCommand extends MultiverseCoreCommand {
public RegenCommand(@NotNull MultiverseCore plugin) {
super(plugin);
public class RegenCommand extends MultiverseCommand {
private final MVWorldManager worldManager;
@Inject
public RegenCommand(@NotNull MVCommandManager commandManager, @NotNull MVWorldManager worldManager) {
super(commandManager);
this.worldManager = worldManager;
registerFlagGroup(CommandFlagGroup.builder("mvregen")
.add(CommandValueFlag.builder("--seed", String.class)
@ -42,38 +53,45 @@ public class RegenCommand extends MultiverseCoreCommand {
@CommandPermission("multiverse.core.regen")
@CommandCompletion("@mvworlds:scope=both @flags:groupName=mvregen")
@Syntax("<world> --seed [seed] --keep-gamerules")
@Description("Regenerates a world on your server. The previous state will be lost PERMANENTLY.")
@Description("{@@mv-core.regen.description}")
public void onRegenCommand(BukkitCommandIssuer issuer,
@Conditions("validWorldName:scope=both")
@Conditions("worldname:scope=both")
@Syntax("<world>")
@Description("World that you want to regen.")
@Description("{@@mv-core.regen.world.description}")
String worldName,
@Optional
@Syntax("--seed [seed] --keep-gamerules")
@Description("Other world settings. See: http://gg.gg/nn8lk")
@Description("{@@mv-core.regen.other.description}")
String[] flags
) {
ParsedCommandFlags parsedFlags = parseFlags(flags);
this.plugin.getMVCommandManager().getCommandQueueManager().addToQueue(new QueuedCommand(
this.commandManager.getCommandQueueManager().addToQueue(new QueuedCommand(
issuer.getIssuer(),
() -> {
issuer.sendMessage(String.format("Regenerating world '%s'...", worldName));
if (!this.plugin.getMVWorldManager().regenWorld(
issuer.sendInfo(MVCorei18n.REGEN_REGENERATING,
"{world}", worldName);
if (!this.worldManager.regenWorld(
worldName,
parsedFlags.hasFlag("--seed"),
!parsedFlags.hasFlagValue("--seed"),
parsedFlags.flagValue("--seed", String.class),
parsedFlags.hasFlag("--keep-gamerules")
)) {
issuer.sendMessage(String.format("%sThere was an issue regenerating '%s'! Please check console for errors.", ChatColor.RED, worldName));
issuer.sendError(MVCorei18n.REGEN_FAILED,
"{world}", worldName);
return;
}
issuer.sendMessage(String.format("%sWorld %s was regenerated!", ChatColor.GREEN, worldName));
issuer.sendInfo(MVCorei18n.REGEN_SUCCESS,
"{world}", worldName);
},
"Are you sure you want to regenerate world '" + worldName + "'?"
this.commandManager.formatMessage(
issuer,
MessageType.INFO,
MVCorei18n.REGEN_PROMPT,
"{world}", worldName)
));
}
}

View File

@ -9,24 +9,52 @@ import co.aikar.commands.annotation.CommandPermission;
import co.aikar.commands.annotation.Description;
import co.aikar.commands.annotation.Subcommand;
import com.onarandombox.MultiverseCore.MultiverseCore;
import com.onarandombox.MultiverseCore.anchor.AnchorManager;
import com.onarandombox.MultiverseCore.api.MVCore;
import com.onarandombox.MultiverseCore.api.MVWorldManager;
import com.onarandombox.MultiverseCore.commandtools.MVCommandManager;
import com.onarandombox.MultiverseCore.commandtools.MultiverseCommand;
import com.onarandombox.MultiverseCore.config.MVCoreConfig;
import com.onarandombox.MultiverseCore.event.MVConfigReloadEvent;
import org.bukkit.ChatColor;
import com.onarandombox.MultiverseCore.utils.MVCorei18n;
import jakarta.inject.Inject;
import org.bukkit.plugin.PluginManager;
import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Service;
@Service
@CommandAlias("mv")
public class ReloadCommand extends MultiverseCoreCommand {
public ReloadCommand(@NotNull MultiverseCore plugin) {
super(plugin);
public class ReloadCommand extends MultiverseCommand {
private final MVCoreConfig config;
private final AnchorManager anchorManager;
private final MVWorldManager worldManager;
private final PluginManager pluginManager;
@Inject
public ReloadCommand(
@NotNull MVCommandManager commandManager,
@NotNull MVCoreConfig config,
@NotNull AnchorManager anchorManager,
@NotNull MVWorldManager worldManager,
@NotNull PluginManager pluginManager
) {
super(commandManager);
this.config = config;
this.anchorManager = anchorManager;
this.worldManager = worldManager;
this.pluginManager = pluginManager;
}
@Subcommand("reload")
@CommandPermission("multiverse.core.reload")
@Description("Reloads config files for all multiverse modules.")
@Description("{@@mv-core.reload.description}")
public void onReloadCommand(@NotNull BukkitCommandIssuer issuer) {
issuer.sendMessage(ChatColor.GOLD + "Reloading all Multiverse Plugin configs...");
this.plugin.loadConfigs();
this.plugin.getAnchorManager().loadAnchors();
this.plugin.getMVWorldManager().loadWorlds(true);
issuer.sendInfo(MVCorei18n.RELOAD_RELOADING);
this.config.load();
this.worldManager.loadWorldsConfig();
this.worldManager.loadWorlds(true);
this.anchorManager.loadAnchors();
List<String> configsLoaded = new ArrayList<>();
configsLoaded.add("Multiverse-Core - config.yml");
@ -34,9 +62,10 @@ public class ReloadCommand extends MultiverseCoreCommand {
configsLoaded.add("Multiverse-Core - anchors.yml");
MVConfigReloadEvent configReload = new MVConfigReloadEvent(configsLoaded);
this.plugin.getServer().getPluginManager().callEvent(configReload);
this.pluginManager.callEvent(configReload);
// @TODO: replace this sendMessage and format the configsLoaded above, maybe?
configReload.getAllConfigsLoaded().forEach(issuer::sendMessage);
issuer.sendMessage(String.format("%sReload Complete!", ChatColor.GREEN));
issuer.sendInfo(MVCorei18n.RELOAD_SUCCESS);
}
}

View File

@ -0,0 +1,52 @@
package com.onarandombox.MultiverseCore.commands;
import co.aikar.commands.BukkitCommandIssuer;
import co.aikar.commands.annotation.CommandAlias;
import co.aikar.commands.annotation.CommandCompletion;
import co.aikar.commands.annotation.CommandPermission;
import co.aikar.commands.annotation.Conditions;
import co.aikar.commands.annotation.Description;
import co.aikar.commands.annotation.Single;
import co.aikar.commands.annotation.Subcommand;
import co.aikar.commands.annotation.Syntax;
import com.onarandombox.MultiverseCore.api.MVWorldManager;
import com.onarandombox.MultiverseCore.commandtools.MVCommandManager;
import com.onarandombox.MultiverseCore.commandtools.MultiverseCommand;
import com.onarandombox.MultiverseCore.utils.MVCorei18n;
import jakarta.inject.Inject;
import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Service;
@Service
@CommandAlias("mv")
public class RemoveCommand extends MultiverseCommand {
private final MVWorldManager worldManager;
@Inject
public RemoveCommand(@NotNull MVCommandManager commandManager, @NotNull MVWorldManager worldManager) {
super(commandManager);
this.worldManager = worldManager;
}
@Subcommand("remove")
@CommandPermission("multiverse.core.remove")
@CommandCompletion("@mvworlds:scope=both")
@Syntax("<world>")
@Description("{@@mv-core.remove.description}")
public void onRemoveCommand(BukkitCommandIssuer issuer,
@Single
@Conditions("mvworlds:scope=both")
@Syntax("<world>")
@Description("{@@mv-core.remove.world.description}")
String worldName
) {
if (!this.worldManager.removeWorldFromConfig(worldName)) {
issuer.sendError(MVCorei18n.REMOVE_FAILED);
return;
}
issuer.sendInfo(MVCorei18n.REMOVE_SUCCESS,
"{world}", worldName);
}
}

View File

@ -0,0 +1,34 @@
package com.onarandombox.MultiverseCore.commands;
import co.aikar.commands.CommandIssuer;
import co.aikar.commands.annotation.CommandAlias;
import com.onarandombox.MultiverseCore.MultiverseCore;
import com.onarandombox.MultiverseCore.commandtools.MVCommandManager;
import com.onarandombox.MultiverseCore.commandtools.MultiverseCommand;
import com.onarandombox.MultiverseCore.utils.MVCorei18n;
import jakarta.inject.Inject;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.PluginDescriptionFile;
import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Service;
@Service
public class RootCommand extends MultiverseCommand {
private final Plugin plugin;
@Inject
public RootCommand(@NotNull MVCommandManager commandManager, @NotNull MultiverseCore plugin) {
super(commandManager);
this.plugin = plugin;
}
@CommandAlias("mv")
public void onRootCommand(CommandIssuer issuer) {
PluginDescriptionFile description = this.plugin.getDescription();
issuer.sendInfo(MVCorei18n.ROOT_TITLE,
"{name}", description.getName(),
"{version}", description.getVersion());
issuer.sendInfo(MVCorei18n.ROOT_HELP);
}
}

View File

@ -1,5 +1,8 @@
package com.onarandombox.MultiverseCore.commands;
import java.util.Arrays;
import java.util.concurrent.CompletableFuture;
import co.aikar.commands.BukkitCommandIssuer;
import co.aikar.commands.CommandIssuer;
import co.aikar.commands.annotation.CommandAlias;
@ -8,42 +11,60 @@ import co.aikar.commands.annotation.Description;
import co.aikar.commands.annotation.Flags;
import co.aikar.commands.annotation.Subcommand;
import co.aikar.commands.annotation.Syntax;
import com.onarandombox.MultiverseCore.MultiverseCore;
import com.dumptruckman.minecraft.util.Logging;
import com.onarandombox.MultiverseCore.commandtools.MVCommandManager;
import com.onarandombox.MultiverseCore.commandtools.MultiverseCommand;
import com.onarandombox.MultiverseCore.destination.DestinationsProvider;
import com.onarandombox.MultiverseCore.destination.ParsedDestination;
import com.onarandombox.MultiverseCore.utils.MVCorei18n;
import jakarta.inject.Inject;
import org.bukkit.entity.Player;
import org.jvnet.hk2.annotations.Service;
@Service
@CommandAlias("mv")
public class TeleportCommand extends MultiverseCoreCommand {
public TeleportCommand(MultiverseCore plugin) {
super(plugin);
public class TeleportCommand extends MultiverseCommand {
private final DestinationsProvider destinationsProvider;
@Inject
public TeleportCommand(MVCommandManager commandManager, DestinationsProvider destinationsProvider) {
super(commandManager);
this.destinationsProvider = destinationsProvider;
}
@Subcommand("teleport|tp")
@CommandCompletion("@players|@mvworlds:playerOnly|@destinations:playerOnly @mvworlds|@destinations")
@Syntax("[player] <destination>")
@Description("Allows you to the teleport to a location on your server!")
@Description("{@@mv-core.teleport.description}")
public void onTeleportCommand(BukkitCommandIssuer issuer,
@Flags("resolve=issuerAware")
@Syntax("[player]")
@Description("Target player to teleport.")
@Description("{@@mv-core.teleport.player.description}")
Player[] players,
@Syntax("<destination>")
@Description("Location, can be a world name.")
@Description("{@@mv-core.teleport.destination.description}")
ParsedDestination<?> destination
) {
// TODO Add warning if teleporting too many players at once.
for (Player player : players) {
issuer.sendMessage("Teleporting "
+ (issuer.getPlayer() == player ? "you" : player.getName())
+ " to " + destination + "...");
this.plugin.getDestinationsProvider().playerTeleport(issuer, player, destination);
}
String playerName = players.length == 1
? issuer.getPlayer() == players[0] ? "you" : players[0].getName()
: players.length + " players";
issuer.sendInfo(MVCorei18n.TELEPORT_SUCCESS,
"{player}", playerName, "{destination}", destination.toString());
CompletableFuture.allOf(Arrays.stream(players)
.map(player -> this.destinationsProvider.playerTeleportAsync(issuer, player, destination))
.toArray(CompletableFuture[]::new))
.thenRun(() -> Logging.finer("Async teleport completed."));
}
@Override
public boolean hasPermission(CommandIssuer issuer) {
return this.plugin.getDestinationsProvider().hasAnyTeleportPermission(issuer);
return this.destinationsProvider.hasAnyTeleportPermission(issuer);
}
}

View File

@ -7,34 +7,48 @@ 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 com.onarandombox.MultiverseCore.MultiverseCore;
import com.onarandombox.MultiverseCore.api.MVWorld;
import com.onarandombox.MultiverseCore.api.MVWorldManager;
import com.onarandombox.MultiverseCore.commandtools.MVCommandManager;
import com.onarandombox.MultiverseCore.commandtools.MultiverseCommand;
import com.onarandombox.MultiverseCore.utils.MVCorei18n;
import jakarta.inject.Inject;
import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Service;
@Service
@CommandAlias("mv")
public class UnloadCommand extends MultiverseCoreCommand {
public UnloadCommand(@NotNull MultiverseCore plugin) {
super(plugin);
public class UnloadCommand extends MultiverseCommand {
private final MVWorldManager worldManager;
@Inject
public UnloadCommand(@NotNull MVCommandManager commandManager, @NotNull MVWorldManager worldManager) {
super(commandManager);
this.worldManager = worldManager;
}
@Subcommand("unload")
@CommandPermission("multiverse.core.unload")
@CommandCompletion("@mvworlds")
@Syntax("<world>")
@Description("Unloads a world from Multiverse. This does NOT remove the world folder. This does NOT remove it from the config file.")
@Description("{@@mv-core.unload.description}")
public void onUnloadCommand(BukkitCommandIssuer issuer,
@Syntax("<world>")
@Description("Name of the world you want to unload.")
@Description("{@@mv-core.unload.world.description}")
MVWorld world
) {
issuer.sendMessage(String.format("Unloading world '%s'...", world.getColoredWorldString()));
issuer.sendInfo(MVCorei18n.UNLOAD_UNLOADING,
"{world}", world.getColoredWorldString());
//TODO API: Should be able to use MVWorld object directly
if (!this.plugin.getMVWorldManager().unloadWorld(world.getName())) {
issuer.sendMessage(String.format("Error unloading world '%s'! See console for more details.", world.getColoredWorldString()));
if (!this.worldManager.unloadWorld(world.getName())) {
issuer.sendError(MVCorei18n.UNLOAD_FAILURE,
"{world}", world.getColoredWorldString());
return;
}
issuer.sendMessage(String.format("Unloaded world '%s'!", world.getColoredWorldString()));
issuer.sendInfo(MVCorei18n.UNLOAD_SUCCESS,
"{world}", world.getColoredWorldString());
}
}

View File

@ -0,0 +1,39 @@
package com.onarandombox.MultiverseCore.commands;
import co.aikar.commands.CommandHelp;
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.HelpCommand;
import co.aikar.commands.annotation.Subcommand;
import co.aikar.commands.annotation.Syntax;
import com.onarandombox.MultiverseCore.commandtools.MVCommandManager;
import com.onarandombox.MultiverseCore.commandtools.MultiverseCommand;
import jakarta.inject.Inject;
import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Service;
@Service
@CommandAlias("mv")
public class UsageCommand extends MultiverseCommand {
@Inject
public UsageCommand(@NotNull MVCommandManager commandManager) {
super(commandManager);
}
@HelpCommand
@Subcommand("help")
@CommandPermission("multiverse.core.help")
@CommandCompletion("@commands:mv")
@Syntax("[filter] [page]")
@Description("{@@mv-core.usage.description}")
public void onUsageCommand(CommandHelp help) {
if (help.getIssuer().isPlayer()) {
// Prevent flooding the chat
help.setPerPage(4);
}
this.commandManager.showUsage(help);
}
}

View File

@ -5,34 +5,51 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import co.aikar.commands.BukkitCommandCompletionContext;
import co.aikar.commands.BukkitCommandIssuer;
import co.aikar.commands.CommandIssuer;
import co.aikar.commands.PaperCommandCompletions;
import co.aikar.commands.RegisteredCommand;
import co.aikar.commands.RootCommand;
import com.google.common.collect.Sets;
import com.onarandombox.MultiverseCore.MultiverseCore;
import com.onarandombox.MultiverseCore.api.MVWorld;
import com.onarandombox.MultiverseCore.api.MVWorldManager;
import com.onarandombox.MultiverseCore.config.MVCoreConfig;
import com.onarandombox.MultiverseCore.destination.DestinationsProvider;
import com.onarandombox.MultiverseCore.destination.ParsedDestination;
import jakarta.inject.Inject;
import org.bukkit.GameRule;
import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Service;
@Service
public class MVCommandCompletions extends PaperCommandCompletions {
protected final MVCommandManager commandManager;
private final MultiverseCore plugin;
private final MVWorldManager worldManager;
public MVCommandCompletions(MVCommandManager mvCommandManager, MultiverseCore plugin) {
protected final MVCommandManager commandManager;
private final MVWorldManager worldManager;
private final DestinationsProvider destinationsProvider;
@Inject
public MVCommandCompletions(
@NotNull MVCommandManager mvCommandManager,
@NotNull MVWorldManager worldManager,
@NotNull DestinationsProvider destinationsProvider,
@NotNull MVCoreConfig config
) {
super(mvCommandManager);
this.commandManager = mvCommandManager;
this.plugin = plugin;
this.worldManager = plugin.getMVWorldManager();
this.worldManager = worldManager;
this.destinationsProvider = destinationsProvider;
registerAsyncCompletion("commands", this::suggestCommands);
registerAsyncCompletion("destinations", this::suggestDestinations);
registerAsyncCompletion("flags", this::suggestFlags);
registerStaticCompletion("gamerules", this::suggestGamerules);
registerStaticCompletion("mvconfigs", config.getNodes().getNames());
registerAsyncCompletion("mvworlds", this::suggestMVWorlds);
setDefaultCompletion("destinations", ParsedDestination.class);
@ -41,12 +58,39 @@ public class MVCommandCompletions extends PaperCommandCompletions {
setDefaultCompletion("mvworlds", MVWorld.class);
}
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) {
if (context.hasConfig("playerOnly") && !context.getIssuer().isPlayer()) {
return Collections.emptyList();
}
return this.plugin.getDestinationsProvider()
return this.destinationsProvider
.suggestDestinations((BukkitCommandIssuer)context.getIssuer(), context.getInput());
}
@ -93,6 +137,9 @@ public class MVCommandCompletions extends PaperCommandCompletions {
case "unloaded":
worlds.addAll(worldManager.getUnloadedWorlds());
break;
case "potential":
worlds.addAll(worldManager.getPotentialWorlds());
break;
}
return worlds;
}

View File

@ -6,44 +6,48 @@ import co.aikar.commands.BukkitConditionContext;
import co.aikar.commands.CommandConditions;
import co.aikar.commands.ConditionContext;
import co.aikar.commands.ConditionFailedException;
import com.onarandombox.MultiverseCore.MultiverseCore;
import com.onarandombox.MultiverseCore.api.MVWorldManager;
import com.onarandombox.MultiverseCore.world.WorldNameChecker;
import org.jetbrains.annotations.NotNull;
public class MVCommandConditions {
static void load(MVCommandManager commandManager, MultiverseCore plugin) {
new MVCommandConditions(commandManager, plugin);
static void load(MVCommandManager commandManager, MVWorldManager worldManager) {
new MVCommandConditions(commandManager, worldManager);
}
private final MVWorldManager worldManager;
private final MVCommandManager commandManager;
private final MultiverseCore plugin;
public MVCommandConditions(MVCommandManager commandManager, MultiverseCore plugin) {
private MVCommandConditions(@NotNull MVCommandManager commandManager, @NotNull MVWorldManager worldManager) {
this.worldManager = worldManager;
this.commandManager = commandManager;
this.plugin = plugin;
registerConditions();
}
private void registerConditions() {
CommandConditions<BukkitCommandIssuer, BukkitCommandExecutionContext, BukkitConditionContext> conditions
= commandManager.getCommandConditions();
conditions.addCondition(String.class, "validWorldName", this::checkValidWorldName);
conditions.addCondition(String.class, "worldname", this::checkWorldname);
}
private void checkValidWorldName(ConditionContext<BukkitCommandIssuer> context,
BukkitCommandExecutionContext executionContext,
String worldName
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.plugin.getMVWorldManager().isMVWorld(worldName)) {
if (!this.worldManager.isMVWorld(worldName)) {
throw new ConditionFailedException("World with name '" + worldName + "' does not exist or is not loaded!");
}
break;
// Worlds that are unloaded
case "unloaded":
if (!this.plugin.getMVWorldManager().hasUnloadedWorld(worldName, false)) {
if (this.plugin.getMVWorldManager().isMVWorld(worldName)) {
if (!this.worldManager.hasUnloadedWorld(worldName, false)) {
if (this.worldManager.isMVWorld(worldName)) {
throw new ConditionFailedException("World with name '" + worldName + "' is loaded already!");
}
throw new ConditionFailedException("World with name '" + worldName + "' does not exist!");
@ -51,13 +55,13 @@ public class MVCommandConditions {
break;
// World that are loaded or unloaded
case "both":
if (!this.plugin.getMVWorldManager().hasUnloadedWorld(worldName, true)) {
if (!this.worldManager.hasUnloadedWorld(worldName, true)) {
throw new ConditionFailedException("World with name '" + worldName + "' does not exist!");
}
break;
// World that are does not exist
case "new":
if (this.plugin.getMVWorldManager().hasUnloadedWorld(worldName, true)) {
if (this.worldManager.hasUnloadedWorld(worldName, true)) {
throw new ConditionFailedException("World with name '" + worldName + "' already exists!");
}
switch (WorldNameChecker.checkName(worldName)) {

View File

@ -1,6 +1,7 @@
package com.onarandombox.MultiverseCore.commandtools;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;
import co.aikar.commands.BukkitCommandExecutionContext;
@ -9,35 +10,66 @@ import co.aikar.commands.InvalidCommandArgument;
import co.aikar.commands.PaperCommandContexts;
import co.aikar.commands.contexts.ContextResolver;
import com.google.common.base.Strings;
import com.onarandombox.MultiverseCore.MultiverseCore;
import com.onarandombox.MultiverseCore.api.MVWorld;
import com.onarandombox.MultiverseCore.api.MVWorldManager;
import com.onarandombox.MultiverseCore.commandtools.context.GameRuleValue;
import com.onarandombox.MultiverseCore.commandtools.context.MVConfigValue;
import com.onarandombox.MultiverseCore.config.MVCoreConfig;
import com.onarandombox.MultiverseCore.configuration.node.Node;
import com.onarandombox.MultiverseCore.configuration.node.ValueNode;
import com.onarandombox.MultiverseCore.destination.DestinationsProvider;
import com.onarandombox.MultiverseCore.destination.ParsedDestination;
import com.onarandombox.MultiverseCore.display.filters.ContentFilter;
import com.onarandombox.MultiverseCore.display.filters.DefaultContentFilter;
import com.onarandombox.MultiverseCore.display.filters.RegexContentFilter;
import com.onarandombox.MultiverseCore.utils.PlayerFinder;
import io.vavr.control.Option;
import jakarta.inject.Inject;
import org.bukkit.GameRule;
import org.bukkit.entity.Player;
import org.jvnet.hk2.annotations.Service;
@Service
public class MVCommandContexts extends PaperCommandContexts {
private final MultiverseCore plugin;
public MVCommandContexts(MVCommandManager mvCommandManager, MultiverseCore plugin) {
private final MVCommandManager mvCommandManager;
private final DestinationsProvider destinationsProvider;
private final MVWorldManager worldManager;
private final MVCoreConfig config;
@Inject
public MVCommandContexts(
MVCommandManager mvCommandManager,
DestinationsProvider destinationsProvider,
MVWorldManager worldManager,
MVCoreConfig config
) {
super(mvCommandManager);
this.plugin = plugin;
this.mvCommandManager = mvCommandManager;
this.destinationsProvider = destinationsProvider;
this.worldManager = worldManager;
this.config = config;
registerIssuerOnlyContext(BukkitCommandIssuer.class, BukkitCommandExecutionContext::getIssuer);
registerIssuerOnlyContext(MVCommandIssuer.class, this::parseMVCommandIssuer);
registerOptionalContext(ContentFilter.class, this::parseContentFilter);
registerContext(ParsedDestination.class, this::parseDestination);
registerContext(GameRule.class, this::parseGameRule);
registerContext(GameRuleValue.class, this::parseGameRuleValue);
registerContext(MVConfigValue.class, this::parseMVConfigValue);
registerIssuerAwareContext(MVWorld.class, this::parseMVWorld);
registerIssuerAwareContext(MVWorld[].class, this::parseMVWorldArray);
registerIssuerAwareContext(Player.class, this::parsePlayer);
registerIssuerAwareContext(Player[].class, this::parsePlayerArray);
}
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();
@ -52,7 +84,7 @@ public class MVCommandContexts extends PaperCommandContexts {
throw new InvalidCommandArgument("No destination specified.");
}
ParsedDestination<?> parsedDestination = plugin.getDestinationsProvider().parseDestination(destination);
ParsedDestination<?> parsedDestination = destinationsProvider.parseDestination(destination);
if (parsedDestination == null) {
throw new InvalidCommandArgument("The destination " + destination + " is not valid.");
}
@ -97,13 +129,46 @@ public class MVCommandContexts extends PaperCommandContexts {
return new GameRuleValue(resolvedValue);
}
private MVConfigValue parseMVConfigValue(BukkitCommandExecutionContext context) {
String configName = (String) context.getResolvedArg(String.class);
if (Strings.isNullOrEmpty(configName)) {
throw new InvalidCommandArgument("No config name specified.");
}
Option<Node> node = config.getNodes().findNode(configName);
if (node.isEmpty()) {
throw new InvalidCommandArgument("The config " + configName + " is not valid.");
}
String valueString = context.getFirstArg();
if (Strings.isNullOrEmpty(valueString)) {
throw new InvalidCommandArgument("No config value specified.");
}
if (!(node.get() instanceof ValueNode)) {
context.popFirstArg();
return new MVConfigValue(valueString);
}
ContextResolver<?, BukkitCommandExecutionContext> resolver = getResolver(((ValueNode<?>) node.get()).getType());
if (resolver == null) {
context.popFirstArg();
return new MVConfigValue(valueString);
}
Object resolvedValue = resolver.getContext(context);
if (resolvedValue == null) {
throw new InvalidCommandArgument("The config value " + valueString + " is not valid for config " + configName + ".");
}
return new MVConfigValue(resolvedValue);
}
private MVWorld parseMVWorld(BukkitCommandExecutionContext context) {
String resolve = context.getFlagValue("resolve", "");
// Get world based on sender only
if (resolve.equals("issuerOnly")) {
if (context.getIssuer().isPlayer()) {
return plugin.getMVWorldManager().getMVWorld(context.getIssuer().getPlayer().getWorld());
return worldManager.getMVWorld(context.getIssuer().getPlayer().getWorld());
}
if (context.isOptional()) {
return null;
@ -112,7 +177,7 @@ public class MVCommandContexts extends PaperCommandContexts {
}
String worldName = context.getFirstArg();
MVWorld world = plugin.getMVWorldManager().getMVWorld(worldName);
MVWorld world = worldManager.getMVWorld(worldName);
// Get world based on input, fallback to sender if input is not a world
if (resolve.equals("issuerAware")) {
@ -121,7 +186,7 @@ public class MVCommandContexts extends PaperCommandContexts {
return world;
}
if (context.getIssuer().isPlayer()) {
return plugin.getMVWorldManager().getMVWorld(context.getIssuer().getPlayer().getWorld());
return worldManager.getMVWorld(context.getIssuer().getPlayer().getWorld());
}
if (context.isOptional()) {
return null;
@ -145,7 +210,7 @@ public class MVCommandContexts extends PaperCommandContexts {
MVWorld playerWorld = null;
if (context.getIssuer().isPlayer()) {
playerWorld = plugin.getMVWorldManager().getMVWorld(context.getIssuer().getPlayer().getWorld());
playerWorld = worldManager.getMVWorld(context.getIssuer().getPlayer().getWorld());
}
// Get world based on sender only
@ -164,9 +229,10 @@ public class MVCommandContexts extends PaperCommandContexts {
Set<MVWorld> worlds = new HashSet<>(worldNames.length);
for (String worldName : worldNames) {
if ("*".equals(worldName)) {
return plugin.getMVWorldManager().getMVWorlds().toArray(new MVWorld[0]);
worlds.addAll(worldManager.getMVWorlds());
break;
}
MVWorld world = plugin.getMVWorldManager().getMVWorld(worldName);
MVWorld world = worldManager.getMVWorld(worldName);
if (world == null) {
throw new InvalidCommandArgument("World " + worldName + " is not a loaded multiverse world.");
}

View File

@ -0,0 +1,48 @@
package com.onarandombox.MultiverseCore.commandtools;
import co.aikar.commands.MessageType;
import co.aikar.commands.OpenBukkitCommandIssuer;
import co.aikar.locales.MessageKeyProvider;
import com.onarandombox.MultiverseCore.utils.message.Message;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
public class MVCommandIssuer extends OpenBukkitCommandIssuer {
private final MVCommandManager commandManager;
MVCommandIssuer(@NotNull MVCommandManager commandManager, @NotNull CommandSender sender) {
super(commandManager, sender);
this.commandManager = commandManager;
}
@Override
public MVCommandManager getManager() {
return commandManager;
}
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);
}
private void sendMessage(MessageType messageType, Message message) {
if (message instanceof MessageKeyProvider) {
sendMessage(messageType, (MessageKeyProvider) message, message.getReplacements());
} else {
var formatter = getManager().getFormat(messageType);
if (formatter != null) {
sendMessage(formatter.format(message.formatted()));
} else {
sendMessage(message.formatted());
}
}
}
}

View File

@ -1,37 +1,66 @@
package com.onarandombox.MultiverseCore.commandtools;
import java.util.Locale;
import java.util.List;
import co.aikar.commands.BukkitCommandCompletionContext;
import co.aikar.commands.BukkitCommandExecutionContext;
import co.aikar.commands.BukkitLocales;
import co.aikar.commands.CommandCompletions;
import co.aikar.commands.CommandContexts;
import co.aikar.commands.CommandHelp;
import co.aikar.commands.HelpEntry;
import co.aikar.commands.PaperCommandManager;
import com.onarandombox.MultiverseCore.MultiverseCore;
import com.onarandombox.MultiverseCore.api.MVWorldManager;
import com.onarandombox.MultiverseCore.commandtools.flags.CommandFlagsManager;
import com.onarandombox.MultiverseCore.commandtools.queue.CommandQueueManager;
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;
/**
* Main class to manage permissions.
*/
@Service
public class MVCommandManager extends PaperCommandManager {
private final MultiverseCore plugin;
private CommandFlagsManager flagsManager;
private CommandQueueManager commandQueueManager;
private final CommandFlagsManager flagsManager;
private final CommandQueueManager commandQueueManager;
private final Provider<MVCommandContexts> commandContextsProvider;
private final Provider<MVCommandCompletions> commandCompletionsProvider;
public MVCommandManager(@NotNull MultiverseCore plugin) {
@Inject
public MVCommandManager(
@NotNull MultiverseCore plugin,
@NotNull CommandFlagsManager flagsManager,
@NotNull CommandQueueManager commandQueueManager,
@NotNull Provider<MVCommandContexts> commandContextsProvider,
@NotNull Provider<MVCommandCompletions> commandCompletionsProvider,
@NotNull MVWorldManager worldManager
) {
super(plugin);
this.plugin = plugin;
this.flagsManager = flagsManager;
this.commandQueueManager = commandQueueManager;
this.commandContextsProvider = commandContextsProvider;
this.commandCompletionsProvider = commandCompletionsProvider;
// Setup conditions
MVCommandConditions.load(this, plugin);
MVCommandConditions.load(this, worldManager);
this.enableUnstableAPI("help");
}
// Setup locale
this.addSupportedLanguage(Locale.ENGLISH);
this.locales.addMessageBundles("multiverse-core");
this.locales.loadLanguages();
void loadLanguages(PluginLocales locales) {
if (this.locales == null) {
this.locales = locales;
this.locales.loadLanguages();
}
}
@Override
public BukkitLocales getLocales() {
return this.locales;
}
/**
@ -40,9 +69,6 @@ public class MVCommandManager extends PaperCommandManager {
* @return A not-null {@link CommandFlagsManager}.
*/
public synchronized @NotNull CommandFlagsManager getFlagsManager() {
if (this.flagsManager == null) {
this.flagsManager = new CommandFlagsManager();
}
return flagsManager;
}
@ -52,9 +78,6 @@ public class MVCommandManager extends PaperCommandManager {
* @return A non-null {@link CommandQueueManager}.
*/
public synchronized @NotNull CommandQueueManager getCommandQueueManager() {
if (this.commandQueueManager == null) {
this.commandQueueManager = new CommandQueueManager(this.plugin);
}
return commandQueueManager;
}
@ -66,7 +89,7 @@ public class MVCommandManager extends PaperCommandManager {
@Override
public synchronized @NotNull CommandContexts<BukkitCommandExecutionContext> getCommandContexts() {
if (this.contexts == null) {
this.contexts = new MVCommandContexts(this, plugin);
this.contexts = commandContextsProvider.get();
}
return this.contexts;
}
@ -79,8 +102,35 @@ public class MVCommandManager extends PaperCommandManager {
@Override
public synchronized @NotNull CommandCompletions<BukkitCommandCompletionContext> getCommandCompletions() {
if (this.completions == null) {
this.completions = new MVCommandCompletions(this, plugin);
this.completions = commandCompletionsProvider.get();
}
return this.completions;
}
/**
* 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);
}
}
}

View File

@ -1,29 +1,35 @@
package com.onarandombox.MultiverseCore.commandtools;
import co.aikar.commands.BaseCommand;
import com.onarandombox.MultiverseCore.api.MVPlugin;
import com.onarandombox.MultiverseCore.commandtools.flags.CommandFlagGroup;
import com.onarandombox.MultiverseCore.commandtools.flags.CommandFlagsManager;
import com.onarandombox.MultiverseCore.commandtools.flags.ParsedCommandFlags;
import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Contract;
@Contract
public abstract class MultiverseCommand extends BaseCommand {
protected final CommandFlagsManager flagsManager;
protected final MVCommandManager commandManager;
private String flagGroupName;
protected MultiverseCommand(@NotNull MVPlugin plugin) {
this.flagsManager = plugin.getCore().getMVCommandManager().getFlagsManager();
protected MultiverseCommand(@NotNull MVCommandManager commandManager) {
this.commandManager = commandManager;
}
protected CommandFlagsManager getFlagsManager() {
return commandManager.getFlagsManager();
}
protected void registerFlagGroup(@NotNull CommandFlagGroup flagGroup) {
if (flagGroupName != null) {
throw new IllegalStateException("Flag group already registered! (name: " + flagGroupName + ")");
}
flagsManager.registerFlagGroup(flagGroup);
getFlagsManager().registerFlagGroup(flagGroup);
flagGroupName = flagGroup.getName();
}
protected @NotNull ParsedCommandFlags parseFlags(@NotNull String[] flags) {
return flagsManager.parse(flagGroupName, flags);
return getFlagsManager().parse(flagGroupName, flags);
}
}

View File

@ -0,0 +1,49 @@
package com.onarandombox.MultiverseCore.commandtools;
import co.aikar.commands.BukkitLocales;
import com.onarandombox.MultiverseCore.utils.file.FileResClassLoader;
import jakarta.inject.Inject;
import org.bukkit.plugin.Plugin;
import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Service;
/**
* Locale manager with additional methods for loading locales from plugin's locales folder.
*/
@Service
public class PluginLocales extends BukkitLocales {
private static final String DEFAULT_LOCALE_FOLDER_PATH = "locales";
/**
* Creates a new instance of {@link PluginLocales}.
*
* @param manager The command manager.
*/
@Inject
public PluginLocales(MVCommandManager manager) {
super(manager);
manager.loadLanguages(this);
}
/**
* Adds a {@link FileResClassLoader} to the list of class loaders to load locales data from.
*
* @param plugin The plugin.
* @return True if the class loader was added successfully.
*/
public boolean addFileResClassLoader(@NotNull Plugin plugin) {
return this.addBundleClassLoader(new FileResClassLoader(plugin, DEFAULT_LOCALE_FOLDER_PATH));
}
/**
* Adds a {@link FileResClassLoader} to the list of class loaders to load locales data from.
*
* @param plugin The plugin.
* @param localesFolderPath The path to the folder containing the locales.
* @return True if the class loader was added successfully.
*/
public boolean addFileResClassLoader(@NotNull Plugin plugin, @NotNull String localesFolderPath) {
return this.addBundleClassLoader(new FileResClassLoader(plugin, localesFolderPath));
}
}

View File

@ -0,0 +1,13 @@
package com.onarandombox.MultiverseCore.commandtools.context;
public class MVConfigValue {
private final Object value;
public MVConfigValue(Object value) {
this.value = value;
}
public Object getValue() {
return value;
}
}

View File

@ -26,11 +26,12 @@ public class CommandFlag {
/**
* Creates a new flag.
*
* @param builder The builder.
* @param key The key for the new flag.
* @param aliases The aliases that also refer to this flag.
*/
protected CommandFlag(@NotNull Builder<?> builder) {
key = builder.key;
aliases = builder.aliases;
protected CommandFlag(@NotNull String key, @NotNull List<String> aliases) {
this.key = key;
this.aliases = aliases;
}
/**
@ -57,8 +58,8 @@ public class CommandFlag {
* @param <S> The type of the builder.
*/
public static class Builder<S extends Builder<?>> {
private final String key;
private final List<String> aliases;
protected final String key;
protected final List<String> aliases;
/**
* Create a new builder.
@ -87,7 +88,7 @@ public class CommandFlag {
* @return The flag.
*/
public @NotNull CommandFlag build(){
return new CommandFlag(this);
return new CommandFlag(key, aliases);
}
}
}

View File

@ -9,10 +9,12 @@ import java.util.Map;
import co.aikar.commands.InvalidCommandArgument;
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;

View File

@ -1,12 +1,16 @@
package com.onarandombox.MultiverseCore.commandtools.flags;
import java.util.Collection;
import java.util.function.Function;
import java.util.function.Supplier;
import co.aikar.commands.InvalidCommandArgument;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
/**
* Represents a flag with a value.
*
@ -16,14 +20,25 @@ 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.
* @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;
@ -33,15 +48,29 @@ public class CommandValueFlag<T> extends CommandFlag {
/**
* Creates a new flag.
*
* @param builder The builder.
* @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 Builder<T, ?> builder) {
super(builder);
type = builder.type;
optional = builder.optional;
defaultValue = builder.defaultValue;
context = builder.context;
completion = builder.completion;
protected CommandValueFlag(
@NotNull String key,
@NotNull List<String> aliases,
@NotNull Class<T> type,
boolean optional,
@Nullable T defaultValue,
@Nullable Function<String, T> context,
@Nullable Supplier<Collection<String>> completion
) {
super(key, aliases);
this.type = type;
this.optional = optional;
this.defaultValue = defaultValue;
this.context = context;
this.completion = completion;
}
/**
@ -96,11 +125,11 @@ public class CommandValueFlag<T> extends CommandFlag {
* @param <S> The type of the builder.
*/
public static class Builder<T, S extends Builder<T, S>> extends CommandFlag.Builder<S> {
private final Class<T> type;
private boolean optional = false;
private T defaultValue = null;
private Function<String, T> context = null;
private Supplier<Collection<String>> completion = null;
protected final Class<T> type;
protected boolean optional = false;
protected T defaultValue = null;
protected Function<String, T> context = null;
protected Supplier<Collection<String>> completion = null;
/**
* Create a new builder.
@ -166,7 +195,77 @@ public class CommandValueFlag<T> extends CommandFlag {
if (context == null && !String.class.equals(type)) {
throw new IllegalStateException("Context is required for none-string value flags");
}
return new CommandValueFlag<>(this);
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 Supplier<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());
} catch (IllegalArgumentException e) {
throw new InvalidCommandArgument("Invalid value for argument " + key + ": " + value);
}
};
}
private void setEnumCompletion() {
List<String> types = Arrays.stream(type.getEnumConstants())
.map(type -> type.name().toLowerCase())
.collect(Collectors.toList());
this.completion = () -> 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 CommandFlag build() {
return new CommandValueFlag<>(key, aliases, type, optional, defaultValue, context, completion);
}
}
}

View File

@ -12,13 +12,16 @@ import java.util.WeakHashMap;
import com.dumptruckman.minecraft.util.Logging;
import com.onarandombox.MultiverseCore.MultiverseCore;
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.plugin.Plugin;
import org.bukkit.scheduler.BukkitTask;
import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Service;
/**
* <p>Manages the queuing of dangerous commands that require {@code /mv confirm} before executing.</p>
@ -26,14 +29,16 @@ import org.jetbrains.annotations.NotNull;
* <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 long TICKS_PER_SECOND = 20;
private static final DummyCommandBlockSender COMMAND_BLOCK = new DummyCommandBlockSender();
private final MultiverseCore plugin;
private final Plugin plugin;
private final Map<CommandSender, QueuedCommand> queuedCommandMap;
@Inject
public CommandQueueManager(@NotNull MultiverseCore plugin) {
this.plugin = plugin;
this.queuedCommandMap = new WeakHashMap<>();

View File

@ -0,0 +1,262 @@
package com.onarandombox.MultiverseCore.config;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import com.dumptruckman.minecraft.util.Logging;
import com.onarandombox.MultiverseCore.MultiverseCore;
import com.onarandombox.MultiverseCore.api.MVConfig;
import com.onarandombox.MultiverseCore.configuration.handle.CommentedYamlConfigHandle;
import com.onarandombox.MultiverseCore.configuration.migration.BooleanMigratorAction;
import com.onarandombox.MultiverseCore.configuration.migration.ConfigMigrator;
import com.onarandombox.MultiverseCore.configuration.migration.IntegerMigratorAction;
import com.onarandombox.MultiverseCore.configuration.migration.InvertBoolMigratorAction;
import com.onarandombox.MultiverseCore.configuration.migration.MoveMigratorAction;
import com.onarandombox.MultiverseCore.configuration.migration.VersionMigrator;
import com.onarandombox.MultiverseCore.configuration.node.NodeGroup;
import io.vavr.control.Try;
import jakarta.inject.Inject;
import org.bukkit.plugin.PluginManager;
import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Service;
@Service
public class MVCoreConfig implements MVConfig {
public static final String CONFIG_FILENAME = "config.yml";
public static final double CONFIG_VERSION = 5.0;
private final Path configPath;
private final MVCoreConfigNodes configNodes;
private final CommentedYamlConfigHandle configHandle;
@Inject
MVCoreConfig(@NotNull MultiverseCore core, @NotNull PluginManager pluginManager) {
this.configPath = Path.of(core.getDataFolder().getPath(), CONFIG_FILENAME);
this.configNodes = new MVCoreConfigNodes(pluginManager);
this.configHandle = CommentedYamlConfigHandle.builder(configPath)
.logger(Logging.getLogger())
.nodes(configNodes.getNodes())
.migrator(ConfigMigrator.builder(configNodes.VERSION)
.addVersionMigrator(VersionMigrator.builder(5.0)
.addAction(MoveMigratorAction.of("multiverse-configuration.enforceaccess", "world.enforce-access"))
.addAction(BooleanMigratorAction.of("world.enforce-access"))
.addAction(MoveMigratorAction.of("multiverse-configuration.prefixchat", "messaging.enable-chat-prefix"))
.addAction(BooleanMigratorAction.of("messaging.enable-chat-prefix"))
.addAction(MoveMigratorAction.of("multiverse-configuration.prefixchatformat", "messaging.chat-prefix-format"))
.addAction(MoveMigratorAction.of("multiverse-configuration.teleportintercept", "world.teleport-intercept"))
.addAction(BooleanMigratorAction.of("world.teleport-intercept"))
.addAction(MoveMigratorAction.of("multiverse-configuration.firstspawnoverride", "spawn.first-spawn-override"))
.addAction(BooleanMigratorAction.of("spawn.first-spawn-override"))
//.addAction(MoveMigratorAction.of("multiverse-configuration.displaypermerrors", ""))
.addAction(MoveMigratorAction.of("multiverse-configuration.globaldebug", "misc.global-debug"))
.addAction(IntegerMigratorAction.of("misc.global-debug"))
.addAction(MoveMigratorAction.of("multiverse-configuration.silentstart", "misc.silent-start"))
.addAction(BooleanMigratorAction.of("misc.silent-start"))
.addAction(MoveMigratorAction.of("multiverse-configuration.firstspawnworld", "spawn.first-spawn-location"))
.addAction(MoveMigratorAction.of("multiverse-configuration.defaultportalsearch", "portal.use-custom-portal-search"))
.addAction(BooleanMigratorAction.of("portal.use-custom-portal-search"))
.addAction(InvertBoolMigratorAction.of("portal.use-custom-portal-search"))
.addAction(MoveMigratorAction.of("multiverse-configuration.portalsearchradius", "portal.custom-portal-search-radius"))
.addAction(IntegerMigratorAction.of("portal.custom-portal-search-radius"))
.addAction(MoveMigratorAction.of("multiverse-configuration.autopurge", "world.auto-purge-entities"))
.addAction(BooleanMigratorAction.of("world.auto-purge-entities"))
.addAction(MoveMigratorAction.of("multiverse-configuration.idonotwanttodonate", "misc.show-donation-message"))
.addAction(BooleanMigratorAction.of("misc.show-donation-message"))
.addAction(InvertBoolMigratorAction.of("misc.show-donation-message"))
.build())
.build())
.build();
load();
save();
}
private void migrateFromOldConfigFile() {
String content;
try {
content = Files.readString(configPath);
} catch (IOException e) {
return;
}
// Remove the old config section if it is still in the old ConfigurationSerializable.
content = content.replace("==: com.onarandombox.MultiverseCore.MultiverseCoreConfiguration", "");
try {
Files.writeString(configPath, content);
} catch (IOException e) {
// ignore
}
}
@Override
public boolean load() {
migrateFromOldConfigFile();
return configHandle.load();
}
@Override
public boolean isLoaded() {
return configHandle.isLoaded();
}
@Override
public boolean save() {
configHandle.save();
return true;
}
@Override
public NodeGroup getNodes() {
return configNodes.getNodes();
}
@Override
public Try<Object> getProperty(String name) {
return configHandle.get(name);
}
@Override
public Try<Void> setProperty(String name, Object value) {
return configHandle.set(name, value);
}
@Override
public void setEnforceAccess(boolean enforceAccess) {
configHandle.set(configNodes.ENFORCE_ACCESS, enforceAccess);
}
@Override
public boolean getEnforceAccess() {
return configHandle.get(configNodes.ENFORCE_ACCESS);
}
@Override
public void setEnforceGameMode(boolean enforceGameMode) {
configHandle.set(configNodes.ENFORCE_GAMEMODE, enforceGameMode);
}
@Override
public boolean getEnforceGameMode() {
return configHandle.get(configNodes.ENFORCE_GAMEMODE);
}
@Override
public void setAutoPurgeEntities(boolean autopurge) {
configHandle.set(configNodes.AUTO_PURGE_ENTITIES, autopurge);
}
@Override
public boolean isAutoPurgeEntities() {
return configHandle.get(configNodes.AUTO_PURGE_ENTITIES);
}
@Override
public void setTeleportIntercept(boolean teleportIntercept) {
configHandle.set(configNodes.TELEPORT_INTERCEPT, teleportIntercept);
}
@Override
public boolean getTeleportIntercept() {
return configHandle.get(configNodes.TELEPORT_INTERCEPT);
}
@Override
public void setFirstSpawnOverride(boolean firstSpawnOverride) {
configHandle.set(configNodes.FIRST_SPAWN_OVERRIDE, firstSpawnOverride);
}
@Override
public boolean getFirstSpawnOverride() {
return configHandle.get(configNodes.FIRST_SPAWN_OVERRIDE);
}
@Override
public void setFirstSpawnLocation(String firstSpawnWorld) {
configHandle.set(configNodes.FIRST_SPAWN_LOCATION, firstSpawnWorld);
}
@Override
public String getFirstSpawnLocation() {
return configHandle.get(configNodes.FIRST_SPAWN_LOCATION);
}
@Override
public void setUseCustomPortalSearch(boolean useDefaultPortalSearch) {
configHandle.set(configNodes.USE_CUSTOM_PORTAL_SEARCH, useDefaultPortalSearch);
}
@Override
public boolean isUsingCustomPortalSearch() {
return configHandle.get(configNodes.USE_CUSTOM_PORTAL_SEARCH);
}
@Override
public void setCustomPortalSearchRadius(int searchRadius) {
configHandle.set(configNodes.CUSTOM_PORTAL_SEARCH_RADIUS, searchRadius);
}
@Override
public int getCustomPortalSearchRadius() {
return configHandle.get(configNodes.CUSTOM_PORTAL_SEARCH_RADIUS);
}
@Override
public void setEnablePrefixChat(boolean prefixChat) {
configHandle.set(configNodes.ENABLE_CHAT_PREFIX, prefixChat);
}
@Override
public boolean isEnablePrefixChat() {
return configHandle.get(configNodes.ENABLE_CHAT_PREFIX);
}
@Override
public void setPrefixChatFormat(String prefixChatFormat) {
configHandle.set(configNodes.CHAT_PREFIX_FORMAT, prefixChatFormat);
}
@Override
public String getPrefixChatFormat() {
return configHandle.get(configNodes.CHAT_PREFIX_FORMAT);
}
@Override
public void setRegisterPapiHook(boolean registerPapiHook) {
configHandle.set(configNodes.REGISTER_PAPI_HOOK, registerPapiHook);
}
@Override
public boolean isRegisterPapiHook() {
return configHandle.get(configNodes.REGISTER_PAPI_HOOK);
}
@Override
public void setGlobalDebug(int globalDebug) {
configHandle.set(configNodes.GLOBAL_DEBUG, globalDebug);
}
@Override
public int getGlobalDebug() {
return configHandle.get(configNodes.GLOBAL_DEBUG);
}
@Override
public void setSilentStart(boolean silentStart) {
configHandle.set(configNodes.SILENT_START, silentStart);
}
@Override
public boolean getSilentStart() {
return configHandle.get(configNodes.SILENT_START);
}
@Override
public void setShowDonateMessage(boolean showDonateMessage) {
configHandle.set(configNodes.SHOW_DONATION_MESSAGE, showDonateMessage);
}
@Override
public boolean isShowingDonateMessage() {
return configHandle.get(configNodes.SHOW_DONATION_MESSAGE);
}
}

View File

@ -0,0 +1,216 @@
package com.onarandombox.MultiverseCore.config;
import com.dumptruckman.minecraft.util.Logging;
import com.onarandombox.MultiverseCore.configuration.node.ConfigHeaderNode;
import com.onarandombox.MultiverseCore.configuration.node.ConfigNode;
import com.onarandombox.MultiverseCore.configuration.node.Node;
import com.onarandombox.MultiverseCore.configuration.node.NodeGroup;
import com.onarandombox.MultiverseCore.event.MVDebugModeEvent;
import com.onarandombox.MultiverseCore.exceptions.MultiverseException;
import io.github.townyadvanced.commentedconfiguration.setting.CommentedNode;
import io.vavr.control.Try;
import org.bukkit.plugin.PluginManager;
class MVCoreConfigNodes {
private final NodeGroup nodes = new NodeGroup();
private PluginManager pluginManager;
MVCoreConfigNodes(PluginManager pluginManager) {
this.pluginManager = pluginManager;
}
public NodeGroup getNodes() {
return nodes;
}
private <N extends Node> N node(N node) {
nodes.add(node);
return node;
}
private final ConfigHeaderNode HEADER = node(ConfigHeaderNode.builder("world") // TODO hacky way to get the header to the top of the file
.comment("####################################################################################################")
.comment("# #")
.comment("# █▀▄▀█ █░█ █░░ ▀█▀ █ █░█ █▀▀ █▀█ █▀ █▀▀   █▀▀ █▀█ █▀█ █▀▀ #")
.comment("# █░▀░█ █▄█ █▄▄ ░█░ █ ▀▄▀ ██▄ █▀▄ ▄█ ██▄   █▄▄ █▄█ █▀▄ ██▄ #")
.comment("# #")
.comment("# #")
.comment("# WIKI: https://github.com/Multiverse/Multiverse-Core/wiki #")
.comment("# DISCORD: https://discord.gg/NZtfKky #")
.comment("# BUG REPORTS: https://github.com/Multiverse/Multiverse-Core/issues #")
.comment("# #")
.comment("# #")
.comment("# Each option in this file is documented and explained here: #")
.comment("# ==> https://github.com/Multiverse/Multiverse-Core/wiki/config.yml #")
.comment("# #")
.comment("# #")
.comment("# New options are added to this file automatically. If you manually made changes #")
.comment("# to this file while your server is running, please run `/mv reload` command. #")
.comment("# #")
.comment("####################################################################################################")
.comment("")
.comment("")
.build());
// private final ConfigHeaderNode WORLD_HEADER = node(ConfigHeaderNode.builder("world")
// .comment("")
// .comment("")
// .build());
public final ConfigNode<Boolean> ENFORCE_ACCESS = node(ConfigNode.builder("world.enforce-access", Boolean.class)
.comment("This setting will prevent players from entering worlds they don't have access to.")
.comment("If this is set to false, players will be able to enter any world they want.")
.comment("If this is set to true, players will only be able to enter worlds they have")
.comment("the `mv.access.<worldname>` permission.")
.defaultValue(false)
.name("enforce-access")
.build());
public final ConfigNode<Boolean> ENFORCE_GAMEMODE = node(ConfigNode.builder("world.enforce-gamemode", Boolean.class)
.comment("")
.comment("Sets whether Multiverse will should enforce gamemode on world change.")
.comment("If enabled, players will be forced into the gamemode of the world they are entering, unless they have")
.comment("the `mv.bypass.gamemode.<worldname>` permission.")
.defaultValue(true)
.name("enforce-gamemode")
.build());
public final ConfigNode<Boolean> AUTO_PURGE_ENTITIES = node(ConfigNode.builder("world.auto-purge-entities", Boolean.class)
.comment("")
.comment("Sets whether Multiverse will purge mobs and entities with be automatically.")
.defaultValue(false)
.name("auto-purge-entities")
.build());
public final ConfigNode<Boolean> TELEPORT_INTERCEPT = node(ConfigNode.builder("world.teleport-intercept", Boolean.class)
.comment("")
.comment("If this is set to true, Multiverse will enforce access permissions for all teleportation,")
.comment("including teleportation from other plugins.")
.defaultValue(true)
.name("teleport-intercept")
.build());
private final ConfigHeaderNode SPAWN_HEADER = node(ConfigHeaderNode.builder("spawn")
.comment("")
.comment("")
.build());
public final ConfigNode<Boolean> FIRST_SPAWN_OVERRIDE = node(ConfigNode.builder("spawn.first-spawn-override", Boolean.class)
.comment("Sets whether Multiverse will override the first spawn location of a world.")
.comment("If enabled, Multiverse will set the first spawn location of a world to the spawn location of the world.")
.comment("If disabled, it will default to server.properties settings.")
.defaultValue(true)
.name("first-spawn-override")
.build());
public final ConfigNode<String> FIRST_SPAWN_LOCATION = node(ConfigNode.builder("spawn.first-spawn-location", String.class)
.comment("")
.comment("Sets the world that Multiverse will use as the location for players that first join the server.")
.comment("This only applies if first-spawn-override is set to true.")
.defaultValue("")
.name("first-spawn-location")
.build());
private final ConfigHeaderNode PORTAL_HEADER = node(ConfigHeaderNode.builder("portal")
.comment("")
.comment("")
.build());
public final ConfigNode<Boolean> USE_CUSTOM_PORTAL_SEARCH = node(ConfigNode.builder("portal.use-custom-portal-search", Boolean.class)
.comment("This config option defines whether or not Multiverse should interfere with's Bukkit's default portal search radius.")
.comment("Setting it to false would mean you want to simply let Bukkit decides the search radius itself.")
.defaultValue(false)
.name("use-custom-portal-search")
.build());
public final ConfigNode<Integer> CUSTOM_PORTAL_SEARCH_RADIUS = node(ConfigNode.builder("portal.custom-portal-search-radius", Integer.class)
.comment("")
.comment("This config option defines the search radius Multiverse should use when searching for a portal.")
.comment("This only applies if use-custom-portal-search is set to true.")
.defaultValue(128)
.name("custom-portal-search-radius")
.validator(value -> value < 0
? Try.failure(new MultiverseException("The value must be greater than or equal to 0.", null))
: Try.success(null))
.build());
private final ConfigHeaderNode MESSAGING_HEADER = node(ConfigHeaderNode.builder("messaging")
.comment("")
.comment("")
.build());
public final ConfigNode<Boolean> ENABLE_CHAT_PREFIX = node(ConfigNode.builder("messaging.enable-chat-prefix", Boolean.class)
.comment("This config option defines whether or not Multiverse should prefix the chat with the world name.")
.comment("This only applies if use-custom-portal-search is set to true.")
.defaultValue(false)
.name("enable-chat-prefix")
.build());
public final ConfigNode<String> CHAT_PREFIX_FORMAT = node(ConfigNode.builder("messaging.chat-prefix-format", String.class)
.comment("")
.comment("This config option defines the format Multiverse should use when prefixing the chat with the world name.")
.comment("This only applies if enable-chat-prefix is set to true.")
.defaultValue("[%world%]%chat%")
.name("chat-prefix-format")
.build());
public final ConfigNode<Boolean> REGISTER_PAPI_HOOK = node(ConfigNode.builder("messaging.register-papi-hook", Boolean.class)
.comment("")
.comment("This config option defines whether or not Multiverse should register the PlaceholderAPI hook.")
.comment("This only applies if PlaceholderAPI is installed.")
.defaultValue(true)
.name("register-papi-hook")
.build());
private final ConfigHeaderNode MISC_HEADER = node(ConfigHeaderNode.builder("misc")
.comment("")
.comment("")
.build());
public final ConfigNode<Integer> GLOBAL_DEBUG = node(ConfigNode.builder("misc.global-debug", Integer.class)
.comment("This is our debug flag to help identify issues with Multiverse.")
.comment("If you are having issues with Multiverse, please set this to 3 and then post your log to pastebin.com")
.comment("Otherwise, there's no need to touch this. If not instructed by a wiki page or developer.")
.comment(" 0 = Off, No debug messages")
.comment(" 1 = fine")
.comment(" 2 = finer")
.comment(" 3 = finest")
.defaultValue(0)
.name("global-debug")
.validator(value -> (value < 0 || value > 3)
? Try.failure(new MultiverseException("Debug level must be between 0 and 3.", null))
: Try.success(null))
.onSetValue((oldValue, newValue) -> {
int level = Logging.getDebugLevel();
Logging.setDebugLevel(newValue);
if (level != Logging.getDebugLevel()) {
pluginManager.callEvent(new MVDebugModeEvent(level));
}
})
.build());
public final ConfigNode<Boolean> SILENT_START = node(ConfigNode.builder("misc.silent-start", Boolean.class)
.comment("")
.comment("If true, the startup console messages will no longer show.")
.defaultValue(false)
.name("silent-start")
.onSetValue((oldValue, newValue) -> Logging.setShowingConfig(!newValue))
.build());
public final ConfigNode<Boolean> SHOW_DONATION_MESSAGE = node(ConfigNode.builder("misc.show-donation-message", Boolean.class)
.comment("")
.comment("If you don't want to donate, you can set this to false and Multiverse will stop nagging you.")
.defaultValue(true)
.name("show-donation-message")
.build());
public final ConfigNode<Double> VERSION = node(ConfigNode.builder("version", Double.class)
.comment("")
.comment("")
.comment("This just signifies the version number so we can see what version of config you have.")
.comment("NEVER TOUCH THIS VALUE")
.defaultValue(MVCoreConfig.CONFIG_VERSION)
.name(null)
.build());
}

View File

@ -0,0 +1,93 @@
package com.onarandombox.MultiverseCore.configuration.handle;
import java.nio.file.Path;
import java.util.logging.Logger;
import com.dumptruckman.minecraft.util.Logging;
import com.onarandombox.MultiverseCore.configuration.migration.ConfigMigrator;
import com.onarandombox.MultiverseCore.configuration.node.NodeGroup;
import com.onarandombox.MultiverseCore.configuration.node.CommentedNode;
import com.onarandombox.MultiverseCore.configuration.node.ValueNode;
import io.github.townyadvanced.commentedconfiguration.CommentedConfiguration;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Configuration handle for commented YAML files.
*/
public class CommentedYamlConfigHandle extends FileConfigHandle<CommentedConfiguration> {
/**
* Creates a new builder for a {@link CommentedYamlConfigHandle}.
*
* @param configPath The path to the config file.
* @return The builder.
*/
public static @NotNull Builder builder(@NotNull Path configPath) {
return new Builder(configPath);
}
protected CommentedYamlConfigHandle(@NotNull Path configPath, @Nullable Logger logger, @Nullable NodeGroup nodes, @Nullable ConfigMigrator migrator) {
super(configPath, logger, nodes, migrator);
}
/**
* {@inheritDoc}
*/
@Override
protected boolean loadConfigObject() {
config = new CommentedConfiguration(configPath, logger);
return config.load();
}
/**
* {@inheritDoc}
*/
@Override
protected void setUpNodes() {
if (nodes == null || nodes.isEmpty()) {
return;
}
CommentedConfiguration oldConfig = config;
this.config = new CommentedConfiguration(configPath, logger);
nodes.forEach(node -> {
if (node instanceof CommentedNode typedNode) {
if (typedNode.getComments().length > 0) {
config.addComment(typedNode.getPath(), typedNode.getComments());
}
}
if (node instanceof ValueNode valueNode) {
set(valueNode, oldConfig.getObject(valueNode.getPath(), valueNode.getType(), valueNode.getDefaultValue())).onFailure(e -> {
Logging.warning("Failed to set node " + valueNode.getPath() + " to " + valueNode.getDefaultValue());
setDefault(valueNode);
});
}
});
}
/**
* {@inheritDoc}
*/
@Override
public boolean save() {
config.save();
return true;
}
public static class Builder extends FileConfigHandle.Builder<CommentedConfiguration, Builder> {
protected Builder(@NotNull Path configPath) {
super(configPath);
}
/**
* {@inheritDoc}
*/
@Override
public @NotNull CommentedYamlConfigHandle build() {
return new CommentedYamlConfigHandle(configPath, logger, nodes, migrator);
}
}
}

View File

@ -0,0 +1,47 @@
package com.onarandombox.MultiverseCore.configuration.handle;
import com.onarandombox.MultiverseCore.configuration.migration.ConfigMigrator;
import com.onarandombox.MultiverseCore.configuration.node.NodeGroup;
import org.bukkit.configuration.ConfigurationSection;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.logging.Logger;
/**
* Configuration handle for a single configuration section.
*/
public class ConfigurationSectionHandle extends GenericConfigHandle<ConfigurationSection> {
public static Builder<? extends Builder> builder(@NotNull ConfigurationSection configurationSection) {
return new Builder<>(configurationSection);
}
public ConfigurationSectionHandle(@NotNull ConfigurationSection configurationSection,
@Nullable Logger logger,
@Nullable NodeGroup nodes,
@Nullable ConfigMigrator migrator) {
super(logger, nodes, migrator);
this.config = configurationSection;
}
/**
* Builder for {@link ConfigurationSectionHandle}.
*
* @param <B> The builder type.
*/
public static class Builder<B extends Builder<B>> extends GenericConfigHandle.Builder<ConfigurationSection, B> {
private final ConfigurationSection configurationSection;
protected Builder(@NotNull ConfigurationSection configurationSection) {
this.configurationSection = configurationSection;
}
/**
* {@inheritDoc}
*/
@Override
public @NotNull ConfigurationSectionHandle build() {
return new ConfigurationSectionHandle(configurationSection, logger, nodes, migrator);
}
}
}

View File

@ -0,0 +1,126 @@
package com.onarandombox.MultiverseCore.configuration.handle;
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.util.logging.Logger;
import com.dumptruckman.minecraft.util.Logging;
import com.onarandombox.MultiverseCore.configuration.migration.ConfigMigrator;
import com.onarandombox.MultiverseCore.configuration.node.NodeGroup;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.plugin.Plugin;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Generic configuration handle for file based configurations.
* @param <C> The configuration type.
*/
abstract class FileConfigHandle<C extends FileConfiguration> extends GenericConfigHandle<C> {
protected final @NotNull Path configPath;
protected final @NotNull File configFile;
protected FileConfigHandle(@NotNull Path configPath, @Nullable Logger logger, @Nullable NodeGroup nodes, @Nullable ConfigMigrator migrator) {
super(logger, nodes, migrator);
this.configPath = configPath;
this.configFile = configPath.toFile();
}
/**
* {@inheritDoc}
*/
@Override
public boolean load() {
if (!createConfigFile()) {
Logging.severe("Failed to create config file: %s", configFile.getName());
return false;
}
if (!loadConfigObject()) {
Logging.severe("Failed to load config file: %s", configFile.getName());
return false;
}
migrateConfig();
setUpNodes();
return true;
}
/**
* Create a new config file if file does not exist
*
* @return True if file exist or created successfully, otherwise false.
*/
protected boolean createConfigFile() {
if (configFile.exists()) {
return true;
}
try {
if (!configFile.createNewFile()) {
return false;
}
Logging.info("Created new config file: %s", configFile.getName());
} catch (IOException e) {
return false;
}
return true;
}
/**
* Loads the configuration object.
*
* @return True if the configuration was loaded successfully, false otherwise.
*/
protected abstract boolean loadConfigObject();
/**
* Saves the configuration.
*/
public abstract boolean save();
/**
* Checks if the configuration is loaded.
*
* @return True if the configuration is loaded, false otherwise.
*/
public boolean isLoaded() {
return config != null;
}
/**
* Gets the configuration.
*
* @return The configuration.
*/
public C getConfig() {
return config;
}
/**
* Abstract builder for {@link FileConfigHandle}.
*
* @param <C> The configuration type.
* @param <B> The builder type.
*/
public static abstract class Builder<C extends FileConfiguration, B extends Builder<C, B>> extends GenericConfigHandle.Builder<C, B> {
protected @NotNull Path configPath;
protected Builder(@NotNull Path configPath) {
this.configPath = configPath;
}
/**
* Builds the configuration handle.
*
* @return The configuration handle.
*/
public abstract @NotNull FileConfigHandle<C> build();
@SuppressWarnings("unchecked")
protected B self() {
return (B) this;
}
}
}

View File

@ -0,0 +1,206 @@
package com.onarandombox.MultiverseCore.configuration.handle;
import com.onarandombox.MultiverseCore.configuration.migration.ConfigMigrator;
import com.onarandombox.MultiverseCore.configuration.node.ConfigNodeNotFoundException;
import com.onarandombox.MultiverseCore.configuration.node.NodeGroup;
import com.onarandombox.MultiverseCore.configuration.node.NodeSerializer;
import com.onarandombox.MultiverseCore.configuration.node.ValueNode;
import io.vavr.control.Option;
import io.vavr.control.Try;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.plugin.Plugin;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.logging.Logger;
/**
* Generic configuration handle for all ConfigurationSection types.
*/
public abstract class GenericConfigHandle<C extends ConfigurationSection> {
protected final @Nullable Logger logger;
protected final @Nullable NodeGroup nodes;
protected final @Nullable ConfigMigrator migrator;
protected C config;
public GenericConfigHandle(@Nullable Logger logger, @Nullable NodeGroup nodes, @Nullable ConfigMigrator migrator) {
this.logger = logger;
this.nodes = nodes;
this.migrator = migrator;
}
/**
* Loads the configuration.
*
* @return True if the configuration was loaded successfully, false otherwise.
*/
public boolean load() {
migrateConfig();
setUpNodes();
return true;
}
/**
* Migrates the configuration.
*/
protected void migrateConfig() {
if (migrator != null) {
migrator.migrate(config);
}
}
/**
* Sets up the nodes.
*/
protected void setUpNodes() {
if (nodes == null || nodes.isEmpty()) {
return;
}
nodes.forEach(node -> {
if (node instanceof ValueNode valueNode) {
set(valueNode, get(valueNode));
}
});
}
/**
* Gets the value of a node, if the node has a default value, it will be returned if the node is not found.
* @param name The name of the node.
* @return The value of the node.
*/
public Try<Object> get(@Nullable String name) {
return nodes.findNode(name, ValueNode.class)
.toTry(() -> new ConfigNodeNotFoundException(name))
.map(node -> get((ValueNode<Object>) node));
}
/**
* Gets the value of a node, if the node has a default value, it will be returned if the node is not found.
*
* @param node The node to get the value of.
* @return The value of the node.
*/
public <T> T get(@NotNull ValueNode<T> node) {
if (node.getSerializer() == null) {
return config.getObject(node.getPath(), node.getType(), node.getDefaultValue());
}
return node.getSerializer().deserialize(config.get(node.getPath(), node.getDefaultValue()), node.getType());
}
/**
* Sets the value of a node, if the validator is not null, it will be tested first.
*
* @param name The name of the node.
* @param value The value to set.
* @return True if the value was set, false otherwise.
*/
public Try<Void> set(@Nullable String name, Object value) {
return nodes.findNode(name, ValueNode.class)
.toTry(() -> new ConfigNodeNotFoundException(name))
.flatMap(node -> set(node, value));
}
/**
* Sets the value of a node, if the validator is not null, it will be tested first.
*
* @param node The node to set the value of.
* @param value The value to set.
* @return True if the value was set, false otherwise.
* @param <T> The type of the node value.
*/
public <T> Try<Void> set(@NotNull ValueNode<T> node, T value) {
return node.validate(value).map(ignore -> {
T oldValue = get(node);
if (node.getSerializer() != null) {
var serialized = node.getSerializer().serialize(value, node.getType());
config.set(node.getPath(), serialized);
} else {
config.set(node.getPath(), value);
}
node.onSetValue(oldValue, get(node));
return null;
});
}
/**
* Sets the default value of a node.
*
* @param node The node to set the default value of.
*/
public void setDefault(@NotNull ValueNode node) {
config.set(node.getPath(), node.getDefaultValue());
}
/**
* Abstract builder for {@link GenericConfigHandle}.
*
* @param <C> The configuration type.
* @param <B> The builder type.
*/
public static abstract class Builder<C extends ConfigurationSection, B extends GenericConfigHandle.Builder<C, B>> {
protected @Nullable Logger logger;
protected @Nullable NodeGroup nodes;
protected @Nullable ConfigMigrator migrator;
protected Builder() {}
/**
* Sets the logger.
*
* @param logger The logger.
* @return The builder.
*/
public B logger(@Nullable Logger logger) {
this.logger = logger;
return self();
}
/**
* Sets the logger.
*
* @param plugin The plugin to get the logger from.
* @return The builder.
*/
public B logger(Plugin plugin) {
this.logger = plugin.getLogger();
return self();
}
/**
* Sets the nodes.
*
* @param nodes The nodes.
* @return The builder.
*/
public B nodes(@Nullable NodeGroup nodes) {
this.nodes = nodes;
return self();
}
/**
* Sets the migrator.
*
* @param migrator The migrator.
* @return The builder.
*/
public B migrator(@Nullable ConfigMigrator migrator) {
this.migrator = migrator;
return self();
}
/**
* Builds the configuration handle.
*
* @return The configuration handle.
*/
public abstract @NotNull GenericConfigHandle<C> build();
@SuppressWarnings("unchecked")
protected B self() {
return (B) this;
}
}
}

View File

@ -0,0 +1,79 @@
package com.onarandombox.MultiverseCore.configuration.handle;
import java.io.IOException;
import java.nio.file.Path;
import java.util.logging.Logger;
import com.onarandombox.MultiverseCore.configuration.migration.ConfigMigrator;
import com.onarandombox.MultiverseCore.configuration.node.NodeGroup;
import com.onarandombox.MultiverseCore.configuration.node.ValueNode;
import org.bukkit.configuration.InvalidConfigurationException;
import org.bukkit.configuration.file.YamlConfiguration;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Configuration handle for YAML files.
*/
public class YamlConfigHandle extends FileConfigHandle<YamlConfiguration> {
/**
* Creates a new builder for {@link YamlConfigHandle}.
*
* @param configPath The path to the config file.
* @return The builder.
*/
public static @NotNull Builder<? extends Builder> builder(@NotNull Path configPath) {
return new Builder<>(configPath);
}
protected YamlConfigHandle(@NotNull Path configPath, @Nullable Logger logger, @Nullable NodeGroup nodes, @Nullable ConfigMigrator migrator) {
super(configPath, logger, nodes, migrator);
}
/**
* {@inheritDoc}
*/
@Override
protected boolean loadConfigObject() {
config = new YamlConfiguration();
try {
config.load(configFile);
} catch (IOException | InvalidConfigurationException e) {
return false;
}
return true;
}
/**
* {@inheritDoc}
*/
@Override
public boolean save() {
try {
config.save(configFile);
} catch (IOException e) {
return false;
}
return true;
}
/**
* Builder for {@link YamlConfigHandle}.
* @param <B> The type of the builder.
*/
public static class Builder<B extends Builder<B>> extends FileConfigHandle.Builder<YamlConfiguration, B> {
protected Builder(@NotNull Path configPath) {
super(configPath);
}
/**
* {@inheritDoc}
*/
@Override
public @NotNull YamlConfigHandle build() {
return new YamlConfigHandle(configPath, logger, nodes, migrator);
}
}
}

View File

@ -0,0 +1,29 @@
package com.onarandombox.MultiverseCore.configuration.migration;
import co.aikar.commands.ACFUtil;
import com.dumptruckman.minecraft.util.Logging;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.FileConfiguration;
/**
* Single migrator action that converts a string value to a boolean.
*/
public class BooleanMigratorAction implements MigratorAction {
public static BooleanMigratorAction of(String path) {
return new BooleanMigratorAction(path);
}
private final String path;
protected BooleanMigratorAction(String path) {
this.path = path;
}
@Override
public void migrate(ConfigurationSection config) {
config.set(path, ACFUtil.isTruthy(config.getString(path, "")));
Logging.info("Converted %s to boolean %s", path, config.getBoolean(path));
}
}

View File

@ -0,0 +1,91 @@
package com.onarandombox.MultiverseCore.configuration.migration;
import java.util.ArrayList;
import java.util.List;
import com.dumptruckman.minecraft.util.Logging;
import com.onarandombox.MultiverseCore.configuration.node.ValueNode;
import io.github.townyadvanced.commentedconfiguration.setting.TypedValueNode;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.FileConfiguration;
/**
* Helper class for migrating configs to the latest config version.
*/
public class ConfigMigrator {
/**
* Creates a new builder for a ConfigMigrator.
*
* @param versionNode The node that stores the version number of the config.
* Default value should be the current latest version number.
* @return The builder instance.
*/
public static Builder builder(ValueNode<Double> versionNode) {
return new Builder(versionNode);
}
private final ValueNode<Double> versionNode;
private final List<VersionMigrator> versionMigrators;
protected ConfigMigrator(ValueNode<Double> versionNode, List<VersionMigrator> versionMigrators) {
this.versionNode = versionNode;
this.versionMigrators = versionMigrators;
}
/**
* Migrates the config to the latest version if necessary.
*
* @param config The target settings instance to migrate.
*/
public void migrate(ConfigurationSection config) {
double versionNumber = config.getDouble(versionNode.getPath());
for (VersionMigrator versionMigrator : versionMigrators) {
if (versionNumber < versionMigrator.getVersion()) {
Logging.info("Migrating config from version %s to %s...", versionNumber, versionMigrator.getVersion());
versionMigrator.migrate(config);
}
}
// Set the version number to the latest version number
config.set(versionNode.getPath(), versionNode.getDefaultValue());
}
/**
* A builder for a ConfigMigrator.
*/
public static class Builder {
private final ValueNode<Double> versionNode;
private final List<VersionMigrator> versionMigrators;
/**
* Creates a new builder for a ConfigMigrator.
*
* @param versionNode The node that stores the version number of the config.
* Default value should be the current latest version number.
*/
public Builder(ValueNode<Double> versionNode) {
this.versionNode = versionNode;
this.versionMigrators = new ArrayList<>();
}
/**
* Adds a version migrator to the list of migrators.
*
* @param versionMigrator The migrator to add.
* @return The builder instance.
*/
public Builder addVersionMigrator(VersionMigrator versionMigrator) {
versionMigrators.add(versionMigrator);
return this;
}
/**
* Builds the ConfigMigrator.
*
* @return The built ConfigMigrator.
*/
public ConfigMigrator build() {
return new ConfigMigrator(versionNode, versionMigrators);
}
}
}

View File

@ -0,0 +1,28 @@
package com.onarandombox.MultiverseCore.configuration.migration;
import co.aikar.commands.ACFUtil;
import com.dumptruckman.minecraft.util.Logging;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.FileConfiguration;
/**
* Single migrator action that converts a string value to an integer.
*/
public class IntegerMigratorAction implements MigratorAction {
public static IntegerMigratorAction of(String path) {
return new IntegerMigratorAction(path);
}
private final String path;
public IntegerMigratorAction(String path) {
this.path = path;
}
@Override
public void migrate(ConfigurationSection config) {
config.set(path, ACFUtil.parseInt(config.getString(path)));
Logging.info("Converted %s to integer %s", path, config.getInt(path));
}
}

View File

@ -0,0 +1,37 @@
package com.onarandombox.MultiverseCore.configuration.migration;
import com.dumptruckman.minecraft.util.Logging;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.FileConfiguration;
/**
* Single migrator action that inverts a boolean value for a given path.
*/
public class InvertBoolMigratorAction implements MigratorAction {
/**
* Creates a new migrator action that inverts a boolean value for a given path.
*
* @param path The path to invert value of.
* @return The new migrator action.
*/
public static InvertBoolMigratorAction of(String path) {
return new InvertBoolMigratorAction(path);
}
private final String path;
protected InvertBoolMigratorAction(String path) {
this.path = path;
}
/**
* {@inheritDoc}
*/
@Override
public void migrate(ConfigurationSection config) {
boolean boolValue = !config.getBoolean(path);
config.set(path, boolValue);
Logging.info("Inverted %s to boolean %s", path, boolValue);
}
}

View File

@ -0,0 +1,17 @@
package com.onarandombox.MultiverseCore.configuration.migration;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.FileConfiguration;
/**
* A migrator action is a single action that is performed when migrating a config.
*/
public interface MigratorAction {
/**
* Performs the migration action.
*
* @param config The target settings instance to migrate.
*/
void migrate(ConfigurationSection config);
}

View File

@ -0,0 +1,45 @@
package com.onarandombox.MultiverseCore.configuration.migration;
import java.util.Optional;
import com.dumptruckman.minecraft.util.Logging;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.FileConfiguration;
/**
* Single migrator action that moves a value from one path to another.
*/
public class MoveMigratorAction implements MigratorAction {
/**
* Creates a new migrator action that moves a value from one path to another.
*
* @param fromPath The path to move value from.
* @param toPath The path to move value to.
* @return The new migrator action.
*/
public static MoveMigratorAction of(String fromPath, String toPath) {
return new MoveMigratorAction(fromPath, toPath);
}
private final String fromPath;
private final String toPath;
protected MoveMigratorAction(String fromPath, String toPath) {
this.fromPath = fromPath;
this.toPath = toPath;
}
/**
* {@inheritDoc}
*/
@Override
public void migrate(ConfigurationSection config) {
Optional.ofNullable(config.get(fromPath))
.ifPresent(value -> {
config.set(toPath, value);
config.set(fromPath, null);
Logging.config("Moved path %s to %s", fromPath, toPath);
});
}
}

View File

@ -0,0 +1,86 @@
package com.onarandombox.MultiverseCore.configuration.migration;
import java.util.ArrayList;
import java.util.List;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.FileConfiguration;
/**
* A version migrator is a collection of migrator actions that are performed when migrating a config to a specific version.
*/
public class VersionMigrator {
/**
* Creates a new builder for a VersionMigrator.
*
* @param version The version number of the config that this migrator migrates to.
* @return The builder instance.
*/
public static Builder builder(double version) {
return new Builder(version);
}
private final double version;
private final List<MigratorAction> actions;
protected VersionMigrator(double version, List<MigratorAction> actions) {
this.version = version;
this.actions = actions;
}
/**
* Performs all the migrator actions.
*
* @param config The target settings instance to migrate.
*/
public void migrate(ConfigurationSection config) {
actions.forEach(action -> action.migrate(config));
}
/**
* Gets the version number of the config that this migrator migrates to.
*
* @return The version number.
*/
public double getVersion() {
return version;
}
/**
* A builder for a VersionMigrator.
*/
public static class Builder {
private final double version;
private final List<MigratorAction> actions = new ArrayList<>();
/**
* Creates a new builder for a VersionMigrator.
*
* @param version The version number of the config that this migrator migrates to.
*/
public Builder(double version) {
this.version = version;
}
/**
* Adds a migrator action to the list of actions.
*
* @param action The action to add.
* @return The builder instance.
*/
public Builder addAction(MigratorAction action) {
actions.add(action);
return this;
}
/**
* Builds the VersionMigrator.
*
* @return The built VersionMigrator.
*/
public VersionMigrator build() {
return new VersionMigrator(version, actions);
}
}
}

View File

@ -0,0 +1,13 @@
package com.onarandombox.MultiverseCore.configuration.node;
import org.jetbrains.annotations.NotNull;
public interface CommentedNode extends Node {
/**
* Gets the comment of the node.
*
* @return The comment of the node.
*/
@NotNull String[] getComments();
}

View File

@ -0,0 +1,81 @@
package com.onarandombox.MultiverseCore.configuration.node;
import java.util.ArrayList;
import java.util.List;
import com.google.common.base.Strings;
import org.jetbrains.annotations.NotNull;
/**
* A node that represents a header without any value.
*/
public class ConfigHeaderNode implements CommentedNode {
/**
* Creates a new builder for a {@link ConfigHeaderNode}.
*
* @param path The path of the node.
* @return The new builder.
*/
public static @NotNull Builder<? extends Builder<?>> builder(String path) {
return new Builder<>(path);
}
private final @NotNull String path;
private final @NotNull String[] comments;
protected ConfigHeaderNode(@NotNull String path, @NotNull String[] comments) {
this.path = path;
this.comments = comments;
}
/**
* {@inheritDoc}
*/
@Override
public @NotNull String getPath() {
return path;
}
/**
* {@inheritDoc}
*/
@Override
public @NotNull String[] getComments() {
return comments;
}
public static class Builder<B extends Builder<B>> {
protected final @NotNull String path;
protected final @NotNull List<String> comments;
public Builder(@NotNull String path) {
this.path = path;
this.comments = new ArrayList<>();
}
/**
* Adds a comment line to the node.
*
* @param comment The comment to add.
* @return This builder.
*/
public @NotNull B comment(@NotNull String comment) {
if (!Strings.isNullOrEmpty(comment) && !comment.startsWith("#")) {
comment = "# " + comment;
}
comments.add(comment);
return (B) this;
}
/**
* Builds the node.
*
* @return The built node.
*/
public @NotNull ConfigHeaderNode build() {
return new ConfigHeaderNode(path, comments.toArray(new String[0]));
}
}
}

View File

@ -0,0 +1,189 @@
package com.onarandombox.MultiverseCore.configuration.node;
import java.util.function.BiConsumer;
import java.util.function.Function;
import io.vavr.control.Option;
import io.vavr.control.Try;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* A node that contains a value.
* @param <T> The type of the value.
*/
public class ConfigNode<T> extends ConfigHeaderNode implements ValueNode<T> {
/**
* Creates a new builder for a {@link ConfigNode}.
*
* @param path The path of the node.
* @param type The type of the value.
* @return The new builder.
* @param <T> The type of the value.
*/
public static @NotNull <T> ConfigNode.Builder<T, ? extends ConfigNode.Builder<T, ?>> builder(
@NotNull String path,
@NotNull Class<T> type
) {
return new ConfigNode.Builder<>(path, type);
}
protected final @Nullable String name;
protected final @NotNull Class<T> type;
protected final @Nullable T defaultValue;
protected final @Nullable NodeSerializer<T> serializer;
protected final @Nullable Function<T, Try<Void>> validator;
protected final @Nullable BiConsumer<T, T> onSetValue;
protected ConfigNode(
@NotNull String path,
@NotNull String[] comments,
@Nullable String name,
@NotNull Class<T> type,
@Nullable T defaultValue,
@Nullable NodeSerializer<T> serializer,
@Nullable Function<T, Try<Void>> validator,
@Nullable BiConsumer<T, T> onSetValue
) {
super(path, comments);
this.name = name;
this.type = type;
this.defaultValue = defaultValue;
this.serializer = serializer;
this.validator = validator;
this.onSetValue = onSetValue;
}
/**
* {@inheritDoc}
*/
@Override
public @NotNull Option<String> getName() {
return Option.of(name);
}
/**
* {@inheritDoc}
*/
@Override
public @NotNull Class<T> getType() {
return type;
}
/**
* {@inheritDoc}
*/
@Override
public @Nullable T getDefaultValue() {
return defaultValue;
}
public @Nullable NodeSerializer<T> getSerializer() {
return serializer;
}
/**
* {@inheritDoc}
*/
@Override
public Try<Void> validate(@Nullable T value) {
if (validator != null) {
return validator.apply(value);
}
return Try.success(null);
}
/**
* {@inheritDoc}
*/
@Override
public void onSetValue(@Nullable T oldValue, @Nullable T newValue) {
if (onSetValue != null) {
onSetValue.accept(oldValue, newValue);
}
}
/**
* Builder for {@link ConfigNode}.
*
* @param <T> The type of the value.
* @param <B> The type of the builder.
*/
public static class Builder<T, B extends ConfigNode.Builder<T, B>> extends ConfigHeaderNode.Builder<B> {
private static final NodeSerializer<?> ENUM_NODE_SERIALIZER = new EnumNodeSerializer<>();
protected @Nullable String name;
protected @NotNull final Class<T> type;
protected @Nullable T defaultValue;
protected @Nullable NodeSerializer<T> serializer;
protected @Nullable Function<T, Try<Void>> validator;
protected @Nullable BiConsumer<T, T> onSetValue;
/**
* Creates a new builder.
*
* @param path The path of the node.
* @param type The type of the value.
*/
protected Builder(@NotNull String path, @NotNull Class<T> type) {
super(path);
this.name = path;
this.type = type;
if (type.isEnum()) {
this.serializer = (NodeSerializer<T>) ENUM_NODE_SERIALIZER;
}
}
/**
* Sets the default value for this node.
*
* @param defaultValue The default value.
* @return This builder.
*/
public @NotNull B defaultValue(@NotNull T defaultValue) {
this.defaultValue = defaultValue;
return (B) this;
}
/**
* Sets the name of this node. Used for identifying the node from user input.
*
* @param name The name of this node.
* @return This builder.
*/
public @NotNull B name(@Nullable String name) {
this.name = name;
return (B) this;
}
public @NotNull B serializer(@NotNull NodeSerializer<T> serializer) {
this.serializer = serializer;
return (B) this;
}
public @NotNull B validator(@NotNull Function<T, Try<Void>> validator) {
this.validator = validator;
return (B) this;
}
/**
* Sets the action to be performed when the value is set.
*
* @param onSetValue The action to be performed.
* @return This builder.
*/
public @NotNull B onSetValue(@NotNull BiConsumer<T, T> onSetValue) {
this.onSetValue = onSetValue;
return (B) this;
}
/**
* {@inheritDoc}
*/
@Override
public @NotNull ConfigNode<T> build() {
return new ConfigNode<>(path, comments.toArray(new String[0]), name, type, defaultValue, serializer, validator, onSetValue);
}
}
}

View File

@ -0,0 +1,14 @@
package com.onarandombox.MultiverseCore.configuration.node;
import com.onarandombox.MultiverseCore.exceptions.MultiverseException;
import org.jetbrains.annotations.Nullable;
import static com.onarandombox.MultiverseCore.utils.MVCorei18n.CONFIG_NODE_NOTFOUND;
import static com.onarandombox.MultiverseCore.utils.message.MessageReplacement.replace;
public class ConfigNodeNotFoundException extends MultiverseException {
public ConfigNodeNotFoundException(@Nullable String nodeName) {
super(CONFIG_NODE_NOTFOUND.bundle("Config node not found: {node}", replace("{node}").with(nodeName)), null);
}
}

View File

@ -0,0 +1,16 @@
package com.onarandombox.MultiverseCore.configuration.node;
import com.dumptruckman.minecraft.util.Logging;
public class EnumNodeSerializer<T extends Enum<T>> implements NodeSerializer<T> {
@Override
public T deserialize(Object object, Class<T> type) {
return Enum.valueOf(type, object.toString().toUpperCase());
}
@Override
public Object serialize(T object, Class<T> type) {
return object.toString();
}
}

View File

@ -0,0 +1,13 @@
package com.onarandombox.MultiverseCore.configuration.node;
import org.jetbrains.annotations.NotNull;
public interface Node {
/**
* Gets the YAML path of the node.
*
* @return The YAML path of the node.
*/
@NotNull String getPath();
}

View File

@ -0,0 +1,146 @@
package com.onarandombox.MultiverseCore.configuration.node;
import io.github.townyadvanced.commentedconfiguration.setting.CommentedNode;
import io.vavr.control.Option;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
/**
* A collection of {@link CommentedNode}s, with mappings to nodes by name.
*/
public class NodeGroup implements Collection<Node> {
private final Collection<Node> nodes;
private final Map<String, Node> nodesMap;
public NodeGroup() {
this.nodes = new ArrayList<>();
this.nodesMap = new HashMap<>();
}
public NodeGroup(Collection<Node> nodes) {
this.nodes = nodes;
this.nodesMap = new HashMap<>(nodes.size());
nodes.forEach(this::addNodeIndex);
}
private void addNodeIndex(Node node) {
if (node instanceof ValueNode) {
((ValueNode<?>) node).getName().peek(name -> nodesMap.put(name, node));
}
}
private void removeNodeIndex(Node node) {
if (node instanceof ValueNode) {
((ValueNode<?>) node).getName().peek(nodesMap::remove);
}
}
/**
* Gets the names of all nodes in this group.
*
* @return The names of all nodes in this group.
*/
public Collection<String> getNames() {
return nodesMap.keySet();
}
/**
* Gets the node with the given name.
*
* @param name The name of the node to get.
* @return The node with the given name, or {@link Option.None} if no node with the given name exists.
*/
public Option<Node> findNode(String name) {
return Option.of(nodesMap.get(name));
}
/**
* Gets the node with the given name.
*
* @param name The name of the node to get.
* @param type The type of the node to get.
* @return The node with the given name, or {@link Option.None} if no node with the given name exists.
*/
public <T extends Node> Option<T> findNode(String name, Class<T> type) {
return Option.of(nodesMap.get(name)).map(node -> type.isAssignableFrom(node.getClass()) ? (T) node : null);
}
@Override
public int size() {
return nodes.size();
}
@Override
public boolean isEmpty() {
return nodes.isEmpty();
}
@Override
public boolean contains(Object o) {
return nodes.contains(o);
}
@NotNull
@Override
public Iterator<Node> iterator() {
return nodes.iterator();
}
@Override
public Object @NotNull [] toArray() {
return nodes.toArray();
}
@Override
public <T> T @NotNull [] toArray(T @NotNull [] ts) {
return nodes.toArray(ts);
}
@Override
public boolean add(Node node) {
if (nodes.add(node)) {
addNodeIndex(node);
return true;
}
return false;
}
@Override
public boolean remove(Object o) {
if (nodes.remove(o) && o instanceof CommentedNode) {
removeNodeIndex((Node) o);
return true;
}
return false;
}
@Override
public boolean containsAll(@NotNull Collection<?> collection) {
return nodes.containsAll(collection);
}
@Override
public boolean addAll(@NotNull Collection<? extends Node> collection) {
return nodes.addAll(collection);
}
@Override
public boolean removeAll(@NotNull Collection<?> collection) {
return nodes.removeAll(collection);
}
@Override
public boolean retainAll(@NotNull Collection<?> collection) {
return nodes.retainAll(collection);
}
@Override
public void clear() {
nodes.clear();
}
}

View File

@ -0,0 +1,6 @@
package com.onarandombox.MultiverseCore.configuration.node;
public interface NodeSerializer<T> {
T deserialize(Object object, Class<T> type);
Object serialize(T object, Class<T> type);
}

View File

@ -0,0 +1,53 @@
package com.onarandombox.MultiverseCore.configuration.node;
import io.vavr.control.Option;
import io.vavr.control.Try;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public interface ValueNode<T> extends Node {
/**
* Gets the name of this node. Used for identifying the node from user input.
*
* @return An {@link Option} containing the name of this node, or {@link Option.None} if the node has no name.
*/
@NotNull Option<String> getName();
/**
* Gets the class type {@link T} of the node value.
*
* @return The class type of the node value.
*/
@NotNull Class<T> getType();
/**
* Gets the default value with type {@link T} of the node.
*
* @return The default value of the node.
*/
@Nullable T getDefaultValue();
/**
* Gets the serializer for this node.
*
* @return The serializer for this node.
*/
@Nullable NodeSerializer<T> getSerializer();
/**
* Validates the value of this node.
*
* @param value The value to validate.
* @return True if the value is valid, false otherwise.
*/
Try<Void> validate(@Nullable T value);
/**
* Called when the value of this node is set.
*
* @param oldValue The old value.
* @param newValue The new value.
*/
void onSetValue(@Nullable T oldValue, @Nullable T newValue);
}

View File

@ -3,38 +3,44 @@ package com.onarandombox.MultiverseCore.destination;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
import co.aikar.commands.BukkitCommandIssuer;
import co.aikar.commands.CommandIssuer;
import com.onarandombox.MultiverseCore.MultiverseCore;
import com.onarandombox.MultiverseCore.api.Destination;
import com.onarandombox.MultiverseCore.api.DestinationInstance;
import com.onarandombox.MultiverseCore.api.SafeTTeleporter;
import com.onarandombox.MultiverseCore.api.Teleporter;
import com.onarandombox.MultiverseCore.teleportation.TeleportResult;
import jakarta.inject.Inject;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.permissions.Permission;
import org.bukkit.plugin.PluginManager;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jvnet.hk2.annotations.Service;
/**
* Provides destinations for teleportation.
*/
@Service
public class DestinationsProvider {
private static final String SEPARATOR = ":";
private static final String PERMISSION_PREFIX = "multiverse.teleport.";
private final MultiverseCore plugin;
private final PluginManager pluginManager;
private final SafeTTeleporter safeTTeleporter;
private final Map<String, Destination<?>> destinationMap;
/**
* Creates a new destinations provider.
*
* @param plugin The plugin.
*/
public DestinationsProvider(@NotNull MultiverseCore plugin) {
this.plugin = plugin;
@Inject
public DestinationsProvider(@NotNull PluginManager pluginManager, @NotNull SafeTTeleporter safeTTeleporter) {
this.pluginManager = pluginManager;
this.safeTTeleporter = safeTTeleporter;
this.destinationMap = new HashMap<>();
}
@ -49,7 +55,6 @@ public class DestinationsProvider {
}
private void registerDestinationPerms(@NotNull Destination<?> destination) {
PluginManager pluginManager = this.plugin.getServer().getPluginManager();
pluginManager.addPermission(new Permission(PERMISSION_PREFIX + "self." + destination.getIdentifier()));
pluginManager.addPermission(new Permission(PERMISSION_PREFIX + "other." + destination.getIdentifier()));
}
@ -125,14 +130,14 @@ public class DestinationsProvider {
* @param teleportee The teleportee.
* @param destination The destination.
*/
public void playerTeleport(@NotNull BukkitCommandIssuer teleporter,
public CompletableFuture<TeleportResult> playerTeleportAsync(@NotNull BukkitCommandIssuer teleporter,
@NotNull Player teleportee,
@NotNull ParsedDestination<?> destination
) {
if (!checkTeleportPermissions(teleporter, teleportee, destination)) {
return;
return CompletableFuture.completedFuture(TeleportResult.FAIL_PERMISSION);
}
teleport(teleporter, teleportee, destination);
return teleportAsync(teleporter, teleportee, destination);
}
/**
@ -142,15 +147,15 @@ public class DestinationsProvider {
* @param teleportee The teleportee.
* @param destination The destination.
*/
public void teleport(@NotNull BukkitCommandIssuer teleporter,
@NotNull Entity teleportee,
@NotNull ParsedDestination<?> destination
public CompletableFuture<TeleportResult> teleportAsync(@NotNull BukkitCommandIssuer teleporter,
@NotNull Entity teleportee,
@NotNull ParsedDestination<?> destination
) {
Teleporter teleportHandler = destination.getDestination().getTeleporter();
if (teleportHandler == null) {
teleportHandler = this.plugin.getSafeTTeleporter();
teleportHandler = safeTTeleporter;
}
teleportHandler.teleport(teleporter, teleportee, destination);
return teleportHandler.teleportAsync(teleporter, teleportee, destination);
}
/**

View File

@ -2,6 +2,10 @@ package com.onarandombox.MultiverseCore.destination;
import com.onarandombox.MultiverseCore.api.Destination;
import com.onarandombox.MultiverseCore.api.DestinationInstance;
import org.bukkit.Location;
import org.bukkit.entity.Entity;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* A parsed destination.
@ -23,6 +27,34 @@ public class ParsedDestination<S extends DestinationInstance> {
this.destinationInstance = destinationInstance;
}
/**
* Shortcut for {@link Destination#getIdentifier()}.
*
* @return The destination identifier.
*/
public @NotNull String getIdentifier() {
return destination.getIdentifier();
}
/**
* Shortcut for {@link DestinationInstance#getLocation(Entity)}.
*
* @param teleportee The entity to teleport.
* @return The location to teleport to.
*/
public @Nullable Location getLocation(@NotNull Entity teleportee) {
return destinationInstance.getLocation(teleportee);
}
/**
* Shortcut for {@link DestinationInstance#getFinerPermissionSuffix()}.
*
* @return The finer permission suffix.
*/
public @Nullable String getFinerPermissionSuffix() {
return destinationInstance.getFinerPermissionSuffix();
}
/**
* Gets the destination.
*
@ -48,6 +80,6 @@ public class ParsedDestination<S extends DestinationInstance> {
*/
@Override
public String toString() {
return destination.getIdentifier() + ":" + destinationInstance.serialise();
return getIdentifier() + ":" + destinationInstance.serialise();
}
}

View File

@ -3,23 +3,23 @@ package com.onarandombox.MultiverseCore.destination.core;
import java.util.Collection;
import co.aikar.commands.BukkitCommandIssuer;
import com.onarandombox.MultiverseCore.MultiverseCore;
import com.onarandombox.MultiverseCore.anchor.AnchorManager;
import com.onarandombox.MultiverseCore.api.Destination;
import com.onarandombox.MultiverseCore.api.Teleporter;
import jakarta.inject.Inject;
import org.bukkit.Location;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jvnet.hk2.annotations.Service;
@Service
public class AnchorDestination implements Destination<AnchorDestinationInstance> {
private final MultiverseCore plugin;
/**
* Constructor.
*
* @param plugin The MultiverseCore plugin.
*/
public AnchorDestination(MultiverseCore plugin) {
this.plugin = plugin;
private final AnchorManager anchorManager;
@Inject
public AnchorDestination(AnchorManager anchorManager) {
this.anchorManager = anchorManager;
}
/**
@ -35,7 +35,7 @@ public class AnchorDestination implements Destination<AnchorDestinationInstance>
*/
@Override
public @Nullable AnchorDestinationInstance getDestinationInstance(@Nullable String destinationParams) {
Location anchorLocation = this.plugin.getAnchorManager().getAnchorLocation(destinationParams);
Location anchorLocation = this.anchorManager.getAnchorLocation(destinationParams);
if (anchorLocation == null) {
return null;
}
@ -47,7 +47,7 @@ public class AnchorDestination implements Destination<AnchorDestinationInstance>
*/
@Override
public @NotNull Collection<String> suggestDestinations(@NotNull BukkitCommandIssuer issuer, @Nullable String destinationParams) {
return this.plugin.getAnchorManager().getAnchors(issuer.getPlayer());
return this.anchorManager.getAnchors(issuer.getPlayer());
}
/**

View File

@ -12,7 +12,9 @@ import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jvnet.hk2.annotations.Service;
@Service
public class BedDestination implements Destination<BedDestinationInstance> {
public static final String OWN_BED_STRING = "playerbed";

View File

@ -4,24 +4,24 @@ import java.util.Collection;
import java.util.Collections;
import co.aikar.commands.BukkitCommandIssuer;
import com.onarandombox.MultiverseCore.MultiverseCore;
import com.onarandombox.MultiverseCore.api.Destination;
import com.onarandombox.MultiverseCore.api.MVWorld;
import com.onarandombox.MultiverseCore.api.MVWorldManager;
import com.onarandombox.MultiverseCore.api.Teleporter;
import jakarta.inject.Inject;
import org.bukkit.Location;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jvnet.hk2.annotations.Service;
@Service
public class CannonDestination implements Destination<CannonDestinationInstance> {
private final MultiverseCore plugin;
/**
* Constructor.
*
* @param plugin The MultiverseCore plugin.
*/
public CannonDestination(MultiverseCore plugin) {
this.plugin = plugin;
private final MVWorldManager worldManager;
@Inject
public CannonDestination(MVWorldManager worldManager) {
this.worldManager = worldManager;
}
/**
@ -53,7 +53,7 @@ public class CannonDestination implements Destination<CannonDestinationInstance>
return null;
}
MVWorld world = this.plugin.getMVWorldManager().getMVWorld(worldName);
MVWorld world = this.worldManager.getMVWorld(worldName);
if (world == null) {
return null;
}

View File

@ -4,24 +4,24 @@ import java.util.Collection;
import java.util.Collections;
import co.aikar.commands.BukkitCommandIssuer;
import com.onarandombox.MultiverseCore.MultiverseCore;
import com.onarandombox.MultiverseCore.api.Destination;
import com.onarandombox.MultiverseCore.api.MVWorld;
import com.onarandombox.MultiverseCore.api.MVWorldManager;
import com.onarandombox.MultiverseCore.api.Teleporter;
import jakarta.inject.Inject;
import org.bukkit.Location;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jvnet.hk2.annotations.Service;
@Service
public class ExactDestination implements Destination<ExactDestinationInstance> {
private final MultiverseCore plugin;
/**
* Constructor.
*
* @param plugin The MultiverseCore plugin.
*/
public ExactDestination(MultiverseCore plugin) {
this.plugin = plugin;
private final MVWorldManager worldManager;
@Inject
public ExactDestination(MVWorldManager worldManager) {
this.worldManager = worldManager;
}
/**
@ -49,7 +49,7 @@ public class ExactDestination implements Destination<ExactDestinationInstance> {
return null;
}
MVWorld world = this.plugin.getMVWorldManager().getMVWorld(worldName);
MVWorld world = this.worldManager.getMVWorld(worldName);
if (world == null) {
return null;
}

View File

@ -11,7 +11,9 @@ import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jvnet.hk2.annotations.Service;
@Service
public class PlayerDestination implements Destination<PlayerDestinationInstance> {
/**
* Creates a new instance of the PlayerDestination.

View File

@ -4,24 +4,26 @@ import java.util.Collection;
import java.util.Collections;
import co.aikar.commands.BukkitCommandIssuer;
import com.onarandombox.MultiverseCore.MultiverseCore;
import com.onarandombox.MultiverseCore.api.Destination;
import com.onarandombox.MultiverseCore.api.LocationManipulation;
import com.onarandombox.MultiverseCore.api.MVWorld;
import com.onarandombox.MultiverseCore.api.MVWorldManager;
import com.onarandombox.MultiverseCore.api.Teleporter;
import jakarta.inject.Inject;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jvnet.hk2.annotations.Service;
@Service
public class WorldDestination implements Destination<WorldDestinationInstance> {
private final MultiverseCore plugin;
private final MVWorldManager worldManager;
private final LocationManipulation locationManipulation;
/**
* Constructor.
*
* @param plugin The MultiverseCore plugin.
*/
public WorldDestination(MultiverseCore plugin) {
this.plugin = plugin;
@Inject
public WorldDestination(MVWorldManager worldManager, LocationManipulation locationManipulation) {
this.worldManager = worldManager;
this.locationManipulation = locationManipulation;
}
/**
@ -43,13 +45,13 @@ public class WorldDestination implements Destination<WorldDestinationInstance> {
}
String worldName = items[0];
MVWorld world = this.plugin.getMVWorldManager().getMVWorld(worldName);
MVWorld world = this.worldManager.getMVWorld(worldName);
if (world == null) {
return null;
}
String direction = (items.length == 2) ? items[1] : null;
float yaw = direction != null ? this.plugin.getLocationManipulation().getYaw(direction) : -1;
float yaw = direction != null ? this.locationManipulation.getYaw(direction) : -1;
return new WorldDestinationInstance(world, direction, yaw);
}

View File

@ -13,7 +13,7 @@ public interface SendHandler {
/**
* Sends all the content to the given command sender.
*
* @param sender The target which the content will be displayed to.
* @param issuer The target which the content will be displayed to.
* @param content The content to display.
*/
void send(@NotNull BukkitCommandIssuer issuer, @NotNull List<String> content);

View File

@ -1,18 +1,23 @@
package com.onarandombox.MultiverseCore.economy;
import com.onarandombox.MultiverseCore.api.MVWorld;
import jakarta.inject.Inject;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import org.jetbrains.annotations.Nullable;
import org.jvnet.hk2.annotations.Service;
/**
* Multiverse's Friendly Economist. This is used to deal with external economies and also item costs for stuff in MV.
*/
@Service
public class MVEconomist {
private final VaultHandler vaultHandler;
@Inject
public MVEconomist(Plugin plugin) {
vaultHandler = new VaultHandler(plugin);
}
@ -87,6 +92,35 @@ public class MVEconomist {
return "Sorry, you don't have enough " + (isItemCurrency(currency) ? "items" : "funds") + ". " + message;
}
/**
* Pays for a given amount of currency either from the player's economy account or inventory if the currency.
*
* @param player the player to deposit currency into.
* @param world the world to take entry fee from.
*/
public void payEntryFee(Player player, MVWorld world) {
payEntryFee(player, world.getPrice(), world.getCurrency());
}
/**
* Pays for a given amount of currency either from the player's economy account or inventory if the currency
*
* @param player the player to take currency from.
* @param price the amount to take.
* @param currency the type of currency.
*/
public void payEntryFee(Player player, double price, Material currency) {
if (price == 0D) {
return;
}
if (price < 0) {
this.deposit(player, -price, currency);
} else {
this.withdraw(player, price, currency);
}
}
/**
* Deposits a given amount of currency either into the player's economy account or inventory if the currency
* is not null.

View File

@ -0,0 +1,52 @@
package com.onarandombox.MultiverseCore.exceptions;
import com.onarandombox.MultiverseCore.commandtools.MVCommandIssuer;
import com.onarandombox.MultiverseCore.utils.message.Message;
import org.jetbrains.annotations.Nullable;
/**
* A base exception for Multiverse.
* <br/>
* {@link #getMVMessage()} provides access to a {@link Message} which can be used to provide a localized message. See
* {@link MVCommandIssuer#sendInfo(Message)}.
*/
public class MultiverseException extends Exception {
private final @Nullable Message message;
/**
* Creates a new exception with the given message and cause.
* <br/>
* If the message is not null, this exception will also contain a {@link Message} which can be accessed via
* {@link #getMVMessage()}. This message will just be the given message wrapped in a {@link Message}.
*
* @param message The message for the exception
* @param cause The cause of the exception
*/
public MultiverseException(@Nullable String message, @Nullable Throwable cause) {
this(message != null ? Message.of(message) : null, cause);
}
/**
* Creates a new exception with the given message and cause.
* <br/>
* If the message is not null, this exception will also contain a String message which can be accessed via
* {@link #getMessage()}. This message will just be the given message formatted without locale support.
*
* @param message The message for the exception
* @param cause The cause of the exception
*/
public MultiverseException(@Nullable Message message, @Nullable Throwable cause) {
super(message != null ? message.formatted() : null, cause);
this.message = message;
}
/**
* Gets the {@link Message} for this exception.
*
* @return The message, or null if none was provided
*/
public final @Nullable Message getMVMessage() {
return message;
}
}

Some files were not shown because too many files have changed in this diff Show More