Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8a8f65ec82
|
||
|
|
feb854817f
|
||
|
|
97a73bf591 | ||
|
|
4e66693b21 | ||
|
|
f9fc0f1a28 | ||
|
|
f246094915 | ||
|
|
72ee0d5bed | ||
|
|
1afecd40ea | ||
|
|
00944ed3b2 | ||
|
|
5f659a80d8 | ||
|
|
5f9b0af8d1 | ||
|
|
5c44c05f6f | ||
|
|
1699a47011 | ||
|
|
96deee719b | ||
|
|
8a60761098 | ||
|
|
a38b4e03b4 | ||
|
|
db9b725e10 | ||
|
|
d43b23e7ea | ||
|
|
86d77ae796 | ||
|
|
86bf785c84 | ||
|
|
626537290d | ||
|
|
9fa47727f4 | ||
|
|
ae3bc6d201 | ||
|
|
b1d126397e | ||
|
|
8ab9993e97 | ||
|
|
13b949c692 | ||
|
|
2b41c1367e | ||
|
|
fde3f388bd | ||
|
|
eb3968b7d6 | ||
|
|
fed1fdcbea | ||
|
|
bfb6fea5f5 | ||
|
|
5b02452a3d | ||
|
|
4a2bfc74fa | ||
|
|
f0539441cf | ||
|
|
efe490d8c4 | ||
|
|
3074cfab17 | ||
|
|
7a5dd44feb | ||
|
|
88a9ee5b6d | ||
|
|
46f67240e8 | ||
|
|
89e765b2e8 | ||
|
|
b8bdec22d3 | ||
|
|
792913cff7 | ||
|
|
6dcb40fdcb | ||
|
|
2bb601098a | ||
|
|
4bcebd51f5 | ||
|
|
e207eb5d0d | ||
|
|
f181bb7ade | ||
|
|
7207d9303f | ||
|
|
82799cdbe8 | ||
|
|
3629babdbc | ||
|
|
eed3353e2b | ||
|
|
147564caab | ||
|
|
30e50866bd | ||
|
|
5dde7608f2 | ||
|
|
2f9915c62e | ||
|
|
e62e37831b | ||
|
|
ef9934f3c6 | ||
|
|
691bcd0ffa | ||
|
|
148644970d | ||
|
|
ad20ec94c5 | ||
|
|
f7d86884b4 | ||
|
|
0e0e4e0434 | ||
|
|
8e878b1ed0 | ||
|
|
e1c6af2f38 | ||
|
|
b52d65757a | ||
|
|
ff2135c775 | ||
|
|
c4e4e311e2 | ||
|
|
3e4ded52e3 | ||
|
|
4ef8390ea3 | ||
|
|
bca71e1acc | ||
|
|
343da8c301 | ||
|
|
d34f3adccc | ||
|
|
b3862faf88 | ||
|
|
8bffffdc65 | ||
|
|
79a71fa58f | ||
|
|
a41476de89 | ||
|
|
fe75e497e4 | ||
|
|
fdd9257e2c | ||
|
|
55d1347d4f | ||
|
|
29cc97486b | ||
|
|
8d06b92edd | ||
|
|
ce3b6ab700 | ||
|
|
58ac0f505c | ||
|
|
4004554733 | ||
|
|
99037b2af5 | ||
|
|
ac46a5f64a | ||
|
|
4d805b921b | ||
|
|
a612854490 | ||
|
|
e85c14e319 | ||
|
|
3983af7b4c | ||
|
|
8853f88f3a | ||
|
|
08d361e0ec | ||
|
|
f985f2db06 | ||
|
|
c068225950 | ||
|
|
82acca3ff9 | ||
|
|
d712c1eb09 | ||
|
|
25e9213ee9 | ||
|
|
9e79708fec | ||
|
|
65d1141289 | ||
|
|
63b2ffdf72 | ||
|
|
14d8ad7a5c | ||
|
|
33e0df925f | ||
|
|
1883edbb28 | ||
|
|
b7a2bd61ca | ||
|
|
a0673ac109 | ||
|
|
7735a33ccf | ||
|
|
5f24520685 | ||
|
|
608cb8611c | ||
|
|
a965b9ce56 |
No files matched your search
@@ -0,0 +1 @@
|
||||
.gitea/CODEOWNERS linguist-detectable=false
|
||||
@@ -0,0 +1,3 @@
|
||||
# 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
|
||||
@@ -11,9 +11,9 @@ jobs:
|
||||
pull-requests: write
|
||||
continue-on-error: true
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
- uses: dbelyaev/action-checkstyle@v0.8.4
|
||||
- uses: dbelyaev/action-checkstyle@v3
|
||||
continue-on-error: true
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
@@ -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.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.21.11, 1.21.10, 1.21.9, 1.21.8, 1.21.7, 1.21.6, 1.21.5, 1.21.4, 1.21.3, 1.21.2, 1.21.1, 1.21, 1.20.6, 1.20.5, 1.20.4, 1.20.3, 1.20.2, 1.20.1, 1.20, 1.19.4, 1.19.3, 1.19.2, 1.19.1, 1.19, 1.18.2
|
||||
version_type: ${{ steps.parse-release-type.outputs.release_type }}
|
||||
loaders: bukkit, spigot, paper
|
||||
loaders: bukkit, spigot, paper, purpur
|
||||
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.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.21.11, 1.21.10, 1.21.9, 1.21.8, 1.21.7, 1.21.6, 1.21.5, 1.21.4, 1.21.3, 1.21.2, 1.21.1, 1.21, 1.20.6, 1.20.5, 1.20.4, 1.20.3, 1.20.2, 1.20.1, 1.20, 1.19.4, 1.19.3, 1.19.2, 1.19.1, 1.19, 1.18.2
|
||||
release_type: ${{ steps.parse-release-type.outputs.release_type }}
|
||||
project_relations: ${{ inputs.dbo_project_relations }}
|
||||
file_path: ${{ github.workspace }}/${{ inputs.plugin_name }}-${{ steps.release-info.outputs.tag_name }}.jar
|
||||
@@ -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.8"]}'
|
||||
platform_dependencies: '{"PAPER": ["1.18.2-1.21.11"]}'
|
||||
plugin_dependencies: ${{ inputs.hangar_plugin_dependencies }}
|
||||
@@ -0,0 +1,4 @@
|
||||
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
|
||||
|
||||
-117
@@ -1,117 +0,0 @@
|
||||
import org.apache.tools.ant.filters.ReplaceTokens
|
||||
|
||||
plugins {
|
||||
id 'org.mvplugins.multiverse-plugin' version '1.2.1'
|
||||
}
|
||||
|
||||
group = 'org.mvplugins.multiverse.core'
|
||||
description = 'Multiverse-Core'
|
||||
|
||||
repositories {
|
||||
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 = "helpchatRepoReleases"
|
||||
url = uri("https://repo.helpch.at/releases/")
|
||||
}
|
||||
}
|
||||
|
||||
configure(apiDependencies) {
|
||||
serverApiVersion = '1.21.8-R0.1-SNAPSHOT'
|
||||
mockBukkitServerApiVersion = '1.21'
|
||||
mockBukkitVersion = '4.69.2'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// 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.7'
|
||||
shadowed 'org.glassfish.hk2:hk2-locator:3.1.1'
|
||||
shadowed('org.glassfish.hk2:hk2-inhabitant-generator:3.1.1') {
|
||||
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.5.2'
|
||||
shadowed 'org.jetbrains:annotations:26.0.2'
|
||||
shadowed 'io.papermc:paperlib:1.0.8'
|
||||
|
||||
// Tests
|
||||
testImplementation('com.googlecode.json-simple:json-simple:1.1.1') {
|
||||
exclude group: 'junit', module: 'junit'
|
||||
}
|
||||
testImplementation("org.hamcrest:hamcrest:3.0")
|
||||
}
|
||||
|
||||
ext.bitlyAccessToken = System.getenv('BITLY_ACCESS_TOKEN') ?: 'bitly-access-token'
|
||||
|
||||
tasks.register('prepareSource', Sync) {
|
||||
inputs.property 'bitlyAccessToken', bitlyAccessToken
|
||||
from sourceSets.main.java
|
||||
into "$buildDir/src"
|
||||
filter(ReplaceTokens, tokens: [
|
||||
'bitly-access-token': bitlyAccessToken,
|
||||
])
|
||||
}
|
||||
|
||||
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 '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'
|
||||
}
|
||||
|
||||
publishing {
|
||||
repositories {
|
||||
maven {
|
||||
name = "GitHubPackages"
|
||||
url = "https://maven.pkg.github.com/Multiverse/Multiverse-Core"
|
||||
credentials {
|
||||
username = System.getenv("GITHUB_ACTOR")
|
||||
password = System.getenv("GITHUB_TOKEN")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,364 @@
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
BIN
Binary file not shown.
+2
-1
@@ -1,6 +1,7 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
@@ -15,6 +15,8 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
#
|
||||
@@ -55,7 +57,7 @@
|
||||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# within the Gradle project.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
@@ -83,10 +85,9 @@ done
|
||||
# This is normally unused
|
||||
# shellcheck disable=SC2034
|
||||
APP_BASE_NAME=${0##*/}
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
|
||||
' "$PWD" ) || exit
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD=maximum
|
||||
@@ -133,10 +134,13 @@ location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD=java
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
if ! command -v java >/dev/null 2>&1
|
||||
then
|
||||
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
@@ -144,7 +148,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=SC3045
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
MAX_FD=$( ulimit -H -n ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
@@ -152,7 +156,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=SC3045
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
ulimit -n "$MAX_FD" ||
|
||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||
esac
|
||||
@@ -197,11 +201,15 @@ if "$cygwin" || "$msys" ; then
|
||||
done
|
||||
fi
|
||||
|
||||
# Collect all arguments for the java command;
|
||||
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
||||
# shell script including quotes and variable substitutions, so put them in
|
||||
# double quotes to make sure that they get re-expanded; and
|
||||
# * put everything else in single quotes, so that it's not re-expanded.
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Collect all arguments for the java command:
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||
# and any embedded shellness will be escaped.
|
||||
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
|
||||
# treated as '${Hostname}' itself on the command line.
|
||||
|
||||
set -- \
|
||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||
|
||||
Vendored
+12
-10
@@ -13,6 +13,8 @@
|
||||
@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 ##########################################################################
|
||||
@@ -43,11 +45,11 @@ set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if %ERRORLEVEL% equ 0 goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
|
||||
@@ -57,11 +59,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
/*
|
||||
* This file was generated by the Gradle 'init' task.
|
||||
*/
|
||||
|
||||
pluginManagement {
|
||||
repositories {
|
||||
gradlePluginPortal()
|
||||
maven {
|
||||
url = uri('https://repo.onarandombox.com/multiverse-releases')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rootProject.name = 'multiverse-core'
|
||||
@@ -0,0 +1,11 @@
|
||||
pluginManagement {
|
||||
repositories {
|
||||
gradlePluginPortal()
|
||||
maven {
|
||||
url = uri("https://repo.onarandombox.com/multiverse-releases")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rootProject.name = "Multiverse-Core"
|
||||
|
||||
@@ -124,7 +124,6 @@ public class MultiverseCore extends MultiverseModule {
|
||||
public void onDisable() {
|
||||
super.onDisable();
|
||||
MultiverseCoreApi.shutdown();
|
||||
saveAllConfigs();
|
||||
shutdownDependencyInjection();
|
||||
PluginServiceLocatorFactory.get().shutdown();
|
||||
Logging.shutdown();
|
||||
@@ -215,11 +214,11 @@ public class MultiverseCore extends MultiverseModule {
|
||||
*/
|
||||
private Try<Void> saveAllConfigs() {
|
||||
return configProvider.get().save()
|
||||
.flatMap(ignore -> worldManagerProvider.get().saveWorldsConfig())
|
||||
.flatMap(ignore ->anchorManagerProvider.get().saveAllAnchors())
|
||||
.onFailure(e -> {
|
||||
Logging.severe("Failed to save configs, things may not work as expected.");
|
||||
});
|
||||
.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()));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package org.mvplugins.multiverse.core;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import io.vavr.control.Option;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import jakarta.inject.Inject;
|
||||
import me.clip.placeholderapi.expansion.PlaceholderExpansion;
|
||||
@@ -20,9 +19,8 @@ import org.mvplugins.multiverse.core.utils.StringFormatter;
|
||||
import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld;
|
||||
import org.mvplugins.multiverse.core.world.WorldManager;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
@Service
|
||||
final class PlaceholderExpansionHook extends PlaceholderExpansion {
|
||||
@@ -82,7 +80,7 @@ final class PlaceholderExpansionHook extends PlaceholderExpansion {
|
||||
return null;
|
||||
}
|
||||
|
||||
final var placeholder = paramsArray.removeFirst();
|
||||
final var placeholder = paramsArray.remove(0);
|
||||
|
||||
String worldName = parseWorldName(offlinePlayer, paramsArray);
|
||||
if (worldName == null) return null;
|
||||
@@ -105,9 +103,9 @@ final class PlaceholderExpansionHook extends PlaceholderExpansion {
|
||||
}
|
||||
|
||||
// Try get from params
|
||||
String paramWorldName = paramsArray.getLast();
|
||||
String paramWorldName = paramsArray.get(paramsArray.size() - 1);
|
||||
if (worldManager.isLoadedWorld(paramWorldName)) {
|
||||
paramsArray.removeLast();
|
||||
paramsArray.remove(paramsArray.size() - 1);
|
||||
return paramWorldName;
|
||||
}
|
||||
|
||||
@@ -123,7 +121,7 @@ final class PlaceholderExpansionHook extends PlaceholderExpansion {
|
||||
@NotNull List<String> placeholderParams,
|
||||
@NotNull LoadedMultiverseWorld world) {
|
||||
// Switch to find what specific placeholder we want
|
||||
switch (placeholder.toLowerCase()) {
|
||||
switch (placeholder.toLowerCase(Locale.ENGLISH)) {
|
||||
case "alias" -> {
|
||||
return world.getAliasOrName();
|
||||
}
|
||||
@@ -166,6 +164,12 @@ final class PlaceholderExpansionHook extends PlaceholderExpansion {
|
||||
case "name" -> {
|
||||
return world.getName();
|
||||
}
|
||||
case "playercount" -> {
|
||||
return String.valueOf(world.getBukkitWorld()
|
||||
.map(World::getPlayers)
|
||||
.map(List::size)
|
||||
.getOrElse(-1));
|
||||
}
|
||||
case "playerlimit" -> {
|
||||
return String.valueOf(world.getPlayerLimit());
|
||||
}
|
||||
@@ -179,7 +183,7 @@ final class PlaceholderExpansionHook extends PlaceholderExpansion {
|
||||
return String.valueOf(world.getSeed());
|
||||
}
|
||||
case "time" -> {
|
||||
String timeFormat = !placeholderParams.isEmpty() ? placeholderParams.getFirst() : "";
|
||||
String timeFormat = !placeholderParams.isEmpty() ? placeholderParams.get(0) : "";
|
||||
long time = world.getBukkitWorld().map(World::getTime).getOrElse(0L);
|
||||
switch (timeFormat) {
|
||||
case "" -> {
|
||||
|
||||
@@ -6,6 +6,7 @@ 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;
|
||||
|
||||
@@ -26,6 +27,7 @@ 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;
|
||||
@@ -42,9 +44,9 @@ 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.utils.StringFormatter;
|
||||
import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld;
|
||||
import org.mvplugins.multiverse.core.world.MultiverseWorld;
|
||||
import org.mvplugins.multiverse.core.world.WorldManager;
|
||||
@@ -87,7 +89,7 @@ public class MVCommandCompletions extends PaperCommandCompletions {
|
||||
registerAsyncCompletion("commands", this::suggestCommands);
|
||||
registerAsyncCompletion("destinations", this::suggestDestinations);
|
||||
registerStaticCompletion("difficulties", suggestEnums(Difficulty.class));
|
||||
registerStaticCompletion("environments", suggestEnums(World.Environment.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);
|
||||
@@ -99,7 +101,7 @@ public class MVCommandCompletions extends PaperCommandCompletions {
|
||||
registerAsyncCompletion("mvworlds", this::suggestMVWorlds);
|
||||
registerAsyncCompletion("mvworldpropsname", this::suggestMVWorldPropsName);
|
||||
registerAsyncCompletion("mvworldpropsvalue", this::suggestMVWorldPropsValue);
|
||||
registerAsyncCompletion("playersarray", this::suggestPlayersArray);
|
||||
registerCompletion("playersarray", this::suggestPlayersArray); // getting online players cannot be async
|
||||
registerStaticCompletion("propsmodifyaction", suggestEnums(PropertyModifyAction.class));
|
||||
registerStaticCompletion("spawncategories", suggestEnums(SpawnCategory.class));
|
||||
registerAsyncCompletion("spawncategorypropsname", this::suggestSpawnCategoryPropsName);
|
||||
@@ -176,7 +178,7 @@ public class MVCommandCompletions extends PaperCommandCompletions {
|
||||
public <T extends Enum<T>> Collection<String> suggestEnums(Class<T> enumClass) {
|
||||
return EnumSet.allOf(enumClass).stream()
|
||||
.map(Enum::name)
|
||||
.map(String::toLowerCase)
|
||||
.map(name -> name.toLowerCase(Locale.ENGLISH))
|
||||
.toList();
|
||||
}
|
||||
|
||||
@@ -223,18 +225,22 @@ public class MVCommandCompletions extends PaperCommandCompletions {
|
||||
// Most likely console did not specify a player
|
||||
return Collections.<String>emptyList();
|
||||
}
|
||||
if (context.hasConfig("othersOnly") && (players.length == 1 && players[0].equals(context.getIssuer().getIssuer()))) {
|
||||
CommandSender sender = context.getIssuer().getIssuer();
|
||||
if (context.hasConfig("othersOnly") && (players.length == 1 && players[0].equals(sender))) {
|
||||
return Collections.<String>emptyList();
|
||||
}
|
||||
return suggestDestinationsWithPerms(context.getIssuer().getIssuer(), players, context.getInput());
|
||||
return suggestDestinationsWithPerms(sender, Arrays.asList(players), context.getInput());
|
||||
})
|
||||
.getOrElse(Collections.emptyList());
|
||||
}
|
||||
|
||||
private Collection<String> suggestDestinationsWithPerms(CommandSender teleporter, Player[] players, String deststring) {
|
||||
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, Arrays.asList(players), packet))
|
||||
.checkDestinationPacketPermission(teleporter, teleportees, packet))
|
||||
.map(DestinationSuggestionPacket::parsableString)
|
||||
.toList();
|
||||
}
|
||||
@@ -278,19 +284,16 @@ public class MVCommandCompletions extends PaperCommandCompletions {
|
||||
case "both" -> {
|
||||
return worldManager.getWorlds().stream()
|
||||
.map(MultiverseWorld::getTabCompleteName)
|
||||
.map(StringFormatter::quoteMultiWordString)
|
||||
.toList();
|
||||
}
|
||||
case "loaded" -> {
|
||||
return worldManager.getLoadedWorlds().stream()
|
||||
.map(MultiverseWorld::getTabCompleteName)
|
||||
.map(StringFormatter::quoteMultiWordString)
|
||||
.toList();
|
||||
}
|
||||
case "unloaded" -> {
|
||||
return worldManager.getUnloadedWorlds().stream()
|
||||
.map(MultiverseWorld::getTabCompleteName)
|
||||
.map(StringFormatter::quoteMultiWordString)
|
||||
.toList();
|
||||
}
|
||||
case "potential" -> {
|
||||
|
||||
@@ -11,6 +11,7 @@ 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;
|
||||
@@ -272,13 +273,25 @@ public class MVCommandContexts extends PaperCommandContexts {
|
||||
private IssuerAwareContextBuilder<Player[]> playerArrayContextBuilder() {
|
||||
return new IssuerAwareContextBuilder<Player[]>()
|
||||
.fromPlayer((context, player) -> new Player[]{player})
|
||||
.fromInput((context, input) -> {
|
||||
Player[] players = PlayerFinder.getMulti(input, context.getSender()).toArray(new Player[0]);
|
||||
return (players.length == 0) ? null : players;
|
||||
})
|
||||
.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) -> Message.of("Player " + input + " not found."));
|
||||
.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) {
|
||||
|
||||
@@ -112,9 +112,9 @@ public class MVCommandIssuer extends BukkitCommandIssuer {
|
||||
} else {
|
||||
var formatter = getManager().getFormat(messageType);
|
||||
if (formatter != null) {
|
||||
sendMessage(formatter.format(message.formatted()));
|
||||
sendMessage(formatter.format(message.formatted(getManager().getLocales(), this)));
|
||||
} else {
|
||||
sendMessage(message.formatted());
|
||||
sendMessage(message.formatted(getManager().getLocales(), this));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,9 @@ public class MVRootCommand extends BukkitRootCommand {
|
||||
@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);
|
||||
return super.getTabCompletions(sender, alias, quoteFormatedArgs, commandsOnly, isAsync)
|
||||
.stream()
|
||||
.map(StringFormatter::quoteMultiWordString)
|
||||
.toList();
|
||||
}
|
||||
}
|
||||
+47
-70
@@ -123,48 +123,8 @@ public final class IssuerAwareContextBuilder<T> {
|
||||
*/
|
||||
@ApiStatus.AvailableSince("5.1")
|
||||
public IssuerAwareContextResolver<T, BukkitCommandExecutionContext> generateContext() {
|
||||
Objects.requireNonNull(fromPlayer);
|
||||
Objects.requireNonNull(fromInput);
|
||||
Objects.requireNonNull(issuerOnlyFailMessage);
|
||||
Objects.requireNonNull(issuerAwarePlayerFailMessage);
|
||||
Objects.requireNonNull(issuerAwareInputFailMessage);
|
||||
Objects.requireNonNull(inputOnlyFailMessage);
|
||||
|
||||
return context -> {
|
||||
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 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 result;
|
||||
}
|
||||
|
||||
if (resolve.equals("issuerAware")) {
|
||||
if (issuer.isPlayer()) {
|
||||
Player player = issuer.getPlayer();
|
||||
result = fromPlayer.apply(context, player);
|
||||
if (result != null) {
|
||||
return 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));
|
||||
};
|
||||
validateRequiredVariables();
|
||||
return context -> resolveValue(context, GenericIssuerAwareValue::new).value;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -178,49 +138,66 @@ public final class IssuerAwareContextBuilder<T> {
|
||||
*/
|
||||
@ApiStatus.AvailableSince("5.1")
|
||||
public <I extends IssuerAwareValue> IssuerAwareContextResolver<I, BukkitCommandExecutionContext> generateContext(BiFunction<Boolean, T, I> createValue) {
|
||||
// todo: This is a copy and paste from above
|
||||
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);
|
||||
}
|
||||
|
||||
return context -> {
|
||||
BukkitCommandIssuer issuer = context.getIssuer();
|
||||
String resolve = context.getFlagValue("resolve", "");
|
||||
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);
|
||||
}
|
||||
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));
|
||||
}
|
||||
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);
|
||||
}
|
||||
String input = context.getFirstArg();
|
||||
T result = fromInput.apply(context, input);
|
||||
if (result != null) {
|
||||
context.popFirstArg();
|
||||
return createValue.apply(false, result);
|
||||
}
|
||||
|
||||
if (resolve.equals("issuerAware")) {
|
||||
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));
|
||||
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(issuerAwareInputFailMessage.apply(context, input).formatted(issuer));
|
||||
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));
|
||||
};
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package org.mvplugins.multiverse.core.command.flag;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.function.Function;
|
||||
|
||||
import co.aikar.commands.InvalidCommandArgument;
|
||||
@@ -220,7 +221,7 @@ public class CommandValueFlag<T> extends CommandFlag {
|
||||
private void setEnumContext() {
|
||||
this.context = (String value) -> {
|
||||
try {
|
||||
return Enum.valueOf(type, value.toUpperCase());
|
||||
return Enum.valueOf(type, value.toUpperCase(Locale.ENGLISH));
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new InvalidCommandArgument("Invalid value for argument " + key + ": " + value);
|
||||
}
|
||||
@@ -229,7 +230,7 @@ public class CommandValueFlag<T> extends CommandFlag {
|
||||
|
||||
private void setEnumCompletion() {
|
||||
List<String> types = Arrays.stream(type.getEnumConstants())
|
||||
.map(typeClass -> typeClass.name().toLowerCase())
|
||||
.map(typeClass -> typeClass.name().toLowerCase(Locale.ENGLISH))
|
||||
.toList();
|
||||
|
||||
this.completion = (input) -> types;
|
||||
|
||||
@@ -67,12 +67,10 @@ class ConfigCommand extends CoreCommand {
|
||||
private void updateConfigValue(MVCommandIssuer issuer, String name, String value) {
|
||||
var stringPropertyHandle = config.getStringPropertyHandle();
|
||||
stringPropertyHandle.setPropertyString(issuer.getIssuer(), name, value)
|
||||
.onSuccess(ignore -> {
|
||||
config.save();
|
||||
issuer.sendMessage(MVCorei18n.CONFIG_SET_SUCCESS,
|
||||
Replace.NAME.with(name),
|
||||
Replace.VALUE.with(stringPropertyHandle.getProperty(name).getOrNull()));
|
||||
})
|
||||
.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),
|
||||
|
||||
@@ -5,6 +5,7 @@ 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;
|
||||
@@ -136,6 +137,9 @@ final class DumpsService {
|
||||
}
|
||||
|
||||
private String getPluginList() {
|
||||
return " - " + StringUtils.join(plugin.getServer().getPluginManager().getPlugins(), "\n - ");
|
||||
return " - " + StringUtils.join(Arrays.stream(plugin.getServer().getPluginManager().getPlugins())
|
||||
.map(Object::toString)
|
||||
.sorted()
|
||||
.toList(), "\n - ");
|
||||
}
|
||||
}
|
||||
@@ -52,7 +52,7 @@ final class EntitySpawnConfigCommand extends CoreCommand {
|
||||
void onInfoCommand(
|
||||
MVCommandIssuer issuer,
|
||||
|
||||
@Flags("resolve=issuerAware")
|
||||
@Flags("resolve=issuerAware,maxArgForAware=0")
|
||||
@Syntax("[world]")
|
||||
MultiverseWorld world,
|
||||
|
||||
@@ -91,7 +91,7 @@ final class EntitySpawnConfigCommand extends CoreCommand {
|
||||
void onModifyCommand(
|
||||
MVCommandIssuer issuer,
|
||||
|
||||
@Flags("resolve=issuerAware")
|
||||
@Flags("resolve=issuerAware,maxArgForAware=4")
|
||||
@Syntax("[world]")
|
||||
MultiverseWorld world,
|
||||
|
||||
@@ -113,11 +113,10 @@ final class EntitySpawnConfigCommand extends CoreCommand {
|
||||
.getSpawnCategoryConfig(spawnCategory)
|
||||
.getStringPropertyHandle()
|
||||
.modifyPropertyString(property, value, action)
|
||||
.onSuccess(ignore -> issuer.sendMessage("Successfully set " + property + " to " + value
|
||||
.andThenTry(worldManager::saveWorldsConfig)
|
||||
.onSuccess(ignore -> issuer.sendInfo("Successfully set " + property + " to " + value
|
||||
+ " for " + spawnCategory.name() + " in " + world.getName()))
|
||||
.onFailure(e -> issuer.sendMessage("Unable to set " + property + " to " + value
|
||||
.onFailure(e -> issuer.sendError("Unable to set " + property + " to " + value
|
||||
+ " for " + spawnCategory.name() + " in " + world.getName() + ": " + e.getMessage()));
|
||||
|
||||
worldManager.saveWorldsConfig();
|
||||
}
|
||||
}
|
||||
@@ -67,7 +67,7 @@ class GameruleCommand extends CoreCommand {
|
||||
@Description("{@@mv-core.gamerule.set.value.description}")
|
||||
GameRuleValue gameRuleValue,
|
||||
|
||||
@Flags("resolve=issuerAware")
|
||||
@Flags("resolve=issuerAware,maxArgForAware=2")
|
||||
@Syntax("[World or *]")
|
||||
@Description("{@@mv-core.gamerule.set.world.description}")
|
||||
LoadedMultiverseWorld[] worlds) {
|
||||
@@ -114,7 +114,7 @@ class GameruleCommand extends CoreCommand {
|
||||
@Description("{@@mv-core.gamerule.reset.gamerule.description}")
|
||||
GameRule gamerule,
|
||||
|
||||
@Flags("resolve=issuerAware")
|
||||
@Flags("resolve=issuerAware,maxArgForAware=1")
|
||||
@Syntax("[World or *]")
|
||||
@Description("{@@mv-core.gamerule.reset.world.description}")
|
||||
LoadedMultiverseWorld[] worlds) {
|
||||
@@ -152,7 +152,7 @@ class GameruleCommand extends CoreCommand {
|
||||
void onGameruleListCommand(
|
||||
MVCommandIssuer issuer,
|
||||
|
||||
@Flags("resolve=issuerAware")
|
||||
@Flags("resolve=issuerAware,maxArgForAware=0")
|
||||
@Syntax("<world>")
|
||||
@Description("{@@mv-core.gamerule.list.description.world}")
|
||||
LoadedMultiverseWorld world,
|
||||
|
||||
@@ -61,8 +61,8 @@ class InfoCommand extends CoreCommand {
|
||||
public void onInfoCommand(
|
||||
MVCommandIssuer issuer,
|
||||
|
||||
@Flags("resolve=issuerAware")
|
||||
@Syntax("<world>")
|
||||
@Flags("resolve=issuerAware,maxArgForAware=0")
|
||||
@Syntax("[world]")
|
||||
@Description("{@@mv-core.info.description.world}")
|
||||
LoadedMultiverseWorld world,
|
||||
|
||||
|
||||
@@ -49,12 +49,12 @@ class ModifyCommand extends CoreCommand {
|
||||
"@propsmodifyaction:notByIssuerForArg=arg1|@mvworldpropsname:byIssuerForArg=arg1 " +
|
||||
"@mvworldpropsname:notByIssuerForArg=arg1|@mvworldpropsvalue:byIssuerForArg=arg1 " +
|
||||
"@mvworldpropsvalue:notByIssuerForArg=arg1")
|
||||
@Syntax("[world] <set|add|remove|reset> <property> <value>")
|
||||
@Syntax("[world] <set|add|remove|reset> <property> [value]")
|
||||
@Description("{@@mv-core.modify.description}")
|
||||
void onModifyCommand(// SUPPRESS CHECKSTYLE: ParameterNumber
|
||||
MVCommandIssuer issuer,
|
||||
|
||||
@Flags("resolve=issuerAware")
|
||||
@Flags("resolve=issuerAware,maxArgForAware=3")
|
||||
@Syntax("[world]")
|
||||
@Description("{@@mv-core.modify.world.description}")
|
||||
@NotNull MultiverseWorldValue worldValue,
|
||||
@@ -94,28 +94,28 @@ class ModifyCommand extends CoreCommand {
|
||||
}
|
||||
|
||||
StringPropertyHandle worldPropertyHandle = world.getStringPropertyHandle();
|
||||
worldPropertyHandle.modifyPropertyString(issuer.getIssuer(), propertyName, propertyValue, action).onSuccess(ignore -> {
|
||||
issuer.sendMessage(MVCorei18n.MODIFY_SUCCESS,
|
||||
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()));
|
||||
worldManager.saveWorldsConfig();
|
||||
}).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()));
|
||||
}
|
||||
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()));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ final class PurgeAllEntitiesCommand extends CoreCommand {
|
||||
void onPurgeAllEntitiesCommand(
|
||||
MVCommandIssuer issuer,
|
||||
|
||||
@Flags("resolve=issuerAware")
|
||||
@Flags("resolve=issuerAware,maxArgForAware=1")
|
||||
@Syntax("[world]")
|
||||
LoadedMultiverseWorld world,
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ final class PurgeEntitiesCommand extends CoreCommand {
|
||||
void onPurgeEntityCommand(
|
||||
MVCommandIssuer issuer,
|
||||
|
||||
@Flags("resolve=issuerAware")
|
||||
@Flags("resolve=issuerAware,maxArgForAware=0")
|
||||
@Syntax("[world]")
|
||||
LoadedMultiverseWorld world
|
||||
) {
|
||||
|
||||
@@ -63,7 +63,7 @@ class SetSpawnCommand extends CoreCommand {
|
||||
|
||||
worldManager.getLoadedWorld(location.getWorld())
|
||||
.peek(mvWorld -> mvWorld.setSpawnLocation(location)
|
||||
.flatMap(ignore -> worldManager.saveWorldsConfig())
|
||||
.andThenTry(worldManager::saveWorldsConfig)
|
||||
.onSuccess(ignore -> issuer.sendMessage(MVCorei18n.SETSPAWN_SUCCESS,
|
||||
Replace.WORLD.with(mvWorld.getName()),
|
||||
Replace.LOCATION.with(prettyLocation(location))))
|
||||
|
||||
@@ -119,7 +119,7 @@ final class SpawnCommand extends CoreCommand {
|
||||
? Message.of(MVCorei18n.GENERIC_YOU)
|
||||
: Message.of(entity.getName())),
|
||||
Replace.WORLD.with(mvWorld.getName()),
|
||||
Replace.REASON.with(failure.getFirst().getFailureMessage())));
|
||||
Replace.REASON.with(failure.get(0).getFailureMessage())));
|
||||
}
|
||||
|
||||
private void handleMultiTeleport(MVCommandIssuer issuer, LoadedMultiverseWorld mvWorld,
|
||||
|
||||
@@ -19,7 +19,10 @@ import org.jvnet.hk2.annotations.Service;
|
||||
|
||||
import org.mvplugins.multiverse.core.command.MVCommandIssuer;
|
||||
import org.mvplugins.multiverse.core.command.context.issueraware.PlayerArrayValue;
|
||||
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.command.flags.UnsafeFlags;
|
||||
import org.mvplugins.multiverse.core.config.CoreConfig;
|
||||
import org.mvplugins.multiverse.core.destination.DestinationInstance;
|
||||
@@ -35,14 +38,14 @@ final class TeleportCommand extends CoreCommand {
|
||||
private final CoreConfig config;
|
||||
private final CorePermissionsChecker permissionsChecker;
|
||||
private final AsyncSafetyTeleporter safetyTeleporter;
|
||||
private final UnsafeFlags flags;
|
||||
private final Flags flags;
|
||||
|
||||
@Inject
|
||||
TeleportCommand(
|
||||
@NotNull CoreConfig config,
|
||||
@NotNull CorePermissionsChecker permissionsChecker,
|
||||
@NotNull AsyncSafetyTeleporter safetyTeleporter,
|
||||
@NotNull UnsafeFlags flags
|
||||
@NotNull Flags flags
|
||||
) {
|
||||
this.config = config;
|
||||
this.permissionsChecker = permissionsChecker;
|
||||
@@ -54,14 +57,14 @@ final class TeleportCommand extends CoreCommand {
|
||||
@Subcommand("teleport|tp")
|
||||
@CommandPermission("@mvteleport")
|
||||
@CommandCompletion("@playersarray:checkPermissions=@mvteleportother|@destinations:byIssuerForArg=arg1 "
|
||||
+ "@destinations:notByIssuerForArg=arg1|@flags:byIssuerForArg=arg1,groupName=" + UnsafeFlags.NAME + " "
|
||||
+ "@flags:notByIssuerForArg=arg1,groupName=" + UnsafeFlags.NAME)
|
||||
+ "@destinations:notByIssuerForArg=arg1|@flags:byIssuerForArg=arg1,groupName=" + Flags.NAME + " "
|
||||
+ "@flags:notByIssuerForArg=arg1,groupName=" + Flags.NAME)
|
||||
@Syntax("[player] <destination> [--unsafe]")
|
||||
@Description("{@@mv-core.teleport.description}")
|
||||
void onTeleportCommand(
|
||||
MVCommandIssuer issuer,
|
||||
|
||||
@Flags("resolve=issuerAware")
|
||||
@co.aikar.commands.annotation.Flags("resolve=issuerAware,maxArgForAware=1")
|
||||
@Syntax("[player]")
|
||||
@Description("{@@mv-core.teleport.player.description}")
|
||||
PlayerArrayValue playersValue,
|
||||
@@ -103,16 +106,21 @@ final class TeleportCommand extends CoreCommand {
|
||||
.checkSafety(!parsedFlags.hasFlag(flags.unsafe) && destination.checkTeleportSafety())
|
||||
.passengerMode(config.getPassengerMode())
|
||||
.teleportSingle(player)
|
||||
.onSuccess(() -> issuer.sendInfo(MVCorei18n.TELEPORT_SUCCESS,
|
||||
Replace.PLAYER.with(getYouOrName(issuer, player)),
|
||||
Replace.DESTINATION.with(destination.toString())))
|
||||
.onSuccess(() -> {
|
||||
if (parsedFlags.hasFlag(flags.silent)) {
|
||||
return;
|
||||
}
|
||||
issuer.sendInfo(MVCorei18n.TELEPORT_SUCCESS,
|
||||
Replace.PLAYER.with(getYouOrName(issuer, player)),
|
||||
Replace.DESTINATION.with(destination.getDisplayMessage()));
|
||||
})
|
||||
.onFailureCount(reasonsCountMap -> {
|
||||
for (var entry : reasonsCountMap.entrySet()) {
|
||||
Logging.finer("Failed to teleport %s players to %s: %s",
|
||||
entry.getValue(), destination, entry.getKey());
|
||||
issuer.sendError(MVCorei18n.TELEPORT_FAILED,
|
||||
Replace.PLAYER.with(player.getName()),
|
||||
Replace.DESTINATION.with(destination.toString()),
|
||||
Replace.DESTINATION.with(destination.getDisplayMessage()),
|
||||
Replace.REASON.with(Message.of(entry.getKey())));
|
||||
}
|
||||
});
|
||||
@@ -136,18 +144,38 @@ final class TeleportCommand extends CoreCommand {
|
||||
.checkSafety(!parsedFlags.hasFlag(flags.unsafe) && destination.checkTeleportSafety())
|
||||
.passengerMode(config.getPassengerMode())
|
||||
.teleport(List.of(players))
|
||||
.onSuccessCount(successCount -> issuer.sendInfo(MVCorei18n.TELEPORT_SUCCESS,
|
||||
Replace.PLAYER.with(successCount + " players"),
|
||||
Replace.DESTINATION.with(destination.toString())))
|
||||
.onSuccessCount(successCount -> {
|
||||
if (parsedFlags.hasFlag(flags.silent)) {
|
||||
return;
|
||||
}
|
||||
issuer.sendInfo(MVCorei18n.TELEPORT_SUCCESS,
|
||||
Replace.PLAYER.with(successCount + " players"),
|
||||
Replace.DESTINATION.with(destination.getDisplayMessage()));
|
||||
})
|
||||
.onFailureCount(reasonsCountMap -> {
|
||||
for (var entry : reasonsCountMap.entrySet()) {
|
||||
Logging.finer("Failed to teleport %s players to %s: %s",
|
||||
entry.getValue(), destination, entry.getKey());
|
||||
issuer.sendError(MVCorei18n.TELEPORT_FAILED,
|
||||
Replace.PLAYER.with(entry.getValue() + " players"),
|
||||
Replace.DESTINATION.with(destination.toString()),
|
||||
Replace.DESTINATION.with(destination.getDisplayMessage()),
|
||||
Replace.REASON.with(Message.of(entry.getKey())));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Service
|
||||
private static final class Flags extends UnsafeFlags {
|
||||
|
||||
private static final String NAME = "mvteleport";
|
||||
|
||||
@Inject
|
||||
private Flags(@NotNull CommandFlagsManager flagsManager) {
|
||||
super(NAME, flagsManager);
|
||||
}
|
||||
|
||||
private final CommandFlag silent = flag(CommandFlag.builder("--silent")
|
||||
.addAlias("-s")
|
||||
.build());
|
||||
}
|
||||
}
|
||||
@@ -74,12 +74,12 @@ class WhoCommand extends CoreCommand {
|
||||
@Subcommand("who")
|
||||
@CommandPermission("multiverse.core.list.who")
|
||||
@CommandCompletion("@mvworlds:scope=both @flags:groupName=" + PageFilterFlags.NAME)
|
||||
@Syntax("<world> [--page <page>] [--filter <filter>]")
|
||||
@Syntax("[world] [--page <page>] [--filter <filter>]")
|
||||
@Description("{@@mv-core.who.description}")
|
||||
void onWhoCommand(
|
||||
MVCommandIssuer issuer,
|
||||
@Flags("resolve=issuerAware")
|
||||
@Syntax("<world>")
|
||||
@Flags("resolve=issuerAware,maxArgForAware=0")
|
||||
@Syntax("[world]")
|
||||
@Description("{@@mv-core.who.world.description}")
|
||||
LoadedMultiverseWorld inputtedWorld,
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ final class WorldBorderCommand extends CoreCommand {
|
||||
@Syntax("[time]")
|
||||
int time,
|
||||
|
||||
@Flags("resolve=issuerAware")
|
||||
@Flags("resolve=issuerAware,maxArgForAware=0")
|
||||
@Syntax("[world]")
|
||||
LoadedMultiverseWorld world
|
||||
) {
|
||||
@@ -54,7 +54,7 @@ final class WorldBorderCommand extends CoreCommand {
|
||||
@Syntax("[z]")
|
||||
double z,
|
||||
|
||||
@Flags("resolve=issuerAware")
|
||||
@Flags("resolve=issuerAware,maxArgForAware=0")
|
||||
@Syntax("[world]")
|
||||
LoadedMultiverseWorld world
|
||||
) {
|
||||
@@ -79,7 +79,7 @@ final class WorldBorderCommand extends CoreCommand {
|
||||
@Syntax("<damage>")
|
||||
double damage,
|
||||
|
||||
@Flags("resolve=issuerAware")
|
||||
@Flags("resolve=issuerAware,maxArgForAware=0")
|
||||
@Syntax("[world]")
|
||||
LoadedMultiverseWorld world
|
||||
) {
|
||||
@@ -103,7 +103,7 @@ final class WorldBorderCommand extends CoreCommand {
|
||||
@Syntax("<distance>")
|
||||
double distance,
|
||||
|
||||
@Flags("resolve=issuerAware")
|
||||
@Flags("resolve=issuerAware,maxArgForAware=0")
|
||||
@Syntax("[world]")
|
||||
LoadedMultiverseWorld world
|
||||
) {
|
||||
@@ -124,7 +124,7 @@ final class WorldBorderCommand extends CoreCommand {
|
||||
void onWorldBorderGet(
|
||||
MVCommandIssuer issuer,
|
||||
|
||||
@Flags("resolve=issuerAware")
|
||||
@Flags("resolve=issuerAware,maxArgForAware=0")
|
||||
@Syntax("[world]")
|
||||
LoadedMultiverseWorld world
|
||||
) {
|
||||
@@ -147,7 +147,7 @@ final class WorldBorderCommand extends CoreCommand {
|
||||
@Syntax("[time]")
|
||||
int time,
|
||||
|
||||
@Flags("resolve=issuerAware")
|
||||
@Flags("resolve=issuerAware,maxArgForAware=0")
|
||||
@Syntax("[world]")
|
||||
LoadedMultiverseWorld world
|
||||
) {
|
||||
@@ -179,7 +179,7 @@ final class WorldBorderCommand extends CoreCommand {
|
||||
@Syntax("<distance>")
|
||||
int distance,
|
||||
|
||||
@Flags("resolve=issuerAware")
|
||||
@Flags("resolve=issuerAware,maxArgForAware=0")
|
||||
@Syntax("[world]")
|
||||
LoadedMultiverseWorld world
|
||||
) {
|
||||
@@ -203,7 +203,7 @@ final class WorldBorderCommand extends CoreCommand {
|
||||
@Syntax("<time>")
|
||||
int time,
|
||||
|
||||
@Flags("resolve=issuerAware")
|
||||
@Flags("resolve=issuerAware,maxArgForAware=0")
|
||||
@Syntax("[world]")
|
||||
LoadedMultiverseWorld world
|
||||
) {
|
||||
|
||||
@@ -8,16 +8,13 @@ import java.util.Locale;
|
||||
import com.dumptruckman.minecraft.util.Logging;
|
||||
import io.vavr.control.Try;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.inject.Provider;
|
||||
import org.bukkit.configuration.file.FileConfiguration;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.plugin.PluginManager;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
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.command.queue.ConfirmMode;
|
||||
import org.mvplugins.multiverse.core.config.handle.CommentedConfigurationHandle;
|
||||
import org.mvplugins.multiverse.core.config.handle.StringPropertyHandle;
|
||||
@@ -28,7 +25,6 @@ import org.mvplugins.multiverse.core.config.migration.action.InvertBoolMigratorA
|
||||
import org.mvplugins.multiverse.core.config.migration.action.MoveMigratorAction;
|
||||
import org.mvplugins.multiverse.core.config.migration.VersionMigrator;
|
||||
import org.mvplugins.multiverse.core.config.migration.action.SetMigratorAction;
|
||||
import org.mvplugins.multiverse.core.destination.DestinationsProvider;
|
||||
import org.mvplugins.multiverse.core.teleportation.PassengerMode;
|
||||
import org.mvplugins.multiverse.core.teleportation.PassengerModes;
|
||||
import org.mvplugins.multiverse.core.world.helpers.DimensionFinder.DimensionFormat;
|
||||
@@ -539,6 +535,16 @@ public final class CoreConfig {
|
||||
return configHandle.get(configNodes.resolveAliasName);
|
||||
}
|
||||
|
||||
@ApiStatus.AvailableSince("5.4")
|
||||
public Try<Void> setSimplifiedDestinationTabCompletion(boolean simplifiedDestinationTabCompletion) {
|
||||
return configHandle.set(configNodes.simplifiedDestinationTabCompletion, simplifiedDestinationTabCompletion);
|
||||
}
|
||||
|
||||
@ApiStatus.AvailableSince("5.4")
|
||||
public boolean getSimplifiedDestinationTabCompletion() {
|
||||
return configHandle.get(configNodes.simplifiedDestinationTabCompletion);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
|
||||
@@ -4,6 +4,7 @@ import com.dumptruckman.minecraft.util.Logging;
|
||||
import io.vavr.control.Try;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.inject.Provider;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.plugin.PluginManager;
|
||||
@@ -76,13 +77,10 @@ final class CoreConfigNodes {
|
||||
.comment("# █░▀░█ █▄█ █▄▄ ░█░ █ ▀▄▀ ██▄ █▀▄ ▄█ ██▄ █▄▄ █▄█ █▀▄ ██▄ #")
|
||||
.comment("# #")
|
||||
.comment("# #")
|
||||
.comment("# WIKI: https://github.com/Multiverse/Multiverse-Core/wiki #")
|
||||
.comment("# WIKI: https://mvplugins.org/core/ #")
|
||||
.comment("# DISCORD: https://discord.gg/NZtfKky #")
|
||||
.comment("# BUG REPORTS: https://github.com/Multiverse/Multiverse-Core/issues #")
|
||||
.comment("# #")
|
||||
.comment("# #")
|
||||
.comment("# Each option in this file is documented and explained here: #")
|
||||
.comment("# ==> https://github.com/Multiverse/Multiverse-Core/wiki/config.yml #")
|
||||
.comment("# DONATE: https://github.com/sponsors/Multiverse #")
|
||||
.comment("# #")
|
||||
.comment("# #")
|
||||
.comment("# New options are added to this file automatically. If you manually made changes #")
|
||||
@@ -142,8 +140,12 @@ final class CoreConfigNodes {
|
||||
.comment("Sets whether Multiverse will apply the world's entity `tick-rate` config in worlds.yml.")
|
||||
.comment("If disabled, the `tick-rate` config in worlds.yml will be ignored.")
|
||||
.comment("Disable this if you want paper-world.yml or another plugin to handle entity spawn rate per world.")
|
||||
.comment("!!!NOTE: This will only apply after a server restart!")
|
||||
.defaultValue(true)
|
||||
.name("apply-entity-spawn-rate")
|
||||
.onChange(((sender, oldValue, newValue) ->
|
||||
sender.sendMessage(ChatColor.YELLOW + "'apply-entity-spawn-rate' config option will only take " +
|
||||
"full effect after a server restart.")))
|
||||
.build());
|
||||
|
||||
final ConfigNode<Boolean> applyEntitySpawnLimit = node(ConfigNode.builder("world.apply-entity-spawn-limit", Boolean.class)
|
||||
@@ -151,8 +153,12 @@ final class CoreConfigNodes {
|
||||
.comment("Sets whether Multiverse will apply the world's entity `spawn-limit` config when a world is loaded.")
|
||||
.comment("If disabled, the `spawn-limit` config in worlds.yml will be ignored.")
|
||||
.comment("Disable this if you want paper-world.yml or another plugin to handle entity limits per world.")
|
||||
.comment("!!!NOTE: This will only apply after a server restart!")
|
||||
.defaultValue(true)
|
||||
.name("apply-entity-spawn-limit")
|
||||
.onChange(((sender, oldValue, newValue) ->
|
||||
sender.sendMessage(ChatColor.YELLOW + "'apply-entity-spawn-limit' config option will only take" +
|
||||
" full effect after a server restart.")))
|
||||
.build());
|
||||
|
||||
final ConfigNode<Boolean> autoPurgeEntities = node(ConfigNode.builder("world.auto-purge-entities", Boolean.class)
|
||||
@@ -205,7 +211,8 @@ final class CoreConfigNodes {
|
||||
final ConfigNode<PassengerModes> passengerMode = node(ConfigNode.builder("teleport.passenger-mode", PassengerModes.class)
|
||||
.comment("")
|
||||
.comment("Configures how passengers and vehicles are handled when an entity is teleported.")
|
||||
.comment(" default: Server will handle passengers and vehicles, this usually means entities will not be teleported to a different world if they have passengers.")
|
||||
.comment(" default: Server will handle passengers and vehicles. On papermc 1.21.10+, all will be retained by default.")
|
||||
.comment(" On older versions, all will be dismounted if world changes.")
|
||||
.comment(" dismount_passengers: Passengers will be removed from the parent entity before the teleport.")
|
||||
.comment(" dismount_vehicle: Vehicle will be removed and from the parent entity before the teleport.")
|
||||
.comment(" dismount_all: All passengers and vehicles will be removed from the parent entity before the teleport.")
|
||||
@@ -388,7 +395,7 @@ final class CoreConfigNodes {
|
||||
.comment("This config option defines the default language Multiverse should use.")
|
||||
.defaultValue(Locale.ENGLISH)
|
||||
.name("default-locale")
|
||||
.onSetValue((oldValue, newValue) ->
|
||||
.onLoadAndChange((oldValue, newValue) ->
|
||||
commandManager.get().getLocales().setDefaultLocale(newValue))
|
||||
.build());
|
||||
|
||||
@@ -398,9 +405,8 @@ final class CoreConfigNodes {
|
||||
.comment("If the player's language does not have a translation, it will use the default language set above instead.")
|
||||
.defaultValue(true)
|
||||
.name("per-player-locale")
|
||||
.onSetValue((oldValue, newValue) -> {
|
||||
commandManager.get().usePerIssuerLocale(newValue);
|
||||
})
|
||||
.onLoadAndChange((oldValue, newValue) ->
|
||||
commandManager.get().usePerIssuerLocale(newValue))
|
||||
.build());
|
||||
|
||||
private final ConfigHeaderNode commandHeader = node(ConfigHeaderNode.builder("command")
|
||||
@@ -416,6 +422,15 @@ final class CoreConfigNodes {
|
||||
.name("resolve-alias-name")
|
||||
.build());
|
||||
|
||||
final ConfigNode<Boolean> simplifiedDestinationTabCompletion = node(ConfigNode.builder("command.simplified-destination-tab-completion", Boolean.class)
|
||||
.comment("")
|
||||
.comment("If this is set to true, Multiverse will only suggest simple destination formats in tab completion.")
|
||||
.comment("This means only world names will be suggested without other destination type such as `e:worldname:x,y,z` or `p:playername`.")
|
||||
.comment("Note: This DOES NOT prevent players from using the destinations, as that is controlled by permissions.")
|
||||
.defaultValue(false)
|
||||
.name("simplified-destination-tab-completion")
|
||||
.build());
|
||||
|
||||
final ConfigNode<ConfirmMode> confirmMode = node(ConfigNode.builder("command.confirm-mode", ConfirmMode.class)
|
||||
.comment("")
|
||||
.comment("This config option defines whether `/mv confirm` is needed before running a DANGEROUS action.")
|
||||
@@ -470,8 +485,11 @@ final class CoreConfigNodes {
|
||||
.comment("")
|
||||
.comment("This config option defines the priority for the PlayerPortalEvent.")
|
||||
.name("event-priority-player-portal")
|
||||
.onSetValue((oldValue, newValue) ->
|
||||
.onLoadAndChange((oldValue, newValue) ->
|
||||
eventPriorityMapper.get().setPriority("mvcore-player-portal", newValue))
|
||||
.onChange(((sender, oldValue, newValue) ->
|
||||
sender.sendMessage(ChatColor.YELLOW + "'event-priority.player-portal' config option will only take " +
|
||||
"full effect after a server restart.")))
|
||||
.build());
|
||||
|
||||
final ConfigNode<EventPriority> eventPriorityPlayerRespawn = node(ConfigNode.builder("event-priority.player-respawn", EventPriority.class)
|
||||
@@ -479,16 +497,23 @@ final class CoreConfigNodes {
|
||||
.name("event-priority-player-respawn")
|
||||
.comment("")
|
||||
.comment("This config option defines the priority for the PlayerRespawnEvent.")
|
||||
.onSetValue((oldValue, newValue) ->
|
||||
.onLoadAndChange((oldValue, newValue) ->
|
||||
eventPriorityMapper.get().setPriority("mvcore-player-respawn", newValue))
|
||||
.onChange((sender, oldValue, newValue) ->
|
||||
sender.sendMessage(ChatColor.YELLOW + "'event-priority.player-respawn' config option will only take " +
|
||||
"full effect after a server restart."))
|
||||
.build());
|
||||
|
||||
final ConfigNode<EventPriority> eventPriorityPlayerSpawnLocation = node(ConfigNode.builder("event-priority.player-spawn-location", EventPriority.class)
|
||||
.defaultValue(EventPriority.NORMAL)
|
||||
.comment("")
|
||||
.comment("This config option defines the priority for the PlayerSpawnLocationEvent.")
|
||||
.name("event-priority-player-spawn-location").onSetValue((oldValue, newValue) ->
|
||||
.name("event-priority-player-spawn-location")
|
||||
.onLoadAndChange((oldValue, newValue) ->
|
||||
eventPriorityMapper.get().setPriority("mvcore-player-spawn-location", newValue))
|
||||
.onChange((sender, oldValue, newValue) ->
|
||||
sender.sendMessage(ChatColor.YELLOW + "'event-priority.player-spawn-location' config option will only take " +
|
||||
"full effect after a server restart."))
|
||||
.build());
|
||||
|
||||
final ConfigNode<EventPriority> eventPriorityPlayerTeleport = node(ConfigNode.builder("event-priority.player-teleport", EventPriority.class)
|
||||
@@ -496,8 +521,11 @@ final class CoreConfigNodes {
|
||||
.name("event-priority-player-teleport")
|
||||
.comment("")
|
||||
.comment("This config option defines the priority for the PlayerTeleportEvent.")
|
||||
.onSetValue((oldValue, newValue) ->
|
||||
.onLoadAndChange((oldValue, newValue) ->
|
||||
eventPriorityMapper.get().setPriority("mvcore-player-teleport", newValue))
|
||||
.onChange((sender, oldValue, newValue) ->
|
||||
sender.sendMessage(ChatColor.YELLOW + "'event-priority.player-teleport' config option will only take " +
|
||||
"full effect after a server restart."))
|
||||
.build());
|
||||
|
||||
private final ConfigHeaderNode miscHeader = node(ConfigHeaderNode.builder("misc")
|
||||
@@ -510,6 +538,9 @@ final class CoreConfigNodes {
|
||||
.comment("Note: this config option needs a server restart to take effect.")
|
||||
.defaultValue("bukkit.yml")
|
||||
.name("bukkit-yml-path")
|
||||
.onChange(((sender, oldValue, newValue) ->
|
||||
sender.sendMessage(ChatColor.YELLOW + "'bukkit-yml-path' config option will only take " +
|
||||
"full effect after a server restart.")))
|
||||
.build());
|
||||
|
||||
final ConfigNode<String> serverPropertiesPath = node(ConfigNode.builder("misc.server-properties-path", String.class)
|
||||
@@ -518,6 +549,9 @@ final class CoreConfigNodes {
|
||||
.comment("Note: this config option needs a server restart to take effect.")
|
||||
.defaultValue("server.properties")
|
||||
.name("server-properties-path")
|
||||
.onChange(((sender, oldValue, newValue) ->
|
||||
sender.sendMessage(ChatColor.YELLOW + "'server-properties-path' config option will only take " +
|
||||
"full effect after a server restart.")))
|
||||
.build());
|
||||
|
||||
final ConfigNode<Boolean> autoDetectGeneratorPlugins = node(ConfigNode.builder("misc.auto-detect-generator-plugins", Boolean.class)
|
||||
@@ -544,7 +578,7 @@ final class CoreConfigNodes {
|
||||
.validator(value -> (value < 0 || value > 3)
|
||||
? Try.failure(new MultiverseException("Debug level must be between 0 and 3."))
|
||||
: Try.success(null))
|
||||
.onSetValue((oldValue, newValue) -> {
|
||||
.onLoadAndChange((oldValue, newValue) -> {
|
||||
if (newValue != Logging.getDebugLevel()) {
|
||||
Logging.setDebugLevel(newValue);
|
||||
pluginManager.callEvent(new MVDebugModeEvent(newValue));
|
||||
@@ -558,7 +592,7 @@ final class CoreConfigNodes {
|
||||
.comment("This will only work if the above 'global-debug' is set to 1 or more.")
|
||||
.defaultValue(false)
|
||||
.name("debug-permissions")
|
||||
.onSetValue((oldValue, newValue) -> PermissionUtils.setDebugPermissions(newValue))
|
||||
.onLoadAndChange((oldValue, newValue) -> PermissionUtils.setDebugPermissions(newValue))
|
||||
.build());
|
||||
|
||||
final ConfigNode<Boolean> silentStart = node(ConfigNode.builder("misc.silent-start", Boolean.class)
|
||||
@@ -566,12 +600,13 @@ final class CoreConfigNodes {
|
||||
.comment("If true, the startup console messages will no longer show.")
|
||||
.defaultValue(false)
|
||||
.name("silent-start")
|
||||
.onSetValue((oldValue, newValue) -> Logging.setShowingConfig(!newValue))
|
||||
.onLoadAndChange((oldValue, newValue) -> Logging.setShowingConfig(!newValue))
|
||||
.build());
|
||||
|
||||
final ConfigNode<Boolean> showDonationMessage = node(ConfigNode.builder("misc.show-donation-message", Boolean.class)
|
||||
.comment("")
|
||||
.comment("If you don't want to donate, you can set this to false and Multiverse will stop nagging you.")
|
||||
.comment("But if you want to donate, here is our Github Sponsors page: https://github.com/sponsors/Multiverse")
|
||||
.defaultValue(true)
|
||||
.name("show-donation-message")
|
||||
.build());
|
||||
|
||||
+24
-3
@@ -3,19 +3,22 @@ package org.mvplugins.multiverse.core.config.handle;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import com.dumptruckman.minecraft.util.Logging;
|
||||
import io.vavr.control.Option;
|
||||
import io.vavr.control.Try;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import org.mvplugins.multiverse.core.config.migration.ConfigMigrator;
|
||||
import org.mvplugins.multiverse.core.config.node.ListValueNode;
|
||||
import org.mvplugins.multiverse.core.config.node.Node;
|
||||
import org.mvplugins.multiverse.core.config.node.NodeGroup;
|
||||
import org.mvplugins.multiverse.core.config.node.ValueNode;
|
||||
|
||||
@@ -84,7 +87,8 @@ public abstract class BaseConfigurationHandle<C extends ConfigurationSection> {
|
||||
});
|
||||
|
||||
nodeValueMap.forEach((valueNode, value) -> {
|
||||
valueNode.onSetValue(value, value);
|
||||
valueNode.onLoad(value);
|
||||
valueNode.onLoadAndChange(Bukkit.getConsoleSender(), null, value);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -156,10 +160,27 @@ public abstract class BaseConfigurationHandle<C extends ConfigurationSection> {
|
||||
* @return Empty try if the value was set, try containing an error otherwise.
|
||||
*/
|
||||
public <T> Try<Void> set(@NotNull ValueNode<T> node, T value) {
|
||||
return set(Bukkit.getConsoleSender(), node, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of a node, if the validator is not null, it will be tested first.
|
||||
*
|
||||
* @param sender The sender who triggered the change.
|
||||
* @param node The node to set the value of.
|
||||
* @param value The value to set.
|
||||
* @param <T> The type of the node value.
|
||||
* @return Empty try if the value was set, try containing an error otherwise.
|
||||
*
|
||||
* @since 5.4
|
||||
*/
|
||||
@ApiStatus.AvailableSince("5.4")
|
||||
public <T> Try<Void> set(@NotNull CommandSender sender, @NotNull ValueNode<T> node, T value) {
|
||||
return node.validate(value).map(ignore -> {
|
||||
T oldValue = get(node);
|
||||
nodeValueMap.put(node, value);
|
||||
node.onSetValue(oldValue, get(node));
|
||||
node.onLoadAndChange(sender, oldValue, value);
|
||||
node.onChange(sender, oldValue, value);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -226,7 +226,7 @@ public class StringPropertyHandle {
|
||||
public Try<Void> setPropertyString(@NotNull CommandSender sender, @Nullable String name, @Nullable String value) {
|
||||
return findNode(name, ValueNode.class)
|
||||
.flatMap(node -> node.parseFromString(sender, value)
|
||||
.flatMap(parsedValue -> handle.set(node, parsedValue)));
|
||||
.flatMap(parsedValue -> handle.set(sender, node, parsedValue)));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -14,6 +14,9 @@ import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import org.mvplugins.multiverse.core.config.node.functions.NodeChangeCallback;
|
||||
import org.mvplugins.multiverse.core.config.node.functions.NodeValueCallback;
|
||||
import org.mvplugins.multiverse.core.config.node.functions.SenderNodeChangeCallback;
|
||||
import org.mvplugins.multiverse.core.config.node.functions.SenderNodeStringParser;
|
||||
import org.mvplugins.multiverse.core.config.node.functions.SenderNodeSuggester;
|
||||
import org.mvplugins.multiverse.core.config.node.serializer.DefaultSerializerProvider;
|
||||
@@ -53,7 +56,9 @@ public class ConfigNode<T> extends ConfigHeaderNode implements ValueNode<T> {
|
||||
protected @Nullable NodeStringParser<T> stringParser;
|
||||
protected @Nullable NodeSerializer<T> serializer;
|
||||
protected @Nullable Function<T, Try<Void>> validator;
|
||||
protected @Nullable BiConsumer<T, T> onSetValue;
|
||||
protected @Nullable NodeValueCallback<T> onLoad;
|
||||
protected @Nullable NodeChangeCallback<T> onLoadAndChange;
|
||||
protected @Nullable NodeChangeCallback<T> onChange;
|
||||
|
||||
protected ConfigNode(
|
||||
@NotNull String path,
|
||||
@@ -66,7 +71,9 @@ public class ConfigNode<T> extends ConfigHeaderNode implements ValueNode<T> {
|
||||
@Nullable NodeStringParser<T> stringParser,
|
||||
@Nullable NodeSerializer<T> serializer,
|
||||
@Nullable Function<T, Try<Void>> validator,
|
||||
@Nullable BiConsumer<T, T> onSetValue) {
|
||||
@Nullable NodeValueCallback<T> onLoad,
|
||||
@Nullable NodeChangeCallback<T> onLoadAndChange,
|
||||
@Nullable NodeChangeCallback<T> onChange) {
|
||||
super(path, comments);
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
@@ -82,7 +89,9 @@ public class ConfigNode<T> extends ConfigHeaderNode implements ValueNode<T> {
|
||||
? serializer
|
||||
: DefaultSerializerProvider.getDefaultSerializer(type);
|
||||
this.validator = validator;
|
||||
this.onSetValue = onSetValue;
|
||||
this.onLoad = onLoad;
|
||||
this.onLoadAndChange = onLoadAndChange;
|
||||
this.onChange = onChange;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -186,9 +195,29 @@ public class ConfigNode<T> extends ConfigHeaderNode implements ValueNode<T> {
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void onSetValue(@Nullable T oldValue, @Nullable T newValue) {
|
||||
if (onSetValue != null) {
|
||||
onSetValue.accept(oldValue, newValue);
|
||||
public void onLoad(@Nullable T value) {
|
||||
if (onLoad != null) {
|
||||
onLoad.run(value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void onLoadAndChange(@NotNull CommandSender sender, @Nullable T oldValue, @Nullable T newValue) {
|
||||
if (onLoadAndChange != null) {
|
||||
onLoadAndChange.run(sender, oldValue, newValue);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void onChange(@NotNull CommandSender sender, @Nullable T oldValue, @Nullable T newValue) {
|
||||
if (onChange != null) {
|
||||
onChange.run(sender, oldValue, newValue);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -208,7 +237,9 @@ public class ConfigNode<T> extends ConfigHeaderNode implements ValueNode<T> {
|
||||
protected @Nullable NodeStringParser<T> stringParser;
|
||||
protected @Nullable NodeSerializer<T> serializer;
|
||||
protected @Nullable Function<T, Try<Void>> validator;
|
||||
protected @Nullable BiConsumer<T, T> onSetValue;
|
||||
protected @Nullable NodeValueCallback<T> onLoad;
|
||||
protected @Nullable NodeChangeCallback<T> onLoadAndChange;
|
||||
protected @Nullable NodeChangeCallback<T> onChange;
|
||||
|
||||
/**
|
||||
* Creates a new builder.
|
||||
@@ -368,15 +399,83 @@ public class ConfigNode<T> extends ConfigHeaderNode implements ValueNode<T> {
|
||||
return self();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the action to be performed when the value is loaded.
|
||||
*
|
||||
* @param onLoad The action to be performed.
|
||||
* @return This builder.
|
||||
*/
|
||||
@ApiStatus.AvailableSince("5.4")
|
||||
public @NotNull B onLoad(@NotNull NodeValueCallback<T> onLoad) {
|
||||
this.onLoad = this.onLoad == null ? onLoad : this.onLoad.then(onLoad);
|
||||
return self();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the action to be performed when the value is loaded and changed.
|
||||
*
|
||||
* @param onLoadAndChange The action to be performed.
|
||||
* @return This builder.
|
||||
*
|
||||
* @since 5.4
|
||||
*/
|
||||
@ApiStatus.AvailableSince("5.4")
|
||||
public @NotNull B onLoadAndChange(@NotNull NodeChangeCallback<T> onLoadAndChange) {
|
||||
this.onLoadAndChange = this.onLoadAndChange == null ? onLoadAndChange : this.onLoadAndChange.then(onLoadAndChange);
|
||||
return self();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the action to be performed when the value is loaded and changed.
|
||||
*
|
||||
* @param onLoadAndChange The action to be performed.
|
||||
* @return This builder.
|
||||
*
|
||||
* @since 5.4
|
||||
*/
|
||||
@ApiStatus.AvailableSince("5.4")
|
||||
public @NotNull B onLoadAndChange(@NotNull SenderNodeChangeCallback<T> onLoadAndChange) {
|
||||
return onLoadAndChange((NodeChangeCallback<T>) onLoadAndChange);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the action to be performed when the value is changed.
|
||||
*
|
||||
* @param onChange The action to be performed.
|
||||
* @return This builder.
|
||||
*
|
||||
* @since 5.4
|
||||
*/
|
||||
@ApiStatus.AvailableSince("5.4")
|
||||
public @NotNull B onChange(@NotNull NodeChangeCallback<T> onChange) {
|
||||
this.onChange = this.onChange == null ? onChange : this.onChange.then(onChange);
|
||||
return self();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the action to be performed when the value is changed.
|
||||
*
|
||||
* @param onChange The action to be performed.
|
||||
* @return This builder.
|
||||
*
|
||||
* @since 5.4
|
||||
*/
|
||||
@ApiStatus.AvailableSince("5.4")
|
||||
public @NotNull B onChange(@NotNull SenderNodeChangeCallback<T> onChange) {
|
||||
return onChange((NodeChangeCallback<T>) onChange);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the action to be performed when the value is set.
|
||||
*
|
||||
* @param onSetValue The action to be performed.
|
||||
* @return This builder.
|
||||
*
|
||||
* @deprecated Use {@link #onLoadAndChange(NodeChangeCallback)} instead.
|
||||
*/
|
||||
@Deprecated(since = "5.4", forRemoval = true)
|
||||
public @NotNull B onSetValue(@NotNull BiConsumer<T, T> onSetValue) {
|
||||
this.onSetValue = this.onSetValue == null ? onSetValue : this.onSetValue.andThen(onSetValue);
|
||||
return self();
|
||||
return onLoadAndChange(onSetValue::accept);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -385,7 +484,8 @@ public class ConfigNode<T> extends ConfigHeaderNode implements ValueNode<T> {
|
||||
@Override
|
||||
public @NotNull ConfigNode<T> build() {
|
||||
return new ConfigNode<>(path, comments.toArray(new String[0]),
|
||||
name, type, aliases, defaultValue, suggester, stringParser, serializer, validator, onSetValue);
|
||||
name, type, aliases, defaultValue, suggester, stringParser, serializer, validator,
|
||||
onLoad, onLoadAndChange, onChange);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,8 @@ import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import org.mvplugins.multiverse.core.config.node.functions.DefaultStringParserProvider;
|
||||
import org.mvplugins.multiverse.core.config.node.functions.DefaultSuggesterProvider;
|
||||
import org.mvplugins.multiverse.core.config.node.functions.NodeChangeCallback;
|
||||
import org.mvplugins.multiverse.core.config.node.functions.NodeValueCallback;
|
||||
import org.mvplugins.multiverse.core.config.node.functions.NodeStringParser;
|
||||
import org.mvplugins.multiverse.core.config.node.functions.NodeSuggester;
|
||||
import org.mvplugins.multiverse.core.config.node.functions.SenderNodeStringParser;
|
||||
@@ -67,7 +69,9 @@ public class ListConfigNode<I> extends ConfigNode<List<I>> implements ListValueN
|
||||
@Nullable NodeStringParser<List<I>> stringParser,
|
||||
@Nullable NodeSerializer<List<I>> serializer,
|
||||
@Nullable Function<List<I>, Try<Void>> validator,
|
||||
@Nullable BiConsumer<List<I>, List<I>> onSetValue,
|
||||
@Nullable NodeValueCallback<List<I>> onLoad,
|
||||
@Nullable NodeChangeCallback<List<I>> onLoadAndChange,
|
||||
@Nullable NodeChangeCallback<List<I>> onChange,
|
||||
@NotNull Class<I> itemType,
|
||||
@Nullable NodeSuggester itemSuggester,
|
||||
@Nullable NodeStringParser<I> itemStringParser,
|
||||
@@ -75,7 +79,7 @@ public class ListConfigNode<I> extends ConfigNode<List<I>> implements ListValueN
|
||||
@Nullable Function<I, Try<Void>> itemValidator,
|
||||
@Nullable BiConsumer<I, I> onSetItemValue) {
|
||||
super(path, comments, name, type, aliases, defaultValueSupplier, suggester, stringParser, serializer,
|
||||
validator, onSetValue);
|
||||
validator, onLoad, onLoadAndChange, onChange);
|
||||
this.itemType = itemType;
|
||||
this.itemSuggester = itemSuggester != null
|
||||
? itemSuggester
|
||||
@@ -105,8 +109,8 @@ public class ListConfigNode<I> extends ConfigNode<List<I>> implements ListValueN
|
||||
if (this.itemSerializer != null && this.serializer == null) {
|
||||
setDefaultSerialiser();
|
||||
}
|
||||
if (this.onSetItemValue != null && this.onSetValue == null) {
|
||||
setDefaultOnSetValue();
|
||||
if (this.onSetItemValue != null && this.onLoadAndChange == null) {
|
||||
setDefaultOnLoadAndChange();
|
||||
}
|
||||
if (this.defaultValue == null) {
|
||||
this.defaultValue = ArrayList::new;
|
||||
@@ -168,8 +172,8 @@ public class ListConfigNode<I> extends ConfigNode<List<I>> implements ListValueN
|
||||
};
|
||||
}
|
||||
|
||||
private void setDefaultOnSetValue() {
|
||||
this.onSetValue = (oldValue, newValue) -> {
|
||||
private void setDefaultOnLoadAndChange() {
|
||||
this.onLoadAndChange = (oldValue, newValue) -> {
|
||||
if (oldValue != null) {
|
||||
oldValue.stream()
|
||||
.filter(value -> !newValue.contains(value))
|
||||
@@ -381,7 +385,9 @@ public class ListConfigNode<I> extends ConfigNode<List<I>> implements ListValueN
|
||||
stringParser,
|
||||
serializer,
|
||||
validator,
|
||||
onSetValue,
|
||||
onLoad,
|
||||
onLoadAndChange,
|
||||
onChange,
|
||||
itemType,
|
||||
itemSuggester,
|
||||
itemStringParser,
|
||||
|
||||
@@ -5,11 +5,13 @@ import java.util.Collection;
|
||||
import io.vavr.control.Option;
|
||||
import io.vavr.control.Try;
|
||||
import org.apache.logging.log4j.util.Strings;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import org.mvplugins.multiverse.core.config.handle.BaseConfigurationHandle;
|
||||
import org.mvplugins.multiverse.core.config.node.serializer.NodeSerializer;
|
||||
|
||||
public interface ValueNode<T> extends Node {
|
||||
@@ -108,11 +110,51 @@ public interface ValueNode<T> extends Node {
|
||||
*/
|
||||
Try<Void> validate(@Nullable T value);
|
||||
|
||||
/**
|
||||
* Called when the value of this node is loaded by {@link BaseConfigurationHandle#load()}.
|
||||
*
|
||||
* @param value The loaded value.
|
||||
*
|
||||
* @since 5.4
|
||||
*/
|
||||
@ApiStatus.AvailableSince("5.4")
|
||||
void onLoad(@Nullable T value);
|
||||
|
||||
/**
|
||||
* Called when the value of this node is loaded or changed.
|
||||
*
|
||||
* @param sender The sender who triggered the change. If triggered by loading or no target sender is specified,
|
||||
* it will be the console sender.
|
||||
* @param oldValue The old value, will always be null on load.
|
||||
* @param newValue The new value.
|
||||
*
|
||||
* @since 5.4
|
||||
*/
|
||||
@ApiStatus.AvailableSince("5.4")
|
||||
void onLoadAndChange(@NotNull CommandSender sender, @Nullable T oldValue, @Nullable T newValue);
|
||||
|
||||
/**
|
||||
* Called when the value of this node is changed by {@link BaseConfigurationHandle#set(ValueNode, Object)}.
|
||||
*
|
||||
* @param sender The sender who changed the value, or console sender if no target sender specified.
|
||||
* @param oldValue The old value.
|
||||
* @param newValue The new value.
|
||||
*
|
||||
* @since 5.4
|
||||
*/
|
||||
@ApiStatus.AvailableSince("5.4")
|
||||
void onChange(@NotNull CommandSender sender, @Nullable T oldValue, @Nullable T newValue);
|
||||
|
||||
/**
|
||||
* Called when the value of this node is set.
|
||||
*
|
||||
* @param oldValue The old value.
|
||||
* @param newValue The new value.
|
||||
*
|
||||
* @deprecated Use {@link #onLoadAndChange(CommandSender, Object, Object)} instead.
|
||||
*/
|
||||
void onSetValue(@Nullable T oldValue, @Nullable T newValue);
|
||||
@Deprecated(since = "5.4", forRemoval = true)
|
||||
default void onSetValue(@Nullable T oldValue, @Nullable T newValue) {
|
||||
onLoadAndChange(Bukkit.getConsoleSender(), oldValue, newValue);
|
||||
}
|
||||
}
|
||||
+3
-2
@@ -1,6 +1,7 @@
|
||||
package org.mvplugins.multiverse.core.config.node.functions;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
import co.aikar.commands.ACFUtil;
|
||||
@@ -41,13 +42,13 @@ public final class DefaultStringParserProvider {
|
||||
}
|
||||
|
||||
private static final NodeStringParser<Enum> ENUM_STRING_PARSER = (input, type) -> Try.of(
|
||||
() -> Enum.valueOf(type, input.toUpperCase()));
|
||||
() -> Enum.valueOf(type, input.toUpperCase(Locale.ENGLISH)));
|
||||
|
||||
private static final NodeStringParser<String> STRING_STRING_PARSER = (input, type) -> Try.of(
|
||||
() -> input);
|
||||
|
||||
private static final NodeStringParser<Boolean> BOOLEAN_STRING_PARSER = (input, type) -> Try.of(
|
||||
() -> switch (String.valueOf(input).toLowerCase()) {
|
||||
() -> switch (String.valueOf(input).toLowerCase(Locale.ENGLISH)) {
|
||||
case "t", "true", "on", "y", "yes", "1", "allow" -> true;
|
||||
case "f", "false", "off", "n", "no", "0", "deny" -> false;
|
||||
default -> throw new MultiverseException("Unable to convert '" + input + "' to boolean. Please use 'true' or 'false'");
|
||||
|
||||
+2
-1
@@ -3,6 +3,7 @@ package org.mvplugins.multiverse.core.config.node.functions;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
@@ -43,7 +44,7 @@ public final class DefaultSuggesterProvider {
|
||||
private static NodeSuggester enumSuggester(Class<?> clazz) {
|
||||
return input -> Arrays.stream(clazz.getEnumConstants())
|
||||
.map(Object::toString)
|
||||
.map(String::toLowerCase)
|
||||
.map(name -> name.toLowerCase(Locale.ENGLISH))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
package org.mvplugins.multiverse.core.config.node.functions;
|
||||
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
/**
|
||||
* A callback interface for handling changes to configuration node values.
|
||||
*
|
||||
* @param <T> The type of the node value.
|
||||
*
|
||||
* @since 5.4
|
||||
*/
|
||||
@ApiStatus.AvailableSince("5.4")
|
||||
@FunctionalInterface
|
||||
public interface NodeChangeCallback<T> {
|
||||
/**
|
||||
* Called when the value of a node changes.
|
||||
*
|
||||
* @param oldValue The old value of the node.
|
||||
* @param newValue The new value of the node.
|
||||
*
|
||||
* @since 5.4
|
||||
*/
|
||||
@ApiStatus.AvailableSince("5.4")
|
||||
void run(T oldValue, T newValue);
|
||||
|
||||
/**
|
||||
* Called when the value of a node changes, with the sender of the change.
|
||||
*
|
||||
* @param sender The sender of the change.
|
||||
* @param oldValue The old value of the node.
|
||||
* @param newValue The new value of the node.
|
||||
*
|
||||
* @since 5.4
|
||||
*/
|
||||
@ApiStatus.AvailableSince("5.4")
|
||||
default void run(CommandSender sender, T oldValue, T newValue) {
|
||||
run(oldValue, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Chains another {@link NodeChangeCallback} to be executed after this one.
|
||||
*
|
||||
* @param after The callback to execute after this one.
|
||||
* @return A new {@link NodeChangeCallback} that executes both callbacks in order.
|
||||
*
|
||||
* @since 5.4
|
||||
*/
|
||||
@ApiStatus.AvailableSince("5.4")
|
||||
default NodeChangeCallback<T> then(NodeChangeCallback<T> after) {
|
||||
return new SenderNodeChangeCallback<T>() {
|
||||
@Override
|
||||
public void run(CommandSender sender, T oldValue, T newValue) {
|
||||
NodeChangeCallback.this.run(sender, oldValue, newValue);
|
||||
after.run(sender, oldValue, newValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(T oldValue, T newValue) {
|
||||
NodeChangeCallback.this.run(oldValue, newValue);
|
||||
after.run(oldValue, newValue);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
package org.mvplugins.multiverse.core.config.node.functions;
|
||||
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
/**
|
||||
* Handler called when an action is performed on a node such as loading.
|
||||
*
|
||||
* @since 5.4
|
||||
*/
|
||||
@ApiStatus.AvailableSince("5.4")
|
||||
@FunctionalInterface
|
||||
public interface NodeValueCallback<T> {
|
||||
/**
|
||||
* Called when an action is performed on a node.
|
||||
*
|
||||
* @param value The value of the node.
|
||||
*
|
||||
* @since 5.4
|
||||
*/
|
||||
@ApiStatus.AvailableSince("5.4")
|
||||
void run(T value);
|
||||
|
||||
/**
|
||||
* Chains another {@link NodeValueCallback} to be executed after this one.
|
||||
*
|
||||
* @param after The callback to execute after this one.
|
||||
* @return A new {@link NodeValueCallback} that executes both callbacks in order.
|
||||
*
|
||||
* @since 5.4
|
||||
*/
|
||||
@ApiStatus.AvailableSince("5.4")
|
||||
default NodeValueCallback<T> then(NodeValueCallback<T> after) {
|
||||
return (T value) -> {
|
||||
this.run(value);
|
||||
after.run(value);
|
||||
};
|
||||
}
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
package org.mvplugins.multiverse.core.config.node.functions;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
/**
|
||||
* A callback interface for handling changes to configuration node values with contextual information from the sender.
|
||||
*
|
||||
* @param <T> The type of the node value.
|
||||
*
|
||||
* @since 5.4
|
||||
*/
|
||||
@ApiStatus.AvailableSince("5.4")
|
||||
@FunctionalInterface
|
||||
public interface SenderNodeChangeCallback<T> extends NodeChangeCallback<T> {
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@ApiStatus.AvailableSince("5.4")
|
||||
void run(CommandSender sender, T oldValue, T newValue);
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@ApiStatus.AvailableSince("5.4")
|
||||
@Override
|
||||
default void run(T oldValue, T newValue) {
|
||||
run(Bukkit.getConsoleSender(), oldValue, newValue);
|
||||
}
|
||||
}
|
||||
+3
@@ -11,8 +11,11 @@ import org.jetbrains.annotations.Nullable;
|
||||
* A function that parses a string into a node value object of type {@link T} with contextual information from the sender.
|
||||
*
|
||||
* @param <T> The type of the object to parse.
|
||||
*
|
||||
* @since 5.1
|
||||
*/
|
||||
@ApiStatus.AvailableSince("5.1")
|
||||
@FunctionalInterface
|
||||
public interface SenderNodeStringParser<T> extends NodeStringParser<T> {
|
||||
/**
|
||||
* Parses a string into a node value object of type {@link T} with contextual information from the sender.
|
||||
|
||||
+3
-3
@@ -48,12 +48,12 @@ public final class DefaultSerializerProvider {
|
||||
if (type.isInstance(object)) {
|
||||
return (Enum<?>) object;
|
||||
}
|
||||
return Enum.valueOf(type, String.valueOf(object).toUpperCase());
|
||||
return Enum.valueOf(type, String.valueOf(object).toUpperCase(Locale.ENGLISH));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object serialize(Enum object, Class<Enum> type) {
|
||||
return object.name().toLowerCase();
|
||||
return object.name().toLowerCase(Locale.ENGLISH);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -80,7 +80,7 @@ public final class DefaultSerializerProvider {
|
||||
}
|
||||
String input = String.valueOf(object);
|
||||
//todo: this is a copy from string parser
|
||||
return switch (input.toLowerCase()) {
|
||||
return switch (input.toLowerCase(Locale.ENGLISH)) {
|
||||
case "t", "true", "on", "y", "yes", "1", "allow" -> true;
|
||||
case "f", "false", "off", "n", "no", "0", "deny" -> false;
|
||||
default -> throw new RuntimeException("Unable to convert '" + input + "' to boolean.");
|
||||
|
||||
@@ -4,8 +4,9 @@ import io.vavr.control.Option;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.util.Vector;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.mvplugins.multiverse.core.utils.result.FailureReason;
|
||||
import org.mvplugins.multiverse.core.locale.message.Message;
|
||||
import org.mvplugins.multiverse.core.world.location.UnloadedWorldLocation;
|
||||
|
||||
/**
|
||||
@@ -79,6 +80,20 @@ public abstract class DestinationInstance<I extends DestinationInstance<I, T>,
|
||||
*/
|
||||
public abstract @NotNull Option<String> getFinerPermissionSuffix();
|
||||
|
||||
/**
|
||||
* Gets a user-friendly display text for this destination instance. This is used when displaying the destination
|
||||
* to the player. By default, this returns the same as {@link #toString()}. Override this method to provide a more
|
||||
* user-friendly display text with colors, formatting and localization support.
|
||||
*
|
||||
* @return The display message.
|
||||
* @since 5.4
|
||||
*/
|
||||
@ApiStatus.AvailableSince("5.4")
|
||||
@NotNull
|
||||
public Message getDisplayMessage() {
|
||||
return Message.of(this.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialises the destination instance to a savable string.
|
||||
*
|
||||
|
||||
+10
@@ -7,6 +7,7 @@ import org.bukkit.util.Vector;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import org.mvplugins.multiverse.core.destination.DestinationInstance;
|
||||
import org.mvplugins.multiverse.core.locale.message.Message;
|
||||
|
||||
/**
|
||||
* Destination instance implementation for the {@link AnchorDestination}.
|
||||
@@ -63,6 +64,15 @@ public final class AnchorDestinationInstance extends DestinationInstance<AnchorD
|
||||
return Option.of(anchorName);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public @NotNull Message getDisplayMessage() {
|
||||
//TODO Localize
|
||||
return Message.of("anchor '" + anchorName + "'");
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
|
||||
@@ -6,7 +6,7 @@ import java.util.stream.Collectors;
|
||||
|
||||
import co.aikar.locales.MessageKey;
|
||||
import co.aikar.locales.MessageKeyProvider;
|
||||
import org.bukkit.Bukkit;
|
||||
import jakarta.inject.Inject;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -20,8 +20,7 @@ import org.mvplugins.multiverse.core.locale.message.MessageReplacement.Replace;
|
||||
import org.mvplugins.multiverse.core.utils.PlayerFinder;
|
||||
import org.mvplugins.multiverse.core.utils.result.Attempt;
|
||||
import org.mvplugins.multiverse.core.utils.result.FailureReason;
|
||||
|
||||
import static org.mvplugins.multiverse.core.locale.message.MessageReplacement.replace;
|
||||
import org.mvplugins.multiverse.core.world.helpers.ConcurrentPlayerWorldTracker;
|
||||
|
||||
/**
|
||||
* {@link Destination} implementation for beds.
|
||||
@@ -30,7 +29,11 @@ import static org.mvplugins.multiverse.core.locale.message.MessageReplacement.re
|
||||
public final class BedDestination implements Destination<BedDestination, BedDestinationInstance, BedDestination.InstanceFailureReason> {
|
||||
static final String OWN_BED_STRING = "playerbed";
|
||||
|
||||
BedDestination() {
|
||||
private final ConcurrentPlayerWorldTracker worldTracker;
|
||||
|
||||
@Inject
|
||||
BedDestination(@NotNull ConcurrentPlayerWorldTracker worldTracker) {
|
||||
this.worldTracker = worldTracker;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -62,8 +65,8 @@ public final class BedDestination implements Destination<BedDestination, BedDest
|
||||
@Override
|
||||
public @NotNull Collection<DestinationSuggestionPacket> suggestDestinations(
|
||||
@NotNull CommandSender sender, @Nullable String destinationParams) {
|
||||
List<DestinationSuggestionPacket> collect = Bukkit.getOnlinePlayers().stream()
|
||||
.map(player -> new DestinationSuggestionPacket(this, player.getName(), player.getName()))
|
||||
List<DestinationSuggestionPacket> collect = worldTracker.getOnlinePlayers().stream()
|
||||
.map(player -> new DestinationSuggestionPacket(this, player, player))
|
||||
.collect(Collectors.toList());
|
||||
if (sender instanceof Player) {
|
||||
collect.add(new DestinationSuggestionPacket(this, OWN_BED_STRING, OWN_BED_STRING));
|
||||
|
||||
+11
-1
@@ -9,12 +9,13 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import org.mvplugins.multiverse.core.destination.DestinationInstance;
|
||||
import org.mvplugins.multiverse.core.locale.message.Message;
|
||||
|
||||
/**
|
||||
* Destination instance implementation for the {@link BedDestination}.
|
||||
*/
|
||||
public final class BedDestinationInstance extends DestinationInstance<BedDestinationInstance, BedDestination> {
|
||||
private final Player player;
|
||||
private final @Nullable Player player;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
@@ -64,6 +65,15 @@ public final class BedDestinationInstance extends DestinationInstance<BedDestina
|
||||
return Option.of(player != null ? player.getName() : BedDestination.OWN_BED_STRING);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public @NotNull Message getDisplayMessage() {
|
||||
//TODO Localize
|
||||
return Message.of(player == null ? "your bed/respawn point" : player.getName() + "'s bed/respawn point");
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
|
||||
@@ -23,14 +23,11 @@ import org.mvplugins.multiverse.core.exceptions.utils.position.PositionParseExce
|
||||
import org.mvplugins.multiverse.core.locale.MVCorei18n;
|
||||
import org.mvplugins.multiverse.core.utils.REPatterns;
|
||||
import org.mvplugins.multiverse.core.utils.position.EntityPosition;
|
||||
import org.mvplugins.multiverse.core.utils.position.PositionNumber;
|
||||
import org.mvplugins.multiverse.core.utils.position.VectorPosition;
|
||||
import org.mvplugins.multiverse.core.utils.result.Attempt;
|
||||
import org.mvplugins.multiverse.core.utils.result.FailureReason;
|
||||
import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld;
|
||||
import org.mvplugins.multiverse.core.world.WorldManager;
|
||||
import org.mvplugins.multiverse.core.world.entrycheck.WorldEntryCheckerProvider;
|
||||
import org.mvplugins.multiverse.core.world.location.UnloadedWorldLocation;
|
||||
|
||||
import static org.mvplugins.multiverse.core.locale.message.MessageReplacement.*;
|
||||
|
||||
@@ -68,6 +65,7 @@ public final class ExactDestination implements Destination<ExactDestination, Exa
|
||||
public @NotNull ExactDestinationInstance fromLocation(@NotNull Location location) {
|
||||
return new ExactDestinationInstance(
|
||||
this,
|
||||
worldManager,
|
||||
location.getWorld().getName(),
|
||||
EntityPosition.ofLocation(location)
|
||||
);
|
||||
@@ -88,6 +86,7 @@ public final class ExactDestination implements Destination<ExactDestination, Exa
|
||||
.map(location -> Attempt.<ExactDestinationInstance, InstanceFailureReason>success(
|
||||
new ExactDestinationInstance(
|
||||
this,
|
||||
worldManager,
|
||||
location.getWorld().getName(),
|
||||
EntityPosition.ofLocation(location)
|
||||
)
|
||||
@@ -112,7 +111,7 @@ public final class ExactDestination implements Destination<ExactDestination, Exa
|
||||
return Attempt.failure(InstanceFailureReason.INVALID_NUMBER_FORMAT, Replace.ERROR.with(e));
|
||||
}
|
||||
|
||||
return Attempt.success(new ExactDestinationInstance(this, worldName, position));
|
||||
return Attempt.success(new ExactDestinationInstance(this, worldManager, worldName, position));
|
||||
}
|
||||
|
||||
//TODO: Extract to a world finder class
|
||||
|
||||
+17
-2
@@ -9,14 +9,16 @@ import org.bukkit.util.Vector;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import org.mvplugins.multiverse.core.destination.DestinationInstance;
|
||||
import org.mvplugins.multiverse.core.locale.message.Message;
|
||||
import org.mvplugins.multiverse.core.utils.position.EntityPosition;
|
||||
import org.mvplugins.multiverse.core.utils.position.VectorPosition;
|
||||
import org.mvplugins.multiverse.core.world.location.UnloadedWorldLocation;
|
||||
import org.mvplugins.multiverse.core.world.MultiverseWorld;
|
||||
import org.mvplugins.multiverse.core.world.WorldManager;
|
||||
|
||||
/**
|
||||
* Destination instance implementation for the {@link ExactDestination}.
|
||||
*/
|
||||
public final class ExactDestinationInstance extends DestinationInstance<ExactDestinationInstance, ExactDestination> {
|
||||
private final WorldManager worldManager;
|
||||
private final String worldName;
|
||||
private final EntityPosition position;
|
||||
|
||||
@@ -28,9 +30,11 @@ public final class ExactDestinationInstance extends DestinationInstance<ExactDes
|
||||
* @param position The position in the world.
|
||||
*/
|
||||
ExactDestinationInstance(@NotNull ExactDestination destination,
|
||||
@NotNull WorldManager worldManager,
|
||||
@NotNull String worldName,
|
||||
@NotNull EntityPosition position) {
|
||||
super(destination);
|
||||
this.worldManager = worldManager;
|
||||
this.worldName = worldName;
|
||||
this.position = position;
|
||||
}
|
||||
@@ -73,6 +77,17 @@ public final class ExactDestinationInstance extends DestinationInstance<ExactDes
|
||||
return Option.of(worldName);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public @NotNull Message getDisplayMessage() {
|
||||
String displayWorldName = worldManager.getWorld(worldName)
|
||||
.map(MultiverseWorld::getAliasOrName)
|
||||
.getOrElse(worldName);
|
||||
return Message.of(displayWorldName + " at " + position.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
|
||||
+10
-5
@@ -4,7 +4,7 @@ import java.util.Collection;
|
||||
|
||||
import co.aikar.locales.MessageKey;
|
||||
import co.aikar.locales.MessageKeyProvider;
|
||||
import org.bukkit.Bukkit;
|
||||
import jakarta.inject.Inject;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -14,21 +14,26 @@ import org.jvnet.hk2.annotations.Service;
|
||||
import org.mvplugins.multiverse.core.destination.Destination;
|
||||
import org.mvplugins.multiverse.core.destination.DestinationSuggestionPacket;
|
||||
import org.mvplugins.multiverse.core.locale.MVCorei18n;
|
||||
import org.mvplugins.multiverse.core.locale.message.MessageReplacement;
|
||||
import org.mvplugins.multiverse.core.locale.message.MessageReplacement.Replace;
|
||||
import org.mvplugins.multiverse.core.utils.PlayerFinder;
|
||||
import org.mvplugins.multiverse.core.utils.result.Attempt;
|
||||
import org.mvplugins.multiverse.core.utils.result.FailureReason;
|
||||
import org.mvplugins.multiverse.core.world.helpers.ConcurrentPlayerWorldTracker;
|
||||
|
||||
/**
|
||||
* {@link Destination} implementation for players.s
|
||||
*/
|
||||
@Service
|
||||
public final class PlayerDestination implements Destination<PlayerDestination, PlayerDestinationInstance, PlayerDestination.InstanceFailureReason> {
|
||||
|
||||
private final ConcurrentPlayerWorldTracker playerWorldTracker;
|
||||
|
||||
/**
|
||||
* Creates a new instance of the PlayerDestination.
|
||||
*/
|
||||
PlayerDestination() {
|
||||
@Inject
|
||||
PlayerDestination(@NotNull ConcurrentPlayerWorldTracker playerWorldTracker) {
|
||||
this.playerWorldTracker = playerWorldTracker;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -60,8 +65,8 @@ public final class PlayerDestination implements Destination<PlayerDestination, P
|
||||
@Override
|
||||
public @NotNull Collection<DestinationSuggestionPacket> suggestDestinations(
|
||||
@NotNull CommandSender sender, @Nullable String destinationParams) {
|
||||
return Bukkit.getOnlinePlayers().stream()
|
||||
.map(p -> new DestinationSuggestionPacket(this, p.getName(), p.getName()))
|
||||
return playerWorldTracker.getOnlinePlayers().stream()
|
||||
.map(player -> new DestinationSuggestionPacket(this, player, player))
|
||||
.toList();
|
||||
}
|
||||
|
||||
|
||||
+9
@@ -9,6 +9,7 @@ import org.bukkit.util.Vector;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import org.mvplugins.multiverse.core.destination.DestinationInstance;
|
||||
import org.mvplugins.multiverse.core.locale.message.Message;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@@ -66,6 +67,14 @@ public final class PlayerDestinationInstance extends DestinationInstance<PlayerD
|
||||
return Option.of(playerName);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public @NotNull Message getDisplayMessage() {
|
||||
return Message.of(playerName);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
|
||||
+9
-4
@@ -8,12 +8,9 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import org.mvplugins.multiverse.core.destination.DestinationInstance;
|
||||
import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld;
|
||||
import org.mvplugins.multiverse.core.locale.message.Message;
|
||||
import org.mvplugins.multiverse.core.world.MultiverseWorld;
|
||||
|
||||
import java.lang.ref.Reference;
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
/**
|
||||
* Destination instance implementation for the {@link WorldDestination}.
|
||||
*/
|
||||
@@ -87,6 +84,14 @@ public final class WorldDestinationInstance extends DestinationInstance<WorldDes
|
||||
return Option.of(world.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public @NotNull Message getDisplayMessage() {
|
||||
return Message.of(world.getAliasOrName());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
|
||||
@@ -16,6 +16,7 @@ import org.mvplugins.multiverse.core.dynamiclistener.annotations.IgnoreIfCancell
|
||||
import org.mvplugins.multiverse.core.dynamiclistener.annotations.SkipIfEventExist;
|
||||
import org.mvplugins.multiverse.core.utils.text.ChatTextFormatter;
|
||||
import org.mvplugins.multiverse.core.world.WorldManager;
|
||||
import org.mvplugins.multiverse.core.world.helpers.ConcurrentPlayerWorldTracker;
|
||||
|
||||
/**
|
||||
* Multiverse's Listener for players.
|
||||
@@ -24,16 +25,17 @@ import org.mvplugins.multiverse.core.world.WorldManager;
|
||||
final class MVChatListener implements CoreListener {
|
||||
private final CoreConfig config;
|
||||
private final WorldManager worldManager;
|
||||
private final MVPlayerListener playerListener;
|
||||
private final ConcurrentPlayerWorldTracker playerWorldTracker;
|
||||
|
||||
@Inject
|
||||
MVChatListener(
|
||||
CoreConfig config,
|
||||
WorldManager worldManager,
|
||||
MVPlayerListener playerListener) {
|
||||
ConcurrentPlayerWorldTracker playerWorldTracker
|
||||
) {
|
||||
this.config = config;
|
||||
this.worldManager = worldManager;
|
||||
this.playerListener = playerListener;
|
||||
this.playerWorldTracker = playerWorldTracker;
|
||||
}
|
||||
|
||||
@EventClass("io.papermc.paper.event.player.AsyncChatEvent")
|
||||
@@ -94,12 +96,9 @@ final class MVChatListener implements CoreListener {
|
||||
}
|
||||
|
||||
private String getWorldName(Player player) {
|
||||
String world = playerListener.getPlayerWorld().get(player.getName());
|
||||
if (world == null) {
|
||||
world = player.getWorld().getName();
|
||||
playerListener.getPlayerWorld().put(player.getName(), world);
|
||||
}
|
||||
return this.worldManager.getLoadedWorld(world)
|
||||
String worldName = playerWorldTracker.getPlayerWorld(player.getName())
|
||||
.getOrElse(() -> player.getWorld().getName());
|
||||
return this.worldManager.getLoadedWorld(worldName)
|
||||
.map(mvworld -> mvworld.isHidden() ? "" : mvworld.getAliasOrName())
|
||||
.getOrElse("");
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@ import com.dumptruckman.minecraft.util.Logging;
|
||||
import jakarta.inject.Inject;
|
||||
import org.bukkit.entity.LivingEntity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.entity.CreatureSpawnEvent;
|
||||
import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason;
|
||||
@@ -43,7 +42,7 @@ final class MVEntityListener implements CoreListener {
|
||||
* @param event The Event that was fired.
|
||||
*/
|
||||
@EventMethod
|
||||
public void foodLevelChange(FoodLevelChangeEvent event) {
|
||||
void foodLevelChange(FoodLevelChangeEvent event) {
|
||||
if (event.isCancelled()) {
|
||||
return;
|
||||
}
|
||||
@@ -65,7 +64,7 @@ final class MVEntityListener implements CoreListener {
|
||||
* @param event The Event that was fired.
|
||||
*/
|
||||
@EventMethod
|
||||
public void entityRegainHealth(EntityRegainHealthEvent event) {
|
||||
void entityRegainHealth(EntityRegainHealthEvent event) {
|
||||
if (event.isCancelled() || event.getRegainReason() != RegainReason.REGEN) {
|
||||
return;
|
||||
}
|
||||
@@ -84,7 +83,7 @@ final class MVEntityListener implements CoreListener {
|
||||
* @param event The event.
|
||||
*/
|
||||
@EventMethod
|
||||
public void creatureSpawn(CreatureSpawnEvent event) {
|
||||
void creatureSpawn(CreatureSpawnEvent event) {
|
||||
if (event.isCancelled()) {
|
||||
return;
|
||||
}
|
||||
@@ -110,7 +109,7 @@ final class MVEntityListener implements CoreListener {
|
||||
* Handle Spawn Category settings for non-creature entities.
|
||||
*/
|
||||
@EventMethod
|
||||
public void entitySpawn(EntitySpawnEvent event) {
|
||||
void entitySpawn(EntitySpawnEvent event) {
|
||||
if (event.getEntity() instanceof LivingEntity) {
|
||||
// Handled by CreatureSpawnEvent
|
||||
return;
|
||||
|
||||
@@ -23,6 +23,7 @@ import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.player.PlayerChangedWorldEvent;
|
||||
import org.bukkit.event.player.PlayerJoinEvent;
|
||||
import org.bukkit.event.player.PlayerPortalEvent;
|
||||
import org.bukkit.event.player.PlayerRespawnEvent;
|
||||
import org.bukkit.event.player.PlayerTeleportEvent;
|
||||
@@ -32,16 +33,16 @@ import org.jvnet.hk2.annotations.Service;
|
||||
import org.mvplugins.multiverse.core.MultiverseCore;
|
||||
import org.mvplugins.multiverse.core.command.MVCommandManager;
|
||||
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.dynamiclistener.EventRunnable;
|
||||
import org.mvplugins.multiverse.core.dynamiclistener.annotations.DefaultEventPriority;
|
||||
import org.mvplugins.multiverse.core.dynamiclistener.annotations.EventClass;
|
||||
import org.mvplugins.multiverse.core.dynamiclistener.annotations.EventMethod;
|
||||
import org.mvplugins.multiverse.core.dynamiclistener.annotations.EventPriorityKey;
|
||||
import org.mvplugins.multiverse.core.economy.MVEconomist;
|
||||
import org.mvplugins.multiverse.core.event.MVRespawnEvent;
|
||||
import org.mvplugins.multiverse.core.locale.PluginLocales;
|
||||
import org.mvplugins.multiverse.core.permissions.CorePermissionsChecker;
|
||||
import org.mvplugins.multiverse.core.teleportation.AsyncSafetyTeleporter;
|
||||
import org.mvplugins.multiverse.core.teleportation.BlockSafety;
|
||||
import org.mvplugins.multiverse.core.teleportation.TeleportQueue;
|
||||
import org.mvplugins.multiverse.core.utils.result.ResultChain;
|
||||
@@ -52,7 +53,6 @@ import org.mvplugins.multiverse.core.world.entrycheck.EntryFeeResult;
|
||||
import org.mvplugins.multiverse.core.world.entrycheck.WorldEntryCheckerProvider;
|
||||
import org.mvplugins.multiverse.core.world.helpers.DimensionFinder;
|
||||
import org.mvplugins.multiverse.core.world.helpers.EnforcementHandler;
|
||||
import org.spigotmc.event.player.PlayerSpawnLocationEvent;
|
||||
|
||||
/**
|
||||
* Multiverse's Listener for players.
|
||||
@@ -72,8 +72,7 @@ final class MVPlayerListener implements CoreListener {
|
||||
private final EnforcementHandler enforcementHandler;
|
||||
private final DimensionFinder dimensionFinder;
|
||||
private final CorePermissionsChecker corePermissionsChecker;
|
||||
|
||||
private final Map<String, String> playerWorld = new ConcurrentHashMap<>();
|
||||
private final AsyncSafetyTeleporter asyncSafetyTeleporter;
|
||||
|
||||
@Inject
|
||||
MVPlayerListener(
|
||||
@@ -89,7 +88,8 @@ final class MVPlayerListener implements CoreListener {
|
||||
DestinationsProvider destinationsProvider,
|
||||
EnforcementHandler enforcementHandler,
|
||||
DimensionFinder dimensionFinder,
|
||||
CorePermissionsChecker corePermissionsChecker) {
|
||||
CorePermissionsChecker corePermissionsChecker,
|
||||
AsyncSafetyTeleporter asyncSafetyTeleporter) {
|
||||
this.plugin = plugin;
|
||||
this.config = config;
|
||||
this.worldManagerProvider = worldManagerProvider;
|
||||
@@ -103,6 +103,7 @@ final class MVPlayerListener implements CoreListener {
|
||||
this.enforcementHandler = enforcementHandler;
|
||||
this.dimensionFinder = dimensionFinder;
|
||||
this.corePermissionsChecker = corePermissionsChecker;
|
||||
this.asyncSafetyTeleporter = asyncSafetyTeleporter;
|
||||
}
|
||||
|
||||
private WorldManager getWorldManager() {
|
||||
@@ -113,19 +114,10 @@ final class MVPlayerListener implements CoreListener {
|
||||
return commandManagerProvider.get();
|
||||
}
|
||||
|
||||
public PluginLocales getLocales() {
|
||||
private PluginLocales getLocales() {
|
||||
return getCommandManager().getLocales();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the map of player and the world name they are in.
|
||||
*
|
||||
* @return the playerWorld-map
|
||||
*/
|
||||
public Map<String, String> getPlayerWorld() {
|
||||
return playerWorld;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called when a player respawns.
|
||||
*
|
||||
@@ -134,7 +126,7 @@ final class MVPlayerListener implements CoreListener {
|
||||
@EventMethod
|
||||
@EventPriorityKey("mvcore-player-respawn")
|
||||
@DefaultEventPriority(EventPriority.LOW)
|
||||
public void playerRespawn(PlayerRespawnEvent event) {
|
||||
void playerRespawn(PlayerRespawnEvent event) {
|
||||
Player player = event.getPlayer();
|
||||
LoadedMultiverseWorld mvWorld = getWorldManager().getLoadedWorld(player.getWorld()).getOrNull();
|
||||
if (mvWorld == null) {
|
||||
@@ -201,68 +193,63 @@ final class MVPlayerListener implements CoreListener {
|
||||
return Option.of(mvWorld.getSpawnLocation());
|
||||
}
|
||||
|
||||
@EventClass("org.spigotmc.event.player.PlayerSpawnLocationEvent")
|
||||
@EventMethod
|
||||
// TODO: Consider if this key is needed anymore, need to remove from config.yml as well
|
||||
@EventPriorityKey("mvcore-player-spawn-location")
|
||||
EventRunnable playerSpawnLocation() {
|
||||
return new EventRunnable<PlayerSpawnLocationEvent>() {
|
||||
@Override
|
||||
public void onEvent(PlayerSpawnLocationEvent event) {
|
||||
Player player = event.getPlayer();
|
||||
MultiverseWorld world = getWorldManager().getLoadedWorld(player.getWorld()).getOrNull();
|
||||
if (world == null) {
|
||||
Logging.finer("Player joined in a world that is not managed by Multiverse.");
|
||||
return;
|
||||
}
|
||||
if (!player.hasPlayedBefore()) {
|
||||
handleFirstSpawn(event);
|
||||
} else {
|
||||
handleJoinLocation(event);
|
||||
}
|
||||
handleGameModeAndFlight(player, event.getSpawnLocation().getWorld());
|
||||
}
|
||||
void onPlayerJoin(PlayerJoinEvent event) {
|
||||
Player player = event.getPlayer();
|
||||
if (player.hasPlayedBefore()) {
|
||||
handleJoinLocation(player);
|
||||
} else {
|
||||
handleFirstSpawn(player);
|
||||
}
|
||||
handleGameModeAndFlight(player, player.getWorld());
|
||||
}
|
||||
|
||||
private void handleFirstSpawn(PlayerSpawnLocationEvent event) {
|
||||
if (!config.getFirstSpawnOverride()) {
|
||||
Logging.finer("FirstSpawnOverride is disabled");
|
||||
// User has disabled the feature in config
|
||||
return;
|
||||
}
|
||||
Logging.fine("Moving NEW player to(firstspawnoverride): %s", config.getFirstSpawnLocation());
|
||||
destinationsProvider.parseDestination(config.getFirstSpawnLocation())
|
||||
.map(destination -> destination.getLocation(event.getPlayer())
|
||||
.peek(event::setSpawnLocation)
|
||||
.onEmpty(() -> Logging.warning("The destination in FirstSpawnLocation in config is invalid")))
|
||||
.onFailure(failure -> {
|
||||
Logging.warning("Invalid destination in FirstSpawnLocation in config: %s");
|
||||
Logging.warning(failure.getFailureMessage().formatted(getLocales()));
|
||||
});
|
||||
}
|
||||
private void handleFirstSpawn(Player player) {
|
||||
if (!config.getFirstSpawnOverride()) {
|
||||
Logging.finer("FirstSpawnOverride is disabled");
|
||||
// User has disabled the feature in config
|
||||
return;
|
||||
}
|
||||
Logging.fine("Moving NEW player to(firstspawnoverride): %s", config.getFirstSpawnLocation());
|
||||
destinationsProvider.parseDestination(config.getFirstSpawnLocation())
|
||||
.peek(destination -> teleportToDestinationOnJoin(player, destination))
|
||||
.onFailure(failure -> {
|
||||
Logging.warning("Invalid destination in FirstSpawnLocation in config: %s");
|
||||
Logging.warning(failure.getFailureMessage().formatted(getLocales()));
|
||||
});
|
||||
}
|
||||
|
||||
private void handleJoinLocation(PlayerSpawnLocationEvent event) {
|
||||
if (!config.getEnableJoinDestination()) {
|
||||
Logging.finer("JoinDestination is disabled");
|
||||
// User has disabled the feature in config
|
||||
return;
|
||||
}
|
||||
if (config.getJoinDestination().isBlank()) {
|
||||
Logging.warning("Joindestination is enabled but no destination has been specified in config!");
|
||||
return;
|
||||
}
|
||||
if (corePermissionsChecker.hasJoinLocationBypassPermission(event.getPlayer())) {
|
||||
Logging.finer("Player %s has bypass permission for JoinDestination", event.getPlayer().getName());
|
||||
return;
|
||||
}
|
||||
Logging.finer("JoinDestination is " + config.getJoinDestination());
|
||||
destinationsProvider.parseDestination(config.getJoinDestination())
|
||||
.map(destination -> destination.getLocation(event.getPlayer())
|
||||
.peek(event::setSpawnLocation)
|
||||
.onEmpty(() -> Logging.warning("The destination in JoinDestination in config is invalid")))
|
||||
.onFailure(failure -> {
|
||||
Logging.warning("Invalid destination in JoinDestination in config: %s");
|
||||
Logging.warning(failure.getFailureMessage().formatted(getLocales()));
|
||||
});
|
||||
}
|
||||
};
|
||||
private void handleJoinLocation(Player player) {
|
||||
if (!config.getEnableJoinDestination()) {
|
||||
Logging.finer("JoinDestination is disabled");
|
||||
// User has disabled the feature in config
|
||||
return;
|
||||
}
|
||||
if (config.getJoinDestination().isBlank()) {
|
||||
Logging.warning("Joindestination is enabled but no destination has been specified in config!");
|
||||
return;
|
||||
}
|
||||
if (corePermissionsChecker.hasJoinLocationBypassPermission(player)) {
|
||||
Logging.finer("Player %s has bypass permission for JoinDestination", player.getName());
|
||||
return;
|
||||
}
|
||||
Logging.finer("JoinDestination is " + config.getJoinDestination());
|
||||
destinationsProvider.parseDestination(config.getJoinDestination())
|
||||
.peek(destination -> teleportToDestinationOnJoin(player, destination))
|
||||
.onFailure(failure -> {
|
||||
Logging.warning("Invalid destination in JoinDestination in config: %s");
|
||||
Logging.warning(failure.getFailureMessage().formatted(getLocales()));
|
||||
});
|
||||
}
|
||||
|
||||
private void teleportToDestinationOnJoin(Player player, DestinationInstance<?, ?> destination) {
|
||||
asyncSafetyTeleporter.to(destination).teleportSingle(player)
|
||||
.onSuccess(result -> Logging.fine("Player %s has been teleported on join",
|
||||
player.getName()))
|
||||
.onFailure(failure -> Logging.warning("Failed to teleport player %s on join: %s",
|
||||
player.getName(), failure.get(0)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -271,10 +258,9 @@ final class MVPlayerListener implements CoreListener {
|
||||
*/
|
||||
@EventMethod
|
||||
@DefaultEventPriority(EventPriority.MONITOR)
|
||||
public void playerChangedWorld(PlayerChangedWorldEvent event) {
|
||||
void playerChangedWorld(PlayerChangedWorldEvent event) {
|
||||
// Permissions now determine whether or not to handle a gamemode.
|
||||
this.handleGameModeAndFlight(event.getPlayer(), event.getPlayer().getWorld());
|
||||
playerWorld.put(event.getPlayer().getName(), event.getPlayer().getWorld().getName());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -284,7 +270,7 @@ final class MVPlayerListener implements CoreListener {
|
||||
@EventMethod
|
||||
@EventPriorityKey("mvcore-player-teleport")
|
||||
@DefaultEventPriority(EventPriority.HIGHEST)
|
||||
public void playerTeleport(PlayerTeleportEvent event) {
|
||||
void playerTeleport(PlayerTeleportEvent event) {
|
||||
Logging.finer("Got teleport event for player '"
|
||||
+ event.getPlayer().getName() + "' with cause '" + event.getCause() + "'");
|
||||
if (event.isCancelled()) {
|
||||
@@ -355,7 +341,7 @@ final class MVPlayerListener implements CoreListener {
|
||||
*/
|
||||
@EventMethod
|
||||
@DefaultEventPriority(EventPriority.LOWEST)
|
||||
public void playerPortalCheck(PlayerPortalEvent event) {
|
||||
void playerPortalCheck(PlayerPortalEvent event) {
|
||||
if (event.isCancelled()) {
|
||||
return;
|
||||
}
|
||||
@@ -381,7 +367,7 @@ final class MVPlayerListener implements CoreListener {
|
||||
@EventMethod
|
||||
@EventPriorityKey("mvcore-player-portal")
|
||||
@DefaultEventPriority(EventPriority.HIGH)
|
||||
public void playerPortal(PlayerPortalEvent event) {
|
||||
void playerPortal(PlayerPortalEvent event) {
|
||||
if (event.isCancelled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@ import jakarta.inject.Inject;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.PortalType;
|
||||
import org.bukkit.block.BlockState;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.block.Action;
|
||||
import org.bukkit.event.entity.EntityPortalEvent;
|
||||
import org.bukkit.event.player.PlayerInteractEvent;
|
||||
@@ -48,7 +47,7 @@ final class MVPortalListener implements CoreListener {
|
||||
*/
|
||||
@EventMethod
|
||||
@IgnoreIfCancelled
|
||||
public void portalCreate(PortalCreateEvent event) {
|
||||
void portalCreate(PortalCreateEvent event) {
|
||||
Logging.fine("Attempting to create portal at '%s' with reason: %s",
|
||||
event.getWorld().getName(), event.getReason());
|
||||
|
||||
@@ -93,7 +92,7 @@ final class MVPortalListener implements CoreListener {
|
||||
*/
|
||||
@EventMethod
|
||||
@IgnoreIfCancelled
|
||||
public void playerInteract(PlayerInteractEvent event) {
|
||||
void playerInteract(PlayerInteractEvent event) {
|
||||
if (isCreateEndPortalInteraction(event)) {
|
||||
return;
|
||||
}
|
||||
@@ -122,7 +121,7 @@ final class MVPortalListener implements CoreListener {
|
||||
* @param event The Event that was fired.
|
||||
*/
|
||||
@EventMethod
|
||||
public void entityPortal(EntityPortalEvent event) {
|
||||
void entityPortal(EntityPortalEvent event) {
|
||||
if (event.isCancelled() || event.getTo() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ package org.mvplugins.multiverse.core.listeners;
|
||||
|
||||
import com.dumptruckman.minecraft.util.Logging;
|
||||
import jakarta.inject.Inject;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.weather.ThunderChangeEvent;
|
||||
import org.bukkit.event.weather.WeatherChangeEvent;
|
||||
import org.jvnet.hk2.annotations.Service;
|
||||
@@ -35,7 +34,7 @@ final class MVWeatherListener implements CoreListener {
|
||||
* @param event The Event that was fired.
|
||||
*/
|
||||
@EventMethod
|
||||
public void weatherChange(WeatherChangeEvent event) {
|
||||
void weatherChange(WeatherChangeEvent event) {
|
||||
if (event.isCancelled() || !event.toWeatherState()) {
|
||||
return;
|
||||
}
|
||||
@@ -54,7 +53,7 @@ final class MVWeatherListener implements CoreListener {
|
||||
* @param event The Event that was fired.
|
||||
*/
|
||||
@EventMethod
|
||||
public void thunderChange(ThunderChangeEvent event) {
|
||||
void thunderChange(ThunderChangeEvent event) {
|
||||
if (event.isCancelled() || !event.toThunderState()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -27,39 +27,12 @@ public final class LocalizedMessage extends Message implements MessageKeyProvide
|
||||
return messageKeyProvider.getMessageKey();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String[] getReplacements(@NotNull Locales locales, @Nullable CommandIssuer commandIssuer) {
|
||||
return toReplacementsArray(locales, commandIssuer, replacements);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String formatted(@NotNull CommandIssuer commandIssuer) {
|
||||
return formatted(commandIssuer.getManager().getLocales(), commandIssuer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String formatted(@NotNull Locales locales, @Nullable CommandIssuer commandIssuer) {
|
||||
Objects.requireNonNull(locales, "locales must not be null");
|
||||
|
||||
String[] parsedReplacements = getReplacements(locales, commandIssuer);
|
||||
if (parsedReplacements.length == 0) {
|
||||
return locales.getMessage(commandIssuer, getMessageKey());
|
||||
}
|
||||
return ACFUtil.replaceStrings(locales.getMessage(commandIssuer, getMessageKey()), parsedReplacements);
|
||||
}
|
||||
|
||||
private static String[] toReplacementsArray(
|
||||
@NotNull Locales locales,
|
||||
@Nullable CommandIssuer commandIssuer,
|
||||
@NotNull MessageReplacement... replacements) {
|
||||
String[] replacementsArray = new String[replacements.length * 2];
|
||||
int i = 0;
|
||||
for (MessageReplacement replacement : replacements) {
|
||||
replacementsArray[i++] = replacement.getKey();
|
||||
replacementsArray[i++] = replacement.getReplacement().fold(
|
||||
str -> str,
|
||||
message -> message.formatted(locales, commandIssuer));
|
||||
}
|
||||
return replacementsArray;
|
||||
}
|
||||
}
|
||||
@@ -103,7 +103,7 @@ public sealed class Message permits LocalizedMessage {
|
||||
* @return The replacements
|
||||
*/
|
||||
public @NotNull String[] getReplacements(@NotNull Locales locales, @Nullable CommandIssuer commandIssuer) {
|
||||
return getReplacements();
|
||||
return toReplacementsArray(locales, commandIssuer, replacements);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -152,7 +152,7 @@ public sealed class Message permits LocalizedMessage {
|
||||
* @return The formatted, localized message
|
||||
*/
|
||||
public @NotNull String formatted(@NotNull CommandIssuer commandIssuer) {
|
||||
return formatted();
|
||||
return formatted(commandIssuer.getManager().getLocales(), commandIssuer);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -166,7 +166,11 @@ public sealed class Message permits LocalizedMessage {
|
||||
* @return The formatted, localized message
|
||||
*/
|
||||
public @NotNull String formatted(@NotNull Locales locales, @Nullable CommandIssuer commandIssuer) {
|
||||
return formatted();
|
||||
String[] parsedReplacements = getReplacements(locales, commandIssuer);
|
||||
if (parsedReplacements.length == 0) {
|
||||
return raw();
|
||||
}
|
||||
return ACFUtil.replaceStrings(message, parsedReplacements);
|
||||
}
|
||||
|
||||
private static String[] toReplacementsArray(@NotNull MessageReplacement... replacements) {
|
||||
@@ -178,4 +182,19 @@ public sealed class Message permits LocalizedMessage {
|
||||
}
|
||||
return replacementsArray;
|
||||
}
|
||||
|
||||
private static String[] toReplacementsArray(
|
||||
@NotNull Locales locales,
|
||||
@Nullable CommandIssuer commandIssuer,
|
||||
@NotNull MessageReplacement... replacements) {
|
||||
String[] replacementsArray = new String[replacements.length * 2];
|
||||
int i = 0;
|
||||
for (MessageReplacement replacement : replacements) {
|
||||
replacementsArray[i++] = replacement.getKey();
|
||||
replacementsArray[i++] = replacement.getReplacement().fold(
|
||||
str -> str,
|
||||
message -> message.formatted(locales, commandIssuer));
|
||||
}
|
||||
return replacementsArray;
|
||||
}
|
||||
}
|
||||
@@ -10,18 +10,16 @@ import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jvnet.hk2.annotations.Contract;
|
||||
import org.mvplugins.multiverse.core.MultiverseCore;
|
||||
import org.mvplugins.multiverse.core.MultiverseCoreApi;
|
||||
import org.mvplugins.multiverse.core.command.MVCommandManager;
|
||||
import org.mvplugins.multiverse.core.command.MultiverseCommand;
|
||||
import org.mvplugins.multiverse.core.commands.CoreCommand;
|
||||
import org.mvplugins.multiverse.core.dynamiclistener.DynamicListener;
|
||||
import org.mvplugins.multiverse.core.dynamiclistener.DynamicListenerRegistration;
|
||||
import org.mvplugins.multiverse.core.inject.PluginServiceLocator;
|
||||
import org.mvplugins.multiverse.core.inject.PluginServiceLocatorFactory;
|
||||
import org.mvplugins.multiverse.core.inject.binder.PluginBinder;
|
||||
import org.mvplugins.multiverse.core.listeners.CoreListener;
|
||||
import org.mvplugins.multiverse.core.utils.REPatterns;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Common plugin class for all Multiverse plugins.
|
||||
*/
|
||||
@@ -151,7 +149,7 @@ public abstract class MultiverseModule extends JavaPlugin {
|
||||
.andThen(pluginLocales -> {
|
||||
pluginLocales.addFileResClassLoader(this);
|
||||
pluginLocales.addBundleClassLoader(this.getClassLoader());
|
||||
pluginLocales.addMessageBundles(this.getDescription().getName().toLowerCase());
|
||||
pluginLocales.addMessageBundles(this.getName().toLowerCase(Locale.ENGLISH));
|
||||
})
|
||||
.onFailure(e -> {
|
||||
Logging.severe("Failed to register locales");
|
||||
|
||||
@@ -13,6 +13,7 @@ import org.mvplugins.multiverse.core.destination.Destination;
|
||||
import org.mvplugins.multiverse.core.world.MultiverseWorld;
|
||||
|
||||
import static org.mvplugins.multiverse.core.permissions.PermissionUtils.concatPermission;
|
||||
import static org.mvplugins.multiverse.core.permissions.PermissionUtils.registerPermissionWithWildcards;
|
||||
|
||||
@Service
|
||||
public final class CorePermissions {
|
||||
@@ -60,28 +61,34 @@ public final class CorePermissions {
|
||||
|
||||
@PostConstruct
|
||||
void registerBasePermissions() {
|
||||
pluginManager.addPermission(new Permission(JOINLOCATION_BYPASS, PermissionDefault.FALSE));
|
||||
Logging.fine("Successfully registered base permissions");
|
||||
Try.run(() -> {
|
||||
registerPermissionWithWildcards(new Permission(JOINLOCATION_BYPASS, PermissionDefault.FALSE));
|
||||
}).onSuccess(ignore -> {
|
||||
Logging.fine("Successfully registered base permissions");
|
||||
}).onFailure(e -> {
|
||||
Logging.fine("Failed to register base permissions: %s", e.getMessage());
|
||||
});
|
||||
}
|
||||
|
||||
public Try<Void> addWorldPermissions(@NotNull MultiverseWorld world) {
|
||||
return Try.run(() -> {
|
||||
pluginManager.addPermission(new Permission(
|
||||
registerPermissionWithWildcards(new Permission(
|
||||
concatPermission(WORLD_ACCESS, world.getName()), PermissionDefault.OP));
|
||||
pluginManager.addPermission(new Permission(
|
||||
registerPermissionWithWildcards(new Permission(
|
||||
concatPermission(WORLD_EXEMPT, world.getName()), PermissionDefault.OP));
|
||||
pluginManager.addPermission(new Permission(
|
||||
registerPermissionWithWildcards(new Permission(
|
||||
concatPermission(GAMEMODE_BYPASS, world.getName()), PermissionDefault.FALSE));
|
||||
pluginManager.addPermission(new Permission(
|
||||
registerPermissionWithWildcards(new Permission(
|
||||
concatPermission(PLAYERLIMIT_BYPASS, world.getName()), PermissionDefault.FALSE));
|
||||
pluginManager.addPermission(new Permission(
|
||||
registerPermissionWithWildcards(new Permission(
|
||||
concatPermission(SPAWN, world.getName()), PermissionDefault.OP));
|
||||
pluginManager.addPermission(new Permission(
|
||||
registerPermissionWithWildcards(new Permission(
|
||||
concatPermission(SPAWN, "self", world.getName()), PermissionDefault.OP));
|
||||
pluginManager.addPermission(new Permission(
|
||||
registerPermissionWithWildcards(new Permission(
|
||||
concatPermission(SPAWN, "other", world.getName()), PermissionDefault.OP));
|
||||
Logging.fine("Successfully registered permissions for world %s", world.getName());
|
||||
});
|
||||
}).onFailure(e -> Logging.fine("Failed to register permissions for world %s: %s",
|
||||
world.getName(), e.getMessage()));
|
||||
}
|
||||
|
||||
public Try<Void> removeWorldPermissions(@NotNull MultiverseWorld world) {
|
||||
@@ -90,20 +97,23 @@ public final class CorePermissions {
|
||||
pluginManager.removePermission(concatPermission(WORLD_EXEMPT, world.getName()));
|
||||
pluginManager.removePermission(concatPermission(GAMEMODE_BYPASS, world.getName()));
|
||||
pluginManager.removePermission(concatPermission(PLAYERLIMIT_BYPASS, world.getName()));
|
||||
pluginManager.removePermission(concatPermission(SPAWN, world.getName()));
|
||||
pluginManager.removePermission(concatPermission(SPAWN, "self", world.getName()));
|
||||
pluginManager.removePermission(concatPermission(SPAWN, "other", world.getName()));
|
||||
Logging.fine("Successfully removed permissions for world %s", world.getName());
|
||||
});
|
||||
}).onFailure(e -> Logging.fine("Failed to remove permissions for world %s: %s",
|
||||
world.getName(), e.getMessage()));
|
||||
}
|
||||
|
||||
public Try<Void> addDestinationPermissions(@NotNull Destination destination) {
|
||||
return Try.run(() -> {
|
||||
pluginManager.addPermission(new Permission(
|
||||
registerPermissionWithWildcards(new Permission(
|
||||
concatPermission(TELEPORT, "self", destination.getIdentifier()), PermissionDefault.OP));
|
||||
pluginManager.addPermission(new Permission(
|
||||
registerPermissionWithWildcards(new Permission(
|
||||
concatPermission(TELEPORT, "other", destination.getIdentifier()), PermissionDefault.OP));
|
||||
Logging.fine("Successfully registered permissions for destination %s", destination.getIdentifier());
|
||||
});
|
||||
}).onFailure(e -> Logging.fine("Failed to register permissions for destination %s: %s",
|
||||
destination.getIdentifier(), e.getMessage()));
|
||||
}
|
||||
|
||||
public Try<Void> removeDestinationPermissions(@NotNull Destination destination) {
|
||||
@@ -111,6 +121,7 @@ public final class CorePermissions {
|
||||
pluginManager.removePermission(concatPermission(TELEPORT, "self", destination.getIdentifier()));
|
||||
pluginManager.removePermission(concatPermission(TELEPORT, "other", destination.getIdentifier()));
|
||||
Logging.fine("Successfully removed permissions for destination %s", destination.getIdentifier());
|
||||
});
|
||||
}).onFailure(e -> Logging.fine("Failed to remove permissions for destination %s: %s",
|
||||
destination.getIdentifier(), e.getMessage()));
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,14 @@
|
||||
package org.mvplugins.multiverse.core.permissions;
|
||||
|
||||
import com.dumptruckman.minecraft.util.Logging;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.command.ConsoleCommandSender;
|
||||
import org.bukkit.permissions.Permission;
|
||||
import org.bukkit.permissions.PermissionDefault;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public final class PermissionUtils {
|
||||
|
||||
@@ -20,6 +26,37 @@ public final class PermissionUtils {
|
||||
PermissionUtils.debugPermissions = debugPermissions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a permission along with all its wildcard parents.
|
||||
* <br />
|
||||
* For example, registering "mv.bypass.joinlocation" will also register "mv.*" and "mv.bypass.*" as parents.
|
||||
*
|
||||
* @param permission The permission to register.
|
||||
*
|
||||
* @since 5.4
|
||||
*/
|
||||
@ApiStatus.AvailableSince("5.4")
|
||||
public static void registerPermissionWithWildcards(Permission permission) {
|
||||
Bukkit.getServer().getPluginManager().addPermission(permission);
|
||||
String[] split = permission.getName().split("\\.");
|
||||
StringBuilder prefix = new StringBuilder();
|
||||
// Skip the last element since it's the actual permission
|
||||
Arrays.stream(Arrays.copyOfRange(split, 0, split.length - 1)).forEach(s -> {
|
||||
prefix.append(s).append(".");
|
||||
Permission perm = getOrAddPermission(prefix + "*");
|
||||
permission.addParent(perm, true);
|
||||
});
|
||||
}
|
||||
|
||||
private static Permission getOrAddPermission(String permission) {
|
||||
Permission perm = Bukkit.getServer().getPluginManager().getPermission(permission);
|
||||
if (perm == null) {
|
||||
perm = new Permission(permission, PermissionDefault.FALSE);
|
||||
Bukkit.getServer().getPluginManager().addPermission(perm);
|
||||
}
|
||||
return perm;
|
||||
}
|
||||
|
||||
/**
|
||||
* Joins permissions with a dot.
|
||||
*
|
||||
|
||||
+8
-5
@@ -238,12 +238,15 @@ public final class AsyncSafetyTeleporterAction {
|
||||
@NotNull Location location,
|
||||
@NotNull List<Entity> passengers
|
||||
) {
|
||||
List<Entity> toTeleport = new ArrayList<>(passengers);
|
||||
toTeleport.addFirst(teleportee);
|
||||
List<Entity> toTeleport = new ArrayList<>(passengers.size() + 1);
|
||||
toTeleport.add(teleportee);
|
||||
toTeleport.addAll(passengers);
|
||||
|
||||
return AsyncAttemptsAggregate.allOfAggregate(toTeleport.stream()
|
||||
.map(passenger -> doAsyncTeleport(passenger, location))
|
||||
.toList())
|
||||
return AsyncAttemptsAggregate.allOfAggregate(
|
||||
toTeleport.stream()
|
||||
.map(passenger -> doAsyncTeleport(passenger, location))
|
||||
.toList()
|
||||
)
|
||||
.onSuccess(() -> Bukkit.getScheduler().runTask(multiverseCore, () -> {
|
||||
passengers.forEach(teleportee::addPassenger);
|
||||
Logging.finer("Mounted %d passengers to %s", passengers.size(), teleportee.getName());
|
||||
|
||||
@@ -228,8 +228,8 @@ public final class LocationManipulation {
|
||||
if (orientation == null) {
|
||||
return 0;
|
||||
}
|
||||
if (ORIENTATION_INTS.containsKey(orientation.toLowerCase())) {
|
||||
return ORIENTATION_INTS.get(orientation.toLowerCase());
|
||||
if (ORIENTATION_INTS.containsKey(orientation.toLowerCase(Locale.ENGLISH))) {
|
||||
return ORIENTATION_INTS.get(orientation.toLowerCase(Locale.ENGLISH));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
package org.mvplugins.multiverse.core.utils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.dumptruckman.minecraft.util.Logging;
|
||||
import com.google.common.base.Strings;
|
||||
import io.vavr.control.Try;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -76,22 +78,33 @@ public final class PlayerFinder {
|
||||
public static @NotNull List<Player> getMulti(@Nullable String playerIdentifiers,
|
||||
@NotNull CommandSender sender
|
||||
) {
|
||||
List<Player> playerResults = new ArrayList<>();
|
||||
return tryGetMulti(playerIdentifiers, sender)
|
||||
.getOrElse(Collections.emptyList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get multiple {@link Player} based on many identifiers of name UUID or selector.
|
||||
*
|
||||
* @param playerIdentifiers An identifier of multiple names, UUIDs or selectors, separated by comma.
|
||||
* @param sender Target sender for selector.
|
||||
* @return A list of all the {@link Player} found.
|
||||
*
|
||||
* @since 5.4
|
||||
*/
|
||||
@ApiStatus.AvailableSince("5.4")
|
||||
public static @NotNull Try<@NotNull List<Player>> tryGetMulti(@Nullable String playerIdentifiers,
|
||||
@NotNull CommandSender sender
|
||||
) {
|
||||
if (playerIdentifiers == null || Strings.isNullOrEmpty(playerIdentifiers)) {
|
||||
return playerResults;
|
||||
return Try.success(Collections.emptyList());
|
||||
}
|
||||
|
||||
// TODO: Currently just assume entire string is a selector. Add support for comma seperated mixture of names, uuids and selectors
|
||||
if (isSelector(playerIdentifiers)) {
|
||||
Logging.finer("Using selector: %s", playerIdentifiers);
|
||||
List<Player> targetPlayers = getMultiBySelector(playerIdentifiers, sender);
|
||||
if (targetPlayers != null) {
|
||||
playerResults.addAll(targetPlayers);
|
||||
}
|
||||
return playerResults;
|
||||
return tryGetMultiBySelector(playerIdentifiers, sender);
|
||||
}
|
||||
|
||||
List<Player> playerResults = new ArrayList<>();
|
||||
String[] playerIdentifierArray = REPatterns.COMMA.split(playerIdentifiers);
|
||||
for (String playerIdentifier : playerIdentifierArray) {
|
||||
Player targetPlayer = getByName(playerIdentifier);
|
||||
@@ -104,15 +117,25 @@ public final class PlayerFinder {
|
||||
playerResults.add(targetPlayer);
|
||||
continue;
|
||||
}
|
||||
List<Player> targetPlayers = getMultiBySelector(playerIdentifier, sender);
|
||||
if (targetPlayers != null) {
|
||||
playerResults.addAll(targetPlayers);
|
||||
Try<@NotNull List<Player>> selectorParseResult = tryGetMultiBySelector(playerIdentifier, sender);
|
||||
if (selectorParseResult.isFailure()) {
|
||||
return Try.failure(selectorParseResult.getCause());
|
||||
}
|
||||
playerResults.addAll(selectorParseResult.getOrElse(Collections.emptyList()));
|
||||
}
|
||||
return playerResults;
|
||||
return Try.success(playerResults);
|
||||
}
|
||||
|
||||
private static boolean isSelector(@NotNull String playerIdentifier) {
|
||||
/**
|
||||
* Check if the player identifier is a selector.
|
||||
*
|
||||
* @param playerIdentifier An identifier of name, UUID or selector.
|
||||
* @return True if the identifier is a selector, else false.
|
||||
*
|
||||
* @since 5.4
|
||||
*/
|
||||
@ApiStatus.AvailableSince("5.4")
|
||||
public static boolean isSelector(@NotNull String playerIdentifier) {
|
||||
return VANILLA_SELECTORS.stream().anyMatch(playerIdentifier::startsWith);
|
||||
}
|
||||
|
||||
@@ -167,7 +190,7 @@ public final class PlayerFinder {
|
||||
@NotNull CommandSender sender
|
||||
) {
|
||||
List<Player> matchedPlayers = getMultiBySelector(playerSelector, sender);
|
||||
if (matchedPlayers == null || matchedPlayers.isEmpty()) {
|
||||
if (matchedPlayers.isEmpty()) {
|
||||
Logging.fine("No player found with selector '%s' for %s.", playerSelector, sender.getName());
|
||||
return null;
|
||||
}
|
||||
@@ -186,22 +209,36 @@ public final class PlayerFinder {
|
||||
* @param sender Target sender for selector.
|
||||
* @return A list of all the {@link Player} found.
|
||||
*/
|
||||
public static @Nullable List<Player> getMultiBySelector(@NotNull String playerSelector,
|
||||
public static @NotNull List<Player> getMultiBySelector(@NotNull String playerSelector,
|
||||
@NotNull CommandSender sender
|
||||
) {
|
||||
return tryGetMultiBySelector(playerSelector, sender)
|
||||
.onFailure(throwable -> Logging.warning(
|
||||
"Error selecting entities with selector '%s' for %s: %s",
|
||||
playerSelector, sender.getName(), throwable.getMessage()
|
||||
))
|
||||
.getOrElse(Collections::emptyList);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get multiple {@link Player} based on <a href="https://minecraft.gamepedia.com/Commands#Target_selectors">vanilla selectors</a>.
|
||||
*
|
||||
* @param playerSelector A target selector, usually starts with an '@'.
|
||||
* @param sender Target sender for selector.
|
||||
* @return A list of all the {@link Player} found.
|
||||
*
|
||||
* @since 5.4
|
||||
*/
|
||||
@ApiStatus.AvailableSince("5.4")
|
||||
public static @NotNull Try<@NotNull List<Player>> tryGetMultiBySelector(@NotNull String playerSelector,
|
||||
@NotNull CommandSender sender
|
||||
) {
|
||||
if (playerSelector.charAt(0) != '@') {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return Bukkit.selectEntities(sender, playerSelector).stream()
|
||||
.filter(e -> e instanceof Player)
|
||||
.map(e -> ((Player) e))
|
||||
.collect(Collectors.toList());
|
||||
} catch (IllegalArgumentException e) {
|
||||
Logging.warning("An error occurred while parsing selector '%s' for %s. Is it is the correct format?",
|
||||
playerSelector, sender.getName());
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
return Try.success(Collections.emptyList());
|
||||
}
|
||||
return Try.of(() -> Bukkit.selectEntities(sender, playerSelector).stream()
|
||||
.filter(e -> e instanceof Player)
|
||||
.map(e -> ((Player) e))
|
||||
.collect(Collectors.toList()));
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
package org.mvplugins.multiverse.core.utils;
|
||||
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Pattern;
|
||||
@@ -16,7 +18,17 @@ public class REPatterns {
|
||||
public static final Pattern COMMA = get(",");
|
||||
public static final Pattern DOT = get("\\.");
|
||||
public static final Pattern EQUALS = get("=");
|
||||
@ApiStatus.AvailableSince("5.4")
|
||||
public static final Pattern HYPHEN = get("-");
|
||||
@ApiStatus.AvailableSince("5.4")
|
||||
public static final Pattern NAMESPACE = get("^[a-z0-9_.-]+$");
|
||||
@ApiStatus.AvailableSince("5.4")
|
||||
public static final Pattern NAMESPACE_KEY = get("^[a-z0-9/_.-]+$");
|
||||
public static final Pattern SEMICOLON = get(";");
|
||||
@ApiStatus.AvailableSince("5.4")
|
||||
public static final Pattern SPACE = get(" ");
|
||||
public static final Pattern UNDERSCORE = get("_");
|
||||
public static final Pattern UUID = get("[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}");
|
||||
@ApiStatus.AvailableSince("5.4")
|
||||
public static final Pattern YAML_KEY = get("^[a-zA-Z0-9\\[\\]_-]+$");
|
||||
}
|
||||
@@ -107,18 +107,14 @@ public sealed interface Attempt<T, F extends FailureReason> permits Attempt.Succ
|
||||
*
|
||||
* @return Whether this attempt is a success.
|
||||
*/
|
||||
default boolean isSuccess() {
|
||||
return this instanceof Success;
|
||||
}
|
||||
boolean isSuccess();
|
||||
|
||||
/**
|
||||
* Returns whether this attempt is a failure.
|
||||
*
|
||||
* @return Whether this attempt is a failure.
|
||||
*/
|
||||
default boolean isFailure() {
|
||||
return this instanceof Failure;
|
||||
}
|
||||
boolean isFailure();
|
||||
|
||||
/**
|
||||
* Converts this {@link Attempt} instance to an equivalent {@link Try} representation. Defaults to a
|
||||
@@ -143,19 +139,25 @@ public sealed interface Attempt<T, F extends FailureReason> permits Attempt.Succ
|
||||
@ApiStatus.AvailableSince("5.1")
|
||||
Try<T> toTry(Function<Failure<T, F>, Throwable> throwableFunction);
|
||||
|
||||
/**
|
||||
* Runs the given runnable after this attempt regardless of success or failure.
|
||||
*
|
||||
* @param runnable The runnable.
|
||||
* @return This attempt.
|
||||
*/
|
||||
default Attempt<T, F> thenRun(Runnable runnable) {
|
||||
runnable.run();
|
||||
return this;
|
||||
}
|
||||
|
||||
default Attempt<T, F> thenAccept(Consumer<Either<T, F>> consumer) {
|
||||
if (this instanceof Success) {
|
||||
consumer.accept(Either.left(get()));
|
||||
} else {
|
||||
consumer.accept(Either.right(getFailureReason()));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Accepts either the value or the failure reason depending on the result type.
|
||||
* This will run regardless of success or failure.
|
||||
*
|
||||
* @param consumer The consumer with either the value or the failure reason.
|
||||
* @return This attempt.
|
||||
*/
|
||||
Attempt<T, F> thenAccept(Consumer<Either<T, F>> consumer);
|
||||
|
||||
/**
|
||||
* Peeks at the value if this is a success attempt.
|
||||
@@ -163,12 +165,7 @@ public sealed interface Attempt<T, F extends FailureReason> permits Attempt.Succ
|
||||
* @param consumer The consumer with the value.
|
||||
* @return This attempt.
|
||||
*/
|
||||
default Attempt<T, F> peek(Consumer<T> consumer) {
|
||||
if (this instanceof Success) {
|
||||
consumer.accept(get());
|
||||
}
|
||||
return this;
|
||||
}
|
||||
Attempt<T, F> peek(Consumer<T> consumer);
|
||||
|
||||
/**
|
||||
* Maps the value to another value if this is a success attempt.
|
||||
@@ -177,13 +174,7 @@ public sealed interface Attempt<T, F extends FailureReason> permits Attempt.Succ
|
||||
* @param <U> The type of the new value.
|
||||
* @return The new attempt.
|
||||
*/
|
||||
default <U> Attempt<U, F> map(Function<? super T, ? extends U> mapper) {
|
||||
if (this instanceof Success) {
|
||||
return new Success<>(mapper.apply(get()));
|
||||
} else {
|
||||
return new Failure<>((Failure<T, F>) this);
|
||||
}
|
||||
}
|
||||
<U> Attempt<U, F> map(Function<? super T, ? extends U> mapper);
|
||||
|
||||
/**
|
||||
* Maps the value to another attempt if this is a success attempt.
|
||||
@@ -192,13 +183,7 @@ public sealed interface Attempt<T, F extends FailureReason> permits Attempt.Succ
|
||||
* @param <U> The type of the new value.
|
||||
* @return The new attempt.
|
||||
*/
|
||||
default <U> Attempt<U, F> map(Supplier<? extends U> mapper) {
|
||||
if (this instanceof Success) {
|
||||
return new Success<>(mapper.get());
|
||||
} else {
|
||||
return new Failure<>((Failure<T, F>) this);
|
||||
}
|
||||
}
|
||||
<U> Attempt<U, F> map(Supplier<? extends U> mapper);
|
||||
|
||||
/**
|
||||
* Maps the value to another attempt with the same fail reason if this is a success attempt.
|
||||
@@ -207,13 +192,7 @@ public sealed interface Attempt<T, F extends FailureReason> permits Attempt.Succ
|
||||
* @param <U> The type of the new value.
|
||||
* @return The new attempt.
|
||||
*/
|
||||
default <U> Attempt<U, F> mapAttempt(Function<? super T, Attempt<U, F>> mapper) {
|
||||
if (this instanceof Success) {
|
||||
return mapper.apply(get());
|
||||
} else {
|
||||
return new Failure<>((Failure<T, F>) this);
|
||||
}
|
||||
}
|
||||
<U> Attempt<U, F> mapAttempt(Function<? super T, Attempt<U, F>> mapper);
|
||||
|
||||
/**
|
||||
* Maps the value to another attempt with the same fail reason if this is a success attempt.
|
||||
@@ -222,13 +201,7 @@ public sealed interface Attempt<T, F extends FailureReason> permits Attempt.Succ
|
||||
* @param <U> The type of the new value.
|
||||
* @return The new attempt.
|
||||
*/
|
||||
default <U> Attempt<U, F> mapAttempt(Supplier<Attempt<U, F>> mapper) {
|
||||
if (this instanceof Success) {
|
||||
return mapper.get();
|
||||
} else {
|
||||
return new Failure<>((Failure<T, F>) this);
|
||||
}
|
||||
}
|
||||
<U> Attempt<U, F> mapAttempt(Supplier<Attempt<U, F>> mapper);
|
||||
|
||||
/**
|
||||
* Maps to another attempt with a different fail reason.
|
||||
@@ -237,13 +210,7 @@ public sealed interface Attempt<T, F extends FailureReason> permits Attempt.Succ
|
||||
* @param <UF> The type of the new fail reason.
|
||||
* @return The new attempt.
|
||||
*/
|
||||
default <UF extends FailureReason> Attempt<T, UF> transform(UF failureReason) {
|
||||
if (this instanceof Success) {
|
||||
return new Success<>(get());
|
||||
} else {
|
||||
return new Failure<>(failureReason, getFailureMessage(), (Failure<T, F>) this);
|
||||
}
|
||||
}
|
||||
<UF extends FailureReason> Attempt<T, UF> transform(UF failureReason);
|
||||
|
||||
/**
|
||||
* Maps attempt result to another value.
|
||||
@@ -256,13 +223,7 @@ public sealed interface Attempt<T, F extends FailureReason> permits Attempt.Succ
|
||||
* @since 5.1
|
||||
*/
|
||||
@ApiStatus.AvailableSince("5.1")
|
||||
default <U> U transform(Function<T, U> successMapper, Function<F, U> failureMapper) {
|
||||
if (this instanceof Success) {
|
||||
return successMapper.apply(get());
|
||||
} else {
|
||||
return failureMapper.apply(getFailureReason());
|
||||
}
|
||||
}
|
||||
<U> U transform(Function<T, U> successMapper, Function<F, U> failureMapper);
|
||||
|
||||
/**
|
||||
* Calls either the failure or success function depending on the result type.
|
||||
@@ -272,13 +233,7 @@ public sealed interface Attempt<T, F extends FailureReason> permits Attempt.Succ
|
||||
* @param <N> The type of the new value.
|
||||
* @return The result of the function.
|
||||
*/
|
||||
default <N> N fold(Function<Failure<T, F>, N> failureMapper, Function<T, N> successMapper) {
|
||||
if (this instanceof Success) {
|
||||
return successMapper.apply(get());
|
||||
} else {
|
||||
return failureMapper.apply((Failure<T, F>) this);
|
||||
}
|
||||
}
|
||||
<N> N fold(Function<Failure<T, F>, N> failureMapper, Function<T, N> successMapper);
|
||||
|
||||
/**
|
||||
* Calls the given runnable if this is a success attempt.
|
||||
@@ -286,12 +241,7 @@ public sealed interface Attempt<T, F extends FailureReason> permits Attempt.Succ
|
||||
* @param runnable The runnable.
|
||||
* @return This attempt.
|
||||
*/
|
||||
default Attempt<T, F> onSuccess(Runnable runnable) {
|
||||
if (this instanceof Success) {
|
||||
runnable.run();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
Attempt<T, F> onSuccess(Runnable runnable);
|
||||
|
||||
/**
|
||||
* Calls the given consumer if this is a success attempt.
|
||||
@@ -299,12 +249,7 @@ public sealed interface Attempt<T, F extends FailureReason> permits Attempt.Succ
|
||||
* @param consumer The consumer with the value.
|
||||
* @return This attempt.
|
||||
*/
|
||||
default Attempt<T, F> onSuccess(Consumer<T> consumer) {
|
||||
if (this instanceof Success) {
|
||||
consumer.accept(get());
|
||||
}
|
||||
return this;
|
||||
}
|
||||
Attempt<T, F> onSuccess(Consumer<T> consumer);
|
||||
|
||||
/**
|
||||
* Calls the given consumer if this is a failure attempt.
|
||||
@@ -312,12 +257,7 @@ public sealed interface Attempt<T, F extends FailureReason> permits Attempt.Succ
|
||||
* @param runnable The runnable.
|
||||
* @return This attempt.
|
||||
*/
|
||||
default Attempt<T, F> onFailure(Runnable runnable) {
|
||||
if (this instanceof Failure) {
|
||||
runnable.run();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
Attempt<T, F> onFailure(Runnable runnable);
|
||||
|
||||
/**
|
||||
* Calls the given consumer if this is a failure attempt.
|
||||
@@ -325,12 +265,7 @@ public sealed interface Attempt<T, F extends FailureReason> permits Attempt.Succ
|
||||
* @param consumer The consumer with the failure instance.
|
||||
* @return This attempt.
|
||||
*/
|
||||
default Attempt<T, F> onFailure(Consumer<Failure<T, F>> consumer) {
|
||||
if (this instanceof Failure) {
|
||||
consumer.accept((Failure<T, F>) this);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
Attempt<T, F> onFailure(Consumer<Failure<T, F>> consumer);
|
||||
|
||||
/**
|
||||
* Calls the given runnable if this is a failure attempt.
|
||||
@@ -338,12 +273,7 @@ public sealed interface Attempt<T, F extends FailureReason> permits Attempt.Succ
|
||||
* @param consumer The consumer with the failure reason.
|
||||
* @return This attempt.
|
||||
*/
|
||||
default Attempt<T, F> onFailureReason(Consumer<F> consumer) {
|
||||
if (this instanceof Failure) {
|
||||
consumer.accept(getFailureReason());
|
||||
}
|
||||
return this;
|
||||
}
|
||||
Attempt<T, F> onFailureReason(Consumer<F> consumer);
|
||||
|
||||
/**
|
||||
* Represents a successful attempt with a value.
|
||||
@@ -378,6 +308,26 @@ public sealed interface Attempt<T, F extends FailureReason> permits Attempt.Succ
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public F getFailureReason() {
|
||||
throw new UnsupportedOperationException("No failure reason as attempt is a success");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Message getFailureMessage() {
|
||||
throw new UnsupportedOperationException("No failure message as attempt is a success");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSuccess() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFailure() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Try<T> toTry() {
|
||||
return Try.success(value);
|
||||
@@ -389,13 +339,83 @@ public sealed interface Attempt<T, F extends FailureReason> permits Attempt.Succ
|
||||
}
|
||||
|
||||
@Override
|
||||
public F getFailureReason() {
|
||||
throw new UnsupportedOperationException("No failure reason as attempt is a success");
|
||||
public Attempt<T, F> thenAccept(Consumer<Either<T, F>> consumer) {
|
||||
consumer.accept(Either.left(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Message getFailureMessage() {
|
||||
throw new UnsupportedOperationException("No failure message as attempt is a success");
|
||||
public Attempt<T, F> peek(Consumer<T> consumer) {
|
||||
consumer.accept(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <U> Attempt<U, F> map(Function<? super T, ? extends U> mapper) {
|
||||
return new Success<>(mapper.apply(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <U> Attempt<U, F> map(Supplier<? extends U> mapper) {
|
||||
return new Success<>(mapper.get());
|
||||
}
|
||||
|
||||
@Override
|
||||
public <U> Attempt<U, F> mapAttempt(Function<? super T, Attempt<U, F>> mapper) {
|
||||
return mapper.apply(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <U> Attempt<U, F> mapAttempt(Supplier<Attempt<U, F>> mapper) {
|
||||
return mapper.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <UF extends FailureReason> Attempt<T, UF> transform(UF failureReason) {
|
||||
return changeFailureType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <U> U transform(Function<T, U> successMapper, Function<F, U> failureMapper) {
|
||||
return successMapper.apply(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <N> N fold(Function<Failure<T, F>, N> failureMapper, Function<T, N> successMapper) {
|
||||
return successMapper.apply(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Attempt<T, F> onSuccess(Runnable runnable) {
|
||||
runnable.run();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Attempt<T, F> onSuccess(Consumer<T> consumer) {
|
||||
consumer.accept(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Attempt<T, F> onFailure(Runnable runnable) {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Attempt<T, F> onFailure(Consumer<Failure<T, F>> consumer) {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Attempt<T, F> onFailureReason(Consumer<F> consumer) {
|
||||
return this;
|
||||
}
|
||||
|
||||
private <UF extends FailureReason> Attempt<T, UF> changeFailureType() {
|
||||
@SuppressWarnings("unchecked")
|
||||
Attempt<T, UF> mappedSuccess = (Attempt<T, UF>) this;
|
||||
return mappedSuccess;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -451,6 +471,26 @@ public sealed interface Attempt<T, F extends FailureReason> permits Attempt.Succ
|
||||
throw exceptionSupplier.apply(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public F getFailureReason() {
|
||||
return failureReason;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Message getFailureMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSuccess() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFailure() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Try<T> toTry() {
|
||||
return Try.failure(new MultiverseException(message));
|
||||
@@ -462,13 +502,83 @@ public sealed interface Attempt<T, F extends FailureReason> permits Attempt.Succ
|
||||
}
|
||||
|
||||
@Override
|
||||
public F getFailureReason() {
|
||||
return failureReason;
|
||||
public Attempt<T, F> thenAccept(Consumer<Either<T, F>> consumer) {
|
||||
consumer.accept(Either.right(failureReason));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Message getFailureMessage() {
|
||||
return message;
|
||||
public Attempt<T, F> peek(Consumer<T> consumer) {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <U> Attempt<U, F> map(Function<? super T, ? extends U> mapper) {
|
||||
return changeValueType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <U> Attempt<U, F> map(Supplier<? extends U> mapper) {
|
||||
return changeValueType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <U> Attempt<U, F> mapAttempt(Function<? super T, Attempt<U, F>> mapper) {
|
||||
return changeValueType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <U> Attempt<U, F> mapAttempt(Supplier<Attempt<U, F>> mapper) {
|
||||
return changeValueType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <UF extends FailureReason> Attempt<T, UF> transform(UF failureReason) {
|
||||
return new Failure<>(failureReason, getFailureMessage(), this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <U> U transform(Function<T, U> successMapper, Function<F, U> failureMapper) {
|
||||
return failureMapper.apply(failureReason);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <N> N fold(Function<Failure<T, F>, N> failureMapper, Function<T, N> successMapper) {
|
||||
return failureMapper.apply(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Attempt<T, F> onSuccess(Runnable runnable) {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Attempt<T, F> onSuccess(Consumer<T> consumer) {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Attempt<T, F> onFailure(Runnable runnable) {
|
||||
runnable.run();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Attempt<T, F> onFailure(Consumer<Failure<T, F>> consumer) {
|
||||
consumer.accept(this);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Attempt<T, F> onFailureReason(Consumer<F> consumer) {
|
||||
consumer.accept(failureReason);
|
||||
return this;
|
||||
}
|
||||
|
||||
private <U> Attempt<U, F> changeValueType() {
|
||||
@SuppressWarnings("unchecked")
|
||||
Attempt<U, F> mappedFailure = (Attempt<U, F>) this;
|
||||
return mappedFailure;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package org.mvplugins.multiverse.core.world;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import com.dumptruckman.minecraft.util.Logging;
|
||||
@@ -129,7 +130,9 @@ final class WorldConfig {
|
||||
}
|
||||
|
||||
Try<Void> save() {
|
||||
return configHandle.save();
|
||||
return configHandle.save().onFailure(ex ->
|
||||
Logging.warning("Failed to save world config for world '%s'. %s",
|
||||
worldName, ex.getLocalizedMessage()));
|
||||
}
|
||||
|
||||
ConfigurationSection getConfigurationSection() {
|
||||
@@ -417,7 +420,7 @@ final class WorldConfig {
|
||||
|
||||
if (alias.get().isEmpty()) return;
|
||||
|
||||
Try.of(() -> Enum.valueOf(EnglishChatColor.class, color.toUpperCase()))
|
||||
Try.of(() -> Enum.valueOf(EnglishChatColor.class, color.toUpperCase(Locale.ENGLISH)))
|
||||
.map(c -> c.color)
|
||||
.onSuccess(c -> {
|
||||
if (c != ChatColor.WHITE) {
|
||||
@@ -425,7 +428,7 @@ final class WorldConfig {
|
||||
}
|
||||
});
|
||||
|
||||
Try.of(() -> Enum.valueOf(EnglishChatStyle.class, style.toUpperCase()))
|
||||
Try.of(() -> Enum.valueOf(EnglishChatStyle.class, style.toUpperCase(Locale.ENGLISH)))
|
||||
.map(c -> c.color)
|
||||
.onSuccess(s -> {
|
||||
if (s != null) {
|
||||
|
||||
@@ -60,11 +60,15 @@ final class WorldConfigNodes {
|
||||
}
|
||||
|
||||
private <T> ConfigNode<T> node(ConfigNode.Builder<T, ?> nodeBuilder) {
|
||||
nodeBuilder.onSetValue((oldValue, newValue) -> {
|
||||
nodeBuilder.onChange((oldValue, newValue) -> {
|
||||
if (Objects.equals(oldValue, newValue)) return;
|
||||
if (world == null) return;
|
||||
MVWorldPropertyChangedEvent<?> mvWorldPropertyChangeEvent = new MVWorldPropertyChangedEvent<>(
|
||||
world, Option.of(nodeBuilder.name()).getOrElse(nodeBuilder.path()), oldValue, newValue);
|
||||
world,
|
||||
Option.of(nodeBuilder.name()).getOrElse(nodeBuilder.path()),
|
||||
oldValue,
|
||||
newValue
|
||||
);
|
||||
Bukkit.getPluginManager().callEvent(mvWorldPropertyChangeEvent);
|
||||
Logging.finer("MVWorldPropertyChangeEvent fired for world '%s' with name '%s' and value '%s'",
|
||||
world.getName(), nodeBuilder.path(), newValue);
|
||||
@@ -83,7 +87,7 @@ final class WorldConfigNodes {
|
||||
|
||||
final ConfigNode<String> alias = node(ConfigNode.builder("alias", String.class)
|
||||
.defaultValue("")
|
||||
.onSetValue((oldValue, newValue) -> {
|
||||
.onLoadAndChange((oldValue, newValue) -> {
|
||||
if (world == null) return;
|
||||
world.updateColourlessAlias();
|
||||
}));
|
||||
@@ -93,14 +97,14 @@ final class WorldConfigNodes {
|
||||
|
||||
final ConfigNode<Boolean> allowFlight = node(ConfigNode.builder("allow-flight", Boolean.class)
|
||||
.defaultValue(false)
|
||||
.onSetValue((oldValue, newValue) -> {
|
||||
.onLoadAndChange((oldValue, newValue) -> {
|
||||
if (!(world instanceof LoadedMultiverseWorld loadedWorld)) return;
|
||||
enforcementHandler.handleAllFlightEnforcement(loadedWorld);
|
||||
}));
|
||||
|
||||
final ConfigNode<Boolean> allowWeather = node(ConfigNode.builder("allow-weather", Boolean.class)
|
||||
.defaultValue(true)
|
||||
.onSetValue((oldValue, newValue) -> {
|
||||
.onLoadAndChange((oldValue, newValue) -> {
|
||||
if (!(world instanceof LoadedMultiverseWorld loadedWorld)) return;
|
||||
loadedWorld.getBukkitWorld().peek(world -> {
|
||||
if (!world.isClearWeather() && !newValue) {
|
||||
@@ -128,7 +132,7 @@ final class WorldConfigNodes {
|
||||
|
||||
final ConfigNode<Difficulty> difficulty = node(ConfigNode.builder("difficulty", Difficulty.class)
|
||||
.defaultValue(Difficulty.NORMAL)
|
||||
.onSetValue((oldValue, newValue) -> {
|
||||
.onLoadAndChange((oldValue, newValue) -> {
|
||||
if (!(world instanceof LoadedMultiverseWorld loadedWorld)) return;
|
||||
loadedWorld.getBukkitWorld().peek(bukkitWorld -> bukkitWorld.setDifficulty(newValue));
|
||||
}));
|
||||
@@ -175,7 +179,7 @@ final class WorldConfigNodes {
|
||||
|
||||
final ConfigNode<GameMode> gamemode = node(ConfigNode.builder("gamemode", GameMode.class)
|
||||
.defaultValue(GameMode.SURVIVAL)
|
||||
.onSetValue((oldValue, newValue) -> {
|
||||
.onLoadAndChange((oldValue, newValue) -> {
|
||||
if (!(world instanceof LoadedMultiverseWorld loadedWorld)) return;
|
||||
enforcementHandler.handleAllGameModeEnforcement(loadedWorld);
|
||||
}));
|
||||
@@ -194,7 +198,7 @@ final class WorldConfigNodes {
|
||||
final ConfigNode<Boolean> keepSpawnInMemory = node(ConfigNode
|
||||
.builder("keep-spawn-in-memory", Boolean.class)
|
||||
.defaultValue(true)
|
||||
.onSetValue((oldValue, newValue) -> {
|
||||
.onLoadAndChange((oldValue, newValue) -> {
|
||||
if (!(world instanceof LoadedMultiverseWorld loadedWorld)) return;
|
||||
loadedWorld.getBukkitWorld().peek(bukkitWorld -> bukkitWorld.setKeepSpawnInMemory(newValue));
|
||||
}));
|
||||
@@ -208,7 +212,7 @@ final class WorldConfigNodes {
|
||||
|
||||
final ConfigNode<Boolean> pvp = node(ConfigNode.builder("pvp", Boolean.class)
|
||||
.defaultValue(true)
|
||||
.onSetValue((oldValue, newValue) -> {
|
||||
.onLoadAndChange((oldValue, newValue) -> {
|
||||
if (!(world instanceof LoadedMultiverseWorld loadedWorld)) return;
|
||||
loadedWorld.getBukkitWorld().peek(bukkitWorld -> bukkitWorld.setPVP(newValue));
|
||||
}));
|
||||
@@ -237,12 +241,12 @@ final class WorldConfigNodes {
|
||||
final ConfigNode<SpawnLocation> spawnLocation = node(ConfigNode.builder("spawn-location", SpawnLocation.class)
|
||||
.defaultValue(NullSpawnLocation.get())
|
||||
.hidden()
|
||||
.onSetValue((oldValue, newValue) -> {
|
||||
.onLoadAndChange((oldValue, newValue) -> {
|
||||
if (!(world instanceof LoadedMultiverseWorld loadedWorld)) return;
|
||||
if (newValue == null || newValue instanceof NullSpawnLocation) return;
|
||||
loadedWorld.getBukkitWorld().peek(bukkitWorld -> {
|
||||
bukkitWorld.setSpawnLocation(newValue);
|
||||
newValue.setWorld(bukkitWorld);
|
||||
bukkitWorld.setSpawnLocation(newValue);
|
||||
});
|
||||
}));
|
||||
|
||||
@@ -263,7 +267,7 @@ final class WorldConfigNodes {
|
||||
return object.toSection();
|
||||
}
|
||||
})
|
||||
.onSetValue((oldValue, newValue) -> {
|
||||
.onLoadAndChange((oldValue, newValue) -> {
|
||||
newValue.setWorldRef(world);
|
||||
newValue.applyConfigToWorld();
|
||||
}));
|
||||
|
||||
@@ -8,6 +8,7 @@ import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
@@ -16,6 +17,7 @@ import java.util.stream.Collectors;
|
||||
|
||||
import com.dumptruckman.minecraft.util.Logging;
|
||||
import com.google.common.base.Strings;
|
||||
import io.papermc.lib.PaperLib;
|
||||
import io.vavr.control.Option;
|
||||
import io.vavr.control.Try;
|
||||
import jakarta.inject.Inject;
|
||||
@@ -173,12 +175,12 @@ public final class WorldManager {
|
||||
|
||||
private void removeWorldsNotInConfigs(Collection<String> removedWorlds) {
|
||||
removedWorlds.forEach(worldName -> getWorld(worldName)
|
||||
.fold(
|
||||
() -> Attempt.failure(FailureReason.GENERIC, Message.of("world already removed")),
|
||||
world -> removeWorld(RemoveWorldOptions.world(world))
|
||||
)
|
||||
.onFailure(failure -> Logging.severe("Failed to unload world %s: %s", worldName, failure))
|
||||
.onSuccess(success -> Logging.fine("Unloaded world %s as it was removed from config", worldName)));
|
||||
.map(world -> removeWorld(RemoveWorldOptions.world(world)))
|
||||
.getOrElse(() -> worldActionResult(RemoveFailureReason.UNLOAD_FAILED, worldName))
|
||||
.onFailure(failure ->
|
||||
Logging.severe("Failed to unload world %s: %s", worldName, failure))
|
||||
.onSuccess(success ->
|
||||
Logging.fine("Unloaded world %s as it was removed from config", worldName)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -207,30 +209,29 @@ public final class WorldManager {
|
||||
}
|
||||
|
||||
private void importExistingBukkitWorld(World bukkitWorld) {
|
||||
if (bukkitWorld == null || isWorld(bukkitWorld.getName())) {
|
||||
return;
|
||||
}
|
||||
importWorld(ImportWorldOptions.worldName(bukkitWorld.getName())
|
||||
.environment(bukkitWorld.getEnvironment())
|
||||
.generator(generatorProvider.getDefaultGeneratorForWorld(bukkitWorld.getName())))
|
||||
.onFailure(failure ->
|
||||
Logging.severe("Failed to import world %s: %s", bukkitWorld.getName(), failure))
|
||||
.onSuccess(success ->
|
||||
Logging.fine("Imported existing world %s", bukkitWorld.getName()));
|
||||
Option.of(bukkitWorld)
|
||||
.filter(world -> !isWorld(world.getName()))
|
||||
.map(world -> importWorld(
|
||||
ImportWorldOptions.worldName(world.getName())
|
||||
.environment(world.getEnvironment())
|
||||
.generator(generatorProvider.getDefaultGeneratorForWorld(world.getName())))
|
||||
.onFailure(failure ->
|
||||
Logging.severe("Failed to import world %s: %s", world.getName(), failure))
|
||||
.onSuccess(newMVWorld ->
|
||||
Logging.fine("Imported existing world %s", newMVWorld.getName())));
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads all worlds that are set to autoload.
|
||||
*/
|
||||
private void autoLoadWorlds() {
|
||||
getWorlds().forEach(world -> {
|
||||
if (isLoadedWorld(world) || !world.isAutoLoad()) {
|
||||
return;
|
||||
}
|
||||
loadWorld(LoadWorldOptions.world(world))
|
||||
.onFailure(failure ->
|
||||
Logging.severe("Failed to load world %s: %s", world.getName(), failure));
|
||||
});
|
||||
getWorlds().stream()
|
||||
.filter(world -> !isLoadedWorld(world) && world.isAutoLoad())
|
||||
.forEach(world -> loadWorld(LoadWorldOptions.world(world))
|
||||
.onFailure(failure ->
|
||||
Logging.severe("Failed to autoload world %s: %s", world.getName(), failure))
|
||||
.onSuccess(newMVWorld ->
|
||||
Logging.fine("Autoloaded world %s", newMVWorld.getName())));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -271,15 +272,12 @@ public final class WorldManager {
|
||||
.mapAttempt(creator -> addGeneratorToCreator(creator, generatorString))
|
||||
.mapAttempt(this::createBukkitWorld)
|
||||
.transform(CreateFailureReason.WORLD_CREATOR_FAILED)
|
||||
.map(bukkitWorld -> {
|
||||
LoadedMultiverseWorld loadedWorld = newLoadedMultiverseWorld(
|
||||
.map(bukkitWorld -> newLoadedMultiverseWorld(
|
||||
bukkitWorld,
|
||||
generatorString,
|
||||
options.biome(),
|
||||
options.useSpawnAdjust());
|
||||
pluginManager.callEvent(new MVWorldCreatedEvent(loadedWorld));
|
||||
return loadedWorld;
|
||||
});
|
||||
options.useSpawnAdjust()))
|
||||
.peek(loadedWorld -> pluginManager.callEvent(new MVWorldCreatedEvent(loadedWorld)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -295,14 +293,9 @@ public final class WorldManager {
|
||||
} else if (isWorld(worldName)) {
|
||||
return worldActionResult(ImportFailureReason.WORLD_EXIST_UNLOADED, worldName);
|
||||
}
|
||||
|
||||
World bukkitWorld = Bukkit.getWorld(worldName);
|
||||
if (bukkitWorld != null) {
|
||||
// World is already loaded, maybe by another plugin
|
||||
return doImportBukkitWorld(options, bukkitWorld);
|
||||
}
|
||||
|
||||
return validateImportWorldOptions(options).mapAttempt(this::doImportWorld);
|
||||
return Option.of(Bukkit.getWorld(worldName))
|
||||
.map(bukkitWorld -> doImportBukkitWorld(options, bukkitWorld))
|
||||
.getOrElse(() -> validateImportWorldOptions(options).mapAttempt(this::doImportWorld));
|
||||
}
|
||||
|
||||
private Attempt<ImportWorldOptions, ImportFailureReason> validateImportWorldOptions(ImportWorldOptions options) {
|
||||
@@ -325,15 +318,12 @@ public final class WorldManager {
|
||||
.mapAttempt(creator -> addGeneratorToCreator(creator, generatorString))
|
||||
.mapAttempt(this::createBukkitWorld)
|
||||
.transform(ImportFailureReason.WORLD_CREATOR_FAILED)
|
||||
.map(bukkitWorld -> {
|
||||
LoadedMultiverseWorld loadedWorld = newLoadedMultiverseWorld(
|
||||
bukkitWorld,
|
||||
generatorString,
|
||||
options.biome(),
|
||||
options.useSpawnAdjust());
|
||||
pluginManager.callEvent(new MVWorldImportedEvent(loadedWorld));
|
||||
return loadedWorld;
|
||||
});
|
||||
.map(bukkitWorld -> newLoadedMultiverseWorld(
|
||||
bukkitWorld,
|
||||
generatorString,
|
||||
options.biome(),
|
||||
options.useSpawnAdjust()))
|
||||
.peek(loadedWorld -> pluginManager.callEvent(new MVWorldImportedEvent(loadedWorld)));
|
||||
}
|
||||
|
||||
private Attempt<LoadedMultiverseWorld, ImportFailureReason> doImportBukkitWorld(ImportWorldOptions options, World bukkitWorld) {
|
||||
@@ -355,7 +345,7 @@ public final class WorldManager {
|
||||
|
||||
private MultiverseWorld newMultiverseWorld(String worldName, WorldConfig worldConfig) {
|
||||
MultiverseWorld mvWorld = new MultiverseWorld(worldName, worldConfig, config);
|
||||
worldsMap.put(mvWorld.getName(), mvWorld);
|
||||
worldsMap.put(mvWorld.getName().toLowerCase(Locale.ENGLISH), mvWorld);
|
||||
corePermissions.addWorldPermissions(mvWorld);
|
||||
return mvWorld;
|
||||
}
|
||||
@@ -370,12 +360,20 @@ public final class WorldManager {
|
||||
private LoadedMultiverseWorld newLoadedMultiverseWorld(
|
||||
@NotNull World world, @Nullable String generator, @Nullable String biome, boolean adjustSpawn) {
|
||||
WorldConfig worldConfig = worldsConfigManager.addWorldConfig(world.getName());
|
||||
|
||||
// Properties from multiverse input
|
||||
worldConfig.setAdjustSpawn(adjustSpawn);
|
||||
worldConfig.setGenerator(generator == null ? "" : generator);
|
||||
worldConfig.setBiome(biome == null ? "" : biome);
|
||||
|
||||
// Properties from the bukkit world
|
||||
worldConfig.setDifficulty(world.getDifficulty());
|
||||
worldConfig.setKeepSpawnInMemory(world.getKeepSpawnInMemory());
|
||||
worldConfig.setScale(getCoordinateScale(world));
|
||||
|
||||
worldConfig.save();
|
||||
|
||||
MultiverseWorld mvWorld = newMultiverseWorld(world.getName(), worldConfig);
|
||||
newMultiverseWorld(world.getName(), worldConfig);
|
||||
LoadedMultiverseWorld loadedWorld = new LoadedMultiverseWorld(
|
||||
world,
|
||||
worldConfig,
|
||||
@@ -384,20 +382,21 @@ public final class WorldManager {
|
||||
locationManipulation,
|
||||
entityPurger
|
||||
);
|
||||
setDefaultEnvironmentScale(mvWorld);
|
||||
loadedWorldsMap.put(loadedWorld.getName(), loadedWorld);
|
||||
loadedWorldsMap.put(loadedWorld.getName().toLowerCase(Locale.ENGLISH), loadedWorld);
|
||||
saveWorldsConfig();
|
||||
pluginManager.callEvent(new MVWorldLoadedEvent(loadedWorld));
|
||||
return loadedWorld;
|
||||
}
|
||||
|
||||
private void setDefaultEnvironmentScale(MultiverseWorld world) {
|
||||
double scale = switch (world.getEnvironment()) {
|
||||
case NETHER -> 8.0;
|
||||
case THE_END -> 16.0;
|
||||
default -> 1.0;
|
||||
private double getCoordinateScale(World world) {
|
||||
if (PaperLib.isPaper()) {
|
||||
return world.getCoordinateScale();
|
||||
}
|
||||
return switch (world.getEnvironment()) {
|
||||
case NORMAL -> 1;
|
||||
case NETHER -> 8;
|
||||
default -> 1;
|
||||
};
|
||||
world.setScale(scale);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -502,7 +501,7 @@ public final class WorldManager {
|
||||
locationManipulation,
|
||||
entityPurger
|
||||
);
|
||||
loadedWorldsMap.put(loadedWorld.getName(), loadedWorld);
|
||||
loadedWorldsMap.put(loadedWorld.getName().toLowerCase(Locale.ENGLISH), loadedWorld);
|
||||
saveWorldsConfig();
|
||||
pluginManager.callEvent(new MVWorldLoadedEvent(loadedWorld));
|
||||
return Attempt.success(loadedWorld);
|
||||
@@ -691,12 +690,13 @@ public final class WorldManager {
|
||||
|
||||
private Attempt<File, DeleteFailureReason> validateWorldToDelete(
|
||||
@NotNull LoadedMultiverseWorld world) {
|
||||
File worldFolder = world.getBukkitWorld().map(World::getWorldFolder).getOrNull();
|
||||
if (worldFolder == null || !worldNameChecker.isValidWorldFolder(worldFolder)) {
|
||||
Logging.severe("Failed to get world folder for world: " + world.getName());
|
||||
return worldActionResult(DeleteFailureReason.WORLD_FOLDER_NOT_FOUND, world.getName());
|
||||
}
|
||||
return worldActionResult(worldFolder);
|
||||
return world.getBukkitWorld().map(World::getWorldFolder)
|
||||
.filter(worldNameChecker::isValidWorldFolder)
|
||||
.map(this::<File, DeleteFailureReason>worldActionResult)
|
||||
.getOrElse(() -> {
|
||||
Logging.severe("World folder does not exist for world: " + world.getName());
|
||||
return worldActionResult(DeleteFailureReason.WORLD_FOLDER_NOT_FOUND, world.getName());
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -891,9 +891,7 @@ public final class WorldManager {
|
||||
}).onFailure(exception -> {
|
||||
Logging.severe("Failed to create bukkit world: " + worldCreator.name());
|
||||
exception.printStackTrace();
|
||||
}).andFinally(() -> {
|
||||
this.loadTracker.remove(worldCreator.name());
|
||||
}).fold(throwable -> Attempt.failure(WorldCreatorFailureReason.BUKKIT_CREATION_FAILED,
|
||||
}).andFinally(() -> this.loadTracker.remove(worldCreator.name())).fold(throwable -> Attempt.failure(WorldCreatorFailureReason.BUKKIT_CREATION_FAILED,
|
||||
Replace.WORLD.with(worldCreator.name()),
|
||||
Replace.ERROR.with(throwable)),
|
||||
Attempt::success);
|
||||
@@ -968,7 +966,9 @@ public final class WorldManager {
|
||||
* @return The world if it exists.
|
||||
*/
|
||||
public Option<MultiverseWorld> getWorld(@Nullable String worldName) {
|
||||
return getLoadedWorld(worldName).fold(() -> getUnloadedWorld(worldName), Option::of);
|
||||
return getLoadedWorld(worldName)
|
||||
.map(world -> (MultiverseWorld) world)
|
||||
.orElse(() -> getUnloadedWorld(worldName));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -980,7 +980,8 @@ public final class WorldManager {
|
||||
*/
|
||||
public Option<MultiverseWorld> getWorldByNameOrAlias(@Nullable String worldNameOrAlias) {
|
||||
return getLoadedWorldByNameOrAlias(worldNameOrAlias)
|
||||
.fold(() -> getUnloadedWorldByNameOrAlias(worldNameOrAlias), Option::of);
|
||||
.map(world -> (MultiverseWorld) world)
|
||||
.orElse(() -> getUnloadedWorldByNameOrAlias(worldNameOrAlias));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -995,7 +996,8 @@ public final class WorldManager {
|
||||
public Collection<MultiverseWorld> getWorlds() {
|
||||
return worldsMap.values().stream()
|
||||
.map(world -> getLoadedWorld(world)
|
||||
.fold(() -> world, loadedWorld -> loadedWorld))
|
||||
.map(loadedWorld -> (MultiverseWorld) loadedWorld)
|
||||
.getOrElse(world))
|
||||
.toList();
|
||||
}
|
||||
|
||||
@@ -1006,7 +1008,7 @@ public final class WorldManager {
|
||||
* @return True if the world is a world is known to multiverse, but may or may not be loaded.
|
||||
*/
|
||||
public boolean isWorld(@Nullable String worldName) {
|
||||
return worldsMap.containsKey(worldName);
|
||||
return worldName != null && worldsMap.containsKey(worldName.toLowerCase(Locale.ENGLISH));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1016,7 +1018,9 @@ public final class WorldManager {
|
||||
* @return The world if it exists.
|
||||
*/
|
||||
public Option<MultiverseWorld> getUnloadedWorld(@Nullable String worldName) {
|
||||
return isLoadedWorld(worldName) ? Option.none() : Option.of(worldsMap.get(worldName));
|
||||
return isLoadedWorld(worldName)
|
||||
? Option.none()
|
||||
: Option.of(worldName).flatMap(name -> Option.of(worldsMap.get(name.toLowerCase(Locale.ENGLISH))));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1068,7 +1072,7 @@ public final class WorldManager {
|
||||
* @return The multiverse world if it exists.
|
||||
*/
|
||||
public Option<LoadedMultiverseWorld> getLoadedWorld(@Nullable World world) {
|
||||
return world == null ? Option.none() : Option.of(loadedWorldsMap.get(world.getName()));
|
||||
return Option.of(world).flatMap(notNullWorld -> getLoadedWorld(notNullWorld.getName()));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1078,7 +1082,7 @@ public final class WorldManager {
|
||||
* @return The multiverse world if it exists.
|
||||
*/
|
||||
public Option<LoadedMultiverseWorld> getLoadedWorld(@Nullable MultiverseWorld world) {
|
||||
return world == null ? Option.none() : Option.of(loadedWorldsMap.get(world.getName()));
|
||||
return Option.of(world).flatMap(notNullWorld -> getLoadedWorld(notNullWorld.getName()));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1088,7 +1092,8 @@ public final class WorldManager {
|
||||
* @return The multiverse world if it exists.
|
||||
*/
|
||||
public Option<LoadedMultiverseWorld> getLoadedWorld(@Nullable String worldName) {
|
||||
return Option.of(loadedWorldsMap.get(worldName));
|
||||
return Option.of(worldName)
|
||||
.flatMap(name -> Option.of(loadedWorldsMap.get(name.toLowerCase(Locale.ENGLISH))));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1149,7 +1154,7 @@ public final class WorldManager {
|
||||
* @return True if the world is a multiverse world that is loaded.
|
||||
*/
|
||||
public boolean isLoadedWorld(@Nullable String worldName) {
|
||||
return loadedWorldsMap.containsKey(worldName);
|
||||
return worldName != null && loadedWorldsMap.containsKey(worldName.toLowerCase(Locale.ENGLISH));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+27
-17
@@ -1,30 +1,40 @@
|
||||
package org.mvplugins.multiverse.core.world.biomeprovider;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.bukkit.NamespacedKey;
|
||||
import org.bukkit.Registry;
|
||||
import org.bukkit.block.Biome;
|
||||
import org.bukkit.generator.BiomeProvider;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A parser for {@link SingleBiomeProvider}
|
||||
*/
|
||||
final class SingleBiomeProviderParser implements BiomeProviderParser {
|
||||
|
||||
private List<String> biomes;
|
||||
private List<String> biomes;
|
||||
|
||||
@Override
|
||||
public BiomeProvider parseBiomeProvider(@NotNull String worldName, @NotNull String params) {
|
||||
return new SingleBiomeProvider(Biome.valueOf(params.toUpperCase()));
|
||||
}
|
||||
@Override
|
||||
public BiomeProvider parseBiomeProvider(@NotNull String worldName, @NotNull String params) {
|
||||
final NamespacedKey biomeKey = NamespacedKey.fromString(StringUtils.lowerCase(params, Locale.ENGLISH));
|
||||
return new SingleBiomeProvider(Registry.BIOME.get(biomeKey));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<String> suggestParams(@NotNull String currentInput) {
|
||||
if (biomes == null) {
|
||||
biomes = Arrays.stream(Biome.values()).map(biome -> biome.toString().toLowerCase()).toList();
|
||||
}
|
||||
return biomes;
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public Collection<String> suggestParams(@NotNull String currentInput) {
|
||||
if (biomes == null) {
|
||||
final List<String> result = new ArrayList<>();
|
||||
for (Biome biome : Registry.BIOME) {
|
||||
result.add(StringUtils.lowerCase(biome.getKey().getKey(), Locale.ENGLISH));
|
||||
}
|
||||
biomes = result;
|
||||
}
|
||||
return biomes;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -11,6 +11,7 @@ import org.mvplugins.multiverse.core.utils.StringFormatter;
|
||||
import org.mvplugins.multiverse.core.world.MultiverseWorld;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
public final class EntitySpawnConfig {
|
||||
@@ -37,8 +38,9 @@ public final class EntitySpawnConfig {
|
||||
}
|
||||
|
||||
public void applyConfigToWorld() {
|
||||
spawnCategoriesConfig.values()
|
||||
.forEach(SpawnCategoryConfig::applyConfigToWorld);
|
||||
for (SpawnCategory category : SpawnCategory.values()) {
|
||||
getSpawnCategoryConfig(category).applyConfigToWorld();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -52,7 +54,7 @@ public final class EntitySpawnConfig {
|
||||
public ConfigurationSection toSection() {
|
||||
MemoryConfiguration section = new MemoryConfiguration();
|
||||
spawnCategoriesConfig.forEach((spawnCategory, spawnCategoryConfig) -> {
|
||||
section.set(spawnCategory.toString().toLowerCase(), spawnCategoryConfig.saveSection());
|
||||
section.set(spawnCategory.toString().toLowerCase(Locale.ENGLISH), spawnCategoryConfig.saveSection());
|
||||
});
|
||||
return section;
|
||||
}
|
||||
@@ -60,14 +62,18 @@ public final class EntitySpawnConfig {
|
||||
@ApiStatus.Internal
|
||||
public static EntitySpawnConfig fromSection(CoreConfig config, ConfigurationSection section) {
|
||||
Map<SpawnCategory, SpawnCategoryConfig> spawnCategoriesConfig = new LinkedHashMap<>();
|
||||
section.getValues(false).forEach((key, value) -> {
|
||||
if (!(value instanceof ConfigurationSection sectionPart)) {
|
||||
Logging.warning("Invalid spawn category config for " + key + ": " + value);
|
||||
return;
|
||||
Map<String, Object> existingCategories = section.getValues(false);
|
||||
for (SpawnCategory category : SpawnCategory.values()) {
|
||||
Object value = existingCategories.get(category.toString().toLowerCase(Locale.ENGLISH));
|
||||
if (!(value instanceof ConfigurationSection)) {
|
||||
if (value != null) {
|
||||
Logging.warning("Invalid spawn category config for " + category + ": " + value);
|
||||
}
|
||||
value = section.createSection(category.toString());
|
||||
}
|
||||
SpawnCategory spawnCategory = SpawnCategory.valueOf(key.toUpperCase());
|
||||
spawnCategoriesConfig.put(spawnCategory, new SpawnCategoryConfig(config, spawnCategory, sectionPart));
|
||||
});
|
||||
ConfigurationSection sectionPart = (ConfigurationSection) value;
|
||||
spawnCategoriesConfig.put(category, new SpawnCategoryConfig(config, category, sectionPart));
|
||||
}
|
||||
return new EntitySpawnConfig(config, spawnCategoriesConfig);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ package org.mvplugins.multiverse.core.world.entity;
|
||||
|
||||
import com.dumptruckman.minecraft.util.Logging;
|
||||
import io.vavr.control.Try;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.entity.Entity;
|
||||
@@ -14,6 +15,7 @@ import org.mvplugins.multiverse.core.config.node.ConfigNode;
|
||||
import org.mvplugins.multiverse.core.config.node.ListConfigNode;
|
||||
import org.mvplugins.multiverse.core.config.node.Node;
|
||||
import org.mvplugins.multiverse.core.config.node.NodeGroup;
|
||||
import org.mvplugins.multiverse.core.config.node.serializer.NodeSerializer;
|
||||
import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld;
|
||||
import org.mvplugins.multiverse.core.world.MultiverseWorld;
|
||||
|
||||
@@ -70,18 +72,17 @@ public final class SpawnCategoryConfig {
|
||||
Logging.finer("World %s %s skipping setTicksPerSpawns due to core config", world.getName(), spawnCategory);
|
||||
return;
|
||||
}
|
||||
if (!isSpawn()) {
|
||||
if (getExceptions().isEmpty()) {
|
||||
Logging.finer("World %s %s setTicksPerSpawns: 0", world.getName(), spawnCategory);
|
||||
bukkitWorld.setTicksPerSpawns(spawnCategory, 0);
|
||||
} else {
|
||||
Logging.finer("World %s %s setTicksPerSpawns: -1", world.getName(), spawnCategory);
|
||||
bukkitWorld.setTicksPerSpawns(spawnCategory, -1);
|
||||
}
|
||||
} else {
|
||||
Logging.finer("World %s %s setTicksPerSpawns: %d", world.getName(), spawnCategory, getTickRate());
|
||||
bukkitWorld.setTicksPerSpawns(spawnCategory, getTickRate());
|
||||
if (!isSpawn() && getExceptions().isEmpty()) {
|
||||
Logging.finer("World %s %s setTicksPerSpawns: 0", world.getName(), spawnCategory);
|
||||
bukkitWorld.setTicksPerSpawns(spawnCategory, 0);
|
||||
return;
|
||||
}
|
||||
if (getTickRate() == -2) {
|
||||
Logging.finer("World %s %s skipping setTicksPerSpawns as tick-rate is UNSET", world.getName(), spawnCategory);
|
||||
return;
|
||||
}
|
||||
Logging.finer("World %s %s setTicksPerSpawns: %d", world.getName(), spawnCategory, getTickRate());
|
||||
bukkitWorld.setTicksPerSpawns(spawnCategory, getTickRate());
|
||||
}
|
||||
|
||||
private void applySpawnLimit(World bukkitWorld) {
|
||||
@@ -89,6 +90,10 @@ public final class SpawnCategoryConfig {
|
||||
Logging.finer("Skipping World %s %s setSpawnLimit due to core config", world.getName(), spawnCategory);
|
||||
return;
|
||||
}
|
||||
if (getSpawnLimit() == -2) {
|
||||
Logging.finer("World %s %s skipping setSpawnLimit as spawn-limit is UNSET", world.getName(), spawnCategory);
|
||||
return;
|
||||
}
|
||||
Logging.finer("World %s %s setSpawnLimit: %d", world.getName(), spawnCategory, getSpawnLimit());
|
||||
bukkitWorld.setSpawnLimit(spawnCategory, getSpawnLimit());
|
||||
}
|
||||
@@ -161,19 +166,39 @@ public final class SpawnCategoryConfig {
|
||||
|
||||
final ConfigNode<Boolean> spawn = node(ConfigNode.builder("spawn", Boolean.class)
|
||||
.defaultValue(true)
|
||||
.onSetValue((oldValue, newValue) -> applyConfigToWorld())
|
||||
.onLoadAndChange((oldValue, newValue) -> applyConfigToWorld())
|
||||
.build());
|
||||
|
||||
final ConfigNode<Integer> tickRate = node(ConfigNode.builder("tick-rate", Integer.class)
|
||||
.defaultValue(-1)
|
||||
.suggester(input -> List.of("-1", "10", "100", "400", "1000"))
|
||||
.onSetValue((oldValue, newValue) -> applyConfigToWorld())
|
||||
.defaultValue(-2)
|
||||
.suggester(input -> List.of("@unset", "@bukkit", "10", "100", "400", "1000"))
|
||||
.serializer(SpawnValueSerializer.INSTANCE)
|
||||
.onLoadAndChange((oldValue, newValue) -> applyConfigToWorld())
|
||||
.onChange((sender, oldValue, newValue) -> {
|
||||
if (!config.getApplyEntitySpawnRate()) {
|
||||
sender.sendMessage(ChatColor.RED + "Warning: Changing tick rates has no effect because " +
|
||||
"'apply-entity-spawn-rate' is disabled in the core config.");
|
||||
} else if (newValue == -2) {
|
||||
sender.sendMessage(ChatColor.YELLOW + "Note: Setting tick-rate to '@unset' may not reset to " +
|
||||
"the server default until the world is reloaded or server is restarted.");
|
||||
}
|
||||
})
|
||||
.build());
|
||||
|
||||
final ConfigNode<Integer> spawnLimit = node(ConfigNode.builder("spawn-limit", Integer.class)
|
||||
.defaultValue(-1)
|
||||
.suggester(input -> List.of("-1", "10", "100", "400", "1000"))
|
||||
.onSetValue((oldValue, newValue) -> applyConfigToWorld())
|
||||
.defaultValue(-2)
|
||||
.suggester(input -> List.of("@unset", "@bukkit", "10", "100", "400", "1000"))
|
||||
.serializer(SpawnValueSerializer.INSTANCE)
|
||||
.onLoadAndChange((oldValue, newValue) -> applyConfigToWorld())
|
||||
.onChange((sender, oldValue, newValue) -> {
|
||||
if (!config.getApplyEntitySpawnLimit()) {
|
||||
sender.sendMessage(ChatColor.RED + "Warning: Changing spawn limits has no effect because " +
|
||||
"'apply-entity-spawn-limit' is disabled in the core config.");
|
||||
} else if (newValue == -2) {
|
||||
sender.sendMessage(ChatColor.YELLOW + "Note: Setting spawn-limit to '@unset' may not reset to " +
|
||||
"the server default until the world is reloaded or server is restarted.");
|
||||
}
|
||||
})
|
||||
.build());
|
||||
|
||||
final ListConfigNode<EntityType> exceptions = node(ListConfigNode.listBuilder("exceptions", EntityType.class)
|
||||
@@ -181,7 +206,32 @@ public final class SpawnCategoryConfig {
|
||||
.itemSuggester(input -> SpawnCategoryMapper.getEntityTypes(spawnCategory).stream()
|
||||
.map(EntityType::name)
|
||||
.toList())
|
||||
.onSetValue((oldValue, newValue) -> applyConfigToWorld())
|
||||
.onLoadAndChange((oldValue, newValue) -> applyConfigToWorld())
|
||||
.build());
|
||||
}
|
||||
|
||||
private static class SpawnValueSerializer implements NodeSerializer<Integer> {
|
||||
private static final SpawnValueSerializer INSTANCE = new SpawnValueSerializer();
|
||||
|
||||
@Override
|
||||
public Integer deserialize(Object object, Class<Integer> type) {
|
||||
String str = String.valueOf(object);
|
||||
if (str.equalsIgnoreCase("@unset")) {
|
||||
return -2;
|
||||
}
|
||||
if (str.equalsIgnoreCase("@bukkit")) {
|
||||
return -1;
|
||||
}
|
||||
return Integer.parseInt(str);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object serialize(Integer object, Class<Integer> type) {
|
||||
return switch (object) {
|
||||
case -2 -> "@unset";
|
||||
case -1 -> "@bukkit";
|
||||
default -> object;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -95,15 +95,19 @@ public final class GeneratorProvider implements Listener {
|
||||
private boolean testIsGeneratorPlugin(Plugin plugin) {
|
||||
String worldName = Bukkit.getWorlds().stream().findFirst().map(World::getName).orElse("world");
|
||||
try {
|
||||
return plugin.getDefaultWorldGenerator(worldName, null) != null;
|
||||
return plugin.getDefaultWorldGenerator(worldName, "") != null;
|
||||
} catch (UnsupportedOperationException e) {
|
||||
// Some plugins throw this if they don't support world generation
|
||||
return false;
|
||||
} catch (Throwable t) {
|
||||
Logging.warning("Plugin %s threw an exception when testing if it is a generator plugin! ",
|
||||
plugin.getName());
|
||||
Logging.warning("This is NOT a bug in Multiverse. Do NOT report this to Multiverse support.");
|
||||
t.printStackTrace();
|
||||
Logging.warning("Error by plugin %s: %s", plugin.getName(), t.getMessage());
|
||||
Logging.warning("This is NOT a bug in Multiverse. Do NOT report this to Multiverse support. " +
|
||||
"Enable debug mode with `/mv debug 1` to see the full stack trace.");
|
||||
if (coreConfig.getGlobalDebug() >= 1) {
|
||||
t.printStackTrace();
|
||||
}
|
||||
// Assume it's a generator plugin since it tried to do something, most likely the id is wrong.
|
||||
return true;
|
||||
}
|
||||
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
package org.mvplugins.multiverse.core.world.helpers;
|
||||
|
||||
import io.vavr.control.Option;
|
||||
import jakarta.inject.Inject;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.PlayerChangedWorldEvent;
|
||||
import org.bukkit.event.player.PlayerJoinEvent;
|
||||
import org.bukkit.event.player.PlayerQuitEvent;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.UnmodifiableView;
|
||||
import org.jvnet.hk2.annotations.Service;
|
||||
import org.mvplugins.multiverse.core.MultiverseCore;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* Tracks which players are in which worlds, using a thread-safe map.
|
||||
* This allows async access to online players list and the world they are in.
|
||||
*
|
||||
* @since 5.4
|
||||
*/
|
||||
@ApiStatus.AvailableSince("5.4")
|
||||
@Service
|
||||
public final class ConcurrentPlayerWorldTracker implements Listener {
|
||||
|
||||
private final Map<String, String> playerWorldMap;
|
||||
|
||||
@Inject
|
||||
ConcurrentPlayerWorldTracker(@NotNull MultiverseCore plugin) {
|
||||
this.playerWorldMap = new ConcurrentHashMap<>();
|
||||
plugin.getServer().getPluginManager().registerEvents(this, plugin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an unmodifiable collection of all online player names on the server.
|
||||
*
|
||||
* @return Unmodifiable collection of online player names.
|
||||
*
|
||||
* @since 5.4
|
||||
*/
|
||||
@ApiStatus.AvailableSince("5.4")
|
||||
@NotNull
|
||||
@UnmodifiableView
|
||||
public Collection<String> getOnlinePlayers() {
|
||||
return Collections.unmodifiableCollection(playerWorldMap.keySet());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the world name a player is currently in.
|
||||
*
|
||||
* @param playerName Name of the player.
|
||||
* @return World name the player is in, or null if the player is not online.
|
||||
*
|
||||
* @since 5.4
|
||||
*/
|
||||
@ApiStatus.AvailableSince("5.4")
|
||||
@NotNull
|
||||
public Option<String> getPlayerWorld(String playerName) {
|
||||
return Option.of(playerWorldMap.get(playerName));
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.LOWEST)
|
||||
private void onPlayerJoin(PlayerJoinEvent event) {
|
||||
setPlayerWorld(event.getPlayer());
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.LOWEST)
|
||||
private void onPlayerChangedWorld(@NotNull PlayerChangedWorldEvent event) {
|
||||
setPlayerWorld(event.getPlayer());
|
||||
}
|
||||
|
||||
private void setPlayerWorld(Player player) {
|
||||
String playerName = player.getName();
|
||||
String worldName = player.getWorld().getName();
|
||||
playerWorldMap.put(playerName, worldName);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
private void onPlayerQuit(@NotNull PlayerQuitEvent event) {
|
||||
String playerName = event.getPlayer().getName();
|
||||
playerWorldMap.remove(playerName);
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,15 @@
|
||||
package org.mvplugins.multiverse.core.world.helpers;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import io.vavr.control.Option;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jvnet.hk2.annotations.Service;
|
||||
import org.mvplugins.multiverse.core.utils.REPatterns;
|
||||
|
||||
/**
|
||||
* <p>Utility class in helping to check the status of a world name and it's associated world folder.</p>
|
||||
@@ -19,11 +20,11 @@ import org.jvnet.hk2.annotations.Service;
|
||||
@Service
|
||||
public final class WorldNameChecker {
|
||||
|
||||
private static final Pattern WORLD_NAME_PATTERN = Pattern.compile("[a-zA-Z0-9/._-]+");
|
||||
private static final Set<String> BLACKLIST_NAMES = Set.of(
|
||||
"cache",
|
||||
"config",
|
||||
"crash-reports",
|
||||
"libraries",
|
||||
"logs",
|
||||
"plugins",
|
||||
"versions");
|
||||
@@ -46,18 +47,21 @@ public final class WorldNameChecker {
|
||||
*/
|
||||
@NotNull
|
||||
public NameStatus checkName(@Nullable String worldName) {
|
||||
return Option.of(worldName).map(name -> {
|
||||
if (name.isEmpty()) {
|
||||
return NameStatus.EMPTY;
|
||||
}
|
||||
if (BLACKLIST_NAMES.contains(name)) {
|
||||
return NameStatus.BLACKLISTED;
|
||||
}
|
||||
if (!WORLD_NAME_PATTERN.matcher(name).matches()) {
|
||||
return NameStatus.INVALID_CHARS;
|
||||
}
|
||||
return NameStatus.VALID;
|
||||
}).getOrElse(NameStatus.EMPTY);
|
||||
return Option.of(worldName)
|
||||
.map(name -> name.toLowerCase(Locale.ENGLISH))
|
||||
.map(name -> {
|
||||
if (name.isEmpty()) {
|
||||
return NameStatus.EMPTY;
|
||||
}
|
||||
if (BLACKLIST_NAMES.contains(name)) {
|
||||
return NameStatus.BLACKLISTED;
|
||||
}
|
||||
if (!REPatterns.NAMESPACE_KEY.matcher(name).matches()) {
|
||||
return NameStatus.INVALID_CHARS;
|
||||
}
|
||||
return NameStatus.VALID;
|
||||
})
|
||||
.getOrElse(NameStatus.EMPTY);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -130,7 +134,7 @@ public final class WorldNameChecker {
|
||||
* @return True if it looks like a world, else false.
|
||||
*/
|
||||
private boolean folderHasDat(@NotNull File worldFolder) {
|
||||
File[] files = worldFolder.listFiles((file, name) -> name.toLowerCase().endsWith(".dat"));
|
||||
File[] files = worldFolder.listFiles((file, name) -> name.toLowerCase(Locale.ENGLISH).endsWith(".dat"));
|
||||
return files != null && files.length > 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -94,7 +94,6 @@ public final class CloneWorldOptions implements KeepWorldSettingsOptions {
|
||||
return keepWorldConfig;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets whether to keep the world border of the world during cloning.
|
||||
*
|
||||
|
||||
@@ -38,7 +38,7 @@ public final class UnloadWorldOptions {
|
||||
* Sets whether to unload the bukkit world.
|
||||
* <br />
|
||||
* By setting this to false, multiverse will essentially untrack the world, but the world itself is still loaded.
|
||||
* This should be only used in edge cases where the world is used by other plugins byt you don't want multiverse
|
||||
* This should be only used in edge cases where the world is used by other plugins but you don't want multiverse
|
||||
* to handle it.
|
||||
*
|
||||
* @param unloadBukkitWorldInput Whether to unload the bukkit world.
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
package org.mvplugins.multiverse.core
|
||||
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertNotNull
|
||||
|
||||
open class MockBukkitTest : TestWithMockBukkit() {
|
||||
|
||||
@Test
|
||||
fun `MockBukkit loads the plugin`() {
|
||||
assertNotNull(multiverseCore)
|
||||
}
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
package org.mvplugins.multiverse.core
|
||||
|
||||
import com.dumptruckman.minecraft.util.Logging
|
||||
import org.bukkit.Location
|
||||
import org.bukkit.configuration.MemorySection
|
||||
import org.bukkit.configuration.file.YamlConfiguration
|
||||
import org.mockbukkit.mockbukkit.MockBukkit
|
||||
import org.mvplugins.multiverse.core.inject.PluginServiceLocator
|
||||
import org.mvplugins.multiverse.core.mock.MVServerMock
|
||||
import kotlin.test.*
|
||||
|
||||
/**
|
||||
* Basic abstract test class that sets up MockBukkit and MultiverseCore.
|
||||
*/
|
||||
abstract class TestWithMockBukkit {
|
||||
|
||||
protected lateinit var server: MVServerMock
|
||||
protected lateinit var multiverseCore: MultiverseCore
|
||||
protected lateinit var serviceLocator : PluginServiceLocator
|
||||
|
||||
@BeforeTest
|
||||
fun setUpMockBukkit() {
|
||||
TestingMode.enable()
|
||||
server = MockBukkit.mock(MVServerMock())
|
||||
multiverseCore = MockBukkit.load(MultiverseCore::class.java)
|
||||
Logging.setDebugLevel(3)
|
||||
serviceLocator = multiverseCore.serviceLocator
|
||||
assertNotNull(server.commandMap)
|
||||
}
|
||||
|
||||
@AfterTest
|
||||
fun tearDownMockBukkit() {
|
||||
MockBukkit.unmock()
|
||||
}
|
||||
|
||||
fun getResourceAsText(path: String): String? = object {}.javaClass.getResource(path)?.readText()
|
||||
|
||||
fun writeResourceFileToPluginDataFolder(resourcePath: String, dataPath: String) {
|
||||
val resourceString = getResourceAsText(resourcePath)
|
||||
assertNotNull(resourceString)
|
||||
multiverseCore.dataFolder.toPath().resolve(dataPath).toFile().writeText(resourceString)
|
||||
}
|
||||
|
||||
fun assertConfigEquals(expectedPath: String, actualPath: String) {
|
||||
val actualString = multiverseCore.dataFolder.toPath().resolve(actualPath).toFile().readText()
|
||||
val expectedString = getResourceAsText(expectedPath)
|
||||
assertNotNull(expectedString)
|
||||
|
||||
val actualYaml = YamlConfiguration()
|
||||
actualYaml.loadFromString(actualString)
|
||||
val actualYamlKeys = HashSet(actualYaml.getKeys(true))
|
||||
|
||||
val expectedYaml = YamlConfiguration()
|
||||
expectedYaml.loadFromString(expectedString)
|
||||
val expectedYamlKeys = HashSet(expectedYaml.getKeys(true))
|
||||
|
||||
for (key in expectedYamlKeys) {
|
||||
assertNotNull(actualYamlKeys.remove(key), "Key $key is missing in actual config")
|
||||
val actualValue = actualYaml.get(key)
|
||||
if (actualValue is MemorySection) {
|
||||
continue
|
||||
}
|
||||
assertEquals(expectedYaml.get(key), actualYaml.get(key), "Value for $key is different.")
|
||||
}
|
||||
for (key in actualYamlKeys) {
|
||||
assertNull(actualYaml.get(key), "Key $key is present in actual config when it should be empty.")
|
||||
}
|
||||
|
||||
assertEquals(0, actualYamlKeys.size,
|
||||
"Actual config has more keys than expected config. The following keys are missing: $actualYamlKeys")
|
||||
}
|
||||
|
||||
fun assertLocationEquals(expected: Location?, actual: Location?) {
|
||||
assertEquals(expected?.world, actual?.world, "Worlds don't match for location comparison ($expected, $actual)")
|
||||
assertEquals(expected?.x, actual?.x, "X values don't match for location comparison ($expected, $actual)")
|
||||
assertEquals(expected?.y, actual?.y, "Y values don't match for location comparison ($expected, $actual)")
|
||||
assertEquals(expected?.z, actual?.z, "Z values don't match for location comparison ($expected, $actual)")
|
||||
assertEquals(expected?.yaw, actual?.yaw, "Yaw values don't match for location comparison ($expected, $actual)")
|
||||
assertEquals(expected?.pitch, actual?.pitch, "Pitch values don't match for location comparison ($expected, $actual)")
|
||||
}
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
package org.mvplugins.multiverse.core.anchor
|
||||
|
||||
import org.mvplugins.multiverse.core.TestWithMockBukkit
|
||||
import org.mvplugins.multiverse.core.world.location.UnloadedWorldLocation
|
||||
import kotlin.test.*
|
||||
|
||||
class AnchorManagerTest : TestWithMockBukkit() {
|
||||
|
||||
private lateinit var anchorManager: AnchorManager
|
||||
|
||||
@BeforeTest
|
||||
fun setUp() {
|
||||
anchorManager = serviceLocator.getActiveService(AnchorManager::class.java).takeIf { it != null } ?: run {
|
||||
throw IllegalStateException("AnchorManager is not available as a service") }
|
||||
|
||||
writeResourceFileToPluginDataFolder("/anchors/default_anchors.yml", "anchors.yml")
|
||||
assertTrue(anchorManager.loadAnchors().isSuccess)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Loaded correct Anchors from file`() {
|
||||
assertEquals(2, anchorManager.allAnchors.size)
|
||||
|
||||
val a1 = anchorManager.getAnchor("a1").orNull
|
||||
assertNotNull(a1)
|
||||
assertLocationEquals(UnloadedWorldLocation("w1", 0.5, 70.0, 0.5, -68.25f, 10.35f), a1.location)
|
||||
|
||||
val a2 = anchorManager.getAnchor("a2").orNull
|
||||
assertNotNull(a2)
|
||||
assertLocationEquals(UnloadedWorldLocation("w2", 3.3, 9.9, 6.6, -1.1f, 2.0f), a2.location)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Set new location for existing anchor`() {
|
||||
val a1 = anchorManager.getAnchor("a1").orNull
|
||||
assertNotNull(a1)
|
||||
val newLocation = UnloadedWorldLocation("w1", 1.0, 1.0, 1.0, 1.0f, 1.0f)
|
||||
anchorManager.setAnchor("a1", newLocation)
|
||||
assertLocationEquals(newLocation, a1.location)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Create new anchor`() {
|
||||
val newLocation = UnloadedWorldLocation("w3", 3.0, 3.0, 3.0, -3.0f, 3.0f)
|
||||
anchorManager.setAnchor("a3", newLocation)
|
||||
assertLocationEquals(newLocation, anchorManager.getAnchor("a3").orNull?.location)
|
||||
|
||||
assertConfigEquals("/anchors/anchors_saved.yml", "anchors.yml")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Delete anchor`() {
|
||||
val a1 = anchorManager.getAnchor("a1").orNull
|
||||
assertNotNull(a1)
|
||||
anchorManager.deleteAnchor(a1)
|
||||
assertNull(anchorManager.getAnchor("a1").orNull)
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
package org.mvplugins.multiverse.core.api
|
||||
|
||||
import org.mvplugins.multiverse.core.MultiverseCoreApi
|
||||
import org.mvplugins.multiverse.core.TestWithMockBukkit
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertNotNull
|
||||
|
||||
class MultiverseCoreApiTest : TestWithMockBukkit() {
|
||||
|
||||
@Test
|
||||
fun `MultiverseCoreApi is available`() {
|
||||
val api = assertNotNull(MultiverseCoreApi.get())
|
||||
assertNotNull(api.blockSafety)
|
||||
assertNotNull(api.destinationsProvider)
|
||||
assertNotNull(api.generatorProvider)
|
||||
assertNotNull(api.locationManipulation)
|
||||
assertNotNull(api.coreConfig)
|
||||
assertNotNull(api.safetyTeleporter)
|
||||
assertNotNull(api.worldManager)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Get MultiverseCoreApi from bukkit service`() {
|
||||
val registeredService = server.servicesManager.getRegistration(MultiverseCoreApi::class.java)
|
||||
val api = assertNotNull(registeredService?.provider)
|
||||
testApiAccess(api)
|
||||
}
|
||||
|
||||
private fun testApiAccess(api: MultiverseCoreApi?) {
|
||||
assertNotNull(api?.anchorManager)
|
||||
assertNotNull(api?.blockSafety)
|
||||
assertNotNull(api?.destinationsProvider)
|
||||
assertNotNull(api?.mvEconomist)
|
||||
assertNotNull(api?.generatorProvider)
|
||||
assertNotNull(api?.locationManipulation)
|
||||
assertNotNull(api?.coreConfig)
|
||||
assertNotNull(api?.safetyTeleporter)
|
||||
assertNotNull(api?.worldManager)
|
||||
}
|
||||
}
|
||||
@@ -1,361 +0,0 @@
|
||||
package org.mvplugins.multiverse.core.command
|
||||
|
||||
import com.natpryce.hamkrest.assertion.assertThat
|
||||
import com.natpryce.hamkrest.containsSubstring
|
||||
import com.natpryce.hamkrest.endsWith
|
||||
import org.bukkit.Bukkit
|
||||
import org.bukkit.command.CommandSender
|
||||
import org.junit.jupiter.api.DisplayName
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.mockbukkit.mockbukkit.entity.PlayerMock
|
||||
import org.mockito.kotlin.argumentCaptor
|
||||
import org.mockito.kotlin.spy
|
||||
import org.mockito.kotlin.verify
|
||||
import org.mvplugins.multiverse.core.TestWithMockBukkit
|
||||
import org.mvplugins.multiverse.core.config.CoreConfig
|
||||
import org.mvplugins.multiverse.core.locale.MVCorei18n
|
||||
import org.mvplugins.multiverse.core.locale.PluginLocales
|
||||
import org.mvplugins.multiverse.core.locale.message.Message
|
||||
import org.mvplugins.multiverse.core.locale.message.MessageReplacement.replace
|
||||
import java.util.Locale
|
||||
import kotlin.test.*
|
||||
|
||||
class LocalizationTest : TestWithMockBukkit() {
|
||||
|
||||
private lateinit var locales: PluginLocales
|
||||
private lateinit var commandManager: MVCommandManager
|
||||
|
||||
@BeforeTest
|
||||
fun setUpLocale() {
|
||||
commandManager = assertNotNull(serviceLocator.getActiveService(MVCommandManager::class.java))
|
||||
locales = commandManager.locales
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("Given a Message with only a non-localized message")
|
||||
inner class BasicMessage {
|
||||
|
||||
private val messageString = "This is a test message"
|
||||
private val message = Message.of(messageString)
|
||||
|
||||
@Test
|
||||
fun `The raw message should be the same as the original`() {
|
||||
assertEquals(messageString, message.raw())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `The formatted message should be the same as the original`() {
|
||||
assertEquals(messageString, message.formatted())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `The formatted message with PluginLocales should be the same as the original`() {
|
||||
assertEquals(messageString, message.formatted(locales))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `The formatted message with PluginLocales for a CommandIssuer should be the same as the original`() {
|
||||
assertEquals(messageString, message.formatted(locales, commandManager.consoleCommandIssuer))
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("And a command sender is provided")
|
||||
inner class WithCommandSender {
|
||||
|
||||
private lateinit var sender: CommandSender
|
||||
private lateinit var issuer: MVCommandIssuer
|
||||
|
||||
@BeforeTest
|
||||
fun setUp() {
|
||||
sender = spy(Bukkit.getConsoleSender())
|
||||
issuer = commandManager.getCommandIssuer(sender)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Sending the issuer the message should send the formatted message to the sender`() {
|
||||
issuer.sendInfo(message);
|
||||
|
||||
verify(sender).sendMessage("§9§9$messageString")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("Given a Message with a non-localized message and one replacement")
|
||||
inner class MessageWithOneReplacement {
|
||||
|
||||
private val replacementKey = "{count}"
|
||||
private val messageString = "This is a test message with $replacementKey replacement"
|
||||
private val replacedMessageString = messageString.replace(replacementKey, "one")
|
||||
|
||||
private val message = Message.of(messageString, replace(replacementKey).with("one"))
|
||||
|
||||
@Test
|
||||
fun `The raw message should be the same as the original`() {
|
||||
assertEquals(messageString, message.raw())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `The formatted message should be the replaced message string`() {
|
||||
assertEquals(replacedMessageString, message.formatted())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `The formatted message with PluginLocales should be the replaced message string`() {
|
||||
assertEquals(replacedMessageString, message.formatted(locales))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `The formatted message with PluginLocales for a CommandIssuer should be the replaced message string`() {
|
||||
assertEquals(replacedMessageString, message.formatted(locales, commandManager.consoleCommandIssuer))
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("And a command sender is provided")
|
||||
inner class WithCommandSender {
|
||||
|
||||
private lateinit var sender: CommandSender
|
||||
private lateinit var issuer: MVCommandIssuer
|
||||
|
||||
@BeforeTest
|
||||
fun setUp() {
|
||||
sender = spy(Bukkit.getConsoleSender())
|
||||
issuer = commandManager.getCommandIssuer(sender)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Sending the issuer the message should send the formatted message to the sender`() {
|
||||
issuer.sendInfo(message);
|
||||
|
||||
verify(sender).sendMessage("§9§9$replacedMessageString")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("Given a Message with a non-localized message and two replacements")
|
||||
inner class MessageWithTwoReplacements {
|
||||
|
||||
private val replacementKey1 = "{thing1}"
|
||||
private val replacementKey2 = "{thing2}"
|
||||
private val messageString = "$replacementKey1 $replacementKey2"
|
||||
private val replacedMessageString = messageString
|
||||
.replace(replacementKey1, "one")
|
||||
.replace(replacementKey2, "two")
|
||||
|
||||
private val message = Message.of(
|
||||
messageString,
|
||||
replace(replacementKey1).with("one"),
|
||||
replace(replacementKey2).with("two"),
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `The raw message should be the same as the original`() {
|
||||
assertEquals(messageString, message.raw())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `The formatted message should be the replaced message string`() {
|
||||
assertEquals(replacedMessageString, message.formatted())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `The formatted message with PluginLocales should be the replaced message string`() {
|
||||
assertEquals(replacedMessageString, message.formatted(locales))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `The formatted message with PluginLocales for a CommandIssuer should be the replaced message string`() {
|
||||
assertEquals(replacedMessageString, message.formatted(locales, commandManager.consoleCommandIssuer))
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("And a command sender is provided")
|
||||
inner class WithCommandSender {
|
||||
|
||||
private lateinit var sender: CommandSender
|
||||
private lateinit var issuer: MVCommandIssuer
|
||||
|
||||
@BeforeTest
|
||||
fun setUp() {
|
||||
sender = spy(Bukkit.getConsoleSender())
|
||||
issuer = commandManager.getCommandIssuer(sender)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Sending the issuer the message should send the formatted message to the sender`() {
|
||||
issuer.sendInfo(message);
|
||||
|
||||
verify(sender).sendMessage("§9§9$replacedMessageString")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("Given a Message with a localized message with one replacement")
|
||||
inner class LocalizedMessage {
|
||||
|
||||
private val replacementKey = "{world}"
|
||||
private val replacementValue = "World"
|
||||
private val messageString = "Hello $replacementKey!"
|
||||
private val replacedMessageString = messageString.replace(replacementKey, replacementValue)
|
||||
|
||||
private val message = MVCorei18n.CLONE_SUCCESS
|
||||
.bundle(messageString, replace(replacementKey).with(replacementValue))
|
||||
|
||||
@Test
|
||||
fun `The raw message should be the same as the original`() {
|
||||
assertEquals(messageString, message.raw())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `The formatted message should be the replaced original string`() {
|
||||
assertEquals(replacedMessageString, message.formatted())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `The formatted message with PluginLocales should be different from the replaced original string`() {
|
||||
assertNotEquals(replacedMessageString, message.formatted(locales))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `The formatted message with PluginLocales should have performed replacement`() {
|
||||
assertThat(message.formatted(locales), !containsSubstring(replacementKey))
|
||||
assertThat(message.formatted(locales), containsSubstring(replacementValue))
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("And a command sender is provided")
|
||||
inner class WithCommandSender {
|
||||
|
||||
private lateinit var sender: CommandSender
|
||||
private lateinit var issuer: MVCommandIssuer
|
||||
|
||||
@BeforeTest
|
||||
fun setUp() {
|
||||
sender = spy(Bukkit.getConsoleSender())
|
||||
issuer = commandManager.getCommandIssuer(sender)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Sending the issuer the message should send the formatted message to the sender`() {
|
||||
issuer.sendInfo(message);
|
||||
|
||||
val sentMessage = argumentCaptor<String> {
|
||||
verify(sender).sendMessage(capture())
|
||||
}.firstValue
|
||||
|
||||
assertNotEquals(replacedMessageString, sentMessage)
|
||||
assertThat(sentMessage, !containsSubstring(replacementKey))
|
||||
assertThat(sentMessage, containsSubstring(replacementValue))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class WithMessagesAsReplacement {
|
||||
private val replacementKey = "{world}"
|
||||
private val replacementValue = Message.of(
|
||||
MVCorei18n.GENERIC_SUCCESS, "success")
|
||||
private val messageString = "Hello $replacementKey!"
|
||||
private val replacedMessageString = "Hello success!"
|
||||
private val replacedMessageStringLocale = "World cloned to 'Success!'!"
|
||||
|
||||
private val message = MVCorei18n.CLONE_SUCCESS
|
||||
.bundle(messageString, replace(replacementKey).with(replacementValue))
|
||||
|
||||
@Test
|
||||
fun `The raw message should be the same as the original`() {
|
||||
assertEquals(messageString, message.raw())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `The formatted message should be the replaced original string`() {
|
||||
assertEquals(replacedMessageString, message.formatted())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `The formatted message with PluginLocales should be different from the replaced original string`() {
|
||||
assertThat(message.formatted(locales), endsWith(replacedMessageStringLocale))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `The formatted message with PluginLocales should have performed replacement`() {
|
||||
assertThat(message.formatted(locales), !containsSubstring(replacementKey))
|
||||
assertThat(message.formatted(locales), containsSubstring("Success!"))
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("And a command sender is provided")
|
||||
inner class WithCommandSender {
|
||||
|
||||
private lateinit var sender: CommandSender
|
||||
private lateinit var issuer: MVCommandIssuer
|
||||
|
||||
@BeforeTest
|
||||
fun setUp() {
|
||||
sender = spy(Bukkit.getConsoleSender())
|
||||
issuer = commandManager.getCommandIssuer(sender)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Sending the issuer the message should send the formatted message to the sender`() {
|
||||
issuer.sendInfo(message);
|
||||
|
||||
val sentMessage = argumentCaptor<String> {
|
||||
verify(sender).sendMessage(capture())
|
||||
}.firstValue
|
||||
|
||||
assertNotEquals(replacedMessageString, sentMessage)
|
||||
assertThat(sentMessage, !containsSubstring(replacementKey))
|
||||
assertThat(sentMessage, containsSubstring(replacedMessageStringLocale))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class LocaleConfiguration {
|
||||
private lateinit var config: CoreConfig
|
||||
private lateinit var player: PlayerMock
|
||||
private lateinit var issuer: MVCommandIssuer
|
||||
|
||||
@BeforeTest
|
||||
fun setUp() {
|
||||
config = assertNotNull(serviceLocator.getActiveService(CoreConfig::class.java))
|
||||
player = server.addPlayer("benji_0224")
|
||||
issuer = commandManager.getCommandIssuer(player)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Change default locale to chinese without per player locale should get chinese message`() {
|
||||
config.perPlayerLocale = false
|
||||
config.defaultLocale = Locale.CHINESE
|
||||
assertEquals("成功!", Message.of(
|
||||
MVCorei18n.GENERIC_SUCCESS, "").formatted(locales, issuer))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Change default locale to chinese with per player locale should get default english message`() {
|
||||
config.perPlayerLocale = true
|
||||
config.defaultLocale = Locale.CHINESE
|
||||
assertEquals("Success!", Message.of(
|
||||
MVCorei18n.GENERIC_SUCCESS, "").formatted(locales, issuer))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `PerPlayerLocale enabled - Player with chinese locale should get chinese message`() {
|
||||
config.perPlayerLocale = true
|
||||
player.setLocale(Locale.CHINESE)
|
||||
assertEquals("成功!", Message.of(
|
||||
MVCorei18n.GENERIC_SUCCESS, "").formatted(locales, issuer))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `PerPlayerLocale disabled - Player with chinese locale should get default english message`() {
|
||||
config.perPlayerLocale = false
|
||||
player.setLocale(Locale.CHINESE)
|
||||
assertEquals("Success!", Message.of(
|
||||
MVCorei18n.GENERIC_SUCCESS, "").formatted(locales, issuer))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
package org.mvplugins.multiverse.core.commands
|
||||
|
||||
import org.bukkit.permissions.PermissionAttachment
|
||||
import org.mockbukkit.mockbukkit.command.ConsoleCommandSenderMock
|
||||
import org.mockbukkit.mockbukkit.entity.PlayerMock
|
||||
import org.mvplugins.multiverse.core.TestWithMockBukkit
|
||||
import org.mvplugins.multiverse.core.command.MVCommandManager
|
||||
import org.mvplugins.multiverse.core.locale.PluginLocales
|
||||
import org.mvplugins.multiverse.core.locale.message.Message
|
||||
import org.mvplugins.multiverse.core.utils.text.ChatTextFormatter
|
||||
import org.mvplugins.multiverse.core.world.WorldManager
|
||||
import org.mvplugins.multiverse.core.world.options.CreateWorldOptions
|
||||
import kotlin.test.BeforeTest
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertNotNull
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
abstract class AbstractCommandTest : TestWithMockBukkit() {
|
||||
|
||||
protected lateinit var worldManager: WorldManager
|
||||
protected lateinit var player: PlayerMock
|
||||
protected lateinit var console: ConsoleCommandSenderMock
|
||||
|
||||
private lateinit var locales : PluginLocales
|
||||
private lateinit var attachment : PermissionAttachment
|
||||
|
||||
@BeforeTest
|
||||
fun setUpCommand() {
|
||||
worldManager = serviceLocator.getActiveService(WorldManager::class.java).takeIf { it != null } ?: run {
|
||||
throw IllegalStateException("WorldManager is not available as a service") }
|
||||
assertTrue(worldManager.createWorld(CreateWorldOptions.worldName("world")).isSuccess)
|
||||
|
||||
val commandManager = serviceLocator.getActiveService(MVCommandManager::class.java).takeIf { it != null } ?: run {
|
||||
throw IllegalStateException("MVCommandManager is not available as a service") }
|
||||
locales = commandManager.locales
|
||||
|
||||
console = server.consoleSender
|
||||
player = server.addPlayer("benwoo1110");
|
||||
assertEquals(player, server.getPlayer("benwoo1110"))
|
||||
attachment = player.addAttachment(multiverseCore)
|
||||
assertNotNull(attachment)
|
||||
}
|
||||
|
||||
fun addPermission(permission: String) {
|
||||
attachment.setPermission(permission, true)
|
||||
}
|
||||
|
||||
fun assertCommandOutput(message : Message) {
|
||||
assertEquals(
|
||||
ChatTextFormatter.removeColor(message.formatted(locales)),
|
||||
ChatTextFormatter.removeColor(player.nextMessage())
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
package org.mvplugins.multiverse.core.commands
|
||||
|
||||
import co.aikar.commands.MessageKeys
|
||||
import org.bukkit.Bukkit
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.mvplugins.multiverse.core.config.CoreConfig
|
||||
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 kotlin.test.assertEquals
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class ConfigCommandTest : AbstractCommandTest() {
|
||||
|
||||
@Test
|
||||
fun `Modify config global-debug`() {
|
||||
assertTrue(Bukkit.dispatchCommand(console, "mv config global-debug 2"))
|
||||
val coreConfig = serviceLocator.getActiveService(CoreConfig::class.java).takeIf { it != null } ?: run {
|
||||
throw IllegalStateException("CoreConfig is not available as a service") }
|
||||
assertEquals(2, coreConfig.globalDebug)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Modify config global-debug player no permission`() {
|
||||
assertTrue(player.performCommand("mv config global-debug 2"))
|
||||
assertCommandOutput(Message.of(MessageKeys.PERMISSION_DENIED, ""))
|
||||
|
||||
val coreConfig = serviceLocator.getActiveService(CoreConfig::class.java).takeIf { it != null } ?: run {
|
||||
throw IllegalStateException("CoreConfig is not available as a service") }
|
||||
assertEquals(0, coreConfig.globalDebug)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Modify non-existing config property`() {
|
||||
addPermission("multiverse.core.config")
|
||||
assertTrue(player.performCommand("mv config invalid-property test"))
|
||||
player.nextMessage() // ignore the first line
|
||||
assertCommandOutput(
|
||||
Message.of(
|
||||
MVCorei18n.CONFIG_NODE_NOTFOUND,
|
||||
"",
|
||||
replace("{node}").with("invalid-property")))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Modify config global-debug invalid type`() {
|
||||
addPermission("multiverse.core.config")
|
||||
assertTrue(player.performCommand("mv config global-debug what"))
|
||||
player.nextMessage() // ignore the first line
|
||||
assertCommandOutput(Message.of("Unable to convert 'what' to number. (integer)"))
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
package org.mvplugins.multiverse.core.commands
|
||||
|
||||
import org.bukkit.Bukkit
|
||||
import org.bukkit.World
|
||||
import org.bukkit.WorldType
|
||||
import org.junit.jupiter.api.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertFalse
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class CreateCommandTest : AbstractCommandTest() {
|
||||
|
||||
@Test
|
||||
fun `Create nether world with default options`() {
|
||||
assertTrue(Bukkit.dispatchCommand(console, "mv create world1_nether nether"))
|
||||
val world = worldManager.getLoadedWorld("world1_nether")
|
||||
assertTrue(world.isDefined)
|
||||
assertEquals(World.Environment.NETHER, world.get().environment)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Create normal world with specific seed and flat world type without structures`() {
|
||||
assertTrue(Bukkit.dispatchCommand(console, "mv create world1 normal --seed 1234 --world-type flat --no-structures"))
|
||||
val world = worldManager.getLoadedWorld("world1")
|
||||
assertTrue(world.isDefined)
|
||||
assertEquals(1234L, world.get().seed)
|
||||
assertEquals(WorldType.FLAT, world.get().worldType.get())
|
||||
assertFalse(world.get().canGenerateStructures().get())
|
||||
}
|
||||
|
||||
//todo: Fix mockbukkit getBiomeProvider then added test on single biome world creation
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
package org.mvplugins.multiverse.core.commands
|
||||
|
||||
import org.bukkit.Bukkit
|
||||
import org.mvplugins.multiverse.core.command.queue.ConfirmMode
|
||||
import org.mvplugins.multiverse.core.config.CoreConfig
|
||||
import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld
|
||||
import org.mvplugins.multiverse.core.world.options.CreateWorldOptions
|
||||
import org.mvplugins.multiverse.core.world.options.UnloadWorldOptions
|
||||
import kotlin.test.BeforeTest
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertFalse
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class DeleteCommandTest : AbstractCommandTest() {
|
||||
|
||||
private lateinit var testWorld : LoadedMultiverseWorld
|
||||
|
||||
@BeforeTest
|
||||
fun setUp() {
|
||||
// Disable confirmation to make tests easier
|
||||
val config = serviceLocator.getActiveService(CoreConfig::class.java).takeIf { it != null } ?: run {
|
||||
throw IllegalStateException("CoreConfig is not available as a service") }
|
||||
config.confirmMode = ConfirmMode.DISABLE
|
||||
|
||||
testWorld = worldManager.createWorld(CreateWorldOptions.worldName("test")).get()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Delete loaded world`() {
|
||||
assertTrue(Bukkit.dispatchCommand(console, "mv delete test"))
|
||||
assertFalse(worldManager.getWorld("test").isDefined)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Delete unloaded world`() {
|
||||
worldManager.unloadWorld(UnloadWorldOptions.world(testWorld))
|
||||
assertTrue(Bukkit.dispatchCommand(console, "mv delete test"))
|
||||
assertFalse(worldManager.getWorld("test").isDefined)
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
package org.mvplugins.multiverse.core.commands
|
||||
|
||||
import org.bukkit.Bukkit
|
||||
import org.mvplugins.multiverse.core.command.queue.ConfirmMode
|
||||
import org.mvplugins.multiverse.core.config.CoreConfig
|
||||
import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld
|
||||
import org.mvplugins.multiverse.core.world.options.CreateWorldOptions
|
||||
import kotlin.test.BeforeTest
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class ModifyCommandTest : AbstractCommandTest() {
|
||||
|
||||
private lateinit var testWorld : LoadedMultiverseWorld
|
||||
|
||||
@BeforeTest
|
||||
fun setUp() {
|
||||
// Disable confirmation to make tests easier
|
||||
val config = serviceLocator.getActiveService(CoreConfig::class.java).takeIf { it != null } ?: run {
|
||||
throw IllegalStateException("CoreConfig is not available as a service") }
|
||||
config.confirmMode = ConfirmMode.DISABLE
|
||||
|
||||
testWorld = worldManager.createWorld(CreateWorldOptions.worldName("test")).get()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Modify alias name with space`() {
|
||||
assertTrue(Bukkit.dispatchCommand(console, "mv modify test set alias \"Test World\""))
|
||||
assertEquals("Test World", testWorld.alias)
|
||||
}
|
||||
}
|
||||
@@ -1,126 +0,0 @@
|
||||
package org.mvplugins.multiverse.core.commands
|
||||
|
||||
import org.bukkit.Bukkit
|
||||
import org.bukkit.GameRule
|
||||
import org.mvplugins.multiverse.core.command.queue.ConfirmMode
|
||||
import org.mvplugins.multiverse.core.config.CoreConfig
|
||||
import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld
|
||||
import org.mvplugins.multiverse.core.world.options.CreateWorldOptions
|
||||
import kotlin.test.*
|
||||
|
||||
class RegenCommandTest : AbstractCommandTest() {
|
||||
|
||||
private lateinit var testWorld : LoadedMultiverseWorld
|
||||
|
||||
@BeforeTest
|
||||
fun setUp() {
|
||||
// Disable confirmation to make tests easier
|
||||
val config = serviceLocator.getActiveService(CoreConfig::class.java).takeIf { it != null } ?: run {
|
||||
throw IllegalStateException("CoreConfig is not available as a service") }
|
||||
assertTrue(config.setConfirmMode(ConfirmMode.DISABLE).isSuccess)
|
||||
|
||||
testWorld = worldManager.createWorld(CreateWorldOptions.worldName("test")).get()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Regen world`() {
|
||||
val previousUID = testWorld.uid
|
||||
val previousSeed = testWorld.seed
|
||||
testWorld.scale = 69.5
|
||||
testWorld.bukkitWorld.map {
|
||||
it.setGameRule(GameRule.DO_DAYLIGHT_CYCLE, false)
|
||||
}
|
||||
testWorld.bukkitWorld.map {
|
||||
it.worldBorder.size = 123.4
|
||||
}
|
||||
assertTrue(Bukkit.dispatchCommand(console, "mv regen test"))
|
||||
assertTrue(Bukkit.dispatchCommand(console, "mv confirm"))
|
||||
|
||||
val newWorld = worldManager.getLoadedWorld("test").orNull
|
||||
|
||||
assertNotNull(newWorld)
|
||||
assertNotEquals(previousUID, newWorld.uid)
|
||||
assertEquals(previousSeed, newWorld.seed)
|
||||
assertEquals(69.5, newWorld.scale)
|
||||
assertEquals(false, newWorld.bukkitWorld.map { it.getGameRuleValue(GameRule.DO_DAYLIGHT_CYCLE) }.orNull)
|
||||
assertEquals(123.4, newWorld.bukkitWorld.map { it.worldBorder.size }.orNull)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Regen world with specific seed`() {
|
||||
val previousUID = testWorld.uid
|
||||
|
||||
assertTrue(Bukkit.dispatchCommand(console, "mv regen test --seed 456"))
|
||||
assertTrue(Bukkit.dispatchCommand(console, "mv confirm"))
|
||||
|
||||
val newWorld = worldManager.getLoadedWorld("test").orNull
|
||||
|
||||
assertNotNull(newWorld)
|
||||
assertNotEquals(previousUID, newWorld.uid)
|
||||
assertEquals(456, newWorld.seed)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Regen world with random seed`() {
|
||||
val previousUID = testWorld.uid
|
||||
val previousSeed = testWorld.seed
|
||||
|
||||
assertTrue(Bukkit.dispatchCommand(console, "mv regen test --seed"))
|
||||
assertTrue(Bukkit.dispatchCommand(console, "mv confirm"))
|
||||
|
||||
val newWorld = worldManager.getLoadedWorld("test").orNull
|
||||
|
||||
assertNotNull(newWorld)
|
||||
assertNotEquals(previousUID, newWorld.uid)
|
||||
assertNotEquals(previousSeed, newWorld.seed)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Regen world and reset world config`() {
|
||||
val previousUID = testWorld.uid
|
||||
testWorld.scale = 69.5
|
||||
|
||||
assertTrue(Bukkit.dispatchCommand(console, "mv regen test --reset-world-config"))
|
||||
assertTrue(Bukkit.dispatchCommand(console, "mv confirm"))
|
||||
|
||||
val newWorld = worldManager.getLoadedWorld("test").orNull
|
||||
|
||||
assertNotNull(newWorld)
|
||||
assertNotEquals(previousUID, newWorld.uid)
|
||||
assertEquals(1.0, newWorld.scale)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Regen world and reset gamerules`() {
|
||||
val previousUID = testWorld.uid
|
||||
testWorld.bukkitWorld.map {
|
||||
it.setGameRule(GameRule.DO_DAYLIGHT_CYCLE, false)
|
||||
}
|
||||
|
||||
assertTrue(Bukkit.dispatchCommand(console, "mv regen test --reset-gamerules"))
|
||||
assertTrue(Bukkit.dispatchCommand(console, "mv confirm"))
|
||||
|
||||
val newWorld = worldManager.getLoadedWorld("test").orNull
|
||||
|
||||
assertNotNull(newWorld)
|
||||
assertNotEquals(previousUID, newWorld.uid)
|
||||
assertEquals(true, newWorld.bukkitWorld.map { it.getGameRuleValue(GameRule.DO_DAYLIGHT_CYCLE) }.orNull)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Regen world and reset world border`() {
|
||||
val previousUID = testWorld.uid
|
||||
testWorld.bukkitWorld.map {
|
||||
it.worldBorder.size = 123.4
|
||||
}
|
||||
|
||||
assertTrue(Bukkit.dispatchCommand(console, "mv regen test --reset-world-border"))
|
||||
assertTrue(Bukkit.dispatchCommand(console, "mv confirm"))
|
||||
|
||||
val newWorld = worldManager.getLoadedWorld("test").orNull
|
||||
|
||||
assertNotNull(newWorld)
|
||||
assertNotEquals(previousUID, newWorld.uid)
|
||||
assertEquals(6.0E7, newWorld.bukkitWorld.map { it.worldBorder.size }.orNull)
|
||||
}
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
package org.mvplugins.multiverse.core.commands
|
||||
|
||||
import org.bukkit.Bukkit
|
||||
import org.bukkit.Location
|
||||
import org.mvplugins.multiverse.core.world.location.SpawnLocation
|
||||
import org.mvplugins.multiverse.core.world.options.CreateWorldOptions
|
||||
import kotlin.test.BeforeTest
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class SpawnCommandTest : AbstractCommandTest() {
|
||||
|
||||
@BeforeTest
|
||||
fun setUp() {
|
||||
server.addPlayer("Player1")
|
||||
server.addPlayer("Player2")
|
||||
server.addPlayer("Player3")
|
||||
assertTrue(worldManager.createWorld(CreateWorldOptions.worldName("otherworld")).isSuccess)
|
||||
server.getWorld("otherworld")?.spawnLocation?.let { Bukkit.getPlayer("Player1")?.teleport(it) }
|
||||
worldManager.getLoadedWorld("world").get().setSpawnLocation(SpawnLocation(20.0, 20.0, 20.0))
|
||||
worldManager.getLoadedWorld("otherworld").get().setSpawnLocation(SpawnLocation(20.0, 20.0, 20.0))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Teleport player to spawn location`() {
|
||||
assertTrue(Bukkit.dispatchCommand(console, "mv spawn Player1 --unsafe"))
|
||||
Thread.sleep(10) // wait for the player to teleport asynchronously
|
||||
assertLocationEquals(server.getWorld("otherworld")?.spawnLocation, server.getPlayer("Player1")?.location)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Teleport multiple players to spawn location`() {
|
||||
assertTrue(Bukkit.dispatchCommand(console, "mv spawn Player1,Player2,Player3 --unsafe"))
|
||||
Thread.sleep(10) // wait for the player to teleport asynchronously
|
||||
assertLocationEquals(server.getWorld("otherworld")?.spawnLocation, server.getPlayer("Player1")?.location)
|
||||
assertLocationEquals(server.getWorld("world")?.spawnLocation, server.getPlayer("Player2")?.location)
|
||||
assertLocationEquals(server.getWorld("world")?.spawnLocation, server.getPlayer("Player3")?.location)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `No permission to teleport to spawn`() {
|
||||
assertTrue(player.performCommand("mv spawn"))
|
||||
Thread.sleep(10) // wait for the player to teleport asynchronously
|
||||
assertLocationEquals(Location(server.getWorld("world"), 0.0, 5.0, 0.0), player.location)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Self permission to teleport to spawn`() {
|
||||
addPermission("multiverse.core.spawn.self.world")
|
||||
assertTrue(player.performCommand("mv spawn --unsafe"))
|
||||
Thread.sleep(10) // wait for the player to teleport asynchronously
|
||||
assertLocationEquals(server.getWorld("world")?.spawnLocation, player.location)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Other permission to teleport to spawn`() {
|
||||
addPermission("multiverse.core.spawn.other.world")
|
||||
assertTrue(player.performCommand("mv spawn Player1,Player2 --unsafe"))
|
||||
Thread.sleep(10) // wait for the player to teleport asynchronously
|
||||
assertLocationEquals(Location(server.getWorld("otherworld"), 0.0, 5.0, 0.0), server.getPlayer("Player1")?.location)
|
||||
assertLocationEquals(server.getWorld("world")?.spawnLocation, server.getPlayer("Player2")?.location)
|
||||
}
|
||||
@Test
|
||||
fun `Teleport others to spawn but not self`() {
|
||||
addPermission("multiverse.core.spawn.other.otherworld")
|
||||
assertTrue(player.performCommand("mv spawn benwoo1110,Player1 --unsafe"))
|
||||
Thread.sleep(10) // wait for the player to teleport asynchronously
|
||||
assertLocationEquals(Location(server.getWorld("world"), 0.0, 5.0, 0.0), server.getPlayer("benwoo1110")?.location)
|
||||
assertLocationEquals(server.getWorld("otherworld")?.spawnLocation, server.getPlayer("Player1")?.location)
|
||||
}
|
||||
}
|
||||
@@ -1,127 +0,0 @@
|
||||
package org.mvplugins.multiverse.core.commands
|
||||
|
||||
import org.bukkit.Bukkit
|
||||
import org.mvplugins.multiverse.core.config.CoreConfig
|
||||
import org.mvplugins.multiverse.core.world.options.CreateWorldOptions
|
||||
import kotlin.test.BeforeTest
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class TeleportCommandTest : AbstractCommandTest() {
|
||||
|
||||
private lateinit var config : CoreConfig
|
||||
|
||||
@BeforeTest
|
||||
fun setUp() {
|
||||
config = serviceLocator.getActiveService(CoreConfig::class.java).takeIf { it != null } ?: run {
|
||||
throw IllegalStateException("CoreConfig is not available as a service") }
|
||||
|
||||
server.addPlayer("Player1")
|
||||
server.addPlayer("Player2")
|
||||
server.addPlayer("Player3")
|
||||
assertTrue(worldManager.createWorld(CreateWorldOptions.worldName("otherworld")).isSuccess)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Teleport a player to other world`() {
|
||||
assertTrue(Bukkit.dispatchCommand(console, "mv tp Player1 otherworld --unsafe"))
|
||||
Thread.sleep(10) // wait for the player to teleport asynchronously
|
||||
assertLocationEquals(server.getWorld("otherworld")?.spawnLocation, server.getPlayer("Player1")?.location)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Teleport multiple players to other world`() {
|
||||
assertTrue(Bukkit.dispatchCommand(console, "mv tp Player1,Player2 otherworld --unsafe"))
|
||||
Thread.sleep(10) // wait for the player to teleport asynchronously
|
||||
assertLocationEquals(server.getWorld("otherworld")?.spawnLocation, server.getPlayer("Player1")?.location)
|
||||
assertLocationEquals(server.getWorld("otherworld")?.spawnLocation, server.getPlayer("Player2")?.location)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Teleport multiple players to invalid world`() {
|
||||
assertTrue(Bukkit.dispatchCommand(console, "mv tp Player1,Player2 invalidworld"))
|
||||
Thread.sleep(10) // wait for the player to teleport asynchronously
|
||||
assertLocationEquals(server.getWorld("world")?.spawnLocation, server.getPlayer("Player1")?.location)
|
||||
assertLocationEquals(server.getWorld("world")?.spawnLocation, server.getPlayer("Player2")?.location)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Player no permission to teleport`() {
|
||||
assertTrue(player.performCommand("mv tp otherworld"))
|
||||
Thread.sleep(10) // wait for the player to teleport asynchronously
|
||||
assertLocationEquals(server.getWorld("world")?.spawnLocation, server.getPlayer("Player1")?.location)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Player permission to teleport self`() {
|
||||
addPermission("multiverse.teleport.self.w.otherworld")
|
||||
|
||||
assertTrue(player.performCommand("mv tp otherworld --unsafe"))
|
||||
Thread.sleep(10) // wait for the player to teleport asynchronously
|
||||
assertLocationEquals(server.getWorld("otherworld")?.spawnLocation, player.location)
|
||||
|
||||
assertTrue(player.performCommand("mv tp Player1 world --unsafe"))
|
||||
Thread.sleep(10) // wait for the player to teleport asynchronously
|
||||
assertLocationEquals(server.getWorld("world")?.spawnLocation, server.getPlayer("Player1")?.location)
|
||||
|
||||
assertTrue(player.performCommand("mv tp Player2,Player3 world --unsafe"))
|
||||
Thread.sleep(10) // wait for the player to teleport asynchronously
|
||||
assertLocationEquals(server.getWorld("world")?.spawnLocation, server.getPlayer("Player2")?.location)
|
||||
assertLocationEquals(server.getWorld("world")?.spawnLocation, server.getPlayer("Player3")?.location)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Player permission to teleport others`() {
|
||||
addPermission("multiverse.teleport.other.w.otherworld")
|
||||
|
||||
assertTrue(player.performCommand("mv tp Player1 otherworld --unsafe"))
|
||||
Thread.sleep(10) // wait for the player to teleport asynchronously
|
||||
assertLocationEquals(server.getWorld("otherworld")?.spawnLocation, server.getPlayer("Player1")?.location)
|
||||
|
||||
assertTrue(player.performCommand("mv tp Player2,Player3 otherworld --unsafe"))
|
||||
Thread.sleep(10) // wait for the player to teleport asynchronously
|
||||
assertLocationEquals(server.getWorld("otherworld")?.spawnLocation, server.getPlayer("Player2")?.location)
|
||||
assertLocationEquals(server.getWorld("otherworld")?.spawnLocation, server.getPlayer("Player3")?.location)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Player permission to teleport others cannot teleport self`() {
|
||||
addPermission("multiverse.teleport.other.w.otherworld")
|
||||
|
||||
assertTrue(player.performCommand("mv tp otherworld --unsafe"))
|
||||
Thread.sleep(10) // wait for the player to teleport asynchronously
|
||||
assertLocationEquals(server.getWorld("world")?.spawnLocation, player.location)
|
||||
|
||||
assertTrue(player.performCommand("mv tp benwoo1110,Player1,Player2 world --unsafe"))
|
||||
Thread.sleep(10) // wait for the player to teleport asynchronously
|
||||
assertLocationEquals(server.getWorld("world")?.spawnLocation, server.getPlayer("benwoo1110")?.location)
|
||||
assertLocationEquals(server.getWorld("world")?.spawnLocation, server.getPlayer("Player1")?.location)
|
||||
assertLocationEquals(server.getWorld("world")?.spawnLocation, server.getPlayer("Player2")?.location)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Player permission to teleport self without finer permission`() {
|
||||
config.setUseFinerTeleportPermissions(false)
|
||||
addPermission("multiverse.teleport.self.w")
|
||||
|
||||
assertTrue(player.performCommand("mv tp otherworld --unsafe"))
|
||||
Thread.sleep(10) // wait for the player to teleport asynchronously
|
||||
assertLocationEquals(server.getWorld("otherworld")?.spawnLocation, player.location)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Player permission to teleport others without finer permission`() {
|
||||
config.setUseFinerTeleportPermissions(false)
|
||||
addPermission("multiverse.teleport.other.w")
|
||||
|
||||
assertTrue(player.performCommand("mv tp Player1,Player2 otherworld --unsafe"))
|
||||
Thread.sleep(10) // wait for the player to teleport asynchronously
|
||||
assertLocationEquals(server.getWorld("otherworld")?.spawnLocation, server.getPlayer("Player1")?.location)
|
||||
assertLocationEquals(server.getWorld("otherworld")?.spawnLocation, server.getPlayer("Player2")?.location)
|
||||
|
||||
assertTrue(player.performCommand("mv tp benwoo1110,Player3 world --unsafe"))
|
||||
Thread.sleep(10) // wait for the player to teleport asynchronously
|
||||
assertLocationEquals(server.getWorld("world")?.spawnLocation, server.getPlayer("benwoo1110")?.location)
|
||||
assertLocationEquals(server.getWorld("world")?.spawnLocation, server.getPlayer("Player3")?.location)
|
||||
}
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
package org.mvplugins.multiverse.core.commands
|
||||
|
||||
import co.aikar.commands.MessageKeys
|
||||
import org.bukkit.Bukkit
|
||||
import org.bukkit.ChatColor
|
||||
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.text.ChatTextFormatter
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class VersionCommandTest : AbstractCommandTest() {
|
||||
|
||||
@Test
|
||||
fun `Run version command as console`() {
|
||||
assertTrue(Bukkit.dispatchCommand(console, "mv version"))
|
||||
val output = ChatTextFormatter.removeColor(console.nextMessage())
|
||||
assertEquals("Multiverse Core Version v" + multiverseCore.getDescription().getVersion(), output)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Run version command as player`() {
|
||||
addPermission("multiverse.core.version")
|
||||
assertTrue(player.performCommand("mv version"))
|
||||
assertCommandOutput(
|
||||
Message.of(
|
||||
MVCorei18n.VERSION_MV,
|
||||
"",
|
||||
replace("{version}").with(multiverseCore.getDescription().getVersion())))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Run version command as player - no permission`() {
|
||||
assertTrue(player.performCommand("mv version"))
|
||||
assertCommandOutput(Message.of(MessageKeys.PERMISSION_DENIED, ""))
|
||||
}
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
package org.mvplugins.multiverse.core.config
|
||||
|
||||
import org.bukkit.Location
|
||||
import org.mockbukkit.mockbukkit.entity.PlayerMock
|
||||
import org.mvplugins.multiverse.core.TestWithMockBukkit
|
||||
import org.mvplugins.multiverse.core.teleportation.AsyncSafetyTeleporter
|
||||
import org.mvplugins.multiverse.core.world.WorldManager
|
||||
import org.mvplugins.multiverse.core.world.options.CreateWorldOptions
|
||||
import java.lang.AssertionError
|
||||
import kotlin.test.*
|
||||
|
||||
class ConfigTeleportInterceptTest : TestWithMockBukkit() {
|
||||
|
||||
private lateinit var config: CoreConfig
|
||||
private lateinit var safetyTeleporter: AsyncSafetyTeleporter
|
||||
private lateinit var player: PlayerMock
|
||||
private lateinit var location: Location
|
||||
|
||||
@BeforeTest
|
||||
fun setUp() {
|
||||
config = serviceLocator.getActiveService(CoreConfig::class.java).takeIf { it != null } ?: run {
|
||||
throw IllegalStateException("CoreConfig is not available as a service") }
|
||||
|
||||
safetyTeleporter = serviceLocator.getActiveService(AsyncSafetyTeleporter::class.java).takeIf { it != null } ?: run {
|
||||
throw IllegalStateException("AsyncSafetyTeleporter is not available as a service") }
|
||||
|
||||
val worldManager = serviceLocator.getActiveService(WorldManager::class.java).takeIf { it != null } ?: run {
|
||||
throw IllegalStateException("WorldManager is not available as a service") }
|
||||
|
||||
player = server.addPlayer()
|
||||
worldManager.createWorld(CreateWorldOptions.worldName("world2")).get()
|
||||
location = Location(server.getWorld("world2"), 0.0, 5.0, 0.0)
|
||||
config.enforceAccess = true
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Generic teleport with teleport intercept enabled`() {
|
||||
config.teleportIntercept = true
|
||||
assertFalse(player.teleport(location))
|
||||
assertEquals("world", player.world.name)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Generic teleport with teleport intercept disabled - no access checking`() {
|
||||
config.teleportIntercept = false
|
||||
assertTrue(player.teleport(location))
|
||||
assertEquals("world2", player.world.name)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Multiverse API teleport with teleport intercept disabled`() {
|
||||
config.teleportIntercept = false
|
||||
safetyTeleporter.to(location).teleport(player).toAttempt()
|
||||
.onSuccess (Runnable { throw AssertionError("Teleport should have failed") })
|
||||
.onFailure(Runnable { assertEquals("world", player.world.name) })
|
||||
}
|
||||
}
|
||||
@@ -1,121 +0,0 @@
|
||||
package org.mvplugins.multiverse.core.config
|
||||
|
||||
import org.mvplugins.multiverse.core.TestWithMockBukkit
|
||||
import org.mvplugins.multiverse.core.command.queue.ConfirmMode
|
||||
import java.io.File
|
||||
import java.nio.file.Path
|
||||
import kotlin.io.path.absolutePathString
|
||||
import kotlin.test.*
|
||||
|
||||
class ConfigTest : TestWithMockBukkit() {
|
||||
|
||||
private lateinit var config : CoreConfig
|
||||
private lateinit var configFile : File
|
||||
|
||||
@BeforeTest
|
||||
fun setUp() {
|
||||
configFile = File(Path.of(multiverseCore.dataFolder.absolutePath, "config.yml").absolutePathString())
|
||||
if (configFile.exists()) configFile.delete()
|
||||
|
||||
config = serviceLocator.getActiveService(CoreConfig::class.java).takeIf { it != null } ?: run {
|
||||
throw IllegalStateException("CoreConfig is not available as a service") }
|
||||
|
||||
assertTrue(config.load().isSuccess)
|
||||
assertTrue(config.save().isSuccess)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Config is loaded`() {
|
||||
assertTrue(config.isLoaded)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Config is fresh`() {
|
||||
assertConfigEquals("/configs/fresh_config.yml", "config.yml")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Old config is migrated`() {
|
||||
val oldConfig = getResourceAsText("/configs/old_config.yml")
|
||||
assertNotNull(oldConfig)
|
||||
multiverseCore.dataFolder.toPath().resolve("config.yml").toFile().writeText(oldConfig)
|
||||
assertTrue(config.load().isSuccess)
|
||||
assertTrue(config.save().isSuccess)
|
||||
|
||||
assertEquals(true, config.enforceAccess)
|
||||
assertEquals(false, config.isEnablePrefixChat)
|
||||
assertEquals("[%world%]>>%chat%", config.prefixChatFormat)
|
||||
assertEquals(false, config.teleportIntercept)
|
||||
assertEquals(true, config.firstSpawnOverride)
|
||||
assertEquals(2, config.globalDebug)
|
||||
assertEquals(false, config.silentStart)
|
||||
assertEquals("world", config.firstSpawnLocation)
|
||||
assertEquals(false, config.isUsingCustomPortalSearch)
|
||||
assertEquals(128, config.customPortalSearchRadius)
|
||||
assertEquals(true, config.isAutoPurgeEntities)
|
||||
assertEquals(false, config.isShowingDonateMessage)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Getting existing config property with getProperty returns expected value`() {
|
||||
assertEquals(false, config.stringPropertyHandle.getProperty("enforce-access").get())
|
||||
assertEquals("", config.stringPropertyHandle.getProperty("first-spawn-location").get())
|
||||
assertEquals(ConfirmMode.ENABLE, config.stringPropertyHandle.getProperty("confirm-mode").get())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Getting non-existing config property with getProperty returns null`() {
|
||||
assertTrue(config.stringPropertyHandle.getProperty("invalid-property").isFailure)
|
||||
assertTrue(config.stringPropertyHandle.getProperty("version").isFailure)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Getting existing config property by getter returns expected value`() {
|
||||
assertEquals(false, config.enforceAccess)
|
||||
assertEquals("", config.firstSpawnLocation)
|
||||
assertEquals(ConfirmMode.ENABLE, config.confirmMode)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Updating an existing config property with setProperty reflects the changes in getProperty`() {
|
||||
assertTrue(config.stringPropertyHandle.setProperty("enforce-access", true).isSuccess)
|
||||
assertEquals(true, config.stringPropertyHandle.getProperty("enforce-access").get())
|
||||
|
||||
assertTrue(config.stringPropertyHandle.setProperty("chat-prefix-format", "%world% %chat%").isSuccess)
|
||||
assertEquals("%world% %chat%", config.stringPropertyHandle.getProperty("chat-prefix-format").get())
|
||||
|
||||
assertTrue(config.stringPropertyHandle.setProperty("global-debug", 1).isSuccess)
|
||||
assertEquals(1, config.stringPropertyHandle.getProperty("global-debug").get())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Updating an existing config property with setPropertyString reflects the changes in getProperty`() {
|
||||
assertTrue(config.stringPropertyHandle.setPropertyString("enforce-access", "true").isSuccess)
|
||||
assertEquals(true, config.stringPropertyHandle.getProperty("enforce-access").get())
|
||||
|
||||
assertTrue(config.stringPropertyHandle.setPropertyString("chat-prefix-format", "%world% %chat%").isSuccess)
|
||||
assertEquals("%world% %chat%", config.stringPropertyHandle.getProperty("chat-prefix-format").get())
|
||||
|
||||
assertTrue(config.stringPropertyHandle.setPropertyString("global-debug", "1").isSuccess)
|
||||
assertEquals(1, config.stringPropertyHandle.getProperty("global-debug").get())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Updating a non-existing property with setProperty returns false`() {
|
||||
assertTrue(config.stringPropertyHandle.setProperty("invalid-property", false).isFailure)
|
||||
assertTrue(config.stringPropertyHandle.setProperty("version", 1.1).isFailure)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Broken config should regen fresh config`() {
|
||||
val brokenConfigData = getResourceAsText("/configs/broken_config.yml")
|
||||
assertNotNull(brokenConfigData)
|
||||
configFile.writeText(brokenConfigData)
|
||||
assertTrue(config.load().isSuccess)
|
||||
assertTrue(config.save().isSuccess)
|
||||
val brokenConfigFile = configFile.parentFile.listFiles({ _, fileName -> fileName.startsWith("config.yml.broken") })
|
||||
assertNotNull(brokenConfigFile)
|
||||
assertEquals(1, brokenConfigFile.size)
|
||||
assertConfigEquals("/configs/fresh_config.yml", "config.yml")
|
||||
}
|
||||
}
|
||||
@@ -1,148 +0,0 @@
|
||||
package org.mvplugins.multiverse.core.destination
|
||||
|
||||
import org.bukkit.Location
|
||||
import org.mockbukkit.mockbukkit.entity.PlayerMock
|
||||
import org.mvplugins.multiverse.core.TestWithMockBukkit
|
||||
import org.mvplugins.multiverse.core.destination.core.*
|
||||
import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld
|
||||
import org.mvplugins.multiverse.core.world.WorldManager
|
||||
import org.mvplugins.multiverse.core.world.options.CreateWorldOptions
|
||||
import kotlin.test.BeforeTest
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class DestinationTest : TestWithMockBukkit() {
|
||||
|
||||
private lateinit var worldManager: WorldManager
|
||||
private lateinit var destinationsProvider: DestinationsProvider
|
||||
private lateinit var world: LoadedMultiverseWorld
|
||||
private lateinit var player: PlayerMock
|
||||
|
||||
@BeforeTest
|
||||
fun setUp() {
|
||||
worldManager = serviceLocator.getActiveService(WorldManager::class.java).takeIf { it != null } ?: run {
|
||||
throw IllegalStateException("WorldManager is not available as a service") }
|
||||
destinationsProvider = serviceLocator.getActiveService(DestinationsProvider::class.java).takeIf { it != null } ?: run {
|
||||
throw IllegalStateException("DestinationsProvider is not available as a service") }
|
||||
|
||||
world = worldManager.createWorld(CreateWorldOptions.worldName("world")).get()
|
||||
player = server.addPlayer("benji_0224")
|
||||
player.bedSpawnLocation = Location(world.bukkitWorld.orNull, 5.0, 10.0, 5.0)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Bed destination instance`() {
|
||||
assertTrue(destinationsProvider.getDestinationById("b") is BedDestination)
|
||||
val destination = destinationsProvider.parseDestination("b:benji_0224").get()
|
||||
assertTrue(destination is BedDestinationInstance)
|
||||
assertEquals(player.bedSpawnLocation, destination.getLocation(player).orNull)
|
||||
assertEquals("b:benji_0224", destination.toString())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Bed destination instance own player`() {
|
||||
val destination = destinationsProvider.parseDestination("b:playerbed").orNull
|
||||
assertTrue(destination is BedDestinationInstance)
|
||||
assertEquals(player.bedSpawnLocation, destination.getLocation(player).orNull)
|
||||
assertEquals("b:playerbed", destination.toString())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Cannon destination instance`() {
|
||||
assertTrue(destinationsProvider.getDestinationById("ca") is CannonDestination)
|
||||
val destination = destinationsProvider.parseDestination("ca:world:1.2,2,3:10.5:9.5:5").orNull
|
||||
assertTrue(destination is CannonDestinationInstance)
|
||||
val expectedLocation = Location(world.bukkitWorld.orNull, 1.2, 2.0, 3.0, 9.5F, 10.5F)
|
||||
assertLocationEquals(expectedLocation, destination.getLocation(player).orNull)
|
||||
// todo: assert the Vector
|
||||
assertEquals("ca:world:1.2,2.0,3.0:10.5:9.5:5.0", destination.toString())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Exact destination instance`() {
|
||||
assertTrue(destinationsProvider.getDestinationById("e") is ExactDestination)
|
||||
val destination = destinationsProvider.parseDestination("e:world:1.2,2,3:10.5:9.5").orNull
|
||||
assertTrue(destination is ExactDestinationInstance)
|
||||
val expectedLocation = Location(world.bukkitWorld.orNull, 1.2, 2.0, 3.0, 9.5F, 10.5F)
|
||||
assertLocationEquals(expectedLocation, destination.getLocation(player).orNull)
|
||||
assertEquals("e:world:1.2,2.0,3.0:10.5:9.5", destination.toString())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Exact destination instance with relative position`() {
|
||||
val destination = destinationsProvider.parseDestination("e:world:~1.5,2,~-3:10.5:~9.5").orNull
|
||||
assertTrue(destination is ExactDestinationInstance)
|
||||
val expectedLocation = Location(
|
||||
world.bukkitWorld.orNull,
|
||||
player.location.x + 1.5,
|
||||
2.0,
|
||||
player.location.z - 3.0,
|
||||
player.location.yaw + 9.5F,
|
||||
10.5F
|
||||
)
|
||||
assertLocationEquals(expectedLocation, destination.getLocation(player).orNull)
|
||||
assertEquals("e:world:~1.5,2.0,~-3.0:10.5:~9.5", destination.toString())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Exact destination instance from location`() {
|
||||
val exactDestination = serviceLocator.getActiveService(ExactDestination::class.java).takeIf { it != null } ?: run {
|
||||
throw IllegalStateException("ExactDestination is not available as a service") }
|
||||
|
||||
val location = Location(world.bukkitWorld.orNull, 1.2, 2.0, 3.0, 9.5F, 10.5F)
|
||||
val destination = exactDestination.fromLocation(location)
|
||||
assertEquals(location, destination.getLocation(player).orNull)
|
||||
assertEquals("e:world:1.2,2.0,3.0:10.5:9.5", destination.toString())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Player destination instance`() {
|
||||
assertTrue(destinationsProvider.getDestinationById("pl") is PlayerDestination)
|
||||
val destination = destinationsProvider.parseDestination("pl:benji_0224").orNull
|
||||
assertTrue(destination is PlayerDestinationInstance)
|
||||
assertEquals(player.location, destination.getLocation(player).orNull)
|
||||
assertEquals("pl:benji_0224", destination.toString())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `World destination instance`() {
|
||||
assertTrue(destinationsProvider.getDestinationById("w") is WorldDestination)
|
||||
val destination = destinationsProvider.parseDestination("w:world").orNull
|
||||
assertTrue(destination is WorldDestinationInstance)
|
||||
assertEquals(world.spawnLocation, destination.getLocation(player).orNull)
|
||||
assertEquals("w:world", destination.toString())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `World destination instance without identifier`() {
|
||||
val destination = destinationsProvider.parseDestination("world").orNull
|
||||
assertTrue(destination is WorldDestinationInstance)
|
||||
assertEquals(world.spawnLocation, destination.getLocation(player).orNull)
|
||||
assertEquals("w:world", destination.toString())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `World destination instance from alias name`() {
|
||||
world.setAlias("testalias")
|
||||
val destination = destinationsProvider.parseDestination("testalias").orNull
|
||||
assertTrue(destination is WorldDestinationInstance)
|
||||
assertEquals(world.spawnLocation, destination.getLocation(player).orNull)
|
||||
assertEquals("w:world", destination.toString())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Invalid destination instance`() {
|
||||
assertTrue(destinationsProvider.parseDestination("").isFailure)
|
||||
assertTrue(destinationsProvider.parseDestination("idk:world").isFailure)
|
||||
assertTrue(destinationsProvider.parseDestination("a:invalid-anchor").isFailure)
|
||||
assertTrue(destinationsProvider.parseDestination("b:invalid-bed").isFailure)
|
||||
assertTrue(destinationsProvider.parseDestination("ca:invalid-cannon").isFailure)
|
||||
assertTrue(destinationsProvider.parseDestination("e:world:1,2,x").isFailure)
|
||||
assertTrue(destinationsProvider.parseDestination("e:world:1,2,3:").isFailure)
|
||||
assertTrue(destinationsProvider.parseDestination("pl:invalid-player").isFailure)
|
||||
assertTrue(destinationsProvider.parseDestination("w:invalid-world").isFailure)
|
||||
// todo: should we make invalid yaw for WorldDestination fail?
|
||||
// assertTrue(destinationsProvider.parseDestination("w:world:f").isEmpty)
|
||||
}
|
||||
}
|
||||
Loaded 100 of 124 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user