Compare commits

..
6 Commits
83 changed files with 776 additions and 5048 deletions

No files matched your search

+1
View File
@@ -0,0 +1 @@
.gitea/CODEOWNERS linguist-detectable=false
+3
View File
@@ -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
@@ -107,7 +107,7 @@ 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: 26.1.2, 26.1.1, 26.1, 1.21.11, 1.21.10, 1.21.9, 1.21.8, 1.21.7, 1.21.6, 1.21.5, 1.21.4, 1.21.3, 1.21.2, 1.21.1, 1.21, 1.20.6, 1.20.5, 1.20.4, 1.20.3, 1.20.2, 1.20.1, 1.20, 1.19.4, 1.19.3, 1.19.2, 1.19.1, 1.19, 1.18.2
game_versions: 1.21.11, 1.21.10, 1.21.9, 1.21.8, 1.21.7, 1.21.6, 1.21.5, 1.21.4, 1.21.3, 1.21.2, 1.21.1, 1.21, 1.20.6, 1.20.5, 1.20.4, 1.20.3, 1.20.2, 1.20.1, 1.20, 1.19.4, 1.19.3, 1.19.2, 1.19.1, 1.19, 1.18.2
version_type: ${{ steps.parse-release-type.outputs.release_type }}
loaders: bukkit, spigot, paper, purpur
dependencies: ${{ inputs.modrinth_dependencies }}
@@ -121,7 +121,7 @@ jobs:
changelog: ${{ steps.release-artifact.outputs.body }}
changelog_type: markdown
display_name: ${{ steps.release-info.outputs.tag_name }}
game_versions: 26.1.2, 26.1.1, 26.1, 1.21.11, 1.21.10, 1.21.9, 1.21.8, 1.21.7, 1.21.6, 1.21.5, 1.21.4, 1.21.3, 1.21.2, 1.21.1, 1.21, 1.20.6, 1.20.5, 1.20.4, 1.20.3, 1.20.2, 1.20.1, 1.20, 1.19.4, 1.19.3, 1.19.2, 1.19.1, 1.19, 1.18.2
game_versions: 1.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-26.1.2"]}'
platform_dependencies: '{"PAPER": ["1.18.2-1.21.11"]}'
plugin_dependencies: ${{ inputs.hangar_plugin_dependencies }}
+4
View File
@@ -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
View File
@@ -1,117 +0,0 @@
import org.apache.tools.ant.filters.ReplaceTokens
plugins {
id 'org.mvplugins.multiverse-plugin' version '1.2.2'
}
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.11-R0.1-SNAPSHOT'
mockBukkitServerApiVersion = '1.21'
mockBukkitVersion = '4.100.0'
}
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")
}
}
}
}
+364
View File
@@ -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)
}
}
}
Binary file not shown.
+2 -1
View File
@@ -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
Vendored
+21 -13
View File
@@ -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
View File
@@ -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
-14
View File
@@ -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'
+11
View File
@@ -0,0 +1,11 @@
pluginManagement {
repositories {
gradlePluginPortal()
maven {
url = uri("https://repo.onarandombox.com/multiverse-releases")
}
}
}
rootProject.name = "Multiverse-Core"
@@ -126,7 +126,6 @@ public class MultiverseCore extends MultiverseModule {
MultiverseCoreApi.shutdown();
shutdownDependencyInjection();
PluginServiceLocatorFactory.get().shutdown();
Logging.info("- Disabled");
Logging.shutdown();
}
@@ -226,12 +225,11 @@ public class MultiverseCore extends MultiverseModule {
* Logs the enable message.
*/
private void logEnableMessage() {
Logging.config("\u001B[32mVersion %s (API v%s) Enabled - By %s\u001B[0m",
Logging.config("\u001B[32mVersion %s (API v%s) Enabled - By %s\u001B[39m",
this.getDescription().getVersion(), getVersionAsNumber(), StringFormatter.joinAnd(getDescription().getAuthors()));
if (configProvider.get().isShowingDonateMessage()) {
Logging.config("\u001B[32mLoving Multiverse-Core? Please consider supporting the project with a " +
"small donation: https://github.com/sponsors/Multiverse\u001B[0m");
Logging.config("\u001B[32mLoving Multiverse-Core? Please consider supporting the project with a small donation: https://github.com/sponsors/Multiverse\u001B[39m");
}
}
@@ -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;
@@ -13,13 +12,11 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jvnet.hk2.annotations.Service;
import org.mvplugins.multiverse.core.config.CoreConfig;
import org.mvplugins.multiverse.core.economy.MVEconomist;
import org.mvplugins.multiverse.core.utils.MinecraftTimeFormatter;
import org.mvplugins.multiverse.core.utils.REPatterns;
import org.mvplugins.multiverse.core.utils.StringFormatter;
import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld;
import org.mvplugins.multiverse.core.world.MultiverseWorld;
import org.mvplugins.multiverse.core.world.WorldManager;
import java.util.List;
@@ -29,17 +26,12 @@ import java.util.Locale;
final class PlaceholderExpansionHook extends PlaceholderExpansion {
private final MultiverseCore plugin;
private final CoreConfig coreConfig;
private final WorldManager worldManager;
private final MVEconomist economist;
@Inject
public PlaceholderExpansionHook(@NotNull MultiverseCore plugin,
@NotNull CoreConfig coreConfig,
@NotNull WorldManager worldManager,
@NotNull MVEconomist economist) {
public PlaceholderExpansionHook(MultiverseCore plugin, WorldManager worldManager, MVEconomist economist) {
this.plugin = plugin;
this.coreConfig = coreConfig;
this.worldManager = worldManager;
this.economist = economist;
}
@@ -50,13 +42,6 @@ final class PlaceholderExpansionHook extends PlaceholderExpansion {
return super.register();
}
@Override
public void warning(String msg) {
if (coreConfig.getWarnInvalidPapiFormat()) {
super.warning(msg);
}
}
@Override
public @NotNull String getIdentifier() {
return "multiverse-core";
@@ -95,102 +80,136 @@ final class PlaceholderExpansionHook extends PlaceholderExpansion {
return null;
}
final var placeholder = paramsArray.remove(0).toLowerCase(Locale.ENGLISH);
final var placeholder = paramsArray.remove(0);
return parseWorldName(offlinePlayer, paramsArray)
.flatMap(worldName -> worldManager.getWorld(worldName)
.onEmpty(() -> warning("Multiverse World not found: " + worldName)))
.flatMap(world -> world.asLoadedWorld()
.flatMap(loadedWorld -> getLoadedWorldPlaceHolderValue(placeholder, paramsArray, loadedWorld))
.orElse(() -> getWorldPlaceHolderValue(placeholder, paramsArray, world)))
.getOrElse(() -> coreConfig.getInvalidPapiFormatReturnsBlank() ? "" : null);
String worldName = parseWorldName(offlinePlayer, paramsArray);
if (worldName == null) return null;
return worldManager.getLoadedWorld(worldName)
.onEmpty(() -> warning("Multiverse World not found: " + worldName))
.map(world -> getWorldPlaceHolderValue(placeholder, paramsArray, world))
.getOrNull();
}
private @NotNull Option<String> parseWorldName(OfflinePlayer offlinePlayer, List<String> paramsArray) {
private @Nullable String parseWorldName(OfflinePlayer offlinePlayer, List<String> paramsArray) {
// No world defined, get from player
if (paramsArray.isEmpty()) {
if (offlinePlayer instanceof Player player) {
return Option.of(player.getWorld().getName());
return player.getWorld().getName();
} else {
warning("You must specify a world name for non-player placeholders");
return Option.none();
return null;
}
}
// Try get from params
String paramWorldName = paramsArray.get(paramsArray.size() - 1);
if (worldManager.isWorld(paramWorldName)) {
if (worldManager.isLoadedWorld(paramWorldName)) {
paramsArray.remove(paramsArray.size() - 1);
return Option.of(paramWorldName);
return paramWorldName;
}
// Param not a world, fallback to player
if (offlinePlayer instanceof Player player) {
return Option.of(player.getWorld().getName());
return player.getWorld().getName();
}
warning("Multiverse World not found: " + paramWorldName);
return Option.none();
return null;
}
private @NotNull Option<String> getWorldPlaceHolderValue(@NotNull String placeholder,
@NotNull List<String> placeholderParams,
@NotNull MultiverseWorld world) {
return Option.of(switch (placeholder) {
case "alias" -> world.getAliasOrName();
case "animalspawn" -> String.valueOf(world.getEntitySpawnConfig()
.getSpawnCategoryConfig(SpawnCategory.ANIMAL)
.isSpawn());
case "autoheal" -> String.valueOf(world.getAutoHeal());
case "blacklist" -> String.join(", ", world.getWorldBlacklist());
case "currency" -> String.valueOf(world.getCurrency());
case "difficulty" -> world.getDifficulty().toString();
case "entryfee" -> economist.formatPrice(world.getPrice(), world.getCurrency());
case "environment" -> world.getEnvironment().toString().toLowerCase();
case "flight" -> String.valueOf(world.isAllowFlight());
case "gamemode" -> world.getGameMode().toString().toLowerCase();
case "generator" -> world.getGenerator();
case "hunger" -> String.valueOf(world.isHunger());
case "isloaded" -> String.valueOf(world.isLoaded());
case "monstersspawn" -> String.valueOf(world.getEntitySpawnConfig()
.getSpawnCategoryConfig(SpawnCategory.MONSTER)
.isSpawn());
case "name" -> world.getName();
case "playerlimit" -> String.valueOf(world.getPlayerLimit());
case "price" -> String.valueOf(world.getPrice());
case "pvp" -> String.valueOf(world.getPvp());
case "seed" -> String.valueOf(world.getSeed());
case "weather" -> String.valueOf(world.isAllowWeather());
case "playercount", "time", "type" -> {
warning("Placeholder '" + placeholder + "' is only available for loaded worlds.");
yield null;
private @Nullable String getWorldPlaceHolderValue(@NotNull String placeholder,
@NotNull List<String> placeholderParams,
@NotNull LoadedMultiverseWorld world) {
// Switch to find what specific placeholder we want
switch (placeholder.toLowerCase(Locale.ENGLISH)) {
case "alias" -> {
return world.getAliasOrName();
}
default -> {
warning("Unknown Placeholder: " + placeholder);
yield null;
case "animalspawn" -> {
return String.valueOf(world.getEntitySpawnConfig().getSpawnCategoryConfig(SpawnCategory.ANIMAL).isSpawn());
}
case "autoheal" -> {
return String.valueOf(world.getAutoHeal());
}
case "blacklist" -> {
return String.join(", ", world.getWorldBlacklist());
}
case "currency" -> {
return String.valueOf(world.getCurrency());
}
case "difficulty" -> {
return world.getDifficulty().toString();
}
case "entryfee" -> {
return economist.formatPrice(world.getPrice(), world.getCurrency());
}
case "environment" -> {
return world.getEnvironment().toString().toLowerCase();
}
case "flight" -> {
return String.valueOf(world.isAllowFlight());
}
case "gamemode" -> {
return world.getGameMode().toString().toLowerCase();
}
case "generator" -> {
return world.getGenerator();
}
case "hunger" -> {
return String.valueOf(world.isHunger());
}
case "monstersspawn" -> {
return String.valueOf(world.getEntitySpawnConfig().getSpawnCategoryConfig(SpawnCategory.MONSTER).isSpawn());
}
case "name" -> {
return world.getName();
}
case "playercount" -> {
return String.valueOf(world.getBukkitWorld()
.map(World::getPlayers)
.map(List::size)
.getOrElse(-1));
}
case "playerlimit" -> {
return String.valueOf(world.getPlayerLimit());
}
case "price" -> {
return String.valueOf(world.getPrice());
}
case "pvp" -> {
return String.valueOf(world.getPvp());
}
case "seed" -> {
return String.valueOf(world.getSeed());
}
});
}
private @NotNull Option<String> getLoadedWorldPlaceHolderValue(@NotNull String placeholder,
@NotNull List<String> placeholderParams,
@NotNull LoadedMultiverseWorld world) {
return Option.of(switch (placeholder) {
case "playercount" -> String.valueOf(world.getBukkitWorld()
.map(World::getPlayers)
.map(List::size)
.getOrElse(-1));
case "time" -> {
String timeFormat = !placeholderParams.isEmpty() ? placeholderParams.get(0) : "";
long time = world.getBukkitWorld().map(World::getTime).getOrElse(0L);
yield switch (timeFormat) {
case "" -> String.valueOf(time);
case "12h" -> MinecraftTimeFormatter.format12h(time);
case "24h" -> MinecraftTimeFormatter.format24h(time);
default -> MinecraftTimeFormatter.formatTime(time, timeFormat);
};
switch (timeFormat) {
case "" -> {
return String.valueOf(time);
}
case "12h" -> {
return MinecraftTimeFormatter.format12h(time);
}
case "24h" -> {
return MinecraftTimeFormatter.format24h(time);
}
default -> {
return MinecraftTimeFormatter.formatTime(time, timeFormat);
}
}
}
case "type" -> world.getBukkitWorld().map(World::getWorldType).map(Enum::name).getOrElse("null");
default -> null;
});
case "type" -> {
return world.getBukkitWorld().map(World::getWorldType).map(Enum::name).getOrElse("null");
}
case "weather" -> {
return String.valueOf(world.isAllowWeather());
}
default -> {
warning("Unknown placeholder: " + placeholder);
return null;
}
}
}
}
@@ -53,7 +53,7 @@ import org.mvplugins.multiverse.core.world.WorldManager;
import org.mvplugins.multiverse.core.world.generators.GeneratorPlugin;
import org.mvplugins.multiverse.core.world.generators.GeneratorProvider;
import static org.mvplugins.multiverse.core.utils.StringFormatter.addOnToCommaSeparated;
import static org.mvplugins.multiverse.core.utils.StringFormatter.addonToCommaSeperated;
@Service
public class MVCommandCompletions extends PaperCommandCompletions {
@@ -163,7 +163,7 @@ public class MVCommandCompletions extends PaperCommandCompletions {
}
}
if (context.hasConfig("multiple")) {
return addOnToCommaSeparated(context.getInput(), handler.getCompletions(context));
return addonToCommaSeperated(context.getInput(), handler.getCompletions(context));
}
return handler.getCompletions(context);
}
@@ -334,7 +334,7 @@ public class MVCommandCompletions extends PaperCommandCompletions {
matchedPlayers.add(name);
}
}
return addOnToCommaSeparated(context.getInput(), matchedPlayers);
return addonToCommaSeperated(context.getInput(), matchedPlayers);
}
private Collection<String> suggestSpawnCategoryPropsName(BukkitCommandCompletionContext context) {
@@ -20,7 +20,7 @@ import org.mvplugins.multiverse.core.command.flag.FlagBuilder;
import org.mvplugins.multiverse.core.command.flag.ParsedCommandFlags;
import org.mvplugins.multiverse.core.locale.MVCorei18n;
import org.mvplugins.multiverse.core.locale.message.MessageReplacement.Replace;
import org.mvplugins.multiverse.core.world.MultiverseWorld;
import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld;
import org.mvplugins.multiverse.core.world.WorldManager;
import org.mvplugins.multiverse.core.world.options.CloneWorldOptions;
@@ -48,7 +48,7 @@ class CloneCommand extends CoreCommand {
@Syntax("<world>")
@Description("{@@mv-core.clone.world.description}")
MultiverseWorld world,
LoadedMultiverseWorld world,
@Syntax("<new-world-name>")
@Description("{@@mv-core.clone.newWorld.description}")
@@ -114,7 +114,7 @@ class CloneCommand extends CoreCommand {
@Override
@CommandAlias("mvcl|mvclone")
void onCloneCommand(MVCommandIssuer issuer, MultiverseWorld world, String newWorldName, String[] flags) {
void onCloneCommand(MVCommandIssuer issuer, LoadedMultiverseWorld world, String newWorldName, String[] flags) {
super.onCloneCommand(issuer, world, newWorldName, flags);
}
}
@@ -19,6 +19,7 @@ import org.jvnet.hk2.annotations.Service;
import org.mvplugins.multiverse.core.command.LegacyAliasCommand;
import org.mvplugins.multiverse.core.command.MVCommandIssuer;
import org.mvplugins.multiverse.core.command.MVCommandManager;
import org.mvplugins.multiverse.core.command.flag.CommandFlag;
import org.mvplugins.multiverse.core.command.flag.CommandFlagsManager;
import org.mvplugins.multiverse.core.command.flag.CommandValueFlag;
@@ -26,7 +27,6 @@ import org.mvplugins.multiverse.core.command.flag.FlagBuilder;
import org.mvplugins.multiverse.core.command.flag.ParsedCommandFlags;
import org.mvplugins.multiverse.core.locale.MVCorei18n;
import org.mvplugins.multiverse.core.locale.message.MessageReplacement.Replace;
import org.mvplugins.multiverse.core.utils.StringFormatter;
import org.mvplugins.multiverse.core.utils.result.Attempt.Failure;
import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld;
import org.mvplugins.multiverse.core.world.WorldManager;
@@ -53,7 +53,7 @@ class CreateCommand extends CoreCommand {
@CommandPermission("multiverse.core.create")
@CommandCompletion("@empty @environments @flags:groupName=" + Flags.NAME)
@Syntax("<name> <environment> [--seed <seed> --generator <generator[:id]> --world-type <worldtype> --adjust-spawn "
+ "--no-structures --biome <biome> --properties <prop1=value1,prop2=value2,...>]")
+ "--no-structures --biome <biome>]")
@Description("{@@mv-core.create.description}")
void onCreateCommand(
MVCommandIssuer issuer,
@@ -68,7 +68,7 @@ class CreateCommand extends CoreCommand {
@Optional
@Syntax("[--seed <seed> --generator <generator[:id]> --world-type <worldtype> --adjust-spawn "
+ "--no-structures --biome <biome> --properties <prop1=value1,prop2=value2,...>]")
+ "--no-structures --biome <biome>]")
@Description("{@@mv-core.create.flags.description}")
String[] flagArray) {
ParsedCommandFlags parsedFlags = flags.parse(flagArray);
@@ -78,15 +78,14 @@ class CreateCommand extends CoreCommand {
issuer.sendInfo(MVCorei18n.CREATE_LOADING);
worldManager.createWorld(CreateWorldOptions.worldName(worldName)
.biome(parsedFlags.flagValue(flags.biome, ""))
.environment(environment)
.seed(parsedFlags.flagValue(flags.seed))
.worldType(parsedFlags.flagValue(flags.worldType, WorldType.NORMAL))
.useSpawnAdjust(!parsedFlags.hasFlag(flags.noAdjustSpawn))
.generator(parsedFlags.flagValue(flags.generator, ""))
.generatorSettings(parsedFlags.flagValue(flags.generatorSettings, ""))
.generateStructures(!parsedFlags.hasFlag(flags.noStructures))
.worldPropertyStrings(StringFormatter.parseCSVMap(parsedFlags.flagValue(flags.properties))))
.biome(parsedFlags.flagValue(flags.biome, ""))
.environment(environment)
.seed(parsedFlags.flagValue(flags.seed))
.worldType(parsedFlags.flagValue(flags.worldType, WorldType.NORMAL))
.useSpawnAdjust(!parsedFlags.hasFlag(flags.noAdjustSpawn))
.generator(parsedFlags.flagValue(flags.generator, ""))
.generatorSettings(parsedFlags.flagValue(flags.generatorSettings, ""))
.generateStructures(!parsedFlags.hasFlag(flags.noStructures)))
.onSuccess(newWorld -> messageSuccess(issuer, newWorld))
.onFailure(failure -> messageFailure(issuer, failure));
}
@@ -181,10 +180,6 @@ class CreateCommand extends CoreCommand {
.addAlias("-b")
.completion(input -> biomeProviderFactory.suggestBiomeString(input))
.build());
private final CommandValueFlag<String> properties = flag(CommandValueFlag.builder("--properties", String.class)
.addAlias("-p")
.build());
}
@Service
@@ -16,8 +16,8 @@ import org.mvplugins.multiverse.core.command.LegacyAliasCommand;
import org.mvplugins.multiverse.core.command.MVCommandIssuer;
import org.mvplugins.multiverse.core.command.flag.CommandFlag;
import org.mvplugins.multiverse.core.command.flag.CommandFlagsManager;
import org.mvplugins.multiverse.core.command.flag.FlagBuilder;
import org.mvplugins.multiverse.core.command.flag.ParsedCommandFlags;
import org.mvplugins.multiverse.core.command.flags.RemovePlayerFlags;
import org.mvplugins.multiverse.core.locale.MVCorei18n;
import org.mvplugins.multiverse.core.locale.message.MessageReplacement.Replace;
import org.mvplugins.multiverse.core.world.MultiverseWorld;
@@ -38,7 +38,7 @@ class LoadCommand extends CoreCommand {
@Subcommand("load")
@CommandPermission("multiverse.core.load")
@CommandCompletion("@mvworlds:scope=unloaded @flags:groupName=" + Flags.NAME)
@CommandCompletion("@mvworlds:scope=unloaded")
@Syntax("<world>")
@Description("{@@mv-core.load.description}")
void onLoadCommand(
@@ -49,7 +49,7 @@ class LoadCommand extends CoreCommand {
MultiverseWorld world,
@Optional
@Syntax("[--skip-folder-check]")
@Syntax("[--remove-players]")
@Description("")
String[] flagArray) {
ParsedCommandFlags parsedFlags = flags.parse(flagArray);
@@ -67,9 +67,9 @@ class LoadCommand extends CoreCommand {
}
@Service
private static final class Flags extends FlagBuilder {
private static final class Flags extends RemovePlayerFlags {
private static final String NAME = "mvload";
private static final String NAME = "mvunload";
@Inject
private Flags(@NotNull CommandFlagsManager flagsManager) {
@@ -186,16 +186,6 @@ public final class CoreConfig {
return configHandle.get(configNodes.enforceFlight);
}
@ApiStatus.AvailableSince("5.5")
public Try<Void> setGamemodeAndFlightEnforceDelay(int delayTicks) {
return configHandle.set(configNodes.gamemodeAndFlightEnforceDelay, delayTicks);
}
@ApiStatus.AvailableSince("5.5")
public int getGamemodeAndFlightEnforceDelay() {
return configHandle.get(configNodes.gamemodeAndFlightEnforceDelay);
}
@ApiStatus.AvailableSince("5.3")
public Try<Void> setApplyEntitySpawnRate(boolean applyEntitySpawnRate) {
return configHandle.set(configNodes.applyEntitySpawnRate, applyEntitySpawnRate);
@@ -503,26 +493,6 @@ public final class CoreConfig {
return configHandle.get(configNodes.registerPapiHook);
}
@ApiStatus.AvailableSince("5.5")
public Try<Void> setWarnInvalidPapiFormat(boolean warnInvalidPapiFormat) {
return configHandle.set(configNodes.warnInvalidPapiFormat, warnInvalidPapiFormat);
}
@ApiStatus.AvailableSince("5.5")
public boolean getWarnInvalidPapiFormat() {
return configHandle.get(configNodes.warnInvalidPapiFormat);
}
@ApiStatus.AvailableSince("5.5")
public Try<Void> setInvalidPapiFormatReturnsBlank(boolean invalidPapiFormatReturnsBlank) {
return configHandle.set(configNodes.invalidPapiFormatReturnsBlank, invalidPapiFormatReturnsBlank);
}
@ApiStatus.AvailableSince("5.5")
public boolean getInvalidPapiFormatReturnsBlank() {
return configHandle.get(configNodes.invalidPapiFormatReturnsBlank);
}
/**
* {@inheritDoc}
*/
@@ -651,16 +621,6 @@ public final class CoreConfig {
return configHandle.get(configNodes.eventPriorityPlayerTeleport);
}
@ApiStatus.AvailableSince("5.5")
public Try<Void> setEventPriorityPlayerWorldChange(EventPriority eventPriorityPlayerWorldChange) {
return configHandle.set(configNodes.eventPriorityPlayerWorldChange, eventPriorityPlayerWorldChange);
}
@ApiStatus.AvailableSince("5.5")
public EventPriority getEventPriorityPlayerWorldChange() {
return configHandle.get(configNodes.eventPriorityPlayerWorldChange);
}
public Try<Void> setBukkitYmlPath(String bukkitYmlPath) {
return configHandle.set(configNodes.bukkitYmlPath, bukkitYmlPath);
}
@@ -135,16 +135,6 @@ final class CoreConfigNodes {
.name("enforce-flight")
.build());
final ConfigNode<Integer> gamemodeAndFlightEnforceDelay = node(ConfigNode.builder("world.gamemode-and-flight-enforce-delay", Integer.class)
.comment("")
.comment("Sets the delay in ticks before Multiverse enforces gamemode and flight ability on world change.")
.comment("Increase this value if you are experiencing issues with other plugins overriding gamemode or flight ability.")
.comment("Or set to 0 to enforce immediately during world change event.")
.defaultValue(1)
.name("gamemode-and-flight-enforce-delay")
.suggester((sender -> List.of("0", "1", "2", "5", "10")))
.build());
final ConfigNode<Boolean> applyEntitySpawnRate = node(ConfigNode.builder("world.apply-entity-spawn-rate", Boolean.class)
.comment("")
.comment("Sets whether Multiverse will apply the world's entity `tick-rate` config in worlds.yml.")
@@ -220,10 +210,7 @@ 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. This config is")
.comment("only applied to /mvtp, join-destination and first-spawn-override. It will not touch any other")
.comment("teleport by other plugins or commands.")
.comment("========= Available options: =========")
.comment("Configures how passengers and vehicles are handled when an entity is teleported.")
.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.")
@@ -403,23 +390,6 @@ final class CoreConfigNodes {
.name("register-papi-hook")
.build());
final ConfigNode<Boolean> warnInvalidPapiFormat = node(ConfigNode.builder("messaging.warn-invalid-papi-format", Boolean.class)
.comment("")
.comment("This config option defines whether or not Multiverse should warn about invalid PlaceholderAPI formats.")
.comment("Only applies if PlaceholderAPI is installed, and only to multiverse placeholders.")
.defaultValue(true)
.name("warn-invalid-papi-format")
.build());
final ConfigNode<Boolean> invalidPapiFormatReturnsBlank = node(ConfigNode.builder("messaging.invalid-papi-format-returns-blank", Boolean.class)
.comment("")
.comment("By default, invalid formats will return the original placeholder string.")
.comment("This config option defines whether or not Multiverse should show invalid PlaceholderAPI formats as blank.")
.comment("Only applies if PlaceholderAPI is installed, and only to multiverse placeholders.")
.defaultValue(false)
.name("invalid-papi-format-returns-blank")
.build());
final ConfigNode<Locale> defaultLocale = node(ConfigNode.builder("messaging.default-locale", Locale.class)
.comment("")
.comment("This config option defines the default language Multiverse should use.")
@@ -558,18 +528,6 @@ final class CoreConfigNodes {
"full effect after a server restart."))
.build());
final ConfigNode<EventPriority> eventPriorityPlayerWorldChange = node(ConfigNode.builder("event-priority.player-changed-world", EventPriority.class)
.defaultValue(EventPriority.NORMAL)
.name("event-priority-player-changed-world")
.comment("")
.comment("This config option defines the priority for the PlayerChangedWorldEvent.")
.onLoadAndChange((oldValue, newValue) ->
eventPriorityMapper.get().setPriority("mvcore-player-changed-world", newValue))
.onChange((sender, oldValue, newValue) ->
sender.sendMessage(ChatColor.YELLOW + "'event-priority.player-changed-world' config option will only take " +
"full effect after a server restart."))
.build());
private final ConfigHeaderNode miscHeader = node(ConfigHeaderNode.builder("misc")
.comment("")
.comment("")
@@ -120,9 +120,9 @@ public class ListConfigNode<I> extends ConfigNode<List<I>> implements ListValueN
private void setDefaultSuggester() {
if (itemSuggester instanceof SenderNodeSuggester senderItemSuggester) {
this.suggester = (SenderNodeSuggester)(sender, input) ->
StringFormatter.addOnToCommaSeparated(input, senderItemSuggester.suggest(sender, input));
StringFormatter.addonToCommaSeperated(input, senderItemSuggester.suggest(sender, input));
} else {
this.suggester = input -> StringFormatter.addOnToCommaSeparated(input, itemSuggester.suggest(input));
this.suggester = input -> StringFormatter.addonToCommaSeperated(input, itemSuggester.suggest(input));
}
}
@@ -1,11 +1,8 @@
package org.mvplugins.multiverse.core.event.world;
import org.bukkit.event.HandlerList;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld;
import org.mvplugins.multiverse.core.world.MultiverseWorld;
/**
* Called when a world has been successfully cloned.
@@ -13,24 +10,14 @@ import org.mvplugins.multiverse.core.world.MultiverseWorld;
public final class MVWorldClonedEvent extends MultiverseWorldEvent<LoadedMultiverseWorld> {
private static final HandlerList HANDLERS = new HandlerList();
private final MultiverseWorld fromWorld;
private final LoadedMultiverseWorld fromWorld;
public MVWorldClonedEvent(LoadedMultiverseWorld world, MultiverseWorld fromWorld) {
public MVWorldClonedEvent(LoadedMultiverseWorld world, LoadedMultiverseWorld fromWorld) {
super(world);
this.fromWorld = fromWorld;
}
/**
* @deprecated Cloning can be done from unloaded worlds as well. Use {@link #getSourceWorld()} instead.
*/
@Deprecated(forRemoval = true, since = "5.6")
@ApiStatus.ScheduledForRemoval(inVersion = "6.0")
public @Nullable LoadedMultiverseWorld getFromWorld() {
return fromWorld.asLoadedWorld().getOrNull();
}
@ApiStatus.AvailableSince("5.6")
public @NotNull MultiverseWorld getSourceWorld() {
public LoadedMultiverseWorld getFromWorld() {
return fromWorld;
}
@@ -50,4 +37,5 @@ public final class MVWorldClonedEvent extends MultiverseWorldEvent<LoadedMultive
public static HandlerList getHandlerList() {
return HANDLERS;
}
}
@@ -7,7 +7,9 @@
package org.mvplugins.multiverse.core.listeners;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import com.dumptruckman.minecraft.util.Logging;
import io.vavr.control.Option;
@@ -36,7 +38,6 @@ import org.mvplugins.multiverse.core.destination.DestinationsProvider;
import org.mvplugins.multiverse.core.dynamiclistener.annotations.DefaultEventPriority;
import org.mvplugins.multiverse.core.dynamiclistener.annotations.EventMethod;
import org.mvplugins.multiverse.core.dynamiclistener.annotations.EventPriorityKey;
import org.mvplugins.multiverse.core.dynamiclistener.annotations.IgnoreIfCancelled;
import org.mvplugins.multiverse.core.economy.MVEconomist;
import org.mvplugins.multiverse.core.event.MVRespawnEvent;
import org.mvplugins.multiverse.core.locale.PluginLocales;
@@ -244,7 +245,7 @@ final class MVPlayerListener implements CoreListener {
}
private void teleportToDestinationOnJoin(Player player, DestinationInstance<?, ?> destination) {
asyncSafetyTeleporter.to(destination).passengerMode(config.getPassengerMode()).teleportSingle(player)
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",
@@ -256,9 +257,9 @@ final class MVPlayerListener implements CoreListener {
* @param event The Event that was fired.
*/
@EventMethod
@EventPriorityKey("mvcore-player-changed-world")
@DefaultEventPriority(EventPriority.NORMAL)
@DefaultEventPriority(EventPriority.MONITOR)
void playerChangedWorld(PlayerChangedWorldEvent event) {
// Permissions now determine whether or not to handle a gamemode.
this.handleGameModeAndFlight(event.getPlayer(), event.getPlayer().getWorld());
}
@@ -270,28 +271,12 @@ final class MVPlayerListener implements CoreListener {
@EventPriorityKey("mvcore-player-teleport")
@DefaultEventPriority(EventPriority.HIGHEST)
void playerTeleport(PlayerTeleportEvent event) {
Player teleportee = event.getPlayer();
if (event.isCancelled()) {
Logging.finer("Teleport event for player '" + teleportee.getName() + "' was already cancelled.");
return;
}
Logging.finer("Got teleport event for player '"
+ teleportee.getName() + "' with cause '" + event.getCause() + "'");
Location fromLocation = event.getFrom();
if (fromLocation == null || fromLocation.getWorld() == null) { // should never be null, but just in case
Logging.finer("Teleport event for player '" + teleportee.getName() + "' has null from-location or world.");
+ event.getPlayer().getName() + "' with cause '" + event.getCause() + "'");
if (event.isCancelled()) {
return;
}
Location toLocation = event.getTo();
if (toLocation == null || toLocation.getWorld() == null) { // may be null on spigot
Logging.finer("Teleport event for player '" + teleportee.getName() + "' has null to-location or world.");
return;
}
Player teleportee = event.getPlayer();
Option<String> teleporterName = teleportQueue.popFromQueue(teleportee.getName());
CommandSender teleporter = teleporterName.map(name -> {
if (name.equalsIgnoreCase("CONSOLE")) {
@@ -312,17 +297,17 @@ final class MVPlayerListener implements CoreListener {
}
Logging.finer("Teleporter %s is teleporting %s from %s to %s", teleporter.getName(), teleportee.getName(),
fromLocation, toLocation);
event.getFrom(), event.getTo());
MultiverseWorld fromWorld = getWorldManager().getLoadedWorld(fromLocation.getWorld()).getOrNull();
LoadedMultiverseWorld toWorld = getWorldManager().getLoadedWorld(toLocation.getWorld()).getOrNull();
MultiverseWorld fromWorld = getWorldManager().getLoadedWorld(event.getFrom().getWorld()).getOrNull();
LoadedMultiverseWorld toWorld = getWorldManager().getLoadedWorld(event.getTo().getWorld()).getOrNull();
if (toWorld == null) {
Logging.fine("Player '" + teleportee.getName() + "' is teleporting to world '"
+ toLocation.getWorld().getName() + "' which is not managed by Multiverse-Core. No further "
+ event.getTo().getWorld().getName() + "' which is not managed by Multiverse-Core. No further "
+ "actions will be taken by Multiverse-Core.");
return;
}
if (fromLocation.getWorld().equals(toLocation.getWorld())) {
if (event.getFrom().getWorld().equals(event.getTo().getWorld())) {
// The player is Teleporting to the same world.
Logging.finer("Player '" + teleportee.getName() + "' is teleporting to the same world.");
this.stateSuccess(teleportee.getName(), toWorld.getName());
@@ -356,19 +341,20 @@ final class MVPlayerListener implements CoreListener {
*/
@EventMethod
@DefaultEventPriority(EventPriority.LOWEST)
@IgnoreIfCancelled
void playerPortalCheck(PlayerPortalEvent event) {
Location fromLocation = event.getFrom();
if (fromLocation == null || fromLocation.getWorld() == null) {
if (event.isCancelled()) {
return;
}
if (event.getFrom().getWorld() == null) {
Logging.warning("PlayerPortalEvent's from world is null!");
return;
}
if (fromLocation.getWorld().getBlockAt(fromLocation).getType() == Material.NETHER_PORTAL) {
if (event.getFrom().getWorld().getBlockAt(event.getFrom()).getType() == Material.NETHER_PORTAL) {
return;
}
// Player was actually outside of the portal, adjust the from location
Location newLocation = blockSafety.findPortalBlockNextTo(fromLocation);
Location newLocation = blockSafety.findPortalBlockNextTo(event.getFrom());
if (newLocation != null) {
event.setFrom(newLocation);
}
@@ -381,33 +367,28 @@ final class MVPlayerListener implements CoreListener {
@EventMethod
@EventPriorityKey("mvcore-player-portal")
@DefaultEventPriority(EventPriority.HIGH)
@IgnoreIfCancelled
void playerPortal(PlayerPortalEvent event) {
Location fromLocation = event.getFrom();
if (fromLocation == null || fromLocation.getWorld() == null) { // should never be null, but just in case
Logging.finer("PlayerPortalEvent's from world is null!");
if (event.isCancelled()) {
return;
}
Location toLocation = event.getTo();
if (toLocation == null || toLocation.getWorld() == null) { // may be null on spigot
if (event.getTo() == null || event.getTo().getWorld() == null) {
Logging.finer("PlayerPortalEvent's to world is null!");
return;
}
if (config.isUsingCustomPortalSearch()) {
event.setSearchRadius(config.getCustomPortalSearchRadius());
}
if (Objects.equals(fromLocation.getWorld(), toLocation.getWorld())) {
if (Objects.equals(event.getFrom().getWorld(), event.getTo().getWorld())) {
// The player is Portaling to the same world.
Logging.finer("Player '" + event.getPlayer().getName() + "' is portaling to the same world.");
return;
}
MultiverseWorld fromWorld = getWorldManager().getLoadedWorld(fromLocation.getWorld()).getOrNull();
LoadedMultiverseWorld toWorld = getWorldManager().getLoadedWorld(toLocation.getWorld()).getOrNull();
MultiverseWorld fromWorld = getWorldManager().getLoadedWorld(event.getFrom().getWorld()).getOrNull();
LoadedMultiverseWorld toWorld = getWorldManager().getLoadedWorld(event.getTo().getWorld()).getOrNull();
if (toWorld == null) {
Logging.fine("Player '" + event.getPlayer().getName() + "' is portaling to world '"
+ toLocation.getWorld().getName() + "' which is not managed by Multiverse-Core. No further "
+ event.getTo().getWorld().getName() + "' which is not managed by Multiverse-Core. No further "
+ "actions will be taken by Multiverse-Core.");
return;
}
@@ -421,31 +402,21 @@ final class MVPlayerListener implements CoreListener {
}
/**
* Handles the gamemode for the specified {@link Player}. Delays the enforcement if configured to do so
* to ensure multiverse has final say over other plugins changing gamemodes.
* Handles the gamemode for the specified {@link Player}.
*
* @param player The {@link Player}.
* @param world The {@link World} the player is supposed to be in.
*/
private void handleGameModeAndFlight(final Player player, World world) {
if (config.getGamemodeAndFlightEnforceDelay() <= 0) {
doGameModeAndFlightEnforcement(player, world);
return;
}
server.getScheduler().runTaskLater(
this.plugin,
() -> doGameModeAndFlightEnforcement(player, world),
config.getGamemodeAndFlightEnforceDelay()
);
}
private void doGameModeAndFlightEnforcement(Player player, World world) {
if (!player.isOnline() || !player.getWorld().equals(world)) {
Logging.finer("Player %s is no longer online or not in the expected world '%s'", player.getName(), world.getName());
return;
}
Logging.finer("Handling gamemode and flight for player %s in world '%s'", player.getName(), world.getName());
enforcementHandler.handleFlightEnforcement(player);
enforcementHandler.handleGameModeEnforcement(player);
// We perform this task one tick later to MAKE SURE that the player actually reaches the
// destination world, otherwise we'd be changing the player mode if they havent moved anywhere.
this.server.getScheduler().runTaskLater(this.plugin, () -> {
if (!player.isOnline() || !player.getWorld().equals(world)) {
return;
}
Logging.finer("Handling gamemode and flight for player %s in world '%s'", player.getName(), world.getName());
enforcementHandler.handleFlightEnforcement(player);
enforcementHandler.handleGameModeEnforcement(player);
}, 1L);
}
}
@@ -292,7 +292,6 @@ public enum MVCorei18n implements MessageKeyProvider {
CLONEWORLD_WORLDEXISTFOLDER,
CLONEWORLD_WORLDEXISTUNLOADED,
CLONEWORLD_WORLDEXISTLOADED,
CLONEWORLD_FROMWORLDFOLDERINVALID,
CLONEWORLD_COPYFAILED,
CREATEWORLD_INVALIDWORLDNAME,
@@ -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());
@@ -132,10 +132,6 @@ public final class BlockSafety {
*/
public boolean canSpawnAtBlockSafely(@NotNull Block block) {
Logging.finest("Checking spawn safety for location: %s, %s, %s", block.getX(), block.getY(), block.getZ());
if (!block.getWorld().getWorldBorder().isInside(block.getLocation())) {
Logging.finest("Location is outside world border.");
return false;
}
if (isUnsafeSpawnBody(block)) {
// Player body will be stuck in solid
Logging.finest("Unsafe location for player's body: " + block);
@@ -1,92 +0,0 @@
package org.mvplugins.multiverse.core.utils;
import org.jetbrains.annotations.ApiStatus;
import org.jspecify.annotations.NonNull;
import org.jspecify.annotations.Nullable;
import java.util.Collection;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
/**
* A map with case-insensitive String keys. All keys are stored in lower-case form.
*
* @param <T> the type of mapped values
*
* @since 5.5
*/
@ApiStatus.AvailableSince("5.5")
public class CaseInsensitiveStringMap<T> implements Map<String, T> {
private final Map<String, T> map;
public CaseInsensitiveStringMap() {
map = new HashMap<>();
}
@Override
public int size() {
return map.size();
}
@Override
public boolean isEmpty() {
return map.isEmpty();
}
@Override
public boolean containsKey(Object key) {
return map.containsKey(normalizeKey(key));
}
@Override
public boolean containsValue(Object value) {
return map.containsValue(value);
}
@Override
public T get(Object key) {
return map.get(normalizeKey(key));
}
@Override
public @Nullable T put(String key, T value) {
return map.put(normalizeKey(key), value);
}
@Override
public T remove(Object key) {
return map.remove(normalizeKey(key));
}
@Override
public void putAll(@NonNull Map<? extends String, ? extends T> m) {
m.forEach((key, value) -> map.put(normalizeKey(key), value));
}
@Override
public void clear() {
map.clear();
}
@Override
public @NonNull Set<String> keySet() {
return map.keySet();
}
@Override
public @NonNull Collection<T> values() {
return map.values();
}
@Override
public @NonNull Set<Entry<String, T>> entrySet() {
return map.entrySet();
}
private String normalizeKey(Object key) {
return String.valueOf(key).toLowerCase(Locale.ROOT);
}
}
@@ -18,7 +18,6 @@ import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import com.dumptruckman.minecraft.util.Logging;
@@ -26,7 +25,6 @@ import io.vavr.control.Try;
import jakarta.inject.Inject;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jspecify.annotations.NonNull;
import org.jvnet.hk2.annotations.Service;
import org.mvplugins.multiverse.core.config.CoreConfig;
@@ -212,19 +210,16 @@ public final class FileUtils {
private final Path sourceDir;
private final Path targetDir;
private final List<Path> excludeFiles;
private final List<String> excludeFiles;
private CopyDirFileVisitor(@NotNull Path sourceDir, @NotNull Path targetDir, @NotNull List<String> excludeFiles) {
this.sourceDir = sourceDir;
this.targetDir = targetDir;
this.excludeFiles = excludeFiles.stream()
.map(sourceDir::resolve)
.toList();
Logging.finest(this.excludeFiles.stream().map(Path::toString).collect(Collectors.joining(", ", "Exclude files: [", "]")));
this.excludeFiles = excludeFiles;
}
@Override
public @NotNull FileVisitResult preVisitDirectory(@NonNull Path dir, @NotNull BasicFileAttributes attrs) throws IOException {
public @NotNull FileVisitResult preVisitDirectory(Path dir, @NotNull BasicFileAttributes attrs) throws IOException {
Path newDir = targetDir.resolve(sourceDir.relativize(dir));
if (!Files.isDirectory(newDir)) {
Files.createDirectory(newDir);
@@ -233,10 +228,10 @@ public final class FileUtils {
}
@Override
public @NotNull FileVisitResult visitFile(@NonNull Path file, @NotNull BasicFileAttributes attrs) throws IOException {
public @NotNull FileVisitResult visitFile(Path file, @NotNull BasicFileAttributes attrs) throws IOException {
// Pass files that are set to ignore
if (excludeFiles.contains(file)) {
Logging.finest("Ignoring file: " + file);
if (excludeFiles.contains(file.getFileName().toString())) {
Logging.finest("Ignoring file: " + file.getFileName());
return FileVisitResult.CONTINUE;
}
// Copy the files
@@ -2,17 +2,13 @@ package org.mvplugins.multiverse.core.utils;
import com.google.common.base.Strings;
import com.google.common.collect.Sets;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.Unmodifiable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
@@ -34,15 +30,15 @@ public final class StringFormatter {
* @param list the list of strings to join. If the list is empty, an empty string is returned.
* @return the concatenated string
*/
public static @NotNull String joinAnd(@Nullable List<String> list) {
public static @NotNull String joinAnd(List<String> list) {
return join(list, ", ", " and ");
}
public static @NotNull String join(@Nullable Collection<?> list, @NotNull String separator) {
public static @NotNull String join(Collection list, String separator) {
if (list == null || list.isEmpty()) {
return "";
}
return list.stream().map(String::valueOf).collect(Collectors.joining(separator));
return list.stream().map(String::valueOf).collect(Collectors.joining(separator)).toString();
}
/**
@@ -54,7 +50,7 @@ public final class StringFormatter {
* @param lastSeparator the separator to use before the last element. For example, " and ".
* @return the concatenated string
*/
public static @NotNull String join(@Nullable List<String> list, @NotNull String separator, @NotNull String lastSeparator) {
public static @NotNull String join(List<String> list, String separator, String lastSeparator) {
if (list == null || list.isEmpty()) {
return "";
}
@@ -75,30 +71,11 @@ public final class StringFormatter {
/**
* Appends a list of suggestions to the end of the input string, separated by commas.
*
* @param input The current input
* @param addons The autocomplete suggestions
* @return A collection of suggestions with the next suggestion appended
*
* @deprecated Method name has a spelling error. Use {@link #addOnToCommaSeparated(String, Collection)} instead.
*/
@Deprecated(forRemoval = true, since = "5.5")
@ApiStatus.ScheduledForRemoval(inVersion = "6.0")
public static Collection<String> addonToCommaSeperated(@Nullable String input, @NotNull Collection<String> addons) {
return addOnToCommaSeparated(input, addons);
}
/**
* Appends a list of suggestions to the end of the input string, separated by commas.
*
* @param input The current input
* @param addons The autocomplete suggestions
* @return A collection of suggestions with the next suggestion appended
*
* @since 5.5
*/
@ApiStatus.AvailableSince("5.5")
public static Collection<String> addOnToCommaSeparated(@Nullable String input, @NotNull Collection<String> addons) {
if (Strings.isNullOrEmpty(input)) {
return addons;
}
@@ -117,7 +94,7 @@ public final class StringFormatter {
* @param args The args to parse
* @return The parsed args
*/
public static @NotNull Collection<String> parseQuotesInArgs(@NotNull String[] args) {
public static Collection<String> parseQuotesInArgs(String[] args) {
List<String> result = new ArrayList<>(args.length);
StringBuilder current = new StringBuilder();
boolean inQuotes = false;
@@ -162,28 +139,7 @@ public final class StringFormatter {
* @param input The string to add quotes to
* @return The quoted string
*/
@Contract("null -> null")
public static @Nullable String quoteMultiWordString(@Nullable String input) {
return input != null && input.contains(" ") ? "\"" + input + "\"" : input;
}
/**
* Parses a CSV string of key=value pairs into a map.
* E.g. "key1=value1,key2=value2" -> {key1=value1, key2=value2}
*
* @param input The CSV string to parse
* @return The parsed map
*
* @since 5.5
*/
@ApiStatus.AvailableSince("5.5")
public static @Unmodifiable Map<String, String> parseCSVMap(@Nullable String input) {
if (Strings.isNullOrEmpty(input)) {
return Map.of();
}
return REPatterns.COMMA.splitAsStream(input)
.map(s -> REPatterns.EQUALS.split(s, 2))
.filter(parts -> parts.length == 2)
.collect(Collectors.toUnmodifiableMap(parts -> parts[0], parts -> parts[1]));
public static String quoteMultiWordString(String input) {
return input.contains(" ") ? "\"" + input + "\"" : input;
}
}
@@ -1,91 +0,0 @@
package org.mvplugins.multiverse.core.utils.compatibility;
import io.vavr.control.Option;
import org.bukkit.Bukkit;
import org.bukkit.NamespacedKey;
import org.bukkit.Server;
import org.bukkit.World;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.mvplugins.multiverse.core.utils.ReflectHelper;
import java.lang.reflect.Method;
import java.nio.file.Path;
/**
* Compatibility class used to handle API changes in {@link Bukkit} class.
*
* @since 5.6
*/
@ApiStatus.AvailableSince("5.6")
public final class BukkitCompatibility {
private static final Option<Method> GET_LEVEL_DIRECTORY_METHOD;
private static final Option<Method> GET_WORLD_NAMESPACED_KEY_METHOD;
static {
GET_LEVEL_DIRECTORY_METHOD = Option.of(ReflectHelper.getMethod(Server.class, "getLevelDirectory"));
GET_WORLD_NAMESPACED_KEY_METHOD = Option.of(ReflectHelper.getMethod(Bukkit.class, "getWorld", NamespacedKey.class));
}
/**
* Check whether the server is using the new dimension storage system introduced in PaperMC 26.1.
* <br />
* This is based of whether getLevelDirectory method exists in the server class,which is the main API change for
* the new dimension storage system.
*
* @return True if the server is using the new dimension storage system, else false.
*
* @since 5.6
*/
@ApiStatus.AvailableSince("5.6")
public static boolean isUsingNewDimensionStorage() {
return GET_LEVEL_DIRECTORY_METHOD
.flatMap(method -> Option.of(ReflectHelper.invokeMethod(Bukkit.getServer(), method)))
.isDefined();
}
/**
* Gets the folder where all the worlds will be store. Before 26.1, all worlds are stored in the root directory
* of the server, which can be obtained by {@link Server#getWorldContainer()}.
* <br />
* After 26.1, PaperMC changed all worlds are stored in the "[level]/dimensions/minecraft" folder under the world
* level directory, which needs to be manually parsed.
*
* @return The location where all the worlds folders should be, depending on server's mc version.
*
* @since 5.6
*/
@ApiStatus.AvailableSince("5.6")
@NotNull
public static Path getWorldFoldersDirectory() {
Server server = Bukkit.getServer();
return GET_LEVEL_DIRECTORY_METHOD.map(method -> ReflectHelper.invokeMethod(server, method))
.filter(Path.class::isInstance)
.map(Path.class::cast)
.map(path -> path.resolve("dimensions/minecraft"))
.getOrElse(() -> server.getWorldContainer().toPath());
}
/**
* Check if the world with the given name or namespaced key (e.g. minecraft:overworld) exists,
* and return it if it does.
* <br />
* Note that some default world names have different namespaced key matched with them.
* E.g.: world -> minecraft:overworld, world_nether -> minecraft:the_nether, world_the_end -> minecraft:the_end.
*
* @param nameOrKey Either a name or namespaced key string representation.
* @return The world if it exists
*
* @since 5.6
*/
@ApiStatus.AvailableSince("5.6")
@NotNull
public static Option<World> getWorldByNameOrKey(@NotNull String nameOrKey) {
return Option.of(Bukkit.getWorld(nameOrKey))
.orElse(() -> GET_WORLD_NAMESPACED_KEY_METHOD
.map(method -> ReflectHelper.invokeMethod(null, method, NamespacedKey.fromString(nameOrKey)))
.filter(World.class::isInstance)
.map(World.class::cast));
}
}
@@ -1,5 +0,0 @@
/**
* Contains classes to handle compatibility due to differing API methods across different server versions and the
* API gaps between PaperMC and Spigot.
*/
package org.mvplugins.multiverse.core.utils.compatibility;
@@ -3,7 +3,6 @@ package org.mvplugins.multiverse.core.world;
import java.util.List;
import com.google.common.base.Strings;
import io.vavr.control.Option;
import io.vavr.control.Try;
import org.bukkit.Bukkit;
import org.bukkit.Difficulty;
@@ -11,7 +10,6 @@ import org.bukkit.GameMode;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -68,8 +66,7 @@ public sealed class MultiverseWorld permits LoadedMultiverseWorld {
}
/**
* Gets whether this world is loaded. Note that this may return false even if this is a stored instance of
* {@link LoadedMultiverseWorld} as worlds can be unloaded anytime.
* Gets whether this world is loaded.
*
* @return True if the world is loaded, else false.
*/
@@ -77,23 +74,6 @@ public sealed class MultiverseWorld permits LoadedMultiverseWorld {
return worldConfig.isLoadedWorld();
}
/**
* Attempts to get loaded world instance. Only returns a value if the world is loaded at the time of calling, even
* if this is a stored instance of {@link LoadedMultiverseWorld}.
* <br/>
* You may use {@link #isLoaded()} to check if the world is loaded first.
*
* @return Loaded world instance wrapped in an Option, or None if the world is not loaded.
*
* @since 5.5
*/
@ApiStatus.AvailableSince("5.5")
public Option<LoadedMultiverseWorld> asLoadedWorld() {
return Option.of(worldConfig.getMVWorld())
.filter(world -> world instanceof LoadedMultiverseWorld)
.map(world -> (LoadedMultiverseWorld) world);
}
/**
* Gets the properties handler of this world.
*
@@ -381,10 +381,6 @@ final class WorldConfig {
configNodes.setWorld(world);
}
MultiverseWorld getMVWorld() {
return configNodes.getWorld();
}
boolean isLoadedWorld() {
return configNodes.getWorld() instanceof LoadedMultiverseWorld;
}
@@ -396,7 +392,7 @@ final class WorldConfig {
/**
* Migrates the entry fee settings. Assumes entry fee is disabled if currency is not set.
*/
private static final class EntryFeeMigrator implements MigratorAction {
static final class EntryFeeMigrator implements MigratorAction {
@Override
public void migrate(ConfigurationSection config) {
String currency = config.getString("entry-fee.currency", "");
@@ -6,7 +6,9 @@ import java.util.ArrayList;
import java.util.Arrays;
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;
@@ -20,10 +22,8 @@ import io.vavr.control.Option;
import io.vavr.control.Try;
import jakarta.inject.Inject;
import org.bukkit.Bukkit;
import org.bukkit.GameRule;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.WorldBorder;
import org.bukkit.WorldCreator;
import org.bukkit.WorldType;
import org.bukkit.plugin.PluginManager;
@@ -48,10 +48,8 @@ import org.mvplugins.multiverse.core.locale.message.MessageReplacement.Replace;
import org.mvplugins.multiverse.core.permissions.CorePermissions;
import org.mvplugins.multiverse.core.teleportation.BlockSafety;
import org.mvplugins.multiverse.core.teleportation.LocationManipulation;
import org.mvplugins.multiverse.core.utils.CaseInsensitiveStringMap;
import org.mvplugins.multiverse.core.utils.ReflectHelper;
import org.mvplugins.multiverse.core.utils.ServerProperties;
import org.mvplugins.multiverse.core.utils.compatibility.BukkitCompatibility;
import org.mvplugins.multiverse.core.utils.result.Attempt;
import org.mvplugins.multiverse.core.utils.result.FailureReason;
import org.mvplugins.multiverse.core.utils.FileUtils;
@@ -89,16 +87,12 @@ import static org.mvplugins.multiverse.core.world.helpers.DataStore.WorldConfigS
@Service // SUPPRESS CHECKSTYLE: ClassFanOutComplexity This is the world manager, it's going to be complex.
public final class WorldManager {
private static final List<String> CLONE_IGNORE_FILES = List.of(
"uid.dat", "session.lock", // All pre 26.1 format
"data/paper/metadata.dat" // New papermc format for 26.1+
);
private static final List<String> CLONE_IGNORE_FILES = Arrays.asList("uid.dat", "session.lock");
private static final DimensionFormat DEFAULT_NETHER_FORMAT = new DimensionFormat("%overworld%_nether");
private static final DimensionFormat DEFAULT_END_FORMAT = new DimensionFormat("%overworld%_the_end");
private final CaseInsensitiveStringMap<MultiverseWorld> worldsMap;
private final CaseInsensitiveStringMap<LoadedMultiverseWorld> loadedWorldsMap;
private final Map<String, MultiverseWorld> worldsMap;
private final Map<String, LoadedMultiverseWorld> loadedWorldsMap;
private final List<String> unloadTracker;
private final List<String> loadTracker;
private final WorldsConfigManager worldsConfigManager;
@@ -141,8 +135,8 @@ public final class WorldManager {
this.config = config;
this.entityPurger = entityPurger;
this.worldsMap = new CaseInsensitiveStringMap<>();
this.loadedWorldsMap = new CaseInsensitiveStringMap<>();
this.worldsMap = new HashMap<>();
this.loadedWorldsMap = new HashMap<>();
this.unloadTracker = new ArrayList<>();
this.loadTracker = new ArrayList<>();
}
@@ -182,7 +176,7 @@ public final class WorldManager {
private void removeWorldsNotInConfigs(Collection<String> removedWorlds) {
removedWorlds.forEach(worldName -> getWorld(worldName)
.map(world -> removeWorld(RemoveWorldOptions.world(world)))
.getOrElse(() -> worldActionResult(RemoveFailureReason.WORLD_NON_EXISTENT, worldName))
.getOrElse(() -> worldActionResult(RemoveFailureReason.UNLOAD_FAILED, worldName))
.onFailure(failure ->
Logging.severe("Failed to unload world %s: %s", worldName, failure))
.onSuccess(success ->
@@ -283,16 +277,7 @@ public final class WorldManager {
generatorString,
options.biome(),
options.useSpawnAdjust()))
.peek(loadedWorld -> postCreateWorld(loadedWorld, options));
}
private void postCreateWorld(LoadedMultiverseWorld loadedWorld, CreateWorldOptions options) {
options.worldPropertyStrings().forEach((key, value) -> loadedWorld.getStringPropertyHandle()
.setPropertyString(key, value)
.onFailure(failure -> Logging.warning("Failed to set property '%s' to '%s' for world %s: %s",
key, value, loadedWorld.getName(), failure.getMessage())));
pluginManager.callEvent(new MVWorldCreatedEvent(loadedWorld));
saveWorldsConfig();
.peek(loadedWorld -> pluginManager.callEvent(new MVWorldCreatedEvent(loadedWorld)));
}
/**
@@ -317,17 +302,8 @@ public final class WorldManager {
String worldName = options.worldName();
if (!worldNameChecker.isValidWorldName(worldName)) {
return worldActionResult(ImportFailureReason.INVALID_WORLDNAME, worldName);
} else if (options.doFolderCheck()) {
//todo This is a duplicate of folder check in load world
WorldNameChecker.FolderStatus folderStatus = worldNameChecker.checkFolder(options.worldName());
if (!folderStatus.isLoadable()) {
return worldActionResult(ImportFailureReason.WORLD_FOLDER_INVALID, options.worldName());
}
if (folderStatus == WorldNameChecker.FolderStatus.REQUIRES_MIGRATION) {
Logging.info("World '%s' will be automatically migrated by PaperMC to the new dimension " +
"location. If you face any issue with migration, please contact PaperMC support!",
options.worldName());
}
} else if (options.doFolderCheck() && !worldNameChecker.isValidWorldFolder(worldName)) {
return worldActionResult(ImportFailureReason.WORLD_FOLDER_INVALID, worldName);
}
return worldActionResult(options);
}
@@ -369,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;
}
@@ -406,7 +382,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 loadedWorld;
@@ -489,16 +465,8 @@ public final class WorldManager {
return doLoadBukkitWorld(bukkitWorld, mvWorld);
}
if (options.doFolderCheck()) {
WorldNameChecker.FolderStatus folderStatus = worldNameChecker.checkFolder(mvWorld.getName());
if (!folderStatus.isLoadable()) {
return worldActionResult(LoadFailureReason.WORLD_FOLDER_INVALID, mvWorld.getName());
}
if (folderStatus == WorldNameChecker.FolderStatus.REQUIRES_MIGRATION) {
Logging.info("World '%s' will be automatically migrated by PaperMC to the new dimension " +
"location. If you face any issue with migration, please contact PaperMC support!",
mvWorld.getName());
}
if (options.doFolderCheck() && !worldNameChecker.isValidWorldFolder(mvWorld.getName())) {
return worldActionResult(LoadFailureReason.WORLD_FOLDER_INVALID, mvWorld.getName());
}
WorldCreator worldCreator = WorldCreator.name(mvWorld.getName())
@@ -533,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);
@@ -723,7 +691,6 @@ public final class WorldManager {
private Attempt<File, DeleteFailureReason> validateWorldToDelete(
@NotNull LoadedMultiverseWorld world) {
return world.getBukkitWorld().map(World::getWorldFolder)
.peek(folder -> Logging.finer("World folder for world %s is at: %s", world.getName(), folder.getPath()))
.filter(worldNameChecker::isValidWorldFolder)
.map(this::<File, DeleteFailureReason>worldActionResult)
.getOrElse(() -> {
@@ -744,19 +711,18 @@ public final class WorldManager {
.mapAttempt(validatedOptions -> {
ImportWorldOptions importWorldOptions = ImportWorldOptions
.worldName(validatedOptions.newWorldName())
.biome(validatedOptions.fromWorld().getBiome())
.environment(validatedOptions.fromWorld().getEnvironment())
.generator(validatedOptions.fromWorld().getGenerator());
.biome(validatedOptions.world().getBiome())
.environment(validatedOptions.world().getEnvironment())
.generator(validatedOptions.world().getGenerator());
return importWorld(importWorldOptions).transform(CloneFailureReason.IMPORT_FAILED);
})
.onSuccess(newWorld -> {
cloneWorldTransferData(options, newWorld);
if (options.keepWorldConfig()) {
// Special case for spawn location as it's considered a hidden config
newWorld.setSpawnLocation(options.fromWorld().getSpawnLocation());
newWorld.setSpawnLocation(options.world().getSpawnLocation());
}
saveWorldsConfig();
pluginManager.callEvent(new MVWorldClonedEvent(newWorld, options.fromWorld()));
pluginManager.callEvent(new MVWorldClonedEvent(newWorld, options.world()));
});
}
@@ -778,24 +744,19 @@ public final class WorldManager {
if (worldNameChecker.hasWorldFolder(newWorldName)) {
return worldActionResult(CloneFailureReason.WORLD_EXIST_FOLDER, newWorldName);
}
if (!worldNameChecker.isValidWorldFolder(options.fromWorld().getName())) {
return worldActionResult(CloneFailureReason.FROM_WORLD_FOLDER_INVALID, options.fromWorld().getName());
}
return worldActionResult(options);
}
private Attempt<CloneWorldOptions, CloneFailureReason> cloneWorldCopyFolder(@NotNull CloneWorldOptions options) {
if (options.saveBukkitWorld()) {
options.fromWorld().asLoadedWorld().peek(loadedWorld -> {
Logging.finer("Saving world before cloning: " + loadedWorld.getName());
loadedWorld.getBukkitWorld().peek(this::saveWorldWithFlush);
});
Logging.finer("Saving bukkit world before cloning: " + options.world().getName());
options.world().getBukkitWorld().peek(this::saveWorldWithFlush);
}
File worldFolder = BukkitCompatibility.getWorldFoldersDirectory().resolve(options.fromWorld().getName()).toFile();
File newWorldFolder = BukkitCompatibility.getWorldFoldersDirectory().resolve(options.newWorldName()).toFile();
File worldFolder = options.world().getBukkitWorld().map(World::getWorldFolder).get();
File newWorldFolder = new File(Bukkit.getWorldContainer(), options.newWorldName());
return fileUtils.copyFolder(worldFolder, newWorldFolder, CLONE_IGNORE_FILES).fold(
exception -> worldActionResult(CloneFailureReason.COPY_FAILED,
options.fromWorld().getName(), exception),
options.world().getName(), exception),
success -> worldActionResult(options));
}
@@ -811,30 +772,25 @@ public final class WorldManager {
}
private void cloneWorldTransferData(@NotNull CloneWorldOptions options, @NotNull LoadedMultiverseWorld newWorld) {
DataTransfer<LoadedMultiverseWorld> dataTransfer = transferData(options, options.world());
dataTransfer.pasteAllTo(newWorld);
}
private DataTransfer<LoadedMultiverseWorld> transferData(
@NotNull KeepWorldSettingsOptions options, @NotNull LoadedMultiverseWorld world) {
DataTransfer<LoadedMultiverseWorld> dataTransfer = new DataTransfer<>();
if (options.keepWorldConfig()) {
new DataTransfer<MultiverseWorld>()
.addDataStore(new WorldConfigStore<>(), options.fromWorld())
.pasteAllTo(newWorld);
dataTransfer.addDataStore(new WorldConfigStore(), world);
}
if (options.keepGameRule()) {
dataTransfer.addDataStore(new GameRulesStore(), world);
}
if (options.keepWorldBorder()) {
dataTransfer.addDataStore(new WorldBorderStore(), world);
}
newWorld.getBukkitWorld().peek(bukkitWorld -> {
if (!options.keepWorldBorder()) {
WorldBorder worldBorder = bukkitWorld.getWorldBorder();
worldBorder.reset();
}
if (!options.keepGameRule()) {
Arrays.stream(bukkitWorld.getGameRules())
.map(gameRuleName -> GameRule.getByName(gameRuleName))
.filter(Objects::nonNull)
.forEach(gameRule -> {
Object gameRuleDefault = bukkitWorld.getGameRuleDefault(gameRule);
if (gameRuleDefault != null) {
bukkitWorld.setGameRule(gameRule, gameRuleDefault);
}
});
}
});
return dataTransfer;
}
/**
@@ -845,12 +801,11 @@ public final class WorldManager {
*/
public Attempt<LoadedMultiverseWorld, RegenFailureReason> regenWorld(@NotNull RegenWorldOptions options) {
LoadedMultiverseWorld world = options.world();
DataTransfer<LoadedMultiverseWorld> dataTransfer = regenWorldTransferData(options, world);
DataTransfer<LoadedMultiverseWorld> dataTransfer = transferData(options, world);
boolean shouldKeepSpawnLocation = options.keepWorldConfig() && options.seed() == world.getSeed();
Location spawnLocation = world.getSpawnLocation();
CreateWorldOptions createWorldOptions = CreateWorldOptions.worldName(world.getName())
.biome(world.getBiome())
.environment(world.getEnvironment())
.generateStructures(world.canGenerateStructures().getOrElse(true))
.generator(world.getGenerator())
@@ -874,23 +829,6 @@ public final class WorldManager {
});
}
private DataTransfer<LoadedMultiverseWorld> regenWorldTransferData(
@NotNull KeepWorldSettingsOptions options, @NotNull LoadedMultiverseWorld world) {
DataTransfer<LoadedMultiverseWorld> dataTransfer = new DataTransfer<>();
if (options.keepWorldConfig()) {
dataTransfer.addDataStore(new WorldConfigStore<>(), world);
}
if (options.keepGameRule()) {
dataTransfer.addDataStore(new GameRulesStore(), world);
}
if (options.keepWorldBorder()) {
dataTransfer.addDataStore(new WorldBorderStore(), world);
}
return dataTransfer;
}
private <T, F extends FailureReason> Attempt<T, F> worldActionResult(@NotNull T value) {
return Attempt.success(value);
}
@@ -953,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);
@@ -1000,12 +936,12 @@ public final class WorldManager {
* @return A list of all potential worlds.
*/
public List<String> getPotentialWorlds() {
File[] files = BukkitCompatibility.getWorldFoldersDirectory().toFile().listFiles();
File[] files = Bukkit.getWorldContainer().listFiles();
if (files == null) {
return Collections.emptyList();
}
return Arrays.stream(files)
.filter(file -> BukkitCompatibility.getWorldByNameOrKey(file.getName()).isEmpty())
.filter(file -> !isWorld(file.getName()))
.filter(worldNameChecker::isValidWorldFolder)
.map(File::getName)
.toList();
@@ -1072,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 worldName != null && worldsMap.containsKey(worldName);
return worldName != null && worldsMap.containsKey(worldName.toLowerCase(Locale.ENGLISH));
}
/**
@@ -1084,7 +1020,7 @@ public final class WorldManager {
public Option<MultiverseWorld> getUnloadedWorld(@Nullable String worldName) {
return isLoadedWorld(worldName)
? Option.none()
: Option.of(worldName).flatMap(name -> Option.of(worldsMap.get(name)));
: Option.of(worldName).flatMap(name -> Option.of(worldsMap.get(name.toLowerCase(Locale.ENGLISH))));
}
/**
@@ -1157,7 +1093,7 @@ public final class WorldManager {
*/
public Option<LoadedMultiverseWorld> getLoadedWorld(@Nullable String worldName) {
return Option.of(worldName)
.flatMap(name -> Option.of(loadedWorldsMap.get(name)));
.flatMap(name -> Option.of(loadedWorldsMap.get(name.toLowerCase(Locale.ENGLISH))));
}
/**
@@ -1218,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 worldName != null && loadedWorldsMap.containsKey(worldName);
return worldName != null && loadedWorldsMap.containsKey(worldName.toLowerCase(Locale.ENGLISH));
}
/**
@@ -1,36 +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;
import java.util.Locale;
/**
* 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) {
NamespacedKey biomeKey = NamespacedKey.fromString(params.toLowerCase(Locale.ENGLISH));
return new SingleBiomeProvider(Registry.BIOME.get(biomeKey));
}
@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 = Registry.BIOME.stream()
.map(biome -> biome.getKey().getKey().toLowerCase(Locale.ENGLISH))
.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;
}
}
@@ -10,7 +10,6 @@ import org.bukkit.World;
import org.jvnet.hk2.annotations.Service;
import org.mvplugins.multiverse.core.config.handle.StringPropertyHandle;
import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld;
import org.mvplugins.multiverse.core.world.MultiverseWorld;
/**
* A data store for storing and restoring data from an object.
@@ -91,14 +90,14 @@ public interface DataStore<T> {
/**
* A {@link DataStore} for storing and restoring world properties for a multiverse world.
*/
class WorldConfigStore<T extends MultiverseWorld> implements DataStore<T> {
class WorldConfigStore implements DataStore<LoadedMultiverseWorld> {
private Map<String, Object> configMap;
/**
* {@inheritDoc}
*/
@Override
public WorldConfigStore<T> copyFrom(T world) {
public WorldConfigStore copyFrom(LoadedMultiverseWorld world) {
this.configMap = new HashMap<>();
StringPropertyHandle worldPropertyHandler = world.getStringPropertyHandle();
worldPropertyHandler.getAllPropertyNames().forEach(name -> worldPropertyHandler.getProperty(name)
@@ -112,7 +111,7 @@ public interface DataStore<T> {
* {@inheritDoc}
*/
@Override
public WorldConfigStore<T> pasteTo(T world) {
public WorldConfigStore pasteTo(LoadedMultiverseWorld world) {
if (configMap == null) {
return this;
}
@@ -1,18 +1,15 @@
package org.mvplugins.multiverse.core.world.helpers;
import java.io.File;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import io.vavr.control.Option;
import org.bukkit.Bukkit;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jvnet.hk2.annotations.Service;
import org.mvplugins.multiverse.core.utils.REPatterns;
import org.mvplugins.multiverse.core.utils.compatibility.BukkitCompatibility;
/**
* <p>Utility class in helping to check the status of a world name and it's associated world folder.</p>
@@ -32,18 +29,6 @@ public final class WorldNameChecker {
"plugins",
"versions");
private static final List<WorldFolderSchema> WORLD_FOLDER_SCHEMA = List.of(
// OLD
WorldFolderSchema.file("level.dat"),
WorldFolderSchema.folder("DIM1"),
WorldFolderSchema.folder("DIM-1"),
// NEW
WorldFolderSchema.file("paper-world.yml"),
WorldFolderSchema.folder("data"),
WorldFolderSchema.folder("entities"),
WorldFolderSchema.folder("poi"),
WorldFolderSchema.folder("region"));
/**
* Checks if a world name is valid.
*
@@ -96,7 +81,7 @@ public final class WorldNameChecker {
* @return True if check result is valid, else false.
*/
public boolean isValidWorldFolder(@Nullable String worldName) {
return checkFolder(worldName).loadable;
return checkFolder(worldName) == FolderStatus.VALID;
}
/**
@@ -106,7 +91,7 @@ public final class WorldNameChecker {
* @return True if check result is valid, else false.
*/
public boolean isValidWorldFolder(@Nullable File worldFolder) {
return checkFolder(worldFolder).loadable;
return checkFolder(worldFolder) == FolderStatus.VALID;
}
/**
@@ -120,16 +105,7 @@ public final class WorldNameChecker {
if (worldName == null) {
return FolderStatus.DOES_NOT_EXIST;
}
if (BukkitCompatibility.isUsingNewDimensionStorage()) {
File oldWorldFolder = Bukkit.getWorldContainer().toPath().resolve(worldName).toFile();
if (checkFolder(oldWorldFolder) == FolderStatus.VALID) {
return FolderStatus.REQUIRES_MIGRATION;
}
worldName = worldName.toLowerCase(Locale.ENGLISH); // namespace is case-insensitive and stored as lowercase
}
File worldFolder = BukkitCompatibility.getWorldFoldersDirectory().resolve(worldName).toFile();
File worldFolder = new File(Bukkit.getWorldContainer(), worldName);
return checkFolder(worldFolder);
}
@@ -144,7 +120,7 @@ public final class WorldNameChecker {
if (worldFolder == null || !worldFolder.exists() || !worldFolder.isDirectory()) {
return FolderStatus.DOES_NOT_EXIST;
}
if (!folderWorldSchemaCheck(worldFolder)) {
if (!folderHasDat(worldFolder)) {
return FolderStatus.NOT_A_WORLD;
}
return FolderStatus.VALID;
@@ -157,54 +133,9 @@ public final class WorldNameChecker {
* @param worldFolder The File that may be a world.
* @return True if it looks like a world, else false.
*/
private boolean folderWorldSchemaCheck(@NotNull File worldFolder) {
return WORLD_FOLDER_SCHEMA.stream()
.filter(schema -> schema.check(worldFolder))
.count() >= 2;
}
/**
* Helper class to check if a file or folder exist.
*/
private interface WorldFolderSchema {
static WorldFolderSchema file(String path) {
return new WorldFile(path);
}
static WorldFolderSchema folder(String path) {
return new WorldFolder(path);
}
boolean check(File worldFolder);
final class WorldFile implements WorldFolderSchema {
private final String path;
private WorldFile(String path) {
this.path = path;
}
@Override
public boolean check(File worldFolder) {
File thisFolder = worldFolder.toPath().resolve(path).toFile();
return thisFolder.exists() && thisFolder.isFile();
}
}
final class WorldFolder implements WorldFolderSchema {
private final String path;
private WorldFolder(String path) {
this.path = path;
}
@Override
public boolean check(File worldFolder) {
File thisFolder = worldFolder.toPath().resolve(path).toFile();
return thisFolder.exists() && thisFolder.isDirectory();
}
}
private boolean folderHasDat(@NotNull File worldFolder) {
File[] files = worldFolder.listFiles((file, name) -> name.toLowerCase(Locale.ENGLISH).endsWith(".dat"));
return files != null && files.length > 0;
}
/**
@@ -239,39 +170,16 @@ public final class WorldNameChecker {
/**
* Folder is valid.
*/
VALID(true),
/**
* This folder will cause PaperMC to migrate to new dimension world folder in 26.1+
*/
REQUIRES_MIGRATION(true),
VALID,
/**
* Folder exist, but contents in it doesnt look like a world.
*/
NOT_A_WORLD(false),
NOT_A_WORLD,
/**
* Folder does not exist.
*/
DOES_NOT_EXIST(false),
;
private final boolean loadable;
FolderStatus(boolean loadable) {
this.loadable = loadable;
}
/**
* Whether this folder status is loadable, meaning it has the basic world data and can be loaded as a world.
* Note that this does not guarantee the server will definitely load the world with no errors.
*
* @return True if folder probably is loadable by the server, else false.
*/
@ApiStatus.AvailableSince("5.6")
public boolean isLoadable() {
return loadable;
}
DOES_NOT_EXIST
}
}
@@ -1,45 +1,25 @@
package org.mvplugins.multiverse.core.world.options;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld;
import org.mvplugins.multiverse.core.world.MultiverseWorld;
/**
* Options for customizing the cloning of a world.
*/
public final class CloneWorldOptions implements KeepWorldSettingsOptions {
/**
* Creates a new {@link CloneWorldOptions} instance with the given loaded world.
*
* @param fromWorld The loaded world to clone.
* @param newWorldName The name of the new world.
* @return A new {@link CloneWorldOptions} instance.
*
* @deprecated Cloning can be done from unloaded worlds as well. Use {@link #fromTo(MultiverseWorld, String)} instead.
*/
@Deprecated(forRemoval = true, since = "5.6")
@ApiStatus.ScheduledForRemoval(inVersion = "6.0")
public static @NotNull CloneWorldOptions fromTo(@NotNull LoadedMultiverseWorld fromWorld, @NotNull String newWorldName) {
return new CloneWorldOptions(fromWorld, newWorldName);
}
/**
* Creates a new {@link CloneWorldOptions} instance with the given world.
*
* @param fromWorld The world to clone.
* @param world The world to clone.
* @param newWorldName The name of the new world.
* @return A new {@link CloneWorldOptions} instance.
*
* @since 5.6
*/
@ApiStatus.AvailableSince("5.6")
public static @NotNull CloneWorldOptions fromTo(@NotNull MultiverseWorld fromWorld, @NotNull String newWorldName) {
return new CloneWorldOptions(fromWorld, newWorldName);
public static CloneWorldOptions fromTo(LoadedMultiverseWorld world, String newWorldName) {
return new CloneWorldOptions(world, newWorldName);
}
private final MultiverseWorld fromWorld;
private final LoadedMultiverseWorld world;
private final String newWorldName;
private boolean keepGameRule = true;
private boolean keepWorldConfig = true;
@@ -47,34 +27,18 @@ public final class CloneWorldOptions implements KeepWorldSettingsOptions {
private boolean keepWorldBorder = true;
CloneWorldOptions(MultiverseWorld fromWorld, String newWorldName) {
this.fromWorld = fromWorld;
CloneWorldOptions(LoadedMultiverseWorld world, String newWorldName) {
this.world = world;
this.newWorldName = newWorldName;
}
/**
* Gets the loaded world to clone.
* Gets the world to clone.
*
* @return The world to clone.
*
* @deprecated Cloning can be done from unloaded worlds as well. Use {@link #fromWorld()} instead.
*/
@Deprecated(forRemoval = true, since = "5.6")
@ApiStatus.ScheduledForRemoval(inVersion = "6.0")
public LoadedMultiverseWorld world() {
return fromWorld.asLoadedWorld().getOrNull();
}
/**
* Gets the loaded world to clone.
*
* @return The world to clone.
*
* @since 5.6
*/
@ApiStatus.AvailableSince("5.6")
public @NotNull MultiverseWorld fromWorld() {
return fromWorld;
return world;
}
/**
@@ -82,7 +46,7 @@ public final class CloneWorldOptions implements KeepWorldSettingsOptions {
*
* @return The name of the new world.
*/
public @NotNull String newWorldName() {
public String newWorldName() {
return newWorldName;
}
@@ -3,14 +3,8 @@ package org.mvplugins.multiverse.core.world.options;
import co.aikar.commands.ACFUtil;
import org.bukkit.World;
import org.bukkit.WorldType;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.UnmodifiableView;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
/**
* Options for customizing the creation of a new world.
@@ -37,7 +31,6 @@ public final class CreateWorldOptions {
private boolean useSpawnAdjust = true;
private WorldType worldType = WorldType.NORMAL;
private boolean doFolderCheck = true;
private final Map<String, String> worldPropertyStrings = new HashMap<>();
/**
* Creates a new {@link CreateWorldOptions} instance with the given world name.
@@ -267,46 +260,4 @@ public final class CreateWorldOptions {
public boolean doFolderCheck() {
return doFolderCheck;
}
/**
* Sets a world property string key-value pair. Overwrites any existing key.
*
* @param key The key of the world property string.
* @param value The value of the world property string.
* @return This {@link CreateWorldOptions} instance.
*
* @since 5.5
*/
@ApiStatus.AvailableSince("5.5")
public @NotNull CreateWorldOptions worldPropertyString(@NotNull String key, @Nullable String value) {
this.worldPropertyStrings.put(key, value);
return this;
}
/**
* Sets multiple world property string key-value pairs. Overwrites any existing keys.
*
* @param worldProperties A map of world property string key-value pairs.
* @return This {@link CreateWorldOptions} instance.
*
* @since 5.5
*/
@ApiStatus.AvailableSince("5.5")
public @NotNull CreateWorldOptions worldPropertyStrings(@NotNull Map<@NotNull String, @Nullable String> worldProperties) {
this.worldPropertyStrings.putAll(worldProperties);
return this;
}
/**
* Gets an unmodifiable view of the world property strings. Use {@link #worldPropertyString(String, String)} and
* {@link #worldPropertyStrings(Map)} to modify the world property strings.
*
* @return An unmodifiable view of the world property strings.
*
* @since 5.5
*/
@ApiStatus.AvailableSince("5.5")
public @UnmodifiableView @NotNull Map<String, String> worldPropertyStrings() {
return Collections.unmodifiableMap(worldPropertyStrings);
}
}
@@ -3,7 +3,6 @@ package org.mvplugins.multiverse.core.world.reasons;
import co.aikar.locales.MessageKey;
import co.aikar.locales.MessageKeyProvider;
import org.jetbrains.annotations.ApiStatus;
import org.mvplugins.multiverse.core.locale.MVCorei18n;
import org.mvplugins.multiverse.core.utils.result.FailureReason;
@@ -31,14 +30,6 @@ public enum CloneFailureReason implements FailureReason {
*/
WORLD_EXIST_LOADED(MVCorei18n.CLONEWORLD_WORLDEXISTLOADED),
/**
* The source world folder is invalid.
*
* @since 5.6
*/
@ApiStatus.AvailableSince("5.6")
FROM_WORLD_FOLDER_INVALID(MVCorei18n.CLONEWORLD_FROMWORLDFOLDERINVALID),
/**
* Failed to copy the world folder contents.
*/
@@ -273,7 +273,6 @@ mv-core.cloneworld.invalidworldname=World '{world}' contains invalid characters!
mv-core.cloneworld.worldexistfolder=World '{world}' exists in server folders! You need to delete it first before cloning.
mv-core.cloneworld.worldexistunloaded=World '{world}' already exists and it's unloaded! You need to delete it first before cloning.
mv-core.cloneworld.worldexistloaded=World '{world}' already exists! You need to delete it first before cloning.
mv-core.cloneworld.fromworldfolderinvalid=Source world '{world}' folder contents does not seem to be a valid world! Make sure it contains the correct world structure of data and region folders.
mv-core.cloneworld.copyfailed=Failed to copy world to '{world}': {error}\n&fSee console for more details.
mv-core.createworld.invalidworldname=World '{world}' contains invalid characters!
@@ -289,7 +288,7 @@ mv-core.deleteworld.failedtodeletefolder=Failed to delete world folder '{world}'
mv-core.importworld.invalidworldname=World '{world}' contains invalid characters!
mv-core.importworld.worldexistunloaded=World '{world}' already exists, but it's not loaded!&f Type '&a/mv load {world}&f' if you wish to load it.
mv-core.importworld.worldexistloaded=World '{world}' already exists!
mv-core.importworld.worldfolderinvalid=World '{world}' folder contents does not seem to be a valid world! Make sure it contains the correct world structure of data and region folders.\n&cIf the server software does something different with world folders, or you are very certain the world is valid, use '&3--skip-folder-check&c' flag.
mv-core.importworld.worldfolderinvalid=World '{world}' folder contents does not seem to be a valid world!\n&cIf the server software does something different with world folders, or you are very certain the world is valid, use '&3--skip-folder-check&c' flag.
mv-core.importworld.bukkitenvironmentmismatch=Environment mismatch detected!&f The world '{world}' is already loaded with environment '{bukkitEnvironment}', but Multiverse is trying to import it with environment '{mvEnvironment}'.
mv-core.loadworld.worldalreadyloading=World '{world}' is already loading! Please wait...
@@ -1,6 +1,6 @@
# configuration
mv-core.config.save.failed=保存 Multiverse-Core 的 config.yml 文件失败,所有修改都将是临时的!
mv-core.config.node.notfound=在配置文件中无法找到节点: {node}
mv-core.config.save.failed=保存 Multiverse-Core 的 config.yml 文件失败,所有操作都将是临时的!
mv-core.config.node.notfound=在配置文件中无法找到节点: {node}
# /mv check
mv-core.check.description=检查某位玩家是否可以将自己传送至某个地方。
@@ -8,7 +8,7 @@ mv-core.check.player.description=要检查的玩家。
mv-core.check.destination.description=目标的地址,例如某个世界的名字。
mv-core.check.haspermission=&a{player} 可以传送至 {destination}
mv-core.check.nopermission=&c{player} 不能传送至 {destination}
mv-core.check.location=目标的地址是: &f{location}
mv-core.check.location=目标的地址是: &f{location}
# /mv clone
mv-core.clone.description=复制一个世界。
@@ -19,7 +19,7 @@ mv-core.clone.success=&a已将世界复制到 '{world}'!
# /mv config
mv-core.config.description=查看或设置某个配置的值。
mv-core.config.name.description=需要设置或查看的配置。
mv-core.config.name.description=需要查看或设置的配置。
mv-core.config.value.description=需要设置的值。如果没有指定,将显示当前配置的值。
mv-core.config.show.success=&a配置 '&6{name}&a' 已被设置为 '&6{value}&a'。
mv-core.config.show.error=&c获取配置 '&6{name}&c' 的值失败。\n&c{error}
@@ -30,29 +30,29 @@ mv-core.config.set.error=&c将配置 '&6{name}&c' 设置为 '&6{value}&c' 失败
mv-core.confirm.description=在执行危险的指令前再一次进行确认。
# /mv coordinates
mv-core.coordinates.description=直接发送你的坐标
mv-core.coordinates.description=发送你的坐标信息
mv-core.coordinates.info.title=&b--- 位置信息 ---
mv-core.coordinates.info.world=&b世界: &f{world}
mv-core.coordinates.info.alias=&b别名: &f{alias}
mv-core.coordinates.info.worldScale=&b世界比例: &f{scale}
mv-core.coordinates.info.coordinates=&b坐标: &f{coordinates}
mv-core.coordinates.info.direction=&b方向: &f{direction}
mv-core.coordinates.info.world=&b世界: &f{world}
mv-core.coordinates.info.alias=&b别名: &f{alias}
mv-core.coordinates.info.worldScale=&b世界尺度: &f{scale}
mv-core.coordinates.info.coordinates=&b坐标: &f{coordinates}
mv-core.coordinates.info.direction=&b方向: &f{direction}
# /mv create
mv-core.create.description=创建一个新的世界并且加载。
mv-core.create.name.description=新世界的名称。
mv-core.create.environment.description=世界的环境。详见: /mv environments
mv-core.create.environment.description=世界的环境。详见:/mv environments
mv-core.create.flags.description=附加的世界设置。在 https://gg.gg/nn8bl 上查看所有可能的标志。
mv-core.create.properties=使用以下属性创建世界 {worldName} :
mv-core.create.properties.environment=- 环境: &f{environment}
mv-core.create.properties.seed=- 种子: &f{seed}
mv-core.create.properties.worldtype=- 世界类型: &f{worldType}
mv-core.create.properties.adjustspawn=- 调节出生点: &f{adjustSpawn}
mv-core.create.properties.biome=- 生物群系: &f{biome}
mv-core.create.properties.generator=- 生成器: &f{generator}
mv-core.create.properties.generatorsettings=- 生成器设置: &f{generatorSettings}
mv-core.create.properties.environment=- 环境: &f{environment}
mv-core.create.properties.seed=- 种子: &f{seed}
mv-core.create.properties.worldtype=- 世界类型: &f{worldType}
mv-core.create.properties.adjustspawn=- 调节出生点: &f{adjustSpawn}
mv-core.create.properties.biome=- 生物群系: &f{biome}
mv-core.create.properties.generator=- 生成器: &f{generator}
mv-core.create.properties.generatorsettings=- 生成器设置: &f{generatorSettings}
mv-core.create.properties.structures=- 结构: &f{structures}
mv-core.create.properties.structures=- 结构: &f{structures}
mv-core.create.loading=创建世界中……
mv-core.create.success=&a世界 '{world}' 已经创建!
@@ -65,14 +65,14 @@ mv-core.debug.change.syntax=等级
mv-core.debug.change.level.description=需要设置的调试等级。
# /mv delete
mv-core.delete.description=在服务器上“永久”删除某个世界。
mv-core.delete.description=在服务器上永久删除某个世界。
mv-core.delete.deleting=正在删除世界 '{world}'……
mv-core.delete.prompt=你确定想要删除世界 '{world}' 吗?
mv-core.delete.success=&a世界 '{world}' 已经删除!
# /mv dumps
mv-core.dumps.description=导出版本信息到控制台或者粘贴服务。
mv-core.dumps.url.list={service}:{link}
mv-core.dumps.url.list={service}:{link}
# /mv gamerule set
mv-core.gamerule.set.description=修改某个世界或某些世界的游戏规则。
@@ -105,24 +105,24 @@ mv-core.generators.empty=&c未找到生成器插件。
# /mv import
mv-core.import.description=导入一个已经存在的世界文件夹。
mv-core.import.name.description=世界文件夹的名称。
mv-core.import.env.description=世界的环境。详见: /mv env
mv-core.import.other.description=其他世界设置。详见: https://gg.gg/nn8c2
mv-core.import.env.description=世界的环境。详见:/mv env
mv-core.import.other.description=其他世界设置。详见:https://gg.gg/nn8c2
mv-core.import.importing=开始导入世界 '{world}'……
mv-core.import.success=&a已导入世界 '{world}'!
# /mv info
mv-core.info.description=显示某个世界所有已配置的属性。
mv-core.info.description.world=需要显示信息的世界。
mv-core.info.header=&a&l---- 世界信息: &f&l{world}&a&l ----
mv-core.info.header=&a&l---- 世界信息: &f&l{world}&a&l ----
mv-core.info.nocontent=&c没有找到这样的世界属性!
# /mv list
mv-core.list.description=显示你所有可进入世界的列表。
mv-core.list.description=显示所有可进入世界的列表。
mv-core.list.header=&6====[ 多世界列表 ]====
mv-core.list.nocontent=&c未找到任何世界,或者你没有权限进入它们!
# /mv load
mv-core.load.description=加载某个世界。世界必须已经在 worlds.yml 中,或者请使用 /mv import 导入。
mv-core.load.description=加载某个世界,世界必须已经在 worlds.yml,或者请使用 /mv import 导入。
mv-core.load.world.description=想要加载的世界名称。
mv-core.load.loading=加载世界 '{world}' 中……
mv-core.load.success=&a已加载世界 '{world}'!
@@ -132,7 +132,6 @@ mv-core.modify.description=修改某个给定世界的属性。
mv-core.modify.world.description=需要修改的世界的名称。
mv-core.modify.property.description=需要修改的属性。
mv-core.modify.value.description=需要修改属性的值。
mv-core.modify.propertyremoved=&c你不能修改 '&3{property}&c',因为它已经改变或被移除。请使用 '&a{replacement}&c'。
mv-core.modify.cannothavevalue=你不能指定一个值来 {action} '{property}'。
mv-core.modify.specifyvalue=你必须指定一个值来 {action} '{property}'。
mv-core.modify.success=&a成功在世界 &9{world}&a 中 {action} '&9{property}&a' 为 '&9{value}&a' 。
@@ -140,12 +139,12 @@ mv-core.modify.failure=&c在世界 &9{world}&c 中 {action} '&9{property}&c' 为
mv-core.modify.failure.novalue=&c在世界 &9{world}&c 中 {action} '&9{property}&c' 失败。\n&c{error}
# /mv regen
mv-core.regen.description=在你的服务器上重新生成一个世界。之前的状态将会“永久”丢失。
mv-core.regen.description=在你的服务器上重新生成一个世界。之前的状态将会被永久丢弃。
mv-core.regen.world.description=你想要重新生成的世界。
mv-core.regen.other.description=其他的世界设置。详见: http://gg.gg/nn8lk
mv-core.regen.other.description=其他的世界设置。详见:http://gg.gg/nn8lk
mv-core.regen.regenerating=重新生成世界 '{world}'……
mv-core.regen.prompt=你确定想要重新生成世界 '{world}' 吗?
mv-core.regen.success=&a已重新生成世界 '{world}'!
mv-core.regen.success=&a已重新生成 世界 '{world}'!
# /mv reload
mv-core.reload.description=重新加载所有 Multiverse 模块的配置文件。
@@ -154,7 +153,7 @@ mv-core.reload.success=&a重新加载完成!
# /mv remove
mv-core.remove.description=从 Multiverse 中卸载某个世界并从 worlds.yml 中移除。这不会删除世界的文件夹。
mv-core.remove.world.description=你想要从 Multiverse 认知中移除的世界。
mv-core.remove.world.description=你想要从MV认知中移除的世界。
mv-core.remove.success=&a已移除世界 '{world}'!
# /mv
@@ -165,10 +164,10 @@ mv-core.root.help=&a查看 &f/mv help&a 了解可以执行的指令。
mv-core.setspawn.description=为某个特定的世界设置出生点。
mv-core.setspawn.location.description=新出生点的位置。
mv-core.setspawn.world.description=需要设置出生点的目标世界(默认为玩家的当前世界)
mv-core.setspawn.unsafe=&c新出生点不安全!如果是有意为之,你可以通过使用 &6--unsafe&c 标志来禁用安全检查。
mv-core.setspawn.success=&a成功在 &6{world}&a 中将出生点设置为 &6{location}&a!
mv-core.setspawn.failed=&c在 &6{world}&c 中将出生点设置为 &6{location}&c 时失败。{error}
mv-core.setspawn.notmvworld=&c无法为 &c{world} 设置出生点,因为它不是一个 Multiverse 世界。
mv-core.setspawn.unsafe=&c新的生成位置不安全!如果这是有意为之,你可以通过使用&6--unsafe&c标志来禁用安全检查。
mv-core.setspawn.success=&a成功在&6{world}&a中将生成点设置为&6{location}&a!
mv-core.setspawn.failed=&c在&6{world}&c中将生成点设置为&6{location}&c时失败。{error}
mv-core.setspawn.notmvworld=&c无法为&c{world}设置生成点,因为它不是一个Multiverse世界。
# /mv spawn
mv-core.spawn.description=传送某个特定的玩家到他们所在世界的出生点。
@@ -185,7 +184,7 @@ mv-core.teleport.success=已传送 {player} 到 {destination}。
mv-core.teleport.failed=传送 {player} 到 {destination}失败。{reason}
# /mv unload
mv-core.unload.description=从 Multiverse 中卸载某个世界。这“不会”移除世界的文件夹。这“不会”将它从配置文件中移除。
mv-core.unload.description=从 Multiverse 中卸载某个世界。这不会移除世界的文件夹。这不会将它从配置文件中移除。
mv-core.unload.world.description=你想要卸载的世界名称。
mv-core.unload.unloading=正在卸载世界 '{world}'……
mv-core.unload.success=&a已卸载 '{world}'!
@@ -197,7 +196,7 @@ mv-core.usage.description=显示 Multiverse-Core 指令的用法。
mv-core.version.description=显示版本和作者。
mv-core.version.mv=Multiverse Core 版本 &fv{version}
mv-core.version.authors=Multiverse Core 作者 &f{authors}
mv-core.version.secretcode=特征码: &fFRN002
mv-core.version.secretcode=特征码: &fFRN002
# /mv who
# /mv whoall
@@ -206,7 +205,7 @@ mv-core.who.all.description=列出所有世界的玩家
mv-core.who.world.description=需要列出玩家的世界名称
mv-core.who.flags.description=筛选 - 仅显示匹配的条目。页码 - 查看的页面序号。
mv-core.who.empty=&7&o空
mv-core.who.header=&b====[ Multiverse世界玩家列表 ]====
mv-core.who.header=&b====[ 多世界玩家列表 ]====
# /mv worldborder
mv-core.worldborder.center.nothingchanged=&c无变化。世界边界的中心在世界 '&6{world}&c' 中已经有相同的设置。
@@ -215,8 +214,8 @@ mv-core.worldborder.center.success=&r已设置世界 '&6{world}&r' 边界的中
mv-core.worldborder.damageamount.nothingchanged=&c无变化。世界边界的伤害在世界 '&6{world}&c' 中已经有相同的设置。
mv-core.worldborder.damageamount.success=&r已设置世界 '&6{world}&r' 的边界伤害为每秒每格 {amount} 点伤害。
mv-core.worldborder.damagebuffer.nothingchanged=&c无变化。世界边界伤害的缓冲区在世界 '&6{world}&c' 中已经有相同的设置。
mv-core.worldborder.damagebuffer.success=&r已设置世界 '&6{world}&r' 边界伤害的缓冲区为 {distance} 格。
mv-core.worldborder.damagebuffer.nothingchanged=&c无变化。世界边界伤害的缓冲在世界 '&6{world}&c' 中已经有相同的设置。
mv-core.worldborder.damagebuffer.success=&r已设置世界 '&6{world}&r' 边界伤害的缓冲为 {distance} 格。
mv-core.worldborder.get.size=&r世界 '&6{world}&r' 的边界宽度目前为 {size} 格。
@@ -233,7 +232,7 @@ mv-core.worldborder.warningtime.success=&r已设置世界 '&6{world}&r' 的边
# commands error
mv-core.commands.error.playersonly=&c该指令仅玩家可以使用。
mv-core.commands.error.multiverseworldonly=&c该指令仅可以在Multiverse世界中使用。
mv-core.commands.error.multiverseworldonly=&c该指令仅可以在多世界中使用。
# entry check
mv-core.entrycheck.blacklisted='{world}' 在黑名单中。
@@ -243,29 +242,29 @@ mv-core.entrycheck.exceedplayerlimit=该世界已经达到最大玩家数量限
mv-core.entrycheck.noworldaccess=你没有权限进入该世界。
# economy
mv-core.economy.item.deposit=&a{price}&f 已存入你的背包。
mv-core.economy.item.withdraw=&f你已被收取 &a{price}。
mv-core.economy.vault.deposit=&2{price}&f 已存入你的账户。
mv-core.economy.vault.withdraw=&f你已被收取 &2{price}。
mv-core.economy.item.deposit=&a{price}&f已存入你的背包。
mv-core.economy.item.withdraw=&f你已被收取&a{price}。
mv-core.economy.vault.deposit=&2{price}&f已存入你的账户。
mv-core.economy.vault.withdraw=&f你已被收取&2{price}。
# multiverse parse destination failure reason
mv-core.destination.anchor.failurereason.anchornotfound=&c锚点 '&6{anchor}&c' 不存在!
mv-core.destination.bed.failurereason.playernotfound=&玩家 '&6{player}&c' 不存在或不在线!如果想传送到自己的床,使用 'playerbed'。
mv-core.destination.cannon.failurereason.invalidformat=&c大炮目标格式是: &6ca:worldname:x,y,z:pitch:yaw:speed
mv-core.destination.exact.failurereason.invalidformat=&c精确目标格式是: &6e:worldname:x,y,z:pitch:yaw
mv-core.destination.cannon.failurereason.invalidformat=&c大炮目标格式是:&6ca:worldname:x,y,z:pitch:yaw:speed
mv-core.destination.exact.failurereason.invalidformat=&c精确目标格式是:&6e:worldname:x,y,z:pitch:yaw
mv-core.destination.player.failurereason.playernotfound=&c玩家 '&6{player}&c' 不存在或不在线!
mv-core.destination.shared.failurereason.invalidcoordinatesformat=你必须指定一个具有以下格式有效的坐标: &6{x},{y},{z}
mv-core.destination.shared.failurereason.invalidnumberformat=无效的目标输入。{error}。
mv-core.destination.shared.failurereason.invalidcoordinatesformat=你必须指定一个具有以下格式有效的坐标:&6{x},{y},{z}
mv-core.destination.shared.failurereason.invalidnumberformat=给定的值不是一个有效的数字。{error}。
mv-core.destination.shared.failurereason.worldnotfound=&c世界 '&6{world}&c' 不存在或着未被加载!
mv-core.destination.parse.failurereason.invaliddestinationid=无效的目标ID: {id}。有效的目标ID有: {ids}
mv-core.destination.parse.failurereason.invaliddestinationid=无效的目标ID:{id}。有效的目标ID有:{ids}
# teleport failure reason
mv-core.teleportfailurereason.null.destination=目标无效!
mv-core.teleportfailurereason.null.location=位置无效!
mv-core.teleportfailurereason.null.world=&c目标世界没有加载或者不存在!
mv-core.teleportfailurereason.unsafe.location=位置被认为是不安全的!如果是有意为之,使用 `--unsafe` 强制执行。
mv-core.teleportfailurereason.teleport.failed=传送被拒绝。请确保其他插件没有阻止传送,并查看服务器的日志获得错误信息。
mv-core.teleportfailurereason.teleport.failed.exception=在传送过程中出现了一个错误。查看服务器日志获取更多信息。
mv-core.teleportfailurereason.null.world=&c位置所在的世界无效!
mv-core.teleportfailurereason.unsafe.location=位置是不安全的!使用 `--unsafe` 忽视该问题。
mv-core.teleportfailurereason.teleport.failed=出于某些拒绝了该传送。
mv-core.teleportfailurereason.teleport.failed.exception=在传送过程中出现了一个错误。查看控制台获取更多信息。
mv-core.teleportfailurereason.event.cancelled=传送被另一个插件取消了。
# world manager result
@@ -273,8 +272,7 @@ mv-core.cloneworld.invalidworldname=世界 '{world}' 包含无效的字符!
mv-core.cloneworld.worldexistfolder=世界 '{world}' 已经存在于服务器的文件中!在复制前你需要先删除它。
mv-core.cloneworld.worldexistunloaded=世界 '{world}' 已经存在并且未被加载!在复制前你需要先删除它。
mv-core.cloneworld.worldexistloaded=世界 '{world}' 已经存在!在复制前你需要先删除它。
mv-core.cloneworld.fromworldfolderinvalid=源世界 '{world}' 文件夹的内容似乎不是一个有效的世界!请确保它包含正确的世界数据和区域文件夹结构。
mv-core.cloneworld.copyfailed=复制世界到 '{world}' 失败: {error}\n&f查看控制台获取更多信息。
mv-core.cloneworld.copyfailed=复制世界到 '{world}' 失败:{error}\n&f查看控制台获取更多信息。
mv-core.createworld.invalidworldname=世界 '{world}' 包含无效的字符!
mv-core.createworld.worldexistfolder=世界 '{world}' 已经存在于服务器的文件中!&f 如果你想要导入它,输入 '&a/mv import {world} <environment>&f'。
@@ -282,37 +280,35 @@ mv-core.createworld.worldexistunloaded=世界 '{world}' 已经存在, 但是未
mv-core.createworld.worldexistloaded=世界 '{world}' 已经存在!
mv-core.deleteworld.worldnonexistent=世界 '{world}' 不存在!
mv-core.deleteworld.loadfailed=不能加载世界 '{world}',请确认世界文件夹是否被删除或者损坏。&f 你可以执行 '&a/mv remove {world}&f' 将它从 Multiverse 中移除,或者使用 '&a/mv load {world}&f' 尝试重新加载。
mv-core.deleteworld.worldfoldernotfound=&c无法定位 '{world}' 文件夹!&f 你可以执行 '&a/mv remove {world}&c' 将它从 Multiverse 中移除,如果存在,也可以手动删除文件夹。
mv-core.deleteworld.failedtodeletefolder=删除世界 '{world}' 的文件夹失败: {error}\n&f查看控制台获取更多信息。
mv-core.deleteworld.loadfailed=不能加载世界 '{world}',请确认世界文件夹是否存在。&f 你可以执行 '&a/mv remove {world}&f' 将它从 Multiverse 中移除,或者使用 '&a/mv load {world}&f' 尝试重新加载。
mv-core.deleteworld.worldfoldernotfound=世界 '{world}' folder not found!
mv-core.deleteworld.failedtodeletefolder=删除世界 '{world}' 的文件夹失败:{error}\n&f查看控制台获取更多信息。
mv-core.importworld.invalidworldname=世界 '{world}' 包含无效的字符!
mv-core.importworld.worldexistunloaded=世界 '{world}' 已经存在, 但是未被加载!&f 如果你想要加载它,输入 '&a/mv load {world}&f'。
mv-core.importworld.worldexistloaded=世界 '{world}' 已经存在!
mv-core.importworld.worldfolderinvalid=世界 '{world}' 文件夹的内容似乎不是一个有效的世界!请确保它包含正确的世界数据和区域文件夹结构。\n&c如果服务器软件对世界文件夹的处理方式不同,或者你确信世界是有效的,使用 '&3--skip-folder-check&c' 标志。
mv-core.importworld.bukkitenvironmentmismatch=环境不匹配!&f 世界 '{world}' 已经用环境 '{bukkitEnvironment}' 加载,但 Multiverse 正在尝试用环境 '{mvEnvironment}' 导入它。
mv-core.importworld.worldfolderinvalid=世界 '{world}' 文件夹的内容看上去不像是一个有效的世界!
mv-core.loadworld.worldalreadyloading=世界 '{world}' 已经正在加载!请稍后……
mv-core.loadworld.worldnonexistent=世界 '{world}' 不存在!使用 '&a/mv create {world} <environment>&f' 来创建。
mv-core.loadworld.worldexistfolder=世界 '{world}' 已经存在于服务器的文件中, 但是未被 Multiverse 识别!&f 如果你想要导入它,输入 '&a/mv import {world} <environment>&f'。
mv-core.loadworld.worldexistloaded=世界 '{world}' 已经加载!
mv-core.loadworld.bukkitenvironmentmismatch=环境不匹配!&f 世界 '{world}' 已经用环境 '{bukkitEnvironment}' 加载,但 Multiverse 正在尝试用环境 '{mvEnvironment}' 加载它。
mv-core.loadworld.worldexistloaded=世界 '{world}' 已经存在!
mv-core.removeworld.worldnonexistent=世界 '{world}' 不存在!
mv-core.unloadworld.worldalreadyunloading=世界 '{world}' 已经在卸载! 请稍后……
mv-core.unloadworld.worldnonexistent=世界 '{world}' 不存在!
mv-core.unloadworld.worldunloaded=世界 '{world}' 已经卸载!
mv-core.unloadworld.bukkitunloadfailed=Bukkit 卸载世界 '{world}' 失败: {error}
mv-core.unloadworld.bukkitunloadfailed=Bukkit 卸载世界 '{world}' 失败:{error}
mv-core.worldcreator.invalidbiomeprovider=&c无效的生物群系 '&6{biome}&c'!{error}\n&c查看控制台获取更多信息。
mv-core.worldcreator.invalidchunkgenerator=&c无效的区块生成器 '&6{generator}&c'! {error}\n&c这更像是生成器插件的问题,而不是 Multiverse。查看控制台获取更多信息。
mv-core.worldcreator.bukkitcreationfailed=&cBukkit 创建世界 '{world}' 失败: {error}\n&c查看控制台获取更多信息。
mv-core.worldcreator.bukkitcreationfailed=&cBukkit 创建世界 '{world}' 失败:{error}\n&c查看控制台获取更多信息。
# queue command result
mv-core.queuecommand.nocommandinqueue=&c你没有任何指令在列队中。
mv-core.queuecommand.invalidotp=&c无效的 OTP 数字 '&6{otp}&c'。请重试……
mv-core.queuecommand.commandexecutionerror=&c在处理列队中命令时发生了错误: {error}\n&f查看控制台获取更多信息。
mv-core.queuecommand.commandexecutionerror=&c在处理列队中命令时发生了错误:{error}\n&f查看控制台获取更多信息。
# content display messages
mv-core.contentdisplay.nocontent=&c没有可显示的内容
@@ -326,12 +322,7 @@ mv-core.contentdisplay.null=&7&o错误
mv-core.exception.multiverseworld.createnull=创建的世界返回为null!
mv-core.exception.multiverseworld.unloaddefaultworld=&c你不能卸载默认的世界!世界 &6{world}&c 是定义在 server.properties '&6level-name&c' 中的默认世界。
mv-core.exception.multiverseworld.unloadplayersinworld=&c目前仍然有 &6{count}&c 位玩家在世界中!在命令中使用 '&6--remove-players&c' 标签将所有的玩家从世界中移除。
mv-core.exception.multiverseworld.unloaderror=&c在卸载世界 &6{world}&c 过程中出现了一个未知的错误。\n&c查看控制台获取更多信息。
# multiverse position parse exception
mv-core.exception.positionparse.invaliddirection=&c无效的方向字符串格式: {format}。预期的格式: <pitch>:<yaw>
mv-core.exception.positionparse.invalidcoordinates=&c无效的坐标格式: {format}。预期的格式: <x>,<y>,<z>
mv-core.exception.positionparse.invalidnumber=&c无效的数字格式: {number}。预期为一个数字值。
mv-core.exception.multiverseworld.unloaderror=&c在卸载世界 &6{world}&c 过程中出现了一个位置的错误。\n&c查看控制台获取更多信息。
# generic
mv-core.generic.success=成功!
@@ -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)
}
}
@@ -1,88 +0,0 @@
package org.mvplugins.multiverse.core.inject
import org.mvplugins.multiverse.core.TestWithMockBukkit
import org.mvplugins.multiverse.core.anchor.AnchorManager
import org.mvplugins.multiverse.core.commands.CoreCommand
import org.mvplugins.multiverse.core.command.MVCommandManager
import org.mvplugins.multiverse.core.config.CoreConfig
import org.mvplugins.multiverse.core.destination.Destination
import org.mvplugins.multiverse.core.economy.MVEconomist
import org.mvplugins.multiverse.core.listeners.CoreListener
import org.mvplugins.multiverse.core.teleportation.AsyncSafetyTeleporter
import org.mvplugins.multiverse.core.teleportation.BlockSafety
import org.mvplugins.multiverse.core.teleportation.LocationManipulation
import org.mvplugins.multiverse.core.teleportation.TeleportQueue
import org.mvplugins.multiverse.core.world.WorldManager
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
class InjectionTest : TestWithMockBukkit() {
@Test
fun `AnchorManager is available as a service`() {
assertNotNull(serviceLocator.getActiveService(AnchorManager::class.java))
}
@Test
fun `AsyncSafetyTeleporter is available as a service`() {
assertNotNull(serviceLocator.getActiveService(AsyncSafetyTeleporter::class.java))
}
@Test
fun `BlockSafety is available as a service`() {
assertNotNull(serviceLocator.getActiveService(BlockSafety::class.java))
assertNotNull(serviceLocator.getActiveService(BlockSafety::class.java))
}
@Test
fun `MVCommandManager is available as a service`() {
assertNotNull(serviceLocator.getActiveService(MVCommandManager::class.java))
}
@Test
fun `MVEconomist is available as a service`() {
assertNotNull(serviceLocator.getActiveService(MVEconomist::class.java))
}
@Test
fun `LocationManipulation is available as a service`() {
assertNotNull(serviceLocator.getActiveService(LocationManipulation::class.java))
assertNotNull(serviceLocator.getActiveService(LocationManipulation::class.java))
}
@Test
fun `TeleportQueue is available as a service`() {
assertNotNull(serviceLocator.getActiveService(TeleportQueue::class.java))
}
@Test
fun `MVWorldManager is available as a service`() {
assertNotNull(serviceLocator.getActiveService(WorldManager::class.java))
assertNotNull(serviceLocator.getActiveService(WorldManager::class.java))
}
@Test
fun `CoreListener(s) is available as a service`() {
val listeners = assertNotNull(serviceLocator.getAllServices(CoreListener::class.java))
assertEquals(7, listeners.size)
}
@Test
fun `CoreConfig is available as a service`() {
assertNotNull(serviceLocator.getActiveService(CoreConfig::class.java))
assertNotNull(serviceLocator.getActiveService(CoreConfig::class.java))
}
@Test
fun `Commands are available as services`() {
val commands = serviceLocator.getAllActiveServices(CoreCommand::class.java)
assertEquals(57, commands.size)
}
@Test
fun `Destinations are available as services`() {
val destinations = serviceLocator.getAllActiveServices(Destination::class.java)
assertEquals(6, destinations.size)
}
}
@@ -1,55 +0,0 @@
package org.mvplugins.multiverse.core.mock;
import org.bukkit.World;
import org.bukkit.WorldCreator;
import org.jetbrains.annotations.NotNull;
import org.mockbukkit.mockbukkit.ServerMock;
import org.mockbukkit.mockbukkit.command.CommandMapMock;
import org.mockbukkit.mockbukkit.world.WorldMock;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
public class MVServerMock extends ServerMock {
private final File worldContainer;
public MVServerMock() throws IOException {
super();
this.worldContainer = Files.createTempDirectory("world-container").toFile();
this.worldContainer.deleteOnExit();
System.out.println("Created test world folder: " + this.worldContainer.getAbsolutePath());
}
// This is required for acf reflection to work
@Override
public @NotNull CommandMapMock getCommandMap() {
return super.getCommandMap();
}
@Override
public @NotNull File getWorldContainer() {
return this.worldContainer;
}
@Override
public World createWorld(@NotNull WorldCreator creator) {
WorldMock world = new MVWorldMock(creator);
world.getWorldFolder().mkdirs();
createFile(new File(world.getWorldFolder(), "uid.dat"));
createFile(new File(world.getWorldFolder(), "level.dat"));
new File(world.getWorldFolder(), "region").mkdir();
new File(world.getWorldFolder(), "data").mkdir();
addWorld(world);
return world;
}
private void createFile(File file) {
try {
file.createNewFile();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
@@ -1,28 +0,0 @@
package org.mvplugins.multiverse.core.mock;
import org.bukkit.WorldCreator;
import org.jetbrains.annotations.NotNull;
import org.mockbukkit.mockbukkit.MockBukkit;
import org.mockbukkit.mockbukkit.world.WorldMock;
import java.io.File;
public class MVWorldMock extends WorldMock {
private final File worldFolder;
public MVWorldMock(@NotNull WorldCreator creator) {
super(creator);
this.worldFolder = new File(MockBukkit.getMock().getWorldContainer(), getName());
}
@Override
public @NotNull File getWorldFolder() {
return this.worldFolder;
}
@Override
public String toString() {
return "MVWorldMock{'name': '" + this.getName() + "'}";
}
}
@@ -1,29 +0,0 @@
package org.mvplugins.multiverse.core.utils
import org.bukkit.Material
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertNull
class MaterialConverterTest {
@Test
fun `Convert dirt name to material`() {
assertEquals(Material.DIRT, MaterialConverter.stringToMaterial("dirt"))
}
@Test
fun `Convert Spruce Planks numerical id to material`() {
assertEquals(Material.SPRUCE_PLANKS, MaterialConverter.stringToMaterial("5:1"))
}
@Test
fun `Convert Oak Sapling item id to material`() {
assertEquals(Material.OAK_SAPLING, MaterialConverter.stringToMaterial("minecraft:oak_sapling"))
}
@Test
fun `Convert invalid string to material`() {
assertNull(MaterialConverter.stringToMaterial("invalid"))
}
}
@@ -1,64 +0,0 @@
package org.mvplugins.multiverse.core.utils
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertNull
class StringFormatterTest {
@Test
fun `StringFormatter parseQuotesInArgs`() {
assertEquals(
listOf("before", "this is a test", "after"),
StringFormatter.parseQuotesInArgs(arrayOf("before", "\"this", "is", "a", "test\"", "after"))
)
assertEquals(
listOf("\""),
StringFormatter.parseQuotesInArgs(arrayOf("\""))
)
assertEquals(
listOf("\"", "after"),
StringFormatter.parseQuotesInArgs(arrayOf("\"", "after"))
)
assertEquals(
listOf("\"word", "after"),
StringFormatter.parseQuotesInArgs(arrayOf("\"word", "after"))
)
assertEquals(
listOf("word\"", "after\""),
StringFormatter.parseQuotesInArgs(arrayOf("word\"", "after\""))
)
assertEquals(
listOf("word\"", "af\"ter"),
StringFormatter.parseQuotesInArgs(arrayOf("word\"", "af\"ter"))
)
}
@Test
fun `StringFormatter quoteMultiWordString`() {
assertEquals(
"\"this is a test\"",
StringFormatter.quoteMultiWordString("this is a test")
)
assertEquals(
"test",
StringFormatter.quoteMultiWordString("test")
)
assertNull(StringFormatter.quoteMultiWordString(null))
}
@Test
fun `StringFormatter parseCSVMap`() {
assertEquals(
emptyMap<String, String>(),
StringFormatter.parseCSVMap("")
)
assertEquals(
mapOf("key" to "value"),
StringFormatter.parseCSVMap("key=value")
)
assertEquals(
mapOf("key1" to "value1", "key2" to "value2", "key3" to "value3"),
StringFormatter.parseCSVMap("key1=value1,key2=value2,key3=value3")
)
}
}
@@ -1,58 +0,0 @@
package org.mvplugins.multiverse.core.utils
import org.mvplugins.multiverse.core.utils.matcher.ExactStringMatcher
import org.mvplugins.multiverse.core.utils.matcher.RegexStringMatcher
import org.mvplugins.multiverse.core.utils.matcher.WildcardStringMatcher
import kotlin.test.Test
class StringMatcherTest {
@Test
fun `exact string matcher - single string`() {
val matcher = ExactStringMatcher("test")
assert(matcher.matches("test"))
assert(!matcher.matches("Test"))
assert(!matcher.matches("testing"))
}
@Test
fun `exact string matcher - multiple strings`() {
val matcher = ExactStringMatcher(setOf("test", "example", "sample"));
assert(matcher.matches("test"))
assert(matcher.matches("example"))
assert(matcher.matches("sample"))
assert(!matcher.matches("Test"))
assert(!matcher.matches("testing"))
}
@Test
fun `wildcard string matcher - single`() {
val matcher = WildcardStringMatcher("test*")
assert(matcher.matches("test"))
assert(matcher.matches("test123"))
assert(matcher.matches("testing"))
assert(!matcher.matches("TEST"))
assert(!matcher.matches("nottest123"))
}
@Test
fun `wildcard string matcher - multiple`() {
val matcher = WildcardStringMatcher("test-*_*-world")
assert(matcher.matches("test-123_pl-world"))
assert(matcher.matches("test-abc_xyz-world"))
assert(matcher.matches("test-hello_world-world"))
assert(!matcher.matches("test-hello-world"))
assert(!matcher.matches("test-hello_world-world!"))
assert(!matcher.matches("TEST-123_PL-WORLD"))
}
@Test
fun `regex string matcher`() {
val matcher = RegexStringMatcher("r=^test[0-9]+$")
assert(matcher.matches("test123"))
assert(matcher.matches("test456"))
assert(!matcher.matches("test"))
assert(!matcher.matches("TEST123"))
assert(!matcher.matches("testing"))
}
}
@@ -1,127 +0,0 @@
package org.mvplugins.multiverse.core.world
import org.bukkit.GameMode
import org.bukkit.Material
import org.bukkit.World.Environment
import org.bukkit.entity.EntityType
import org.bukkit.entity.SpawnCategory
import org.mvplugins.multiverse.core.TestWithMockBukkit
import org.mvplugins.multiverse.core.economy.MVEconomist
import org.mvplugins.multiverse.core.world.location.SpawnLocation
import java.io.File
import java.nio.file.Path
import kotlin.io.path.absolutePathString
import kotlin.test.*
class WorldConfigMangerTest : TestWithMockBukkit() {
private lateinit var worldConfigManager : WorldsConfigManager
@BeforeTest
fun setUp() {
val defaultWorldsData = getResourceAsText("/worlds/default_worlds.yml")
assertNotNull(defaultWorldsData)
val worldsFile = File(Path.of(multiverseCore.dataFolder.absolutePath, "worlds.yml").absolutePathString())
if (worldsFile.exists()) worldsFile.delete()
worldsFile.writeText(defaultWorldsData)
worldConfigManager = serviceLocator.getActiveService(WorldsConfigManager::class.java).takeIf { it != null } ?: run {
throw IllegalStateException("WorldsConfigManager is not available as a service") }
assertTrue(worldConfigManager.load().isSuccess)
assertTrue(worldConfigManager.isLoaded)
}
@Test
fun `Old world config is migrated`() {
val oldConfig = getResourceAsText("/worlds/old_worlds.yml")
assertNotNull(oldConfig)
File(Path.of(multiverseCore.dataFolder.absolutePath, "worlds.yml").absolutePathString()).writeText(oldConfig)
assertTrue(worldConfigManager.load().isSuccess)
assertTrue(worldConfigManager.save().isSuccess)
val endWorldConfig = worldConfigManager.getWorldConfig("world_the_end").orNull
assertNotNull(endWorldConfig)
assertEquals("&aworld the end", endWorldConfig.alias)
assertEquals(Environment.THE_END, endWorldConfig.environment)
assertFalse(endWorldConfig.isEntryFeeEnabled)
assertEquals(MVEconomist.VAULT_ECONOMY_MATERIAL, endWorldConfig.entryFeeCurrency)
assertEquals(0.0, endWorldConfig.entryFeeAmount)
val worldConfig = worldConfigManager.getWorldConfig("world.a.b").orNull
assertNotNull(worldConfig)
assertEquals(-5176596003035866649, worldConfig.seed)
assertEquals(listOf("test"), worldConfig.worldBlacklist)
assertTrue(worldConfig.isEntryFeeEnabled)
assertEquals(Material.DIRT, worldConfig.entryFeeCurrency)
assertEquals(5.0, worldConfig.entryFeeAmount)
val world2Config = worldConfigManager.getWorldConfig("world.a.c").orNull
assertNotNull(world2Config)
assertConfigEquals("/worlds/migrated_worlds.yml", "worlds.yml")
}
@Test
fun `Add a new world to config`() {
val worldConfig = worldConfigManager.addWorldConfig("new.world")
assertNotNull(worldConfig)
assertEquals("new.world", worldConfig.worldName)
assertTrue(worldConfigManager.save().isSuccess)
assertConfigEquals("/worlds/newworld_worlds.yml", "worlds.yml")
// Make sure the world still can be loaded after save
assertTrue(worldConfigManager.load().isSuccess)
assertEquals("new.world", worldConfigManager.getWorldConfig("new.world").orNull?.worldName)
assertConfigEquals("/worlds/newworld_worlds.yml", "worlds.yml")
}
@Test
fun `Updating existing world properties`() {
val worldConfig = worldConfigManager.getWorldConfig("world").orNull
assertNotNull(worldConfig)
assertTrue(worldConfig.stringPropertyHandle.setProperty("adjust-spawn", true).isSuccess)
assertTrue(worldConfig.stringPropertyHandle.setProperty("alias", "newalias").isSuccess)
assertTrue(worldConfig.setSpawnLocation(
SpawnLocation(
-50.0,
50.0,
50.0
)
).isSuccess)
worldConfig.entitySpawnConfig.getSpawnCategoryConfig(SpawnCategory.MISC).isSpawn = false
worldConfig.entitySpawnConfig.getSpawnCategoryConfig(SpawnCategory.ANIMAL).tickRate = 111
worldConfig.entitySpawnConfig.getSpawnCategoryConfig(SpawnCategory.ANIMAL).exceptions = listOf(EntityType.COW, EntityType.PIG)
assertTrue(worldConfigManager.save().isSuccess)
assertConfigEquals("/worlds/updated_worlds.yml", "worlds.yml")
}
@Test
fun `Delete world section from config`() {
worldConfigManager.deleteWorldConfig("world")
assertTrue(worldConfigManager.save().isSuccess)
assertConfigEquals("/worlds/delete_worlds.yml", "worlds.yml")
}
@Test
fun `Edge case parsing tests for world config`() {
val edgecaseConfig = getResourceAsText("/worlds/edgecase_worlds.yml")
assertNotNull(edgecaseConfig)
File(Path.of(multiverseCore.dataFolder.absolutePath, "worlds.yml").absolutePathString()).writeText(edgecaseConfig)
assertTrue(worldConfigManager.load().isSuccess)
assertTrue(worldConfigManager.save().isSuccess)
val worldConfig = assertNotNull(worldConfigManager.getWorldConfig("world").orNull)
assertEquals("1234", worldConfig.alias)
assertTrue(worldConfig.bedRespawn)
assertEquals(GameMode.SURVIVAL, worldConfig.gameMode)
assertEquals(4.0, worldConfig.scale)
assertEquals(listOf("a", "1", "2"), worldConfig.worldBlacklist)
assertEquals(listOf(EntityType.COW), worldConfig.entitySpawnConfig.getSpawnCategoryConfig(SpawnCategory.ANIMAL).exceptions)
}
}
@@ -1,97 +0,0 @@
package org.mvplugins.multiverse.core.world
import org.bukkit.Material
import org.mvplugins.multiverse.core.TestWithMockBukkit
import java.io.File
import java.nio.file.Path
import kotlin.io.path.absolutePathString
import kotlin.test.*
class WorldConfigTest : TestWithMockBukkit() {
private lateinit var worldConfigManager : WorldsConfigManager
private lateinit var worldConfig : WorldConfig
private lateinit var worldNetherConfig : WorldConfig
@BeforeTest
fun setUp() {
val defaultConfig = getResourceAsText("/worlds/default_worlds.yml")
assertNotNull(defaultConfig)
File(Path.of(multiverseCore.dataFolder.absolutePath, "worlds.yml").absolutePathString()).writeText(defaultConfig)
worldConfigManager = serviceLocator.getActiveService(WorldsConfigManager::class.java).takeIf { it != null } ?: run {
throw IllegalStateException("WorldsConfigManager is not available as a service") }
assertTrue(worldConfigManager.load().isSuccess)
worldConfig = worldConfigManager.getWorldConfig("world").orNull.takeIf { it != null } ?: run {
throw IllegalStateException("WorldConfig for world is not available") }
assertNotNull(worldConfig)
worldNetherConfig = worldConfigManager.getWorldConfig("world_nether").orNull.takeIf { it != null } ?: run {
throw IllegalStateException("WorldConfig for world is not available") }
assertNotNull(worldNetherConfig);
}
@Test
fun `Getting existing world property with getProperty returns expected value`() {
assertEquals("my world", worldConfig.stringPropertyHandle.getProperty("alias").get())
assertEquals(false, worldConfig.stringPropertyHandle.getProperty("hidden").get())
assertEquals(1.0, worldConfig.stringPropertyHandle.getProperty("scale").get())
assertEquals(8.0, worldNetherConfig.stringPropertyHandle.getProperty("scale").get())
}
@Test
fun `Getting non-existing world property with getProperty returns null`() {
assertTrue(worldConfig.stringPropertyHandle.getProperty("invalid-property").isFailure)
assertTrue(worldConfig.stringPropertyHandle.getProperty("version").isFailure)
}
@Test
fun `Getting existing world property by getter returns expected value`() {
assertEquals("my world", worldConfig.alias)
assertEquals(false, worldConfig.isHidden)
assertEquals(emptyList(), worldConfig.worldBlacklist)
}
@Test
fun `Updating an existing world property with setProperty reflects the changes in getProperty`() {
assertTrue(worldConfig.stringPropertyHandle.setProperty("adjust-spawn", true).isSuccess)
assertEquals(true, worldConfig.stringPropertyHandle.getProperty("adjust-spawn").get())
assertTrue(worldConfig.stringPropertyHandle.setProperty("alias", "abc").isSuccess)
assertEquals("abc", worldConfig.stringPropertyHandle.getProperty("alias").get())
assertTrue(worldConfig.stringPropertyHandle.setProperty("scale", 2.0).isSuccess)
assertEquals(2.0, worldConfig.stringPropertyHandle.getProperty("scale").get())
val blacklists = listOf("a", "b", "c")
assertTrue(worldConfig.stringPropertyHandle.setProperty("world-blacklist", blacklists).isSuccess)
assertEquals(blacklists, worldConfig.stringPropertyHandle.getProperty("world-blacklist").get())
assertTrue(worldConfig.stringPropertyHandle.setProperty("entryfee-currency", Material.JUNGLE_WOOD).isSuccess)
assertEquals(Material.JUNGLE_WOOD, worldConfig.stringPropertyHandle.getProperty("entryfee-currency").get())
}
@Test
fun `Updating an existing world property with setPropertyString reflects the changes in getProperty`() {
assertTrue(worldConfig.stringPropertyHandle.setPropertyString("adjust-spawn", "true").isSuccess)
assertEquals(true, worldConfig.getStringPropertyHandle().getProperty("adjust-spawn").get())
assertTrue(worldConfig.stringPropertyHandle.setPropertyString("alias", "abc").isSuccess)
assertEquals("abc", worldConfig.stringPropertyHandle.getProperty("alias").get())
assertTrue(worldConfig.stringPropertyHandle.setPropertyString("scale", "2.0").isSuccess)
assertEquals(2.0, worldConfig.stringPropertyHandle.getProperty("scale").get())
assertTrue(worldConfig.stringPropertyHandle.setPropertyString("world-blacklist", "a,b,c").isSuccess)
assertEquals(listOf("a", "b", "c"), worldConfig.stringPropertyHandle.getProperty("world-blacklist").get())
assertTrue(worldConfig.stringPropertyHandle.setPropertyString("entryfee-currency", "jungle_wood").isSuccess)
assertEquals(Material.JUNGLE_WOOD, worldConfig.stringPropertyHandle.getProperty("entryfee-currency").get())
}
@Test
fun `Updating a non-existing property with setProperty returns false`() {
assertTrue(worldConfig.stringPropertyHandle.setProperty("invalid-property", false).isFailure)
assertTrue(worldConfig.stringPropertyHandle.setProperty("version", 1.1).isFailure)
}
}
@@ -1,353 +0,0 @@
package org.mvplugins.multiverse.core.world
import org.bukkit.Bukkit
import org.bukkit.World
import org.bukkit.WorldType
import org.hamcrest.MatcherAssert.assertThat
import org.mockbukkit.mockbukkit.matcher.plugin.PluginManagerFiredEventClassMatcher.hasFiredEventInstance
import org.mockbukkit.mockbukkit.matcher.plugin.PluginManagerFiredEventClassMatcher.hasNotFiredEventInstance
import org.mvplugins.multiverse.core.TestWithMockBukkit
import org.mvplugins.multiverse.core.event.world.MVWorldClonedEvent
import org.mvplugins.multiverse.core.event.world.MVWorldCreatedEvent
import org.mvplugins.multiverse.core.event.world.MVWorldDeleteEvent
import org.mvplugins.multiverse.core.event.world.MVWorldImportedEvent
import org.mvplugins.multiverse.core.event.world.MVWorldLoadedEvent
import org.mvplugins.multiverse.core.event.world.MVWorldPropertyChangedEvent
import org.mvplugins.multiverse.core.event.world.MVWorldRegeneratedEvent
import org.mvplugins.multiverse.core.event.world.MVWorldRemovedEvent
import org.mvplugins.multiverse.core.event.world.MVWorldUnloadedEvent
import org.mvplugins.multiverse.core.world.options.CloneWorldOptions
import org.mvplugins.multiverse.core.world.options.CreateWorldOptions
import org.mvplugins.multiverse.core.world.options.DeleteWorldOptions
import org.mvplugins.multiverse.core.world.options.LoadWorldOptions
import org.mvplugins.multiverse.core.world.options.RegenWorldOptions
import org.mvplugins.multiverse.core.world.options.RemoveWorldOptions
import org.mvplugins.multiverse.core.world.options.UnloadWorldOptions
import org.mvplugins.multiverse.core.world.reasons.CloneFailureReason
import org.mvplugins.multiverse.core.world.reasons.CreateFailureReason
import org.mvplugins.multiverse.core.world.reasons.LoadFailureReason
import java.io.File
import kotlin.test.*
class WorldManagerTest : TestWithMockBukkit() {
private lateinit var worldManager: WorldManager
private lateinit var world: LoadedMultiverseWorld
private lateinit var world2: LoadedMultiverseWorld
@BeforeTest
fun setUp() {
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)
world = worldManager.getLoadedWorld("world").get()
assertNotNull(world)
assertTrue(worldManager.createWorld(CreateWorldOptions.worldName("World2")).isSuccess)
world2 = worldManager.getLoadedWorld("World2").get()
assertNotNull(world2)
}
@Test
fun `Create world with custom options`() {
assertTrue(worldManager.createWorld(
CreateWorldOptions.worldName("Section/my-nether_world")
.environment(World.Environment.NETHER)
.generateStructures(false)
.seed(1234L)
.useSpawnAdjust(false)
.worldType(WorldType.FLAT)
).isSuccess)
val getWorld = worldManager.getLoadedWorld("Section/my-nether_world")
assertTrue(getWorld.isDefined)
val world = getWorld.get()
assertNotNull(world)
assertEquals("Section/my-nether_world", world.name)
assertEquals(World.Environment.NETHER, world.environment)
assertFalse(world.canGenerateStructures().get())
assertEquals(1234L, world.seed)
assertFalse(world.adjustSpawn)
assertEquals(WorldType.FLAT, world.worldType.get())
assertEquals("", world.generator)
assertEquals(8.0, world.scale)
assertThat(server.pluginManager, hasFiredEventInstance(MVWorldLoadedEvent::class.java))
assertThat(server.pluginManager, hasFiredEventInstance(MVWorldCreatedEvent::class.java))
}
@Test
fun `Create world failed - invalid worldname`() {
assertEquals(
CreateFailureReason.INVALID_WORLDNAME,
worldManager.createWorld(CreateWorldOptions.worldName("*!@^&#*(")).failureReason
)
}
@Test
fun `Create world failed - world exists and loaded`() {
assertEquals(
CreateFailureReason.WORLD_EXIST_LOADED,
worldManager.createWorld(CreateWorldOptions.worldName("world")).failureReason
)
}
@Test
fun `Create world failed - world exists and loaded (case insensitive)`() {
assertEquals(
CreateFailureReason.WORLD_EXIST_LOADED,
worldManager.createWorld(CreateWorldOptions.worldName("WoRld2")).failureReason
)
assertEquals(
CreateFailureReason.WORLD_EXIST_LOADED,
worldManager.createWorld(CreateWorldOptions.worldName("WORLD2")).failureReason
)
}
@Test
fun `Create world failed - world exists but unloaded`() {
assertTrue(worldManager.unloadWorld(UnloadWorldOptions.world(world)).isSuccess)
assertEquals(
CreateFailureReason.WORLD_EXIST_UNLOADED,
worldManager.createWorld(CreateWorldOptions.worldName("world")).failureReason
)
}
@Test
fun `Create world failed - world folder exists`() {
File(Bukkit.getWorldContainer(), "worldfolder").mkdir()
assertEquals(
CreateFailureReason.WORLD_EXIST_FOLDER,
worldManager.createWorld(CreateWorldOptions.worldName("worldfolder")).failureReason
)
}
@Test
fun `Remove world`() {
assertTrue(worldManager.removeWorld(RemoveWorldOptions.world(world)).isSuccess)
assertFalse(worldManager.getWorld("world").isDefined)
assertFalse(worldManager.getLoadedWorld("world").isDefined)
assertFalse(worldManager.getUnloadedWorld("world").isDefined)
assertTrue(worldManager.removeWorld(RemoveWorldOptions.world(world2)).isSuccess)
assertFalse(worldManager.getWorld("World2").isDefined)
assertFalse(worldManager.getLoadedWorld("World2").isDefined)
assertFalse(worldManager.getUnloadedWorld("World2").isDefined)
assertThat(server.pluginManager, hasFiredEventInstance(MVWorldUnloadedEvent::class.java))
assertThat(server.pluginManager, hasFiredEventInstance(MVWorldRemovedEvent::class.java))
}
@Test
fun `Delete world`() {
assertTrue(File(Bukkit.getWorldContainer(), "world").isDirectory)
assertTrue(worldManager.deleteWorld(DeleteWorldOptions.world(world)).isSuccess)
assertFalse(worldManager.getWorld("world").isDefined)
assertFalse(worldManager.getLoadedWorld("world").isDefined)
assertFalse(worldManager.getUnloadedWorld("world").isDefined)
assertFalse(File(Bukkit.getWorldContainer(), "world").isDirectory)
assertTrue(File(Bukkit.getWorldContainer(), "World2").isDirectory)
assertTrue(worldManager.deleteWorld(DeleteWorldOptions.world(world2)).isSuccess)
assertFalse(worldManager.getWorld("World2").isDefined)
assertFalse(worldManager.getLoadedWorld("World2").isDefined)
assertFalse(worldManager.getUnloadedWorld("World2").isDefined)
assertFalse(File(Bukkit.getWorldContainer(), "World2").isDirectory)
assertThat(server.pluginManager, hasFiredEventInstance(MVWorldDeleteEvent::class.java))
assertThat(server.pluginManager, hasFiredEventInstance(MVWorldUnloadedEvent::class.java))
assertThat(server.pluginManager, hasFiredEventInstance(MVWorldRemovedEvent::class.java))
}
@Test
fun `Unload and load world`() {
assertTrue(worldManager.unloadWorld(UnloadWorldOptions.world(world2).saveBukkitWorld(true)).isSuccess)
assertFalse(world2.isLoaded)
assertFalse(world2.bukkitWorld.isDefined)
assertFalse(worldManager.getLoadedWorld("World2").isDefined)
assertTrue(worldManager.getWorld("World2").isDefined)
assertTrue(worldManager.getUnloadedWorld("World2").isDefined)
assertTrue(worldManager.loadWorld(LoadWorldOptions.world(world2)).isSuccess)
assertTrue(world2.isLoaded)
assertTrue(worldManager.getLoadedWorld("World2").flatMap{ w -> w.bukkitWorld }.isDefined)
assertTrue(worldManager.getLoadedWorld("World2").isDefined)
assertFalse(worldManager.getUnloadedWorld("World2").isDefined)
}
@Test
fun `Load world failed - invalid world folder`() {
assertTrue(worldManager.unloadWorld(UnloadWorldOptions.world(world2)).isSuccess)
File(Bukkit.getWorldContainer(), "World2/").deleteRecursively()
assertEquals(
LoadFailureReason.WORLD_FOLDER_INVALID,
worldManager.loadWorld(LoadWorldOptions.world(world2)).failureReason
)
}
@Test
fun `Load world failed - non-existent world`() {
assertEquals(
LoadFailureReason.WORLD_NON_EXISTENT,
worldManager.loadWorld("ghost").failureReason
)
}
@Test
fun `Load world failed - world folder exists but not imported`() {
File(Bukkit.getWorldContainer(), "worldfolder").mkdir()
File(Bukkit.getWorldContainer(), "worldfolder/level.dat").createNewFile()
File(Bukkit.getWorldContainer(), "worldfolder/data").mkdir()
File(Bukkit.getWorldContainer(), "worldfolder/region").mkdir()
assertEquals(
LoadFailureReason.WORLD_EXIST_FOLDER,
worldManager.loadWorld("worldfolder").failureReason
)
}
@Test
fun `Regen world`() {
assertTrue(worldManager.regenWorld(
RegenWorldOptions
.world(world2)
.seed(4321L)
).isSuccess)
val getWorld = worldManager.getLoadedWorld("World2")
assertTrue(getWorld.isDefined)
val world = getWorld.get()
assertNotNull(world)
assertEquals(4321L, world.seed)
assertThat(server.pluginManager, hasFiredEventInstance(MVWorldDeleteEvent::class.java))
assertThat(server.pluginManager, hasFiredEventInstance(MVWorldUnloadedEvent::class.java))
assertThat(server.pluginManager, hasFiredEventInstance(MVWorldRemovedEvent::class.java))
assertThat(server.pluginManager, hasFiredEventInstance(MVWorldLoadedEvent::class.java))
assertThat(server.pluginManager, hasFiredEventInstance(MVWorldCreatedEvent::class.java))
assertThat(server.pluginManager, hasFiredEventInstance(MVWorldRegeneratedEvent::class.java))
}
@Test
fun `Clone world`() {
assertTrue(worldManager.cloneWorld(CloneWorldOptions.fromTo(world, "cloneworld").saveBukkitWorld(false)).isSuccess)
val getWorld = worldManager.getLoadedWorld("cloneworld")
assertTrue(getWorld.isDefined)
val world = getWorld.get()
assertNotNull(world)
assertEquals("cloneworld", world.name)
assertThat(server.pluginManager, hasFiredEventInstance(MVWorldLoadedEvent::class.java))
assertThat(server.pluginManager, hasFiredEventInstance(MVWorldImportedEvent::class.java))
assertThat(server.pluginManager, hasFiredEventInstance(MVWorldClonedEvent::class.java))
}
@Test
fun `Clone world failed - invalid world name`() {
assertEquals(
CloneFailureReason.INVALID_WORLDNAME,
worldManager.cloneWorld(CloneWorldOptions.fromTo(world, "HU(@*!#")).failureReason
)
}
@Test
fun `Clone world failed - target world exists and loaded`() {
assertEquals(
CloneFailureReason.WORLD_EXIST_LOADED,
worldManager.cloneWorld(CloneWorldOptions.fromTo(world, "World2")).failureReason
)
}
@Test
fun `Clone world failed - target world exists but unloaded`() {
assertTrue(worldManager.unloadWorld(UnloadWorldOptions.world(world2)).isSuccess)
assertEquals(
CloneFailureReason.WORLD_EXIST_UNLOADED,
worldManager.cloneWorld(CloneWorldOptions.fromTo(world, "World2")).failureReason
)
}
@Test
fun `Clone world failed - target world folder exists`() {
File(Bukkit.getWorldContainer(), "worldfolder").mkdir()
assertEquals(
CloneFailureReason.WORLD_EXIST_FOLDER,
worldManager.cloneWorld(CloneWorldOptions.fromTo(world, "worldfolder")).failureReason
)
}
@Test
fun `Get potential worlds`() {
File(Bukkit.getWorldContainer(), "newworld1").mkdir()
File(Bukkit.getWorldContainer(), "newworld1/level.dat").createNewFile()
File(Bukkit.getWorldContainer(), "newworld1/data").mkdir()
File(Bukkit.getWorldContainer(), "newworld2").mkdir()
File(Bukkit.getWorldContainer(), "newworld2/level.dat").createNewFile()
File(Bukkit.getWorldContainer(), "newworld2/data").mkdir()
assertEquals(setOf("newworld1", "newworld2"), worldManager.getPotentialWorlds().toSet())
}
@Test
fun `Get world with alias`() {
world.setAlias("testalias")
assertEquals(world, worldManager.getLoadedWorldByNameOrAlias("testalias").orNull)
assertEquals(world, worldManager.getWorldByNameOrAlias("testalias").orNull)
assertNull(worldManager.getUnloadedWorldByNameOrAlias("testalias").orNull)
}
@Test
fun `Get world`() {
assertEquals(world, worldManager.getLoadedWorld("world").orNull)
assertNull(worldManager.getUnloadedWorld("world").orNull)
assertEquals(world, worldManager.getWorld("world").orNull)
assertTrue(worldManager.isLoadedWorld("world"))
assertFalse(worldManager.isUnloadedWorld("world"))
assertTrue(worldManager.isWorld("world"))
val unloadedWorld = worldManager.unloadWorld(UnloadWorldOptions.world(world)).get()
assertNull(worldManager.getLoadedWorld("world").orNull)
assertEquals(unloadedWorld, worldManager.getUnloadedWorld("world").orNull)
assertEquals(unloadedWorld, worldManager.getWorld("world").orNull)
assertFalse(worldManager.isLoadedWorld("world"))
assertTrue(worldManager.isUnloadedWorld("world"))
assertTrue(worldManager.isWorld("world"))
}
@Test
fun `Get world is case insensitive`() {
assertEquals(world2, worldManager.getLoadedWorld("WORLD2").orNull)
assertNull(worldManager.getUnloadedWorld("wOrld2").orNull)
assertEquals(world2, worldManager.getWorld("wOrld2").orNull)
assertTrue(worldManager.isLoadedWorld("WoRlD2"))
assertFalse(worldManager.isUnloadedWorld("WoRlD2"))
assertTrue(worldManager.isWorld("wORLD2"))
val unloadedWorld2 = worldManager.unloadWorld(UnloadWorldOptions.world(world2)).get()
assertNull(worldManager.getLoadedWorld("wOrld2").orNull)
assertEquals(unloadedWorld2, worldManager.getUnloadedWorld("wOrld2").orNull)
assertEquals(unloadedWorld2, worldManager.getWorld("wORLD2").orNull)
assertFalse(worldManager.isLoadedWorld("WoRlD2"))
assertTrue(worldManager.isUnloadedWorld("WoRlD2"))
assertTrue(worldManager.isWorld("wORLD2"))
}
@Test
fun `Set different value - MVWorldPropertyChangedEvent event is fired`() {
server.pluginManager.clearEvents()
world.scale = 8.0
assertThat(server.pluginManager, hasFiredEventInstance(MVWorldPropertyChangedEvent::class.java))
}
@Test
fun `Set equal value - MVWorldPropertyChangedEvent event is not fired`() {
server.pluginManager.clearEvents()
world.scale = 1.0
assertThat(server.pluginManager, hasNotFiredEventInstance(MVWorldPropertyChangedEvent::class.java))
}
}
@@ -1,72 +0,0 @@
package org.mvplugins.multiverse.core.world
import org.bukkit.Bukkit
import org.mvplugins.multiverse.core.TestWithMockBukkit
import org.mvplugins.multiverse.core.world.helpers.WorldNameChecker
import java.io.File
import kotlin.test.BeforeTest
import kotlin.test.Test
import kotlin.test.*
class WorldNameCheckerTest : TestWithMockBukkit() {
lateinit var worldNameChecker: WorldNameChecker
@BeforeTest
fun setUp() {
worldNameChecker = serviceLocator.getActiveService(WorldNameChecker::class.java).takeIf { it != null } ?: run {
throw IllegalStateException("WorldNameChecker is not available as a service") }
}
@Test
fun `Valid world name`() {
assertEquals(WorldNameChecker.NameStatus.VALID, worldNameChecker.checkName("test"))
}
@Test
fun `Invalid characters in world name`() {
assertEquals(WorldNameChecker.NameStatus.INVALID_CHARS, worldNameChecker.checkName("test!"))
}
@Test
fun `Empty world name`() {
assertEquals(WorldNameChecker.NameStatus.EMPTY, worldNameChecker.checkName(null))
assertEquals(WorldNameChecker.NameStatus.EMPTY, worldNameChecker.checkName(""))
}
@Test
fun `Blacklisted world name`() {
assertEquals(WorldNameChecker.NameStatus.BLACKLISTED, worldNameChecker.checkName("logs"))
assertEquals(WorldNameChecker.NameStatus.BLACKLISTED, worldNameChecker.checkName("plugins"))
assertEquals(WorldNameChecker.NameStatus.BLACKLISTED, worldNameChecker.checkName("CACHE"))
}
@Test
fun `Valid world folder`() {
File(Bukkit.getWorldContainer(), "test").mkdir()
File(Bukkit.getWorldContainer(), "test/level.dat").createNewFile()
File(Bukkit.getWorldContainer(), "test/data").mkdir()
assertEquals(WorldNameChecker.FolderStatus.VALID, worldNameChecker.checkFolder("test"))
}
@Test
fun `Valid world folder v26-1 format`() {
File(Bukkit.getWorldContainer(), "test").mkdir()
File(Bukkit.getWorldContainer(), "test/region").mkdir()
File(Bukkit.getWorldContainer(), "test/data").mkdir()
assertEquals(WorldNameChecker.FolderStatus.VALID, worldNameChecker.checkFolder("test"))
}
@Test
fun `Not a valid world folder`() {
File(Bukkit.getWorldContainer(), "test").mkdir()
File(Bukkit.getWorldContainer(), "test/random.txt").createNewFile()
assertEquals(WorldNameChecker.FolderStatus.NOT_A_WORLD, worldNameChecker.checkFolder("test"))
}
@Test
fun `World folder does not exist`() {
File(Bukkit.getWorldContainer(), "test").mkdir()
assertEquals(WorldNameChecker.FolderStatus.DOES_NOT_EXIST, worldNameChecker.checkFolder("test2"))
}
}
@@ -1,36 +0,0 @@
package org.mvplugins.multiverse.core.world.helper
import org.mvplugins.multiverse.core.TestWithMockBukkit
import org.mvplugins.multiverse.core.world.helpers.DimensionFinder.DimensionFormat
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
import kotlin.test.assertFalse
class DimensionFormatTest : TestWithMockBukkit() {
@Test
fun `Test invalid DimensionFormat`() {
assertFailsWith(IllegalArgumentException::class) {
DimensionFormat("%idk%_nether")
}
}
@Test
fun `Test DimensionFormat replace`() {
val dimensionFormat = DimensionFormat("%overworld%_nether")
assertEquals("cool_nether", dimensionFormat.replaceOverworld("cool"))
}
@Test
fun `Test DimensionFormat get overworld`() {
val dimensionFormat = DimensionFormat("%overworld%_n")
assertEquals("cool", dimensionFormat.getOverworldFromName("cool_n").get())
}
@Test
fun `Test DimensionFormat does not match`() {
val dimensionFormat = DimensionFormat("%overworld%_nether")
assertFalse(dimensionFormat.getOverworldFromName("cool_end").isDefined)
}
}
@@ -1,4 +0,0 @@
anchors:
a1: w1:0.50,70.00,0.50:-68.25:10.35
a2: w2:3.3,9.9,6.6:-1.1:2
a3: w3:3.00,3.00,3.00:-3.00:3.00
@@ -1,3 +0,0 @@
anchors:
a1: w1:0.50,70.00,0.50:-68.25:10.35
a2: w2:3.3,9.9,6.6:-1.1:2
@@ -1,38 +0,0 @@
world:
enforce-access: false
enforce-gamemode: true
auto-purge-entities: false
teleport:
use-finer-teleport-permissions: true
concurrent-teleport-limit: 50
teleport-intercept: true
spawn:
first-spawn-override: true
first-spawn-location: ''
enable-join-destination: false
join-destination: ''
portal:
use-custom-portal-search: false
custom-portal-search-radius: 128
messaging:
enable-chat-prefix: false
chat-prefix-format: '[%world%]%chat%'
register-papi-hook: true
default-locale: en
per-player-locale: true
?!
command:
resolve-alias-name: true
confirm-mode: enable
use-confirm-otp: true
misc:
global-debug: 0
silent-start: false
show-donation-message: true
version: 5.1
@@ -1,69 +0,0 @@
world:
auto-import-default-worlds: true
auto-import-3rd-party-worlds: true
enforce-access: false
enforce-gamemode: true
enforce-flight: true
gamemode-and-flight-enforce-delay: 1
apply-entity-spawn-rate: true
apply-entity-spawn-limit: true
auto-purge-entities: false
world-name-format:
nether: '%overworld%_nether'
end: '%overworld%_the_end'
teleport:
use-finer-teleport-permissions: true
passenger-mode: default
concurrent-teleport-limit: 50
teleport-intercept: true
safe-location-horizontal-search-radius: 3
safe-location-vertical-search-radius: 3
spawn:
first-spawn-override: false
first-spawn-location: ''
enable-join-destination: false
join-destination: ''
default-respawn-in-overworld: true
default-respawn-within-same-world: true
enforce-respawn-at-world-spawn: true
portal:
use-custom-portal-search: false
custom-portal-search-radius: 128
messaging:
enable-chat-prefix: false
chat-prefix-format: '[%world%]%chat%'
register-papi-hook: true
warn-invalid-papi-format: true
invalid-papi-format-returns-blank: false
default-locale: en
per-player-locale: true
command:
resolve-alias-name: true
simplified-destination-tab-completion: false
confirm-mode: enable
use-confirm-otp: true
confirm-timeout: 30
show-legacy-aliases: false
event-priority:
player-portal: high
player-respawn: low
player-spawn-location: normal
player-teleport: highest
player-changed-world: normal
misc:
bukkit-yml-path: bukkit.yml
server-properties-path: server.properties
auto-detect-generator-plugins: true
global-debug: 0
debug-permissions: false
silent-start: false
show-donation-message: true
version: 5.2
-20
View File
@@ -1,20 +0,0 @@
multiverse-configuration:
==: com.onarandombox.MultiverseCore.MultiverseCoreConfiguration
enforceaccess: 'true'
prefixchat: 'false'
prefixchatformat: '[%world%]>>%chat%'
useasyncchat: 'true'
teleportintercept: 'false'
firstspawnoverride: 'true'
displaypermerrors: 'false'
enablebuscript: 'false'
globaldebug: '2'
silentstart: 'false'
messagecooldown: '5000'
version: '2.9'
firstspawnworld: world
teleportcooldown: '1000'
defaultportalsearch: 'true'
portalsearchradius: '128'
autopurge: 'true'
idonotwanttodonate: 'true'
@@ -1,156 +0,0 @@
world:
adjust-spawn: false
alias: my world
allow-advancement-grant: true
allow-flight: false
allow-weather: true
anchor-respawn: true
auto-heal: true
auto-load: true
bed-respawn: true
difficulty: normal
entry-fee:
enabled: false
amount: 0.0
currency: '@vault-economy'
environment: normal
gamemode: survival
biome: ''
generator: ''
hidden: false
hunger: true
keep-spawn-in-memory: true
player-limit: -1
portal-form: all
pvp: true
respawn-world: ''
scale: 1.0
seed: -9223372036854775808
spawn-location:
==: MVSpawnLocation
x: -64.0
y: 64.0
z: 48.0
pitch: 0.0
yaw: 0.0
spawning:
monster:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
animal:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
water_animal:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
water_ambient:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
water_underground_creature:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
ambient:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
axolotl:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
misc:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
world-blacklist: []
version: 1.2
world_nether:
adjust-spawn: false
alias: ''
allow-advancement-grant: true
allow-flight: false
allow-weather: true
anchor-respawn: true
auto-heal: true
auto-load: true
bed-respawn: true
difficulty: normal
entry-fee:
enabled: false
amount: 0.0
currency: '@vault-economy'
environment: nether
gamemode: survival
biome: ''
generator: ''
hidden: false
hunger: true
keep-spawn-in-memory: true
player-limit: -1
portal-form: all
pvp: true
respawn-world: ''
scale: 8.0
seed: -9223372036854775808
spawn-location:
==: MVSpawnLocation
x: -64.0
y: 64.0
z: 48.0
pitch: 0.0
yaw: 0.0
spawning:
monster:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
animal:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
water_animal:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
water_ambient:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
water_underground_creature:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
ambient:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
axolotl:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
misc:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
world-blacklist: []
version: 1.2
@@ -1,78 +0,0 @@
world_nether:
adjust-spawn: false
alias: ''
allow-advancement-grant: true
allow-flight: false
allow-weather: true
anchor-respawn: true
auto-heal: true
auto-load: true
bed-respawn: true
difficulty: normal
entry-fee:
enabled: false
amount: 0.0
currency: '@vault-economy'
environment: nether
gamemode: survival
biome: ''
generator: ''
hidden: false
hunger: true
keep-spawn-in-memory: true
player-limit: -1
portal-form: all
pvp: true
respawn-world: ''
scale: 8.0
seed: -9223372036854775808
spawn-location:
==: MVSpawnLocation
x: -64.0
y: 64.0
z: 48.0
pitch: 0.0
yaw: 0.0
spawning:
monster:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
animal:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
water_animal:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
water_ambient:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
water_underground_creature:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
ambient:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
axolotl:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
misc:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
world-blacklist: []
version: 1.2
@@ -1,158 +0,0 @@
world:
adjust-spawn: false
alias: 1234 # should be parsed as string and not integer
allow-advancement-grant: true
allow-flight: false
allow-weather: true
anchor-respawn: true
auto-heal: true
auto-load: true
bed-respawn: allow # should parse to true as boolean
difficulty: normal
entry-fee:
enabled: false
amount: 0.0
currency: dirt # should be parsed to @minecraft:dirt
environment: normal
gamemode: sUrvivAl # random casing shouldn't affect parsing
biome: ''
generator: ''
hidden: false
hunger: true
keep-spawn-in-memory: true
player-limit: -1
portal-form: all
pvp: true
respawn-world: ''
scale: '4' # string number should be parsed as double
seed: -9223372036854775808
spawn-location:
==: MVSpawnLocation
x: -64.0
y: 64.0
z: 48.0
pitch: 0.0
yaw: 0.0
spawning:
monster:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
animal:
spawn: falSe
tick-rate: 123
spawn-limit: '@unset'
exceptions: [ cow ]
water_animal:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
water_ambient:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
water_underground_creature:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
ambient:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
axolotl:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
misc:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
world-blacklist: # should be parsed as list of string
- a
- 1
- 2
version: 1.2
world_nether:
adjust-spawn: false
alias: ''
allow-advancement-grant: true
allow-flight: false
allow-weather: true
auto-heal: true
auto-load: true
bed-respawn: true
difficulty: normal
entry-fee:
enabled: false
amount: 0.0
currency: '@vault-economy'
environment: nether
gamemode: survival
biome: ''
generator: ''
hidden: false
hunger: true
keep-spawn-in-memory: true
player-limit: -1
portal-form: all
pvp: true
respawn-world: ''
scale: 8.0
seed: -9223372036854775808
spawn-location:
==: MVSpawnLocation
x: -64.0
y: 64.0
z: 48.0
pitch: 0.0
yaw: 0.0
spawning:
monster:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
animal:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
water_animal:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
water_ambient:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
water_underground_creature:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
ambient:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
axolotl:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
misc:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
world-blacklist: []
version: 1.2
@@ -1,236 +0,0 @@
world_the_end:
adjust-spawn: false
alias: '&aworld the end'
allow-advancement-grant: true
allow-flight: true
allow-weather: true
anchor-respawn: true
auto-heal: true
auto-load: true
bed-respawn: true
difficulty: normal
entry-fee:
enabled: false
amount: 0.0
currency: '@vault-economy'
environment: the_end
gamemode: survival
biome: ''
generator: ''
hidden: false
hunger: true
keep-spawn-in-memory: true
player-limit: -1
portal-form: all
pvp: true
respawn-world: ''
scale: 16.0
seed: -5176596003035866649
spawn-location:
==: MVSpawnLocation
x: 0.0
y: 65.0
z: 0.0
pitch: 0.0
yaw: 0.0
spawning:
monster:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@bukkit'
animal:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@bukkit'
water_animal:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
water_ambient:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
water_underground_creature:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
ambient:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
axolotl:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
misc:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
world-blacklist: []
version: 1.2
world[dot]a[dot]b:
adjust-spawn: true
alias: ''
allow-advancement-grant: true
allow-flight: false
allow-weather: true
anchor-respawn: true
auto-heal: true
auto-load: true
bed-respawn: true
difficulty: normal
entry-fee:
enabled: true
amount: 5.0
currency: DIRT
environment: normal
gamemode: survival
biome: ''
generator: ''
hidden: false
hunger: true
keep-spawn-in-memory: true
player-limit: -1
portal-form: all
pvp: true
respawn-world: ''
scale: 1.0
seed: -5176596003035866649
spawn-location:
==: MVSpawnLocation
x: -64.0
y: 64.0
z: 48.0
pitch: 0.0
yaw: 0.0
spawning:
monster:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@bukkit'
animal:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@bukkit'
water_animal:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
water_ambient:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
water_underground_creature:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
ambient:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
axolotl:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
misc:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
world-blacklist:
- test
version: 1.2
world[dot]a[dot]c:
adjust-spawn: true
alias: ''
allow-advancement-grant: true
allow-flight: false
allow-weather: true
anchor-respawn: true
auto-heal: true
auto-load: true
bed-respawn: true
difficulty: normal
entry-fee:
enabled: true
amount: 5.0
currency: DIRT
environment: normal
gamemode: survival
biome: ''
generator: ''
hidden: false
hunger: true
keep-spawn-in-memory: true
player-limit: -1
portal-form: all
pvp: true
respawn-world: ''
scale: 1.0
seed: -5176596003035866649
spawn-location:
==: MVSpawnLocation
x: -64.0
y: 64.0
z: 48.0
pitch: 0.0
yaw: 0.0
spawning:
monster:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@bukkit'
animal:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@bukkit'
water_animal:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
water_ambient:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
water_underground_creature:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
ambient:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
axolotl:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
misc:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
world-blacklist:
- test
version: 1.2
@@ -1,229 +0,0 @@
world:
adjust-spawn: false
alias: my world
allow-advancement-grant: true
allow-flight: false
allow-weather: true
anchor-respawn: true
auto-heal: true
auto-load: true
bed-respawn: true
difficulty: normal
entry-fee:
enabled: false
amount: 0.0
currency: '@vault-economy'
environment: normal
gamemode: survival
biome: ''
generator: ''
hidden: false
hunger: true
keep-spawn-in-memory: true
player-limit: -1
portal-form: all
pvp: true
respawn-world: ''
scale: 1.0
seed: -9223372036854775808
spawn-location:
==: MVSpawnLocation
x: -64.0
y: 64.0
z: 48.0
pitch: 0.0
yaw: 0.0
spawning:
monster:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
animal:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
water_animal:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
water_ambient:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
water_underground_creature:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
ambient:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
axolotl:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
misc:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
world-blacklist: []
version: 1.2
world_nether:
adjust-spawn: false
alias: ''
allow-advancement-grant: true
allow-flight: false
allow-weather: true
anchor-respawn: true
auto-heal: true
auto-load: true
bed-respawn: true
difficulty: normal
entry-fee:
enabled: false
amount: 0.0
currency: '@vault-economy'
environment: nether
gamemode: survival
biome: ''
generator: ''
hidden: false
hunger: true
keep-spawn-in-memory: true
player-limit: -1
portal-form: all
pvp: true
respawn-world: ''
scale: 8.0
seed: -9223372036854775808
spawn-location:
==: MVSpawnLocation
x: -64.0
y: 64.0
z: 48.0
pitch: 0.0
yaw: 0.0
spawning:
monster:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
animal:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
water_animal:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
water_ambient:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
water_underground_creature:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
ambient:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
axolotl:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
misc:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
world-blacklist: []
version: 1.2
new[dot]world:
adjust-spawn: false
alias: ''
allow-advancement-grant: true
allow-flight: false
allow-weather: true
anchor-respawn: true
auto-heal: true
auto-load: true
bed-respawn: true
biome: '@error'
difficulty: normal
entry-fee:
enabled: false
amount: 0.0
currency: '@vault-economy'
environment: normal
gamemode: survival
generator: '@error'
hidden: false
hunger: true
keep-spawn-in-memory: true
player-limit: -1
portal-form: all
pvp: true
respawn-world: ''
scale: 1.0
seed: -9223372036854775808
spawn-location:
==: MVNullLocation (It's a bug if you see this in your config file)
spawning:
monster:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
animal:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
water_animal:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
water_ambient:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
water_underground_creature:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
ambient:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
axolotl:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
misc:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
world-blacklist: []
version: 1.2
-146
View File
@@ -1,146 +0,0 @@
worlds:
world_the_end:
==: MVWorld
hidden: 'false'
alias: world the end
color: GREEN
style: NORMAL
pvp: 'true'
scale: '16.0'
respawnWorld: ''
allowWeather: 'true'
difficulty: NORMAL
spawning:
==: MVSpawnSettings
animals:
==: MVSpawnSubSettings
spawn: 'true'
spawnrate: '-1'
exceptions: []
monsters:
==: MVSpawnSubSettings
spawn: 'true'
spawnrate: '-1'
exceptions: []
entryfee:
==: MVEntryFee
amount: '0.0'
hunger: 'true'
autoHeal: 'true'
adjustSpawn: 'false'
portalForm: ALL
gameMode: SURVIVAL
keepSpawnInMemory: 'true'
spawnLocation:
==: MVSpawnLocation
x: 0.0
y: 65.0
z: 0.0
pitch: 0.0
yaw: 0.0
autoLoad: 'true'
bedRespawn: 'true'
worldBlacklist: []
environment: THE_END
seed: '-5176596003035866649'
generator: 'null'
playerLimit: '-1'
allowFlight: 'true'
world.a.b:
==: MVWorld
hidden: 'false'
alias: ''
color: WHITE
style: NORMAL
pvp: 'true'
scale: '1.0'
respawnWorld: ''
allowWeather: 'true'
difficulty: NORMAL
spawning:
==: MVSpawnSettings
animals:
==: MVSpawnSubSettings
spawn: 'true'
spawnrate: '-1'
exceptions: []
monsters:
==: MVSpawnSubSettings
spawn: 'true'
spawnrate: '-1'
exceptions: []
entryfee:
==: MVEntryFee
amount: '5.0'
currency: 'DIRT'
hunger: 'true'
autoHeal: 'true'
adjustSpawn: 'true'
portalForm: ALL
gameMode: SURVIVAL
keepSpawnInMemory: 'true'
spawnLocation:
==: MVSpawnLocation
x: -64.0
y: 64.0
z: 48.0
pitch: 0.0
yaw: 0.0
autoLoad: 'true'
bedRespawn: 'true'
worldBlacklist:
- test
environment: NORMAL
seed: '-5176596003035866649'
generator: 'null'
playerLimit: '-1'
allowFlight: 'false'
world.a.c:
==: MVWorld
hidden: 'false'
alias: ''
color: WHITE
style: NORMAL
pvp: 'true'
scale: '1.0'
respawnWorld: ''
allowWeather: 'true'
difficulty: NORMAL
spawning:
==: MVSpawnSettings
animals:
==: MVSpawnSubSettings
spawn: 'true'
spawnrate: '-1'
exceptions: []
monsters:
==: MVSpawnSubSettings
spawn: 'true'
spawnrate: '-1'
exceptions: []
entryfee:
==: MVEntryFee
amount: '5.0'
currency: 'DIRT'
hunger: 'true'
autoHeal: 'true'
adjustSpawn: 'true'
portalForm: ALL
gameMode: SURVIVAL
keepSpawnInMemory: 'true'
spawnLocation:
==: MVSpawnLocation
x: -64.0
y: 64.0
z: 48.0
pitch: 0.0
yaw: 0.0
autoLoad: 'true'
bedRespawn: 'true'
worldBlacklist:
- test
environment: NORMAL
seed: '-5176596003035866649'
generator: 'null'
playerLimit: '-1'
allowFlight: 'false'
@@ -1,151 +0,0 @@
world:
adjust-spawn: true
alias: newalias
allow-advancement-grant: true
allow-flight: false
allow-weather: true
anchor-respawn: true
auto-heal: true
auto-load: true
bed-respawn: true
difficulty: NORMAL
entry-fee:
enabled: false
amount: 0.0
currency: @vault-economy
environment: NORMAL
gamemode: SURVIVAL
biome: ''
generator: ''
hidden: false
hunger: true
keep-spawn-in-memory: true
player-limit: -1
portal-form: ALL
pvp: true
respawn-world: ''
scale: 1.0
seed: -9223372036854775808
spawn-location:
==: MVSpawnLocation
x: -64.0
y: 64.0
z: 48.0
pitch: 0.0
yaw: 0.0
spawning:
monster:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
animal:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
water_animal:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
water_ambient:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
water_underground_creature:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
ambient:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
axolotl:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
misc:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
world-blacklist: []
version: 1.2
world_nether:
adjust-spawn: false
alias: ''
allow-advancement-grant: true
allow-flight: false
allow-weather: true
anchor-respawn: true
auto-heal: true
auto-load: true
bed-respawn: true
difficulty: NORMAL
entry-fee:
enabled: false
amount: 0.0
currency: @vault-economy
environment: NETHER
gamemode: SURVIVAL
biome: ''
generator: ''
hidden: false
hunger: true
keep-spawn-in-memory: true
player-limit: -1
portal-form: ALL
pvp: true
respawn-world: ''
scale: 1.0
seed: -9223372036854775808
spawn-location:
==: MVNullLocation (It's a bug if you see this in your config file)
spawning:
monster:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
animal:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
water_animal:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
water_ambient:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
water_underground_creature:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
ambient:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
axolotl:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
misc:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
world-blacklist: []
version: 1.2
@@ -1,158 +0,0 @@
world:
adjust-spawn: true
alias: newalias
allow-advancement-grant: true
allow-flight: false
allow-weather: true
anchor-respawn: true
auto-heal: true
auto-load: true
bed-respawn: true
difficulty: normal
entry-fee:
enabled: false
amount: 0.0
currency: '@vault-economy'
environment: normal
gamemode: survival
biome: ''
generator: ''
hidden: false
hunger: true
keep-spawn-in-memory: true
player-limit: -1
portal-form: all
pvp: true
respawn-world: ''
scale: 1.0
seed: -9223372036854775808
spawn-location:
==: MVSpawnLocation
x: -50.0
y: 50.0
z: 50.0
pitch: 0.0
yaw: 0.0
spawning:
monster:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
animal:
spawn: true
tick-rate: 111
spawn-limit: '@unset'
exceptions:
- cow
- pig
water_animal:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
water_ambient:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
water_underground_creature:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
ambient:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
axolotl:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
misc:
spawn: false
tick-rate: '@unset'
spawn-limit: '@unset'
exceptions: [ ]
world-blacklist: []
version: 1.2
world_nether:
adjust-spawn: false
alias: ''
allow-advancement-grant: true
allow-flight: false
allow-weather: true
anchor-respawn: true
auto-heal: true
auto-load: true
bed-respawn: true
difficulty: normal
entry-fee:
enabled: false
amount: 0.0
currency: '@vault-economy'
environment: nether
gamemode: survival
biome: ''
generator: ''
hidden: false
hunger: true
keep-spawn-in-memory: true
player-limit: -1
portal-form: all
pvp: true
respawn-world: ''
scale: 8.0
seed: -9223372036854775808
spawn-location:
==: MVSpawnLocation
x: -64.0
y: 64.0
z: 48.0
pitch: 0.0
yaw: 0.0
spawning:
monster:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
animal:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
water_animal:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
water_ambient:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
water_underground_creature:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
ambient:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
axolotl:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
misc:
exceptions: [ ]
spawn: true
spawn-limit: '@unset'
tick-rate: '@unset'
world-blacklist: []
version: 1.2