Compare commits

..
Author SHA1 Message Date
Ben Woo ee1bf8216e Better defaults and completions for ListConfigNode 2023-09-14 19:07:39 +08:00
Ben Woo 362d871ab8 Implement better suggestion and string parsing for config node 2023-09-14 16:48:07 +08:00
Ben Woo 738101d5d4 Fix imports from world manager merge 2023-09-14 11:40:08 +08:00
Ben Woo 782d58fcac Merge branch 'MV5' into ben/mv5/modify-command
# Conflicts:
#	src/main/java/org/mvplugins/multiverse/core/commandtools/MVCommandContexts.java
#	src/main/java/org/mvplugins/multiverse/core/world/MultiverseWorld.java
2023-09-14 11:36:01 +08:00
Ben Woo 94b1e77a98 Implement support for property modification with ConfigModifyType and ListValueNode 2023-09-13 18:21:14 +08:00
443 changed files with 13715 additions and 23893 deletions

No files matched your search

-1
View File
@@ -1 +0,0 @@
.gitea/CODEOWNERS linguist-detectable=false
-3
View File
@@ -1,3 +0,0 @@
# Pull requests request review from the eligible staff team(s).
# One approval from either an Admin or Developer satisfies branch protection.
* @trueog/Admins @trueog/Developers
-2
View File
@@ -1,2 +0,0 @@
github: Multiverse
open_collective: multiverse-plugins
+5 -5
View File
@@ -18,10 +18,10 @@ body:
When completing the report, please ensure all fields are filled with the correct details requested. Failure to do so will result in your issue being closed.
- type: input
id: multiverse-version-info
id: mutliverse-version-info
attributes:
label: '`/mv dumps` output'
description: Run `/mv dumps` in the console, then copy and paste the link from the output of the command into this box. If `/mv dumps` command is not available, update multiverse to v5.
label: '`/mv version -p` output'
description: Run `/mv version -p` in the console, then copy and paste the link from the output of the command into this box.
validations:
required: true
- type: input
@@ -29,7 +29,7 @@ body:
attributes:
label: Server logs
description: >
Include full startup log and console error (if any). upload all relevant logs to either https://pastes.dev
Include full startup log and console error (if any). upload all relevant logs to either https://paste.gg
or https://gist.github.com, save and the paste the link in this box.
validations:
required: true
@@ -54,7 +54,7 @@ body:
id: reproduce-steps
attributes:
label: Steps to reproduce
description: Provide detailed instructions to trigger the bug.
description: Provide detailed intructions to trigger the bug.
placeholder: |
Example:
1. Start a server with only multiverse installed.
@@ -1,39 +0,0 @@
name: 'Dispatch: Create Release'
on:
workflow_dispatch:
inputs:
release_type:
description: 'Stable or pre-release'
required: true
type: choice
options:
- prerelease
- release
version_bump:
description: 'Version bump type'
required: true
type: choice
options:
- major
- minor
- patch
jobs:
github_release:
uses: ./.github/workflows/call.github_release.yml
secrets: inherit
with:
release_mode: ${{ inputs.release_type }}
version_bump: ${{ inputs.version_bump }}
platform_uploads:
needs: github_release
if: needs.github_release.outputs.release_created == 'true'
uses: ./.github/workflows/call.platform_uploads.yml
secrets: inherit
with:
target_tag: ${{ needs.github_release.outputs.tag_name }}
upload_modrinth: 'true'
upload_dbo: 'false'
upload_hangar: 'false'
-55
View File
@@ -1,55 +0,0 @@
name: 'Dispatch: Deploy Javadoc'
on:
workflow_dispatch:
inputs:
target_tag:
description: 'Version to generate javadoc'
required: true
replace_latest:
description: 'Replace the latest folder'
type: boolean
jobs:
javadocs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
ref: ${{ inputs.target_tag }}
- uses: actions/setup-java@v4
with:
java-version: '21'
distribution: 'adopt'
cache: gradle
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4
- name: build javadoc
run: ./gradlew javadoc
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_VERSION: ${{ inputs.target_tag }}
- name: Copy javadoc to deploy folder
run: |
mkdir -p build/docs/javadoc-deploy/${{ inputs.target_tag }}
cp -r build/docs/javadoc/* build/docs/javadoc-deploy/${{ inputs.target_tag }}
- name: Copy javadoc to latest folder
if: inputs.replace_latest
run: |
mkdir -p build/docs/javadoc-deploy/latest
cp -r build/docs/javadoc/* build/docs/javadoc-deploy/latest
- name: Deploy javadoc to gh pages
uses: JamesIves/github-pages-deploy-action@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}
folder: build/docs/javadoc-deploy
branch: javadoc
target-folder: javadoc
clean: false
commit-message: Deploy javadoc for ${{ inputs.target_tag }}
+2 -2
View File
@@ -11,9 +11,9 @@ jobs:
pull-requests: write
continue-on-error: true
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v3
- uses: dbelyaev/action-checkstyle@v3
- uses: dbelyaev/action-checkstyle@v0.8.4
continue-on-error: true
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
+14 -54
View File
@@ -43,28 +43,28 @@ jobs:
echo "version_bump: ${{ inputs.version_bump }}"
echo "promote_from: ${{ inputs.promote_from }}"
- uses: actions/checkout@v4
- uses: actions/checkout@v3
with:
ref: ${{ inputs.promote_from }}
- uses: actions/setup-java@v4
- uses: actions/setup-java@v3
with:
java-version: '21'
java-version: '11'
distribution: 'adopt'
cache: gradle
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4
- name: Validate Gradle wrapper
uses: gradle/wrapper-validation-action@v1
- name: Build to test
id: test
run: ./gradlew clean build -x assemble -x shadowJar
- name: Build and test
uses: gradle/gradle-build-action@v2
with:
arguments: clean build -x assemble -x shadowJar
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Create release
id: release
if: steps.test.outcome == 'success'
uses: benwoo1110/semantic-release-action@v1
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
@@ -74,58 +74,18 @@ jobs:
- name: Publish package
if: steps.release.outputs.release_created == 'true'
run: >
./gradlew publish -x test
-PmultiverseUsername=${{ secrets.REPOSILITE_REPO_USERNAME }}
-PmultiversePassword=${{ secrets.REPOSILITE_REPO_PASSWORD }}
uses: gradle/gradle-build-action@v2
with:
arguments: publish -x test
env:
GITHUB_VERSION: ${{ steps.release.outputs.publish_version }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Build for upload
id: build
if: steps.release.outputs.release_created == 'true'
run: ./gradlew build -x test
env:
GITHUB_VERSION: ${{ steps.release.outputs.tag_name }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Upload release artifact
if: steps.build.outcome == 'success'
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.tag_name }}.jar
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 }}
- name: Generate javadoc
id: javadoc
if: steps.build.outcome == 'success'
run: ./gradlew javadoc -x test
env:
GITHUB_VERSION: ${{ steps.release.outputs.publish_version }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Upload javadoc
if: steps.javadoc.outcome == 'success'
run: |
mkdir -p build/docs/javadoc-deploy/${{ steps.release.outputs.publish_version }}
cp -r build/docs/javadoc/* build/docs/javadoc-deploy/${{ steps.release.outputs.publish_version }}
- name: Override latest javadoc for stable release
if: steps.javadoc.outcome == 'success' && steps.release.outputs.release_type == 'release'
run: |
mkdir -p build/docs/javadoc-deploy/latest
cp -r build/docs/javadoc/* build/docs/javadoc-deploy/latest
- name: Deploy javadoc to gh pages
if: steps.javadoc.outcome == 'success'
uses: JamesIves/github-pages-deploy-action@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}
folder: build/docs/javadoc-deploy
branch: javadoc
target-folder: javadoc
clean: false
commit-message: Deploy javadoc for ${{ steps.release.outputs.tag_name }}
@@ -107,9 +107,9 @@ jobs:
files: '["${{ github.workspace }}/${{ inputs.plugin_name }}-${{ steps.release-info.outputs.tag_name }}.jar"]'
name: ${{ steps.release-info.outputs.tag_name }}
changelog: ${{ steps.release-artifact.outputs.body }}
game_versions: 1.21.11, 1.21.10, 1.21.9, 1.21.8, 1.21.7, 1.21.6, 1.21.5, 1.21.4, 1.21.3, 1.21.2, 1.21.1, 1.21, 1.20.6, 1.20.5, 1.20.4, 1.20.3, 1.20.2, 1.20.1, 1.20, 1.19.4, 1.19.3, 1.19.2, 1.19.1, 1.19, 1.18.2
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, purpur
loaders: bukkit, spigot, paper
dependencies: ${{ inputs.modrinth_dependencies }}
- name: Upload to DBO
@@ -121,7 +121,7 @@ jobs:
changelog: ${{ steps.release-artifact.outputs.body }}
changelog_type: markdown
display_name: ${{ steps.release-info.outputs.tag_name }}
game_versions: 1.21.11, 1.21.10, 1.21.9, 1.21.8, 1.21.7, 1.21.6, 1.21.5, 1.21.4, 1.21.3, 1.21.2, 1.21.1, 1.21, 1.20.6, 1.20.5, 1.20.4, 1.20.3, 1.20.2, 1.20.1, 1.20, 1.19.4, 1.19.3, 1.19.2, 1.19.1, 1.19, 1.18.2
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
@@ -136,5 +136,5 @@ jobs:
channel: ${{ steps.parse-release-type.outputs.release_type }}
files: '[{"path": "${{ github.workspace }}/${{ inputs.plugin_name }}-${{ steps.release-info.outputs.tag_name }}.jar", "platforms": ["PAPER"]}]'
description: ${{ steps.release-artifact.outputs.body }}
platform_dependencies: '{"PAPER": ["1.18.2-1.21.11"]}'
platform_dependencies: '{"PAPER": ["1.13-1.20.1"]}'
plugin_dependencies: ${{ inputs.hangar_plugin_dependencies }}
+10 -18
View File
@@ -9,40 +9,32 @@ on:
type: string
jobs:
test-and-artifact:
test:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v3
- uses: actions/setup-java@v4
- uses: actions/setup-java@v3
with:
java-version: '21'
java-version: '17'
distribution: 'adopt'
cache: gradle
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4
- name: Validate Gradle wrapper
uses: gradle/wrapper-validation-action@v1
- name: Run unit tests
run: ./gradlew build
uses: gradle/gradle-build-action@v2
with:
arguments: build
env:
GITHUB_VERSION: pr${{ github.event.pull_request.number }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Artifact output
uses: actions/upload-artifact@v4
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
comment-artifact:
needs: test-and-artifact
runs-on: ubuntu-latest
permissions:
pull-requests: write
steps:
- uses: benwoo1110/artifact-comment-action@v1
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
-4
View File
@@ -1,4 +0,0 @@
kotlin version: 2.0.21
error message: The daemon has terminated unexpectedly on startup attempt #1 with error code: 0. The daemon process output:
1. Kotlin compile daemon is ready
+15 -16
View File
@@ -1,21 +1,22 @@
<p align="center">
<img src="config/multiverse-banner.png" alt="Multiverse Logo">
<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://raw.githubusercontent.com/intergrav/devins-badges/refs/heads/v3/assets/cozy/available/bukkit_vector.svg)](https://dev.bukkit.org/projects/multiverse-core)
[![Spigot](https://raw.githubusercontent.com/intergrav/devins-badges/refs/heads/v3/assets/cozy/available/spigot_vector.svg)](https://www.spigotmc.org/resources/multiverse-core.390/)
[![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/)
[![Release](https://img.shields.io/nexus/r/com.onarandombox.multiversecore/multiverse-core?server=https%3A%2F%2Frepo.onarandombox.com&label=release&color=dark-green)](https://dev.bukkit.org/projects/multiverse-core)
[![Dev builds](https://img.shields.io/nexus/s/com.onarandombox.multiversecore/multiverse-core?server=https%3A%2F%2Frepo.onarandombox.com&label=prerelease&color=orange)](https://ci.onarandombox.com/job/Multiverse-Core/)
[![Discord](https://img.shields.io/discord/325459248047980545?label=discord&logo=discord)](https://discord.gg/NZtfKky)
[![Support us on Patreon](https://img.shields.io/badge/donate-patreon-white?logo=patreon)](https://patreon.com/dumptruckman)
[![License](https://img.shields.io/github/license/Multiverse/Multiverse-Core?color=blue)](LICENSE.md)
[![Release](https://img.shields.io/github/v/release/multiverse/multiverse-core)](https://github.com/Multiverse/Multiverse-Core/releases/latest)
[![Pre-Release](https://img.shields.io/github/v/release/multiverse/multiverse-core?include_prereleases&label=Pre-release)](https://github.com/Multiverse/Multiverse-Core/releases)
[![Discord](https://img.shields.io/discord/325459248047980545?label=Discord&logo=discord)](https://discord.gg/NZtfKky)
[![Donate on Github Sponsor](https://img.shields.io/badge/Github%20Sponsor-Donate-pink?logo=githubsponsors)](https://github.com/sponsors/Multiverse)
[![Donate on Open Collective](https://img.shields.io/badge/Open%20Collective-Donate-blue?style=flat&logo=opencollective)](https://opencollective.com/multiverse-plugins)
# About
[Multiverse](https://modrinth.com/plugin/multiverse-core) was created at the dawn of Bukkit multiworld support. It has since then grown into a **complete world management solution!** Multiverse provides the easiest to use world management solution for your Minecraft server, big or small, and with great addons like [Portals](https://dev.bukkit.org/projects/multiverse-portals) and [NetherPortals](https://dev.bukkit.org/projects/multiverse-netherportals/), what's not to love!
[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!
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!
@@ -26,24 +27,22 @@ Now it's time to create your very own Multiverse server, do check out our [Wiki]
* [Multiverse-Inventories](https://github.com/Multiverse/Multiverse-Inventories) -> Have separated players stats and inventories per world or per group of worlds.
* [Multiverse-SignPortals](https://github.com/Multiverse/Multiverse-SignPortals) -> Signs as teleporters!
## Usage Guide
We have a cool new website hosting our Wiki: https://mvplugins.org
## Building
Simply build the source with Gradle:
```
./gradlew build
```
More details are available on the [build instructions wiki page](https://github.com/Multiverse/Multiverse-Core/wiki/Building).
## Contributing
**Want to help improve Multiverse?** There are several ways you can support and contribute to the project.
* Take a look at our "Bug: Unconfirmed" issues, where you can find issues that need extra testing and investigation.
* Want others to love Multiverse too? You can join the [Multiverse Discord community](https://discord.gg/NZtfKky) and help others with issues and setup!
* A Multiverse guru? You can update our [Wiki](https://github.com/Multiverse/multiverse-web) with your latest tip, tricks and guides! The wiki open for all to edit and improve.
* 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 donating to our [Github Sponsors](https://github.com/sponsors/Multiverse) or [Open Collective](https://opencollective.com/multiverse-plugins)!
* 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!
+321
View File
@@ -0,0 +1,321 @@
import org.apache.tools.ant.filters.ReplaceTokens
plugins {
id 'java-library'
id 'maven-publish'
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/'
}
maven {
name = 'papermc repo'
url = 'https://repo.papermc.io/repository/maven-public/'
}
}
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.18.2-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.9'
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.20:3.20.2'
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.20.1-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 }
}
javadoc {
source = sourceSets.main.allJava
classpath = configurations.compileClasspath
}
project.configurations.api.canBeResolved = true
shadowJar {
relocate 'co.aikar', 'org.mvplugins.multiverse.external.acf'
relocate 'com.dumptruckman.minecraft.util.Logging', 'org.mvplugins.multiverse.core.utils.CoreLogging'
relocate 'com.dumptruckman.minecraft.util.DebugLog', 'org.mvplugins.multiverse.core.utils.DebugFileLogger'
relocate 'de.themoep.idconverter', 'org.mvplugins.multiverse.external.idconverter'
relocate 'io.github.townyadvanced.commentedconfiguration', 'org.mvplugins.multiverse.external.commentedconfiguration'
relocate 'me.main__.util', 'org.mvplugins.multiverse.external.serializationconfig'
relocate 'org.bstats', 'org.mvplugins.multiverse.external.bstats'
relocate 'com.sun', 'org.mvplugins.multiverse.external.sun'
relocate 'net.minidev', 'org.mvplugins.multiverse.external.minidev'
relocate 'org.objectweb', 'org.mvplugins.multiverse.external.objectweb'
relocate 'io.vavr', 'org.mvplugins.multiverse.external.vavr'
relocate 'jakarta', 'org.mvplugins.multiverse.external.jakarta'
relocate 'javassist', 'org.mvplugins.multiverse.external.javassist'
relocate 'org.aopalliance', 'org.mvplugins.multiverse.external.aopalliance'
relocate 'org.glassfish', 'org.mvplugins.multiverse.external.glassfish'
relocate 'org.jvnet', 'org.mvplugins.multiverse.external.jvnet'
relocate 'org.intellij', 'org.mvplugins.multiverse.external.intellij'
relocate 'org.jetbrains', 'org.mvplugins.multiverse.external.jetbrains'
relocate 'io.papermc.lib', 'org.mvplugins.multiverse.external.paperlib'
configurations = [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'
}
}
-364
View File
@@ -1,364 +0,0 @@
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
import groovy.util.Node
import groovy.util.NodeList
import org.apache.tools.ant.filters.ReplaceTokens
import org.gradle.api.artifacts.Configuration
import org.gradle.api.credentials.PasswordCredentials
import org.gradle.api.publish.maven.MavenPublication
import org.gradle.api.tasks.SourceSetContainer
import org.gradle.api.tasks.Sync
import org.gradle.api.tasks.compile.JavaCompile
import org.gradle.api.tasks.javadoc.Javadoc
import org.gradle.api.tasks.testing.Test
import org.gradle.api.tasks.testing.logging.TestExceptionFormat
import org.gradle.external.javadoc.StandardJavadocDocletOptions
import org.gradle.jvm.tasks.Jar
import org.gradle.language.jvm.tasks.ProcessResources
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
`java-library`
eclipse
`maven-publish`
id("com.gradleup.shadow") version "8.3.9"
kotlin("jvm") version "2.0.21"
}
group = "org.mvplugins.multiverse.core"
version = System.getenv("GITHUB_VERSION") ?: "5.3.4.17"
description = "Multiverse-Core"
val sourceSets = extensions.getByType<SourceSetContainer>()
val serverApi: Configuration by configurations.creating
val externalPlugin: Configuration by configurations.creating
val shadowed: Configuration by configurations.creating
repositories {
mavenLocal()
mavenCentral()
maven {
url = uri("https://hub.spigotmc.org/nexus/content/repositories/snapshots/")
}
maven {
url = uri("https://repo.onarandombox.com/content/groups/public")
}
maven {
url = uri("https://hub.spigotmc.org/nexus/content/groups/public/")
}
maven {
url = uri("https://jitpack.io")
}
maven {
url = uri("https://repo.minebench.de/")
}
maven {
url = uri("https://repo.maven.apache.org/maven2/")
}
maven {
name = "aikar repo"
url = uri("https://repo.aikar.co/content/groups/aikar/")
}
maven {
name = "glaremasters repo"
url = uri("https://repo.glaremasters.me/repository/towny/")
}
maven {
name = "helpchatRepoReleases"
url = uri("https://repo.helpch.at/releases/")
}
maven {
name = "papermc repo"
url = uri("https://repo.papermc.io/repository/maven-public/")
}
// Purpur API
maven("https://repo.purpurmc.org/snapshots")
}
// Configuration wiring like in the Groovy build
configurations["compileOnly"].extendsFrom(serverApi)
configurations["runtimeClasspath"].extendsFrom(serverApi)
configurations["implementation"].extendsFrom(externalPlugin)
configurations["compileClasspath"].extendsFrom(shadowed)
configurations["testCompileClasspath"].extendsFrom(shadowed)
configurations["testRuntimeClasspath"].extendsFrom(shadowed)
dependencies {
// Server API: Purpur 1.19.4
serverApi("org.purpurmc.purpur:purpur-api:1.19.4-R0.1-SNAPSHOT")
// Economy
externalPlugin("com.github.MilkBowl:VaultAPI:1.7.1") {
exclude(group = "org.bukkit", module = "bukkit")
}
// PlaceholderAPI
externalPlugin("me.clip:placeholderapi:2.11.6")
// Command Framework
shadowed("co.aikar:acf-paper:0.5.1-SNAPSHOT")
// Config
shadowed("io.github.townyadvanced.commentedconfiguration:CommentedConfiguration:1.0.1") {
exclude(group = "org.spigotmc", module = "spigot-api")
}
// Utils
shadowed("io.vavr:vavr:0.10.4")
shadowed("org.glassfish.hk2:hk2-locator:3.0.3")
shadowed("org.glassfish.hk2:hk2-inhabitant-generator:3.0.3") {
exclude(group = "org.apache.maven", module = "maven-core")
}
shadowed("com.dumptruckman.minecraft:Logging:1.1.1") {
exclude(group = "junit", module = "junit")
}
shadowed("de.themoep.idconverter:mappings:1.2-SNAPSHOT")
shadowed("org.bstats:bstats-bukkit:3.1.0") {
exclude(group = "org.bukkit", module = "bukkit")
}
shadowed("net.minidev:json-smart:2.4.9")
shadowed("org.jetbrains:annotations:22.0.0")
shadowed("io.papermc:paperlib:1.0.8")
// Tests
testImplementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.0.21")
testImplementation("com.github.seeseemelk:MockBukkit-v1.19:3.1.0")
testImplementation("com.googlecode.json-simple:json-simple:1.1.1") {
exclude(group = "junit", module = "junit")
}
testImplementation("org.jetbrains.kotlin:kotlin-test")
testImplementation("com.natpryce:hamkrest:1.8.0.1")
testImplementation("org.mockito.kotlin:mockito-kotlin:4.1.0")
// Annotation Processors
annotationProcessor("org.glassfish.hk2:hk2-metadata-generator:3.0.3")
testAnnotationProcessor("org.glassfish.hk2:hk2-metadata-generator:3.0.3")
}
java {
// Plugin is compiled for Java 17 (GraalVM 17-friendly)
toolchain.languageVersion.set(JavaLanguageVersion.of(17))
withSourcesJar()
withJavadocJar()
}
val bitlyAccessToken: String = System.getenv("BITLY_ACCESS_TOKEN") ?: "bitly-access-token"
// === Token-filtered source generation (prepareSource) ===
val prepareSource by tasks.registering(Sync::class) {
inputs.property("bitlyAccessToken", bitlyAccessToken)
from(sourceSets["main"].java)
// avoid deprecated buildDir getter
into(layout.buildDirectory.dir("src"))
filter<ReplaceTokens>(
"tokens" to mapOf(
"bitly-access-token" to bitlyAccessToken,
),
)
}
// Use ONLY the filtered sources for compileJava (fixes duplicate classes)
tasks.named<JavaCompile>("compileJava") {
dependsOn(prepareSource)
source = prepareSource.get().outputs.files.asFileTree
sourceCompatibility = JavaVersion.VERSION_17.toString()
targetCompatibility = JavaVersion.VERSION_17.toString()
}
tasks.named<JavaCompile>("compileTestJava") {
sourceCompatibility = JavaVersion.VERSION_17.toString()
targetCompatibility = JavaVersion.VERSION_17.toString()
}
tasks.withType<JavaCompile>().configureEach {
options.encoding = "UTF-8"
options.compilerArgs.add(
"-Aorg.glassfish.hk2.metadata.location=META-INF/hk2-locator/Multiverse-Core",
)
}
tasks.withType<Javadoc>().configureEach {
(options as StandardJavadocDocletOptions).encoding = "UTF-8"
}
tasks.named<KotlinCompile>("compileTestKotlin") {
compilerOptions {
jvmTarget.set(JvmTarget.JVM_17)
javaParameters.set(true)
}
}
// We're not using Kotlin in the plugin itself, just tests!
tasks.named<KotlinCompile>("compileKotlin") {
enabled = false
}
configurations
.filterNot { it.name.startsWith("test") }
.forEach {
it.exclude(group = "org.jetbrains.kotlin", module = "kotlin-stdlib-jdk8")
}
tasks.named<ProcessResources>("processResources") {
val props = mapOf("version" to project.version.toString())
inputs.properties(props)
filteringCharset = "UTF-8"
filesMatching("plugin.yml") {
expand(props)
}
// Never up-to-date: tests depend on fresh version expansion
outputs.upToDateWhen { false }
}
tasks.named<Javadoc>("javadoc") {
source = sourceSets["main"].allJava
classpath = configurations["compileClasspath"]
}
// Removed: configurations["api"].isCanBeResolved = true (deprecated in Gradle 8/9)
val jarTask = tasks.named<Jar>("jar")
val shadowJarTask = tasks.named<ShadowJar>("shadowJar")
listOf("apiElements", "runtimeElements").forEach { configurationName ->
configurations.named(configurationName) {
outgoing.artifacts.removeIf { artifact ->
artifact.buildDependencies.getDependencies(null).contains(jarTask.get())
}
outgoing.artifact(shadowJarTask)
}
}
tasks.named<ShadowJar>("shadowJar") {
relocate("co.aikar", "org.mvplugins.multiverse.external.acf")
relocate("com.dumptruckman.minecraft.util.Logging", "org.mvplugins.multiverse.core.utils.CoreLogging")
relocate("com.dumptruckman.minecraft.util.DebugLog", "org.mvplugins.multiverse.core.utils.DebugFileLogger")
relocate("de.themoep.idconverter", "org.mvplugins.multiverse.external.idconverter")
relocate("io.github.townyadvanced.commentedconfiguration", "org.mvplugins.multiverse.external.commentedconfiguration")
relocate("me.main__.util", "org.mvplugins.multiverse.external.serializationconfig")
relocate("org.bstats", "org.mvplugins.multiverse.external.bstats")
relocate("com.sun", "org.mvplugins.multiverse.external.sun")
relocate("net.minidev", "org.mvplugins.multiverse.external.minidev")
relocate("org.objectweb", "org.mvplugins.multiverse.external.objectweb")
relocate("io.vavr", "org.mvplugins.multiverse.external.vavr")
relocate("jakarta", "org.mvplugins.multiverse.external.jakarta")
relocate("javassist", "org.mvplugins.multiverse.external.javassist")
relocate("org.aopalliance", "org.mvplugins.multiverse.external.aopalliance")
relocate("org.glassfish", "org.mvplugins.multiverse.external.glassfish")
relocate("org.jvnet", "org.mvplugins.multiverse.external.jvnet")
relocate("org.intellij", "org.mvplugins.multiverse.external.intellij")
relocate("org.jetbrains", "org.mvplugins.multiverse.external.jetbrains")
relocate("io.papermc.lib", "org.mvplugins.multiverse.external.paperlib")
configurations = listOf(project.configurations["shadowed"])
archiveClassifier.set("")
dependencies {
exclude { dependency ->
dependency.moduleGroup == "org.jetbrains.kotlin"
}
}
}
tasks.named("build") {
dependsOn("shadowJar")
}
tasks.named<Jar>("jar") {
enabled = false
}
tasks.named<Test>("test") {
useJUnitPlatform()
testLogging {
exceptionFormat = TestExceptionFormat.FULL
}
}
publishing {
publications {
create<MavenPublication>("maven") {
from(components["java"])
pom.withXml {
val pomNode = asNode()
val dependenciesNode = (
(pomNode.get("dependencies") as? NodeList)
?.firstOrNull() as? Node
) ?: (pomNode.appendNode("dependencies") as Node)
val dependencyNodes = dependenciesNode.children().filterIsInstance<Node>().toList()
// Remove Kotlin dependency
dependencyNodes
.filter { dependencyNode ->
val groupIdNodes = dependencyNode.get("groupId") as? NodeList ?: return@filter false
val groupId = (groupIdNodes.firstOrNull() as? Node)?.text()
groupId == "org.jetbrains.kotlin"
}
.forEach { dependencyNode ->
dependenciesNode.remove(dependencyNode)
}
// Switch runtime deps to provided
dependencyNodes.forEach { dependencyNode ->
val scopeNodes = dependencyNode.get("scope") as? NodeList ?: return@forEach
val scopeNode = scopeNodes.firstOrNull() as? Node ?: return@forEach
if (scopeNode.text() == "runtime") {
scopeNode.setValue("provided")
}
}
// Add server API (Purpur) to pom as provided
configurations["serverApi"].allDependencies.forEach { dependency ->
val depNode = dependenciesNode.appendNode("dependency")
depNode.appendNode("groupId", dependency.group)
depNode.appendNode("artifactId", dependency.name)
depNode.appendNode("version", dependency.version)
depNode.appendNode("scope", "provided")
}
}
}
}
repositories {
maven {
name = "GitHubPackages"
url = uri("https://maven.pkg.github.com/Multiverse/Multiverse-Core")
credentials {
username = System.getenv("GITHUB_ACTOR")
password = System.getenv("GITHUB_TOKEN")
}
}
maven {
name = "multiverse"
val releasesRepoUrl = uri("https://repo.dumptruckman.com/multiverse-releases")
val snapshotsRepoUrl = uri("https://repo.dumptruckman.com/multiverse-snapshots")
url = if (version.toString().endsWith("SNAPSHOT")) snapshotsRepoUrl else releasesRepoUrl
credentials(PasswordCredentials::class)
}
maven {
// todo: remove before mv5 release
name = "multiverseBeta"
url = uri("https://repo.c0ding.party/multiverse-beta")
credentials(PasswordCredentials::class)
}
}
}
-1
View File
@@ -1 +0,0 @@
out.txt
-20
View File
@@ -1,20 +0,0 @@
filepath = "../src/main/resources/multiverse-core_en.properties"
enumList = []
with open(filepath) as f:
for line in f:
line = line.strip()
if len(line) == 0:
enumList.append("")
elif line.startswith("#"):
enumList.append(line.replace("#", "//"))
else:
parts = line.split("=")
if (len(parts) < 2):
continue
parts = parts[0].split(".")[1:]
enumList.append("_".join([p.upper() for p in parts]) + ",")
with open("out.txt", "w") as f:
f.write("\n".join(enumList))
Binary file not shown.

Before

Width:  |  Height:  |  Size: 166 KiB

+2 -2
View File
@@ -134,7 +134,7 @@
<module name="MutableException"/>
<module name="OneTopLevelClass"/>
<module name="ThrowsCount">
<property name="max" value="2"/>
<property name="max" value="0"/>
</module>
<module name="VisibilityModifier">
<property name="protectedAllowed" value="true"/>
@@ -380,7 +380,7 @@
<property name="throwsIndent" value="8"/>
<property name="arrayInitIndent" value="4"/>
<property name="lineWrappingIndentation" value="8"/>
<property name="forceStrictCondition" value="false"/>
<property name="forceStrictCondition" value="true"/>
</module>
<module name="NoCodeInFile"/>
<module name="OuterTypeFilename"/>
Binary file not shown.
+1 -2
View File
@@ -1,7 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Vendored
+13 -21
View File
@@ -15,8 +15,6 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
# SPDX-License-Identifier: Apache-2.0
#
##############################################################################
#
@@ -57,7 +55,7 @@
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# 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/.
@@ -85,9 +83,10 @@ done
# This is normally unused
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
' "$PWD" ) || exit
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
@@ -134,13 +133,10 @@ location of your Java installation."
fi
else
JAVACMD=java
if ! command -v java >/dev/null 2>&1
then
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
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
fi
# Increase the maximum file descriptors if we can.
@@ -148,7 +144,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
# shellcheck disable=SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
@@ -156,7 +152,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
'' | soft) :;; #(
*)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
# shellcheck disable=SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
@@ -201,15 +197,11 @@ if "$cygwin" || "$msys" ; then
done
fi
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line.
# 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" \
Vendored
+10 -12
View File
@@ -13,8 +13,6 @@
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@rem SPDX-License-Identifier: Apache-2.0
@rem
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@@ -45,11 +43,11 @@ set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute
echo. 1>&2
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
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
@@ -59,11 +57,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo. 1>&2
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
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
+11
View File
@@ -0,0 +1,11 @@
/*
* This file was generated by the Gradle 'init' task.
*/
pluginManagement {
repositories {
gradlePluginPortal()
}
}
rootProject.name = 'multiverse-core'
-11
View File
@@ -1,11 +0,0 @@
pluginManagement {
repositories {
gradlePluginPortal()
maven {
url = uri("https://repo.onarandombox.com/multiverse-releases")
}
}
}
rootProject.name = "Multiverse-Core"
@@ -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);
}
}
@@ -1,108 +0,0 @@
package org.mvplugins.multiverse.core;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.function.Consumer;
import com.dumptruckman.minecraft.util.Logging;
import jakarta.annotation.PostConstruct;
import jakarta.inject.Inject;
import org.apache.commons.lang3.text.WordUtils;
import org.bstats.bukkit.Metrics;
import org.bstats.charts.AdvancedPie;
import org.bstats.charts.SingleLineChart;
import org.bukkit.World;
import org.jvnet.hk2.annotations.Service;
import org.mvplugins.multiverse.core.utils.REPatterns;
import org.mvplugins.multiverse.core.world.MultiverseWorld;
import org.mvplugins.multiverse.core.world.WorldManager;
/**
* Helper class that registers and sends metrics to <a href="https://bstats.org">bstats website</a>.
*/
@Service
final class BstatsMetricsConfigurator {
private static final int PLUGIN_ID = 7765;
private static final String NO_GENERATOR_NAME = "N/A";
private final WorldManager worldManager;
private final Metrics metrics;
@Inject
private BstatsMetricsConfigurator(MultiverseCore plugin, WorldManager worldManager) {
this.worldManager = worldManager;
this.metrics = new Metrics(plugin, PLUGIN_ID);
}
@PostConstruct
private void initMetrics() {
try {
addCustomGeneratorsMetric();
addEnvironmentsMetric();
addWorldCountMetric();
Logging.fine("Metrics enabled.");
} catch (Exception e) {
Logging.warning("There was an issue while enabling metrics:");
e.printStackTrace();
}
}
private void addCustomGeneratorsMetric() {
addAdvancedPieMetric("custom_generators", map -> {
for (MultiverseWorld w : worldManager.getLoadedWorlds()) {
incrementCount(map, getGeneratorName(w));
}
});
}
private String getGeneratorName(MultiverseWorld world) {
String gen = world.getGenerator();
return (gen != null && !gen.equalsIgnoreCase("null"))
? REPatterns.COLON.split(gen)[0]
: NO_GENERATOR_NAME;
}
private void addEnvironmentsMetric() {
addAdvancedPieMetric("environments", map -> {
for (MultiverseWorld w : worldManager.getLoadedWorlds()) {
incrementCount(map, titleCaseEnv(w.getEnvironment()));
}
});
}
private String titleCaseEnv(World.Environment env) {
String envName = env.name().replaceAll("_+", " ");
return WordUtils.capitalizeFully(envName);
}
private void addWorldCountMetric() {
addLineMetric("worlds", () -> worldManager.getWorlds().size());
}
private void addAdvancedPieMetric(String chartId, Consumer<Map<String, Integer>> metricsFunc) {
metrics.addCustomChart(createAdvancedPieChart(chartId, metricsFunc));
}
private void addLineMetric(String chartId, Callable<Integer> metricsFunc) {
metrics.addCustomChart(createLineChart(chartId, metricsFunc));
}
private void incrementCount(Map<String, Integer> map, String key) {
Integer count = map.getOrDefault(key, 0);
map.put(key, count + 1);
}
private AdvancedPie createAdvancedPieChart(String chartId, Consumer<Map<String, Integer>> metricsFunc) {
Map<String, Integer> map = new HashMap<>();
metricsFunc.accept(map);
return new AdvancedPie(chartId, () -> map);
}
private SingleLineChart createLineChart(String chartId, Callable<Integer> metricsFunc) {
return new SingleLineChart(chartId, metricsFunc);
}
}
@@ -7,51 +7,73 @@
package org.mvplugins.multiverse.core;
import java.io.File;
import java.lang.annotation.Annotation;
import java.util.List;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import com.dumptruckman.minecraft.util.Logging;
import io.vavr.control.Try;
import jakarta.inject.Inject;
import jakarta.inject.Provider;
import org.bukkit.configuration.file.FileConfiguration;
import me.main__.util.SerializationConfig.SerializationConfig;
import org.bukkit.configuration.serialization.ConfigurationSerialization;
import org.jetbrains.annotations.ApiStatus;
import org.bukkit.plugin.PluginDescriptionFile;
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 org.mvplugins.multiverse.core.anchor.AnchorManager;
import org.mvplugins.multiverse.core.destination.Destination;
import org.mvplugins.multiverse.core.api.Destination;
import org.mvplugins.multiverse.core.api.MVCore;
import org.mvplugins.multiverse.core.commandtools.MVCommandManager;
import org.mvplugins.multiverse.core.commandtools.MultiverseCommand;
import org.mvplugins.multiverse.core.commandtools.PluginLocales;
import org.mvplugins.multiverse.core.config.MVCoreConfig;
import org.mvplugins.multiverse.core.destination.DestinationsProvider;
import org.mvplugins.multiverse.core.commands.CoreCommand;
import org.mvplugins.multiverse.core.config.CoreConfig;
import org.mvplugins.multiverse.core.economy.MVEconomist;
import org.mvplugins.multiverse.core.listeners.CoreListener;
import org.mvplugins.multiverse.core.inject.PluginServiceLocatorFactory;
import org.mvplugins.multiverse.core.module.MultiverseModule;
import org.mvplugins.multiverse.core.utils.StringFormatter;
import org.mvplugins.multiverse.core.inject.InjectableListener;
import org.mvplugins.multiverse.core.inject.PluginInjection;
import org.mvplugins.multiverse.core.placeholders.MultiverseCorePlaceholders;
import org.mvplugins.multiverse.core.utils.TestingMode;
import org.mvplugins.multiverse.core.utils.metrics.MetricsConfigurator;
import org.mvplugins.multiverse.core.world.WorldManager;
import org.mvplugins.multiverse.core.world.location.NullSpawnLocation;
import org.mvplugins.multiverse.core.world.location.SpawnLocation;
import org.mvplugins.multiverse.core.world.location.UnloadedWorldLocation;
import org.mvplugins.multiverse.core.world.config.NullLocation;
import org.mvplugins.multiverse.core.world.config.SpawnLocation;
/**
* The start of the Multiverse-Core plugin
* The implementation of the Multiverse-{@link MVCore}.
*/
@Service
public class MultiverseCore extends MultiverseModule {
public class MultiverseCore extends JavaPlugin implements MVCore {
private static final int PROTOCOL = 50;
private ServiceLocator serviceLocator;
@Inject
private Provider<CoreConfig> configProvider;
private Provider<MVCoreConfig> configProvider;
@Inject
private Provider<WorldManager> worldManagerProvider;
@Inject
private Provider<AnchorManager> anchorManagerProvider;
@Inject
private Provider<MVCommandManager> commandManagerProvider;
@Inject
private Provider<DestinationsProvider> destinationsProviderProvider;
@Inject
private Provider<BstatsMetricsConfigurator> metricsConfiguratorProvider;
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;
/**
* This is the constructor for the MultiverseCore.
@@ -73,9 +95,9 @@ public class MultiverseCore extends MultiverseModule {
}
// Register our config classes
ConfigurationSerialization.registerClass(NullSpawnLocation.class);
SerializationConfig.initLogging(Logging.getLogger());
ConfigurationSerialization.registerClass(NullLocation.class);
ConfigurationSerialization.registerClass(SpawnLocation.class);
ConfigurationSerialization.registerClass(UnloadedWorldLocation.class);
}
/**
@@ -83,13 +105,11 @@ public class MultiverseCore extends MultiverseModule {
*/
@Override
public void onEnable() {
super.onEnable();
initializeDependencyInjection(new MultiverseCorePluginBinder(this));
initializeDependencyInjection();
// Load our configs first as we need them for everything else.
var config = configProvider.get();
var loadSuccess = config.load().andThenTry(config::save).isSuccess();
if (!loadSuccess || !config.isLoaded()) {
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);
@@ -99,15 +119,17 @@ public class MultiverseCore extends MultiverseModule {
// Initialize the worlds
worldManagerProvider.get().initAllWorlds().andThenTry(() -> {
loadEconomist(); // Setup economy here so vault is loaded
// Setup economy here so vault is loaded
loadEconomist();
// Init all the other stuff
loadAnchors();
registerDynamicListeners(CoreListener.class);
registerEvents();
setUpLocales();
registerCommands(CoreCommand.class);
registerCommands();
registerDestinations();
setupMetrics();
loadPlaceholderApiIntegration();
loadApiService();
saveAllConfigs();
logEnableMessage();
}).onFailure(e -> {
@@ -122,13 +144,30 @@ public class MultiverseCore extends MultiverseModule {
*/
@Override
public void onDisable() {
super.onDisable();
MultiverseCoreApi.shutdown();
saveAllConfigs();
shutdownDependencyInjection();
PluginServiceLocatorFactory.get().shutdown();
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();
}
@@ -143,13 +182,60 @@ public class MultiverseCore extends MultiverseModule {
private void loadAnchors() {
Try.of(() -> anchorManagerProvider.get())
.flatMap(AnchorManager::loadAnchors)
.onSuccess(AnchorManager::loadAnchors)
.onFailure(e -> {
Logging.severe("Failed to load anchors");
e.printStackTrace();
});
}
/**
* Function to Register all the Events needed.
*/
private void registerEvents() {
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() {
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.
*/
@@ -182,12 +268,21 @@ public class MultiverseCore extends MultiverseModule {
}
/**
* Setup placeholder api hook
* Logs the enable message.
*/
private void logEnableMessage() {
Logging.config("Version %s (API v%s) Enabled - By %s", this.getDescription().getVersion(), PROTOCOL, getAuthors());
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.getService(PlaceholderExpansionHook.class))
Try.run(() -> serviceLocator.createAndInitialize(MultiverseCorePlaceholders.class))
.onFailure(e -> {
Logging.severe("Failed to load PlaceholderAPI integration.");
e.printStackTrace();
@@ -196,67 +291,53 @@ public class MultiverseCore extends MultiverseModule {
}
/**
* Setup the api service for {@link MultiverseCoreApi}
* {@inheritDoc}
*/
private void loadApiService() {
Try.run(() -> MultiverseCoreApi.init(this))
.onSuccess(ignore -> Logging.info("API service loaded!"))
.onFailure(e -> {
Logging.severe("Failed to load API service!");
e.printStackTrace();
});
}
/**
* Save config.yml, worlds.yml, and anchors.yml.
*
* @return {@link Try#isSuccess()} true if all configs were successfully saved
*/
private Try<Void> saveAllConfigs() {
return configProvider.get().save()
.andThenTry(() -> worldManagerProvider.get().saveWorldsConfig())
.andThenTry(() -> anchorManagerProvider.get().saveAllAnchors())
.onFailure(e ->
Logging.severe("Failed to save all configs, things may not work as expected. %s",
e.getLocalizedMessage()));
}
/**
* Logs the enable message.
*/
private void logEnableMessage() {
Logging.config("\u001B[32mVersion %s (API v%s) Enabled - By %s\u001B[39m",
this.getDescription().getVersion(), getVersionAsNumber(), StringFormatter.joinAnd(getDescription().getAuthors()));
if (configProvider.get().isShowingDonateMessage()) {
Logging.config("\u001B[32mLoving Multiverse-Core? Please consider supporting the project with a small donation: https://github.com/sponsors/Multiverse\u001B[39m");
}
}
/**
* Gets the MultiverseCoreApi
*
* @return The MultiverseCoreApi
*
* @deprecated Use {@link MultiverseCoreApi#get()} directly.
*/
@Deprecated(since = "5.1", forRemoval = true)
@ApiStatus.ScheduledForRemoval(inVersion = "6.0")
public MultiverseCoreApi getApi() {
return MultiverseCoreApi.get();
@Override
public MVCore getCore() {
return this;
}
/**
* {@inheritDoc}
*/
@Override
public double getTargetCoreVersion() {
return getVersionAsNumber();
public int getProtocolVersion() {
return PROTOCOL;
}
/**
* {@inheritDoc}
*/
@Override
public String getAuthors() {
List<String> authorsList = this.getDescription().getAuthors();
if (authorsList.isEmpty()) {
return "";
}
StringBuilder authors = new StringBuilder();
authors.append(authorsList.get(0));
for (int i = 1; i < authorsList.size(); i++) {
if (i == authorsList.size() - 1) {
authors.append(" and ").append(authorsList.get(i));
} else {
authors.append(", ").append(authorsList.get(i));
}
}
return authors.toString();
}
/**
* {@inheritDoc}
*/
@Override
public int getPluginCount() {
return this.pluginCount;
}
@NotNull
@Override
public Logger getLogger() {
@@ -267,33 +348,78 @@ public class MultiverseCore extends MultiverseModule {
* {@inheritDoc}
*/
@Override
public @NotNull FileConfiguration getConfig() {
CoreConfig coreConfig = this.configProvider.get();
var config = coreConfig.getConfig();
if (config != null && coreConfig.isLoaded()) {
return config;
}
var loadSuccess = coreConfig.load().isSuccess();
if (!loadSuccess || !coreConfig.isLoaded()) {
throw new RuntimeException("Failed to load configs");
}
return coreConfig.getConfig();
public void incrementPluginCount() {
this.pluginCount += 1;
}
/**
* {@inheritDoc}
*/
@Override
public void reloadConfig() {
this.configProvider.get().load();
public void decrementPluginCount() {
this.pluginCount -= 1;
}
/**
* {@inheritDoc}
*/
@Override
public void saveConfig() {
this.configProvider.get().save();
public boolean saveAllConfigs() {
// TODO: Make this all Try<Void>
return configProvider.get().save().isSuccess()
&& worldManagerProvider.get().saveWorldsConfig()
&& anchorManagerProvider.get().saveAnchors();
}
/**
* Gets the best service from this plugin that implements the given contract or has the given implementation.
*
* @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
* @param <T> The type of the contract to get
* @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
*/
@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;
}
/**
* Gets all services from this plugin that implement the given contract or have the given implementation and have
* the provided qualifiers.
*
* @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
* @param <T> The type of the contract to get
* @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.
*/
public MultiverseCore(JavaPluginLoader loader, PluginDescriptionFile description, File dataFolder, File file) {
super(loader, description, dataFolder, file);
}
}
@@ -1,205 +0,0 @@
package org.mvplugins.multiverse.core;
import org.bukkit.Bukkit;
import org.bukkit.plugin.ServicePriority;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.mvplugins.multiverse.core.anchor.AnchorManager;
import org.mvplugins.multiverse.core.config.CoreConfig;
import org.mvplugins.multiverse.core.destination.DestinationsProvider;
import org.mvplugins.multiverse.core.economy.MVEconomist;
import org.mvplugins.multiverse.core.inject.PluginServiceLocator;
import org.mvplugins.multiverse.core.teleportation.AsyncSafetyTeleporter;
import org.mvplugins.multiverse.core.teleportation.BlockSafety;
import org.mvplugins.multiverse.core.teleportation.LocationManipulation;
import org.mvplugins.multiverse.core.world.WorldManager;
import org.mvplugins.multiverse.core.world.biomeprovider.BiomeProviderFactory;
import org.mvplugins.multiverse.core.world.generators.GeneratorProvider;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.function.Consumer;
/**
* Provides access to the MultiverseCore API.
*/
public class MultiverseCoreApi {
private static MultiverseCoreApi instance;
private static final List<Consumer<MultiverseCoreApi>> whenLoadedCallbacks = new ArrayList<>();
static void init(@NotNull MultiverseCore multiverseCore) {
if (instance != null) {
throw new IllegalStateException("MultiverseCoreApi has already been initialized!");
}
instance = new MultiverseCoreApi(multiverseCore.getServiceLocator());
Bukkit.getServicesManager().register(MultiverseCoreApi.class, instance, multiverseCore, ServicePriority.Normal);
whenLoadedCallbacks.forEach(c -> c.accept(instance));
whenLoadedCallbacks.clear();
}
static void shutdown() {
Bukkit.getServicesManager().unregister(instance);
instance = null;
}
/**
* Hook your plugin into the MultiverseCoreApi here to ensure you only start using the API after it has been initialized.
* Use this if you know your plugin may load before Multiverse-Core is fully initialized.
* <br/>
* This handy method removes the need for you to check with plugin manager or listen to plugin enable event.
* <br/>
* Callback will be called immediately if the MultiverseCoreApi has already been initialized.
*
* @param consumer The callback to execute when the MultiverseCoreApi has been initialized.
*
* @since 5.1
*/
@ApiStatus.AvailableSince("5.1")
public static void whenLoaded(@NotNull Consumer<MultiverseCoreApi> consumer) {
if (instance != null) {
consumer.accept(instance);
} else {
whenLoadedCallbacks.add(consumer);
}
}
/**
* Checks if the MultiverseCoreApi has been initialized.
*
* @return True if the MultiverseCoreApi has been initialized, false otherwise
*
* @since 5.1
*/
@ApiStatus.AvailableSince("5.1")
public static boolean isLoaded() {
return instance != null;
}
/**
* Gets the MultiverseCoreApi. This will throw an exception if the Multiverse-Core has not been initialized.
* <br/>
* You can check if the MultiverseCoreApi has been initialized with {@link #isLoaded()} before using this method.
* <br/>
* Alternatively, you can use {@link #whenLoaded(Consumer)} to hook into the MultiverseCoreApi if your plugin may
* load before Multiverse-Core is fully initialized.
*
* @return The MultiverseCoreApi
* @throws IllegalStateException if the MultiverseCoreApi has not been initialized
*/
public static @NotNull MultiverseCoreApi get() {
if (instance == null) {
throw new IllegalStateException("MultiverseCoreApi has not been initialized!");
}
return instance;
}
private final PluginServiceLocator serviceLocator;
private MultiverseCoreApi(@NotNull PluginServiceLocator serviceProvider) {
this.serviceLocator = serviceProvider;
}
/**
* Gets the instance of AnchorManager.
*
* @return The AnchorManager instance
*/
public @NotNull AnchorManager getAnchorManager() {
return Objects.requireNonNull(serviceLocator.getActiveService(AnchorManager.class));
}
/**
* Gets the instance of BiomeProviderFactory.
*
* @return The BiomeProviderFactory instance
*/
public @NotNull BiomeProviderFactory getBiomeProviderFactory() {
return Objects.requireNonNull(serviceLocator.getActiveService(BiomeProviderFactory.class));
}
/**
* Gets the instance of BlockSafety.
*
* @return The BlockSafety instance
*/
public @NotNull BlockSafety getBlockSafety() {
return Objects.requireNonNull(serviceLocator.getActiveService(BlockSafety.class));
}
/**
* Gets the instance of CoreConfig.
*
* @return The CoreConfig instance
*/
public @NotNull CoreConfig getCoreConfig() {
return Objects.requireNonNull(serviceLocator.getActiveService(CoreConfig.class));
}
/**
* Gets the instance of DestinationsProvider.
*
* @return The DestinationsProvider instance
*/
public @NotNull DestinationsProvider getDestinationsProvider() {
return Objects.requireNonNull(serviceLocator.getActiveService(DestinationsProvider.class));
}
/**
* Gets the instance of GeneratorProvider.
*
* @return The GeneratorProvider instance
*/
public @NotNull GeneratorProvider getGeneratorProvider() {
return Objects.requireNonNull(serviceLocator.getActiveService(GeneratorProvider.class));
}
/**
* Gets the instance of LocationManipulation.
*
* @return The LocationManipulation instance
*/
public @NotNull LocationManipulation getLocationManipulation() {
return Objects.requireNonNull(serviceLocator.getActiveService(LocationManipulation.class));
}
/**
* Gets the instance of MVEconomist.
*
* @return The MVEconomist instance
*/
public @NotNull MVEconomist getMVEconomist() {
return Objects.requireNonNull(serviceLocator.getActiveService(MVEconomist.class));
}
/**
* Gets the instance of SafetyTeleporter.
*
* @return The SafetyTeleporter instance
*/
public @NotNull AsyncSafetyTeleporter getSafetyTeleporter() {
return Objects.requireNonNull(serviceLocator.getActiveService(AsyncSafetyTeleporter.class));
}
/**
* Gets the instance of WorldManager.
*
* @return The WorldManager instance
*/
public @NotNull WorldManager getWorldManager() {
return Objects.requireNonNull(serviceLocator.getActiveService(WorldManager.class));
}
/**
* Gets the instance of Multiverse-Core's PluginServiceLocator.
* <br/>
* You can use this to hook into the hk2 dependency injection system used by Multiverse-Core.
*
* @return The Multiverse-Core's PluginServiceLocator
*/
public @NotNull PluginServiceLocator getServiceLocator() {
return serviceLocator;
}
}
@@ -3,17 +3,18 @@ package org.mvplugins.multiverse.core;
import org.glassfish.hk2.utilities.binding.ScopedBindingBuilder;
import org.jetbrains.annotations.NotNull;
import org.mvplugins.multiverse.core.module.MultiverseModuleBinder;
import org.mvplugins.multiverse.core.api.MVCore;
import org.mvplugins.multiverse.core.inject.binder.JavaPluginBinder;
final class MultiverseCorePluginBinder extends MultiverseModuleBinder<MultiverseCore> {
class MultiverseCorePluginBinder extends JavaPluginBinder<MultiverseCore> {
MultiverseCorePluginBinder(@NotNull MultiverseCore plugin) {
protected MultiverseCorePluginBinder(@NotNull MultiverseCore plugin) {
super(plugin);
}
@Override
protected ScopedBindingBuilder<MultiverseCore> bindPluginClass(
ScopedBindingBuilder<MultiverseCore> bindingBuilder) {
return super.bindPluginClass(bindingBuilder).to(MultiverseCore.class);
return super.bindPluginClass(bindingBuilder).to(MVCore.class).to(MultiverseCore.class);
}
}
@@ -8,14 +8,14 @@
package org.mvplugins.multiverse.core.anchor;
import java.io.File;
import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import com.dumptruckman.minecraft.util.Logging;
import io.vavr.control.Option;
import io.vavr.control.Try;
import jakarta.inject.Inject;
import org.bukkit.Location;
import org.bukkit.configuration.ConfigurationSection;
@@ -23,183 +23,167 @@ import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jvnet.hk2.annotations.Service;
import org.mvplugins.multiverse.core.MultiverseCore;
import org.mvplugins.multiverse.core.teleportation.LocationManipulation;
import org.mvplugins.multiverse.core.utils.result.Result;
import org.mvplugins.multiverse.core.world.WorldManager;
import org.mvplugins.multiverse.core.world.entrycheck.WorldEntryCheckerProvider;
import org.mvplugins.multiverse.core.world.location.UnloadedWorldLocation;
import org.mvplugins.multiverse.core.api.LocationManipulation;
import org.mvplugins.multiverse.core.config.MVCoreConfig;
/**
* Manages anchors and it's location.
* Manages anchors.
*/
@Service
public final class AnchorManager {
private static final String ANCHORS_FILE = "anchors.yml";
private static final String ANCHORS_CONFIG_SECTION = "anchors";
private final Map<String, MultiverseAnchor> anchors;
public class AnchorManager {
private Map<String, Location> anchors;
private FileConfiguration anchorConfig;
private final Plugin plugin;
private final LocationManipulation locationManipulation;
private final WorldEntryCheckerProvider entryCheckerProvider;
private final WorldManager worldManager;
private final MVCoreConfig config;
@Inject
AnchorManager(
public AnchorManager(
MultiverseCore plugin,
LocationManipulation locationManipulation,
WorldEntryCheckerProvider entryCheckerProvider,
WorldManager worldManager
MVCoreConfig config
) {
this.plugin = plugin;
this.locationManipulation = locationManipulation;
this.entryCheckerProvider = entryCheckerProvider;
this.worldManager = worldManager;
this.config = config;
this.anchors = new HashMap<>();
this.anchors = new HashMap<String, Location>();
}
/**
* Loads all anchors.
*/
public Try<Void> loadAnchors() {
anchors.clear();
return Try.run(() -> {
anchorConfig = YamlConfiguration.loadConfiguration(new File(plugin.getDataFolder(), ANCHORS_FILE));
parseAnchorsFromConfig();
});
}
private void parseAnchorsFromConfig() {
var anchorsSection = getAnchorsConfigSection();
public void loadAnchors() {
this.anchors = new HashMap<String, Location>();
this.anchorConfig = YamlConfiguration.loadConfiguration(new File(this.plugin.getDataFolder(), "anchors.yml"));
this.ensureConfigIsPrepared();
ConfigurationSection anchorsSection = this.anchorConfig.getConfigurationSection("anchors");
Set<String> anchorKeys = anchorsSection.getKeys(false);
for (String key : anchorKeys) {
//world:x,y,z:pitch:yaw
Location anchorLocation = locationManipulation.stringToLocation(anchorsSection.getString(key, ""));
Location anchorLocation = this.locationManipulation.stringToLocation(anchorsSection.getString(key, ""));
if (anchorLocation != null) {
Logging.config("Loading anchor: '%s'...", key);
anchors.put(key, new MultiverseAnchor(key, UnloadedWorldLocation.fromLocation(anchorLocation)));
this.anchors.put(key, anchorLocation);
} else {
Logging.warning("The location for anchor '%s' is INVALID.", key);
}
}
}
private ConfigurationSection getAnchorsConfigSection() {
var anchorsConfigSection = anchorConfig.getConfigurationSection(ANCHORS_CONFIG_SECTION);
if (anchorsConfigSection == null) {
anchorsConfigSection = anchorConfig.createSection(ANCHORS_CONFIG_SECTION);
private void ensureConfigIsPrepared() {
if (this.anchorConfig.getConfigurationSection("anchors") == null) {
this.anchorConfig.createSection("anchors");
}
return anchorsConfigSection;
}
/**
* Saves all anchors.
*
* @return True if all anchors were successfully saved.
*/
public Try<Void> saveAllAnchors() {
return Try.run(() -> anchorConfig.save(new File(plugin.getDataFolder(), ANCHORS_FILE)))
.onFailure(failure ->
Logging.severe("Failed to save anchors.yml. Please check your file permissions."));
public boolean saveAnchors() {
try {
this.anchorConfig.save(new File(this.plugin.getDataFolder(), "anchors.yml"));
return true;
} catch (IOException e) {
Logging.severe("Failed to save anchors.yml. Please check your file permissions.");
return false;
}
}
/**
* Gets the {@link Location} associated with an anchor.
*
* @param anchorName The name of the anchor.
* @param anchor The name of the anchor.
* @return The {@link Location}.
*/
public Option<MultiverseAnchor> getAnchor(String anchorName) {
return Option.of(anchors.get(anchorName));
}
/**
* Sets a new location for an anchor. Creates a new anchor if it doesn't exist.
*
* @param anchorName The name of the anchor.
* @param location The location of the anchor as string (world:x,y,z:pitch:yaw).
* @return True if the anchor was successfully saved.
*/
public Try<Void> setAnchor(@NotNull String anchorName, @NotNull String location) {
Location parsed = locationManipulation.stringToLocation(location);
if (parsed == null) {
return Try.failure(new IllegalArgumentException("The location for anchor '" + anchorName + "' is INVALID."));
public Location getAnchorLocation(String anchor) {
if (this.anchors.containsKey(anchor)) {
return this.anchors.get(anchor);
}
return setAnchor(anchorName, parsed);
return null;
}
/**
* Sets a new location for an anchor. Creates a new anchor if it doesn't exist.
*
* @param anchorName The name of the anchor.
* @param location The {@link Location} of the anchor.
* Saves an anchor.
* @param anchor The name of the anchor.
* @param location The location of the anchor as string.
* @return True if the anchor was successfully saved.
*/
public Try<Void> setAnchor(@NotNull String anchorName, @NotNull Location location) {
Option.of(anchors.get(anchorName))
.peek(anchor -> anchor.setLocation(UnloadedWorldLocation.fromLocation(location)))
.onEmpty(() -> anchors.put(anchorName, new MultiverseAnchor(anchorName, UnloadedWorldLocation.fromLocation(location))));
getAnchorsConfigSection().set(anchorName, locationManipulation.locationToString(location));
return saveAllAnchors();
public boolean saveAnchorLocation(String anchor, String location) {
Location parsed = this.locationManipulation.stringToLocation(location);
return parsed != null && this.saveAnchorLocation(anchor, parsed);
}
/**
* Saves an anchor.
* @param anchor The name of the anchor.
* @param l The {@link Location} of the anchor.
* @return True if the anchor was successfully saved.
*/
public boolean saveAnchorLocation(String anchor, Location l) {
if (l == null) {
return false;
}
this.anchorConfig.set("anchors." + anchor, this.locationManipulation.locationToString(l));
this.anchors.put(anchor, l);
return this.saveAnchors();
}
/**
* Gets all anchors.
*
* @return An unmodifiable {@link List} containing all anchors.
* @return An unmodifiable {@link Set} containing all anchors.
*/
public List<MultiverseAnchor> getAllAnchors() {
return anchors.values().stream().toList();
public Set<String> getAllAnchors() {
return Collections.unmodifiableSet(this.anchors.keySet());
}
/**
* Gets all anchors that the specified {@link Player} can access.
*
* @param player The {@link Player}.
* @return An unmodifiable {@link List} containing all anchors the specified {@link Player} can access.
* @param p The {@link Player}.
* @return An unmodifiable {@link Set} containing all anchors the specified {@link Player} can access.
*/
public List<MultiverseAnchor> getAnchors(@Nullable Player player) {
if (player == null) {
return getAllAnchors();
} else {
return getAnchorsForPlayer(player);
public Set<String> getAnchors(Player p) {
if (p == null) {
return this.anchors.keySet();
}
}
private List<MultiverseAnchor> getAnchorsForPlayer(@NotNull Player player) {
return anchors.values().stream()
.filter(anchor -> shouldIncludeAnchorForPlayer(anchor, player))
.toList();
}
private boolean shouldIncludeAnchorForPlayer(MultiverseAnchor anchor, Player player) {
return worldManager.getWorld(anchor.getLocationWorld())
.map(mvWorld -> entryCheckerProvider.forSender(player).canAccessWorld(mvWorld))
.peek(result -> Logging.finer("Result for %s can access anchor %s: %s", player.getName(), anchor.getName(), result))
.map(Result::isSuccess)
.getOrElse(true);
Set<String> myAnchors = new HashSet<String>();
for (String anchor : this.anchors.keySet()) {
Location ancLoc = this.anchors.get(anchor);
if (ancLoc == null) {
continue;
}
String worldPerm = "multiverse.access." + ancLoc.getWorld().getName();
// Add to the list if we're not enforcing access
// OR
// We are enforcing access and the user has the permission.
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 " +
"permission and 'enforceaccess' is enabled!",
anchor, p.getName(), worldPerm));
}
}
return Collections.unmodifiableSet(myAnchors);
}
/**
* Deletes the specified anchor.
*
* @param anchor The anchor to delete.
* @param s The name of the anchor.
* @return True if the anchor was successfully deleted.
*/
public Try<Void> deleteAnchor(@NotNull MultiverseAnchor anchor) {
if (anchors.containsKey(anchor.getName())) {
anchors.remove(anchor.getName());
getAnchorsConfigSection().set(anchor.getName(), null);
return saveAllAnchors();
public boolean deleteAnchor(String s) {
if (this.anchors.containsKey(s)) {
this.anchors.remove(s);
this.anchorConfig.set("anchors." + s, null);
return this.saveAnchors();
}
return Try.failure(new IllegalArgumentException("Anchor does not exist"));
return false;
}
}
@@ -1,54 +0,0 @@
package org.mvplugins.multiverse.core.anchor;
import org.bukkit.Location;
import org.bukkit.World;
import org.jetbrains.annotations.Nullable;
import org.mvplugins.multiverse.core.world.location.UnloadedWorldLocation;
/**
* Represents a single anchor location.
*/
public final class MultiverseAnchor {
private final String name;
private UnloadedWorldLocation location;
MultiverseAnchor(String name, UnloadedWorldLocation location) {
this.name = name;
this.location = location;
}
/**
* Gets the name of the anchor.
*
* @return The name.
*/
public String getName() {
return name;
}
/**
* Gets a copy of the anchor's location.
*
* @return The location.
*/
public Location getLocation() {
return location.toBukkitLocation();
}
void setLocation(Location location) {
this.location = new UnloadedWorldLocation(location);
}
/**
* Gets the world of the anchor's location.
*
* @return The world.
*/
@Nullable World getLocationWorld() {
if (location == null) {
return null;
}
return location.getWorld();
}
}
@@ -0,0 +1,81 @@
package org.mvplugins.multiverse.core.api;
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.
* @param l The {@link Location} of the block.
* @return True if the block at that {@link Location} is above air.
*/
boolean isBlockAboveAir(Location l);
/**
* Checks if a player can spawn safely at the given coordinates.
* @param world The {@link World}.
* @param x The x-coordinate.
* @param y The y-coordinate.
* @param z The z-coordinate.
* @return True if a player can spawn safely at the given coordinates.
*/
boolean playerCanSpawnHereSafely(World world, double x, double y, double z);
/**
* This function checks whether the block at the coordinates given is safe or not by checking for Lava/Fire/Air
* etc. This also ensures there is enough space for a player to spawn!
*
* @param l The {@link Location}
* @return Whether the player can spawn safely at the given {@link Location}
*/
boolean playerCanSpawnHereSafely(Location l);
/**
* Gets a safe bed spawn location OR null if the bed is invalid.
* @param l The location of the bead head (block with the pillow on it).
* @return Safe location around the bed or null if no location was found.
*/
Location getSafeBedSpawn(Location l);
/**
* Gets the location of the top block at the specified {@link Location}.
* @param l Any {@link Location}.
* @return The {@link Location} of the top-block.
*/
Location getTopBlock(Location l);
/**
* Gets the location of the top block at the specified {@link Location}.
* @param l Any {@link Location}.
* @return The {@link Location} of the top-block.
*/
Location getBottomBlock(Location l);
/**
* Checks if an entity would be on track at the specified {@link Location}.
* @param l The {@link Location}.
* @return True if an entity would be on tracks at the specified {@link Location}.
*/
boolean isEntitiyOnTrack(Location l);
/**
* Checks if the specified {@link Minecart} can spawn safely.
* @param cart The {@link Minecart}.
* @return True if the minecart can spawn safely.
*/
boolean canSpawnCartSafely(Minecart cart);
/**
* Checks if the specified {@link Vehicle} can spawn safely.
* @param vehicle The {@link Vehicle}.
* @return True if the vehicle can spawn safely.
*/
boolean canSpawnVehicleSafely(Vehicle vehicle);
}
@@ -0,0 +1,58 @@
package org.mvplugins.multiverse.core.api;
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.
*
* <p>Portals have a prefix of "p" for example and OpenWarp (third party plugin) uses "ow". This is derived from a
* hash and cannot have duplicate values. Read that as your plugin cannot use 'p' because it's already used.
* Please check the wiki when adding a custom destination!</p>
*
* @return The identifier or prefix that is required for this destination.
*/
@NotNull String getIdentifier();
/**
* Returns the destination instance for the given destination parameters.
*
* @param destinationParams The destination parameters. ex: p:MyPortal:nw
* @return The destination instance, or null if the destination parameters are invalid.
*/
@Nullable T getDestinationInstance(@Nullable String destinationParams);
/**
* Returns a list of possible destinations for the given destination parameters.
*
* @param issuer The command issuer.
* @param destinationParams The destination parameters. ex: p:MyPortal:nw
* @return A list of possible destinations.
*/
@NotNull Collection<String> suggestDestinations(@NotNull BukkitCommandIssuer issuer, @Nullable String destinationParams);
/**
* Should the Multiverse SafeTeleporter be used?
*
* <p>If not, MV will blindly take people to the location specified.</p>
*
* @return True if the SafeTeleporter will be used, false if not.
*/
boolean checkTeleportSafety();
/**
* Returns the teleporter to use for this destination.
*
* <p>By default, Multiverse will automatically use SafeTeleporter. If you want to use a different teleporter, you can
* override this method.</p>
*
* @return The custom teleporter to use for this destination. Return null to use the default teleporter.
*/
@Nullable Teleporter getTeleporter();
}
@@ -0,0 +1,45 @@
package org.mvplugins.multiverse.core.api;
import org.bukkit.Location;
import org.bukkit.entity.Entity;
import org.bukkit.util.Vector;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public interface DestinationInstance {
/**
* Gets the exact location to teleport an entity to.
*
* @param teleportee The entity to teleport.
* @return The location to teleport to.
*/
@Nullable Location getLocation(@NotNull Entity teleportee);
/**
* Gets the velocity to apply to an entity after teleporting.
*
* @param teleportee The entity to teleport.
* @return A vector representing the speed/direction the player should travel when arriving at the destination.
*/
@Nullable Vector getVelocity(@NotNull Entity teleportee);
/**
* Gets the permission suffix to check for when teleporting to this destination.
* This is used for finer per world/player permissions, such as "multiverse.teleport.self.worldname".
*
* <p>For example, if the destination is "w:world", the permission suffix is "world".</p>
*
* @return The permission suffix.
*/
@Nullable String getFinerPermissionSuffix();
/**
* Serialises the destination instance to a savable string.
*
* <p>This is used when plugins save destinations to configuration,
* and when the destination is displayed to the user.</p>
*
* @return The serialised destination instance.
*/
@NotNull String serialise();
}
@@ -0,0 +1,101 @@
package org.mvplugins.multiverse.core.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.
* <p>
* WORLD:X,Y,Z:yaw:pitch
* <p>
* The corresponding String2Loc function is {@link #stringToLocation}
*
* @param location The Location to save.
* @return The location as a string in this format: WORLD:x,y,z:yaw:pitch
*/
String locationToString(Location location);
/**
* This method simply does some rounding, rather than forcing a call to the server to get the blockdata.
*
* @param l The location to round to the block location
* @return A rounded location.
*/
Location getBlockLocation(Location l);
/**
* Returns a new location from a given string. The format is as follows:
* <p>
* WORLD:X,Y,Z:yaw:pitch
* <p>
* The corresponding Location2String function is {@link #stringToLocation}
*
* @param locationString The location represented as a string (WORLD:X,Y,Z:yaw:pitch)
* @return A new location defined by the string or null if the string was invalid.
*/
Location stringToLocation(String locationString);
/**
* Returns a colored string with the coords.
*
* @param l The {@link Location}
* @return The {@link String}
*/
String strCoords(Location l);
/**
* Converts a location to a printable readable formatted string including pitch/yaw.
*
* @param l The {@link Location}
* @return The {@link String}
*/
String strCoordsRaw(Location l);
/**
* Return the NESW Direction a Location is facing.
*
* @param location The {@link Location}
* @return The NESW Direction
*/
String getDirection(Location location);
/**
* Returns the float yaw position for the given cardinal direction.
*
* @param orientation The cardinal direction
* @return The yaw
*/
float getYaw(String orientation);
/**
* Returns a speed float from a given vector.
*
* @param v The {@link Vector}
* @return The speed
*/
float getSpeed(Vector v);
/**
* Returns a translated vector from the given direction.
*
* @param v The old {@link Vector}
* @param direction The new direction
* @return The translated {@link Vector}
*/
Vector getTranslatedVector(Vector v, String direction);
/**
* Returns the next Location that a {@link Vehicle} is traveling at.
*
* @param v The {@link Vehicle}
* @return The {@link Location}
*/
Location getNextBlock(Vehicle v);
}
@@ -0,0 +1,256 @@
package org.mvplugins.multiverse.core.api;
import io.vavr.control.Try;
import org.jvnet.hk2.annotations.Contract;
import org.mvplugins.multiverse.core.configuration.node.NodeGroup;
import org.mvplugins.multiverse.core.placeholders.MultiverseCorePlaceholders;
@Contract
public interface MVConfig {
/**
* Loads the config from disk.
* @return True if the config was loaded successfully.
*/
Try<Void> load();
/**
* Whether the config has been loaded.
* @return True if the config has been loaded.
*/
boolean isLoaded();
/**
* Saves the config to disk.
*/
Try<Void> save();
/**
* Gets the nodes for the config.
*
* @return The nodes for the config.
*/
NodeGroup getNodes();
/**
* 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.
*/
Try<Object> getProperty(String name);
/**
* 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.
*/
Try<Void> setProperty(String name, Object value);
/**
* Sets world access permissions should be enforced.
* @param enforceAccess The new value.
*/
void setEnforceAccess(boolean enforceAccess);
/**
* Gets enforceAccess.
* @return enforceAccess.
*/
boolean getEnforceAccess();
/**
* Sets whether the game mode should be enforced.
* @param enforceGameMode The new value.
*/
void setEnforceGameMode(boolean enforceGameMode);
/**
* Gets enforceGameMode value.
* @return True if game mode should be enforced.
*/
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.
* @param teleportIntercept The new value.
*/
void setTeleportIntercept(boolean teleportIntercept);
/**
* Gets teleportIntercept.
* @return teleportIntercept.
*/
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 join destination should be enabled.
* @param enableJoinDestination The new value
*/
void setEnableJoinDestination(boolean enableJoinDestination);
/**
* Gets enableJoinDestination.
* @return enableJoinDestination
*/
boolean getEnableJoinDestination();
/**
* Sets alwaysSpawnDestination.
* @param alwaysSpawnDestination The new value
*/
void setJoinDestination(String alwaysSpawnDestination);
/**
* Gets alwaysSpawnDestination.
* @return alwaysSpawnLocation
*/
String getJoinDestination();
/**
* 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 setEnablePrefixChat(boolean prefixChat);
/**
* Gets prefixChat.
* @return prefixChat.
*/
boolean isEnablePrefixChat();
/**
* Sets prefixChatFormat.
* @param prefixChatFormat The new value.
*/
void setPrefixChatFormat(String prefixChatFormat);
/**
* Gets prefixChatFormat.
* @return prefixChatFormat.
*/
String getPrefixChatFormat();
/**
* Sets whether to register the {@link MultiverseCorePlaceholders} class with PlaceholderAPI plugin.
* @param registerPapiHook The new value.
*/
void setRegisterPapiHook(boolean registerPapiHook);
/**
* Gets whether to register the {@link MultiverseCorePlaceholders} class with PlaceholderAPI plugin.
* @return registerPapiHook.
*/
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.
*
* @param silentStart true to suppress messages.
*/
void setSilentStart(boolean silentStart);
/**
* Whether we are suppressing startup messages.
*
* @return true if we are suppressing startup messages.
*/
boolean getSilentStart();
/**
* Sets whether or not the donation/patreon messages are shown.
*
* @param idonotwanttodonate True if donation/patreon messages should be shown.
*/
void setShowDonateMessage(boolean idonotwanttodonate);
/**
* Gets whether or not the donation/patreon messages are shown.
*
* @return True if donation/patreon messages should be shown.
*/
boolean isShowingDonateMessage();
}
@@ -0,0 +1,40 @@
/******************************************************************************
* Multiverse 2 Copyright (c) the Multiverse Team 2011. *
* Multiverse 2 is licensed under the BSD License. *
* For more information please check the README.md file included *
* with this project. *
******************************************************************************/
package org.mvplugins.multiverse.core.api;
/**
* 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 {
/**
* Saves all configs.
*
* @return Whether the config was successfully saved
*/
boolean saveAllConfigs();
/**
* Decrements the number of plugins that have specifically hooked into core.
*/
void decrementPluginCount();
/**
* Increments the number of plugins that have specifically hooked into core.
*/
void incrementPluginCount();
/**
* Returns the number of plugins that have specifically hooked into core.
*
* @return The number if plugins that have hooked into core.
*/
int getPluginCount();
}
@@ -0,0 +1,36 @@
/******************************************************************************
* Multiverse 2 Copyright (c) the Multiverse Team 2011. *
* Multiverse 2 is licensed under the BSD License. *
* For more information please check the README.md file included *
* with this project. *
******************************************************************************/
package org.mvplugins.multiverse.core.api;
/**
* This interface is implemented by every official Multiverse-plugin.
*/
public interface MVPlugin {
/**
* Gets the reference to MultiverseCore.
*
* @return A valid {@link org.mvplugins.multiverse.core.MultiverseCore}.
*/
MVCore getCore();
/**
* Allows Multiverse or a plugin to query another Multiverse plugin to see what version its protocol is. This
* number
* should change when something will break the code.
*
* @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();
}
@@ -0,0 +1,88 @@
package org.mvplugins.multiverse.core.api;
import java.util.concurrent.CompletableFuture;
import co.aikar.commands.BukkitCommandIssuer;
import org.bukkit.Location;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Entity;
import org.jvnet.hk2.annotations.Contract;
import org.mvplugins.multiverse.core.destination.ParsedDestination;
import org.mvplugins.multiverse.core.teleportation.TeleportResult;
/**
* Used to safely teleport people.
*/
@Contract
public interface SafeTTeleporter extends Teleporter {
/**
* Gets the next safe location around the given location.
* @param l A {@link Location}.
* @return A safe {@link Location}.
*/
Location getSafeLocation(Location l);
/**
* Gets the next safe location around the given location.
* @param l A {@link Location}.
* @param tolerance The tolerance.
* @param radius The radius.
* @return A safe {@link Location}.
*/
Location getSafeLocation(Location l, int tolerance, int radius);
/**
* 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
*/
@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
* it's not, will adjust the final destination accordingly.
*
* @param teleporter Person who issued the teleport command.
* @param teleportee Entity to teleport.
* @param location Location to teleport them to.
* @param safely Should the destination be checked for safety before teleport?
* @return true for success, false for failure.
*/
TeleportResult safelyTeleport(CommandSender teleporter, Entity teleportee, Location location,
boolean safely);
/**
* Returns a safe location for the entity to spawn at.
*
* @param entity The entity to spawn
* @param destination The MVDestination to take the entity to.
* @return A new location to spawn the entity at.
*/
Location getSafeLocation(Entity entity, DestinationInstance destination);
/**
* Finds a portal-block next to the specified {@link Location}.
* @param l The {@link Location}
* @return The next portal-block's {@link Location}.
*/
Location findPortalBlockNextTo(Location l);
}
@@ -0,0 +1,34 @@
package org.mvplugins.multiverse.core.api;
import java.util.concurrent.CompletableFuture;
import co.aikar.commands.BukkitCommandIssuer;
import org.bukkit.entity.Entity;
import org.jvnet.hk2.annotations.Contract;
import org.mvplugins.multiverse.core.destination.ParsedDestination;
import org.mvplugins.multiverse.core.teleportation.TeleportResult;
@Contract
public interface Teleporter {
/**
* 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
*/
@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);
}
@@ -0,0 +1,5 @@
/**
* The Multiverse-API, containing lots of interfaces that can be quite useful for other
* plugins when interacting with Multiverse.
*/
package org.mvplugins.multiverse.core.api;
@@ -1,7 +0,0 @@
package org.mvplugins.multiverse.core.command;
/**
* Extends a command class to add legacy alias annotations.
*/
public interface LegacyAliasCommand {
}
@@ -1,364 +0,0 @@
package org.mvplugins.multiverse.core.command;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.stream.Collectors;
import co.aikar.commands.BukkitCommandCompletionContext;
import co.aikar.commands.CommandIssuer;
import co.aikar.commands.PaperCommandCompletions;
import co.aikar.commands.RegisteredCommand;
import co.aikar.commands.RootCommand;
import com.dumptruckman.minecraft.util.Logging;
import io.vavr.control.Option;
import io.vavr.control.Try;
import jakarta.inject.Inject;
import org.apache.commons.lang3.Validate;
import org.bukkit.Bukkit;
import org.bukkit.Difficulty;
import org.bukkit.GameMode;
import org.bukkit.GameRule;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.entity.SpawnCategory;
import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Service;
import org.mvplugins.multiverse.core.anchor.AnchorManager;
import org.mvplugins.multiverse.core.anchor.MultiverseAnchor;
import org.mvplugins.multiverse.core.command.context.issueraware.IssuerAwareValue;
import org.mvplugins.multiverse.core.command.context.issueraware.MultiverseWorldValue;
import org.mvplugins.multiverse.core.command.context.issueraware.PlayerArrayValue;
import org.mvplugins.multiverse.core.config.CoreConfig;
import org.mvplugins.multiverse.core.config.node.functions.DefaultSuggesterProvider;
import org.mvplugins.multiverse.core.config.handle.PropertyModifyAction;
import org.mvplugins.multiverse.core.destination.DestinationInstance;
import org.mvplugins.multiverse.core.destination.DestinationSuggestionPacket;
import org.mvplugins.multiverse.core.destination.DestinationsProvider;
import org.mvplugins.multiverse.core.destination.core.WorldDestination;
import org.mvplugins.multiverse.core.permissions.CorePermissionsChecker;
import org.mvplugins.multiverse.core.utils.REPatterns;
import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld;
import org.mvplugins.multiverse.core.world.MultiverseWorld;
import org.mvplugins.multiverse.core.world.WorldManager;
import org.mvplugins.multiverse.core.world.generators.GeneratorPlugin;
import org.mvplugins.multiverse.core.world.generators.GeneratorProvider;
import static org.mvplugins.multiverse.core.utils.StringFormatter.addonToCommaSeperated;
@Service
public class MVCommandCompletions extends PaperCommandCompletions {
private final MVCommandManager commandManager;
private final WorldManager worldManager;
private final DestinationsProvider destinationsProvider;
private final CoreConfig config;
private final CorePermissionsChecker corePermissionsChecker;
private final AnchorManager anchorManager;
private final GeneratorProvider generatorProvider;
@Inject
MVCommandCompletions(
@NotNull MVCommandManager mvCommandManager,
@NotNull WorldManager worldManager,
@NotNull DestinationsProvider destinationsProvider,
@NotNull CoreConfig config,
@NotNull CorePermissionsChecker corePermissionsChecker,
@NotNull AnchorManager anchorManager,
@NotNull GeneratorProvider generatorProvider
) {
super(mvCommandManager);
this.commandManager = mvCommandManager;
this.worldManager = worldManager;
this.destinationsProvider = destinationsProvider;
this.config = config;
this.corePermissionsChecker = corePermissionsChecker;
this.anchorManager = anchorManager;
this.generatorProvider = generatorProvider;
registerAsyncCompletion("anchornames", this::suggestAnchorNames);
registerAsyncCompletion("commands", this::suggestCommands);
registerAsyncCompletion("destinations", this::suggestDestinations);
registerStaticCompletion("difficulties", suggestEnums(Difficulty.class));
registerStaticCompletion("environments", List.of("normal", "nether", "the_end")); // Don't tab complete the "custom" environment
registerAsyncCompletion("flags", this::suggestFlags);
registerStaticCompletion("gamemodes", suggestEnums(GameMode.class));
registerStaticCompletion("gamerules", this::suggestGamerules);
registerAsyncCompletion("gamerulesvalues", this::suggestGamerulesValues);
registerAsyncCompletion("generatorplugins", this::suggestGeneratorPlugins);
registerStaticCompletion("materials", suggestEnums(Material.class));
registerStaticCompletion("mvconfigs", config.getStringPropertyHandle().getAllPropertyNames());
registerAsyncCompletion("mvconfigvalues", this::suggestMVConfigValues);
registerAsyncCompletion("mvworlds", this::suggestMVWorlds);
registerAsyncCompletion("mvworldpropsname", this::suggestMVWorldPropsName);
registerAsyncCompletion("mvworldpropsvalue", this::suggestMVWorldPropsValue);
registerCompletion("playersarray", this::suggestPlayersArray); // getting online players cannot be async
registerStaticCompletion("propsmodifyaction", suggestEnums(PropertyModifyAction.class));
registerStaticCompletion("spawncategories", suggestEnums(SpawnCategory.class));
registerAsyncCompletion("spawncategorypropsname", this::suggestSpawnCategoryPropsName);
registerAsyncCompletion("spawncategorypropsvalue", this::suggestSpawnCategoryPropsValue);
setDefaultCompletion("destinations", DestinationInstance.class);
setDefaultCompletion("difficulties", Difficulty.class);
setDefaultCompletion("environments", World.Environment.class);
setDefaultCompletion("flags", String[].class);
setDefaultCompletion("gamemodes", GameMode.class);
setDefaultCompletion("gamerules", GameRule.class);
setDefaultCompletion("mvworlds", LoadedMultiverseWorld.class);
}
@Override
public CommandCompletionHandler registerCompletion(String id, CommandCompletionHandler<BukkitCommandCompletionContext> handler) {
return super.registerCompletion(id, context ->
completeWithPreconditions(context, handler));
}
@Override
public CommandCompletionHandler registerAsyncCompletion(String id, AsyncCommandCompletionHandler<BukkitCommandCompletionContext> handler) {
return super.registerAsyncCompletion(id, context ->
completeWithPreconditions(context, handler));
}
private Collection<String> completeWithPreconditions(
BukkitCommandCompletionContext context,
CommandCompletionHandler<BukkitCommandCompletionContext> handler) {
if (context.hasConfig("playerOnly") && !context.getIssuer().isPlayer()) {
return Collections.emptyList();
}
if (context.hasConfig("byIssuerForArg")) {
Boolean byIssuerForArg = Try.of(() -> context.getContextValueByName(IssuerAwareValue.class, context.getConfig("byIssuerForArg")))
.map(IssuerAwareValue::isByIssuer)
.getOrElse(false);
if (!byIssuerForArg) {
return Collections.emptyList();
}
}
if (context.hasConfig("notByIssuerForArg")) {
Boolean byIssuerForArg = Try.of(() -> context.getContextValueByName(IssuerAwareValue.class, context.getConfig("notByIssuerForArg")))
.map(IssuerAwareValue::isByIssuer)
.getOrElse(false);
if (byIssuerForArg) {
return Collections.emptyList();
}
}
if (context.hasConfig("resolveUntil")) {
if (!Try.run(() -> context.getContextValueByName(Object.class, context.getConfig("resolveUntil"))).isSuccess()) {
return Collections.emptyList();
}
}
if (context.hasConfig("checkPermissions")) {
for (String permission : REPatterns.SEMICOLON.split(context.getConfig("checkPermissions"))) {
if (!commandManager.getCommandPermissions().hasPermission(context.getIssuer(), permission)) {
return Collections.emptyList();
}
}
}
if (context.hasConfig("multiple")) {
return addonToCommaSeperated(context.getInput(), handler.getCompletions(context));
}
return handler.getCompletions(context);
}
/**
* Shortcut to suggest enums values
*
* @param enumClass The enum class with values
* @return A collection of possible string values
* @param <T> The enum type
*/
public <T extends Enum<T>> Collection<String> suggestEnums(Class<T> enumClass) {
return EnumSet.allOf(enumClass).stream()
.map(Enum::name)
.map(name -> name.toLowerCase(Locale.ENGLISH))
.toList();
}
private Collection<String> suggestAnchorNames(BukkitCommandCompletionContext context) {
return anchorManager.getAnchors(context.getPlayer()).stream()
.map(MultiverseAnchor::getName)
.toList();
}
private Collection<String> suggestCommands(BukkitCommandCompletionContext context) {
String rootCmdName = context.getConfig();
if (rootCmdName == null) {
return Collections.emptyList();
}
RootCommand rootCommand = this.commandManager.getRegisteredRootCommands().stream()
.unordered()
.filter(c -> c.getCommandName().equals(rootCmdName))
.findFirst()
.orElse(null);
if (rootCommand == null) {
return Collections.emptyList();
}
return rootCommand.getSubCommands().entries().stream()
.filter(entry -> checkPerms(context.getIssuer(), entry.getValue()))
.map(Map.Entry::getKey)
.filter(cmdName -> !cmdName.startsWith("__"))
.collect(Collectors.toList());
}
private boolean checkPerms(CommandIssuer issuer, RegisteredCommand<?> command) {
return this.commandManager.hasPermission(issuer, command.getRequiredPermissions());
}
private Collection<String> suggestDestinations(BukkitCommandCompletionContext context) {
return Try.of(() -> context.getContextValue(PlayerArrayValue.class))
.map(PlayerArrayValue::value)
.recover(IllegalStateException.class, e -> context.getContextValue(Player[].class))
.recover(IllegalStateException.class, e -> new Player[]{context.getContextValue(Player.class)})
.map(players -> {
if (players.length == 0) {
// Most likely console did not specify a player
return Collections.<String>emptyList();
}
CommandSender sender = context.getIssuer().getIssuer();
if (context.hasConfig("othersOnly") && (players.length == 1 && players[0].equals(sender))) {
return Collections.<String>emptyList();
}
return suggestDestinationsWithPerms(sender, Arrays.asList(players), context.getInput());
})
.getOrElse(Collections.emptyList());
}
private Collection<String> suggestDestinationsWithPerms(CommandSender teleporter, List<Entity> teleportees, String deststring) {
return destinationsProvider.suggestDestinations(teleporter, deststring).stream()
.filter(packet -> !config.getSimplifiedDestinationTabCompletion()
|| packet.destination() instanceof WorldDestination
|| deststring.startsWith(packet.destination().getIdentifier() + ":"))
.filter(packet -> corePermissionsChecker
.checkDestinationPacketPermission(teleporter, teleportees, packet))
.map(DestinationSuggestionPacket::parsableString)
.toList();
}
private Collection<String> suggestFlags(@NotNull BukkitCommandCompletionContext context) {
String groupName = context.getConfig("groupName", "");
return Try.of(() -> context.getContextValue(String[].class))
.map(flags -> commandManager.getFlagsManager().suggest(groupName, flags))
.getOrElse(Collections.emptyList());
}
private Collection<String> suggestGamerules() {
return Arrays.stream(GameRule.values()).map(GameRule::getName).collect(Collectors.toList());
}
private Collection<String> suggestGamerulesValues(BukkitCommandCompletionContext context) {
return Try.of(() -> context.getContextValue(GameRule.class))
// Just use our suggester from configuration lib since gamerules are only boolean or int
.mapTry(gamerule -> Option.of(DefaultSuggesterProvider.getDefaultSuggester(gamerule.getType()))
.map(s -> s.suggest(context.getInput())).getOrElse(Collections.emptyList()))
.getOrElse(Collections.emptyList());
}
private Collection<String> suggestGeneratorPlugins(BukkitCommandCompletionContext context) {
return generatorProvider.getRegisteredGeneratorPlugins().stream()
.map(GeneratorPlugin::getPluginName)
.toList();
}
private Collection<String> suggestMVConfigValues(BukkitCommandCompletionContext context) {
return Try.of(() -> context.getContextValue(String.class))
.map(propertyName -> config.getStringPropertyHandle()
.getSuggestedPropertyValue(propertyName, context.getInput(), PropertyModifyAction.SET, context.getSender()))
.getOrElse(Collections.emptyList());
}
private Collection<String> suggestMVWorlds(BukkitCommandCompletionContext context) {
String scope = context.getConfig("scope", "loaded");
switch (scope) {
case "both" -> {
return worldManager.getWorlds().stream()
.map(MultiverseWorld::getTabCompleteName)
.toList();
}
case "loaded" -> {
return worldManager.getLoadedWorlds().stream()
.map(MultiverseWorld::getTabCompleteName)
.toList();
}
case "unloaded" -> {
return worldManager.getUnloadedWorlds().stream()
.map(MultiverseWorld::getTabCompleteName)
.toList();
}
case "potential" -> {
return worldManager.getPotentialWorlds();
}
}
Logging.severe("Invalid MVWorld scope: " + scope);
return Collections.emptyList();
}
private Collection<String> suggestMVWorldPropsName(BukkitCommandCompletionContext context) {
return Try.of(() -> {
MultiverseWorld world = context.getContextValue(MultiverseWorldValue.class).value();
PropertyModifyAction action = context.getContextValue(PropertyModifyAction.class);
return world.getStringPropertyHandle().getModifiablePropertyNames(action);
}).getOrElse(Collections.emptyList());
}
private Collection<String> suggestMVWorldPropsValue(BukkitCommandCompletionContext context) {
return Try.of(() -> {
MultiverseWorld world = context.getContextValue(MultiverseWorldValue.class).value();
PropertyModifyAction action = context.getContextValue(PropertyModifyAction.class);
String propertyName = context.getContextValue(String.class);
return world.getStringPropertyHandle().getSuggestedPropertyValue(propertyName, context.getInput(), action, context.getSender());
}).getOrElse(Collections.emptyList());
}
private Collection<String> suggestPlayersArray(BukkitCommandCompletionContext context) {
CommandSender sender = context.getSender();
Validate.notNull(sender, "Sender cannot be null");
Player senderPlayer = sender instanceof Player ? (Player)sender : null;
List<String> matchedPlayers = new ArrayList<>();
for(Player player : Bukkit.getOnlinePlayers()) {
String name = player.getName();
if ((senderPlayer == null || senderPlayer.canSee(player))
&& (!context.hasConfig("excludeSelf") || !player.equals(senderPlayer))) {
matchedPlayers.add(name);
}
}
return addonToCommaSeperated(context.getInput(), matchedPlayers);
}
private Collection<String> suggestSpawnCategoryPropsName(BukkitCommandCompletionContext context) {
return Try.of(() -> {
MultiverseWorld world = context.getContextValue(MultiverseWorld.class);
SpawnCategory spawnCategory = context.getContextValue(SpawnCategory.class);
PropertyModifyAction action = context.getContextValue(PropertyModifyAction.class);
return world.getEntitySpawnConfig()
.getSpawnCategoryConfig(spawnCategory)
.getStringPropertyHandle()
.getModifiablePropertyNames(action);
}).getOrElse(Collections.emptyList());
}
private Collection<String> suggestSpawnCategoryPropsValue(BukkitCommandCompletionContext context) {
return Try.of(() -> {
MultiverseWorld world = context.getContextValue(MultiverseWorld.class);
SpawnCategory spawnCategory = context.getContextValue(SpawnCategory.class);
PropertyModifyAction action = context.getContextValue(PropertyModifyAction.class);
String propertyName = context.getContextValue(String.class);
return world.getEntitySpawnConfig()
.getSpawnCategoryConfig(spawnCategory)
.getStringPropertyHandle()
.getSuggestedPropertyValue(propertyName, context.getInput(), action, context.getSender());
}).getOrElse(Collections.emptyList());
}
}
@@ -1,396 +0,0 @@
package org.mvplugins.multiverse.core.command;
import java.util.HashSet;
import java.util.Set;
import co.aikar.commands.ACFUtil;
import co.aikar.commands.BukkitCommandExecutionContext;
import co.aikar.commands.BukkitCommandIssuer;
import co.aikar.commands.InvalidCommandArgument;
import co.aikar.commands.MinecraftMessageKeys;
import co.aikar.commands.PaperCommandContexts;
import co.aikar.commands.contexts.ContextResolver;
import com.google.common.base.Strings;
import io.vavr.control.Option;
import io.vavr.control.Try;
import jakarta.inject.Inject;
import org.bukkit.Bukkit;
import org.bukkit.GameRule;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.command.BlockCommandSender;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.entity.SpawnCategory;
import org.jetbrains.annotations.Nullable;
import org.jvnet.hk2.annotations.Service;
import org.mvplugins.multiverse.core.anchor.AnchorManager;
import org.mvplugins.multiverse.core.anchor.MultiverseAnchor;
import org.mvplugins.multiverse.core.command.context.GameRuleValue;
import org.mvplugins.multiverse.core.command.context.issueraware.IssuerAwareContextBuilder;
import org.mvplugins.multiverse.core.command.context.PlayerLocation;
import org.mvplugins.multiverse.core.command.context.issueraware.MultiverseWorldValue;
import org.mvplugins.multiverse.core.command.context.issueraware.PlayerArrayValue;
import org.mvplugins.multiverse.core.config.CoreConfig;
import org.mvplugins.multiverse.core.destination.DestinationInstance;
import org.mvplugins.multiverse.core.destination.DestinationsProvider;
import org.mvplugins.multiverse.core.display.filters.ContentFilter;
import org.mvplugins.multiverse.core.display.filters.DefaultContentFilter;
import org.mvplugins.multiverse.core.display.filters.RegexContentFilter;
import org.mvplugins.multiverse.core.exceptions.command.MVInvalidCommandArgument;
import org.mvplugins.multiverse.core.locale.message.Message;
import org.mvplugins.multiverse.core.utils.PlayerFinder;
import org.mvplugins.multiverse.core.utils.REPatterns;
import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld;
import org.mvplugins.multiverse.core.world.MultiverseWorld;
import org.mvplugins.multiverse.core.world.WorldManager;
import org.mvplugins.multiverse.core.world.generators.GeneratorPlugin;
import org.mvplugins.multiverse.core.world.generators.GeneratorProvider;
@Service
public class MVCommandContexts extends PaperCommandContexts {
private final MVCommandManager mvCommandManager;
private final DestinationsProvider destinationsProvider;
private final WorldManager worldManager;
private final CoreConfig config;
private final AnchorManager anchorManager;
private final GeneratorProvider generatorProvider;
@Inject
MVCommandContexts(
MVCommandManager mvCommandManager,
DestinationsProvider destinationsProvider,
WorldManager worldManager,
CoreConfig config,
AnchorManager anchorManager,
GeneratorProvider generatorProvider
) {
super(mvCommandManager);
this.mvCommandManager = mvCommandManager;
this.destinationsProvider = destinationsProvider;
this.worldManager = worldManager;
this.config = config;
this.anchorManager = anchorManager;
this.generatorProvider = generatorProvider;
registerIssuerOnlyContext(BukkitCommandIssuer.class, BukkitCommandExecutionContext::getIssuer);
registerIssuerOnlyContext(MVCommandIssuer.class, this::parseMVCommandIssuer);
registerOptionalContext(ContentFilter.class, this::parseContentFilter);
registerContext(DestinationInstance.class, this::parseDestination);
registerContext(GameRule.class, this::parseGameRule);
registerContext(GameRuleValue.class, this::parseGameRuleValue);
registerContext(GeneratorPlugin.class, this::parseGeneratorPlugin);
registerIssuerAwareContext(LoadedMultiverseWorld.class, loadedMultiverseWorldContextBuilder().generateContext());
registerIssuerAwareContext(LoadedMultiverseWorld[].class, loadedMultiverseWorldArrayContextBuilder().generateContext());
registerIssuerAwareContext(MultiverseWorld.class, multiverseWorldContextBuilder().generateContext());
registerIssuerAwareContext(MultiverseWorldValue.class, multiverseWorldContextBuilder().generateContext(MultiverseWorldValue::new));
registerIssuerAwareContext(MultiverseWorld[].class, multiverseWorldArrayContextBuilder().generateContext());
registerContext(MultiverseAnchor.class, this::parseMultiverseAnchor);
registerIssuerAwareContext(Player.class, playerContextBuilder().generateContext());
registerIssuerAwareContext(Player[].class, playerArrayContextBuilder().generateContext());
registerIssuerAwareContext(PlayerArrayValue.class, playerArrayContextBuilder().generateContext(PlayerArrayValue::new));
registerIssuerAwareContext(PlayerLocation.class, this::parsePlayerLocation);
registerContext(SpawnCategory[].class, this::parseSpawnCategories);
}
private MVCommandIssuer parseMVCommandIssuer(BukkitCommandExecutionContext context) {
if (context.getIssuer() instanceof MVCommandIssuer) {
return (MVCommandIssuer) context.getIssuer();
}
return mvCommandManager.getCommandIssuer(context.getSender());
}
private ContentFilter parseContentFilter(BukkitCommandExecutionContext context) {
if (Strings.isNullOrEmpty(context.getFirstArg())) {
return DefaultContentFilter.get();
}
String filterString = context.popFirstArg();
return RegexContentFilter.fromString(filterString);
}
private DestinationInstance<?, ?> parseDestination(BukkitCommandExecutionContext context) {
String destination = context.popFirstArg();
if (Strings.isNullOrEmpty(destination)) {
throw new InvalidCommandArgument("No destination specified.");
}
return destinationsProvider.parseDestination(context.getSender(), destination)
.getOrThrow(failure ->
new InvalidCommandArgument(failure.getFailureMessage().formatted(context.getIssuer())));
}
private GameRule<?> parseGameRule(BukkitCommandExecutionContext context) {
String gameRuleName = context.popFirstArg();
if (Strings.isNullOrEmpty(gameRuleName)) {
throw new InvalidCommandArgument("No game rule specified.");
}
GameRule<?> gameRule = GameRule.getByName(gameRuleName);
if (gameRule == null) {
throw new InvalidCommandArgument("The game rule " + gameRuleName + " is not valid.");
}
return gameRule;
}
private GameRuleValue parseGameRuleValue(BukkitCommandExecutionContext context) {
GameRule<?> gameRule = (GameRule<?>) context.getResolvedArg(GameRule.class);
if (gameRule == null) {
throw new InvalidCommandArgument("No game rule specified.");
}
String valueString = context.getFirstArg();
if (Strings.isNullOrEmpty(valueString)) {
throw new InvalidCommandArgument("No game rule value specified.");
}
ContextResolver<?, BukkitCommandExecutionContext> resolver = getResolver(gameRule.getType());
if (resolver == null) {
return new GameRuleValue(valueString);
}
Object resolvedValue = resolver.getContext(context);
if (resolvedValue == null) {
throw new InvalidCommandArgument("The game rule value " + valueString + " is not valid for game rule " + gameRule.getName() + ".");
}
return new GameRuleValue(resolvedValue);
}
private GeneratorPlugin parseGeneratorPlugin(BukkitCommandExecutionContext context) {
GeneratorPlugin generatorPlugin = generatorProvider.getGeneratorPlugin(context.popFirstArg());
if (generatorPlugin == null) {
throw new InvalidCommandArgument("Invalid generator plugin: " + context.getFirstArg());
}
return generatorPlugin;
}
private IssuerAwareContextBuilder<LoadedMultiverseWorld> loadedMultiverseWorldContextBuilder() {
return new IssuerAwareContextBuilder<LoadedMultiverseWorld>()
.fromPlayer((context, player) -> worldManager.getLoadedWorld(player.getWorld()).getOrNull())
.fromInput((context, input) -> getLoadedMultiverseWorld(input))
.issuerOnlyFailMessage((context) -> Message.of("This command can only be used by a player in a loaded Multiverse World."))
.issuerAwarePlayerFailMessage((context, player) -> Message.of("You are not in a loaded multiverse world. Either specify a multiverse world name or use this command in a loaded multiverse world."))
.issuerAwareInputFailMessage((context, input) -> Message.of("World '" + input + "' is not a loaded multiverse world. Remember to specify the world name when using this command in console."))
.inputOnlyFailMessage((context, input) -> Message.of("World " + input + " is not a loaded multiverse world."));
}
private IssuerAwareContextBuilder<LoadedMultiverseWorld[]> loadedMultiverseWorldArrayContextBuilder() {
return new IssuerAwareContextBuilder<LoadedMultiverseWorld[]>()
.fromPlayer((context, player) -> worldManager.getLoadedWorld(player.getWorld())
.map(world -> new LoadedMultiverseWorld[]{world})
.getOrNull())
.fromInput((context, input) -> {
String[] worldNames = input == null ? new String[0] : REPatterns.COMMA.split(input);
Set<LoadedMultiverseWorld> worlds = new HashSet<>(worldNames.length);
for (String worldName : worldNames) {
if ("*".equals(worldName)) {
worlds.addAll(worldManager.getLoadedWorlds());
break;
}
LoadedMultiverseWorld world = getLoadedMultiverseWorld(worldName);
if (world == null) {
throw new InvalidCommandArgument("World " + worldName + " is not a loaded multiverse world.");
}
worlds.add(world);
}
return worlds.isEmpty() ? null : worlds.toArray(new LoadedMultiverseWorld[0]);
})
.issuerOnlyFailMessage((context) -> Message.of("This command can only be used by a player in a loaded Multiverse World."))
.issuerAwarePlayerFailMessage((context, player) -> Message.of("You are not in a loaded multiverse world. Either specify a multiverse world name or use this command in a loaded multiverse world."))
.issuerAwareInputFailMessage((context, input) -> Message.of("World '" + input + "' is not a loaded multiverse world. Remember to specify the world name when using this command in console."))
.inputOnlyFailMessage((context, input) -> Message.of("World " + input + " is not a loaded multiverse world."));
}
@Nullable
private LoadedMultiverseWorld getLoadedMultiverseWorld(String worldName) {
return config.getResolveAliasName()
? worldManager.getLoadedWorldByNameOrAlias(worldName).getOrNull()
: worldManager.getLoadedWorld(worldName).getOrNull();
}
private IssuerAwareContextBuilder<MultiverseWorld> multiverseWorldContextBuilder() {
return new IssuerAwareContextBuilder<MultiverseWorld>()
.fromPlayer((context, player) -> worldManager.getWorld(player.getWorld()).getOrNull())
.fromInput((context, input) -> getMultiverseWorld(input))
.issuerOnlyFailMessage((context) -> Message.of("This command can only be used by a player in a Multiverse World."))
.issuerAwarePlayerFailMessage((context, player) -> Message.of("You are not in a multiverse world. Either specify a multiverse world name or use this command in a multiverse world."))
.issuerAwareInputFailMessage((context, input) -> Message.of("World '" + input + "' is not a multiverse world. Remember to specify the world name when using this command in console."))
.inputOnlyFailMessage((context, input) -> Message.of("World " + input + " is not a multiverse world."));
}
private IssuerAwareContextBuilder<MultiverseWorld[]> multiverseWorldArrayContextBuilder() {
return new IssuerAwareContextBuilder<MultiverseWorld[]>()
.fromPlayer((context, player) -> worldManager.getWorld(player.getWorld())
.map(world -> new MultiverseWorld[]{world})
.getOrNull())
.fromInput((context, input) -> {
String[] worldNames = input == null ? new String[0] : REPatterns.COMMA.split(input);
Set<MultiverseWorld> worlds = new HashSet<>(worldNames.length);
for (String worldName : worldNames) {
if ("*".equals(worldName)) {
worlds.addAll(worldManager.getWorlds());
break;
}
MultiverseWorld world = getMultiverseWorld(worldName);
if (world == null) {
throw new InvalidCommandArgument("World " + worldName + " is not a multiverse world.");
}
worlds.add(world);
}
return worlds.isEmpty() ? null : worlds.toArray(new MultiverseWorld[0]);
})
.issuerOnlyFailMessage((context) -> Message.of("This command can only be used by a player in a Multiverse World."))
.issuerAwarePlayerFailMessage((context, player) -> Message.of("You are not in a multiverse world. Either specify a multiverse world name or use this command in a multiverse world."))
.issuerAwareInputFailMessage((context, input) -> Message.of("World '" + input + "' is not a multiverse world. Remember to specify the world name when using this command in console."))
.inputOnlyFailMessage((context, input) -> Message.of("World " + input + " is not a multiverse world."));
}
@Nullable
private MultiverseWorld getMultiverseWorld(String worldName) {
return config.getResolveAliasName()
? worldManager.getWorldByNameOrAlias(worldName).getOrNull()
: worldManager.getWorld(worldName).getOrNull();
}
private MultiverseAnchor parseMultiverseAnchor(BukkitCommandExecutionContext context) {
String anchorName = context.popFirstArg();
return anchorManager.getAnchor(anchorName)
.getOrElseThrow(() -> new InvalidCommandArgument("The anchor '" +anchorName + "' does not exist."));
}
private IssuerAwareContextBuilder<Player> playerContextBuilder() {
return new IssuerAwareContextBuilder<Player>()
.fromPlayer((context, player) -> player)
.fromInput((context, input) -> PlayerFinder.get(input, context.getSender()))
.issuerOnlyFailMessage((context) -> Message.of("This command can only be used by a player."))
.issuerAwareInputFailMessage((context, input) -> Message.of("Invalid player: " + input + ". Either specify an online player or use this command as a player."))
.inputOnlyFailMessage((context, input) -> Message.of("Player " + input + " not found."));
}
private IssuerAwareContextBuilder<Player[]> playerArrayContextBuilder() {
return new IssuerAwareContextBuilder<Player[]>()
.fromPlayer((context, player) -> new Player[]{player})
.fromInput((context, input) -> PlayerFinder
.tryGetMulti(input, context.getSender())
.map(list -> list.toArray(new Player[0]))
.map(arr -> (arr.length == 0) ? null : arr)
.getOrElseThrow(failure -> {
if (failure instanceof MVInvalidCommandArgument mvFailure) {
throw mvFailure;
}
throw new InvalidCommandArgument(failure.getLocalizedMessage() + " "
+ Option.of(failure.getCause()).map(Throwable::getLocalizedMessage).getOrElse(""));
}))
.issuerOnlyFailMessage((context) -> Message.of("This command can only be used by a player."))
.issuerAwareInputFailMessage((context, input) -> Message.of("Invalid player: " + input + ". Either specify an online player or use this command as a player."))
.inputOnlyFailMessage((context, input) -> {
if (PlayerFinder.isSelector(input)) {
return Message.of("No player(s) matched selector: " + input + ".");
}
return Message.of("Player(s) " + input + " not found.");
});
}
private PlayerLocation parsePlayerLocation(BukkitCommandExecutionContext context) {
Try<Location> location = Try.of(() -> parseLocationFromInput(context.getFirstArg(), context.getSender()));
if (location.isSuccess()) {
context.popFirstArg();
return new PlayerLocation(location.get());
}
if (context.getPlayer() != null) {
return new PlayerLocation(context.getPlayer().getLocation());
}
if (context.getFirstArg() == null) {
throw new InvalidCommandArgument("You must specify a world location when using this command in console.");
}
if (location.getCause() instanceof InvalidCommandArgument) {
throw (InvalidCommandArgument)location.getCause();
}
throw new RuntimeException(location.getCause());
}
// copied from ACF
private Location parseLocationFromInput(String input, CommandSender sender) {
String[] split = REPatterns.COLON.split(input, 2);
if (split.length == 0) {
throw new InvalidCommandArgument(true);
} else if (split.length < 2 && !(sender instanceof Player) && !(sender instanceof BlockCommandSender)) {
throw new InvalidCommandArgument(MinecraftMessageKeys.LOCATION_PLEASE_SPECIFY_WORLD, new String[0]);
} else {
Location sourceLoc = null;
String world;
String rest;
if (split.length == 2) {
world = split[0];
rest = split[1];
} else if (sender instanceof Player) {
sourceLoc = ((Player)sender).getLocation();
world = sourceLoc.getWorld().getName();
rest = split[0];
} else {
if (!(sender instanceof BlockCommandSender)) {
throw new InvalidCommandArgument(true);
}
sourceLoc = ((BlockCommandSender)sender).getBlock().getLocation();
world = sourceLoc.getWorld().getName();
rest = split[0];
}
boolean rel = rest.startsWith("~");
split = REPatterns.COMMA.split(rel ? rest.substring(1) : rest);
if (split.length < 3) {
throw new InvalidCommandArgument(MinecraftMessageKeys.LOCATION_PLEASE_SPECIFY_XYZ, new String[0]);
} else {
Double x = ACFUtil.parseDouble(split[0], rel ? (double)0.0F : null);
Double y = ACFUtil.parseDouble(split[1], rel ? (double)0.0F : null);
Double z = ACFUtil.parseDouble(split[2], rel ? (double)0.0F : null);
if (sourceLoc != null && rel) {
x = x + sourceLoc.getX();
y = y + sourceLoc.getY();
z = z + sourceLoc.getZ();
} else if (rel) {
throw new InvalidCommandArgument(MinecraftMessageKeys.LOCATION_CONSOLE_NOT_RELATIVE, new String[0]);
}
if (x != null && y != null && z != null) {
World worldObj = Bukkit.getWorld(world);
if (worldObj == null) {
throw new InvalidCommandArgument(MinecraftMessageKeys.INVALID_WORLD, new String[0]);
} else if (split.length >= 5) {
Float yaw = ACFUtil.parseFloat(split[3]);
Float pitch = ACFUtil.parseFloat(split[4]);
if (pitch != null && yaw != null) {
return new Location(worldObj, x, y, z, yaw, pitch);
} else {
throw new InvalidCommandArgument(MinecraftMessageKeys.LOCATION_PLEASE_SPECIFY_XYZ, new String[0]);
}
} else {
return new Location(worldObj, x, y, z);
}
} else {
throw new InvalidCommandArgument(MinecraftMessageKeys.LOCATION_PLEASE_SPECIFY_XYZ, new String[0]);
}
}
}
}
private SpawnCategory[] parseSpawnCategories(BukkitCommandExecutionContext context) {
if (context.isOptional() && context.getArgs().isEmpty()) {
return new SpawnCategory[0];
}
Set<SpawnCategory> categories = new HashSet<>();
String[] split = REPatterns.COMMA.split(context.popFirstArg());
for (String category : split) {
SpawnCategory spawnCategory = ACFUtil.simpleMatch(SpawnCategory.class, category);
if (spawnCategory != null) {
categories.add(spawnCategory);
}
}
return categories.toArray(new SpawnCategory[0]);
}
}
@@ -1,121 +0,0 @@
package org.mvplugins.multiverse.core.command;
import co.aikar.commands.BukkitCommandIssuer;
import co.aikar.commands.MessageType;
import co.aikar.locales.MessageKeyProvider;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
import org.mvplugins.multiverse.core.locale.message.Message;
import org.mvplugins.multiverse.core.locale.message.MessageReplacement;
import org.mvplugins.multiverse.core.utils.text.ChatTextFormatter;
public class MVCommandIssuer extends BukkitCommandIssuer {
private final MVCommandManager commandManager;
MVCommandIssuer(@NotNull MVCommandManager commandManager, @NotNull CommandSender sender) {
super(commandManager, sender);
this.commandManager = commandManager;
}
@Override
public MVCommandManager getManager() {
return commandManager;
}
@Override
public void sendMessageInternal(String message) {
ChatTextFormatter.sendFormattedMessage(getIssuer(), message);
}
public void sendError(String message, MessageReplacement... replacements) {
sendMessage(MessageType.ERROR, message, replacements);
}
public void sendSyntax(String message, MessageReplacement... replacements) {
sendMessage(MessageType.SYNTAX, message, replacements);
}
public void sendInfo(String message, MessageReplacement... replacements) {
sendMessage(MessageType.INFO, message, replacements);
}
public void sendMessage(String message, MessageReplacement... replacements) {
sendMessage(null, Message.of(message, replacements));
}
public void sendMessage(MessageType type, String message, MessageReplacement... replacements) {
sendMessage(type, Message.of(message, replacements));
}
public void sendMessage(MessageType messageType, String message) {
var formatter = getManager().getFormat(messageType);
if (formatter != null) {
sendMessage(formatter.format(message));
} else {
sendMessage(message);
}
}
public void sendError(MessageKeyProvider key) {
sendMessage(MessageType.ERROR, key, new String[0]);
}
public void sendSyntax(MessageKeyProvider key) {
sendMessage(MessageType.SYNTAX, key, new String[0]);
}
public void sendInfo(MessageKeyProvider key) {
sendMessage(MessageType.INFO, key, new String[0]);
}
public void sendError(MessageKeyProvider key, MessageReplacement... replacements) {
sendMessage(MessageType.ERROR, key, replacements);
}
public void sendSyntax(MessageKeyProvider key, MessageReplacement... replacements) {
sendMessage(MessageType.SYNTAX, key, replacements);
}
public void sendInfo(MessageKeyProvider key, MessageReplacement... replacements) {
sendMessage(MessageType.INFO, key, replacements);
}
public void sendMessage(MessageKeyProvider key, MessageReplacement... replacements) {
sendMessage(null, Message.of(key, replacements));
}
public void sendMessage(MessageType messageType, MessageKeyProvider key, MessageReplacement... replacements) {
sendMessage(messageType, Message.of(key, replacements));
}
public void sendError(Message message) {
sendMessage(MessageType.ERROR, message);
}
public void sendSyntax(Message message) {
sendMessage(MessageType.SYNTAX, message);
}
public void sendInfo(Message message) {
sendMessage(MessageType.INFO, message);
}
public void sendMessage(Message message) {
sendMessage(null, message);
}
public void sendMessage(MessageType messageType, Message message) {
if (message instanceof MessageKeyProvider) {
sendMessage(messageType, (MessageKeyProvider) message,
message.getReplacements(getManager().getLocales(), this));
} else {
var formatter = getManager().getFormat(messageType);
if (formatter != null) {
sendMessage(formatter.format(message.formatted(getManager().getLocales(), this)));
} else {
sendMessage(message.formatted(getManager().getLocales(), this));
}
}
}
}
@@ -1,62 +0,0 @@
package org.mvplugins.multiverse.core.command;
import co.aikar.commands.CommandIssuer;
import co.aikar.commands.annotation.CommandPermission;
import io.vavr.control.Option;
import jakarta.inject.Inject;
import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Service;
import org.mvplugins.multiverse.core.permissions.CorePermissionsChecker;
import org.mvplugins.multiverse.core.permissions.PermissionUtils;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.function.Predicate;
import static org.mvplugins.multiverse.core.permissions.CorePermissionsChecker.*;
/**
* Maps permission checking to custom logic for commands, to allow more complex permission checking.
*/
@Service
public class MVCommandPermissions {
private final Map<String, Predicate<CommandIssuer>> permissionsCheckMap;
@Inject
MVCommandPermissions(@NotNull CorePermissionsChecker permissionsChecker) {
this.permissionsCheckMap = new HashMap<>();
registerPermissionChecker("mvteleport", issuer -> permissionsChecker.hasAnyTeleportPermission(issuer.getIssuer()));
registerPermissionChecker("mvteleportother", issuer -> permissionsChecker.hasAnyTeleportPermission(issuer.getIssuer(), Scope.OTHER));
registerPermissionChecker("mvspawn", issuer -> permissionsChecker.hasAnySpawnPermission(issuer.getIssuer()));
registerPermissionChecker("mvspawnother", issuer -> permissionsChecker.hasAnySpawnPermission(issuer.getIssuer(), Scope.OTHER));
}
/**
* Registers a custom permission checker callback. Use `@id-name` in {@link CommandPermission} decorator to use
* the callback instead of the default permission string checking.
*
* @param id The permission id
* @param checker The permission checker callback
*/
public void registerPermissionChecker(String id, Predicate<CommandIssuer> checker) {
permissionsCheckMap.put(prepareId(id), checker);
}
private static @NotNull String prepareId(String id) {
return (id.startsWith("@") ? "" : "@") + id.toLowerCase(Locale.ENGLISH);
}
/**
* Check if the issuer has the given permission.
* @param issuer The issuer
* @param permission The permission to check
* @return True if the issuer has the permission
*/
boolean hasPermission(CommandIssuer issuer, String permission) {
return Option.of(permissionsCheckMap.get(permission))
.map(checker -> checker.test(issuer))
.getOrElse(() -> PermissionUtils.hasPermission(issuer.getIssuer(), permission));
}
}
@@ -1,22 +0,0 @@
package org.mvplugins.multiverse.core.command;
import co.aikar.commands.BaseCommand;
import co.aikar.commands.CommandIssuer;
import co.aikar.commands.ExceptionHandler;
import co.aikar.commands.RegisteredCommand;
import com.dumptruckman.minecraft.util.Logging;
import java.util.List;
/**
* Default handler that will print the stack trace when the command throws an exception.
*/
public class MVDefaultExceptionHandler implements ExceptionHandler {
@Override
public boolean execute(BaseCommand command, RegisteredCommand registeredCommand, CommandIssuer sender, List<String> args, Throwable t) {
Logging.severe("Failed to execute command /%s %s %s",
registeredCommand.getCommand(), registeredCommand.getPrefSubCommand(), String.join(" ", args));
t.printStackTrace();
return false;
}
}
@@ -1,30 +0,0 @@
package org.mvplugins.multiverse.core.command;
import co.aikar.commands.BaseCommand;
import co.aikar.commands.BukkitCommandManager;
import co.aikar.commands.BukkitRootCommand;
import co.aikar.commands.CommandIssuer;
import org.mvplugins.multiverse.core.utils.StringFormatter;
import java.util.List;
public class MVRootCommand extends BukkitRootCommand {
protected MVRootCommand(BukkitCommandManager manager, String name) {
super(manager, name);
}
@Override
public BaseCommand execute(CommandIssuer sender, String commandLabel, String[] args) {
String[] quoteFormatedArgs = StringFormatter.parseQuotesInArgs(args).toArray(String[]::new);
return super.execute(sender, commandLabel, quoteFormatedArgs);
}
@Override
public List<String> getTabCompletions(CommandIssuer sender, String alias, String[] args, boolean commandsOnly, boolean isAsync) {
String[] quoteFormatedArgs = StringFormatter.parseQuotesInArgs(args).toArray(String[]::new);
return super.getTabCompletions(sender, alias, quoteFormatedArgs, commandsOnly, isAsync)
.stream()
.map(StringFormatter::quoteMultiWordString)
.toList();
}
}
@@ -1,19 +0,0 @@
package org.mvplugins.multiverse.core.command;
import co.aikar.commands.BaseCommand;
import com.dumptruckman.minecraft.util.Logging;
import jakarta.annotation.PostConstruct;
import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Contract;
import org.mvplugins.multiverse.core.command.flag.CommandFlag;
import org.mvplugins.multiverse.core.command.flag.CommandFlagGroup;
import org.mvplugins.multiverse.core.command.flag.CommandFlagsManager;
import org.mvplugins.multiverse.core.command.flag.ParsedCommandFlags;
/**
* Base class for all Multiverse commands.
*/
@Contract
public abstract class MultiverseCommand extends BaseCommand {
}
@@ -1,7 +0,0 @@
package org.mvplugins.multiverse.core.command.context;
/**
* Simple wrapper for game rule value, as they may be different types.
*/
public record GameRuleValue(Object value) {
}
@@ -1,16 +0,0 @@
package org.mvplugins.multiverse.core.command.context;
import org.bukkit.Location;
import org.jetbrains.annotations.ApiStatus;
/**
* Wrapper for context that get location from player if executed by player,
* else requires user input of location coordinates.
*
* @param value The location
*
* @since 5.1
*/
@ApiStatus.AvailableSince("5.1")
public record PlayerLocation(Location value) {
}
@@ -1,203 +0,0 @@
package org.mvplugins.multiverse.core.command.context.issueraware;
import co.aikar.commands.BukkitCommandExecutionContext;
import co.aikar.commands.BukkitCommandIssuer;
import co.aikar.commands.InvalidCommandArgument;
import co.aikar.commands.contexts.IssuerAwareContextResolver;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.ApiStatus;
import org.mvplugins.multiverse.core.locale.message.Message;
import java.util.Objects;
import java.util.function.BiFunction;
import java.util.function.Function;
/**
* Reusable logic of issuer only and issuer aware context resolvers
*
* @since 5.1
*/
@ApiStatus.AvailableSince("5.1")
public final class IssuerAwareContextBuilder<T> {
private BiFunction<BukkitCommandExecutionContext, Player, T> fromPlayer;
private BiFunction<BukkitCommandExecutionContext, String, T> fromInput;
private Function<BukkitCommandExecutionContext, Message> issuerOnlyFailMessage = context -> Message.of("This command can only be used by a player.");
private BiFunction<BukkitCommandExecutionContext, Player, Message> issuerAwarePlayerFailMessage = (context, player) -> Message.of("Unable to resolve context for player '" + player.getName() + "'.");
private BiFunction<BukkitCommandExecutionContext, String, Message> issuerAwareInputFailMessage = (context, input) -> Message.of("Unable to resolve context for input '" + input + "'.");
private BiFunction<BukkitCommandExecutionContext, String, Message> inputOnlyFailMessage = (context, input) -> Message.of("Unable to resolve context for input '" + input + "'.");
public IssuerAwareContextBuilder() {
}
/**
* Parse value from player itself.
*
* @param fromInput the parsing function
* @return The same builder for chaining
*
* @since 5.1
*/
@ApiStatus.AvailableSince("5.1")
public IssuerAwareContextBuilder<T> fromPlayer(BiFunction<BukkitCommandExecutionContext, Player, T> fromInput) {
this.fromPlayer = fromInput;
return this;
}
/**
* Parse value from command input string.
*
* @param fromInput the parsing function
* @return The same builder for chaining
*
* @since 5.1
*/
@ApiStatus.AvailableSince("5.1")
public IssuerAwareContextBuilder<T> fromInput(BiFunction<BukkitCommandExecutionContext, String, T> fromInput) {
this.fromInput = fromInput;
return this;
}
/**
* When getting value fails as issuer is not a player.
*
* @param issuerOnlyFailMessage The message
* @return The same builder for chaining
*
* @since 5.1
*/
@ApiStatus.AvailableSince("5.1")
public IssuerAwareContextBuilder<T> issuerOnlyFailMessage(Function<BukkitCommandExecutionContext, Message> issuerOnlyFailMessage) {
this.issuerOnlyFailMessage = issuerOnlyFailMessage;
return this;
}
/**
* When getting value fails as player cannot parse the value.
*
* @param issuerAwarePlayerFailMessage The message
* @return The same builder for chaining
*
* @since 5.1
*/
@ApiStatus.AvailableSince("5.1")
public IssuerAwareContextBuilder<T> issuerAwarePlayerFailMessage(BiFunction<BukkitCommandExecutionContext, Player, Message> issuerAwarePlayerFailMessage) {
this.issuerAwarePlayerFailMessage = issuerAwarePlayerFailMessage;
return this;
}
/**
* When getting value fails as input cannot parse the value.
*
* @param issuerAwareInputFailMessage The message
* @return The same builder for chaining
*
* @since 5.1
*/
@ApiStatus.AvailableSince("5.1")
public IssuerAwareContextBuilder<T> issuerAwareInputFailMessage(BiFunction<BukkitCommandExecutionContext, String, Message> issuerAwareInputFailMessage) {
this.issuerAwareInputFailMessage = issuerAwareInputFailMessage;
return this;
}
/**
* When getting value fails as input cannot parse the value.
*
* @param inputOnlyFailMessage The message
* @return The same builder for chaining
*
* @since 5.1
*/
@ApiStatus.AvailableSince("5.1")
public IssuerAwareContextBuilder<T> inputOnlyFailMessage(BiFunction<BukkitCommandExecutionContext, String, Message> inputOnlyFailMessage) {
this.inputOnlyFailMessage = inputOnlyFailMessage;
return this;
}
/**
* Creates the issuer aware context resolver logic.
*
* @return The generated context resolver
*
* @since 5.1
*/
@ApiStatus.AvailableSince("5.1")
public IssuerAwareContextResolver<T, BukkitCommandExecutionContext> generateContext() {
validateRequiredVariables();
return context -> resolveValue(context, GenericIssuerAwareValue::new).value;
}
/**
* Creates the issuer aware context resolver logic, with marking of whether the value was resolved from player or input.
*
* @param createValue The function to create the {@link IssuerAwareValue} value
* @param <I> The type of the value
* @return The generated context resolver
*
* @since 5.1
*/
@ApiStatus.AvailableSince("5.1")
public <I extends IssuerAwareValue> IssuerAwareContextResolver<I, BukkitCommandExecutionContext> generateContext(BiFunction<Boolean, T, I> createValue) {
validateRequiredVariables();
return context -> resolveValue(context, createValue);
}
private void validateRequiredVariables() {
Objects.requireNonNull(fromPlayer);
Objects.requireNonNull(fromInput);
Objects.requireNonNull(issuerOnlyFailMessage);
Objects.requireNonNull(issuerAwarePlayerFailMessage);
Objects.requireNonNull(issuerAwareInputFailMessage);
Objects.requireNonNull(inputOnlyFailMessage);
}
private <I extends IssuerAwareValue> I resolveValue(BukkitCommandExecutionContext context, BiFunction<Boolean, T, I> createValue) {
BukkitCommandIssuer issuer = context.getIssuer();
String resolve = context.getFlagValue("resolve", "");
if (resolve.equals("issuerOnly")) {
if (issuer.isPlayer()) {
T result = fromPlayer.apply(context, issuer.getPlayer());
if (result != null) {
return createValue.apply(true, result);
}
}
throw new InvalidCommandArgument(issuerOnlyFailMessage.apply(context).formatted(issuer));
}
String input = context.getFirstArg();
T result = fromInput.apply(context, input);
if (result != null) {
context.popFirstArg();
return createValue.apply(false, result);
}
int maxArgForAware = context.getFlagValue("maxArgForAware", Integer.MAX_VALUE);
long argLengthWithoutFlags = context.getArgs().stream()
.takeWhile(value -> !value.startsWith("-") && !value.isEmpty()) // ignore the flags
.count();
if (resolve.equals("issuerAware") && argLengthWithoutFlags <= maxArgForAware) {
if (issuer.isPlayer()) {
Player player = issuer.getPlayer();
result = fromPlayer.apply(context, player);
if (result != null) {
return createValue.apply(true, result);
}
throw new InvalidCommandArgument(issuerAwarePlayerFailMessage.apply(context, player).formatted(issuer));
}
throw new InvalidCommandArgument(issuerAwareInputFailMessage.apply(context, input).formatted(issuer));
}
throw new InvalidCommandArgument(inputOnlyFailMessage.apply(context, input).formatted(issuer));
}
private static class GenericIssuerAwareValue<T> extends IssuerAwareValue {
private final T value;
public GenericIssuerAwareValue(boolean byIssuer, T value) {
super(byIssuer);
this.value = value;
}
}
}
@@ -1,35 +0,0 @@
package org.mvplugins.multiverse.core.command.context.issueraware;
import org.jetbrains.annotations.ApiStatus;
/**
* Base class for issuer aware values
*
* @since 5.1
*/
@ApiStatus.AvailableSince("5.1")
public abstract class IssuerAwareValue {
protected final boolean byIssuer;
/**
* Constructor to create an issuer aware value
*
* @since 5.1
*/
@ApiStatus.AvailableSince("5.1")
protected IssuerAwareValue(boolean byIssuer) {
this.byIssuer = byIssuer;
}
/**
* Gets whether the value is by issuer or input
*
* @return true if by issuer, false if by input
*
* @since 5.1
*/
@ApiStatus.AvailableSince("5.1")
public boolean isByIssuer() {
return byIssuer;
}
}
@@ -1,38 +0,0 @@
package org.mvplugins.multiverse.core.command.context.issueraware;
import org.jetbrains.annotations.ApiStatus;
import org.mvplugins.multiverse.core.world.MultiverseWorld;
/**
* Issuer aware value wrapper for {@link MultiverseWorld}
*
* @since 5.1
*/
@ApiStatus.AvailableSince("5.1")
public final class MultiverseWorldValue extends IssuerAwareValue {
private final MultiverseWorld world;
/**
* Constructor for issuer aware value
*
* @since 5.1
*/
@ApiStatus.AvailableSince("5.1")
public MultiverseWorldValue(boolean byIssuer, MultiverseWorld world) {
super(byIssuer);
this.world = world;
}
/**
* The containing world wrapped
*
* @return wrapped world
*
* @since 5.1
*/
@ApiStatus.AvailableSince("5.1")
public MultiverseWorld value() {
return world;
}
}
@@ -1,36 +0,0 @@
package org.mvplugins.multiverse.core.command.context.issueraware;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.ApiStatus;
/**
* Issuer aware value wrapper for {@link Player} array
*
* @since 5.1
*/
@ApiStatus.AvailableSince("5.1")
public final class PlayerArrayValue extends IssuerAwareValue {
private final Player[] players;
/**
* @since 5.1
*/
@ApiStatus.AvailableSince("5.1")
public PlayerArrayValue(boolean byIssuer, Player[] players) {
super(byIssuer);
this.players = players;
}
/**
* The containing player array wrapped
*
* @return wrapped player array
*
* @since 5.1
*/
@ApiStatus.AvailableSince("5.1")
public Player[] value() {
return players;
}
}
@@ -1 +0,0 @@
package org.mvplugins.multiverse.core.command.context;
@@ -1,61 +0,0 @@
package org.mvplugins.multiverse.core.command.flag;
import com.dumptruckman.minecraft.util.Logging;
import jakarta.annotation.PostConstruct;
import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Contract;
import java.util.ArrayList;
import java.util.List;
/**
* Builds and registers a flag group on initialization. Flags should be final fields that calls {@link #flag(CommandFlag)}.
*/
@Contract
public abstract class FlagBuilder {
private final String name;
private final CommandFlagsManager flagsManager;
private final List<CommandFlag> flags;
protected FlagBuilder(@NotNull String name, @NotNull CommandFlagsManager flagsManager) {
this.name = name;
this.flagsManager = flagsManager;
this.flags = new ArrayList<>();
}
@PostConstruct
private void postConstruct() {
CommandFlagGroup.Builder flagGroupBuilder = CommandFlagGroup.builder(name);
flags.forEach(flagGroupBuilder::add);
registerFlagGroup(flagGroupBuilder.build());
}
private void registerFlagGroup(@NotNull CommandFlagGroup flagGroup) {
flagsManager.registerFlagGroup(flagGroup);
Logging.finer("Registered flag group: " + flagGroup.getName());
}
/**
* Add a new flag to the flag builder.
*
* @param flag The flag to add.
* @param <T> The type of the flag.
* @return The flag.
*/
protected <T extends CommandFlag> T flag(T flag) {
flags.add(flag);
Logging.finer("Added flag: " + flag);
return flag;
}
/**
* Parses flags.
*
* @param flags The raw string array to parse into flags.
* @return The parsed flags.
*/
public @NotNull ParsedCommandFlags parse(@NotNull String[] flags) {
return flagsManager.parse(name, flags);
}
}
@@ -1,4 +0,0 @@
/**
* This package contains classes related to parsing command flags.
*/
package org.mvplugins.multiverse.core.command.flag;
@@ -1,41 +0,0 @@
package org.mvplugins.multiverse.core.command.flags;
import java.util.List;
import java.util.function.Function;
import co.aikar.commands.InvalidCommandArgument;
import org.jetbrains.annotations.NotNull;
import org.mvplugins.multiverse.core.command.flag.CommandValueFlag;
import org.mvplugins.multiverse.core.display.filters.ContentFilter;
import org.mvplugins.multiverse.core.display.filters.RegexContentFilter;
/**
* A command flag for regex filtering.
* <br/>
* Parses the value of the --filter (or -f) flag as a {@link ContentFilter}.
*/
public final class FilterCommandFlag extends CommandValueFlag<ContentFilter> {
private static final Function<String, ContentFilter> VALUE_PARSER = value -> {
try {
return RegexContentFilter.fromString(value);
} catch (IllegalArgumentException e) {
throw new InvalidCommandArgument("Invalid filter: " + value);
}
};
/**
* Creates a new instance of this flag.
*
* @return The new instance.
*/
@NotNull
public static FilterCommandFlag create() {
return new FilterCommandFlag();
}
private FilterCommandFlag() {
super("--filter", List.of("-f"), ContentFilter.class, false, null, VALUE_PARSER, null);
}
}
@@ -1,38 +0,0 @@
package org.mvplugins.multiverse.core.command.flags;
import java.util.List;
import java.util.function.Function;
import org.jetbrains.annotations.NotNull;
import org.mvplugins.multiverse.core.command.flag.CommandValueFlag;
/**
* A command flag for page number.
* <br/>
* Parses the value of the --page (or -p) flag as an integer.
*/
public final class PageCommandFlag extends CommandValueFlag<Integer> {
private static final Function<String, Integer> VALUE_PARSER = value -> {
try {
return Integer.parseInt(value);
} catch (NumberFormatException e) {
throw new IllegalArgumentException("Invalid page number: " + value);
}
};
/**
* Creates a new instance of this flag.
*
* @return The new instance.
*/
@NotNull
public static PageCommandFlag create() {
return new PageCommandFlag();
}
private PageCommandFlag() {
super("--page", List.of("-p"), Integer.class, false, null, VALUE_PARSER, null);
}
}
@@ -1,28 +0,0 @@
package org.mvplugins.multiverse.core.command.flags;
import jakarta.inject.Inject;
import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Service;
import org.mvplugins.multiverse.core.command.flag.CommandFlagsManager;
import org.mvplugins.multiverse.core.command.flag.CommandValueFlag;
import org.mvplugins.multiverse.core.command.flag.FlagBuilder;
import org.mvplugins.multiverse.core.display.filters.ContentFilter;
@Service
public class PageFilterFlags extends FlagBuilder {
public static final String NAME = "pagefilter";
protected PageFilterFlags(@NotNull String name, @NotNull CommandFlagsManager flagsManager) {
super(name, flagsManager);
}
@Inject
protected PageFilterFlags(@NotNull CommandFlagsManager flagsManager) {
super(NAME, flagsManager);
}
public final CommandValueFlag<Integer> page = flag(PageCommandFlag.create());
public final CommandValueFlag<ContentFilter> filter = flag(FilterCommandFlag.create());
}
@@ -1,27 +0,0 @@
package org.mvplugins.multiverse.core.command.flags;
import jakarta.inject.Inject;
import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Service;
import org.mvplugins.multiverse.core.command.flag.CommandFlag;
import org.mvplugins.multiverse.core.command.flag.CommandFlagsManager;
import org.mvplugins.multiverse.core.command.flag.FlagBuilder;
@Service
public class RemovePlayerFlags extends FlagBuilder {
public static final String NAME = "removeplayer";
protected RemovePlayerFlags(@NotNull String name, @NotNull CommandFlagsManager flagsManager) {
super(name, flagsManager);
}
@Inject
protected RemovePlayerFlags(@NotNull CommandFlagsManager flagsManager) {
super(NAME, flagsManager);
}
public final CommandFlag removePlayers = flag(CommandFlag.builder("--remove-players")
.addAlias("-r")
.build());
}
@@ -1,27 +0,0 @@
package org.mvplugins.multiverse.core.command.flags;
import jakarta.inject.Inject;
import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Service;
import org.mvplugins.multiverse.core.command.flag.CommandFlag;
import org.mvplugins.multiverse.core.command.flag.CommandFlagsManager;
import org.mvplugins.multiverse.core.command.flag.FlagBuilder;
@Service
public class UnsafeFlags extends FlagBuilder {
public static final String NAME = "unsafe";
protected UnsafeFlags(@NotNull String name, @NotNull CommandFlagsManager flagsManager) {
super(name, flagsManager);
}
@Inject
protected UnsafeFlags(@NotNull CommandFlagsManager flagsManager) {
super(NAME, flagsManager);
}
public final CommandFlag unsafe = flag(CommandFlag.builder("--unsafe")
.addAlias("-u")
.build());
}
@@ -1,4 +0,0 @@
/**
* This package contains predefined flags for commands.
*/
package org.mvplugins.multiverse.core.command.flags;
@@ -1,192 +0,0 @@
/******************************************************************************
* Multiverse 2 Copyright (c) the Multiverse Team 2020. *
* Multiverse 2 is licensed under the BSD License. *
* For more information please check the README.md file included *
* with this project. *
******************************************************************************/
package org.mvplugins.multiverse.core.command.queue;
import java.util.Map;
import java.util.Objects;
import java.util.WeakHashMap;
import com.dumptruckman.minecraft.util.Logging;
import io.vavr.control.Option;
import jakarta.inject.Inject;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.block.data.type.CommandBlock;
import org.bukkit.command.BlockCommandSender;
import org.bukkit.command.CommandSender;
import org.bukkit.command.ConsoleCommandSender;
import org.bukkit.plugin.Plugin;
import org.bukkit.scheduler.BukkitTask;
import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Service;
import org.mvplugins.multiverse.core.MultiverseCore;
import org.mvplugins.multiverse.core.command.MVCommandIssuer;
import org.mvplugins.multiverse.core.config.CoreConfig;
import org.mvplugins.multiverse.core.utils.result.Attempt;
import static org.mvplugins.multiverse.core.locale.message.MessageReplacement.*;
/**
* <p>Manages the queuing of dangerous commands that require {@code /mv confirm} before executing.</p>
*
* <p>Each sender can only have one command in queue at any given time. When a queued command is added
* for a sender that already has a command in queue, it will replace the old queued command.</p>
*/
@Service
public class CommandQueueManager {
private static final String CONSOLE_NAME = "@console";
private static final String COMMAND_BLOCK_NAME = "@commandblock";
private static final long TICKS_PER_SECOND = 20;
private final Plugin plugin;
private final CoreConfig config;
private final Map<String, CommandQueuePayload> queuedCommandMap;
@Inject
CommandQueueManager(@NotNull MultiverseCore plugin, @NotNull CoreConfig config) {
this.plugin = plugin;
this.config = config;
this.queuedCommandMap = new WeakHashMap<>();
}
/**
* Adds a {@link CommandQueuePayload} into queue.
*
* @param payload The command queue payload to add.
*/
public void addToQueue(CommandQueuePayload payload) {
String senderName = parseSenderName(payload.issuer());
if (canRunImmediately(senderName)) {
payload.action().run();
return;
}
this.removeFromQueue(senderName);
Logging.finer("Add new command to queue for sender %s.", senderName);
this.queuedCommandMap.put(senderName, payload);
payload.expireTask(runExpireLater(senderName, config.getConfirmTimeout()));
payload.issuer().sendInfo(payload.prompt());
var confirmCommand = "/mv confirm";
if (config.getUseConfirmOtp()) {
confirmCommand += " " + payload.otp();
}
payload.issuer().sendMessage(String.format("Run %s%s %sto continue. This will expire in %s seconds.",
ChatColor.GREEN, confirmCommand, ChatColor.WHITE, config.getConfirmTimeout()));
}
/**
* Check if sender does not need to queue before running.
*
* @param senderName The name of sender.
* @return True if sender does not need to queue before running.
*/
private boolean canRunImmediately(@NotNull String senderName) {
return switch (config.getConfirmMode()) {
case ENABLE -> false;
case PLAYER_ONLY -> senderName.equals(CONSOLE_NAME) || senderName.equals(COMMAND_BLOCK_NAME);
case DISABLE_COMMAND_BLOCKS -> senderName.equals(COMMAND_BLOCK_NAME);
case DISABLE_CONSOLE -> senderName.equals(CONSOLE_NAME);
case DISABLE -> true;
};
}
/**
* Expire task that removes a {@link CommandQueuePayload} from queue after valid duration defined.
*
* @param senderName The name of the sender.
* @return The expire {@link BukkitTask}.
*/
@NotNull
private BukkitTask runExpireLater(@NotNull String senderName, int validDuration) {
return Bukkit.getScheduler().runTaskLater(
this.plugin,
expireRunnable(senderName),
validDuration * TICKS_PER_SECOND);
}
/**
* Runnable responsible for expiring the queued command.
*
* @param senderName The name of the sender.
* @return The expire runnable.
*/
@NotNull
private Runnable expireRunnable(@NotNull String senderName) {
return () -> {
CommandQueuePayload payload = this.queuedCommandMap.remove(senderName);
if (payload == null) {
// Payload already removed
return;
}
payload.issuer().sendMessage("Your queued command has expired.");
};
}
/**
* Runs the command in queue for the given sender, if any.
*
* @param issuer Sender that confirmed the command.
* @return True if queued command ran successfully, else false.
*/
public Attempt<Void, RunQueuedFailedReason> runQueuedCommand(@NotNull MVCommandIssuer issuer, String otpInput) {
String senderName = parseSenderName(issuer);
return Option.of(this.queuedCommandMap.get(senderName)).fold(
() -> Attempt.failure(RunQueuedFailedReason.NO_COMMAND_IN_QUEUE),
payload -> runPayload(senderName, otpInput, payload));
}
private Attempt<Void, RunQueuedFailedReason> runPayload(String senderName, String otpInput, CommandQueuePayload payload) {
if (config.getUseConfirmOtp() && !Objects.equals(payload.otp(), otpInput)) {
return Attempt.failure(RunQueuedFailedReason.INVALID_OTP, replace("{otp}").with(otpInput));
}
this.removeFromQueue(senderName);
Logging.finer("Running queued command...");
payload.action().run();
return Attempt.success(null);
}
/**
* Since only one command is stored in queue per sender, we remove the old one.
*
* @param senderName The sender that executed the command.
*/
public void removeFromQueue(@NotNull String senderName) {
CommandQueuePayload payload = this.queuedCommandMap.remove(senderName);
if (payload == null) {
Logging.finer("No queue command to remove for sender %s.", senderName);
return;
}
Option.of(payload.expireTask()).peek(BukkitTask::cancel);
Logging.finer("Removed queue command for sender %s.", senderName);
}
private String parseSenderName(MVCommandIssuer issuer) {
CommandSender sender = issuer.getIssuer();
if (isCommandBlock(sender)) {
return COMMAND_BLOCK_NAME;
} else if (sender instanceof ConsoleCommandSender) {
return CONSOLE_NAME;
}
return sender.getName();
}
/**
* Checks if the sender is a command block.
*
* @param sender The sender to check.
* @return True if sender is a command block, else false.
*/
private boolean isCommandBlock(@NotNull CommandSender sender) {
return sender instanceof BlockCommandSender
&& ((BlockCommandSender) sender).getBlock().getBlockData() instanceof CommandBlock;
}
}
@@ -1,116 +0,0 @@
package org.mvplugins.multiverse.core.command.queue;
import co.aikar.commands.ACFUtil;
import org.bukkit.scheduler.BukkitTask;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.mvplugins.multiverse.core.command.MVCommandIssuer;
import org.mvplugins.multiverse.core.locale.message.Message;
/**
* Represents a single command used in {@link CommandQueueManager} for confirming before running potentially
* dangerous action.
*/
public class CommandQueuePayload {
/**
* Creates a new {@link CommandQueuePayload}
*
* @param issuer The issuer of the command
* @return The new {@link CommandQueuePayload}
*/
public static CommandQueuePayload issuer(@NotNull MVCommandIssuer issuer) {
return new CommandQueuePayload(issuer);
}
private static final String DEFAULT_PROMPT_MESSAGE = "The command you are trying to run is deemed dangerous."; // todo: localize
private final MVCommandIssuer issuer;
private String otp;
private Runnable action = () -> {};
private Message prompt = Message.of(DEFAULT_PROMPT_MESSAGE);
private BukkitTask expireTask;
protected CommandQueuePayload(@NotNull MVCommandIssuer issuer) {
this.otp = String.valueOf(ACFUtil.rand(100, 999));
this.issuer = issuer;
}
/**
* Gets the issuer of the command.
* @return The issuer
*/
@NotNull
public MVCommandIssuer issuer() {
return issuer;
}
/**
* Sets the logic to execute upon confirming.
* @param action The logic to execute
* @return The same {@link CommandQueuePayload} for method chaining
*/
public CommandQueuePayload action(@NotNull Runnable action) {
this.action = action;
return this;
}
/**
* Gets the logic to execute upon confirming.
*
* @return The logic to execute
*/
@NotNull
public Runnable action() {
return action;
}
/**
* Sets the OTP number for validation.
*
* @param otp The OTP number
* @return The same {@link CommandQueuePayload} for method chaining
*/
public CommandQueuePayload otp(String otp) {
this.otp = otp;
return this;
}
/**
* Gets the OTP number for validation.
*
* @return The OTP number
*/
public String otp() {
return otp;
}
/**
* Sets the question to ask sender to confirm.
*
* @param prompt The prompt message.
* @return The same {@link CommandQueuePayload} for method chaining.
*/
public CommandQueuePayload prompt(Message prompt) {
this.prompt = prompt;
return this;
}
/**
* Gets the question to ask sender to confirm.
*
* @return The prompt message.
*/
public Message prompt() {
return prompt;
}
void expireTask(BukkitTask expireTask) {
this.expireTask = expireTask;
}
@Nullable
BukkitTask expireTask() {
return expireTask;
}
}
@@ -1,31 +0,0 @@
package org.mvplugins.multiverse.core.command.queue;
/**
* Sets whether the command executor needs to confirm before executing a command.
*/
public enum ConfirmMode {
/**
* Every command executor needs to confirm before executing a command.
*/
ENABLE,
/**
* Only players need to confirm before executing a command. Command blocks and console will not need to confirm.
*/
PLAYER_ONLY,
/**
* Everyone except command blocks will not need to confirm before executing a command.
*/
DISABLE_COMMAND_BLOCKS,
/**
* Everyone except console will not need to confirm before executing a command.
*/
DISABLE_CONSOLE,
/**
* Everyone will not need to confirm before executing a command.
*/
DISABLE,
}
@@ -1,27 +0,0 @@
package org.mvplugins.multiverse.core.command.queue;
import co.aikar.locales.MessageKey;
import co.aikar.locales.MessageKeyProvider;
import org.mvplugins.multiverse.core.locale.MVCorei18n;
import org.mvplugins.multiverse.core.utils.result.FailureReason;
public enum RunQueuedFailedReason implements FailureReason {
NO_COMMAND_IN_QUEUE(MVCorei18n.QUEUECOMMAND_NOCOMMANDINQUEUE),
INVALID_OTP(MVCorei18n.QUEUECOMMAND_INVALIDOTP),
COMMAND_EXECUTION_ERROR(MVCorei18n.QUEUECOMMAND_COMMANDEXECUTIONERROR),
;
private final MessageKeyProvider message;
RunQueuedFailedReason(MessageKeyProvider message) {
this.message = message;
}
/**
* {@inheritDoc}
*/
@Override
public MessageKey getMessageKey() {
return message.getMessageKey();
}
}
@@ -1,43 +0,0 @@
package org.mvplugins.multiverse.core.commands;
import co.aikar.commands.annotation.CommandCompletion;
import co.aikar.commands.annotation.CommandPermission;
import co.aikar.commands.annotation.Description;
import co.aikar.commands.annotation.Subcommand;
import co.aikar.commands.annotation.Syntax;
import jakarta.inject.Inject;
import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Service;
import org.mvplugins.multiverse.core.anchor.AnchorManager;
import org.mvplugins.multiverse.core.anchor.MultiverseAnchor;
import org.mvplugins.multiverse.core.command.MVCommandIssuer;
@Service
final class AnchorDeleteCommand extends CoreCommand {
private final AnchorManager anchorManager;
@Inject
AnchorDeleteCommand(@NotNull AnchorManager anchorManager) {
this.anchorManager = anchorManager;
}
@Subcommand("anchor delete")
@CommandPermission("multiverse.core.anchor.delete")
@CommandCompletion("@anchornames")
@Syntax("<name>")
@Description("")
void onAnchorDeleteCommand(
MVCommandIssuer issuer,
@Syntax("<name>")
@Description("")
MultiverseAnchor anchor) {
if (anchorManager.deleteAnchor(anchor).isSuccess()) {
issuer.sendMessage("&aAnchor &f" + anchor.getName() + "&a deleted.");
} else {
issuer.sendMessage("&cFailed to delete anchor.");
}
}
}
@@ -1,75 +0,0 @@
package org.mvplugins.multiverse.core.commands;
import java.util.List;
import co.aikar.commands.annotation.CommandCompletion;
import co.aikar.commands.annotation.CommandPermission;
import co.aikar.commands.annotation.Description;
import co.aikar.commands.annotation.Optional;
import co.aikar.commands.annotation.Subcommand;
import co.aikar.commands.annotation.Syntax;
import jakarta.inject.Inject;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Service;
import org.mvplugins.multiverse.core.anchor.AnchorManager;
import org.mvplugins.multiverse.core.command.MVCommandIssuer;
import org.mvplugins.multiverse.core.command.MVCommandManager;
import org.mvplugins.multiverse.core.command.flag.ParsedCommandFlags;
import org.mvplugins.multiverse.core.command.flags.PageFilterFlags;
import org.mvplugins.multiverse.core.display.ContentDisplay;
import org.mvplugins.multiverse.core.display.filters.DefaultContentFilter;
import org.mvplugins.multiverse.core.display.handlers.PagedSendHandler;
import org.mvplugins.multiverse.core.display.parsers.ListContentProvider;
import org.mvplugins.multiverse.core.teleportation.LocationManipulation;
@Service
final class AnchorListCommand extends CoreCommand {
private final AnchorManager anchorManager;
private final LocationManipulation locationManipulation;
private final PageFilterFlags flags;
@Inject
AnchorListCommand(
@NotNull AnchorManager anchorManager,
@NotNull LocationManipulation locationManipulation,
@NotNull PageFilterFlags flags
) {
this.anchorManager = anchorManager;
this.locationManipulation = locationManipulation;
this.flags = flags;
}
@Subcommand("anchor list")
@CommandPermission("multiverse.core.anchor.list")
@CommandCompletion("@flags:groupName=" + PageFilterFlags.NAME)
@Syntax("[--page <page>] [--filter <filter>]")
@Description("")
void onAnchorListCommand(
MVCommandIssuer issuer,
@Optional
@Syntax("[--page <page>] [--filter <filter>]")
@Description("")
String[] flagArray) {
ParsedCommandFlags parsedFlags = flags.parse(flagArray);
ContentDisplay.create()
.addContent(ListContentProvider.forContent(getAnchors(issuer.getPlayer())))
.withSendHandler(PagedSendHandler.create()
.withHeader("&3==== [ Multiverse Anchors ] ====")
.doPagination(true)
.withTargetPage(parsedFlags.flagValue(flags.page, 1))
.withFilter(parsedFlags.flagValue(flags.filter, DefaultContentFilter.get())))
.send(issuer);
}
private List<String> getAnchors(Player player) {
return anchorManager.getAnchors(player).stream()
.map(anchor ->
"&a%s&7 - &f%s".formatted(
anchor.getName(), locationManipulation.locationToString(anchor.getLocation())))
.toList();
}
}
@@ -1,89 +0,0 @@
package org.mvplugins.multiverse.core.commands;
import co.aikar.commands.annotation.CommandCompletion;
import co.aikar.commands.annotation.CommandPermission;
import co.aikar.commands.annotation.Description;
import co.aikar.commands.annotation.Flags;
import co.aikar.commands.annotation.Single;
import co.aikar.commands.annotation.Subcommand;
import co.aikar.commands.annotation.Syntax;
import jakarta.inject.Inject;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Service;
import org.mvplugins.multiverse.core.anchor.AnchorManager;
import org.mvplugins.multiverse.core.command.MVCommandIssuer;
import org.mvplugins.multiverse.core.teleportation.LocationManipulation;
@Service
final class AnchorSetCommand extends CoreCommand {
private final AnchorManager anchorManager;
private final LocationManipulation locationManipulation;
@Inject
AnchorSetCommand(
@NotNull AnchorManager anchorManager,
@NotNull LocationManipulation locationManipulation
) {
this.anchorManager = anchorManager;
this.locationManipulation = locationManipulation;
}
@Subcommand("anchor set")
@CommandPermission("multiverse.core.anchor.create")
@CommandCompletion("@empty @locations")
@Syntax("<name> [location]")
@Description("")
void onAnchorSetCommand(
MVCommandIssuer issuer,
@Flags("resolve=issuerOnly")
Player player,
@Single
@Syntax("<name>")
@Description("")
//TODO: Check if anchor name is valid or exists already.
String anchorName) {
Location anchorLocation = player.getLocation();
if (anchorManager.setAnchor(anchorName, anchorLocation).isSuccess()) {
sendAnchorSetSuccessMessage(issuer, anchorName, locationManipulation.locationToString(anchorLocation));
} else {
sendAnchorSetFailedMessage(issuer, anchorName);
}
}
@Subcommand("anchor set")
@CommandPermission("multiverse.core.anchor.create")
@CommandCompletion("@anchornames @locations")
@Syntax("<name> [location]")
@Description("")
void onAnchorSetCommand(
MVCommandIssuer issuer,
@Syntax("<name>")
@Description("")
String anchorName,
@Single
@Syntax("[location]")
@Description("")
String locationString) {
if (anchorManager.setAnchor(anchorName, locationString).isSuccess()) {
sendAnchorSetSuccessMessage(issuer, anchorName, locationString);
} else {
sendAnchorSetFailedMessage(issuer, anchorName);
}
}
private void sendAnchorSetSuccessMessage(MVCommandIssuer issuer, String anchorName, String locationString) {
issuer.sendMessage("&aAnchor &f" + anchorName + "&a set to &f" + locationString);
}
private void sendAnchorSetFailedMessage(MVCommandIssuer issuer, String anchorName) {
issuer.sendMessage("&cFailed to set anchor &f" + anchorName + ".");
}
}
@@ -11,35 +11,28 @@ import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Service;
import org.mvplugins.multiverse.core.command.LegacyAliasCommand;
import org.mvplugins.multiverse.core.command.MVCommandIssuer;
import org.mvplugins.multiverse.core.destination.DestinationInstance;
import org.mvplugins.multiverse.core.locale.MVCorei18n;
import org.mvplugins.multiverse.core.locale.message.Message;
import org.mvplugins.multiverse.core.locale.message.MessageReplacement.Replace;
import org.mvplugins.multiverse.core.permissions.CorePermissionsChecker;
import org.mvplugins.multiverse.core.teleportation.LocationManipulation;
import static org.mvplugins.multiverse.core.locale.message.MessageReplacement.replace;
import org.mvplugins.multiverse.core.commandtools.MVCommandIssuer;
import org.mvplugins.multiverse.core.commandtools.MVCommandManager;
import org.mvplugins.multiverse.core.commandtools.MultiverseCommand;
import org.mvplugins.multiverse.core.destination.DestinationsProvider;
import org.mvplugins.multiverse.core.destination.ParsedDestination;
import org.mvplugins.multiverse.core.utils.MVCorei18n;
@Service
class CheckCommand extends CoreCommand {
@CommandAlias("mv")
class CheckCommand extends MultiverseCommand {
private final CorePermissionsChecker corePermissionsChecker;
private final LocationManipulation locationManipulation;
private final DestinationsProvider destinationsProvider;
@Inject
CheckCommand(
@NotNull CorePermissionsChecker corePermissionsChecker,
@NotNull LocationManipulation locationManipulation
) {
this.corePermissionsChecker = corePermissionsChecker;
this.locationManipulation = locationManipulation;
CheckCommand(@NotNull MVCommandManager commandManager, @NotNull DestinationsProvider destinationsProvider) {
super(commandManager);
this.destinationsProvider = destinationsProvider;
}
@Subcommand("check")
@CommandPermission("multiverse.core.check")
@CommandCompletion("@players @destinations")
@CommandCompletion("@players @destinations|@mvworlds")
@Syntax("<player> <destination>")
@Description("{@@mv-core.check.description}")
void onCheckCommand(
@@ -51,32 +44,11 @@ class CheckCommand extends CoreCommand {
@Syntax("<destination>")
@Description("{@@mv-core.check.destination.description}")
DestinationInstance<?, ?> destination) {
issuer.sendInfo(this.corePermissionsChecker.checkTeleportPermission(player, player, destination)
? MVCorei18n.CHECK_HASPERMISSION
: MVCorei18n.CHECK_NOPERMISSION,
Replace.PLAYER.with(player.getName()),
Replace.DESTINATION.with(destination));
issuer.sendInfo(MVCorei18n.CHECK_LOCATION,
replace("{location}").with(destination.getLocation(player)
.map(locationManipulation::locationToString)
.map(Message::of)
.getOrElse(() -> Message.of(MVCorei18n.GENERIC_NULL, "Null!"))));
// TODO: Show permission required for this particular destination
}
@Service
private final static class LegacyAlias extends CheckCommand implements LegacyAliasCommand {
@Inject
LegacyAlias(@NotNull CorePermissionsChecker corePermissionsChecker, @NotNull LocationManipulation locationManipulation) {
super(corePermissionsChecker, locationManipulation);
}
@Override
@CommandAlias("mvcheck")
void onCheckCommand(MVCommandIssuer issuer, Player player, DestinationInstance<?, ?> destination) {
super.onCheckCommand(issuer, player, destination);
}
ParsedDestination<?> destination) {
issuer.sendInfo(MVCorei18n.CHECK_CHECKING,
"{player}", player.getName(),
"{destination}", destination.toString());
// TODO: More detailed output on permissions required.
this.destinationsProvider.checkTeleportPermissions(issuer, player, destination);
}
}
@@ -12,36 +12,44 @@ import jakarta.inject.Inject;
import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Service;
import org.mvplugins.multiverse.core.command.LegacyAliasCommand;
import org.mvplugins.multiverse.core.command.MVCommandIssuer;
import org.mvplugins.multiverse.core.command.flag.CommandFlag;
import org.mvplugins.multiverse.core.command.flag.CommandFlagsManager;
import org.mvplugins.multiverse.core.command.flag.FlagBuilder;
import org.mvplugins.multiverse.core.command.flag.ParsedCommandFlags;
import org.mvplugins.multiverse.core.locale.MVCorei18n;
import org.mvplugins.multiverse.core.locale.message.MessageReplacement.Replace;
import org.mvplugins.multiverse.core.commandtools.MVCommandIssuer;
import org.mvplugins.multiverse.core.commandtools.MVCommandManager;
import org.mvplugins.multiverse.core.commandtools.MultiverseCommand;
import org.mvplugins.multiverse.core.commandtools.flags.CommandFlag;
import org.mvplugins.multiverse.core.commandtools.flags.ParsedCommandFlags;
import org.mvplugins.multiverse.core.utils.MVCorei18n;
import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld;
import org.mvplugins.multiverse.core.world.WorldManager;
import org.mvplugins.multiverse.core.world.options.CloneWorldOptions;
import static org.mvplugins.multiverse.core.locale.message.MessageReplacement.replace;
@Service
class CloneCommand extends CoreCommand {
@CommandAlias("mv")
class CloneCommand extends MultiverseCommand {
private final WorldManager worldManager;
private final CloneCommand.Flags flags;
private final CommandFlag RESET_WORLD_CONFIG_FLAG = flag(CommandFlag.builder("--reset-world-config")
.addAlias("-wc")
.build());
private final CommandFlag RESET_GAMERULES_FLAG = flag(CommandFlag.builder("--reset-gamerules")
.addAlias("-gm")
.build());
private final CommandFlag RESET_WORLD_BORDER_FLAG = flag(CommandFlag.builder("--reset-world-border")
.addAlias("-wb")
.build());
@Inject
CloneCommand(@NotNull WorldManager worldManager, @NotNull Flags flags) {
CloneCommand(@NotNull MVCommandManager commandManager, @NotNull WorldManager worldManager) {
super(commandManager);
this.worldManager = worldManager;
this.flags = flags;
}
@Subcommand("clone")
@CommandPermission("multiverse.core.clone")
@CommandCompletion("@mvworlds:scope=loaded @empty @flags:groupName=" + Flags.NAME)
@Syntax("<world> <new-world-name> [--reset-world-config --reset-gamerules --reset-world-border]")
@CommandCompletion("@mvworlds:scope=loaded @empty @flags:groupName=mvclonecommand")
@Syntax("<world> <new world name>")
@Description("{@@mv-core.clone.description}")
void onCloneCommand(
MVCommandIssuer issuer,
@@ -50,72 +58,28 @@ class CloneCommand extends CoreCommand {
@Description("{@@mv-core.clone.world.description}")
LoadedMultiverseWorld world,
@Syntax("<new-world-name>")
@Syntax("<new world name>")
@Description("{@@mv-core.clone.newWorld.description}")
String newWorldName,
@Optional
@Syntax("[--reset-world-config --reset-gamerules --reset-world-border]")
@Syntax(/* TODO */ "")
@Description("{@@mv-core.regen.other.description}")
String[] flagArray) {
ParsedCommandFlags parsedFlags = flags.parse(flagArray);
String[] flags) {
ParsedCommandFlags parsedFlags = parseFlags(flags);
issuer.sendInfo(MVCorei18n.CLONE_CLONING,
Replace.WORLD.with(world.getName()),
replace("{newworld}").with(newWorldName));
issuer.sendInfo(MVCorei18n.CLONE_CLONING, "{world}", world.getName(), "{newworld}", newWorldName);
CloneWorldOptions cloneWorldOptions = CloneWorldOptions.fromTo(world, newWorldName)
.keepWorldConfig(!parsedFlags.hasFlag(flags.resetWorldConfig))
.keepGameRule(!parsedFlags.hasFlag(flags.resetGamerules))
.keepWorldBorder(!parsedFlags.hasFlag(flags.resetWorldBorder))
.saveBukkitWorld(!parsedFlags.hasFlag(flags.noSave));
.keepWorldConfig(!parsedFlags.hasFlag(RESET_WORLD_CONFIG_FLAG))
.keepGameRule(!parsedFlags.hasFlag(RESET_GAMERULES_FLAG))
.keepWorldBorder(!parsedFlags.hasFlag(RESET_WORLD_BORDER_FLAG));
worldManager.cloneWorld(cloneWorldOptions)
.onSuccess(newWorld -> {
Logging.fine("World clone success: " + newWorld);
issuer.sendInfo(MVCorei18n.CLONE_SUCCESS, Replace.WORLD.with(newWorld.getName()));
issuer.sendInfo(MVCorei18n.CLONE_SUCCESS, "{world}", newWorld.getName());
}).onFailure(failure -> {
Logging.fine("World clone failure: " + failure);
issuer.sendError(failure.getFailureMessage());
});
}
@Service
private static final class Flags extends FlagBuilder {
private static final String NAME = "mvclone";
@Inject
private Flags(@NotNull CommandFlagsManager flagsManager) {
super(NAME, flagsManager);
}
private final CommandFlag resetWorldConfig = flag(CommandFlag.builder("--reset-world-config")
.addAlias("-wc")
.build());
private final CommandFlag resetGamerules = flag(CommandFlag.builder("--reset-gamerules")
.addAlias("-gm")
.build());
private final CommandFlag resetWorldBorder = flag(CommandFlag.builder("--reset-world-border")
.addAlias("-wb")
.build());
private final CommandFlag noSave = flag(CommandFlag.builder("--no-save")
.addAlias("-s")
.build());
}
@Service
private final static class LegacyAlias extends CloneCommand implements LegacyAliasCommand {
@Inject
LegacyAlias(@NotNull WorldManager worldManager, @NotNull Flags flags) {
super(worldManager, flags);
}
@Override
@CommandAlias("mvcl|mvclone")
void onCloneCommand(MVCommandIssuer issuer, LoadedMultiverseWorld world, String newWorldName, String[] flags) {
super.onCloneCommand(issuer, world, newWorldName, flags);
}
}
}
@@ -12,82 +12,61 @@ import jakarta.inject.Inject;
import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Service;
import org.mvplugins.multiverse.core.command.LegacyAliasCommand;
import org.mvplugins.multiverse.core.command.MVCommandIssuer;
import org.mvplugins.multiverse.core.config.CoreConfig;
import org.mvplugins.multiverse.core.commandtools.MVCommandIssuer;
import org.mvplugins.multiverse.core.commandtools.MVCommandManager;
import org.mvplugins.multiverse.core.commandtools.MultiverseCommand;
import org.mvplugins.multiverse.core.commandtools.context.MVConfigValue;
import org.mvplugins.multiverse.core.config.MVCoreConfig;
import org.mvplugins.multiverse.core.exceptions.MultiverseException;
import org.mvplugins.multiverse.core.locale.MVCorei18n;
import org.mvplugins.multiverse.core.locale.message.Message;
import org.mvplugins.multiverse.core.locale.message.MessageReplacement;
import org.mvplugins.multiverse.core.locale.message.MessageReplacement.Replace;
@Service
class ConfigCommand extends CoreCommand {
@CommandAlias("mv")
class ConfigCommand extends MultiverseCommand {
private final CoreConfig config;
private final MVCoreConfig config;
@Inject
ConfigCommand(@NotNull CoreConfig config) {
ConfigCommand(@NotNull MVCommandManager commandManager, @NotNull MVCoreConfig config) {
super(commandManager);
this.config = config;
}
@Subcommand("config")
@CommandPermission("multiverse.core.config")
@CommandCompletion("@mvconfigs @mvconfigvalues")
@Syntax("<name> [value]")
@Description("{@@mv-core.config.description}")
@CommandCompletion("@mvconfigs")
@Syntax("<name> [new-value]")
@Description("") // TODO: Description
void onConfigCommand(
MVCommandIssuer issuer,
@Syntax("<name>")
@Description("{@@mv-core.config.name.description}")
@Description("") // TODO: Description
String name,
@Optional
@Syntax("[value]")
@Description("{@@mv-core.config.value.description}")
String value) {
@Syntax("[new-value]")
@Description("") // TODO: Description
MVConfigValue value) {
if (value == null) {
showConfigValue(issuer, name);
return;
}
updateConfigValue(issuer, name, value);
updateConfigValue(issuer, name, value.getValue());
}
private void showConfigValue(MVCommandIssuer issuer, String name) {
config.getStringPropertyHandle().getProperty(name)
.onSuccess(value -> issuer.sendMessage(MVCorei18n.CONFIG_SHOW_SUCCESS,
Replace.NAME.with(name),
Replace.VALUE.with(value)))
.onFailure(e -> issuer.sendMessage(MVCorei18n.CONFIG_SHOW_ERROR,
Replace.NAME.with(name),
Replace.ERROR.with(e)));
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, String value) {
var stringPropertyHandle = config.getStringPropertyHandle();
stringPropertyHandle.setPropertyString(issuer.getIssuer(), name, value)
.andThenTry(config::save)
.onSuccess(ignore -> issuer.sendMessage(MVCorei18n.CONFIG_SET_SUCCESS,
Replace.NAME.with(name),
Replace.VALUE.with(stringPropertyHandle.getProperty(name).getOrNull())))
.onFailure(e -> issuer.sendMessage(MVCorei18n.CONFIG_SET_ERROR,
Replace.NAME.with(name),
Replace.VALUE.with(value),
Replace.ERROR.with(e)));
}
@Service
private final static class LegacyAlias extends ConfigCommand implements LegacyAliasCommand {
@Inject
LegacyAlias(@NotNull CoreConfig config) {
super(config);
}
@Override
@CommandAlias("mvconfig|mvconf")
void onConfigCommand(MVCommandIssuer issuer, String name, String value) {
super.onConfigCommand(issuer, name, value);
}
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));
}
}
@@ -1,54 +1,30 @@
package org.mvplugins.multiverse.core.commands;
import co.aikar.commands.BukkitCommandIssuer;
import co.aikar.commands.annotation.CommandAlias;
import co.aikar.commands.annotation.CommandPermission;
import co.aikar.commands.annotation.Default;
import co.aikar.commands.annotation.Description;
import co.aikar.commands.annotation.Subcommand;
import co.aikar.commands.annotation.Syntax;
import jakarta.inject.Inject;
import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Service;
import org.mvplugins.multiverse.core.command.LegacyAliasCommand;
import org.mvplugins.multiverse.core.command.MVCommandIssuer;
import org.mvplugins.multiverse.core.command.queue.CommandQueueManager;
import org.mvplugins.multiverse.core.commandtools.MVCommandManager;
import org.mvplugins.multiverse.core.commandtools.MultiverseCommand;
@Service
class ConfirmCommand extends CoreCommand {
@NotNull
private final CommandQueueManager commandQueueManager;
@CommandAlias("mv")
class ConfirmCommand extends MultiverseCommand {
@Inject
ConfirmCommand(@NotNull CommandQueueManager commandQueueManager) {
this.commandQueueManager = commandQueueManager;
ConfirmCommand(@NotNull MVCommandManager commandManager) {
super(commandManager);
}
@Subcommand("confirm")
@CommandPermission("multiverse.core.confirm")
@Syntax("[otp]")
@Description("{@@mv-core.confirm.description}")
void onConfirmCommand(
MVCommandIssuer issuer,
@Default("0")
String otp) {
this.commandQueueManager.runQueuedCommand(issuer, otp)
.onFailure(failure -> issuer.sendError(failure.getFailureMessage()));
}
@Service
private final static class LegacyAlias extends ConfirmCommand implements LegacyAliasCommand {
@Inject
LegacyAlias(@NotNull CommandQueueManager commandQueueCommand) {
super(commandQueueCommand);
}
@Override
@CommandAlias("mvconfirm")
void onConfirmCommand(MVCommandIssuer issuer, String otp) {
super.onConfirmCommand(issuer, otp);
}
void onConfirmCommand(@NotNull BukkitCommandIssuer issuer) {
this.commandManager.getCommandQueueManager().runQueuedCommand(issuer.getIssuer());
}
}
@@ -1,5 +1,6 @@
package org.mvplugins.multiverse.core.commands;
import co.aikar.commands.BukkitCommandIssuer;
import co.aikar.commands.annotation.CommandAlias;
import co.aikar.commands.annotation.CommandPermission;
import co.aikar.commands.annotation.Description;
@@ -10,60 +11,42 @@ import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Service;
import org.mvplugins.multiverse.core.command.LegacyAliasCommand;
import org.mvplugins.multiverse.core.command.MVCommandIssuer;
import org.mvplugins.multiverse.core.command.MVCommandManager;
import org.mvplugins.multiverse.core.locale.MVCorei18n;
import org.mvplugins.multiverse.core.locale.message.MessageReplacement.Replace;
import org.mvplugins.multiverse.core.teleportation.LocationManipulation;
import org.mvplugins.multiverse.core.world.MultiverseWorld;
import static org.mvplugins.multiverse.core.locale.message.MessageReplacement.replace;
import org.mvplugins.multiverse.core.api.LocationManipulation;
import org.mvplugins.multiverse.core.commandtools.MVCommandManager;
import org.mvplugins.multiverse.core.commandtools.MultiverseCommand;
import org.mvplugins.multiverse.core.utils.MVCorei18n;
import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld;
@Service
class CoordinatesCommand extends CoreCommand {
@CommandAlias("mv")
class CoordinatesCommand extends MultiverseCommand {
private final LocationManipulation locationManipulation;
@Inject
CoordinatesCommand(@NotNull LocationManipulation locationManipulation) {
CoordinatesCommand(
@NotNull MVCommandManager commandManager,
@NotNull LocationManipulation locationManipulation) {
super(commandManager);
this.locationManipulation = locationManipulation;
}
@Subcommand("coordinates")
@Subcommand("coordinates|coords|coord|co")
@CommandPermission("multiverse.core.coord")
@Description("{@@mv-core.coordinates.description}")
void onCoordinatesCommand(
MVCommandIssuer issuer,
BukkitCommandIssuer issuer,
@Flags("resolve=issuerOnly")
Player player,
@Flags("resolve=issuerOnly")
MultiverseWorld world) {
LoadedMultiverseWorld world) {
issuer.sendInfo(MVCorei18n.COORDINATES_INFO_TITLE);
issuer.sendInfo(MVCorei18n.COORDINATES_INFO_WORLD, Replace.WORLD.with(world.getName()));
issuer.sendInfo(MVCorei18n.COORDINATES_INFO_ALIAS, replace("{alias}").with(world.getAliasOrName()));
issuer.sendInfo(MVCorei18n.COORDINATES_INFO_WORLDSCALE,
replace("{scale}").with(String.valueOf(world.getScale())));
issuer.sendInfo(MVCorei18n.COORDINATES_INFO_COORDINATES,
replace("{coordinates}").with(locationManipulation.strCoords(player.getLocation())));
issuer.sendInfo(MVCorei18n.COORDINATES_INFO_DIRECTION,
replace("{direction}").with(locationManipulation.getDirection(player.getLocation())));
}
@Service
private static final class LegacyAlias extends CoordinatesCommand implements LegacyAliasCommand {
@Inject
LegacyAlias(@NotNull LocationManipulation locationManipulation) {
super(locationManipulation);
}
@Override
@CommandAlias("mvcoord|mvco")
@Subcommand("coords|coord|co")
void onCoordinatesCommand(MVCommandIssuer issuer, Player player, MultiverseWorld world) {
super.onCoordinatesCommand(issuer, player, world);
}
issuer.sendInfo(MVCorei18n.COORDINATES_INFO_WORLD, "{world}", world.getName());
issuer.sendInfo(MVCorei18n.COORDINATES_INFO_ALIAS, "{alias}", world.getAlias());
issuer.sendInfo(MVCorei18n.COORDINATES_INFO_WORLDSCALE, "{scale}", String.valueOf(world.getScale()));
issuer.sendInfo(MVCorei18n.COORDINATES_INFO_COORDINATES, "{coordinates}", locationManipulation.strCoords(player.getLocation()));
issuer.sendInfo(MVCorei18n.COORDINATES_INFO_DIRECTION, "{direction}", locationManipulation.getDirection(player.getLocation()));
}
}
@@ -1,16 +0,0 @@
package org.mvplugins.multiverse.core.commands;
import co.aikar.commands.annotation.CommandAlias;
import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Contract;
import org.mvplugins.multiverse.core.command.MVCommandManager;
import org.mvplugins.multiverse.core.command.MultiverseCommand;
/**
* Represents a command that is part of the Multiverse-Core plugin.
*/
@Contract
@CommandAlias("mv")
public abstract class CoreCommand extends MultiverseCommand {
}
@@ -1,8 +1,8 @@
package org.mvplugins.multiverse.core.commands;
import java.util.Collections;
import java.util.Random;
import co.aikar.commands.ACFUtil;
import co.aikar.commands.annotation.CommandAlias;
import co.aikar.commands.annotation.CommandCompletion;
import co.aikar.commands.annotation.CommandPermission;
@@ -17,43 +17,62 @@ import org.bukkit.WorldType;
import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Service;
import org.mvplugins.multiverse.core.command.LegacyAliasCommand;
import org.mvplugins.multiverse.core.command.MVCommandIssuer;
import org.mvplugins.multiverse.core.command.MVCommandManager;
import org.mvplugins.multiverse.core.command.flag.CommandFlag;
import org.mvplugins.multiverse.core.command.flag.CommandFlagsManager;
import org.mvplugins.multiverse.core.command.flag.CommandValueFlag;
import org.mvplugins.multiverse.core.command.flag.FlagBuilder;
import org.mvplugins.multiverse.core.command.flag.ParsedCommandFlags;
import org.mvplugins.multiverse.core.locale.MVCorei18n;
import org.mvplugins.multiverse.core.locale.message.MessageReplacement.Replace;
import org.mvplugins.multiverse.core.utils.result.Attempt.Failure;
import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld;
import org.mvplugins.multiverse.core.commandtools.MVCommandIssuer;
import org.mvplugins.multiverse.core.commandtools.MVCommandManager;
import org.mvplugins.multiverse.core.commandtools.MultiverseCommand;
import org.mvplugins.multiverse.core.commandtools.flags.CommandFlag;
import org.mvplugins.multiverse.core.commandtools.flags.CommandValueFlag;
import org.mvplugins.multiverse.core.commandtools.flags.ParsedCommandFlags;
import org.mvplugins.multiverse.core.utils.MVCorei18n;
import org.mvplugins.multiverse.core.world.WorldManager;
import org.mvplugins.multiverse.core.world.biomeprovider.BiomeProviderFactory;
import org.mvplugins.multiverse.core.world.generators.GeneratorProvider;
import org.mvplugins.multiverse.core.world.options.CreateWorldOptions;
import org.mvplugins.multiverse.core.world.reasons.CreateFailureReason;
import static org.mvplugins.multiverse.core.locale.message.MessageReplacement.replace;
@Service
class CreateCommand extends CoreCommand {
@CommandAlias("mv")
class CreateCommand extends MultiverseCommand {
private final WorldManager worldManager;
private final CreateCommand.Flags flags;
private GeneratorProvider generatorProvider;
private final CommandValueFlag<String> SEED_FLAG = flag(CommandValueFlag.builder("--seed", String.class)
.addAlias("-s")
.completion(input -> Collections.singleton(String.valueOf(new Random().nextLong())))
.build());
private final CommandValueFlag<String> GENERATOR_FLAG = flag(CommandValueFlag
.builder("--generator", String.class)
.addAlias("-g")
.completion(input -> generatorProvider.suggestGeneratorString(input))
.build());
private final CommandValueFlag<WorldType> WORLD_TYPE_FLAG = flag(CommandValueFlag
.enumBuilder("--world-type", WorldType.class)
.addAlias("-t")
.build());
private final CommandFlag NO_ADJUST_SPAWN_FLAG = flag(CommandFlag.builder("--no-adjust-spawn")
.addAlias("-n")
.build());
private final CommandFlag NO_STRUCTURES_FLAG = flag(CommandFlag.builder("--no-structures")
.addAlias("-a")
.build());
@Inject
CreateCommand(@NotNull WorldManager worldManager, @NotNull Flags flags) {
CreateCommand(
@NotNull MVCommandManager commandManager,
@NotNull WorldManager worldManager,
@NotNull GeneratorProvider generatorProvider) {
super(commandManager);
this.worldManager = worldManager;
this.flags = flags;
this.generatorProvider = generatorProvider;
}
@Subcommand("create")
@CommandPermission("multiverse.core.create")
@CommandCompletion("@empty @environments @flags:groupName=" + Flags.NAME)
@Syntax("<name> <environment> [--seed <seed> --generator <generator[:id]> --world-type <worldtype> --adjust-spawn "
+ "--no-structures --biome <biome>]")
@CommandCompletion("@empty @flags:groupName=mvcreatecommand")
@Syntax("<name> <environment> --seed [seed] --generator [generator[:id]] --world-type [worldtype] --adjust-spawn --no-structures")
@Description("{@@mv-core.create.description}")
void onCreateCommand(
MVCommandIssuer issuer,
@@ -67,132 +86,41 @@ class CreateCommand extends CoreCommand {
World.Environment environment,
@Optional
@Syntax("[--seed <seed> --generator <generator[:id]> --world-type <worldtype> --adjust-spawn "
+ "--no-structures --biome <biome>]")
@Syntax("--seed [seed] --generator [generator[:id]] --world-type [worldtype] --adjust-spawn --no-structures")
@Description("{@@mv-core.create.flags.description}")
String[] flagArray) {
ParsedCommandFlags parsedFlags = flags.parse(flagArray);
String[] flags) {
ParsedCommandFlags parsedFlags = parseFlags(flags);
messageWorldDetails(issuer, worldName, environment, parsedFlags);
issuer.sendInfo(MVCorei18n.CREATE_PROPERTIES,
"{worldName}", worldName);
issuer.sendInfo(MVCorei18n.CREATE_PROPERTIES_ENVIRONMENT,
"{environment}", environment.name());
issuer.sendInfo(MVCorei18n.CREATE_PROPERTIES_SEED,
"{seed}", parsedFlags.flagValue(SEED_FLAG, "RANDOM"));
issuer.sendInfo(MVCorei18n.CREATE_PROPERTIES_WORLDTYPE,
"{worldType}", parsedFlags.flagValue(WORLD_TYPE_FLAG, WorldType.NORMAL).name());
issuer.sendInfo(MVCorei18n.CREATE_PROPERTIES_ADJUSTSPAWN,
"{adjustSpawn}", String.valueOf(!parsedFlags.hasFlag(NO_ADJUST_SPAWN_FLAG)));
issuer.sendInfo(MVCorei18n.CREATE_PROPERTIES_GENERATOR,
"{generator}", parsedFlags.flagValue(GENERATOR_FLAG, ""));
issuer.sendInfo(MVCorei18n.CREATE_PROPERTIES_STRUCTURES,
"{structures}", String.valueOf(!parsedFlags.hasFlag(NO_STRUCTURES_FLAG)));
issuer.sendInfo(MVCorei18n.CREATE_LOADING);
worldManager.createWorld(CreateWorldOptions.worldName(worldName)
.biome(parsedFlags.flagValue(flags.biome, ""))
.environment(environment)
.seed(parsedFlags.flagValue(flags.seed))
.worldType(parsedFlags.flagValue(flags.worldType, WorldType.NORMAL))
.useSpawnAdjust(!parsedFlags.hasFlag(flags.noAdjustSpawn))
.generator(parsedFlags.flagValue(flags.generator, ""))
.generatorSettings(parsedFlags.flagValue(flags.generatorSettings, ""))
.generateStructures(!parsedFlags.hasFlag(flags.noStructures)))
.onSuccess(newWorld -> messageSuccess(issuer, newWorld))
.onFailure(failure -> messageFailure(issuer, failure));
}
private void messageWorldDetails(MVCommandIssuer issuer, String worldName,
World.Environment environment, ParsedCommandFlags parsedFlags) {
issuer.sendInfo(MVCorei18n.CREATE_PROPERTIES,
replace("{worldName}").with(worldName));
issuer.sendInfo(MVCorei18n.CREATE_PROPERTIES_ENVIRONMENT,
replace("{environment}").with(environment.name()));
issuer.sendInfo(MVCorei18n.CREATE_PROPERTIES_SEED,
replace("{seed}").with(parsedFlags.flagValue(flags.seed, "RANDOM")));
issuer.sendInfo(MVCorei18n.CREATE_PROPERTIES_WORLDTYPE,
replace("{worldType}").with(parsedFlags.flagValue(flags.worldType, WorldType.NORMAL).name()));
issuer.sendInfo(MVCorei18n.CREATE_PROPERTIES_ADJUSTSPAWN,
replace("{adjustSpawn}").with(String.valueOf(!parsedFlags.hasFlag(flags.noAdjustSpawn))));
if (parsedFlags.hasFlag(flags.biome)) {
issuer.sendInfo(MVCorei18n.CREATE_PROPERTIES_BIOME,
replace("{biome}").with(parsedFlags.flagValue(flags.biome)));
}
if (parsedFlags.hasFlag(flags.generator)) {
issuer.sendInfo(MVCorei18n.CREATE_PROPERTIES_GENERATOR,
replace("{generator}").with(parsedFlags.flagValue(flags.generator)));
}
if (parsedFlags.hasFlag(flags.generatorSettings)) {
issuer.sendInfo(MVCorei18n.CREATE_PROPERTIES_GENERATORSETTINGS,
replace("{generatorSettings}").with(parsedFlags.flagValue(flags.generatorSettings)));
}
issuer.sendInfo(MVCorei18n.CREATE_PROPERTIES_STRUCTURES,
replace("{structures}").with(String.valueOf(!parsedFlags.hasFlag(flags.noStructures))));
}
private void messageSuccess(MVCommandIssuer issuer, LoadedMultiverseWorld newWorld) {
Logging.fine("World create success: " + newWorld);
issuer.sendInfo(MVCorei18n.CREATE_SUCCESS, Replace.WORLD.with(newWorld.getName()));
}
private void messageFailure(MVCommandIssuer issuer, Failure<LoadedMultiverseWorld, CreateFailureReason> failure) {
Logging.fine("World create failure: " + failure);
issuer.sendError(failure.getFailureMessage());
}
@Service
private static final class Flags extends FlagBuilder {
private static final String NAME = "mvcreate";
private GeneratorProvider generatorProvider;
private BiomeProviderFactory biomeProviderFactory;
@Inject
private Flags(
@NotNull CommandFlagsManager flagsManager,
@NotNull GeneratorProvider generatorProvider,
@NotNull BiomeProviderFactory biomeProviderFactory
) {
super(NAME, flagsManager);
this.generatorProvider = generatorProvider;
this.biomeProviderFactory = biomeProviderFactory;
}
private final CommandValueFlag<String> seed = flag(CommandValueFlag.builder("--seed", String.class)
.addAlias("-s")
.completion(input -> Collections.singleton(String.valueOf(ACFUtil.RANDOM.nextLong())))
.build());
private final CommandValueFlag<String> generator = flag(CommandValueFlag
.builder("--generator", String.class)
.addAlias("-g")
.completion(input -> generatorProvider.suggestGeneratorString(input))
.build());
private final CommandValueFlag<String> generatorSettings = flag(CommandValueFlag
.builder("--generator-settings", String.class)
.addAlias("-gs")
.build());
private final CommandValueFlag<WorldType> worldType = flag(CommandValueFlag
.enumBuilder("--world-type", WorldType.class)
.addAlias("-t")
.build());
private final CommandFlag noAdjustSpawn = flag(CommandFlag.builder("--no-adjust-spawn")
.addAlias("-n")
.build());
private final CommandFlag noStructures = flag(CommandFlag.builder("--no-structures")
.addAlias("-a")
.build());
private final CommandValueFlag<String> biome = flag(CommandValueFlag.builder("--biome", String.class)
.addAlias("-b")
.completion(input -> biomeProviderFactory.suggestBiomeString(input))
.build());
}
@Service
private static final class LegacyAlias extends CreateCommand implements LegacyAliasCommand {
@Inject
LegacyAlias(@NotNull WorldManager worldManager, @NotNull Flags flags) {
super(worldManager, flags);
}
@Override
@CommandAlias("mvcreate|mvc")
void onCreateCommand(MVCommandIssuer issuer, String worldName, World.Environment environment, String[] flags) {
super.onCreateCommand(issuer, worldName, environment, flags);
}
.seed(parsedFlags.flagValue(SEED_FLAG))
.worldType(parsedFlags.flagValue(WORLD_TYPE_FLAG, WorldType.NORMAL))
.useSpawnAdjust(!parsedFlags.hasFlag(NO_ADJUST_SPAWN_FLAG))
.generator(parsedFlags.flagValue(GENERATOR_FLAG, ""))
.generateStructures(!parsedFlags.hasFlag(NO_STRUCTURES_FLAG)))
.onSuccess(newWorld -> {
Logging.fine("World create success: " + newWorld);
issuer.sendInfo(MVCorei18n.CREATE_SUCCESS, "{world}", newWorld.getName());
}).onFailure(failure -> {
Logging.fine("World create failure: " + failure);
issuer.sendError(failure.getFailureMessage());
});
}
}
@@ -12,18 +12,20 @@ import jakarta.inject.Inject;
import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Service;
import org.mvplugins.multiverse.core.command.LegacyAliasCommand;
import org.mvplugins.multiverse.core.command.MVCommandManager;
import org.mvplugins.multiverse.core.config.CoreConfig;
import org.mvplugins.multiverse.core.locale.MVCorei18n;
import org.mvplugins.multiverse.core.commandtools.MVCommandManager;
import org.mvplugins.multiverse.core.commandtools.MultiverseCommand;
import org.mvplugins.multiverse.core.config.MVCoreConfig;
import org.mvplugins.multiverse.core.utils.MVCorei18n;
@Service
class DebugCommand extends CoreCommand {
@CommandAlias("mv")
class DebugCommand extends MultiverseCommand {
private final CoreConfig config;
private final MVCoreConfig config;
@Inject
DebugCommand(@NotNull CoreConfig config) {
DebugCommand(@NotNull MVCommandManager commandManager, @NotNull MVCoreConfig config) {
super(commandManager);
this.config = config;
}
@@ -60,18 +62,4 @@ class DebugCommand extends CoreCommand {
issuer.sendInfo(MVCorei18n.DEBUG_INFO_ON, "{level}", String.valueOf(debugLevel));
Logging.fine("Multiverse Debug ENABLED.");
}
@Service
private static final class LegacyAlias extends DebugCommand implements LegacyAliasCommand {
@Inject
public LegacyAlias(@NotNull CoreConfig config) {
super(config);
}
@Override
@CommandAlias("mvdebug")
void onChangeDebugCommand(BukkitCommandIssuer issuer, int level) {
super.onChangeDebugCommand(issuer, level);
}
}
}
@@ -1,10 +1,11 @@
package org.mvplugins.multiverse.core.commands;
import co.aikar.commands.MessageType;
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.Single;
import co.aikar.commands.annotation.Subcommand;
import co.aikar.commands.annotation.Syntax;
@@ -13,114 +14,51 @@ import jakarta.inject.Inject;
import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Service;
import org.mvplugins.multiverse.core.command.LegacyAliasCommand;
import org.mvplugins.multiverse.core.command.MVCommandIssuer;
import org.mvplugins.multiverse.core.command.MVCommandManager;
import org.mvplugins.multiverse.core.command.flag.ParsedCommandFlags;
import org.mvplugins.multiverse.core.command.flags.RemovePlayerFlags;
import org.mvplugins.multiverse.core.command.queue.CommandQueueManager;
import org.mvplugins.multiverse.core.command.queue.CommandQueuePayload;
import org.mvplugins.multiverse.core.locale.MVCorei18n;
import org.mvplugins.multiverse.core.locale.message.Message;
import org.mvplugins.multiverse.core.locale.message.MessageReplacement.Replace;
import org.mvplugins.multiverse.core.utils.WorldTickDeferrer;
import org.mvplugins.multiverse.core.utils.result.AsyncAttemptsAggregate;
import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld;
import org.mvplugins.multiverse.core.world.MultiverseWorld;
import org.mvplugins.multiverse.core.commandtools.MVCommandIssuer;
import org.mvplugins.multiverse.core.commandtools.MVCommandManager;
import org.mvplugins.multiverse.core.commandtools.MultiverseCommand;
import org.mvplugins.multiverse.core.commandtools.queue.QueuedCommand;
import org.mvplugins.multiverse.core.utils.MVCorei18n;
import org.mvplugins.multiverse.core.world.WorldManager;
import org.mvplugins.multiverse.core.world.helpers.PlayerWorldTeleporter;
import org.mvplugins.multiverse.core.world.options.DeleteWorldOptions;
@Service
class DeleteCommand extends CoreCommand {
@CommandAlias("mv")
class DeleteCommand extends MultiverseCommand {
private final CommandQueueManager commandQueueManager;
private final WorldManager worldManager;
private final PlayerWorldTeleporter playerWorldTeleporter;
private final WorldTickDeferrer worldTickDeferrer;
private final RemovePlayerFlags flags;
@Inject
DeleteCommand(
@NotNull CommandQueueManager commandQueueManager,
@NotNull WorldManager worldManager,
@NotNull PlayerWorldTeleporter playerWorldTeleporter,
@NotNull WorldTickDeferrer worldTickDeferrer,
@NotNull RemovePlayerFlags flags
) {
this.commandQueueManager = commandQueueManager;
DeleteCommand(@NotNull MVCommandManager commandManager, @NotNull WorldManager worldManager) {
super(commandManager);
this.worldManager = worldManager;
this.playerWorldTeleporter = playerWorldTeleporter;
this.worldTickDeferrer = worldTickDeferrer;
this.flags = flags;
}
@Subcommand("delete")
@CommandPermission("multiverse.core.delete")
@CommandCompletion("@mvworlds:scope=both @flags:groupName=" + RemovePlayerFlags.NAME)
@CommandCompletion("@mvworlds:scope=both")
@Syntax("<world>")
@Description("{@@mv-core.delete.description}")
void onDeleteCommand(
MVCommandIssuer issuer,
@Single
@Conditions("worldname:scope=both")
@Syntax("<world>")
@Description("The world you want to delete.")
MultiverseWorld world,
@Optional
@Syntax("[--remove-players]")
@Description("")
String[] flagArray) {
ParsedCommandFlags parsedFlags = flags.parse(flagArray);
commandQueueManager.addToQueue(CommandQueuePayload
.issuer(issuer)
.action(() -> runDeleteCommand(issuer, world, parsedFlags))
.prompt(Message.of(MVCorei18n.DELETE_PROMPT, "",
Replace.WORLD.with(world.getName()))));
}
private void runDeleteCommand(MVCommandIssuer issuer, MultiverseWorld world, ParsedCommandFlags parsedFlags) {
issuer.sendInfo(MVCorei18n.DELETE_DELETING, Replace.WORLD.with(world.getName()));
var future = parsedFlags.hasFlag(flags.removePlayers)
&& world.isLoaded()
&& world instanceof LoadedMultiverseWorld loadedWorld
? playerWorldTeleporter.removeFromWorld(loadedWorld)
: AsyncAttemptsAggregate.emptySuccess();
future.onSuccess(() -> worldTickDeferrer.deferWorldTick(() -> doWorldDeleting(issuer, world)))
.onFailure(() -> issuer.sendError("Failed to teleport one or more players out of the world!"));
}
private void doWorldDeleting(MVCommandIssuer issuer, MultiverseWorld world) {
worldManager.deleteWorld(DeleteWorldOptions.world(world))
.onSuccess(deletedWorldName -> {
Logging.fine("World delete success: " + deletedWorldName);
issuer.sendInfo(MVCorei18n.DELETE_SUCCESS, Replace.WORLD.with(deletedWorldName));
}).onFailure(failure -> {
Logging.fine("World delete failure: " + failure);
issuer.sendError(failure.getFailureMessage());
});
}
@Service
private static final class LegacyAlias extends DeleteCommand implements LegacyAliasCommand {
@Inject
LegacyAlias(
@NotNull CommandQueueManager commandQueueManager,
@NotNull WorldManager worldManager,
@NotNull PlayerWorldTeleporter playerWorldTeleporter,
@NotNull WorldTickDeferrer worldTickDeferrer,
@NotNull RemovePlayerFlags flags) {
super(commandQueueManager, worldManager, playerWorldTeleporter, worldTickDeferrer, flags);
}
@Override
@CommandAlias("mvdelete")
void onDeleteCommand(MVCommandIssuer issuer, MultiverseWorld world, String[] flags) {
super.onDeleteCommand(issuer, world, flags);
}
String worldName) {
this.commandManager.getCommandQueueManager().addToQueue(new QueuedCommand(
issuer.getIssuer(),
() -> {
issuer.sendInfo(MVCorei18n.DELETE_DELETING, "{world}", worldName);
worldManager.deleteWorld(worldName)
.onSuccess(deletedWorldName -> {
Logging.fine("World delete success: " + deletedWorldName);
issuer.sendInfo(MVCorei18n.DELETE_SUCCESS, "{world}", deletedWorldName);
}).onFailure(failure -> {
Logging.fine("World delete failure: " + failure);
issuer.sendError(failure.getFailureMessage());
});
}, this.commandManager.formatMessage(
issuer, MessageType.INFO, MVCorei18n.DELETE_PROMPT, "{world}", worldName)));
}
}
@@ -1,81 +1,282 @@
package org.mvplugins.multiverse.core.commands;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Map;
import co.aikar.commands.CommandIssuer;
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.Subcommand;
import co.aikar.commands.annotation.Syntax;
import com.dumptruckman.minecraft.util.Logging;
import jakarta.inject.Inject;
import org.apache.commons.lang.StringUtils;
import org.bukkit.scheduler.BukkitRunnable;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jvnet.hk2.annotations.Service;
import org.mvplugins.multiverse.core.command.flag.CommandFlagsManager;
import org.mvplugins.multiverse.core.command.flag.FlagBuilder;
import org.mvplugins.multiverse.core.commands.DumpsLogPoster.LogsType;
import org.mvplugins.multiverse.core.commands.DumpsLogPoster.UploadType;
import org.mvplugins.multiverse.core.command.MVCommandIssuer;
import org.mvplugins.multiverse.core.command.MVCommandManager;
import org.mvplugins.multiverse.core.command.flag.CommandFlag;
import org.mvplugins.multiverse.core.command.flag.CommandValueFlag;
import org.mvplugins.multiverse.core.command.flag.ParsedCommandFlags;
import org.mvplugins.multiverse.core.MultiverseCore;
import org.mvplugins.multiverse.core.commandtools.MVCommandManager;
import org.mvplugins.multiverse.core.commandtools.MultiverseCommand;
import org.mvplugins.multiverse.core.commandtools.flags.CommandFlag;
import org.mvplugins.multiverse.core.commandtools.flags.CommandValueFlag;
import org.mvplugins.multiverse.core.commandtools.flags.ParsedCommandFlags;
import org.mvplugins.multiverse.core.event.MVVersionEvent;
import org.mvplugins.multiverse.core.utils.MVCorei18n;
import org.mvplugins.multiverse.core.utils.webpaste.PasteFailedException;
import org.mvplugins.multiverse.core.utils.webpaste.PasteService;
import org.mvplugins.multiverse.core.utils.webpaste.PasteServiceFactory;
import org.mvplugins.multiverse.core.utils.webpaste.PasteServiceType;
import org.mvplugins.multiverse.core.world.WorldManager;
import static org.mvplugins.multiverse.core.utils.file.FileUtils.getBukkitConfig;
import static org.mvplugins.multiverse.core.utils.file.FileUtils.getServerProperties;
@Service
final class DumpsCommand extends CoreCommand {
@CommandAlias("mv")
class DumpsCommand extends MultiverseCommand {
private final DumpsService dumpsService;
private final DumpsCommand.Flags flags;
private final MultiverseCore plugin;
private final WorldManager worldManager;
private final CommandValueFlag<LogsTypeOption> LOGS_FLAG = flag(CommandValueFlag
.enumBuilder("--logs", LogsTypeOption.class)
.addAlias("-l")
.build());
private final CommandValueFlag<ServiceTypeOption> UPLOAD_FLAG = flag(CommandValueFlag
.enumBuilder("--upload", ServiceTypeOption.class)
.addAlias("-u")
.build());
// Does not upload logs or plugin list (except if --logs mclogs is there)
private final CommandFlag PARANOID_FLAG = flag(CommandFlag.builder("--paranoid")
.addAlias("-p")
.build());
@Inject
DumpsCommand(@NotNull DumpsService dumpsService, @NotNull Flags flags) {
this.dumpsService = dumpsService;
this.flags = flags;
DumpsCommand(@NotNull MVCommandManager commandManager,
@NotNull MultiverseCore plugin,
@NotNull WorldManager worldManager) {
super(commandManager);
this.plugin = plugin;
this.worldManager = worldManager;
}
private enum ServiceTypeOption {
PASTEGG,
PASTESDEV
}
private enum LogsTypeOption {
APPEND,
MCLOGS
}
@Subcommand("dumps")
@CommandPermission("multiverse.core.dumps")
@CommandCompletion("@flags:groupName=" + Flags.NAME)
@CommandCompletion("@flags:groupName=mvdumpscommand")
@Syntax("[--logs <mclogs | append>] [--upload <pastesdev | pastegg>] [--paranoid]")
@Description("{@@mv-core.dumps.description}")
void onDumpsCommand(
MVCommandIssuer issuer,
CommandIssuer issuer,
@Optional
@Syntax("[--logs <mclogs | append>] [--upload <pastesdev | pastegg>] [--paranoid]")
String[] flagArray) {
ParsedCommandFlags parsedFlags = flags.parse(flagArray);
String[] flags) {
final ParsedCommandFlags parsedFlags = parseFlags(flags);
// Grab all our flags
boolean paranoid = parsedFlags.hasFlag(flags.paranoid);
LogsType logsType = parsedFlags.flagValue(flags.logs, LogsType.MCLOGS);
UploadType servicesType = parsedFlags.flagValue(flags.upload, UploadType.PASTESDEV);
final boolean paranoid = parsedFlags.hasFlag(PARANOID_FLAG);
final LogsTypeOption logsType = parsedFlags.flagValue(LOGS_FLAG, LogsTypeOption.MCLOGS);
final ServiceTypeOption servicesType = parsedFlags.flagValue(UPLOAD_FLAG, ServiceTypeOption.PASTEGG);
dumpsService.postLogs(issuer, logsType, servicesType, paranoid);
}
// Initialise and add info to the debug event
MVVersionEvent versionEvent = new MVVersionEvent();
this.addDebugInfoToEvent(versionEvent);
plugin.getServer().getPluginManager().callEvent(versionEvent);
@Service
private static final class Flags extends FlagBuilder {
private static final String NAME = "mvdumps";
@Inject
private Flags(@NotNull CommandFlagsManager flagsManager) {
super(NAME, flagsManager);
// Add plugin list if user isn't paranoid
if (!paranoid) {
versionEvent.putDetailedVersionInfo("plugins.md", "# Plugins\n\n" + getPluginList());
}
private final CommandValueFlag<LogsType> logs = flag(CommandValueFlag
.enumBuilder("--logs", LogsType.class)
.addAlias("-l")
.build());
BukkitRunnable logPoster = new BukkitRunnable() {
@Override
public void run() {
// TODO: Refactor into smaller methods
Logging.finer("Logs type is: " + logsType);
Logging.finer("Services is: " + servicesType);
private final CommandValueFlag<UploadType> upload = flag(CommandValueFlag
.enumBuilder("--upload", UploadType.class)
.addAlias("-u")
.build());
// Deal with logs flag
if (!paranoid) {
switch (logsType) {
case MCLOGS -> issuer.sendInfo(MVCorei18n.DUMPS_URL_LIST,
"{service}", "Logs",
"{link}", postToService(PasteServiceType.MCLOGS, true, getLogs(), null));
case APPEND -> versionEvent.putDetailedVersionInfo("latest.log", getLogs());
}
}
// Does not upload logs or plugin list (except if --logs mclogs is there)
private final CommandFlag paranoid = flag(CommandFlag.builder("--paranoid")
.addAlias("-p")
.build());
// Get the files from the event
final Map<String, String> files = versionEvent.getDetailedVersionInfo();
// Deal with uploading debug info
switch (servicesType) {
case PASTEGG -> issuer.sendInfo(MVCorei18n.DUMPS_URL_LIST,
"{service}", "paste.gg",
"{link}", postToService(PasteServiceType.PASTEGG, true, null, files));
case PASTESDEV -> issuer.sendInfo(MVCorei18n.DUMPS_URL_LIST,
"{service}", "pastes.dev",
"{link}", postToService(PasteServiceType.PASTESDEV, true, null, files));
}
}
};
// Run the uploader async as it could take some time to upload the debug info
logPoster.runTaskAsynchronously(plugin);
}
/**
* Get the contents of the latest.log file
*
* @return A string containing the latest.log file
*/
private String getLogs() {
// Get the Path of latest.log
Path logsPath = plugin.getServer().getWorldContainer().toPath().resolve("logs").resolve("latest.log");
File logsFile = logsPath.toFile();
if (!logsFile.exists()) {
Logging.warning("Could not read logs/latest.log");
return "Could not find log";
}
// Try reading as ANSI encoded
try {
return Files.readString(logsPath, StandardCharsets.ISO_8859_1);
} catch (IOException e) {
Logging.finer("Log is not ANSI encoded. Trying UTF-8");
// Must be a UTF-8 encoded log then
}
// Try reading as UTF-8 encoded
try {
return Files.readString(logsPath, StandardCharsets.UTF_8);
} catch (IOException ex) {
// It is some other strange encoding
Logging.severe("Could not read ./logs/latest.log. See below for stack trace");
ex.printStackTrace();
}
return "Could not read log";
}
private String getVersionString() {
return "# Multiverse-Core Version info" + "\n\n"
+ " - Multiverse-Core Version: " + this.plugin.getDescription().getVersion() + '\n'
+ " - Bukkit Version: " + this.plugin.getServer().getVersion() + '\n'
+ " - Loaded Worlds: " + worldManager.getLoadedWorlds() + '\n'
+ " - Multiverse Plugins Loaded: " + this.plugin.getPluginCount() + '\n';
}
private void addDebugInfoToEvent(MVVersionEvent event) {
// Add the legacy file, but as markdown, so it's readable
event.putDetailedVersionInfo("version.md", this.getVersionString());
// add config.yml
File configFile = new File(plugin.getDataFolder(), "config.yml");
event.putDetailedVersionInfo("multiverse-core/config.yml", configFile);
// add worlds.yml
File worldsFile = new File(plugin.getDataFolder(), "worlds.yml");
event.putDetailedVersionInfo("multiverse-core/worlds.yml", worldsFile);
// Add bukkit.yml if we found it
if (getBukkitConfig() != null) {
event.putDetailedVersionInfo(getBukkitConfig().getPath(), getBukkitConfig());
} else {
Logging.warning("/mv version could not find bukkit.yml. Not including file");
}
// Add server.properties if we found it
if (getServerProperties() != null) {
event.putDetailedVersionInfo(getServerProperties().getPath(), getServerProperties());
} else {
Logging.warning("/mv version could not find server.properties. Not including file");
}
}
private String getPluginList() {
return " - " + StringUtils.join(plugin.getServer().getPluginManager().getPlugins(), "\n - ");
}
/**
* Turns a list of files in to a string containing askii art.
*
* @param files Map of filenames/contents
* @return The askii art
*/
private String encodeAsString(Map<String, String> files) {
StringBuilder uploadData = new StringBuilder();
for (String file : files.keySet()) {
String data = files.get(file);
uploadData.append("# ---------- ")
.append(file)
.append(" ----------\n\n")
.append(data)
.append("\n\n");
}
return uploadData.toString();
}
/**
* Send the current contents of this.pasteBinBuffer to a web service.
*
* @param type Service type to send paste data to.
* @param isPrivate Should the paste be marked as private.
* @param rawPasteData Legacy string containing only data to post to a service.
* @param pasteFiles Map of filenames/contents of debug info.
* @return URL of visible paste
*/
private String postToService(@NotNull PasteServiceType type, boolean isPrivate, @Nullable String rawPasteData, @Nullable Map<String, String> pasteFiles) {
PasteService pasteService = PasteServiceFactory.getService(type, isPrivate);
try {
// Upload normally when multi file is supported
if (pasteService.supportsMultiFile()) {
return pasteService.postData(pasteFiles);
}
// When there is raw paste data, use that
if (rawPasteData != null) { // For the logs
return pasteService.postData(rawPasteData);
}
// If all we have are files and the paste service does not support multi file then encode them
if (pasteFiles != null) {
return pasteService.postData(this.encodeAsString(pasteFiles));
}
// Should never get here
return "No data specified in code";
} catch (PasteFailedException e) {
e.printStackTrace();
return "Error posting to service.";
} catch (NullPointerException e) {
e.printStackTrace();
return "That service isn't supported yet.";
}
}
}
@@ -1,143 +0,0 @@
package org.mvplugins.multiverse.core.commands;
import java.util.Map;
import com.dumptruckman.minecraft.util.Logging;
import org.bukkit.scheduler.BukkitRunnable;
import org.jetbrains.annotations.NotNull;
import org.mvplugins.multiverse.core.command.MVCommandIssuer;
import org.mvplugins.multiverse.core.event.MVDumpsDebugInfoEvent;
import org.mvplugins.multiverse.core.locale.MVCorei18n;
import org.mvplugins.multiverse.core.utils.webpaste.PasteFailedException;
import org.mvplugins.multiverse.core.utils.webpaste.PasteService;
import org.mvplugins.multiverse.core.utils.webpaste.PasteServiceFactory;
import org.mvplugins.multiverse.core.utils.webpaste.PasteServiceType;
final class DumpsLogPoster extends BukkitRunnable {
enum UploadType {
// BEGIN CHECKSTYLE-SUPPRESSION: JavadocVariable
PASTEGG(PasteServiceType.PASTEGG, "paste.gg"),
PASTESDEV(PasteServiceType.PASTESDEV, "pastes.dev");
// END CHECKSTYLE-SUPPRESSION: JavadocVariable
private final PasteServiceType pasteServiceType;
private final String serviceName;
UploadType(PasteServiceType pasteServiceType, String serviceName) {
this.pasteServiceType = pasteServiceType;
this.serviceName = serviceName;
}
}
enum LogsType {
/**
* Appends the log file to the debug info.
*/
APPEND,
/**
* Uploads the log file to a paste service.
*/
MCLOGS
}
private final MVCommandIssuer issuer;
private final LogsType logsType;
private final UploadType uploadType;
private final boolean paranoid;
private final String logs;
private final MVDumpsDebugInfoEvent versionEvent;
DumpsLogPoster(@NotNull MVCommandIssuer issuer,
@NotNull DumpsLogPoster.LogsType logsType,
@NotNull DumpsLogPoster.UploadType uploadType,
boolean paranoid,
@NotNull String logs,
@NotNull MVDumpsDebugInfoEvent versionEvent) {
this.issuer = issuer;
this.logsType = logsType;
this.uploadType = uploadType;
this.paranoid = paranoid;
this.logs = logs;
this.versionEvent = versionEvent;
}
@Override
public void run() {
handleLogs();
handleVersionEvent();
}
@SuppressWarnings("checkstyle:MissingSwitchDefault")
private void handleLogs() {
Logging.finer("Logs type is: " + logsType);
if (!paranoid) {
switch (logsType) {
case MCLOGS -> sendDumpsUrl("Logs", postRawDataToMcLogs(logs));
case APPEND -> versionEvent.putDetailedDebugInfo("latest.log", logs);
default -> Logging.finer("Not uploading logs.");
}
} else {
Logging.finer("Paranoid mode is on, not uploading logs.");
}
}
private void handleVersionEvent() {
Logging.finer("Upload service is: " + uploadType);
final Map<String, String> files = versionEvent.getDetailedDebugInfo();
sendDumpsUrl(uploadType.serviceName, postFilesToService(files));
}
private void sendDumpsUrl(String service, String url) {
issuer.sendInfo(MVCorei18n.DUMPS_URL_LIST, "{service}", service, "{link}", url);
}
private String postRawDataToMcLogs(@NotNull String rawPasteData) {
PasteService pasteService = PasteServiceFactory.getService(PasteServiceType.MCLOGS, true);
return pasteToService(() -> pasteService.postData(rawPasteData));
}
private String postFilesToService(@NotNull Map<String, String> pasteFiles) {
PasteService pasteService = PasteServiceFactory.getService(uploadType.pasteServiceType, true);
return pasteToService(() -> {
if (pasteService.supportsMultiFile()) {
return pasteService.postData(pasteFiles);
} else {
return pasteService.postData(this.encodeAsString(pasteFiles));
}
});
}
private String encodeAsString(Map<String, String> files) {
StringBuilder uploadData = new StringBuilder();
for (Map.Entry<String, String> entry : files.entrySet()) {
String file = entry.getKey();
String data = entry.getValue();
uploadData.append("# ---------- ")
.append(file)
.append(" ----------\n\n")
.append(data)
.append("\n\n");
}
return uploadData.toString();
}
private String pasteToService(LogPaster paster) {
try {
return paster.postLogs();
} catch (PasteFailedException e) {
e.printStackTrace();
return "Error posting to service.";
}
}
private interface LogPaster {
String postLogs() throws PasteFailedException;
}
}
@@ -1,145 +0,0 @@
package org.mvplugins.multiverse.core.commands;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import com.dumptruckman.minecraft.util.Logging;
import jakarta.inject.Inject;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Service;
import org.mvplugins.multiverse.core.MultiverseCore;
import org.mvplugins.multiverse.core.module.MultiverseModulesRegistry;
import org.mvplugins.multiverse.core.commands.DumpsLogPoster.LogsType;
import org.mvplugins.multiverse.core.commands.DumpsLogPoster.UploadType;
import org.mvplugins.multiverse.core.command.MVCommandIssuer;
import org.mvplugins.multiverse.core.event.MVDumpsDebugInfoEvent;
import org.mvplugins.multiverse.core.utils.FileUtils;
import org.mvplugins.multiverse.core.utils.StringFormatter;
import org.mvplugins.multiverse.core.world.WorldManager;
@Service
final class DumpsService {
private final MultiverseCore plugin;
private final WorldManager worldManager;
private final FileUtils fileUtils;
@Inject
DumpsService(@NotNull MultiverseCore plugin,
@NotNull WorldManager worldManager,
@NotNull FileUtils fileUtils) {
this.plugin = plugin;
this.worldManager = worldManager;
this.fileUtils = fileUtils;
}
void postLogs(MVCommandIssuer issuer, LogsType logsType, UploadType servicesType, boolean paranoid) {
// Initialise and add info to the debug event
MVDumpsDebugInfoEvent versionEvent = createAndCallDebugInfoEvent();
// Add plugin list if user isn't paranoid
if (!paranoid) {
versionEvent.putDetailedDebugInfo("plugins.md", "# Plugins\n\n" + getPluginList());
}
new DumpsLogPoster(issuer, logsType, servicesType, paranoid, getLogs(), versionEvent)
.runTaskAsynchronously(plugin);
}
/**
* Get the contents of the latest.log file.
*
* @return A string containing the latest.log file
*/
private @NotNull String getLogs() {
// Get the Path of latest.log
Path logsPath = fileUtils.getServerFolder().toPath().resolve("logs/latest.log");
File logsFile = logsPath.toFile();
if (!logsFile.exists()) {
Logging.warning("Could not read logs/latest.log");
return "Could not find log";
}
return readLogsFromFile(logsPath);
}
private @NotNull String readLogsFromFile(Path logsPath) {
String logs = "Could not read log";
// Try reading as ANSI encoded
try {
logs = Files.readString(logsPath, StandardCharsets.UTF_8);
} catch (IOException e) {
Logging.severe("Could not read logs/latest.log. See below for stack trace");
e.printStackTrace();
try {
logs = Files.readString(logsPath, StandardCharsets.UTF_8);
} catch (IOException ex) {
// It is some other strange encoding
Logging.severe("Could not read ./logs/latest.log. See below for stack trace");
ex.printStackTrace();
}
}
return logs;
}
private String getDebugInfoString() {
return "# Multiverse-Core Version info" + "\n\n"
+ " - Multiverse-Core Version: " + this.plugin.getDescription().getVersion() + '\n'
+ " - Bukkit Version: " + this.plugin.getServer().getVersion() + '\n'
+ " - Loaded Worlds: " + worldManager.getLoadedWorlds() + '\n'
+ " - Multiverse Plugins Loaded: " + StringFormatter.joinAnd(MultiverseModulesRegistry.get().getRegisteredPlugins()) + '\n'
+ " - Multiverse Plugins Count: " + MultiverseModulesRegistry.get().getPluginCount() + '\n';
}
private MVDumpsDebugInfoEvent createAndCallDebugInfoEvent() {
MVDumpsDebugInfoEvent event = new MVDumpsDebugInfoEvent();
addDebugInfoToEvent(event);
plugin.getServer().getPluginManager().callEvent(event);
return event;
}
private void addDebugInfoToEvent(MVDumpsDebugInfoEvent event) {
// Add the legacy file, but as markdown, so it's readable
event.putDetailedDebugInfo("version.md", this.getDebugInfoString());
// add config.yml
File configFile = new File(plugin.getDataFolder(), "config.yml");
event.putDetailedDebugInfo("Multiverse-Core/config.yml", configFile);
// add worlds.yml
File worldsFile = new File(plugin.getDataFolder(), "worlds.yml");
event.putDetailedDebugInfo("Multiverse-Core/worlds.yml", worldsFile);
// Add bukkit.yml if we found it
if (fileUtils.getBukkitConfig() != null) {
event.putDetailedDebugInfo(fileUtils.getBukkitConfig().getPath(), fileUtils.getBukkitConfig());
} else {
Logging.warning("/mv dumps could not find bukkit.yml. Not including file");
}
// Add server.properties if we found it
if (fileUtils.getServerProperties() != null) {
event.putDetailedDebugInfo(fileUtils.getServerProperties().getPath(), fileUtils.getServerProperties());
} else {
Logging.warning("/mv dumps could not find server.properties. Not including file");
}
}
private String getPluginList() {
return " - " + StringUtils.join(Arrays.stream(plugin.getServer().getPluginManager().getPlugins())
.map(Object::toString)
.sorted()
.toList(), "\n - ");
}
}
@@ -1,122 +0,0 @@
package org.mvplugins.multiverse.core.commands;
import co.aikar.commands.annotation.CommandCompletion;
import co.aikar.commands.annotation.CommandPermission;
import co.aikar.commands.annotation.Description;
import co.aikar.commands.annotation.Flags;
import co.aikar.commands.annotation.Optional;
import co.aikar.commands.annotation.Single;
import co.aikar.commands.annotation.Subcommand;
import co.aikar.commands.annotation.Syntax;
import jakarta.inject.Inject;
import org.bukkit.entity.SpawnCategory;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jvnet.hk2.annotations.Service;
import org.mvplugins.multiverse.core.command.MVCommandIssuer;
import org.mvplugins.multiverse.core.command.flag.ParsedCommandFlags;
import org.mvplugins.multiverse.core.command.flags.PageFilterFlags;
import org.mvplugins.multiverse.core.config.handle.PropertyModifyAction;
import org.mvplugins.multiverse.core.display.ContentDisplay;
import org.mvplugins.multiverse.core.display.filters.DefaultContentFilter;
import org.mvplugins.multiverse.core.display.handlers.PagedSendHandler;
import org.mvplugins.multiverse.core.display.parsers.ListContentProvider;
import org.mvplugins.multiverse.core.locale.message.Message;
import org.mvplugins.multiverse.core.utils.StringFormatter;
import org.mvplugins.multiverse.core.world.MultiverseWorld;
import org.mvplugins.multiverse.core.world.WorldManager;
import org.mvplugins.multiverse.core.world.entity.SpawnCategoryConfig;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@Service
@Subcommand("entity-spawn-config")
final class EntitySpawnConfigCommand extends CoreCommand {
private final WorldManager worldManager;
private final PageFilterFlags flags;
@Inject
EntitySpawnConfigCommand(@NotNull WorldManager worldManager, @NotNull PageFilterFlags flags) {
this.worldManager = worldManager;
this.flags = flags;
}
@Subcommand("info")
@CommandPermission("multiverse.core.entityspawnconfig.info")
@CommandCompletion("@mvworlds:scope=both|@flags:resolveUntil=arg1,groupName=" + PageFilterFlags.NAME + " @flags:groupName=" + PageFilterFlags.NAME)
@Syntax("[world] [--page <page>] [--filter <filter>]")
@Description("")
void onInfoCommand(
MVCommandIssuer issuer,
@Flags("resolve=issuerAware,maxArgForAware=0")
@Syntax("[world]")
MultiverseWorld world,
@Optional
@Syntax("[--page <page>] [--filter <filter>]")
String[] flagArray
) {
ParsedCommandFlags parsedFlags = flags.parse(flagArray);
ContentDisplay.create()
.addContent(ListContentProvider.forContent(getEntitySpawnConfigList(world)))
.withSendHandler(PagedSendHandler.create()
.withHeader(Message.of("==== [ Entity Spawn Config '" + world.getName() + "' ] ===="))
.withLinesPerPage(8)
.withTargetPage(parsedFlags.flagValue(flags.page, 1))
.withFilter(parsedFlags.flagValue(flags.filter, DefaultContentFilter.get())))
.send(issuer);
}
private List<String> getEntitySpawnConfigList(MultiverseWorld world) {
List<String> list = new ArrayList<>();
Arrays.stream(SpawnCategory.values()).forEach(spawnCategory -> {
list.add(spawnCategory.name() + ": ");
SpawnCategoryConfig spawnCategoryConfig = world.getEntitySpawnConfig().getSpawnCategoryConfig(spawnCategory);
list.add(" spawn: " + spawnCategoryConfig.isSpawn());
list.add(" tick-rate: " + spawnCategoryConfig.getTickRate());
list.add(" exceptions: " + StringFormatter.join(spawnCategoryConfig.getExceptions(), ", "));
});
return list;
}
@Subcommand("modify")
@CommandPermission("multiverse.core.entityspawnconfig.modify")
@CommandCompletion("@mvworlds:scope=both @spawncategories @propsmodifyaction @spawncategorypropsname @spawncategorypropsvalue")
@Syntax("[world] <spawn-category> <set|add|reset|remove> <property> [value]")
@Description("")
void onModifyCommand(
MVCommandIssuer issuer,
@Flags("resolve=issuerAware,maxArgForAware=4")
@Syntax("[world]")
MultiverseWorld world,
@Syntax("<spawn-category>")
SpawnCategory spawnCategory,
@Syntax("<set|add|reset|remove>")
PropertyModifyAction action,
@Syntax("<property>")
String property,
@Optional
@Single
@Syntax("[value]")
@Nullable String value
) {
world.getEntitySpawnConfig()
.getSpawnCategoryConfig(spawnCategory)
.getStringPropertyHandle()
.modifyPropertyString(property, value, action)
.andThenTry(worldManager::saveWorldsConfig)
.onSuccess(ignore -> issuer.sendInfo("Successfully set " + property + " to " + value
+ " for " + spawnCategory.name() + " in " + world.getName()))
.onFailure(e -> issuer.sendError("Unable to set " + property + " to " + value
+ " for " + spawnCategory.name() + " in " + world.getName() + ": " + e.getMessage()));
}
}
@@ -1,233 +1,81 @@
package org.mvplugins.multiverse.core.commands;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import co.aikar.commands.CommandIssuer;
import co.aikar.commands.MessageType;
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.Description;
import co.aikar.commands.annotation.Flags;
import co.aikar.commands.annotation.Optional;
import co.aikar.commands.annotation.Subcommand;
import co.aikar.commands.annotation.Syntax;
import io.vavr.control.Option;
import jakarta.inject.Inject;
import org.bukkit.ChatColor;
import org.bukkit.GameRule;
import org.bukkit.World;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jvnet.hk2.annotations.Service;
import org.mvplugins.multiverse.core.command.LegacyAliasCommand;
import org.mvplugins.multiverse.core.command.MVCommandIssuer;
import org.mvplugins.multiverse.core.command.MVCommandManager;
import org.mvplugins.multiverse.core.command.context.GameRuleValue;
import org.mvplugins.multiverse.core.command.flag.ParsedCommandFlags;
import org.mvplugins.multiverse.core.command.flags.PageFilterFlags;
import org.mvplugins.multiverse.core.display.ContentDisplay;
import org.mvplugins.multiverse.core.display.filters.DefaultContentFilter;
import org.mvplugins.multiverse.core.display.handlers.PagedSendHandler;
import org.mvplugins.multiverse.core.display.parsers.MapContentProvider;
import org.mvplugins.multiverse.core.locale.MVCorei18n;
import org.mvplugins.multiverse.core.locale.message.Message;
import org.mvplugins.multiverse.core.locale.message.MessageReplacement.Replace;
import org.mvplugins.multiverse.core.commandtools.MVCommandManager;
import org.mvplugins.multiverse.core.commandtools.MultiverseCommand;
import org.mvplugins.multiverse.core.commandtools.context.GameRuleValue;
import org.mvplugins.multiverse.core.utils.MVCorei18n;
import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld;
import static org.mvplugins.multiverse.core.locale.message.MessageReplacement.replace;
@Service
class GameruleCommand extends CoreCommand {
private final PageFilterFlags flags;
@CommandAlias("mv")
class GameruleCommand extends MultiverseCommand {
@Inject
GameruleCommand(@NotNull PageFilterFlags flags) {
this.flags = flags;
GameruleCommand(@NotNull MVCommandManager commandManager) {
super(commandManager);
}
@Subcommand("gamerule|rule set")
@Subcommand("gamerule")
@CommandPermission("multiverse.core.gamerule.set")
@CommandCompletion("@gamerules @gamerulesvalues @mvworlds:multiple|*")
@CommandCompletion("@gamerules true|false|@range:1-10 @mvworlds:multiple|*")
@Syntax("<Gamerule> <Gamerule value> [World or *]")
@Description("{@@mv-core.gamerule.set.description}")
void onGameruleSetCommand(
MVCommandIssuer issuer,
@Description("{@@mv-core.gamerule.description}")
void onGameruleCommand(
BukkitCommandIssuer issuer,
@Syntax("<Gamerule>")
@Description("{@@mv-core.gamerule.set.gamerule.description}")
@Description("{@@mv-core.gamerule.gamerule.description}")
GameRule gamerule,
@Syntax("<Value>")
@Description("{@@mv-core.gamerule.set.value.description}")
@Description("{@@mv-core.gamerule.value.description}")
GameRuleValue gameRuleValue,
@Flags("resolve=issuerAware,maxArgForAware=2")
@Flags("resolve=issuerAware")
@Syntax("[World or *]")
@Description("{@@mv-core.gamerule.set.world.description}")
@Description("{@@mv-core.gamerule.world.description}")
LoadedMultiverseWorld[] worlds) {
Object value = gameRuleValue.value();
Object value = gameRuleValue.getValue();
boolean success = true;
for (LoadedMultiverseWorld world : worlds) {
// Set gamerules and add false to list if it fails
World bukkitWorld = world.getBukkitWorld().getOrNull();
if (bukkitWorld == null || !bukkitWorld.setGameRule(gamerule, value)) {
issuer.sendError(MVCorei18n.GAMERULE_SET_FAILED,
Replace.GAMERULE.with(gamerule.getName()),
Replace.VALUE.with(value.toString()),
Replace.WORLD.with(world.getName()),
replace("{type}").with(gamerule.getType().getName()));
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.sendInfo(MVCorei18n.GAMERULE_SET_SUCCESS_SINGLE,
Replace.GAMERULE.with(gamerule.getName()),
Replace.VALUE.with(value.toString()),
Replace.WORLD.with(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.sendInfo(MVCorei18n.GAMERULE_SET_SUCCESS_MULTIPLE,
Replace.GAMERULE.with(gamerule.getName()),
Replace.VALUE.with(value.toString()),
Replace.COUNT.with(String.valueOf(worlds.length)));
issuer.sendInfo(MVCorei18n.GAMERULE_SUCCESS_MULTIPLE,
"{gamerule}", gamerule.getName(),
"{value}", value.toString(),
"{count}", String.valueOf(worlds.length));
}
}
}
@SuppressWarnings("rawtypes,unchecked")
@Subcommand("gamerule|rule reset")
@CommandPermission("multiverse.core.gamerule.set")
@CommandCompletion("@gamerules @mvworlds:multiple|*")
@Syntax("<Gamerule> [World or *]")
@Description("{@@mv-core.gamerule.reset.description}")
void onGameruleResetCommand(
MVCommandIssuer issuer,
@Syntax("<Gamerule>")
@Description("{@@mv-core.gamerule.reset.gamerule.description}")
GameRule gamerule,
@Flags("resolve=issuerAware,maxArgForAware=1")
@Syntax("[World or *]")
@Description("{@@mv-core.gamerule.reset.world.description}")
LoadedMultiverseWorld[] worlds) {
AtomicBoolean success = new AtomicBoolean(true);
Arrays.stream(worlds)
.forEach(world -> world.getBukkitWorld()
.flatMap(bukkitWorld -> Option.of(bukkitWorld.getGameRuleDefault(gamerule))
.map(value -> bukkitWorld.setGameRule(gamerule, value)))
.onEmpty(() -> {
success.set(false);
issuer.sendError(MVCorei18n.GAMERULE_RESET_FAILED,
Replace.GAMERULE.with(gamerule.getName()),
Replace.WORLD.with(world.getName()));
}));
// Tell user if it was successful
if (success.get()) {
if (worlds.length == 1) {
issuer.sendInfo(MVCorei18n.GAMERULE_RESET_SUCCESS_SINGLE,
Replace.GAMERULE.with(gamerule.getName()),
Replace.WORLD.with(worlds[0].getName()));
} else if (worlds.length > 1) {
issuer.sendInfo(MVCorei18n.GAMERULE_RESET_SUCCESS_MULTIPLE,
Replace.GAMERULE.with(gamerule.getName()),
Replace.COUNT.with(String.valueOf(worlds.length)));
}
}
}
@Subcommand("gamerule|rule list")
@CommandPermission("multiverse.core.gamerule.list")
@CommandCompletion("@mvworlds|@flags:groupName=" + PageFilterFlags.NAME + " @flags:groupName=" + PageFilterFlags.NAME)
@Syntax("[world] [--page <page>] [--filter <filter>]")
@Description("{@@mv-core.gamerule.list.description}")
void onGameruleListCommand(
MVCommandIssuer issuer,
@Flags("resolve=issuerAware,maxArgForAware=0")
@Syntax("<world>")
@Description("{@@mv-core.gamerule.list.description.world}")
LoadedMultiverseWorld world,
@Optional
@Syntax("[--page <page>] [--filter <filter>]")
@Description("{@@mv-core.gamerule.list.description.page}")
String[] flagArray) {
ParsedCommandFlags parsedFlags = flags.parse(flagArray);
ContentDisplay.create()
.addContent(MapContentProvider.forContent(getGameRuleMap(world.getBukkitWorld().getOrNull()))
.withKeyColor(ChatColor.AQUA)
.withValueColor(ChatColor.WHITE))
.withSendHandler(PagedSendHandler.create()
.withHeader(this.getListTitle(world.getBukkitWorld().getOrNull()))
.doPagination(true)
.withTargetPage(parsedFlags.flagValue(flags.page, 1))
.withFilter(parsedFlags.flagValue(flags.filter, DefaultContentFilter.get())))
.send(issuer);
}
/**
* Gets all the gamerules and their values for a given world.
*
* @param world The world to find gamerules for.
* @return A map of the gamerules and their values
*/
private Map<String, String> getGameRuleMap(World world) {
Map<String, String> gameRuleMap = new HashMap<>();
if (world == null) {
return gameRuleMap;
}
for (String gamerule : world.getGameRules()) {
GameRule<?> gameruleEnum = GameRule.getByName(gamerule);
if (gameruleEnum == null) {
continue;
}
Object gameruleValue = world.getGameRuleValue(gameruleEnum);
if (gameruleValue == null) {
gameRuleMap.put(gameruleEnum.getName(), "null");
continue;
}
gameRuleMap.put(gameruleEnum.getName(), gameruleValue.toString());
}
return gameRuleMap;
}
private Message getListTitle(@Nullable World world) {
return Message.of(MVCorei18n.GAMERULE_LIST_TITLE, Replace.WORLD.with(world == null ? "null" : world.getName()));
}
@Service
private static final class LegacyAlias extends GameruleCommand implements LegacyAliasCommand {
@Inject
LegacyAlias(@NotNull PageFilterFlags flags) {
super(flags);
}
@Override
@CommandAlias("mvrule|mvgamerule")
void onGameruleSetCommand(MVCommandIssuer issuer, GameRule gamerule, GameRuleValue gameRuleValue, LoadedMultiverseWorld[] worlds) {
super.onGameruleSetCommand(issuer, gamerule, gameRuleValue, worlds);
}
@Override
void onGameruleResetCommand(MVCommandIssuer issuer, GameRule gamerule, LoadedMultiverseWorld[] worlds) {
super.onGameruleResetCommand(issuer, gamerule, worlds);
}
@Override
@CommandAlias("mvrules|mvgamerules")
void onGameruleListCommand(MVCommandIssuer issuer, LoadedMultiverseWorld world, String[] flags) {
super.onGameruleListCommand(issuer, world, flags);
}
}
}
@@ -0,0 +1,132 @@
package org.mvplugins.multiverse.core.commands;
import java.util.HashMap;
import java.util.Map;
import co.aikar.commands.CommandIssuer;
import co.aikar.commands.InvalidCommandArgument;
import co.aikar.commands.MessageType;
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.Flags;
import co.aikar.commands.annotation.Optional;
import co.aikar.commands.annotation.Subcommand;
import co.aikar.commands.annotation.Syntax;
import jakarta.inject.Inject;
import org.bukkit.ChatColor;
import org.bukkit.GameRule;
import org.bukkit.World;
import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Service;
import org.mvplugins.multiverse.core.commandtools.MVCommandIssuer;
import org.mvplugins.multiverse.core.commandtools.MVCommandManager;
import org.mvplugins.multiverse.core.commandtools.MultiverseCommand;
import org.mvplugins.multiverse.core.commandtools.flags.CommandValueFlag;
import org.mvplugins.multiverse.core.commandtools.flags.ParsedCommandFlags;
import org.mvplugins.multiverse.core.display.ContentDisplay;
import org.mvplugins.multiverse.core.display.filters.ContentFilter;
import org.mvplugins.multiverse.core.display.filters.DefaultContentFilter;
import org.mvplugins.multiverse.core.display.filters.RegexContentFilter;
import org.mvplugins.multiverse.core.display.handlers.PagedSendHandler;
import org.mvplugins.multiverse.core.display.parsers.MapContentProvider;
import org.mvplugins.multiverse.core.utils.MVCorei18n;
import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld;
/**
* List all gamerules in your current or specified world.
*/
@Service
@CommandAlias("mv")
class GamerulesCommand extends MultiverseCommand {
private final CommandValueFlag<Integer> PAGE_FLAG = flag(CommandValueFlag
.builder("--page", Integer.class)
.addAlias("-p")
.context(value -> {
try {
return Integer.parseInt(value);
} catch (NumberFormatException e) {
throw new InvalidCommandArgument("Invalid page number: " + value);
}
})
.build());
private final CommandValueFlag<ContentFilter> FILTER_FLAG = flag(CommandValueFlag
.builder("--filter", ContentFilter.class)
.addAlias("-f")
.context(value -> {
try {
return RegexContentFilter.fromString(value);
} catch (IllegalArgumentException e) {
throw new InvalidCommandArgument("Invalid filter: " + value);
}
})
.build());
@Inject
GamerulesCommand(@NotNull MVCommandManager commandManager) {
super(commandManager);
}
@Subcommand("gamerules|rules")
@CommandPermission("multiverse.core.gamerule.list")
@CommandCompletion("@mvworlds|@flags:groupName=mvgamerulescommand @flags:groupName=mvgamerulescommand")
@Syntax("[world] [--page <page>] [--filter <filter>]")
@Description("{@@mv-core.gamerules.description}")
void onGamerulesCommand(
@NotNull MVCommandIssuer issuer,
@Flags("resolve=issuerAware")
@Syntax("<world>")
@Description("{@@mv-core.gamerules.description.world}")
LoadedMultiverseWorld world,
@Optional
@Syntax("[--page <page>] [--filter <filter>]")
@Description("{@@mv-core.gamerules.description.page}")
String[] flags) {
ParsedCommandFlags parsedFlags = parseFlags(flags);
ContentDisplay.create()
.addContent(MapContentProvider.forContent(getGameRuleMap(world.getBukkitWorld().getOrNull())) // TODO: Handle null
.withKeyColor(ChatColor.AQUA)
.withValueColor(ChatColor.WHITE))
.withSendHandler(PagedSendHandler.create()
.withHeader(this.getTitle(issuer, world.getBukkitWorld().getOrNull()))
.doPagination(true)
.withTargetPage(parsedFlags.flagValue(PAGE_FLAG, 1))
.withFilter(parsedFlags.flagValue(FILTER_FLAG, DefaultContentFilter.get())))
.send(issuer);
}
/**
* Gets all the gamerules and their values for a given world.
*
* @param world The world to find gamerules for.
* @return A map of the gamerules and their values
*/
private Map<String, String> getGameRuleMap(World world) {
Map<String, String> gameRuleMap = new HashMap<>();
for (GameRule<?> gamerule : GameRule.values()) {
Object gameruleValue = world.getGameRuleValue(gamerule);
if (gameruleValue == null) {
gameRuleMap.put(gamerule.getName(), "null");
continue;
}
gameRuleMap.put(gamerule.getName(), gameruleValue.toString());
}
return gameRuleMap;
}
private String getTitle(CommandIssuer issuer, World world) {
return this.commandManager.formatMessage(
issuer,
MessageType.INFO,
MVCorei18n.GAMERULES_TITLE,
"{world}", world.getName());
}
}
@@ -1,107 +0,0 @@
package org.mvplugins.multiverse.core.commands;
import java.util.List;
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.Subcommand;
import co.aikar.commands.annotation.Syntax;
import jakarta.inject.Inject;
import org.bukkit.ChatColor;
import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Service;
import org.mvplugins.multiverse.core.command.LegacyAliasCommand;
import org.mvplugins.multiverse.core.command.MVCommandIssuer;
import org.mvplugins.multiverse.core.command.flag.ParsedCommandFlags;
import org.mvplugins.multiverse.core.command.flags.PageFilterFlags;
import org.mvplugins.multiverse.core.display.ContentDisplay;
import org.mvplugins.multiverse.core.display.filters.DefaultContentFilter;
import org.mvplugins.multiverse.core.display.handlers.PagedSendHandler;
import org.mvplugins.multiverse.core.display.parsers.ListContentProvider;
import org.mvplugins.multiverse.core.locale.MVCorei18n;
import org.mvplugins.multiverse.core.utils.StringFormatter;
import org.mvplugins.multiverse.core.world.generators.GeneratorPlugin;
import org.mvplugins.multiverse.core.world.generators.GeneratorProvider;
/**
* List all gamerules in your current or specified world.
*/
@Service
class GeneratorsCommand extends CoreCommand {
private final GeneratorProvider generatorProvider;
private final PageFilterFlags flags;
@Inject
GeneratorsCommand(@NotNull GeneratorProvider generatorProvider, @NotNull PageFilterFlags flags) {
this.generatorProvider = generatorProvider;
this.flags = flags;
}
@Subcommand("generators list")
@CommandPermission("multiverse.core.generator")
@CommandCompletion("@flags:groupName=" + PageFilterFlags.NAME)
@Syntax("[--page <page>] [--filter <filter>]")
@Description("{@@mv-core.generators.description}")
void onGeneratorsListCommand(
@NotNull MVCommandIssuer issuer,
@Optional
@Syntax("[--page <page>] [--filter <filter>]")
@Description("{@@mv-core.generators.description.flags}")
String[] flagArray) {
ParsedCommandFlags parsedFlags = flags.parse(flagArray);
// Get the generators loaded using the command suggestions
List<String> generators = (List<String>) generatorProvider.suggestGeneratorString("");
// Tell the user if we cannot find any generator plugins, then abort
if (generators.isEmpty()) {
issuer.sendMessage(MVCorei18n.GENERATORS_EMPTY);
return;
}
ContentDisplay.create()
.addContent(ListContentProvider.forContent(generators))
.withSendHandler(PagedSendHandler.create()
.withHeader("%s====[ Multiverse Generator List ]====", ChatColor.AQUA)
.doPagination(true)
.withTargetPage(parsedFlags.flagValue(flags.page, 1))
.withFilter(parsedFlags.flagValue(flags.filter, DefaultContentFilter.get())))
.send(issuer);
}
@Subcommand("generators info")
@CommandPermission("multiverse.core.generator")
@CommandCompletion("@generatorplugins")
@Syntax("<generator>")
void onGeneratorsInfoCommand(
@NotNull MVCommandIssuer issuer,
@Syntax("<generator>")
GeneratorPlugin generatorPlugin
) {
issuer.sendMessage(ChatColor.RESET + "Generator Plugin: " + generatorPlugin.getPluginName());
issuer.sendMessage(ChatColor.RESET + "Example usages: ");
issuer.sendMessage(ChatColor.RESET + StringFormatter.join(generatorPlugin.getExampleUsages(), "\n"));
issuer.sendMessage(ChatColor.RESET + "Link to more info: " + generatorPlugin.getInfoLink());
}
@Service
private static final class LegacyAlias extends GeneratorsCommand implements LegacyAliasCommand {
@Inject
LegacyAlias(@NotNull GeneratorProvider generatorProvider, @NotNull PageFilterFlags flags) {
super(generatorProvider, flags);
}
@Override
@CommandAlias("mvgenerators|mvgens")
void onGeneratorsListCommand(@NotNull MVCommandIssuer issuer, String[] flags) {
super.onGeneratorsListCommand(issuer, flags);
}
}
}
@@ -14,37 +14,49 @@ import org.bukkit.World;
import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Service;
import org.mvplugins.multiverse.core.command.LegacyAliasCommand;
import org.mvplugins.multiverse.core.command.MVCommandIssuer;
import org.mvplugins.multiverse.core.command.MVCommandManager;
import org.mvplugins.multiverse.core.command.flag.CommandFlag;
import org.mvplugins.multiverse.core.command.flag.CommandFlagsManager;
import org.mvplugins.multiverse.core.command.flag.CommandValueFlag;
import org.mvplugins.multiverse.core.command.flag.FlagBuilder;
import org.mvplugins.multiverse.core.command.flag.ParsedCommandFlags;
import org.mvplugins.multiverse.core.locale.MVCorei18n;
import org.mvplugins.multiverse.core.locale.message.MessageReplacement.Replace;
import org.mvplugins.multiverse.core.commandtools.MVCommandIssuer;
import org.mvplugins.multiverse.core.commandtools.MVCommandManager;
import org.mvplugins.multiverse.core.commandtools.MultiverseCommand;
import org.mvplugins.multiverse.core.commandtools.flags.CommandFlag;
import org.mvplugins.multiverse.core.commandtools.flags.CommandValueFlag;
import org.mvplugins.multiverse.core.commandtools.flags.ParsedCommandFlags;
import org.mvplugins.multiverse.core.utils.MVCorei18n;
import org.mvplugins.multiverse.core.world.WorldManager;
import org.mvplugins.multiverse.core.world.generators.GeneratorProvider;
import org.mvplugins.multiverse.core.world.options.ImportWorldOptions;
@Service
class ImportCommand extends CoreCommand {
@CommandAlias("mv")
class ImportCommand extends MultiverseCommand {
private final WorldManager worldManager;
private final ImportCommand.Flags flags;
private GeneratorProvider generatorProvider;
private final CommandValueFlag<String> GENERATOR_FLAG = flag(CommandValueFlag
.builder("--generator", String.class)
.addAlias("-g")
.completion(input -> generatorProvider.suggestGeneratorString(input))
.build());
private final CommandFlag NO_ADJUST_SPAWN_FLAG = flag(CommandFlag.builder("--no-adjust-spawn")
.addAlias("-n")
.build());
@Inject
ImportCommand(@NotNull WorldManager worldManager, @NotNull Flags flags) {
ImportCommand(
@NotNull MVCommandManager commandManager,
@NotNull WorldManager worldManager,
@NotNull GeneratorProvider generatorProvider) {
super(commandManager);
this.worldManager = worldManager;
this.flags = flags;
this.generatorProvider = generatorProvider;
}
@Subcommand("import")
@CommandPermission("multiverse.core.import")
@CommandCompletion("@mvworlds:scope=potential @environments @flags:groupName=" + Flags.NAME)
@Syntax("<name> <environment> [--generator <generator[:id]> --adjust-spawn --biome <biome>]")
@Description("{@@mv-core.import.description}")
@CommandCompletion("@mvworlds:scope=potential @flags:groupName=mvimportcommand")
@Syntax("<name> <env> --generator [generator[:id]] --adjust-spawn")
@Description("{@@mv-core.import.description")
void onImportCommand(
MVCommandIssuer issuer,
@@ -58,71 +70,23 @@ class ImportCommand extends CoreCommand {
World.Environment environment,
@Optional
@Syntax("[--generator <generator[:id]> --adjust-spawn --biome <biome>]")
@Syntax("--generator [generator[:id]] --adjust-spawn")
@Description("{@@mv-core.import.other.description}")
String[] flagArray) {
ParsedCommandFlags parsedFlags = flags.parse(flagArray);
String[] flags) {
ParsedCommandFlags parsedFlags = parseFlags(flags);
issuer.sendInfo(MVCorei18n.IMPORT_IMPORTING, Replace.WORLD.with(worldName));
issuer.sendInfo(MVCorei18n.IMPORT_IMPORTING, "{world}", worldName);
worldManager.importWorld(ImportWorldOptions.worldName(worldName)
.biome(parsedFlags.flagValue(flags.biome, ""))
.environment(environment)
.generator(parsedFlags.flagValue(flags.generator, String.class))
.useSpawnAdjust(!parsedFlags.hasFlag(flags.noAdjustSpawn))
.doFolderCheck(!parsedFlags.hasFlag(flags.skipFolderCheck)))
.environment(environment)
.generator(parsedFlags.flagValue(GENERATOR_FLAG, String.class))
.useSpawnAdjust(!parsedFlags.hasFlag(NO_ADJUST_SPAWN_FLAG)))
.onSuccess(newWorld -> {
Logging.fine("World import success: " + newWorld);
issuer.sendInfo(MVCorei18n.IMPORT_SUCCESS, Replace.WORLD.with(newWorld.getName()));
issuer.sendInfo(MVCorei18n.IMPORT_SUCCESS, "{world}", newWorld.getName());
})
.onFailure(failure -> {
Logging.fine("World import failure: " + failure);
issuer.sendError(failure.getFailureMessage());
});
}
@Service
private static final class Flags extends FlagBuilder {
private static final String NAME = "mvimport";
private GeneratorProvider generatorProvider;
@Inject
private Flags(@NotNull CommandFlagsManager flagsManager, @NotNull GeneratorProvider generatorProvider) {
super(NAME, flagsManager);
this.generatorProvider = generatorProvider;
}
private final CommandValueFlag<String> generator = flag(CommandValueFlag
.builder("--generator", String.class)
.addAlias("-g")
.completion(input -> generatorProvider.suggestGeneratorString(input))
.build());
private final CommandFlag noAdjustSpawn = flag(CommandFlag.builder("--no-adjust-spawn")
.addAlias("-n")
.build());
private final CommandValueFlag<String> biome = flag(CommandValueFlag.builder("--biome", String.class)
.addAlias("-b")
.build());
private final CommandFlag skipFolderCheck = flag(CommandFlag.builder("--skip-folder-check")
.addAlias("-f")
.build());
}
@Service
private static final class LegacyAlias extends ImportCommand implements LegacyAliasCommand {
@Inject
LegacyAlias(@NotNull WorldManager worldManager, @NotNull Flags flags) {
super(worldManager, flags);
}
@Override
@CommandAlias("mvimport|mvim")
void onImportCommand(MVCommandIssuer issuer, String worldName, World.Environment environment, String[] flags) {
super.onImportCommand(issuer, worldName, environment, flags);
}
}
}
@@ -1,179 +0,0 @@
package org.mvplugins.multiverse.core.commands;
import java.util.LinkedHashMap;
import java.util.Map;
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.Flags;
import co.aikar.commands.annotation.Optional;
import co.aikar.commands.annotation.Subcommand;
import co.aikar.commands.annotation.Syntax;
import jakarta.inject.Inject;
import org.bukkit.ChatColor;
import org.bukkit.WorldType;
import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Service;
import org.mvplugins.multiverse.core.command.LegacyAliasCommand;
import org.mvplugins.multiverse.core.command.MVCommandIssuer;
import org.mvplugins.multiverse.core.command.flag.ParsedCommandFlags;
import org.mvplugins.multiverse.core.command.flags.PageFilterFlags;
import org.mvplugins.multiverse.core.display.ContentDisplay;
import org.mvplugins.multiverse.core.display.filters.DefaultContentFilter;
import org.mvplugins.multiverse.core.display.handlers.PagedSendHandler;
import org.mvplugins.multiverse.core.display.parsers.MapContentProvider;
import org.mvplugins.multiverse.core.economy.MVEconomist;
import org.mvplugins.multiverse.core.locale.MVCorei18n;
import org.mvplugins.multiverse.core.locale.message.Message;
import org.mvplugins.multiverse.core.teleportation.LocationManipulation;
import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld;
import org.mvplugins.multiverse.core.world.MultiverseWorld;
import static org.mvplugins.multiverse.core.locale.message.MessageReplacement.replace;
@Service
class InfoCommand extends CoreCommand {
private final LocationManipulation locationManipulation;
private final MVEconomist economist;
private final PageFilterFlags flags;
@Inject
InfoCommand(
@NotNull LocationManipulation locationManipulation,
@NotNull MVEconomist economist,
@NotNull PageFilterFlags flags
) {
this.locationManipulation = locationManipulation;
this.economist = economist;
this.flags = flags;
}
// TODO: support info for unloaded worlds
@Subcommand("info")
@CommandPermission("multiverse.core.info")
@CommandCompletion("@mvworlds:scope=both|@flags:resolveUntil=arg1,groupName=" + PageFilterFlags.NAME + " @flags:groupName=" + PageFilterFlags.NAME)
@Syntax("[world] [--page <page>] [--filter <filter>]")
@Description("{@@mv-core.info.description}")
public void onInfoCommand(
MVCommandIssuer issuer,
@Flags("resolve=issuerAware,maxArgForAware=0")
@Syntax("[world]")
@Description("{@@mv-core.info.description.world}")
LoadedMultiverseWorld world,
@Optional
@Syntax("[--page <page>] [--filter <filter>]")
String[] flagArray) {
ParsedCommandFlags parsedFlags = flags.parse(flagArray);
ContentDisplay.create()
.addContent(MapContentProvider.forContent(getInfo(world))
.withKeyColor(ChatColor.AQUA)
.withValueColor(ChatColor.WHITE))
.withSendHandler(PagedSendHandler.create()
.withHeader(Message.of(MVCorei18n.INFO_HEADER, replace("{world}").with(world.getName())))
.noContentMessage(Message.of(MVCorei18n.INFO_NOCONTENT))
.doPagination(true)
.withTargetPage(parsedFlags.flagValue(flags.page, 1))
.withFilter(parsedFlags.flagValue(flags.filter, DefaultContentFilter.get())))
.send(issuer);
}
private Map<String, String> getInfo(LoadedMultiverseWorld world) {
Map<String, String> outMap = new LinkedHashMap<>();
outMap.put("World Name", world.getName());
outMap.put("World Alias", world.getAlias());
outMap.put("World UID", world.getUID().toString());
outMap.put("Game Mode", world.getGameMode().toString());
outMap.put("Difficulty", world.getDifficulty().toString());
outMap.put("Spawn Location", locationManipulation.strCoords(world.getSpawnLocation()));
outMap.put("Respawn World", world.getRespawnWorldName());
outMap.put("Bed Respawn", String.valueOf(world.getBedRespawn()));
outMap.put("Anchor Respawn", String.valueOf(world.getAnchorRespawn()));
outMap.put("Seed", String.valueOf(world.getSeed()));
outMap.put("Environment", String.valueOf(world.getEnvironment()));
outMap.put("World Type", world.getWorldType().map(WorldType::getName).getOrNull());
outMap.put("Biome", world.getBiome());
outMap.put("Generator", world.getGenerator());
outMap.put("Generate Structures", world.canGenerateStructures().map(String::valueOf).getOrNull());
outMap.put("Auto Load", String.valueOf(world.isAutoLoad()));
outMap.put("Keep Spawn In Memory", String.valueOf(world.isKeepSpawnInMemory()));
getEntryFeeInfo(outMap, world);
outMap.put("World Scale", String.valueOf(world.getScale()));
outMap.put("Weather Enabled", String.valueOf(world.isAllowWeather()));
outMap.put("Allow Flight", String.valueOf(world.isAllowFlight()));
outMap.put("Hunger Depletes", String.valueOf(world.isHunger()));
outMap.put("Keep Spawn In Memory", String.valueOf(world.isKeepSpawnInMemory()));
outMap.put("PVP Enabled", String.valueOf(world.getPvp()));
outMap.put("Portal Form", String.valueOf(world.getPortalForm()));
outMap.put("Player Limit", String.valueOf(world.getPlayerLimit()));
// getAnimalSpawningInfo(outMap, world);
// getMonsterSpawningInfo(outMap, world);
outMap.put("World Blacklist", String.join(", ", world.getWorldBlacklist()));
return outMap;
}
private void getEntryFeeInfo(Map<String, String> outMap, MultiverseWorld world) {
double price = world.getPrice();
if (price == 0) {
outMap.put("Entry Fee", "FREE!");
} else if (price > 0) {
outMap.put("Entry Fee", economist.formatPrice(price, world.getCurrency()));
} else if (price < 0) {
outMap.put("Entry Reward", economist.formatPrice(-price, world.getCurrency()));
}
}
// private void getAnimalSpawningInfo(Map<String, String> outMap, MultiverseWorld world) {
// if (world.isSpawningAnimals()) {
// outMap.put("Spawning Animals", "ALL");
// } else {
// if (!world.getSpawningAnimalsExceptions().isEmpty()) {
// outMap.put("Spawning Animals", world.getSpawningAnimalsExceptions().toString());
// } else {
// outMap.put("Spawning Animals", "NONE");
// }
// }
// }
//
// private void getMonsterSpawningInfo(Map<String, String> outMap, MultiverseWorld world) {
// if (world.isSpawningMonsters()) {
// outMap.put("Spawning Monsters", "ALL");
// } else {
// if (!world.getSpawningMonstersExceptions().isEmpty()) {
// outMap.put("Spawning Monsters", world.getSpawningMonstersExceptions().toString());
// } else {
// outMap.put("Spawning Monsters", "NONE");
// }
// }
// }
@Service
private static final class LegacyAlias extends InfoCommand implements LegacyAliasCommand {
@Inject
LegacyAlias(
@NotNull LocationManipulation locationManipulation,
@NotNull MVEconomist economist,
@NotNull PageFilterFlags flags
) {
super(locationManipulation, economist, flags);
}
@Override
@CommandAlias("mvinfo|mvi")
public void onInfoCommand(MVCommandIssuer issuer, LoadedMultiverseWorld world, String[] flags) {
super.onInfoCommand(issuer, world, flags);
}
}
}
@@ -3,6 +3,7 @@ package org.mvplugins.multiverse.core.commands;
import java.util.ArrayList;
import java.util.List;
import co.aikar.commands.InvalidCommandArgument;
import co.aikar.commands.annotation.CommandAlias;
import co.aikar.commands.annotation.CommandCompletion;
import co.aikar.commands.annotation.CommandPermission;
@@ -15,107 +16,110 @@ import org.bukkit.World;
import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Service;
import org.mvplugins.multiverse.core.command.LegacyAliasCommand;
import org.mvplugins.multiverse.core.command.MVCommandIssuer;
import org.mvplugins.multiverse.core.command.MVCommandManager;
import org.mvplugins.multiverse.core.command.flag.CommandFlag;
import org.mvplugins.multiverse.core.command.flag.CommandFlagsManager;
import org.mvplugins.multiverse.core.command.flag.ParsedCommandFlags;
import org.mvplugins.multiverse.core.command.flags.PageFilterFlags;
import org.mvplugins.multiverse.core.commandtools.MVCommandIssuer;
import org.mvplugins.multiverse.core.commandtools.MVCommandManager;
import org.mvplugins.multiverse.core.commandtools.MultiverseCommand;
import org.mvplugins.multiverse.core.commandtools.flags.CommandValueFlag;
import org.mvplugins.multiverse.core.commandtools.flags.ParsedCommandFlags;
import org.mvplugins.multiverse.core.display.ContentDisplay;
import org.mvplugins.multiverse.core.display.filters.ContentFilter;
import org.mvplugins.multiverse.core.display.filters.DefaultContentFilter;
import org.mvplugins.multiverse.core.display.filters.RegexContentFilter;
import org.mvplugins.multiverse.core.display.handlers.PagedSendHandler;
import org.mvplugins.multiverse.core.display.parsers.ListContentProvider;
import org.mvplugins.multiverse.core.locale.MVCorei18n;
import org.mvplugins.multiverse.core.locale.message.Message;
import org.mvplugins.multiverse.core.world.MultiverseWorld;
import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld;
import org.mvplugins.multiverse.core.world.WorldManager;
import org.mvplugins.multiverse.core.world.entrycheck.WorldEntryChecker;
import org.mvplugins.multiverse.core.world.entrycheck.WorldEntryCheckerProvider;
@Service
class ListCommand extends CoreCommand {
@CommandAlias("mv")
class ListCommand extends MultiverseCommand {
private final WorldManager worldManager;
private final WorldEntryCheckerProvider worldEntryCheckerProvider;
private final ListCommand.Flags flags;
private final CommandValueFlag<Integer> PAGE_FLAG = flag(CommandValueFlag
.builder("--page", Integer.class)
.addAlias("-p")
.context(value -> {
try {
return Integer.parseInt(value);
} catch (NumberFormatException e) {
throw new InvalidCommandArgument("Invalid page number: " + value);
}
})
.build());
private final CommandValueFlag<ContentFilter> FILTER_FLAG = flag(CommandValueFlag
.builder("--filter", ContentFilter.class)
.addAlias("-f")
.context(value -> {
try {
return RegexContentFilter.fromString(value);
} catch (IllegalArgumentException e) {
throw new InvalidCommandArgument("Invalid filter: " + value);
}
})
.build());
@Inject
ListCommand(
@NotNull MVCommandManager commandManager,
@NotNull WorldManager worldManager,
@NotNull WorldEntryCheckerProvider worldEntryCheckerProvider,
@NotNull Flags flags
) {
@NotNull WorldEntryCheckerProvider worldEntryCheckerProvider) {
super(commandManager);
this.worldManager = worldManager;
this.worldEntryCheckerProvider = worldEntryCheckerProvider;
this.flags = flags;
}
@Subcommand("list")
@CommandPermission("multiverse.core.list.worlds")
@CommandCompletion("@flags:groupName=" + Flags.NAME)
@Syntax("--filter [filter] --page [page] --raw")
@Description("{{@mv-core.list.description}}")
@CommandCompletion("@flags:groupName=mvlistcommand")
@Syntax("--filter [filter] --page [page]")
@Description("Displays a listing of all worlds that you can enter.")
public void onListCommand(
MVCommandIssuer issuer,
@Syntax("[--filter <filter>] [--page <page>]")
@Description("Filters the list of worlds by the given regex and displays the given page.")
String[] flagArray) {
ParsedCommandFlags parsedFlags = flags.parse(flagArray);
String[] flags) {
ParsedCommandFlags parsedFlags = parseFlags(flags);
ContentDisplay.create()
.addContent(ListContentProvider.forContent(getListContents(issuer, parsedFlags.hasFlag(flags.raw))))
.addContent(ListContentProvider.forContent(getListContents(issuer)))
.withSendHandler(PagedSendHandler.create()
.noContentMessage(Message.of(MVCorei18n.LIST_NOCONTENT))
.withHeader(Message.of(MVCorei18n.LIST_HEADER))
.withTargetPage(parsedFlags.flagValue(flags.page, 1))
.withFilter(parsedFlags.flagValue(flags.filter, DefaultContentFilter.get())))
.withHeader("%s====[ Multiverse World List ]====", ChatColor.GOLD)
.withTargetPage(parsedFlags.flagValue(PAGE_FLAG, 1))
.withFilter(parsedFlags.flagValue(FILTER_FLAG, DefaultContentFilter.get())))
.send(issuer);
}
private List<String> getListContents(MVCommandIssuer issuer, boolean useRawNames) {
private List<String> getListContents(MVCommandIssuer issuer) {
List<String> worldList = new ArrayList<>();
WorldEntryChecker worldEntryChecker = worldEntryCheckerProvider.forSender(issuer.getIssuer());
worldManager.getLoadedWorlds().stream()
.filter(world -> worldEntryChecker.canAccessWorld(world).isSuccess())
.filter(world -> canSeeWorld(issuer, world))
.map(world -> hiddenText(world) + getWorldName(world, useRawNames) + " - "
+ parseColouredEnvironment(world.getEnvironment()))
.map(world -> hiddenText(world) + world.getAlias() + " - " + parseColouredEnvironment(world.getEnvironment()))
.sorted()
.forEach(worldList::add);
worldManager.getUnloadedWorlds().stream()
.filter(world -> worldEntryChecker.canAccessWorld(world).isSuccess())
.map(world -> ChatColor.GRAY + getWorldName(world, useRawNames) + " - UNLOADED")
.map(world -> ChatColor.GRAY + world.getAlias() + " - UNLOADED")
.sorted()
.forEach(worldList::add);
return worldList;
}
/**
* Gets a world's name or alias.
*
* @param world The world to retrieve the name of
* @param useRawNames True to return the name, false to return the alias
* @return The name
*/
private String getWorldName(MultiverseWorld world, boolean useRawNames) {
if (useRawNames) {
return world.getName();
}
return world.getAliasOrName();
}
private boolean canSeeWorld(MVCommandIssuer issuer, MultiverseWorld world) {
private boolean canSeeWorld(MVCommandIssuer issuer, LoadedMultiverseWorld world) {
return !world.isHidden()
// TODO: Refactor stray permission check
|| issuer.hasPermission("multiverse.core.modify");
|| issuer.hasPermission("multiverse.core.modify"); // TODO: Refactor stray permission check
}
private String hiddenText(MultiverseWorld world) {
private String hiddenText(LoadedMultiverseWorld world) {
return (world.isHidden()) ? String.format("%s[H] ", ChatColor.GRAY) : "";
}
@@ -128,37 +132,4 @@ class ListCommand extends CoreCommand {
};
return color + env.toString();
}
@Service
private static final class Flags extends PageFilterFlags {
private static final String NAME = "mvlist";
@Inject
private Flags(@NotNull CommandFlagsManager flagsManager) {
super(NAME, flagsManager);
}
private final CommandFlag raw = flag(CommandFlag.builder("--raw")
.addAlias("-r")
.build());
}
@Service
private static final class LegacyAlias extends ListCommand implements LegacyAliasCommand {
@Inject
LegacyAlias(
@NotNull WorldManager worldManager,
@NotNull WorldEntryCheckerProvider worldEntryCheckerProvider,
@NotNull Flags flags
) {
super(worldManager, worldEntryCheckerProvider, flags);
}
@Override
@CommandAlias("mvlist|mvl")
public void onListCommand(MVCommandIssuer issuer, String[] flags) {
super.onListCommand(issuer, flags);
}
}
}
@@ -3,8 +3,9 @@ package org.mvplugins.multiverse.core.commands;
import co.aikar.commands.annotation.CommandAlias;
import co.aikar.commands.annotation.CommandCompletion;
import co.aikar.commands.annotation.CommandPermission;
import co.aikar.commands.annotation.Conditions;
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.dumptruckman.minecraft.util.Logging;
@@ -12,28 +13,22 @@ import jakarta.inject.Inject;
import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Service;
import org.mvplugins.multiverse.core.command.LegacyAliasCommand;
import org.mvplugins.multiverse.core.command.MVCommandIssuer;
import org.mvplugins.multiverse.core.command.flag.CommandFlag;
import org.mvplugins.multiverse.core.command.flag.CommandFlagsManager;
import org.mvplugins.multiverse.core.command.flag.ParsedCommandFlags;
import org.mvplugins.multiverse.core.command.flags.RemovePlayerFlags;
import org.mvplugins.multiverse.core.locale.MVCorei18n;
import org.mvplugins.multiverse.core.locale.message.MessageReplacement.Replace;
import org.mvplugins.multiverse.core.world.MultiverseWorld;
import org.mvplugins.multiverse.core.commandtools.MVCommandIssuer;
import org.mvplugins.multiverse.core.commandtools.MVCommandManager;
import org.mvplugins.multiverse.core.commandtools.MultiverseCommand;
import org.mvplugins.multiverse.core.utils.MVCorei18n;
import org.mvplugins.multiverse.core.world.WorldManager;
import org.mvplugins.multiverse.core.world.options.LoadWorldOptions;
@Service
class LoadCommand extends CoreCommand {
@CommandAlias("mv")
class LoadCommand extends MultiverseCommand {
private final WorldManager worldManager;
private final LoadCommand.Flags flags;
@Inject
LoadCommand(@NotNull WorldManager worldManager, @NotNull Flags flags) {
LoadCommand(@NotNull MVCommandManager commandManager, @NotNull WorldManager worldManager) {
super(commandManager);
this.worldManager = worldManager;
this.flags = flags;
}
@Subcommand("load")
@@ -44,54 +39,19 @@ class LoadCommand extends CoreCommand {
void onLoadCommand(
MVCommandIssuer issuer,
@Single
@Conditions("worldname:scope=unloaded")
@Syntax("<world>")
@Description("{@@mv-core.load.world.description}")
MultiverseWorld world,
@Optional
@Syntax("[--remove-players]")
@Description("")
String[] flagArray) {
ParsedCommandFlags parsedFlags = flags.parse(flagArray);
issuer.sendInfo(MVCorei18n.LOAD_LOADING, Replace.WORLD.with(world.getName()));
worldManager.loadWorld(LoadWorldOptions.world(world)
.doFolderCheck(!parsedFlags.hasFlag(flags.skipFolderCheck)))
String worldName) {
issuer.sendInfo(MVCorei18n.LOAD_LOADING, "{world}", worldName);
worldManager.loadWorld(worldName)
.onSuccess(newWorld -> {
Logging.fine("World load success: " + newWorld);
issuer.sendInfo(MVCorei18n.LOAD_SUCCESS, Replace.WORLD.with(newWorld.getName()));
issuer.sendInfo(MVCorei18n.LOAD_SUCCESS, "{world}", newWorld.getName());
}).onFailure(failure -> {
Logging.fine("World load failure: " + failure);
issuer.sendError(failure.getFailureMessage());
});
}
@Service
private static final class Flags extends RemovePlayerFlags {
private static final String NAME = "mvunload";
@Inject
private Flags(@NotNull CommandFlagsManager flagsManager) {
super(NAME, flagsManager);
}
private final CommandFlag skipFolderCheck = flag(CommandFlag.builder("--skip-folder-check")
.addAlias("-f")
.build());
}
@Service
private static final class LegacyAlias extends LoadCommand implements LegacyAliasCommand {
@Inject
LegacyAlias(@NotNull WorldManager worldManager, @NotNull Flags flags) {
super(worldManager, flags);
}
@Override
@CommandAlias("mvload")
void onLoadCommand(MVCommandIssuer issuer, MultiverseWorld world, String[] flagArray) {
super.onLoadCommand(issuer, world, flagArray);
}
}
}
@@ -5,131 +5,72 @@ import co.aikar.commands.annotation.CommandCompletion;
import co.aikar.commands.annotation.CommandPermission;
import co.aikar.commands.annotation.Description;
import co.aikar.commands.annotation.Flags;
import co.aikar.commands.annotation.Optional;
import co.aikar.commands.annotation.Single;
import co.aikar.commands.annotation.Subcommand;
import co.aikar.commands.annotation.Syntax;
import com.dumptruckman.minecraft.util.Logging;
import jakarta.inject.Inject;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jvnet.hk2.annotations.Optional;
import org.jvnet.hk2.annotations.Service;
import org.mvplugins.multiverse.core.command.LegacyAliasCommand;
import org.mvplugins.multiverse.core.command.MVCommandIssuer;
import org.mvplugins.multiverse.core.command.context.issueraware.MultiverseWorldValue;
import org.mvplugins.multiverse.core.config.handle.PropertyModifyAction;
import org.mvplugins.multiverse.core.config.handle.StringPropertyHandle;
import org.mvplugins.multiverse.core.locale.MVCorei18n;
import org.mvplugins.multiverse.core.commandtools.MVCommandIssuer;
import org.mvplugins.multiverse.core.commandtools.MVCommandManager;
import org.mvplugins.multiverse.core.commandtools.MultiverseCommand;
import org.mvplugins.multiverse.core.configuration.handle.ConfigModifyType;
import org.mvplugins.multiverse.core.world.MultiverseWorld;
import org.mvplugins.multiverse.core.world.WorldManager;
import java.util.Map;
import static org.mvplugins.multiverse.core.locale.message.MessageReplacement.*;
import static org.mvplugins.multiverse.core.locale.message.MessageReplacement.replace;
@Service
class ModifyCommand extends CoreCommand {
private static final Map<String, String> REMOVED_PROPERTIES = Map.of(
"animals", "/mv entity-spawn-config modify [world] animal set spawn <true|false>",
"monsters", "/mv entity-spawn-config modify [world] monster set spawn <true|false>"
);
@CommandAlias("mv")
class ModifyCommand extends MultiverseCommand {
private final WorldManager worldManager;
@Inject
ModifyCommand(WorldManager worldManager) {
ModifyCommand(@NotNull MVCommandManager commandManager, WorldManager worldManager) {
super(commandManager);
this.worldManager = worldManager;
}
/* /mv modify [world] <set|add|remove|reset> <property> <value> */
@Subcommand("modify")
@CommandPermission("multiverse.core.modify")
@CommandCompletion("@mvworlds:scope=both|@propsmodifyaction:byIssuerForArg=arg1 " +
"@propsmodifyaction:notByIssuerForArg=arg1|@mvworldpropsname:byIssuerForArg=arg1 " +
"@mvworldpropsname:notByIssuerForArg=arg1|@mvworldpropsvalue:byIssuerForArg=arg1 " +
"@mvworldpropsvalue:notByIssuerForArg=arg1")
@Syntax("[world] <set|add|remove|reset> <property> [value]")
@Description("{@@mv-core.modify.description}")
void onModifyCommand(// SUPPRESS CHECKSTYLE: ParameterNumber
@CommandCompletion("@mvworlds:scope=both @configmodifytype @mvworldpropsname @mvworldpropsvalue")
@Syntax("[world] <set|add|remove|reset> <property> <value>")
@Description("")
void onModifyCommand(
MVCommandIssuer issuer,
@Flags("resolve=issuerAware,maxArgForAware=3")
@Flags("resolve=issuerAware")
@Syntax("[world]")
@Description("{@@mv-core.modify.world.description}")
@NotNull MultiverseWorldValue worldValue,
@Description("")
MultiverseWorld world,
@Syntax("<set|add|remove|reset>")
@Description("")
@NotNull PropertyModifyAction action,
ConfigModifyType configModifyType,
@Syntax("<property>")
@Description("{@@mv-core.modify.property.description}")
@NotNull String propertyName,
@Description("")
String propertyName,
@Optional
@Single
@Syntax("[value]")
@Description("{@@mv-core.modify.value.description}")
@Nullable String propertyValue) {
if (REMOVED_PROPERTIES.containsKey(propertyName)) {
issuer.sendMessage(MVCorei18n.MODIFY_PROPERTYREMOVED,
replace("{property}").with(propertyName),
replace("{replacement}").with(REMOVED_PROPERTIES.get(propertyName)));
return;
}
@Description("")
String propertyValue) {
Logging.fine("ModifyCommand.onModifyCommand: world=%s, configModifyType=%s, propertyName=%s, propertyValue=%s",
world, configModifyType, propertyName, propertyValue);
MultiverseWorld world = worldValue.value();
if (action.isRequireValue() && propertyValue == null) {
issuer.sendMessage(MVCorei18n.MODIFY_SPECIFYVALUE,
replace("{action}").with(action.name().toLowerCase()),
replace("{property}").with(propertyName));
return;
} else if (!action.isRequireValue() && propertyValue != null) {
issuer.sendMessage(MVCorei18n.MODIFY_CANNOTHAVEVALUE,
replace("{action}").with(action.name().toLowerCase()),
replace("{property}").with(propertyName));
return;
}
StringPropertyHandle worldPropertyHandle = world.getStringPropertyHandle();
worldPropertyHandle.modifyPropertyString(issuer.getIssuer(), propertyName, propertyValue, action)
.andThenTry(worldManager::saveWorldsConfig)
.onSuccess(ignore -> issuer.sendMessage(MVCorei18n.MODIFY_SUCCESS,
replace("{action}").with(action.name().toLowerCase()),
replace("{property}").with(propertyName),
Replace.VALUE.with(worldPropertyHandle.getProperty(propertyName).getOrNull()),
Replace.WORLD.with(world.getName())))
.onFailure(exception -> {
if (propertyValue == null) {
issuer.sendMessage(MVCorei18n.MODIFY_FAILURE_NOVALUE,
replace("{action}").with(action.name().toLowerCase()),
replace("{property}").with(propertyName),
Replace.WORLD.with(world.getName()),
Replace.ERROR.with(exception.getMessage()));
} else {
issuer.sendMessage(MVCorei18n.MODIFY_FAILURE,
replace("{action}").with(action.name().toLowerCase()),
replace("{property}").with(propertyName),
Replace.VALUE.with(propertyValue),
Replace.WORLD.with(world.getName()),
Replace.ERROR.with(exception.getMessage()));
}
world.modifyProperty(configModifyType, propertyName, propertyValue).onSuccess(ignore -> {
issuer.sendMessage("Property " + propertyName + " set to " + world.getProperty(propertyName).getOrNull()
+ " for world " + world.getName() + ".");
worldManager.saveWorldsConfig();
}).onFailure(exception -> {
issuer.sendMessage("Failed to " + configModifyType.name().toLowerCase() + " property " + propertyName
+ " to " + propertyValue + " for world " + world.getName() + ".");
issuer.sendMessage(exception.getMessage());
});
}
@Service
private static final class LegacyAlias extends ModifyCommand implements LegacyAliasCommand {
@Inject
LegacyAlias(WorldManager worldManager) {
super(worldManager);
}
@Override
@CommandAlias("mvmodify|mvm")
void onModifyCommand(MVCommandIssuer issuer, @NotNull MultiverseWorldValue world, @NotNull PropertyModifyAction action, @NotNull String propertyName, @Nullable String propertyValue) {
super.onModifyCommand(issuer, world, action, propertyName, propertyValue);
}
}
}
@@ -1,54 +0,0 @@
package org.mvplugins.multiverse.core.commands;
import co.aikar.commands.annotation.CommandCompletion;
import co.aikar.commands.annotation.CommandPermission;
import co.aikar.commands.annotation.Flags;
import co.aikar.commands.annotation.Optional;
import co.aikar.commands.annotation.Subcommand;
import co.aikar.commands.annotation.Syntax;
import jakarta.inject.Inject;
import org.bukkit.entity.SpawnCategory;
import org.jvnet.hk2.annotations.Service;
import org.mvplugins.multiverse.core.command.MVCommandIssuer;
import org.mvplugins.multiverse.core.utils.StringFormatter;
import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld;
import org.mvplugins.multiverse.core.world.entity.EntityPurger;
import java.util.List;
@Service
final class PurgeAllEntitiesCommand extends CoreCommand {
private final EntityPurger entityPurger;
@Inject
PurgeAllEntitiesCommand(EntityPurger entityPurger) {
this.entityPurger = entityPurger;
}
@Subcommand("purge-all-entities")
@CommandPermission("multiverse.core.purgeall")
@CommandCompletion("@mvworlds:scope=loaded|@spawncategories:multiple,resolveUntil=arg1 @spawncategories:multiple")
@Syntax("[world] [spawn-categories]")
void onPurgeAllEntitiesCommand(
MVCommandIssuer issuer,
@Flags("resolve=issuerAware,maxArgForAware=1")
@Syntax("[world]")
LoadedMultiverseWorld world,
@Optional
@Syntax("[spawn-categories]")
SpawnCategory[] spawnCategories
) {
if (spawnCategories == null || spawnCategories.length == 0) {
int purgeCount = entityPurger.purgeAllEntities(world);
issuer.sendMessage("Successfully purged " + purgeCount + " entities in world " + world.getName() + ".");
return;
}
int purgeCount = entityPurger.purgeEntities(world, spawnCategories);
issuer.sendMessage("Successfully purged " + purgeCount + " entities in world " + world.getName() +
" for spawn categories " + StringFormatter.join(List.of(spawnCategories), ", ") + ".");
}
}
@@ -1,38 +0,0 @@
package org.mvplugins.multiverse.core.commands;
import co.aikar.commands.annotation.CommandCompletion;
import co.aikar.commands.annotation.CommandPermission;
import co.aikar.commands.annotation.Flags;
import co.aikar.commands.annotation.Subcommand;
import co.aikar.commands.annotation.Syntax;
import jakarta.inject.Inject;
import org.jvnet.hk2.annotations.Service;
import org.mvplugins.multiverse.core.command.MVCommandIssuer;
import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld;
import org.mvplugins.multiverse.core.world.entity.EntityPurger;
@Service
final class PurgeEntitiesCommand extends CoreCommand {
private final EntityPurger entityPurger;
@Inject
PurgeEntitiesCommand(EntityPurger entityPurger) {
this.entityPurger = entityPurger;
}
@Subcommand("purge-entities")
@CommandPermission("multiverse.core.purge")
@CommandCompletion("@mvworlds:scope=loaded")
@Syntax("[world]")
void onPurgeEntityCommand(
MVCommandIssuer issuer,
@Flags("resolve=issuerAware,maxArgForAware=0")
@Syntax("[world]")
LoadedMultiverseWorld world
) {
int purgeCount = entityPurger.purgeEntities(world);
issuer.sendMessage("Successfully purged " + purgeCount + " entities in world " + world.getName() + ".");
}
}
@@ -1,9 +1,9 @@
package org.mvplugins.multiverse.core.commands;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import co.aikar.commands.ACFUtil;
import co.aikar.commands.MessageType;
import co.aikar.commands.annotation.CommandAlias;
import co.aikar.commands.annotation.CommandCompletion;
import co.aikar.commands.annotation.CommandPermission;
@@ -13,59 +13,54 @@ import co.aikar.commands.annotation.Subcommand;
import co.aikar.commands.annotation.Syntax;
import com.dumptruckman.minecraft.util.Logging;
import jakarta.inject.Inject;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Service;
import org.mvplugins.multiverse.core.command.LegacyAliasCommand;
import org.mvplugins.multiverse.core.command.MVCommandIssuer;
import org.mvplugins.multiverse.core.command.flag.CommandFlag;
import org.mvplugins.multiverse.core.command.flag.CommandFlagsManager;
import org.mvplugins.multiverse.core.command.flag.CommandValueFlag;
import org.mvplugins.multiverse.core.command.flag.ParsedCommandFlags;
import org.mvplugins.multiverse.core.command.flags.RemovePlayerFlags;
import org.mvplugins.multiverse.core.command.queue.CommandQueueManager;
import org.mvplugins.multiverse.core.command.queue.CommandQueuePayload;
import org.mvplugins.multiverse.core.locale.MVCorei18n;
import org.mvplugins.multiverse.core.locale.message.Message;
import org.mvplugins.multiverse.core.locale.message.MessageReplacement.Replace;
import org.mvplugins.multiverse.core.utils.REPatterns;
import org.mvplugins.multiverse.core.utils.WorldTickDeferrer;
import org.mvplugins.multiverse.core.utils.result.AsyncAttemptsAggregate;
import org.mvplugins.multiverse.core.commandtools.MVCommandIssuer;
import org.mvplugins.multiverse.core.commandtools.MVCommandManager;
import org.mvplugins.multiverse.core.commandtools.MultiverseCommand;
import org.mvplugins.multiverse.core.commandtools.flags.CommandFlag;
import org.mvplugins.multiverse.core.commandtools.flags.CommandValueFlag;
import org.mvplugins.multiverse.core.commandtools.flags.ParsedCommandFlags;
import org.mvplugins.multiverse.core.commandtools.queue.QueuedCommand;
import org.mvplugins.multiverse.core.utils.MVCorei18n;
import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld;
import org.mvplugins.multiverse.core.world.WorldManager;
import org.mvplugins.multiverse.core.world.helpers.PlayerWorldTeleporter;
import org.mvplugins.multiverse.core.world.options.RegenWorldOptions;
@Service
class RegenCommand extends CoreCommand {
@CommandAlias("mv")
class RegenCommand extends MultiverseCommand {
@NotNull
private final CommandQueueManager commandQueueManager;
private final WorldManager worldManager;
private final PlayerWorldTeleporter playerWorldTeleporter;
private final WorldTickDeferrer worldTickDeferrer;
private final RegenCommand.Flags flags;
private final CommandValueFlag<String> SEED_FLAG = flag(CommandValueFlag.builder("--seed", String.class)
.addAlias("-s")
.completion(input -> Collections.singleton(String.valueOf(new Random().nextLong())))
.build());
private final CommandFlag RESET_WORLD_CONFIG_FLAG = flag(CommandFlag.builder("--reset-world-config")
.addAlias("-wc")
.build());
private final CommandFlag RESET_GAMERULES_FLAG = flag(CommandFlag.builder("--reset-gamerules")
.addAlias("-gm")
.build());
private final CommandFlag RESET_WORLD_BORDER_FLAG = flag(CommandFlag.builder("--reset-world-border")
.addAlias("-wb")
.build());
@Inject
RegenCommand(
@NotNull CommandQueueManager commandQueueManager,
@NotNull WorldManager worldManager,
@NotNull PlayerWorldTeleporter playerWorldTeleporter,
@NotNull WorldTickDeferrer worldTickDeferrer,
@NotNull Flags flags
) {
this.commandQueueManager = commandQueueManager;
RegenCommand(@NotNull MVCommandManager commandManager, @NotNull WorldManager worldManager) {
super(commandManager);
this.worldManager = worldManager;
this.playerWorldTeleporter = playerWorldTeleporter;
this.worldTickDeferrer = worldTickDeferrer;
this.flags = flags;
}
@Subcommand("regen")
@CommandPermission("multiverse.core.regen")
@CommandCompletion("@mvworlds:scope=loaded @flags:groupName=" + Flags.NAME)
@Syntax("<world> [--seed [seed] --reset-world-config --reset-gamerules --reset-world-border --remove-players]")
@CommandCompletion("@mvworlds:scope=loaded @flags:groupName=mvregencommand")
@Syntax("<world> --seed [seed] --keep-gamerules")
@Description("{@@mv-core.regen.description}")
void onRegenCommand(
MVCommandIssuer issuer,
@@ -75,111 +70,30 @@ class RegenCommand extends CoreCommand {
LoadedMultiverseWorld world,
@Optional
@Syntax("[--seed [seed] --reset-world-config --reset-gamerules --reset-world-border --remove-players]")
@Syntax("--seed [seed] --reset-gamerules")
@Description("{@@mv-core.regen.other.description}")
String[] flagArray) {
ParsedCommandFlags parsedFlags = flags.parse(flagArray);
String[] flags) {
ParsedCommandFlags parsedFlags = parseFlags(flags);
commandQueueManager.addToQueue(CommandQueuePayload
.issuer(issuer)
.action(() -> runRegenCommand(issuer, world, parsedFlags))
.prompt(Message.of(MVCorei18n.REGEN_PROMPT, "",
Replace.WORLD.with(world.getName()))));
}
private void runRegenCommand(MVCommandIssuer issuer, LoadedMultiverseWorld world, ParsedCommandFlags parsedFlags) {
issuer.sendInfo(MVCorei18n.REGEN_REGENERATING, Replace.WORLD.with(world.getName()));
List<Player> worldPlayers = world.getPlayers().getOrElse(Collections.emptyList());
var future = parsedFlags.hasFlag(flags.removePlayers)
? playerWorldTeleporter.removeFromWorld(world)
: AsyncAttemptsAggregate.emptySuccess();
// todo: using future will hide stacktrace
future.onSuccess(() -> worldTickDeferrer
.deferWorldTick(() -> doWorldRegening(issuer, world, parsedFlags, worldPlayers)))
.onFailure(() -> issuer.sendError("Failed to teleport one or more players out of the world!"));
}
private void doWorldRegening(
MVCommandIssuer issuer,
LoadedMultiverseWorld world,
ParsedCommandFlags parsedFlags,
List<Player> worldPlayers) {
//todo: Change biome on regen
RegenWorldOptions regenWorldOptions = RegenWorldOptions.world(world)
.randomSeed(parsedFlags.hasFlag(flags.seed))
.seed(parsedFlags.flagValue(flags.seed))
.keepWorldConfig(!parsedFlags.hasFlag(flags.resetWorldConfig))
.keepGameRule(!parsedFlags.hasFlag(flags.resetGamerules))
.keepWorldBorder(!parsedFlags.hasFlag(flags.resetWorldBorder))
.keepFiles(parsedFlags.flagValue(flags.keepFiles));
worldManager.regenWorld(regenWorldOptions).onSuccess(newWorld -> {
Logging.fine("World regen success: " + newWorld);
issuer.sendInfo(MVCorei18n.REGEN_SUCCESS, Replace.WORLD.with(newWorld.getName()));
if (parsedFlags.hasFlag(flags.removePlayers)) {
playerWorldTeleporter.teleportPlayersToWorld(worldPlayers, newWorld);
}
}).onFailure(failure -> {
Logging.warning("World regen failure: " + failure);
issuer.sendError(failure.getFailureMessage());
});
}
@Service
private static final class Flags extends RemovePlayerFlags {
private static final String NAME = "mvregen";
@Inject
private Flags(@NotNull CommandFlagsManager flagsManager) {
super(NAME, flagsManager);
}
private final CommandValueFlag<String> seed = flag(CommandValueFlag.builder("--seed", String.class)
.addAlias("-s")
.completion(input -> Collections.singleton(String.valueOf(ACFUtil.RANDOM.nextLong())))
.optional()
.build());
private final CommandFlag resetWorldConfig = flag(CommandFlag.builder("--reset-world-config")
.addAlias("-wc")
.build());
private final CommandFlag resetGamerules = flag(CommandFlag.builder("--reset-gamerules")
.addAlias("-gm")
.build());
private final CommandFlag resetWorldBorder = flag(CommandFlag.builder("--reset-world-border")
.addAlias("-wb")
.build());
private final CommandValueFlag<List> keepFiles = flag(CommandValueFlag.builder("--keep-files", List.class)
.addAlias("-f")
.completion(input -> List.of("paper-world.yml"))
.defaultValue(Collections.emptyList())
.context(input -> List.of(REPatterns.COMMA.split(input)))
.build());
}
@Service
private static final class LegacyAlias extends RegenCommand implements LegacyAliasCommand {
@Inject
LegacyAlias(
@NotNull CommandQueueManager commandQueueManager,
@NotNull WorldManager worldManager,
@NotNull PlayerWorldTeleporter playerWorldTeleporter,
@NotNull WorldTickDeferrer worldTickDeferrer,
@NotNull Flags flags
) {
super(commandQueueManager, worldManager, playerWorldTeleporter, worldTickDeferrer, flags);
}
@Override
@CommandAlias("mvregen")
void onRegenCommand(MVCommandIssuer issuer, LoadedMultiverseWorld world, String[] flags) {
super.onRegenCommand(issuer, world, flags);
}
this.commandManager.getCommandQueueManager().addToQueue(new QueuedCommand(
issuer.getIssuer(),
() -> {
issuer.sendInfo(MVCorei18n.REGEN_REGENERATING, "{world}", world.getName());
worldManager.regenWorld(RegenWorldOptions.world(world)
.randomSeed(parsedFlags.hasFlag(SEED_FLAG))
.seed(parsedFlags.flagValue(SEED_FLAG))
.keepWorldConfig(!parsedFlags.hasFlag(RESET_WORLD_CONFIG_FLAG))
.keepGameRule(!parsedFlags.hasFlag(RESET_GAMERULES_FLAG))
.keepWorldBorder(!parsedFlags.hasFlag(RESET_WORLD_BORDER_FLAG))
).onSuccess(newWorld -> {
Logging.fine("World regen success: " + newWorld);
issuer.sendInfo(MVCorei18n.REGEN_SUCCESS, "{world}", newWorld.getName());
}).onFailure(failure -> {
Logging.fine("World regen failure: " + failure);
issuer.sendError(failure.getFailureMessage());
});
},
this.commandManager.formatMessage(
issuer, MessageType.INFO, MVCorei18n.REGEN_PROMPT, "{world}", world.getName())));
}
}
@@ -14,28 +14,30 @@ import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Service;
import org.mvplugins.multiverse.core.anchor.AnchorManager;
import org.mvplugins.multiverse.core.command.LegacyAliasCommand;
import org.mvplugins.multiverse.core.command.MVCommandManager;
import org.mvplugins.multiverse.core.config.CoreConfig;
import org.mvplugins.multiverse.core.commandtools.MVCommandManager;
import org.mvplugins.multiverse.core.commandtools.MultiverseCommand;
import org.mvplugins.multiverse.core.config.MVCoreConfig;
import org.mvplugins.multiverse.core.event.MVConfigReloadEvent;
import org.mvplugins.multiverse.core.locale.MVCorei18n;
import org.mvplugins.multiverse.core.utils.MVCorei18n;
import org.mvplugins.multiverse.core.world.WorldManager;
@Service
class ReloadCommand extends CoreCommand {
@CommandAlias("mv")
class ReloadCommand extends MultiverseCommand {
private final CoreConfig config;
private final MVCoreConfig config;
private final AnchorManager anchorManager;
private final WorldManager worldManager;
private final PluginManager pluginManager;
@Inject
ReloadCommand(
@NotNull CoreConfig config,
@NotNull MVCommandManager commandManager,
@NotNull MVCoreConfig config,
@NotNull AnchorManager anchorManager,
@NotNull WorldManager worldManager,
@NotNull PluginManager pluginManager
) {
@NotNull PluginManager pluginManager) {
super(commandManager);
this.config = config;
this.anchorManager = anchorManager;
this.worldManager = worldManager;
@@ -48,36 +50,24 @@ class ReloadCommand extends CoreCommand {
void onReloadCommand(@NotNull BukkitCommandIssuer issuer) {
issuer.sendInfo(MVCorei18n.RELOAD_RELOADING);
try {
// TODO: Make this all Try<Void>
this.config.load().getOrElseThrow(e -> new RuntimeException("Failed to load config", e));
this.worldManager.initAllWorlds().getOrElseThrow(e -> new RuntimeException("Failed to init worlds", e));
this.anchorManager.loadAnchors().getOrElseThrow(e -> new RuntimeException("Failed to load anchors", e));
this.anchorManager.loadAnchors();
} catch (Exception e) {
e.printStackTrace();
}
List<String> configsLoaded = new ArrayList<>();
configsLoaded.add("Multiverse-Core - config.yml");
configsLoaded.add("Multiverse-Core - worlds.yml");
configsLoaded.add("Multiverse-Core - worlds2.yml");
configsLoaded.add("Multiverse-Core - anchors.yml");
MVConfigReloadEvent configReload = new MVConfigReloadEvent(configsLoaded);
this.pluginManager.callEvent(configReload);
// TODO: replace this sendMessage and format the configsLoaded above, maybe?
configReload.getAllConfigsLoaded().forEach(issuer::sendMessage);
issuer.sendInfo(MVCorei18n.RELOAD_SUCCESS);
}
@Service
private static final class LegacyAlias extends ReloadCommand implements LegacyAliasCommand {
@Inject
LegacyAlias(@NotNull CoreConfig config, @NotNull AnchorManager anchorManager, @NotNull WorldManager worldManager, @NotNull PluginManager pluginManager) {
super(config, anchorManager, worldManager, pluginManager);
}
@Override
@CommandAlias("mvreload|mvr")
void onReloadCommand(@NotNull BukkitCommandIssuer issuer) {
super.onReloadCommand(issuer);
}
}
}
@@ -3,8 +3,9 @@ package org.mvplugins.multiverse.core.commands;
import co.aikar.commands.annotation.CommandAlias;
import co.aikar.commands.annotation.CommandCompletion;
import co.aikar.commands.annotation.CommandPermission;
import co.aikar.commands.annotation.Conditions;
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.dumptruckman.minecraft.util.Logging;
@@ -12,112 +13,44 @@ import jakarta.inject.Inject;
import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Service;
import org.mvplugins.multiverse.core.command.LegacyAliasCommand;
import org.mvplugins.multiverse.core.command.MVCommandIssuer;
import org.mvplugins.multiverse.core.command.MVCommandManager;
import org.mvplugins.multiverse.core.command.flag.CommandFlag;
import org.mvplugins.multiverse.core.command.flag.CommandFlagsManager;
import org.mvplugins.multiverse.core.command.flag.ParsedCommandFlags;
import org.mvplugins.multiverse.core.command.flags.RemovePlayerFlags;
import org.mvplugins.multiverse.core.locale.MVCorei18n;
import org.mvplugins.multiverse.core.locale.message.MessageReplacement.Replace;
import org.mvplugins.multiverse.core.utils.result.AsyncAttemptsAggregate;
import org.mvplugins.multiverse.core.world.MultiverseWorld;
import org.mvplugins.multiverse.core.commandtools.MVCommandIssuer;
import org.mvplugins.multiverse.core.commandtools.MVCommandManager;
import org.mvplugins.multiverse.core.commandtools.MultiverseCommand;
import org.mvplugins.multiverse.core.utils.MVCorei18n;
import org.mvplugins.multiverse.core.world.WorldManager;
import org.mvplugins.multiverse.core.world.helpers.PlayerWorldTeleporter;
import org.mvplugins.multiverse.core.world.options.RemoveWorldOptions;
@Service
class RemoveCommand extends CoreCommand {
@CommandAlias("mv")
class RemoveCommand extends MultiverseCommand {
private final WorldManager worldManager;
private final PlayerWorldTeleporter playerWorldTeleporter;
private final Flags flags;
@Inject
RemoveCommand(
@NotNull WorldManager worldManager,
@NotNull PlayerWorldTeleporter playerWorldTeleporter,
@NotNull Flags flags
) {
RemoveCommand(@NotNull MVCommandManager commandManager, @NotNull WorldManager worldManager) {
super(commandManager);
this.worldManager = worldManager;
this.playerWorldTeleporter = playerWorldTeleporter;
this.flags = flags;
}
@Subcommand("remove")
@CommandPermission("multiverse.core.remove")
@CommandCompletion("@mvworlds:scope=both @flags:groupName=" + Flags.NAME)
@CommandCompletion("@mvworlds:scope=both")
@Syntax("<world>")
@Description("{@@mv-core.remove.description}")
void onRemoveCommand(
MVCommandIssuer issuer,
@Single
@Conditions("mvworlds:scope=both")
@Syntax("<world>")
@Description("{@@mv-core.remove.world.description}")
MultiverseWorld world,
@Optional
@Syntax("[--remove-players]")
@Description("")
String[] flagArray) {
ParsedCommandFlags parsedFlags = flags.parse(flagArray);
var future = parsedFlags.hasFlag(flags.removePlayers)
? worldManager.getLoadedWorld(world).map(playerWorldTeleporter::removeFromWorld).getOrElse(AsyncAttemptsAggregate::emptySuccess)
: AsyncAttemptsAggregate.emptySuccess();
future.onSuccess(() -> doWorldRemoving(issuer, world, parsedFlags))
.onFailure(() -> issuer.sendError("Failed to teleport one or more players out of the world!"));
}
private void doWorldRemoving(MVCommandIssuer issuer, MultiverseWorld world, ParsedCommandFlags parsedFlags) {
worldManager.removeWorld(RemoveWorldOptions.world(world)
.saveBukkitWorld(!parsedFlags.hasFlag(flags.noSave))
.unloadBukkitWorld(!parsedFlags.hasFlag(flags.noUnloadBukkitWorld)))
String worldName) {
worldManager.removeWorld(worldName)
.onSuccess(removedWorldName -> {
Logging.fine("World remove success: " + removedWorldName);
issuer.sendInfo(MVCorei18n.REMOVE_SUCCESS, Replace.WORLD.with(removedWorldName));
issuer.sendInfo(MVCorei18n.REMOVEWORLD_REMOVED, "{world}", removedWorldName);
}).onFailure(failure -> {
Logging.fine("World remove failure: " + failure);
issuer.sendError(failure.getFailureMessage());
});
}
@Service
private static final class Flags extends RemovePlayerFlags {
private static final String NAME = "mvremove";
@Inject
private Flags(@NotNull CommandFlagsManager flagsManager) {
super(NAME, flagsManager);
}
private final CommandFlag noUnloadBukkitWorld = flag(CommandFlag.builder("--no-unload-bukkit-world")
.addAlias("-b")
.build());
private final CommandFlag noSave = flag(CommandFlag.builder("--no-save")
.addAlias("-n")
.build());
}
@Service
private static final class LegacyAlias extends RemoveCommand implements LegacyAliasCommand {
@Inject
LegacyAlias(
@NotNull WorldManager worldManager,
@NotNull PlayerWorldTeleporter playerWorldTeleporter,
@NotNull Flags flags
) {
super(worldManager, playerWorldTeleporter, flags);
}
@Override
@CommandAlias("mvremove")
void onRemoveCommand(MVCommandIssuer issuer, MultiverseWorld world, String[] flags) {
super.onRemoveCommand(issuer, world, flags);
}
}
}
@@ -9,16 +9,18 @@ import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Service;
import org.mvplugins.multiverse.core.MultiverseCore;
import org.mvplugins.multiverse.core.command.MVCommandManager;
import org.mvplugins.multiverse.core.locale.MVCorei18n;
import org.mvplugins.multiverse.core.commandtools.MVCommandManager;
import org.mvplugins.multiverse.core.commandtools.MultiverseCommand;
import org.mvplugins.multiverse.core.utils.MVCorei18n;
@Service
final class RootCommand extends CoreCommand {
class RootCommand extends MultiverseCommand {
private final Plugin plugin;
@Inject
RootCommand(@NotNull MultiverseCore plugin) {
RootCommand(@NotNull MVCommandManager commandManager, @NotNull MultiverseCore plugin) {
super(commandManager);
this.plugin = plugin;
}
@@ -1,95 +0,0 @@
package org.mvplugins.multiverse.core.commands;
import co.aikar.commands.annotation.CommandAlias;
import co.aikar.commands.annotation.CommandCompletion;
import co.aikar.commands.annotation.CommandPermission;
import co.aikar.commands.annotation.Description;
import co.aikar.commands.annotation.Optional;
import co.aikar.commands.annotation.Subcommand;
import co.aikar.commands.annotation.Syntax;
import jakarta.inject.Inject;
import org.bukkit.Location;
import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Service;
import org.mvplugins.multiverse.core.command.LegacyAliasCommand;
import org.mvplugins.multiverse.core.command.MVCommandIssuer;
import org.mvplugins.multiverse.core.command.context.PlayerLocation;
import org.mvplugins.multiverse.core.command.flag.ParsedCommandFlags;
import org.mvplugins.multiverse.core.command.flags.UnsafeFlags;
import org.mvplugins.multiverse.core.locale.MVCorei18n;
import org.mvplugins.multiverse.core.locale.message.MessageReplacement.Replace;
import org.mvplugins.multiverse.core.teleportation.BlockSafety;
import org.mvplugins.multiverse.core.world.WorldManager;
@Service
class SetSpawnCommand extends CoreCommand {
private final WorldManager worldManager;
private final BlockSafety blockSafety;
private final UnsafeFlags flags;
@Inject
SetSpawnCommand(@NotNull WorldManager worldManager, @NotNull BlockSafety blockSafety, @NotNull UnsafeFlags flags) {
this.worldManager = worldManager;
this.blockSafety = blockSafety;
this.flags = flags;
}
@CommandAlias("mvsetspawn")
@Subcommand("setspawn")
@CommandPermission("multiverse.core.spawn.set")
@CommandCompletion("@flags:groupName=" + UnsafeFlags.NAME + " @flags:resolveUntil=arg1,groupName=" + UnsafeFlags.NAME)
@Syntax("[worldname:x,y,z[,pitch,yaw]] [--unsafe]")
@Description("{@@mv-core.setspawn.description}")
void onSetSpawnCommand(
MVCommandIssuer issuer,
@Syntax("[worldname:x,y,z[,pitch,yaw]]")
@Description("{@@mv-core.setspawn.location.description}")
PlayerLocation playerLocation,
@Optional
@Syntax("[--unsafe]")
@Description("")
String[] flagArray) {
ParsedCommandFlags parsedFlags = flags.parse(flagArray);
Location location = playerLocation.value();
if (!parsedFlags.hasFlag(flags.unsafe) && !blockSafety.canSpawnAtLocationSafely(location)) {
issuer.sendMessage(MVCorei18n.SETSPAWN_UNSAFE);
return;
}
worldManager.getLoadedWorld(location.getWorld())
.peek(mvWorld -> mvWorld.setSpawnLocation(location)
.andThenTry(worldManager::saveWorldsConfig)
.onSuccess(ignore -> issuer.sendMessage(MVCorei18n.SETSPAWN_SUCCESS,
Replace.WORLD.with(mvWorld.getName()),
Replace.LOCATION.with(prettyLocation(location))))
.onFailure(e -> issuer.sendMessage(MVCorei18n.SETSPAWN_FAILED,
Replace.WORLD.with(mvWorld.getName()),
Replace.ERROR.with(e))))
.onEmpty(() -> issuer.sendMessage(MVCorei18n.SETSPAWN_NOTMVWORLD,
Replace.WORLD.with(location.getWorld().getName())));
}
private String prettyLocation(Location location) {
return "%.2f, %.2f, %.2f, pitch:%.2f, yaw:%.2f"
.formatted(location.getX(), location.getY(), location.getZ(), location.getPitch(), location.getYaw());
}
@Service
private static final class LegacyAlias extends SetSpawnCommand implements LegacyAliasCommand {
@Inject
LegacyAlias(@NotNull WorldManager worldManager, @NotNull BlockSafety blockSafety, @NotNull UnsafeFlags flags) {
super(worldManager, blockSafety, flags);
}
@Override
@CommandAlias("mvss")
void onSetSpawnCommand(MVCommandIssuer issuer, PlayerLocation location, String[] flagArray) {
super.onSetSpawnCommand(issuer, location, flagArray);
}
}
}
@@ -1,145 +0,0 @@
package org.mvplugins.multiverse.core.commands;
import java.util.Arrays;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
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.Flags;
import co.aikar.commands.annotation.Optional;
import co.aikar.commands.annotation.Subcommand;
import co.aikar.commands.annotation.Syntax;
import com.dumptruckman.minecraft.util.Logging;
import jakarta.inject.Inject;
import org.bukkit.World;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Service;
import org.mvplugins.multiverse.core.command.MVCommandIssuer;
import org.mvplugins.multiverse.core.command.MVCommandManager;
import org.mvplugins.multiverse.core.command.flag.CommandFlag;
import org.mvplugins.multiverse.core.command.flag.ParsedCommandFlags;
import org.mvplugins.multiverse.core.command.flags.UnsafeFlags;
import org.mvplugins.multiverse.core.locale.MVCorei18n;
import org.mvplugins.multiverse.core.locale.message.Message;
import org.mvplugins.multiverse.core.locale.message.MessageReplacement.Replace;
import org.mvplugins.multiverse.core.permissions.CorePermissionsChecker;
import org.mvplugins.multiverse.core.teleportation.AsyncSafetyTeleporter;
import org.mvplugins.multiverse.core.teleportation.TeleportFailureReason;
import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld;
import org.mvplugins.multiverse.core.world.WorldManager;
@Service
final class SpawnCommand extends CoreCommand {
private final WorldManager worldManager;
private final AsyncSafetyTeleporter safetyTeleporter;
private final CorePermissionsChecker permissionsChecker;
private final UnsafeFlags flags;
@Inject
SpawnCommand(
@NotNull WorldManager worldManager,
@NotNull AsyncSafetyTeleporter safetyTeleporter,
@NotNull CorePermissionsChecker permissionsChecker,
@NotNull UnsafeFlags flags
) {
this.worldManager = worldManager;
this.safetyTeleporter = safetyTeleporter;
this.permissionsChecker = permissionsChecker;
this.flags = flags;
}
@CommandAlias("mvspawn")
@Subcommand("spawn")
@CommandPermission("@mvspawn")
@CommandCompletion("@playersarray:checkPermissions=@mvspawnother|@flags:resolveUntil=arg1,groupName=" + UnsafeFlags.NAME
+ " @flags:groupName=" + UnsafeFlags.NAME)
@Syntax("[player]")
@Description("{@@mv-core.spawn.description}")
void onSpawnTpCommand(
MVCommandIssuer issuer,
@Flags("resolve=issuerAware")
@Syntax("[player]")
@Description("{@@mv-core.spawn.player.description}")
Player[] players,
@Optional
@Syntax("[--unsafe]")
@Description("")
String[] flagArray) {
ParsedCommandFlags parsedFlags = flags.parse(flagArray);
Map<World, List<Entity>> playersByWorld = Arrays.stream(players)
.collect(Collectors.groupingBy(Entity::getWorld));
playersByWorld.forEach((world, entities) ->
teleportPlayersToSpawn(issuer, world, entities, !parsedFlags.hasFlag(flags.unsafe)));
}
private void teleportPlayersToSpawn(MVCommandIssuer issuer, World world,
List<Entity> entities, boolean checkSafety) {
LoadedMultiverseWorld mvWorld = worldManager.getLoadedWorld(world).getOrNull();
if (mvWorld == null) {
issuer.sendMessage("The world '" + world.getName() + "' is not a multiverse world!");
return;
}
if (!permissionsChecker.checkSpawnPermission(issuer.getIssuer(), entities, mvWorld)) {
issuer.sendMessage("You do not have permission to use this command in this world!");
return;
}
if (entities.size() == 1) {
handleSingleTeleport(issuer, mvWorld, entities.get(0), checkSafety);
} else {
handleMultiTeleport(issuer, mvWorld, entities, checkSafety);
}
}
private void handleSingleTeleport(MVCommandIssuer issuer, LoadedMultiverseWorld mvWorld,
Entity entity, boolean checkSafety) {
safetyTeleporter.to(mvWorld.getSpawnLocation())
.by(issuer)
.checkSafety(checkSafety)
.teleportSingle(entity)
.onSuccess(() -> issuer.sendInfo(MVCorei18n.SPAWN_SUCCESS,
Replace.PLAYER.with(entity.equals(issuer.getPlayer())
? Message.of(MVCorei18n.GENERIC_YOU)
: Message.of(entity.getName())),
Replace.WORLD.with(mvWorld.getName())))
.onFailure(failure -> issuer.sendError(MVCorei18n.SPAWN_FAILED,
Replace.PLAYER.with(entity.equals(issuer.getPlayer())
? Message.of(MVCorei18n.GENERIC_YOU)
: Message.of(entity.getName())),
Replace.WORLD.with(mvWorld.getName()),
Replace.REASON.with(failure.get(0).getFailureMessage())));
}
private void handleMultiTeleport(MVCommandIssuer issuer, LoadedMultiverseWorld mvWorld,
List<Entity> entities, boolean checkSafety) {
safetyTeleporter.to(mvWorld.getSpawnLocation())
.by(issuer)
.checkSafety(checkSafety)
.teleport(entities)
.onSuccessCount(successCount -> issuer.sendMessage(MVCorei18n.SPAWN_SUCCESS,
Replace.PLAYER.with(successCount + " players"), //todo: replace this with localised "{count} players"
Replace.WORLD.with(mvWorld.getName())))
.onFailureCount(reasonsCountMap -> {
for (var entry : reasonsCountMap.entrySet()) {
Logging.finer("Failed to teleport %s players to %s: %s",
entry.getValue(), mvWorld.getName(), entry.getKey());
issuer.sendError(MVCorei18n.SPAWN_FAILED,
Replace.PLAYER.with(entry.getValue() + " players"),
Replace.WORLD.with(mvWorld.getName()),
Replace.REASON.with(entry.getKey().getMessageKey()));
}
});
}
}
Loaded 100 of 443 files, more files were not shown because too many files have changed in this diff. Show more