Compare commits

..
71 Commits
Author SHA1 Message Date
Ben Woo 34185b90a9 Merge pull request #3524 from Multiverse/chore/ignore-kolin
Add Kolin build cache folder to gitignore
2026-10-19 13:17:52 +08:00
Ben Woo 5d57bbfda9 Add Kolin build cache folder to gitignore 2026-10-19 11:14:11 +08:00
Ben Woo a78459ab77 Merge pull request #3523 from Multiverse/fix/update-legacy-name
Update legacy world name on world load
2026-10-19 11:06:04 +08:00
Ben Woo 0ad795b27f Update legacy world name on world load
This should elevate any issues of legacy world name being desynced with actual world name.
2026-10-19 10:57:18 +08:00
Ben Woo 2765cf6f3e Merge pull request #3522 from Multiverse/fix/string-parse
Fix string parse for special `@` values
2026-10-19 10:47:51 +08:00
Ben Woo 7a9b21f9a0 Fix command input not allowing @vault-economy currency type 2026-10-19 10:43:25 +08:00
Ben Woo f55ceda42c Fix command input not allowing @unset, @bukkit spawn values 2026-10-19 10:42:42 +08:00
Ben Woo 893c69a6e1 Merge pull request #3521 from Multiverse/chore/async-fail
Clearer message that fail async teleport is not a Multiverse bug
2026-10-09 14:09:39 +08:00
Ben Woo 6bb93766d4 Clearer message that fail async teleport is not a Multiverse bug 2026-10-09 14:03:06 +08:00
Ben Woo fe49efe276 Merge pull request #3520 from Multiverse/chore/simplify-pr-comment
Simplify artifact PR comment message
2026-09-27 23:26:18 +08:00
Ben Woo 8de11b59a1 Simplify artifact PR comment message 2026-09-27 23:22:25 +08:00
Ben Woo 744450cbbb Merge pull request #3517 from Multiverse/chore/artifact-comment
Improve artifact PR comment with better message and supports forks
2026-09-27 16:23:48 +08:00
Ben Woo ecf107563d Improve artifact PR comment with better message and supports forks 2026-09-27 16:20:31 +08:00
Ben Woo f7ea31d949 Merge pull request #3516 from Multiverse/chore/mc-26.3
Mark builds at compatible with 26.3
2026-09-21 15:52:44 +08:00
Ben Woo 1bf4b99ecf Mark builds at compatible with 26.3 2026-09-21 15:41:15 +08:00
Ben Woo 4de4e5a68e Merge pull request #3515 from Multiverse/fix/level-name-change
Fix level-name changes causing mismatched name and key identities
2026-08-25 15:15:49 +08:00
Ben Woo f6a8a6b3b4 Better error handling for invalid/mismatch world keys 2026-08-25 15:03:10 +08:00
Ben Woo d485238c56 Fix level-name changes causing mismatched name and key identities 2026-08-25 14:15:23 +08:00
Ben Woo 5234445746 Merge pull request #3514 from Multiverse/fix/spawn-reason-chunk-load
Fix spawn reason check not done for chunk load entity pruning
2026-08-24 13:08:54 +08:00
Ben Woo 0b626d9eaf Fix spawn reason check not done for chunk load entity pruning 2026-08-21 22:48:27 +08:00
Ben Woo 2ff321e475 Merge pull request #3512 from Multiverse/fix/command-syntax
Fix various missing parameters and typos in command syntax
2026-08-15 23:49:29 +08:00
Ben Woo 58dbe55be4 Merge pull request #3513 from Multiverse/fix/apply-spawn-rate
Fix disabling spawn limit also disabled tick rate
2026-08-15 21:06:07 +08:00
Ben Woo a2045b52bd Fix disabling spawn limit also disabled tick rate 2026-08-15 20:03:09 +08:00
Ben Woo 396ece4d27 Fix various missing parameters and typos in command syntax 2026-08-09 23:32:06 +08:00
Ben Woo e25ac0e531 Merge pull request #3500 from Multiverse/v5.8
V5.8
2026-08-09 10:51:40 +08:00
Ben Woo 7cf53ee7a8 Checking for alias name conflict should be case-insensitive 2026-08-09 00:22:45 +08:00
Ben Woo 4a46c13754 Add missing deprecated reason javadoc 2026-08-09 00:09:00 +08:00
Ben Woo 5449ade6f3 Merge pull request #3511 from Multiverse/fix/locale-keys
Fix various incorrectly named locale keys
2026-08-08 23:47:10 +08:00
Ben Woo a9f8deeb01 Merge pull request #3510 from Multiverse/fix/underscore-worlds
Fix unable to select worlds with underscore in placeholders
2026-08-08 23:46:47 +08:00
Ben Woo 1ce7eb6d3e Fix various incorrectly named locale keys 2026-08-08 23:43:45 +08:00
Ben Woo d11d664020 Fix unable to select worlds with underscore in placeholders 2026-08-08 23:34:11 +08:00
Ben Woo abd86f81e5 Merge pull request #3509 from Multiverse/chore/for-removal
Add missing ScheduledForRemoval annotations
2026-08-08 22:44:00 +08:00
Ben Woo 0faec79250 Add missing ScheduledForRemoval annotations 2026-08-08 22:37:27 +08:00
Ben Woo 9264f5e2ce Merge branch 'main' into v5.8
# Conflicts:
#	src/main/java/org/mvplugins/multiverse/core/world/WorldConfigNodes.java
2026-07-23 22:14:29 +08:00
Ben Woo 892ce24845 Merge pull request #3507 from Multiverse/chore/glare-down
Switch to use jitpack for config dep as glare is down
2026-07-23 21:41:50 +08:00
Ben Woo 091eec96b9 Switch to use jitpack for config dep as glare is down 2026-07-23 21:37:28 +08:00
Ben Woo 37ecceb179 Merge pull request #3506 from Multiverse/fix/load-callback
Improve error handling for when loaded API callback
2026-07-21 13:49:15 +08:00
Ben Woo 7697065519 Improve error handling for when loaded API callback 2026-07-21 12:41:26 +08:00
Ben Woo ad7b9638f1 Merge pull request #3505 from Multiverse/feat/nested-potential-worlds
Add support for searching potential world in nested sub-folders
2026-07-20 20:02:49 +08:00
Ben Woo 37ba5ecc04 Add support for searching potential world in nested sub-folders 2026-07-20 19:59:07 +08:00
Ben Woo 2445464ab8 Merge pull request #3504 from Multiverse/fix/exact-dest-alias
Fix exact destination not working with alias names
2026-07-20 14:37:11 +08:00
Ben Woo 7fad9aa2e4 Fix exact destination not working with alias names 2026-07-20 14:19:20 +08:00
Ben Woo a8c0b221cd Merge pull request #3503 from Multiverse/fix/alias-mapping
Map alias even when world isn't loaded and case insensitive lookup
2026-07-20 13:18:39 +08:00
Ben Woo 826fe4c6d0 Map alias even when world isn't loaded and case insensitive lookup 2026-07-20 13:15:00 +08:00
Ben Woo 28240c3649 Merge pull request #3501 from Multiverse/feat/inject-locales
Inject default locales instance to Message
2026-07-16 20:41:02 +08:00
Ben Woo c8e4c3239a Merge pull request #3497 from Multiverse/feat/alias-conflict
Add checks for alias name conflict
2026-07-16 15:11:23 +08:00
Ben Woo b23735dc72 Inject default locales instance to Message 2026-07-16 15:09:21 +08:00
Ben Woo 97cbb4576d Merge pull request #3499 from Multiverse/refactor/entity-purge
Clean up entity purger and ensure player is not removed
2026-07-16 12:48:58 +08:00
Ben Woo 1ccfa2b24b Merge pull request #3496 from Multiverse/chore/deps
Bump various dependencies
2026-07-16 12:44:54 +08:00
Ben Woo d50cd6cce5 Merge pull request #3498 from Multiverse/fix/purge-entities-chunk
Remove entities on chunk load based on spawn config
2026-07-16 12:44:30 +08:00
Ben Woo b500956555 Clean up entity purger and ensure player is not removed 2026-07-16 12:43:22 +08:00
Ben Woo de80667bb4 Remove entities on chunk load based on spawn config 2026-07-16 12:22:37 +08:00
Ben Woo 68bbb6ab90 Add checks for alias name conflict 2026-07-14 16:14:51 +08:00
Ben Woo 16738f181a Bump various dependencies 2026-07-14 14:51:02 +08:00
Ben Woo 9dddbdcd97 Merge pull request #3493 from Multiverse/feat/polish-trans
Add polish translation
2026-06-22 11:08:07 +08:00
Ben Woo 0c829f5a4e Add polish translation
Thanks to @santandersonek on discord!
2026-06-22 11:04:08 +08:00
Ben Woo 6f91d290db Merge pull request #3491 from Multiverse/feat/localise
Refactor all remaining issuer raw string messages to use localized strings
2026-06-22 10:55:25 +08:00
Ben Woo cf65076f69 Merge pull request #3492 from amhunter1/feat/turkish-locale
feat: add Turkish (tr) locale
2026-06-22 10:34:13 +08:00
Ben Woo 25daacadba Merge pull request #3490 from Multiverse/fix/name-or-alias
Always try to get world by name/namespace first before alias
2026-06-22 10:19:40 +08:00
amhunter1andClaude Sonnet 4.6 8d9daa4bdf feat: update Turkish locale with keys from PR #3491
Added translations for new keys introduced in the localisation refactor:
- config string parser errors, core config validations
- anchor commands (delete/list/set)
- entity spawn config commands
- generators info keys
- spawn/teleport permission messages
- commands error context keys (loadedmultiverseworld, player, resolve, removeplayers)
- destination display keys (anchor, bed)
- purge entities commands
- queue command prompt/expired keys
- content display pagefilter key
- generic error.details, playercount, teleportplayers.failed keys

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-21 20:07:57 +03:00
amhunter1andClaude Sonnet 4.6 d2c35a029d feat: add Turkish (tr) locale
Adds Turkish language support via multiverse-core_tr.properties.
All 267 translation keys from the English source file are included.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-21 18:55:25 +03:00
Ben Woo 61f4d2308c Refactor all remaining issuer raw string messages to use localized strings 2026-06-21 14:41:23 +08:00
Ben Woo 767b7af5e1 Always try to get world by name/namespace first before alias 2026-06-21 12:09:28 +08:00
Ben Woo 3e46f198f5 Merge pull request #3489 from Multiverse/fix/entity-warning
Improve spawn category map logging
2026-06-20 21:11:18 +08:00
Ben Woo 9f06740873 Improve spawn category map logging 2026-06-20 21:08:51 +08:00
Ben Woo 5648211722 Merge pull request #3488 from Multiverse/fix/spawn-category
Fix spawn category mapping changes in 26.2
2026-06-20 15:36:41 +08:00
Ben Woo ed96764474 Merge pull request #3487 from Multiverse/fix/tele-perms-msg
Make teleport no permission message red
2026-06-20 15:36:17 +08:00
Ben Woo 15453e6443 Add logging if spawn category map is empty after building 2026-06-20 15:32:51 +08:00
Ben Woo 0df35f0e26 Make teleport no permission message red 2026-06-20 15:29:07 +08:00
Ben Woo 0424ac81bd Add 26.2 as supported mc version for platforms upload 2026-06-20 15:28:41 +08:00
Ben Woo 4ee6901df1 Fix spawn category mapping changes in 26.2 2026-06-20 15:27:42 +08:00
84 changed files with 2569 additions and 347 deletions

No files matched your search

@@ -0,0 +1,77 @@
name: 'Generic: Artifact Comment'
on:
workflow_call:
inputs:
plugin_name:
description: 'Plugin name used by Generic: Test'
required: true
type: string
jobs:
comment-artifact:
if: >-
github.event_name == 'workflow_run' &&
github.event.workflow_run.event == 'pull_request' &&
github.event.workflow_run.conclusion == 'success'
runs-on: ubuntu-latest
permissions:
actions: read
pull-requests: write
steps:
# Run separately so fork builds can post comments without a write token.
# Do not check out or execute code from the pull request in this workflow.
- name: Find pull request and artifact
id: artifact
uses: actions/github-script@v7
env:
PLUGIN_NAME: ${{ inputs.plugin_name }}
with:
script: |
const run = context.payload.workflow_run;
// Fork runs can have an empty pull_requests array in the event.
const pulls = await github.paginate(github.rest.pulls.list, {
...context.repo,
state: 'open',
head: `${run.head_repository.owner.login}:${run.head_branch}`,
per_page: 100,
});
const pull = pulls.find(pr =>
pr.head.repo?.id === run.head_repository.id &&
pr.head.ref === run.head_branch &&
pr.head.sha === run.head_sha
);
if (!pull) {
core.info('No open pull request matches this build; skipping comment.');
return;
}
const artifacts = await github.paginate(github.rest.actions.listWorkflowRunArtifacts, {
...context.repo,
run_id: run.id,
per_page: 100,
});
const artifact = artifacts.find(item =>
item.name === `${process.env.PLUGIN_NAME}-pr${pull.number}` && !item.expired
);
if (!artifact) {
core.info('No build artifact is available; skipping comment.');
return;
}
core.setOutput('number', pull.number);
core.setOutput('url', `${run.html_url}/artifacts/${artifact.id}`);
- name: Comment artifact
if: steps.artifact.outputs.number != ''
uses: marocchino/sticky-pull-request-comment@v3
with:
number: ${{ steps.artifact.outputs.number }}
header: build-artifact-${{ inputs.plugin_name }}
message: |
### ✅ `${{ inputs.plugin_name }}` test build ready
Last updated for: [${{ github.event.workflow_run.head_sha }}](${{ github.server_url }}/${{ github.repository }}/commit/${{ github.event.workflow_run.head_sha }})
**Download:** [${{ inputs.plugin_name }}-pr${{ steps.artifact.outputs.number }}.zip](${{ steps.artifact.outputs.url }})
View [CI build and checks](${{ github.event.workflow_run.html_url }}) for more details.
@@ -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: 26.3, 26.2, 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
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: 26.3, 26.2, 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
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-26.3"]}'
plugin_dependencies: ${{ inputs.hangar_plugin_dependencies }}
-10
View File
@@ -36,13 +36,3 @@ jobs:
with:
name: ${{ inputs.plugin_name }}-pr${{ github.event.pull_request.number }}
path: build/libs/${{ inputs.plugin_name }}-pr${{ github.event.pull_request.number }}.jar
comment-artifact:
needs: test-and-artifact
runs-on: ubuntu-latest
permissions:
pull-requests: write
steps:
- uses: benwoo1110/artifact-comment-action@v1
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
+15
View File
@@ -0,0 +1,15 @@
name: 'PR: Artifact Comment'
on:
workflow_run:
workflows: ['PR: Test']
types: [completed]
jobs:
comment-artifact:
permissions:
actions: read
pull-requests: write
uses: ./.github/workflows/generic.artifact_comment.yml
with:
plugin_name: multiverse-core
+3
View File
@@ -46,3 +46,6 @@ debug.txt
# Gradle
.gradle
# Kotlin generated files
/.kotlin/
+10 -10
View File
@@ -9,13 +9,13 @@ description = 'Multiverse-Core'
repositories {
maven {
name = 'aikar repo'
url = uri('https://repo.aikar.co/content/groups/aikar/')
name = 'jitpack repo'
url = uri('https://jitpack.io')
}
maven {
name = 'glaremasters repo'
url = 'https://repo.glaremasters.me/repository/towny/'
name = 'aikar repo'
url = uri('https://repo.aikar.co/content/groups/aikar/')
}
maven {
@@ -37,18 +37,18 @@ dependencies {
}
// PlaceholderAPI
externalPlugin 'me.clip:placeholderapi:2.11.6'
externalPlugin 'me.clip:placeholderapi:2.12.3'
// Command Framework
shadowed 'co.aikar:acf-paper:0.5.1-SNAPSHOT'
// Config
shadowed('io.github.townyadvanced.commentedconfiguration:CommentedConfiguration:1.0.1') {
shadowed('com.github.TownyAdvanced:CommentedConfiguration:1.0.3') {
exclude group: 'org.spigotmc', module: 'spigot-api'
}
// Utils
shadowed 'io.vavr:vavr:0.10.7'
shadowed 'io.vavr:vavr:1.0.1'
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'
@@ -57,11 +57,11 @@ dependencies {
exclude group: 'junit', module: 'junit'
}
shadowed 'de.themoep.idconverter:mappings:1.2-SNAPSHOT'
shadowed('org.bstats:bstats-bukkit:3.1.0') {
shadowed('org.bstats:bstats-bukkit:3.2.1') {
exclude group: 'org.bukkit', module: 'bukkit'
}
shadowed 'net.minidev:json-smart:2.5.2'
shadowed 'org.jetbrains:annotations:26.0.2'
shadowed 'net.minidev:json-smart:2.6.0'
shadowed 'org.jetbrains:annotations:26.1.0'
shadowed 'io.papermc:paperlib:1.0.8'
// Tests
@@ -30,6 +30,7 @@ import org.mvplugins.multiverse.core.inject.PluginServiceLocatorFactory;
import org.mvplugins.multiverse.core.module.MultiverseModule;
import org.mvplugins.multiverse.core.utils.StringFormatter;
import org.mvplugins.multiverse.core.world.WorldManager;
import org.mvplugins.multiverse.core.world.entity.SpawnCategoryMapper;
import org.mvplugins.multiverse.core.world.location.NullSpawnLocation;
import org.mvplugins.multiverse.core.world.location.SpawnLocation;
import org.mvplugins.multiverse.core.world.location.UnloadedWorldLocation;
@@ -97,12 +98,16 @@ public class MultiverseCore extends MultiverseModule {
}
Logging.setShowingConfig(shouldShowConfig());
// Build it here so our logger can work, and failure messages will be logged
SpawnCategoryMapper.buildSpawnCategoryMap();
// Initialize the worlds
worldManagerProvider.get().initAllWorlds().andThenTry(() -> {
Try.run(() -> {
setUpLocales();
worldManagerProvider.get().initAllWorlds();
loadEconomist(); // Setup economy here so vault is loaded
loadAnchors();
registerDynamicListeners(CoreListener.class);
setUpLocales();
registerCommands(CoreCommand.class);
registerDestinations();
setupMetrics();
@@ -1,9 +1,17 @@
package org.mvplugins.multiverse.core;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.function.Consumer;
import com.dumptruckman.minecraft.util.Logging;
import io.vavr.control.Try;
import org.bukkit.Bukkit;
import org.bukkit.plugin.ServicePriority;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.mvplugins.multiverse.core.anchor.AnchorManager;
import org.mvplugins.multiverse.core.config.CoreConfig;
import org.mvplugins.multiverse.core.destination.DestinationsProvider;
@@ -16,18 +24,13 @@ import org.mvplugins.multiverse.core.world.WorldManager;
import org.mvplugins.multiverse.core.world.biomeprovider.BiomeProviderFactory;
import org.mvplugins.multiverse.core.world.generators.GeneratorProvider;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.function.Consumer;
/**
* Provides access to the MultiverseCore API.
*/
public final class MultiverseCoreApi {
private static MultiverseCoreApi instance;
private static final List<Consumer<MultiverseCoreApi>> whenLoadedCallbacks = new ArrayList<>();
private static final List<Consumer<MultiverseCoreApi>> WHEN_LOADED_CALLBACKS = new ArrayList<>();
static void init(@NotNull MultiverseCore multiverseCore) {
if (instance != null) {
@@ -36,11 +39,24 @@ public final class MultiverseCoreApi {
instance = new MultiverseCoreApi(multiverseCore.getServiceLocator());
Bukkit.getServicesManager().register(MultiverseCoreApi.class, instance, multiverseCore, ServicePriority.Normal);
whenLoadedCallbacks.forEach(c -> c.accept(instance));
whenLoadedCallbacks.clear();
List<Consumer<MultiverseCoreApi>> callbacks = List.copyOf(WHEN_LOADED_CALLBACKS);
WHEN_LOADED_CALLBACKS.clear();
MultiverseCoreApi loadedApi = instance;
callbacks.forEach(callback -> runLoadCallback(callback, loadedApi));
}
private static void runLoadCallback(
@NotNull Consumer<MultiverseCoreApi> callback,
@NotNull MultiverseCoreApi loadedApi) {
Try.run(() -> callback.accept(loadedApi))
.onFailure(exception -> Logging.warning(
"A Multiverse-Core API load callback failed: %s", exception.getMessage()));
}
static void shutdown() {
if (instance == null) {
return;
}
Bukkit.getServicesManager().unregister(instance);
instance = null;
}
@@ -62,7 +78,7 @@ public final class MultiverseCoreApi {
if (instance != null) {
consumer.accept(instance);
} else {
whenLoadedCallbacks.add(consumer);
WHEN_LOADED_CALLBACKS.add(consumer);
}
}
@@ -87,7 +87,8 @@ final class PlaceholderExpansionHook extends PlaceholderExpansion {
@Override
public @Nullable String onRequest(OfflinePlayer offlinePlayer, @NotNull String params) {
// Split string in to an Array with underscores
List<String> paramsArray = Lists.newArrayList(REPatterns.UNDERSCORE.split(params));
List<String> paramsArray = Lists.newArrayList(
StringFormatter.parseQuotesInArgs(REPatterns.UNDERSCORE.split(params), "_"));
// No placeholder defined
if (paramsArray.isEmpty()) {
@@ -39,7 +39,9 @@ import org.mvplugins.multiverse.core.display.filters.ContentFilter;
import org.mvplugins.multiverse.core.display.filters.DefaultContentFilter;
import org.mvplugins.multiverse.core.display.filters.RegexContentFilter;
import org.mvplugins.multiverse.core.exceptions.command.MVInvalidCommandArgument;
import org.mvplugins.multiverse.core.locale.MVCorei18n;
import org.mvplugins.multiverse.core.locale.message.Message;
import org.mvplugins.multiverse.core.locale.message.MessageReplacement.Replace;
import org.mvplugins.multiverse.core.utils.PlayerFinder;
import org.mvplugins.multiverse.core.utils.REPatterns;
import org.mvplugins.multiverse.core.utils.tick.TickDuration;
@@ -168,15 +170,67 @@ public class MVCommandContexts extends PaperCommandContexts {
return generatorPlugin;
}
private Message loadedMultiverseWorldPlayerOnlyMessage() {
return Message.of(MVCorei18n.COMMANDS_ERROR_LOADEDMULTIVERSEWORLD_PLAYERSONLY);
}
private Message loadedMultiverseWorldIssuerMessage() {
return Message.of(MVCorei18n.COMMANDS_ERROR_LOADEDMULTIVERSEWORLD_ISSUER);
}
private Message loadedMultiverseWorldInputConsoleMessage(String worldName) {
return Message.of(MVCorei18n.COMMANDS_ERROR_LOADEDMULTIVERSEWORLD_INPUTCONSOLE,
Replace.WORLD.with(worldName));
}
private Message loadedMultiverseWorldInputMessage(String worldName) {
return Message.of(MVCorei18n.COMMANDS_ERROR_LOADEDMULTIVERSEWORLD_INPUT, Replace.WORLD.with(worldName));
}
private Message multiverseWorldPlayerOnlyMessage() {
return Message.of(MVCorei18n.COMMANDS_ERROR_MULTIVERSEWORLD_PLAYERSONLY);
}
private Message multiverseWorldIssuerMessage() {
return Message.of(MVCorei18n.COMMANDS_ERROR_MULTIVERSEWORLD_ISSUER);
}
private Message multiverseWorldInputConsoleMessage(String worldName) {
return Message.of(MVCorei18n.COMMANDS_ERROR_MULTIVERSEWORLD_INPUTCONSOLE, Replace.WORLD.with(worldName));
}
private Message multiverseWorldInputMessage(String worldName) {
return Message.of(MVCorei18n.COMMANDS_ERROR_MULTIVERSEWORLD_INPUT, Replace.WORLD.with(worldName));
}
private Message playerOnlyMessage() {
return Message.of(MVCorei18n.COMMANDS_ERROR_PLAYERSONLY);
}
private Message playerInputIssuerMessage(String playerName) {
return Message.of(MVCorei18n.COMMANDS_ERROR_PLAYER_ISSUERINPUT, Replace.PLAYER.with(playerName));
}
private Message playerInputMessage(String playerName) {
return Message.of(MVCorei18n.COMMANDS_ERROR_PLAYER_INPUT, Replace.PLAYER.with(playerName));
}
private Message playerSelectorMessage(String selector) {
return Message.of(MVCorei18n.COMMANDS_ERROR_PLAYER_SELECTOR, Replace.PLAYER.with(selector));
}
private Message playersInputMessage(String playerName) {
return Message.of(MVCorei18n.COMMANDS_ERROR_PLAYERS_INPUT, Replace.PLAYER.with(playerName));
}
private IssuerAwareContextBuilder<LoadedMultiverseWorld> loadedMultiverseWorldContextBuilder() {
return new IssuerAwareContextBuilder<LoadedMultiverseWorld>()
.fromPlayer((context, player) -> worldManager.getLoadedWorld(player.getWorld()).getOrNull())
.fromInput((context, input) -> getLoadedMultiverseWorld(input))
.issuerOnlyFailMessage((context) -> Message.of("This command can only be used by a player in a loaded Multiverse World."))
.issuerAwarePlayerFailMessage((context, player) -> Message.of("You are not in a loaded multiverse world. Either specify a multiverse world name or use this command in a loaded multiverse world."))
.issuerAwareInputFailMessage((context, input) -> Message.of("World '" + input + "' is not a loaded multiverse world. Remember to specify the world name when using this command in console."))
.inputOnlyFailMessage((context, input) -> Message.of("World " + input + " is not a loaded multiverse world."));
.issuerOnlyFailMessage((context) -> loadedMultiverseWorldPlayerOnlyMessage())
.issuerAwarePlayerFailMessage((context, player) -> loadedMultiverseWorldIssuerMessage())
.issuerAwareInputFailMessage((context, input) -> loadedMultiverseWorldInputConsoleMessage(input))
.inputOnlyFailMessage((context, input) -> loadedMultiverseWorldInputMessage(input));
}
private IssuerAwareContextBuilder<LoadedMultiverseWorld[]> loadedMultiverseWorldArrayContextBuilder() {
@@ -194,16 +248,17 @@ public class MVCommandContexts extends PaperCommandContexts {
}
LoadedMultiverseWorld world = getLoadedMultiverseWorld(worldName);
if (world == null) {
throw new InvalidCommandArgument("World " + worldName + " is not a loaded multiverse world.");
throw new InvalidCommandArgument(
loadedMultiverseWorldInputMessage(worldName).formatted(context.getIssuer()));
}
worlds.add(world);
}
return worlds.isEmpty() ? null : worlds.toArray(new LoadedMultiverseWorld[0]);
})
.issuerOnlyFailMessage((context) -> Message.of("This command can only be used by a player in a loaded Multiverse World."))
.issuerAwarePlayerFailMessage((context, player) -> Message.of("You are not in a loaded multiverse world. Either specify a multiverse world name or use this command in a loaded multiverse world."))
.issuerAwareInputFailMessage((context, input) -> Message.of("World '" + input + "' is not a loaded multiverse world. Remember to specify the world name when using this command in console."))
.inputOnlyFailMessage((context, input) -> Message.of("World " + input + " is not a loaded multiverse world."));
.issuerOnlyFailMessage((context) -> loadedMultiverseWorldPlayerOnlyMessage())
.issuerAwarePlayerFailMessage((context, player) -> loadedMultiverseWorldIssuerMessage())
.issuerAwareInputFailMessage((context, input) -> loadedMultiverseWorldInputConsoleMessage(input))
.inputOnlyFailMessage((context, input) -> loadedMultiverseWorldInputMessage(input));
}
@Nullable
@@ -217,10 +272,10 @@ public class MVCommandContexts extends PaperCommandContexts {
return new IssuerAwareContextBuilder<MultiverseWorld>()
.fromPlayer((context, player) -> worldManager.getWorld(player.getWorld()).getOrNull())
.fromInput((context, input) -> getMultiverseWorld(input))
.issuerOnlyFailMessage((context) -> Message.of("This command can only be used by a player in a Multiverse World."))
.issuerAwarePlayerFailMessage((context, player) -> Message.of("You are not in a multiverse world. Either specify a multiverse world name or use this command in a multiverse world."))
.issuerAwareInputFailMessage((context, input) -> Message.of("World '" + input + "' is not a multiverse world. Remember to specify the world name when using this command in console."))
.inputOnlyFailMessage((context, input) -> Message.of("World " + input + " is not a multiverse world."));
.issuerOnlyFailMessage((context) -> multiverseWorldPlayerOnlyMessage())
.issuerAwarePlayerFailMessage((context, player) -> multiverseWorldIssuerMessage())
.issuerAwareInputFailMessage((context, input) -> multiverseWorldInputConsoleMessage(input))
.inputOnlyFailMessage((context, input) -> multiverseWorldInputMessage(input));
}
private IssuerAwareContextBuilder<MultiverseWorld[]> multiverseWorldArrayContextBuilder() {
@@ -238,16 +293,17 @@ public class MVCommandContexts extends PaperCommandContexts {
}
MultiverseWorld world = getMultiverseWorld(worldName);
if (world == null) {
throw new InvalidCommandArgument("World " + worldName + " is not a multiverse world.");
throw new InvalidCommandArgument(
multiverseWorldInputMessage(worldName).formatted(context.getIssuer()));
}
worlds.add(world);
}
return worlds.isEmpty() ? null : worlds.toArray(new MultiverseWorld[0]);
})
.issuerOnlyFailMessage((context) -> Message.of("This command can only be used by a player in a Multiverse World."))
.issuerAwarePlayerFailMessage((context, player) -> Message.of("You are not in a multiverse world. Either specify a multiverse world name or use this command in a multiverse world."))
.issuerAwareInputFailMessage((context, input) -> Message.of("World '" + input + "' is not a multiverse world. Remember to specify the world name when using this command in console."))
.inputOnlyFailMessage((context, input) -> Message.of("World " + input + " is not a multiverse world."));
.issuerOnlyFailMessage((context) -> multiverseWorldPlayerOnlyMessage())
.issuerAwarePlayerFailMessage((context, player) -> multiverseWorldIssuerMessage())
.issuerAwareInputFailMessage((context, input) -> multiverseWorldInputConsoleMessage(input))
.inputOnlyFailMessage((context, input) -> multiverseWorldInputMessage(input));
}
@Nullable
@@ -267,9 +323,9 @@ public class MVCommandContexts extends PaperCommandContexts {
return new IssuerAwareContextBuilder<Player>()
.fromPlayer((context, player) -> player)
.fromInput((context, input) -> PlayerFinder.get(input, context.getSender()))
.issuerOnlyFailMessage((context) -> Message.of("This command can only be used by a player."))
.issuerAwareInputFailMessage((context, input) -> Message.of("Invalid player: " + input + ". Either specify an online player or use this command as a player."))
.inputOnlyFailMessage((context, input) -> Message.of("Player " + input + " not found."));
.issuerOnlyFailMessage((context) -> playerOnlyMessage())
.issuerAwareInputFailMessage((context, input) -> playerInputIssuerMessage(input))
.inputOnlyFailMessage((context, input) -> playerInputMessage(input));
}
private IssuerAwareContextBuilder<Player[]> playerArrayContextBuilder() {
@@ -286,13 +342,13 @@ public class MVCommandContexts extends PaperCommandContexts {
throw new InvalidCommandArgument(failure.getLocalizedMessage() + " "
+ Option.of(failure.getCause()).map(Throwable::getLocalizedMessage).getOrElse(""));
}))
.issuerOnlyFailMessage((context) -> Message.of("This command can only be used by a player."))
.issuerAwareInputFailMessage((context, input) -> Message.of("Invalid player: " + input + ". Either specify an online player or use this command as a player."))
.issuerOnlyFailMessage((context) -> playerOnlyMessage())
.issuerAwareInputFailMessage((context, input) -> playerInputIssuerMessage(input))
.inputOnlyFailMessage((context, input) -> {
if (PlayerFinder.isSelector(input)) {
return Message.of("No player(s) matched selector: " + input + ".");
return playerSelectorMessage(input);
}
return Message.of("Player(s) " + input + " not found.");
return playersInputMessage(input);
});
}
@@ -12,6 +12,8 @@ import co.aikar.commands.LogLevel;
import co.aikar.commands.PaperCommandManager;
import co.aikar.commands.RootCommand;
import com.dumptruckman.minecraft.util.Logging;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import jakarta.inject.Inject;
import jakarta.inject.Provider;
import org.bukkit.Bukkit;
@@ -24,6 +26,7 @@ import org.mvplugins.multiverse.core.command.flag.CommandFlagsManager;
import org.mvplugins.multiverse.core.command.queue.CommandQueueManager;
import org.mvplugins.multiverse.core.config.CoreConfig;
import org.mvplugins.multiverse.core.locale.PluginLocales;
import org.mvplugins.multiverse.core.locale.message.LocalizedMessage;
import org.mvplugins.multiverse.core.world.WorldManager;
import org.mvplugins.multiverse.core.world.helpers.WorldNameChecker;
@@ -68,6 +71,16 @@ public class MVCommandManager extends PaperCommandManager {
this.setDefaultExceptionHandler(new MVDefaultExceptionHandler());
}
@PostConstruct
private void postConstruct() {
LocalizedMessage.setDefaultLocalesManager(getLocales());
}
@PreDestroy
private void preDestroy() {
LocalizedMessage.setDefaultLocalesManager(null);
}
/**
* Registers a list of commands and handles {@link LegacyAliasCommand} based on config option.
* @param commands The commands to register
@@ -6,12 +6,16 @@ import co.aikar.commands.InvalidCommandArgument;
import co.aikar.commands.contexts.IssuerAwareContextResolver;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.ApiStatus;
import org.mvplugins.multiverse.core.locale.MVCorei18n;
import org.mvplugins.multiverse.core.locale.message.Message;
import org.mvplugins.multiverse.core.locale.message.MessageReplacement.Replace;
import java.util.Objects;
import java.util.function.BiFunction;
import java.util.function.Function;
import static org.mvplugins.multiverse.core.locale.message.MessageReplacement.replace;
/**
* Reusable logic of issuer only and issuer aware context resolvers
*
@@ -22,10 +26,17 @@ public final class IssuerAwareContextBuilder<T> {
private BiFunction<BukkitCommandExecutionContext, Player, T> fromPlayer;
private BiFunction<BukkitCommandExecutionContext, String, T> fromInput;
private Function<BukkitCommandExecutionContext, Message> issuerOnlyFailMessage = context -> Message.of("This command can only be used by a player.");
private BiFunction<BukkitCommandExecutionContext, Player, Message> issuerAwarePlayerFailMessage = (context, player) -> Message.of("Unable to resolve context for player '" + player.getName() + "'.");
private BiFunction<BukkitCommandExecutionContext, String, Message> issuerAwareInputFailMessage = (context, input) -> Message.of("Unable to resolve context for input '" + input + "'.");
private BiFunction<BukkitCommandExecutionContext, String, Message> inputOnlyFailMessage = (context, input) -> Message.of("Unable to resolve context for input '" + input + "'.");
private Function<BukkitCommandExecutionContext, Message> issuerOnlyFailMessage =
context -> Message.of(MVCorei18n.COMMANDS_ERROR_PLAYERSONLY);
private BiFunction<BukkitCommandExecutionContext, Player, Message> issuerAwarePlayerFailMessage =
(context, player) -> Message.of(MVCorei18n.COMMANDS_ERROR_RESOLVE_PLAYER,
Replace.PLAYER.with(player.getName()));
private BiFunction<BukkitCommandExecutionContext, String, Message> issuerAwareInputFailMessage =
(context, input) -> Message.of(MVCorei18n.COMMANDS_ERROR_RESOLVE_INPUT,
replace("{input}").with(input));
private BiFunction<BukkitCommandExecutionContext, String, Message> inputOnlyFailMessage =
(context, input) -> Message.of(MVCorei18n.COMMANDS_ERROR_RESOLVE_INPUT,
replace("{input}").with(input));
public IssuerAwareContextBuilder() {
}
@@ -1,6 +1,5 @@
package org.mvplugins.multiverse.core.command.flags;
import co.aikar.commands.InvalidCommandArgument;
import jakarta.inject.Inject;
import org.bukkit.Bukkit;
import org.jetbrains.annotations.ApiStatus;
@@ -13,6 +12,8 @@ import org.mvplugins.multiverse.core.destination.DestinationInstance;
import org.mvplugins.multiverse.core.destination.DestinationsProvider;
import org.mvplugins.multiverse.core.destination.core.WorldDestination;
import org.mvplugins.multiverse.core.exceptions.command.MVInvalidCommandArgument;
import org.mvplugins.multiverse.core.locale.MVCorei18n;
import org.mvplugins.multiverse.core.locale.message.Message;
import org.mvplugins.multiverse.core.world.WorldManager;
@ApiStatus.AvailableSince("5.7")
@@ -55,7 +56,8 @@ public class RemovePlayerDestinationFlags extends FlagBuilder {
.addAlias("-r")
.defaultValue(() -> worldManager.getDefaultWorld()
.map(defaultWorld -> worldDestination.fromWorld(defaultWorld))
.getOrElseThrow(() -> new InvalidCommandArgument("No default world found, so the --remove-players flag requires a destination argument."))) //TODO: locale
.getOrElseThrow(() -> MVInvalidCommandArgument.of(
Message.of(MVCorei18n.COMMANDS_ERROR_REMOVEPLAYERS_NODEFAULT))))
.completion(input -> destinationsProvider.suggestDestinationStrings(Bukkit.getConsoleSender(), input))
.context(input -> destinationsProvider.parseDestination(input)
.getOrThrow(failure ->
@@ -15,7 +15,6 @@ import com.dumptruckman.minecraft.util.Logging;
import io.vavr.control.Option;
import jakarta.inject.Inject;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.block.data.type.CommandBlock;
import org.bukkit.command.BlockCommandSender;
import org.bukkit.command.CommandSender;
@@ -28,6 +27,7 @@ import org.jvnet.hk2.annotations.Service;
import org.mvplugins.multiverse.core.MultiverseCore;
import org.mvplugins.multiverse.core.command.MVCommandIssuer;
import org.mvplugins.multiverse.core.config.CoreConfig;
import org.mvplugins.multiverse.core.locale.MVCorei18n;
import org.mvplugins.multiverse.core.utils.result.Attempt;
import static org.mvplugins.multiverse.core.locale.message.MessageReplacement.*;
@@ -79,8 +79,9 @@ public class CommandQueueManager {
if (config.getUseConfirmOtp()) {
confirmCommand += " " + payload.otp();
}
payload.issuer().sendMessage(String.format("Run %s%s %sto continue. This will expire in %s seconds.",
ChatColor.GREEN, confirmCommand, ChatColor.WHITE, config.getConfirmTimeout()));
payload.issuer().sendMessage(MVCorei18n.QUEUECOMMAND_PROMPT,
replace("{command}").with(confirmCommand),
replace("{timeout}").with(config.getConfirmTimeout()));
}
/**
@@ -127,7 +128,7 @@ public class CommandQueueManager {
// Payload already removed
return;
}
payload.issuer().sendMessage("Your queued command has expired.");
payload.issuer().sendMessage(MVCorei18n.QUEUECOMMAND_EXPIRED);
};
}
@@ -5,6 +5,7 @@ import org.bukkit.scheduler.BukkitTask;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.mvplugins.multiverse.core.command.MVCommandIssuer;
import org.mvplugins.multiverse.core.locale.MVCorei18n;
import org.mvplugins.multiverse.core.locale.message.Message;
/**
@@ -23,12 +24,10 @@ public class CommandQueuePayload {
return new CommandQueuePayload(issuer);
}
private static final String DEFAULT_PROMPT_MESSAGE = "The command you are trying to run is deemed dangerous."; // todo: localize
private final MVCommandIssuer issuer;
private String otp;
private Runnable action = () -> {};
private Message prompt = Message.of(DEFAULT_PROMPT_MESSAGE);
private Message prompt = Message.of(MVCorei18n.QUEUECOMMAND_DEFAULTPROMPT);
private BukkitTask expireTask;
protected CommandQueuePayload(@NotNull MVCommandIssuer issuer) {
@@ -12,6 +12,9 @@ import org.jvnet.hk2.annotations.Service;
import org.mvplugins.multiverse.core.anchor.AnchorManager;
import org.mvplugins.multiverse.core.anchor.MultiverseAnchor;
import org.mvplugins.multiverse.core.command.MVCommandIssuer;
import org.mvplugins.multiverse.core.locale.MVCorei18n;
import static org.mvplugins.multiverse.core.locale.message.MessageReplacement.replace;
@Service
final class AnchorDeleteCommand extends CoreCommand {
@@ -35,9 +38,9 @@ final class AnchorDeleteCommand extends CoreCommand {
@Description("")
MultiverseAnchor anchor) {
if (anchorManager.deleteAnchor(anchor).isSuccess()) {
issuer.sendMessage("&aAnchor &f" + anchor.getName() + "&a deleted.");
issuer.sendMessage(MVCorei18n.ANCHOR_DELETE_SUCCESS, replace("{anchor}").with(anchor.getName()));
} else {
issuer.sendMessage("&cFailed to delete anchor.");
issuer.sendError(MVCorei18n.ANCHOR_DELETE_FAILURE);
}
}
}
@@ -15,15 +15,18 @@ import org.jvnet.hk2.annotations.Service;
import org.mvplugins.multiverse.core.anchor.AnchorManager;
import org.mvplugins.multiverse.core.command.MVCommandIssuer;
import org.mvplugins.multiverse.core.command.MVCommandManager;
import org.mvplugins.multiverse.core.command.flag.ParsedCommandFlags;
import org.mvplugins.multiverse.core.command.flags.PageFilterFlags;
import org.mvplugins.multiverse.core.display.ContentDisplay;
import org.mvplugins.multiverse.core.display.filters.DefaultContentFilter;
import org.mvplugins.multiverse.core.display.handlers.PagedSendHandler;
import org.mvplugins.multiverse.core.display.parsers.ListContentProvider;
import org.mvplugins.multiverse.core.locale.MVCorei18n;
import org.mvplugins.multiverse.core.locale.message.Message;
import org.mvplugins.multiverse.core.teleportation.LocationManipulation;
import static org.mvplugins.multiverse.core.locale.message.MessageReplacement.replace;
@Service
final class AnchorListCommand extends CoreCommand {
@@ -58,18 +61,19 @@ final class AnchorListCommand extends CoreCommand {
ContentDisplay.create()
.addContent(ListContentProvider.forContent(getAnchors(issuer.getPlayer())))
.withSendHandler(PagedSendHandler.create()
.withHeader("&3==== [ Multiverse Anchors ] ====")
.withHeader(Message.of(MVCorei18n.ANCHOR_LIST_HEADER))
.doPagination(true)
.withTargetPage(parsedFlags.flagValue(flags.page, 1))
.withFilter(parsedFlags.flagValue(flags.filter, DefaultContentFilter.get())))
.send(issuer);
}
private List<String> getAnchors(Player player) {
private List<? extends Message> getAnchors(Player player) {
return anchorManager.getAnchors(player).stream()
.map(anchor ->
"&a%s&7 - &f%s".formatted(
anchor.getName(), locationManipulation.locationToString(anchor.getLocation())))
Message.of(MVCorei18n.ANCHOR_LIST_ENTRY,
replace("{anchor}").with(anchor.getName()),
replace("{location}").with(locationManipulation.locationToString(anchor.getLocation()))))
.toList();
}
}
@@ -15,8 +15,11 @@ import org.jvnet.hk2.annotations.Service;
import org.mvplugins.multiverse.core.anchor.AnchorManager;
import org.mvplugins.multiverse.core.command.MVCommandIssuer;
import org.mvplugins.multiverse.core.locale.MVCorei18n;
import org.mvplugins.multiverse.core.teleportation.LocationManipulation;
import static org.mvplugins.multiverse.core.locale.message.MessageReplacement.replace;
@Service
final class AnchorSetCommand extends CoreCommand {
@@ -80,10 +83,12 @@ final class AnchorSetCommand extends CoreCommand {
}
private void sendAnchorSetSuccessMessage(MVCommandIssuer issuer, String anchorName, String locationString) {
issuer.sendMessage("&aAnchor &f" + anchorName + "&a set to &f" + locationString);
issuer.sendMessage(MVCorei18n.ANCHOR_SET_SUCCESS,
replace("{anchor}").with(anchorName),
replace("{location}").with(locationString));
}
private void sendAnchorSetFailedMessage(MVCommandIssuer issuer, String anchorName) {
issuer.sendMessage("&cFailed to set anchor &f" + anchorName + ".");
issuer.sendMessage(MVCorei18n.ANCHOR_SET_FAILURE, replace("{anchor}").with(anchorName));
}
}
@@ -61,7 +61,7 @@ class CheckCommand extends CoreCommand {
replace("{location}").with(destination.getLocation(player)
.map(locationManipulation::locationToString)
.map(Message::of)
.getOrElse(() -> Message.of(MVCorei18n.GENERIC_NULL, "Null!"))));
.getOrElse(() -> Message.of(MVCorei18n.GENERIC_NULL))));
// TODO: Show permission required for this particular destination
}
@@ -41,7 +41,7 @@ class CloneCommand extends CoreCommand {
@Subcommand("clone")
@CommandPermission("multiverse.core.clone")
@CommandCompletion("@mvworlds:scope=loaded @empty @flags:groupName=" + Flags.NAME)
@Syntax("<world> <new-world-name> [--reset-world-config --reset-gamerules --reset-world-border]")
@Syntax("<world> <new-world-name> [--reset-world-config --reset-gamerules --reset-world-border --no-save]")
@Description("{@@mv-core.clone.description}")
void onCloneCommand(
MVCommandIssuer issuer,
@@ -51,11 +51,11 @@ class CloneCommand extends CoreCommand {
MultiverseWorld world,
@Syntax("<new-world-name>")
@Description("{@@mv-core.clone.newWorld.description}")
@Description("{@@mv-core.clone.newworld.description}")
String newWorldName,
@Optional
@Syntax("[--reset-world-config --reset-gamerules --reset-world-border]")
@Syntax("[--reset-world-config --reset-gamerules --reset-world-border --no-save]")
@Description("{@@mv-core.regen.other.description}")
String[] flagArray) {
ParsedCommandFlags parsedFlags = flags.parse(flagArray);
@@ -56,7 +56,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 --generate-bonus-chest --force-spawn-position <x,y,z:pitch:yaw> " +
"--no-adjust-spawn --no-structures --generate-bonus-chest --force-spawn-position <x,y,z:pitch:yaw> " +
"--biome <biome> --generator-settings <json-settings> --properties <prop1=value1,prop2=value2,...>]")
@Description("{@@mv-core.create.description}")
void onCreateCommand(
@@ -71,7 +71,7 @@ class CreateCommand extends CoreCommand {
World.Environment environment,
@Optional
@Syntax("[--seed <seed> --generator <generator[:id]> --world-type <worldtype> --adjust-spawn " +
@Syntax("[--seed <seed> --generator <generator[:id]> --world-type <worldtype> --no-adjust-spawn " +
"--no-structures --generate-bonus-chest --force-spawn-position <x,y,z:pitch:yaw> --biome <biome> " +
"--generator-settings <json-settings> --properties <prop1=value1,prop2=value2,...>]")
@Description("{@@mv-core.create.flags.description}")
@@ -79,7 +79,7 @@ class DeleteCommand extends CoreCommand {
commandQueueManager.addToQueue(CommandQueuePayload
.issuer(issuer)
.action(() -> runDeleteCommand(issuer, world, parsedFlags))
.prompt(Message.of(MVCorei18n.DELETE_PROMPT, "",
.prompt(Message.of(MVCorei18n.DELETE_PROMPT,
Replace.WORLD.with(world.getName()))));
}
@@ -94,7 +94,7 @@ class DeleteCommand extends CoreCommand {
: AsyncAttemptsAggregate.emptySuccess();
future.onSuccess(() -> worldTickDeferrer.deferWorldTick(() -> doWorldDeleting(issuer, world)))
.onFailure(() -> issuer.sendError("Failed to teleport one or more players out of the world!"));
.onFailure(() -> issuer.sendError(MVCorei18n.GENERIC_TELEPORTPLAYERS_FAILED));
}
private void doWorldDeleting(MVCommandIssuer issuer, MultiverseWorld world) {
@@ -21,7 +21,9 @@ import org.mvplugins.multiverse.core.display.ContentDisplay;
import org.mvplugins.multiverse.core.display.filters.DefaultContentFilter;
import org.mvplugins.multiverse.core.display.handlers.PagedSendHandler;
import org.mvplugins.multiverse.core.display.parsers.ListContentProvider;
import org.mvplugins.multiverse.core.locale.MVCorei18n;
import org.mvplugins.multiverse.core.locale.message.Message;
import org.mvplugins.multiverse.core.locale.message.MessageReplacement.Replace;
import org.mvplugins.multiverse.core.utils.StringFormatter;
import org.mvplugins.multiverse.core.world.MultiverseWorld;
import org.mvplugins.multiverse.core.world.WorldManager;
@@ -31,6 +33,8 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import static org.mvplugins.multiverse.core.locale.message.MessageReplacement.replace;
@Service
@Subcommand("entity-spawn-config")
final class EntitySpawnConfigCommand extends CoreCommand {
@@ -64,21 +68,26 @@ final class EntitySpawnConfigCommand extends CoreCommand {
ContentDisplay.create()
.addContent(ListContentProvider.forContent(getEntitySpawnConfigList(world)))
.withSendHandler(PagedSendHandler.create()
.withHeader(Message.of("==== [ Entity Spawn Config '" + world.getName() + "' ] ===="))
.withHeader(Message.of(MVCorei18n.ENTITYSPAWNCONFIG_INFO_HEADER,
Replace.WORLD.with(world.getName())))
.withLinesPerPage(8)
.withTargetPage(parsedFlags.flagValue(flags.page, 1))
.withFilter(parsedFlags.flagValue(flags.filter, DefaultContentFilter.get())))
.send(issuer);
}
private List<String> getEntitySpawnConfigList(MultiverseWorld world) {
List<String> list = new ArrayList<>();
private List<Message> getEntitySpawnConfigList(MultiverseWorld world) {
List<Message> list = new ArrayList<>();
Arrays.stream(SpawnCategory.values()).forEach(spawnCategory -> {
list.add(spawnCategory.name() + ": ");
list.add(Message.of(MVCorei18n.ENTITYSPAWNCONFIG_INFO_CATEGORY,
replace("{category}").with(spawnCategory.name())));
SpawnCategoryConfig spawnCategoryConfig = world.getEntitySpawnConfig().getSpawnCategoryConfig(spawnCategory);
list.add(" spawn: " + spawnCategoryConfig.isSpawn());
list.add(" tick-rate: " + spawnCategoryConfig.getTickRate());
list.add(" exceptions: " + StringFormatter.join(spawnCategoryConfig.getExceptions(), ", "));
list.add(Message.of(MVCorei18n.ENTITYSPAWNCONFIG_INFO_SPAWN,
replace("{spawn}").with(spawnCategoryConfig.isSpawn())));
list.add(Message.of(MVCorei18n.ENTITYSPAWNCONFIG_INFO_TICKRATE,
replace("{tickRate}").with(spawnCategoryConfig.getTickRate())));
list.add(Message.of(MVCorei18n.ENTITYSPAWNCONFIG_INFO_EXCEPTIONS,
replace("{exceptions}").with(StringFormatter.join(spawnCategoryConfig.getExceptions(), ", "))));
});
return list;
}
@@ -114,9 +123,16 @@ final class EntitySpawnConfigCommand extends CoreCommand {
.getStringPropertyHandle()
.modifyPropertyString(property, value, action)
.andThenTry(worldManager::saveWorldsConfig)
.onSuccess(ignore -> issuer.sendInfo("Successfully set " + property + " to " + value
+ " for " + spawnCategory.name() + " in " + world.getName()))
.onFailure(e -> issuer.sendError("Unable to set " + property + " to " + value
+ " for " + spawnCategory.name() + " in " + world.getName() + ": " + e.getMessage()));
.onSuccess(ignore -> issuer.sendInfo(MVCorei18n.ENTITYSPAWNCONFIG_MODIFY_SUCCESS,
replace("{property}").with(property),
Replace.VALUE.with(value),
replace("{category}").with(spawnCategory.name()),
Replace.WORLD.with(world.getName())))
.onFailure(e -> issuer.sendError(MVCorei18n.ENTITYSPAWNCONFIG_MODIFY_FAILURE,
replace("{property}").with(property),
Replace.VALUE.with(value),
replace("{category}").with(spawnCategory.name()),
Replace.WORLD.with(world.getName()),
Replace.ERROR.with(e)));
}
}
@@ -54,21 +54,21 @@ class GameruleCommand extends CoreCommand {
@Subcommand("gamerule|rule set")
@CommandPermission("multiverse.core.gamerule.set")
@CommandCompletion("@gamerules @gamerulesvalues @mvworlds:multiple|*")
@Syntax("<Gamerule> <Gamerule value> [World or *]")
@Syntax("<gamerule> <value> [worlds|*]")
@Description("{@@mv-core.gamerule.set.description}")
void onGameruleSetCommand(
MVCommandIssuer issuer,
@Syntax("<Gamerule>")
@Syntax("<gamerule>")
@Description("{@@mv-core.gamerule.set.gamerule.description}")
GameRule gamerule,
@Syntax("<Value>")
@Syntax("<value>")
@Description("{@@mv-core.gamerule.set.value.description}")
GameRuleValue gameRuleValue,
@Flags("resolve=issuerAware,maxArgForAware=2")
@Syntax("[World or *]")
@Syntax("[worlds|*]")
@Description("{@@mv-core.gamerule.set.world.description}")
LoadedMultiverseWorld[] worlds) {
Object value = gameRuleValue.value();
@@ -105,17 +105,17 @@ class GameruleCommand extends CoreCommand {
@Subcommand("gamerule|rule reset")
@CommandPermission("multiverse.core.gamerule.set")
@CommandCompletion("@gamerules @mvworlds:multiple|*")
@Syntax("<Gamerule> [World or *]")
@Syntax("<gamerule> [worlds|*]")
@Description("{@@mv-core.gamerule.reset.description}")
void onGameruleResetCommand(
MVCommandIssuer issuer,
@Syntax("<Gamerule>")
@Syntax("<gamerule>")
@Description("{@@mv-core.gamerule.reset.gamerule.description}")
GameRule gamerule,
@Flags("resolve=issuerAware,maxArgForAware=1")
@Syntax("[World or *]")
@Syntax("[worlds|*]")
@Description("{@@mv-core.gamerule.reset.world.description}")
LoadedMultiverseWorld[] worlds) {
AtomicBoolean success = new AtomicBoolean(true);
@@ -153,7 +153,7 @@ class GameruleCommand extends CoreCommand {
MVCommandIssuer issuer,
@Flags("resolve=issuerAware,maxArgForAware=0")
@Syntax("<world>")
@Syntax("[world]")
@Description("{@@mv-core.gamerule.list.description.world}")
LoadedMultiverseWorld world,
@@ -10,7 +10,6 @@ import co.aikar.commands.annotation.Optional;
import co.aikar.commands.annotation.Subcommand;
import co.aikar.commands.annotation.Syntax;
import jakarta.inject.Inject;
import org.bukkit.ChatColor;
import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Service;
@@ -23,10 +22,13 @@ import org.mvplugins.multiverse.core.display.filters.DefaultContentFilter;
import org.mvplugins.multiverse.core.display.handlers.PagedSendHandler;
import org.mvplugins.multiverse.core.display.parsers.ListContentProvider;
import org.mvplugins.multiverse.core.locale.MVCorei18n;
import org.mvplugins.multiverse.core.locale.message.Message;
import org.mvplugins.multiverse.core.utils.StringFormatter;
import org.mvplugins.multiverse.core.world.generators.GeneratorPlugin;
import org.mvplugins.multiverse.core.world.generators.GeneratorProvider;
import static org.mvplugins.multiverse.core.locale.message.MessageReplacement.replace;
/**
* List all gamerules in your current or specified world.
*/
@@ -68,7 +70,7 @@ class GeneratorsCommand extends CoreCommand {
ContentDisplay.create()
.addContent(ListContentProvider.forContent(generators))
.withSendHandler(PagedSendHandler.create()
.withHeader("%s====[ Multiverse Generator List ]====", ChatColor.AQUA)
.withHeader(Message.of(MVCorei18n.GENERATORS_HEADER))
.doPagination(true)
.withTargetPage(parsedFlags.flagValue(flags.page, 1))
.withFilter(parsedFlags.flagValue(flags.filter, DefaultContentFilter.get())))
@@ -85,10 +87,13 @@ class GeneratorsCommand extends CoreCommand {
@Syntax("<generator>")
GeneratorPlugin generatorPlugin
) {
issuer.sendMessage(ChatColor.RESET + "Generator Plugin: " + generatorPlugin.getPluginName());
issuer.sendMessage(ChatColor.RESET + "Example usages: ");
issuer.sendMessage(ChatColor.RESET + StringFormatter.join(generatorPlugin.getExampleUsages(), "\n"));
issuer.sendMessage(ChatColor.RESET + "Link to more info: " + generatorPlugin.getInfoLink());
issuer.sendMessage(MVCorei18n.GENERATORS_INFO_PLUGIN,
replace("{plugin}").with(generatorPlugin.getPluginName()));
issuer.sendMessage(MVCorei18n.GENERATORS_INFO_EXAMPLEUSAGES);
issuer.sendMessage(MVCorei18n.GENERATORS_INFO_USAGES,
replace("{usages}").with(StringFormatter.join(generatorPlugin.getExampleUsages(), "\n")));
issuer.sendMessage(MVCorei18n.GENERATORS_INFO_INFOLINK,
replace("{link}").with(generatorPlugin.getInfoLink()));
}
@Service
@@ -43,7 +43,8 @@ class ImportCommand extends CoreCommand {
@Subcommand("import")
@CommandPermission("multiverse.core.import")
@CommandCompletion("@mvworlds:scope=potential @environments @flags:groupName=" + Flags.NAME)
@Syntax("<name> <environment> [--generator <generator[:id]> --adjust-spawn --biome <biome>]")
@Syntax("<name> <environment> [--generator <generator[:id]> --no-adjust-spawn --biome <biome> " +
"--skip-folder-check --generator-settings <json-settings>]")
@Description("{@@mv-core.import.description}")
void onImportCommand(
MVCommandIssuer issuer,
@@ -53,12 +54,13 @@ class ImportCommand extends CoreCommand {
@Description("{@@mv-core.import.name.description}")
String worldName,
@Syntax("<env>")
@Syntax("<environment>")
@Description("{@@mv-core.import.env.description}")
World.Environment environment,
@Optional
@Syntax("[--generator <generator[:id]> --adjust-spawn --biome <biome>]")
@Syntax("[--generator <generator[:id]> --no-adjust-spawn --biome <biome> " +
"--skip-folder-check --generator-settings <json-settings>]")
@Description("{@@mv-core.import.other.description}")
String[] flagArray) {
ParsedCommandFlags parsedFlags = flags.parse(flagArray);
@@ -54,12 +54,12 @@ class ListCommand extends CoreCommand {
@Subcommand("list")
@CommandPermission("multiverse.core.list.worlds")
@CommandCompletion("@flags:groupName=" + Flags.NAME)
@Syntax("--filter [filter] --page [page] --raw")
@Syntax("[--filter <filter>] [--page <page>] [--raw]")
@Description("{{@mv-core.list.description}}")
public void onListCommand(
MVCommandIssuer issuer,
@Syntax("[--filter <filter>] [--page <page>]")
@Syntax("[--filter <filter>] [--page <page>] [--raw]")
@Description("Filters the list of worlds by the given regex and displays the given page.")
String[] flagArray) {
ParsedCommandFlags parsedFlags = flags.parse(flagArray);
@@ -39,7 +39,7 @@ class LoadCommand extends CoreCommand {
@Subcommand("load")
@CommandPermission("multiverse.core.load")
@CommandCompletion("@mvworlds:scope=unloaded @flags:groupName=" + Flags.NAME)
@Syntax("<world>")
@Syntax("<world> [--skip-folder-check]")
@Description("{@@mv-core.load.description}")
void onLoadCommand(
MVCommandIssuer issuer,
@@ -107,14 +107,14 @@ class ModifyCommand extends CoreCommand {
replace("{action}").with(action.name().toLowerCase()),
replace("{property}").with(propertyName),
Replace.WORLD.with(world.getName()),
Replace.ERROR.with(exception.getMessage()));
Replace.ERROR.with(exception));
} else {
issuer.sendMessage(MVCorei18n.MODIFY_FAILURE,
replace("{action}").with(action.name().toLowerCase()),
replace("{property}").with(propertyName),
Replace.VALUE.with(propertyValue),
Replace.WORLD.with(world.getName()),
Replace.ERROR.with(exception.getMessage()));
Replace.ERROR.with(exception));
}
});
}
@@ -10,12 +10,16 @@ import jakarta.inject.Inject;
import org.bukkit.entity.SpawnCategory;
import org.jvnet.hk2.annotations.Service;
import org.mvplugins.multiverse.core.command.MVCommandIssuer;
import org.mvplugins.multiverse.core.locale.MVCorei18n;
import org.mvplugins.multiverse.core.locale.message.MessageReplacement.Replace;
import org.mvplugins.multiverse.core.utils.StringFormatter;
import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld;
import org.mvplugins.multiverse.core.world.entity.EntityPurger;
import java.util.List;
import static org.mvplugins.multiverse.core.locale.message.MessageReplacement.replace;
@Service
final class PurgeAllEntitiesCommand extends CoreCommand {
@@ -43,12 +47,16 @@ final class PurgeAllEntitiesCommand extends CoreCommand {
) {
if (spawnCategories == null || spawnCategories.length == 0) {
int purgeCount = entityPurger.purgeAllEntities(world);
issuer.sendMessage("Successfully purged " + purgeCount + " entities in world " + world.getName() + ".");
issuer.sendMessage(MVCorei18n.PURGEALLENTITIES_SUCCESS,
Replace.COUNT.with(purgeCount),
Replace.WORLD.with(world.getName()));
return;
}
int purgeCount = entityPurger.purgeEntities(world, spawnCategories);
issuer.sendMessage("Successfully purged " + purgeCount + " entities in world " + world.getName() +
" for spawn categories " + StringFormatter.join(List.of(spawnCategories), ", ") + ".");
issuer.sendMessage(MVCorei18n.PURGEALLENTITIES_SUCCESS_CATEGORIES,
Replace.COUNT.with(purgeCount),
Replace.WORLD.with(world.getName()),
replace("{categories}").with(StringFormatter.join(List.of(spawnCategories), ", ")));
}
}
@@ -8,6 +8,8 @@ import co.aikar.commands.annotation.Syntax;
import jakarta.inject.Inject;
import org.jvnet.hk2.annotations.Service;
import org.mvplugins.multiverse.core.command.MVCommandIssuer;
import org.mvplugins.multiverse.core.locale.MVCorei18n;
import org.mvplugins.multiverse.core.locale.message.MessageReplacement.Replace;
import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld;
import org.mvplugins.multiverse.core.world.entity.EntityPurger;
@@ -33,6 +35,8 @@ final class PurgeEntitiesCommand extends CoreCommand {
LoadedMultiverseWorld world
) {
int purgeCount = entityPurger.purgeEntities(world);
issuer.sendMessage("Successfully purged " + purgeCount + " entities in world " + world.getName() + ".");
issuer.sendMessage(MVCorei18n.PURGEENTITIES_SUCCESS,
Replace.COUNT.with(purgeCount),
Replace.WORLD.with(world.getName()));
}
}
@@ -70,7 +70,7 @@ class RegenCommand extends CoreCommand {
@CommandPermission("multiverse.core.regen")
@CommandCompletion("@mvworlds:scope=loaded @flags:groupName=" + Flags.NAME)
@Syntax("<world> [--seed [seed]] [--reset-world-config] [--reset-gamerules] [--reset-world-border] " +
"[--remove-players [destination]]")
"[--keep-files <files>] [--remove-players [destination]]")
@Description("{@@mv-core.regen.description}")
void onRegenCommand(
MVCommandIssuer issuer,
@@ -81,7 +81,7 @@ class RegenCommand extends CoreCommand {
@Optional
@Syntax("[--seed [seed]] [--reset-world-config] [--reset-gamerules] [--reset-world-border] " +
"[--remove-players [destination]]")
"[--keep-files <files>] [--remove-players [destination]]")
@Description("{@@mv-core.regen.other.description}")
String[] flagArray) {
ParsedCommandFlags parsedFlags = flags.parse(flagArray);
@@ -89,7 +89,7 @@ class RegenCommand extends CoreCommand {
commandQueueManager.addToQueue(CommandQueuePayload
.issuer(issuer)
.action(() -> runRegenCommand(issuer, world, parsedFlags))
.prompt(Message.of(MVCorei18n.REGEN_PROMPT, "",
.prompt(Message.of(MVCorei18n.REGEN_PROMPT,
Replace.WORLD.with(world.getName()))));
}
@@ -105,7 +105,7 @@ class RegenCommand extends CoreCommand {
// todo: using future will hide stacktrace
future.onSuccess(() -> worldTickDeferrer
.deferWorldTick(() -> doWorldRegening(issuer, world, parsedFlags, worldPlayers)))
.onFailure(() -> issuer.sendError("Failed to teleport one or more players out of the world!"));
.onFailure(() -> issuer.sendError(MVCorei18n.GENERIC_TELEPORTPLAYERS_FAILED));
}
private void doWorldRegening(
@@ -75,7 +75,7 @@ class RemoveCommand extends CoreCommand {
: AsyncAttemptsAggregate.emptySuccess();
future.onSuccess(() -> doWorldRemoving(issuer, world, parsedFlags))
.onFailure(() -> issuer.sendError("Failed to teleport one or more players out of the world!"));
.onFailure(() -> issuer.sendError(MVCorei18n.GENERIC_TELEPORTPLAYERS_FAILED));
}
private void doWorldRemoving(MVCommandIssuer issuer, MultiverseWorld world, ParsedCommandFlags parsedFlags) {
@@ -40,12 +40,12 @@ class SetSpawnCommand extends CoreCommand {
@Subcommand("setspawn")
@CommandPermission("multiverse.core.spawn.set")
@CommandCompletion("@flags:groupName=" + UnsafeFlags.NAME + " @flags:resolveUntil=arg1,groupName=" + UnsafeFlags.NAME)
@Syntax("[worldname:x,y,z[,pitch,yaw]] [--unsafe]")
@Syntax("[worldname:x,y,z[,yaw,pitch]] [--unsafe]")
@Description("{@@mv-core.setspawn.description}")
void onSetSpawnCommand(
MVCommandIssuer issuer,
@Syntax("[worldname:x,y,z[,pitch,yaw]]")
@Syntax("[worldname:x,y,z[,yaw,pitch]]")
@Description("{@@mv-core.setspawn.location.description}")
PlayerLocation playerLocation,
@@ -62,7 +62,7 @@ final class SpawnCommand extends CoreCommand {
@CommandPermission("@mvspawn")
@CommandCompletion("@playersarray:checkPermissions=@mvspawnother|@flags:resolveUntil=arg1,groupName=" + UnsafeFlags.NAME
+ " @flags:groupName=" + UnsafeFlags.NAME)
@Syntax("[player]")
@Syntax("[player] [--unsafe]")
@Description("{@@mv-core.spawn.description}")
void onSpawnTpCommand(
MVCommandIssuer issuer,
@@ -88,11 +88,11 @@ final class SpawnCommand extends CoreCommand {
List<Entity> entities, boolean checkSafety) {
LoadedMultiverseWorld mvWorld = worldManager.getLoadedWorld(world).getOrNull();
if (mvWorld == null) {
issuer.sendMessage("The world '" + world.getName() + "' is not a multiverse world!");
issuer.sendMessage(MVCorei18n.SPAWN_NOTMVWORLD, Replace.WORLD.with(world.getName()));
return;
}
if (!permissionsChecker.checkSpawnPermission(issuer.getIssuer(), entities, mvWorld)) {
issuer.sendMessage("You do not have permission to use this command in this world!");
issuer.sendMessage(MVCorei18n.SPAWN_NOPERMISSION);
return;
}
@@ -129,16 +129,18 @@ final class SpawnCommand extends CoreCommand {
.checkSafety(checkSafety)
.teleport(entities)
.onSuccessCount(successCount -> issuer.sendMessage(MVCorei18n.SPAWN_SUCCESS,
Replace.PLAYER.with(successCount + " players"), //todo: replace this with localised "{count} players"
Replace.PLAYER.with(Message.of(MVCorei18n.GENERIC_PLAYERCOUNT,
Replace.COUNT.with(successCount))),
Replace.WORLD.with(mvWorld.getName())))
.onFailureCount(reasonsCountMap -> {
for (var entry : reasonsCountMap.entrySet()) {
Logging.finer("Failed to teleport %s players to %s: %s",
entry.getValue(), mvWorld.getName(), entry.getKey());
issuer.sendError(MVCorei18n.SPAWN_FAILED,
Replace.PLAYER.with(entry.getValue() + " players"),
Replace.PLAYER.with(Message.of(MVCorei18n.GENERIC_PLAYERCOUNT,
Replace.COUNT.with(entry.getValue()))),
Replace.WORLD.with(mvWorld.getName()),
Replace.REASON.with(entry.getKey().getMessageKey()));
Replace.REASON.with(Message.of(entry.getKey())));
}
});
}
@@ -59,7 +59,7 @@ final class TeleportCommand extends CoreCommand {
@CommandCompletion("@playersarray:checkPermissions=@mvteleportother|@destinations:byIssuerForArg=arg1 "
+ "@destinations:notByIssuerForArg=arg1|@flags:byIssuerForArg=arg1,groupName=" + Flags.NAME + " "
+ "@flags:notByIssuerForArg=arg1,groupName=" + Flags.NAME)
@Syntax("[player] <destination> [--unsafe]")
@Syntax("[player] <destination> [--unsafe --silent]")
@Description("{@@mv-core.teleport.description}")
void onTeleportCommand(
MVCommandIssuer issuer,
@@ -74,7 +74,7 @@ final class TeleportCommand extends CoreCommand {
DestinationInstance<?, ?> destination,
@Optional
@Syntax("[--unsafe]")
@Syntax("[--unsafe --silent]")
@Description("")
String[] flagArray) {
Player[] players = playersValue.value();
@@ -94,10 +94,9 @@ final class TeleportCommand extends CoreCommand {
DestinationInstance<?, ?> destination,
ParsedCommandFlags parsedFlags) {
if (!permissionsChecker.checkTeleportPermission(issuer.getIssuer(), player, destination)) {
// TODO localize
issuer.sendMessage(player == issuer.getPlayer()
? "You do not have permission to teleport yourself!"
: "You do not have permission to teleport other players!");
issuer.sendError(player == issuer.getPlayer()
? MVCorei18n.TELEPORT_NOPERMISSION_SELF
: MVCorei18n.TELEPORT_NOPERMISSION_OTHER);
return;
}
@@ -134,8 +133,7 @@ final class TeleportCommand extends CoreCommand {
DestinationInstance<?, ?> destination,
ParsedCommandFlags parsedFlags) {
if (!permissionsChecker.checkTeleportPermission(issuer.getIssuer(), Arrays.asList(players), destination)) {
// TODO localize
issuer.sendMessage("You do not have permission to teleport all these players!");
issuer.sendError(MVCorei18n.TELEPORT_NOPERMISSION_ALL);
return;
}
@@ -149,7 +147,8 @@ final class TeleportCommand extends CoreCommand {
return;
}
issuer.sendInfo(MVCorei18n.TELEPORT_SUCCESS,
Replace.PLAYER.with(successCount + " players"),
Replace.PLAYER.with(Message.of(MVCorei18n.GENERIC_PLAYERCOUNT,
Replace.COUNT.with(successCount))),
Replace.DESTINATION.with(destination.getDisplayMessage()));
})
.onFailureCount(reasonsCountMap -> {
@@ -157,7 +156,8 @@ final class TeleportCommand extends CoreCommand {
Logging.finer("Failed to teleport %s players to %s: %s",
entry.getValue(), destination, entry.getKey());
issuer.sendError(MVCorei18n.TELEPORT_FAILED,
Replace.PLAYER.with(entry.getValue() + " players"),
Replace.PLAYER.with(Message.of(MVCorei18n.GENERIC_PLAYERCOUNT,
Replace.COUNT.with(entry.getValue()))),
Replace.DESTINATION.with(destination.getDisplayMessage()),
Replace.REASON.with(Message.of(entry.getKey())));
}
@@ -52,7 +52,7 @@ class UnloadCommand extends CoreCommand {
@Subcommand("unload")
@CommandPermission("multiverse.core.unload")
@CommandCompletion("@mvworlds @flags:groupName=" + Flags.NAME)
@Syntax("<world> [--remove-players [destination]] [--no-save]")
@Syntax("<world> [--remove-players [destination]] [--no-unload-bukkit-world] [--no-save]")
@Description("{@@mv-core.unload.description}")
void onUnloadCommand(
MVCommandIssuer issuer,
@@ -62,7 +62,7 @@ class UnloadCommand extends CoreCommand {
LoadedMultiverseWorld world,
@Optional
@Syntax("[--remove-players [destination]] [--no-save]")
@Syntax("[--remove-players [destination]] [--no-unload-bukkit-world] [--no-save]")
@Description("{@@mv-core.gamerules.description.page}")
String[] flagArray) {
ParsedCommandFlags parsedFlags = flags.parse(flagArray);
@@ -75,7 +75,7 @@ class UnloadCommand extends CoreCommand {
: AsyncAttemptsAggregate.emptySuccess();
future.onSuccess(() -> doWorldUnloading(issuer, world, parsedFlags))
.onFailure(() -> issuer.sendError("Failed to teleport one or more players out of the world!"));
.onFailure(() -> issuer.sendError(MVCorei18n.GENERIC_TELEPORTPLAYERS_FAILED));
}
private void doWorldUnloading(MVCommandIssuer issuer, LoadedMultiverseWorld world, ParsedCommandFlags parsedFlags) {
@@ -50,10 +50,10 @@ final class WorldBorderCommand extends CoreCommand {
void onWorldBorderCenter(
MVCommandIssuer issuer,
@Syntax("[x]")
@Syntax("<x>")
double x,
@Syntax("[z]")
@Syntax("<z>")
double z,
@Flags("resolve=issuerAware,maxArgForAware=0")
@@ -236,6 +236,6 @@ final class WorldBorderCommand extends CoreCommand {
private void worldBorderAction(MVCommandIssuer issuer, LoadedMultiverseWorld world, Consumer<WorldBorder> worldBorderAction) {
Try.run(() -> world.getWorldBorder().peek(worldBorderAction))
.onFailure(error -> issuer.sendError(error.getLocalizedMessage()));
.onFailure(error -> issuer.sendError(MVCorei18n.GENERIC_ERROR_DETAILS, Replace.ERROR.with(error)));
}
}
@@ -246,6 +246,16 @@ public final class CoreConfig {
return configHandle.get(configNodes.endWorldNameFormat);
}
@ApiStatus.AvailableSince("5.8")
public Try<Void> setWarnAliasConflicts(boolean warnAliasConflicts) {
return configHandle.set(configNodes.warnAliasConflicts, warnAliasConflicts);
}
@ApiStatus.AvailableSince("5.8")
public boolean getWarnAliasConflicts() {
return configHandle.get(configNodes.warnAliasConflicts);
}
/**
* {@inheritDoc}
*/
@@ -25,6 +25,8 @@ import org.mvplugins.multiverse.core.destination.DestinationsProvider;
import org.mvplugins.multiverse.core.dynamiclistener.EventPriorityMapper;
import org.mvplugins.multiverse.core.event.MVDebugModeEvent;
import org.mvplugins.multiverse.core.exceptions.MultiverseException;
import org.mvplugins.multiverse.core.locale.MVCorei18n;
import org.mvplugins.multiverse.core.locale.message.Message;
import org.mvplugins.multiverse.core.permissions.PermissionUtils;
import org.mvplugins.multiverse.core.teleportation.PassengerModes;
import org.mvplugins.multiverse.core.world.helpers.DimensionFinder.DimensionFormat;
@@ -198,6 +200,15 @@ final class CoreConfigNodes {
.stringParser(DimensionFormatNodeStringParser.INSTANCE)
.build());
final ConfigNode<Boolean> warnAliasConflicts = node(ConfigNode.builder("world.warn-alias-conflicts", Boolean.class)
.comment("")
.comment("Sets whether Multiverse will warn about alias duplicates when adding worlds or modifying aliases.")
.comment("Although not enforced, it is highly recommended to not have multiple worlds with the same alias or")
.comment("alias matching another world's name as it can cause confusion during world listing and selection in commands.")
.defaultValue(true)
.name("warn-alias-conflicts")
.build());
private final ConfigHeaderNode teleportHeader = node(ConfigHeaderNode.builder("teleport")
.comment("")
.comment("")
@@ -371,7 +382,8 @@ final class CoreConfigNodes {
.defaultValue(128)
.name("custom-portal-search-radius")
.validator(value -> value < 0
? Try.failure(new MultiverseException("The value must be greater than or equal to 0.", null))
? Try.failure(new MultiverseException(
Message.of(MVCorei18n.CORE_CONFIG_CUSTOMPORTALSEARCHRADIUS_NONNEGATIVE), null))
: Try.success(null))
.build());
@@ -487,7 +499,8 @@ final class CoreConfigNodes {
.defaultValue(30)
.name("confirm-timeout")
.validator(value -> (value <= 0)
? Try.failure(new MultiverseException("Confirm timeout must be a positive number!"))
? Try.failure(new MultiverseException(
Message.of(MVCorei18n.CORE_CONFIG_CONFIRMTIMEOUT_POSITIVE)))
: Try.success(null))
.build());
@@ -618,7 +631,8 @@ final class CoreConfigNodes {
.name("global-debug")
.suggester(input -> List.of("0", "1", "2", "3"))
.validator(value -> (value < 0 || value > 3)
? Try.failure(new MultiverseException("Debug level must be between 0 and 3."))
? Try.failure(new MultiverseException(
Message.of(MVCorei18n.CORE_CONFIG_DEBUGLEVEL_RANGE)))
: Try.success(null))
.onLoadAndChange((oldValue, newValue) -> {
if (newValue != Logging.getDebugLevel()) {
@@ -492,6 +492,7 @@ public class ConfigNode<T> extends ConfigHeaderNode implements ValueNode<T> {
* @deprecated Use {@link #onLoadAndChange(NodeChangeCallback)} instead.
*/
@Deprecated(since = "5.4", forRemoval = true)
@ApiStatus.ScheduledForRemoval(inVersion = "6.0")
public @NotNull B onSetValue(@NotNull BiConsumer<T, T> onSetValue) {
return onLoadAndChange(onSetValue::accept);
}
@@ -18,6 +18,8 @@ import org.mvplugins.multiverse.core.config.node.functions.SenderNodeSuggester;
import org.mvplugins.multiverse.core.config.node.serializer.DefaultSerializerProvider;
import org.mvplugins.multiverse.core.config.node.serializer.NodeSerializer;
import org.mvplugins.multiverse.core.exceptions.MultiverseException;
import org.mvplugins.multiverse.core.locale.MVCorei18n;
import org.mvplugins.multiverse.core.locale.message.Message;
import org.mvplugins.multiverse.core.utils.REPatterns;
import java.util.Collection;
@@ -28,6 +30,8 @@ import java.util.Objects;
import java.util.function.Function;
import java.util.function.Supplier;
import static org.mvplugins.multiverse.core.locale.message.MessageReplacement.replace;
/**
* A config node that contains key-value mappings.
*
@@ -127,8 +131,8 @@ public class MapConfigNode<K, V> extends ConfigNode<Map<K, V>> implements MapVal
private Function<K, Try<Void>> defaultYamlKeyValidator() {
return key -> Try.of(() -> {
if (!REPatterns.YAML_KEY.matcher(String.valueOf(serializeKey(key))).matches()) {
throw new MultiverseException("Invalid yaml key: '" + key + "'. Keys can only " +
"contain alphanumeric characters, underscores and hyphens.");
throw new MultiverseException(Message.of(MVCorei18n.CONFIG_NODE_INVALIDYAMLKEY,
replace("{key}").with(key)));
}
return null;
});
@@ -8,6 +8,10 @@ import co.aikar.commands.ACFUtil;
import io.vavr.control.Option;
import io.vavr.control.Try;
import org.mvplugins.multiverse.core.exceptions.MultiverseException;
import org.mvplugins.multiverse.core.locale.MVCorei18n;
import org.mvplugins.multiverse.core.locale.message.Message;
import static org.mvplugins.multiverse.core.locale.message.MessageReplacement.replace;
/**
* Provides default string parsers for common types.
@@ -51,24 +55,33 @@ public final class DefaultStringParserProvider {
() -> switch (String.valueOf(input).toLowerCase(Locale.ENGLISH)) {
case "t", "true", "on", "y", "yes", "1", "allow" -> true;
case "f", "false", "off", "n", "no", "0", "deny" -> false;
default -> throw new MultiverseException("Unable to convert '" + input + "' to boolean. Please use 'true' or 'false'");
default -> throw new MultiverseException(Message.of(MVCorei18n.CONFIG_STRING_PARSER_INVALIDBOOLEAN,
replace("{input}").with(input)));
});
private static final NodeStringParser<Integer> INTEGER_STRING_PARSER = (input, type) -> Try.of(
() -> ACFUtil.parseInt(input))
.flatMap(number -> Option.of(number).toTry(() -> new MultiverseException("Unable to convert '" + input + "' to number. (integer)")));
.flatMap(number -> Option.of(number).toTry(() -> new MultiverseException(Message.of(
MVCorei18n.CONFIG_STRING_PARSER_INVALIDINTEGER,
replace("{input}").with(input)))));
private static final NodeStringParser<Double> DOUBLE_STRING_PARSER = (input, type) -> Try.of(
() -> ACFUtil.parseDouble(input))
.flatMap(number -> Option.of(number).toTry(() -> new MultiverseException("Unable to convert '" + input + "' to number. (double)")));
.flatMap(number -> Option.of(number).toTry(() -> new MultiverseException(Message.of(
MVCorei18n.CONFIG_STRING_PARSER_INVALIDDOUBLE,
replace("{input}").with(input)))));
private static final NodeStringParser<Float> FLOAT_STRING_PARSER = (input, type) -> Try.of(
() -> ACFUtil.parseFloat(input))
.flatMap(number -> Option.of(number).toTry(() -> new MultiverseException("Unable to convert '" + input + "' to number. (float)")));
.flatMap(number -> Option.of(number).toTry(() -> new MultiverseException(Message.of(
MVCorei18n.CONFIG_STRING_PARSER_INVALIDFLOAT,
replace("{input}").with(input)))));
private static final NodeStringParser<Long> LONG_STRING_PARSER = (input, type) -> Try.of(
() -> ACFUtil.parseLong(input))
.flatMap(number -> Option.of(number).toTry(() -> new MultiverseException("Unable to convert '" + input + "' to number. (long)")));
.flatMap(number -> Option.of(number).toTry(() -> new MultiverseException(Message.of(
MVCorei18n.CONFIG_STRING_PARSER_INVALIDLONG,
replace("{input}").with(input)))));
static {
addDefaultStringParser(String.class, STRING_STRING_PARSER);
@@ -7,8 +7,11 @@ import org.bukkit.util.Vector;
import org.jetbrains.annotations.NotNull;
import org.mvplugins.multiverse.core.destination.DestinationInstance;
import org.mvplugins.multiverse.core.locale.MVCorei18n;
import org.mvplugins.multiverse.core.locale.message.Message;
import static org.mvplugins.multiverse.core.locale.message.MessageReplacement.replace;
/**
* Destination instance implementation for the {@link AnchorDestination}.
*/
@@ -69,8 +72,7 @@ public final class AnchorDestinationInstance extends DestinationInstance<AnchorD
*/
@Override
public @NotNull Message getDisplayMessage() {
//TODO Localize
return Message.of("anchor '" + anchorName + "'");
return Message.of(MVCorei18n.DESTINATION_ANCHOR_DISPLAY, replace("{anchor}").with(anchorName));
}
/**
@@ -9,7 +9,9 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.mvplugins.multiverse.core.destination.DestinationInstance;
import org.mvplugins.multiverse.core.locale.MVCorei18n;
import org.mvplugins.multiverse.core.locale.message.Message;
import org.mvplugins.multiverse.core.locale.message.MessageReplacement.Replace;
/**
* Destination instance implementation for the {@link BedDestination}.
@@ -70,8 +72,9 @@ public final class BedDestinationInstance extends DestinationInstance<BedDestina
*/
@Override
public @NotNull Message getDisplayMessage() {
//TODO Localize
return Message.of(player == null ? "your bed/respawn point" : player.getName() + "'s bed/respawn point");
return player == null
? Message.of(MVCorei18n.DESTINATION_BED_DISPLAY_OWN)
: Message.of(MVCorei18n.DESTINATION_BED_DISPLAY_OTHER, Replace.PLAYER.with(player.getName()));
}
/**
@@ -96,12 +96,14 @@ public final class ExactDestination implements Destination<ExactDestination, Exa
return Attempt.failure(InstanceFailureReason.INVALID_FORMAT);
}
String worldName = items[0];
String worldNameOrAlias = items[0];
String positionStr = items[1];
World world = getLoadedMultiverseWorld(worldName).flatMap(LoadedMultiverseWorld::getBukkitWorld).getOrNull();
World world = getLoadedMultiverseWorld(worldNameOrAlias)
.flatMap(LoadedMultiverseWorld::getBukkitWorld)
.getOrNull();
if (world == null) {
return Attempt.failure(InstanceFailureReason.WORLD_NOT_FOUND, Replace.WORLD.with(worldName));
return Attempt.failure(InstanceFailureReason.WORLD_NOT_FOUND, Replace.WORLD.with(worldNameOrAlias));
}
EntityPosition position;
@@ -111,7 +113,7 @@ public final class ExactDestination implements Destination<ExactDestination, Exa
return Attempt.failure(InstanceFailureReason.INVALID_NUMBER_FORMAT, Replace.ERROR.with(e));
}
return Attempt.success(new ExactDestinationInstance(this, worldManager, worldName, position));
return Attempt.success(new ExactDestinationInstance(this, worldManager, world.getName(), position));
}
//TODO: Extract to a world finder class
@@ -2,8 +2,6 @@ package org.mvplugins.multiverse.core.display.handlers;
import java.util.List;
import co.aikar.commands.BukkitCommandIssuer;
import org.bukkit.ChatColor;
import org.bukkit.command.ConsoleCommandSender;
import org.jetbrains.annotations.NotNull;
import org.mvplugins.multiverse.core.command.MVCommandIssuer;
@@ -69,12 +67,12 @@ public class PagedSendHandler extends BaseSendHandler<PagedSendHandler> {
private void sendPaged(@NotNull MVCommandIssuer issuer, @NotNull List<String> content) {
int totalPages = (content.size() + linesPerPage - 1) / linesPerPage; // Basically just divide round up
if (targetPage < 1 || targetPage > totalPages) {
issuer.sendMessage(String.format("%sInvalid page number. Please enter a page number between 1 and %s", ChatColor.RED, totalPages));
issuer.sendMessage(MVCorei18n.CONTENTDISPLAY_INVALIDPAGE, replace("{total}").with(totalPages));
return;
}
if (filter.needToFilter()) {
issuer.sendMessage("{page} {filter}",
issuer.sendMessage(MVCorei18n.CONTENTDISPLAY_PAGEFILTER,
replace("{page}").with(Message.of(MVCorei18n.CONTENTDISPLAY_PAGE,
replace("{current}").with(targetPage),
replace("{total}").with(totalPages))),
@@ -1,6 +1,5 @@
package org.mvplugins.multiverse.core.exceptions;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.mvplugins.multiverse.core.locale.message.LocalizableMessage;
@@ -2,6 +2,7 @@ package org.mvplugins.multiverse.core.exceptions.command;
import co.aikar.commands.InvalidCommandArgument;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.mvplugins.multiverse.core.locale.message.LocalizableMessage;
import org.mvplugins.multiverse.core.locale.message.LocalizedMessage;
import org.mvplugins.multiverse.core.locale.message.Message;
@@ -18,16 +19,20 @@ public class MVInvalidCommandArgument extends InvalidCommandArgument {
@ApiStatus.AvailableSince("5.7")
public static MVInvalidCommandArgument causeBy(Throwable throwable, boolean showSyntax) {
return (throwable instanceof LocalizableMessage localizableMessage)
? of(localizableMessage.getLocalizableMessage(), showSyntax)
: new MVInvalidCommandArgument(throwable.getLocalizedMessage(), showSyntax);
if (throwable instanceof LocalizableMessage localizableMessage) {
Message message = localizableMessage.getLocalizableMessage();
return message == null
? new MVInvalidCommandArgument(throwable.getLocalizedMessage(), showSyntax)
: of(message, showSyntax);
}
return new MVInvalidCommandArgument(throwable.getLocalizedMessage(), showSyntax);
}
public static MVInvalidCommandArgument of(Message message) {
public static MVInvalidCommandArgument of(@NotNull Message message) {
return of(message, true);
}
public static MVInvalidCommandArgument of(Message message, boolean showSyntax) {
public static MVInvalidCommandArgument of(@NotNull Message message, boolean showSyntax) {
return message instanceof LocalizedMessage
? new MVInvalidCommandArgument((LocalizedMessage) message, showSyntax)
: new MVInvalidCommandArgument(message, showSyntax);
@@ -42,6 +47,6 @@ public class MVInvalidCommandArgument extends InvalidCommandArgument {
}
private MVInvalidCommandArgument(LocalizedMessage message, boolean showSyntax) {
super(message.getMessageKey(), showSyntax, message.getReplacements());
super(message.getMessageKey(), showSyntax, message.getRawReplacements());
}
}
@@ -10,6 +10,7 @@ package org.mvplugins.multiverse.core.listeners;
import com.destroystokyo.paper.event.entity.PreCreatureSpawnEvent;
import com.dumptruckman.minecraft.util.Logging;
import jakarta.inject.Inject;
import org.bukkit.entity.Entity;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import org.bukkit.event.Listener;
@@ -19,14 +20,19 @@ import org.bukkit.event.entity.EntityRegainHealthEvent;
import org.bukkit.event.entity.EntityRegainHealthEvent.RegainReason;
import org.bukkit.event.entity.EntitySpawnEvent;
import org.bukkit.event.entity.FoodLevelChangeEvent;
import org.bukkit.event.world.ChunkLoadEvent;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jvnet.hk2.annotations.Service;
import org.mvplugins.multiverse.core.dynamiclistener.EventRunnable;
import org.mvplugins.multiverse.core.dynamiclistener.annotations.EventClass;
import org.mvplugins.multiverse.core.dynamiclistener.annotations.EventMethod;
import org.mvplugins.multiverse.core.utils.compatibility.EntityCompatibility;
import org.mvplugins.multiverse.core.world.WorldManager;
import java.util.Arrays;
/**
* Multiverse's Entity {@link Listener}.
*/
@@ -92,10 +98,7 @@ final class MVEntityListener implements CoreListener {
@Override
public void onEvent(PreCreatureSpawnEvent event) {
// Always allow custom command and plugins to spawn creatures
if (event.getReason() == SpawnReason.CUSTOM
|| event.getReason() == SpawnReason.COMMAND
|| event.getReason() == SpawnReason.BREEDING
|| event.getReason() == SpawnReason.SPAWNER_EGG) {
if (isAlwaysAllowedSpawnReason(event.getReason())) {
return;
}
@@ -123,16 +126,13 @@ final class MVEntityListener implements CoreListener {
}
// Always allow custom command and plugins to spawn creatures
if (event.getSpawnReason() == SpawnReason.CUSTOM
|| event.getSpawnReason() == SpawnReason.COMMAND
|| event.getSpawnReason() == SpawnReason.BREEDING
|| event.getSpawnReason() == SpawnReason.SPAWNER_EGG) {
if (isAlwaysAllowedSpawnReason(event.getSpawnReason())) {
return;
}
worldManager.getLoadedWorld(event.getEntity().getWorld())
.peek(world -> {
if (!world.getEntitySpawnConfig().shouldAllowSpawn(event.getEntity())) {
if (!world.getEntitySpawnConfig().shouldAllowSpawn(event.getEntityType())) {
Logging.finest("Cancelling Creature Spawn Event for: " + event.getEntity());
event.setCancelled(true);
}
@@ -151,10 +151,34 @@ final class MVEntityListener implements CoreListener {
worldManager.getLoadedWorld(event.getEntity().getWorld())
.peek(world -> {
if (!world.getEntitySpawnConfig().shouldAllowSpawn(event.getEntity())) {
if (!world.getEntitySpawnConfig().shouldAllowSpawn(event.getEntityType())) {
Logging.finest("Cancelling Entity Spawn Event for: " + event.getEntity());
event.setCancelled(true);
}
});
}
@EventMethod
void chunkLoad(ChunkLoadEvent event) {
worldManager.getLoadedWorld(event.getWorld())
.peek(world -> {
long count = Arrays.stream(event.getChunk().getEntities())
.filter(entity -> !(entity instanceof Player))
.filter(entity -> !isAlwaysAllowedSpawnReason(EntityCompatibility.getEntitySpawnReason(entity)))
.filter(entity -> !world.getEntitySpawnConfig().shouldAllowSpawn(entity))
.peek(Entity::remove)
.count();
if (count > 0) {
Logging.finest("Removed %d entities from chunk %d, %d in world %s due to spawn category settings.",
count, event.getChunk().getX(), event.getChunk().getZ(), event.getWorld().getName());
}
});
}
private boolean isAlwaysAllowedSpawnReason(@Nullable SpawnReason reason) {
return reason == SpawnReason.CUSTOM
|| reason == SpawnReason.COMMAND
|| reason == SpawnReason.BREEDING
|| reason == SpawnReason.SPAWNER_EGG;
}
}
@@ -20,6 +20,26 @@ public enum MVCorei18n implements MessageKeyProvider {
// configuration
CONFIG_SAVE_FAILED,
CONFIG_NODE_NOTFOUND,
CONFIG_NODE_INVALIDYAMLKEY,
CONFIG_STRING_PARSER_INVALIDBOOLEAN,
CONFIG_STRING_PARSER_INVALIDINTEGER,
CONFIG_STRING_PARSER_INVALIDDOUBLE,
CONFIG_STRING_PARSER_INVALIDFLOAT,
CONFIG_STRING_PARSER_INVALIDLONG,
CONFIG_STRING_PARSER_INVALIDMATERIAL,
// core config
CORE_CONFIG_CUSTOMPORTALSEARCHRADIUS_NONNEGATIVE,
CORE_CONFIG_CONFIRMTIMEOUT_POSITIVE,
CORE_CONFIG_DEBUGLEVEL_RANGE,
// /mv anchor
ANCHOR_DELETE_SUCCESS,
ANCHOR_DELETE_FAILURE,
ANCHOR_LIST_HEADER,
ANCHOR_LIST_ENTRY,
ANCHOR_SET_SUCCESS,
ANCHOR_SET_FAILURE,
// /mv check
CHECK_DESCRIPTION,
@@ -94,6 +114,15 @@ public enum MVCorei18n implements MessageKeyProvider {
DUMPS_STARTING,
DUMPS_URL_LIST,
// /mv entity-spawn-config
ENTITYSPAWNCONFIG_INFO_HEADER,
ENTITYSPAWNCONFIG_INFO_CATEGORY,
ENTITYSPAWNCONFIG_INFO_SPAWN,
ENTITYSPAWNCONFIG_INFO_TICKRATE,
ENTITYSPAWNCONFIG_INFO_EXCEPTIONS,
ENTITYSPAWNCONFIG_MODIFY_SUCCESS,
ENTITYSPAWNCONFIG_MODIFY_FAILURE,
// /mv gamerule set
GAMERULE_SET_DESCRIPTION,
GAMERULE_SET_GAMERULE_DESCRIPTION,
@@ -120,7 +149,12 @@ public enum MVCorei18n implements MessageKeyProvider {
// /mv generators
GENERATORS_DESCRIPTION,
GENERATORS_DESCRIPTION_FLAGS,
GENERATORS_HEADER,
GENERATORS_EMPTY,
GENERATORS_INFO_PLUGIN,
GENERATORS_INFO_EXAMPLEUSAGES,
GENERATORS_INFO_USAGES,
GENERATORS_INFO_INFOLINK,
// /mv import
IMPORT_DESCRIPTION,
@@ -149,7 +183,7 @@ public enum MVCorei18n implements MessageKeyProvider {
// /mv meta info
META_INFO_DESCRIPTION,
META_INFO_WORLD,
META_INFO_WORLD_DESCRIPTION,
META_INFO_HEADER,
META_INFO_NOCONTENT,
@@ -172,6 +206,13 @@ public enum MVCorei18n implements MessageKeyProvider {
MODIFY_FAILURE,
MODIFY_FAILURE_NOVALUE,
// /mv purge-all-entities
PURGEALLENTITIES_SUCCESS,
PURGEALLENTITIES_SUCCESS_CATEGORIES,
// /mv purge-entities
PURGEENTITIES_SUCCESS,
// /mv regen
REGEN_DESCRIPTION,
REGEN_WORLD_DESCRIPTION,
@@ -206,6 +247,8 @@ public enum MVCorei18n implements MessageKeyProvider {
// /mv spawn
SPAWN_DESCRIPTION,
SPAWN_PLAYER_DESCRIPTION,
SPAWN_NOTMVWORLD,
SPAWN_NOPERMISSION,
SPAWN_SUCCESS,
SPAWN_FAILED,
@@ -214,6 +257,9 @@ public enum MVCorei18n implements MessageKeyProvider {
TELEPORT_PLAYER_DESCRIPTION,
TELEPORT_DESTINATION_DESCRIPTION,
TELEPORT_TOOMANYPLAYERS,
TELEPORT_NOPERMISSION_SELF,
TELEPORT_NOPERMISSION_OTHER,
TELEPORT_NOPERMISSION_ALL,
TELEPORT_SUCCESS,
TELEPORT_FAILED,
@@ -269,6 +315,21 @@ public enum MVCorei18n implements MessageKeyProvider {
// commands error
COMMANDS_ERROR_PLAYERSONLY,
COMMANDS_ERROR_MULTIVERSEWORLDONLY,
COMMANDS_ERROR_LOADEDMULTIVERSEWORLD_PLAYERSONLY,
COMMANDS_ERROR_LOADEDMULTIVERSEWORLD_ISSUER,
COMMANDS_ERROR_LOADEDMULTIVERSEWORLD_INPUTCONSOLE,
COMMANDS_ERROR_LOADEDMULTIVERSEWORLD_INPUT,
COMMANDS_ERROR_MULTIVERSEWORLD_PLAYERSONLY,
COMMANDS_ERROR_MULTIVERSEWORLD_ISSUER,
COMMANDS_ERROR_MULTIVERSEWORLD_INPUTCONSOLE,
COMMANDS_ERROR_MULTIVERSEWORLD_INPUT,
COMMANDS_ERROR_PLAYER_ISSUERINPUT,
COMMANDS_ERROR_PLAYER_INPUT,
COMMANDS_ERROR_PLAYER_SELECTOR,
COMMANDS_ERROR_PLAYERS_INPUT,
COMMANDS_ERROR_RESOLVE_PLAYER,
COMMANDS_ERROR_RESOLVE_INPUT,
COMMANDS_ERROR_REMOVEPLAYERS_NODEFAULT,
// entry check
ENTRYCHECK_BLACKLISTED,
@@ -285,7 +346,10 @@ public enum MVCorei18n implements MessageKeyProvider {
// multiverse parse destination failure reason
DESTINATION_ANCHOR_FAILUREREASON_ANCHORNOTFOUND,
DESTINATION_ANCHOR_DISPLAY,
DESTINATION_BED_FAILUREREASON_PLAYERNOTFOUND,
DESTINATION_BED_DISPLAY_OWN,
DESTINATION_BED_DISPLAY_OTHER,
DESTINATION_CANNON_FAILUREREASON_INVALIDFORMAT,
DESTINATION_EXACT_FAILUREREASON_INVALIDFORMAT,
DESTINATION_PLAYER_FAILUREREASON_PLAYERNOTFOUND,
@@ -307,6 +371,11 @@ public enum MVCorei18n implements MessageKeyProvider {
TELEPORTFAILUREREASON_TELEPORT_FAILED_EXCEPTION,
TELEPORTFAILUREREASON_EVENT_CANCELLED,
// alias name conflict
ALIASNAMECONFLICT_DETECTED,
ALIASNAMECONFLICT_DUPLICATEALIAS,
ALIASNAMECONFLICT_DUPLICATEWORLDNAME,
// world manager result
CLONEWORLD_INVALIDWORLDNAME,
CLONEWORLD_WORLDEXISTFOLDER,
@@ -351,6 +420,9 @@ public enum MVCorei18n implements MessageKeyProvider {
WORLDCREATOR_BUKKITCREATIONFAILED,
// queue command result
QUEUECOMMAND_DEFAULTPROMPT,
QUEUECOMMAND_PROMPT,
QUEUECOMMAND_EXPIRED,
QUEUECOMMAND_NOCOMMANDINQUEUE,
QUEUECOMMAND_INVALIDOTP,
QUEUECOMMAND_COMMANDEXECUTIONERROR,
@@ -359,6 +431,7 @@ public enum MVCorei18n implements MessageKeyProvider {
CONTENTDISPLAY_NOCONTENT,
CONTENTDISPLAY_FILTER,
CONTENTDISPLAY_PAGE,
CONTENTDISPLAY_PAGEFILTER,
CONTENTDISPLAY_INVALIDPAGE,
CONTENTDISPLAY_NULL,
CONTENTDISPLAY_EMPTY,
@@ -384,8 +457,11 @@ public enum MVCorei18n implements MessageKeyProvider {
GENERIC_SUCCESS,
GENERIC_FAILURE,
GENERIC_ERROR,
GENERIC_ERROR_DETAILS,
GENERIC_NULL,
GENERIC_YOU,
GENERIC_PLAYERCOUNT,
GENERIC_TELEPORTPLAYERS_FAILED,
;
// END CHECKSTYLE-SUPPRESSION: Javadoc
@@ -1,17 +1,23 @@
package org.mvplugins.multiverse.core.locale.message;
import java.util.Objects;
import co.aikar.commands.ACFUtil;
import co.aikar.commands.CommandIssuer;
import co.aikar.commands.Locales;
import co.aikar.locales.MessageKey;
import co.aikar.locales.MessageKeyProvider;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public final class LocalizedMessage extends Message implements MessageKeyProvider {
private static @Nullable Locales locales;
@ApiStatus.Internal
public static void setDefaultLocalesManager(@Nullable Locales locales) {
LocalizedMessage.locales = locales;
}
private final @NotNull MessageKeyProvider messageKeyProvider;
LocalizedMessage(
@@ -22,11 +28,33 @@ public final class LocalizedMessage extends Message implements MessageKeyProvide
this.messageKeyProvider = messageKeyProvider;
}
/**
* {@inheritDoc}
*/
@Override
public MessageKey getMessageKey() {
return messageKeyProvider.getMessageKey();
}
/**
* {@inheritDoc}
*/
@Override
public @NotNull String[] getReplacements() {
return locales == null ? super.getReplacements() : getReplacements(locales, null);
}
/**
* {@inheritDoc}
*/
@Override
public @NotNull String formatted() {
return locales == null ? super.formatted() : formatted(locales, null);
}
/**
* {@inheritDoc}
*/
@Override
public @NotNull String formatted(@NotNull Locales locales, @Nullable CommandIssuer commandIssuer) {
String[] parsedReplacements = getReplacements(locales, commandIssuer);
@@ -6,9 +6,11 @@ import co.aikar.commands.ACFUtil;
import co.aikar.commands.CommandIssuer;
import co.aikar.commands.Locales;
import co.aikar.locales.MessageKeyProvider;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.mvplugins.multiverse.core.command.MVCommandManager;
/**
* A message that can be formatted with replacements and localized.
@@ -81,15 +83,45 @@ public sealed class Message permits LocalizedMessage {
}
/**
* Gets the replacements for this message.
* Gets the replacements for this message. This is the raw, non-localized parsing of replacements.
* <br/>
* This array is guaranteed to be of even length and suitable for use with
* {@link ACFUtil#replaceStrings(String, String...)}.
*
* @return The replacements
*
* @since 5.8
*/
@ApiStatus.AvailableSince("5.8")
public @NotNull String[] getRawReplacements() {
return toReplacementsArray(replacements);
}
/**
* Gets the replacements for this message with localization support when the plugin has loaded and injected its
* default {@link MVCommandManager#getLocales()} instance.
* Otherwise, it will fall back to use {@link getRawReplacements()}.
*
* @return The replacements
*/
public @NotNull String[] getReplacements() {
return toReplacementsArray(replacements);
return getRawReplacements();
}
/**
* Gets the replacements for this message with localization support based on the command issuer's locale config.
* <br/>
* This array is guaranteed to be of even length and suitable for use with
* {@link ACFUtil#replaceStrings(String, String...)}.
*
* @param issuer The command issuer the message is for
* @return The replacements
*
* @since 5.8
*/
@ApiStatus.AvailableSince("5.8")
public @NotNull String[] getReplacements(@NotNull CommandIssuer issuer) {
return getReplacements(issuer.getManager().getLocales(), issuer);
}
/**
@@ -116,20 +148,36 @@ public sealed class Message permits LocalizedMessage {
}
/**
* Gets the formatted message.
* Gets the raw formatted message.
* <br/>
* This is the raw, non-localized message with replacements applied.
* This method will never use the locale message key.
*
* @return The formatted message
*
* @since 5.8
*/
public @NotNull String formatted() {
String[] parsedReplacements = getReplacements();
@ApiStatus.AvailableSince("5.8")
public @NotNull String rawFormatted() {
String[] parsedReplacements = getRawReplacements();
if (parsedReplacements.length == 0) {
return raw();
}
return ACFUtil.replaceStrings(message, parsedReplacements);
}
/**
* Gets the formatted message.
* <br/>
* This is the localized message with replacements applied when the plugin has loaded and injected its default
* {@link MVCommandManager#getLocales()} instance. Otherwise, it will fall back to use {@link rawFormatted()}.
*
* @return The formatted message
*/
public @NotNull String formatted() {
return rawFormatted();
}
/**
* Gets the formatted message from localization data.
* <br/>
@@ -146,9 +194,9 @@ public sealed class Message permits LocalizedMessage {
* Gets the formatted message from localization data.
* <br/>
* This is the localized message with replacements applied. The message is localized using the locale of the given
* command issuer, if not null.
* command issuer.
*
* @param commandIssuer The command issuer the message is for, or null for the console (default locale)
* @param commandIssuer The command issuer the message is for.
* @return The formatted, localized message
*/
public @NotNull String formatted(@NotNull CommandIssuer commandIssuer) {
@@ -178,7 +226,7 @@ public sealed class Message permits LocalizedMessage {
int i = 0;
for (MessageReplacement replacement : replacements) {
replacementsArray[i++] = replacement.getKey();
replacementsArray[i++] = replacement.getReplacement().fold(s -> s, Message::formatted);
replacementsArray[i++] = replacement.getReplacement().fold(s -> s, Message::rawFormatted);
}
return replacementsArray;
}
@@ -264,7 +264,8 @@ public final class AsyncSafetyTeleporterAction {
Logging.finer("Teleported async %s to %s", teleportee.getName(), location);
return Attempt.success(null);
}
Logging.warning("Failed to async teleport %s to %s", teleportee.getName(), location);
Logging.warning("Failed to async teleport %s to %s. This is usually caused by another " +
"plugin canceling the teleport event and not a bug with Multiverse.", teleportee.getName(), location);
return Attempt.failure(TeleportFailureReason.TELEPORT_FAILED);
});
}
@@ -189,6 +189,7 @@ public final class ReflectHelper {
* the failure case more explicitly.
*/
@Deprecated(forRemoval = true, since = "5.7")
@ApiStatus.ScheduledForRemoval(inVersion = "6.0")
@Nullable
public static Class<?> getClass(String classPath) {
try {
@@ -211,6 +212,7 @@ public final class ReflectHelper {
* used to handle the failure case more explicitly.
*/
@Deprecated(forRemoval = true, since = "5.7")
@ApiStatus.ScheduledForRemoval(inVersion = "6.0")
@Nullable
public static <C> Method getMethod(Class<C> clazz, String methodName, Class<?>... parameterTypes) {
try {
@@ -235,6 +237,7 @@ public final class ReflectHelper {
* used to handle the failure case more explicitly.
*/
@Deprecated(forRemoval = true, since = "5.7")
@ApiStatus.ScheduledForRemoval(inVersion = "6.0")
@Nullable
public static <C> Method getMethod(C classInstance, String methodName, Class<?>... parameterTypes) {
return getMethod(classInstance.getClass(), methodName, parameterTypes);
@@ -254,6 +257,7 @@ public final class ReflectHelper {
* be used to handle the failure case more explicitly.
*/
@Deprecated(forRemoval = true, since = "5.7")
@ApiStatus.ScheduledForRemoval(inVersion = "6.0")
@Nullable
@SuppressWarnings("unchecked")
public static <C, R> R invokeMethod(C classInstance, Method method, Object...parameters) {
@@ -276,6 +280,7 @@ public final class ReflectHelper {
* handle the failure case more explicitly.
*/
@Deprecated(forRemoval = true, since = "5.7")
@ApiStatus.ScheduledForRemoval(inVersion = "6.0")
@Nullable
public static <C> Field getField(Class<C> clazz, String fieldName) {
try {
@@ -299,6 +304,7 @@ public final class ReflectHelper {
* handle the failure case more explicitly.
*/
@Deprecated(forRemoval = true, since = "5.7")
@ApiStatus.ScheduledForRemoval(inVersion = "6.0")
@Nullable
public static <C> Field getField(C classInstance, String fieldName) {
return getField(classInstance.getClass(), fieldName);
@@ -318,6 +324,7 @@ public final class ReflectHelper {
* used to handle the failure case more explicitly.
*/
@Deprecated(forRemoval = true, since = "5.7")
@ApiStatus.ScheduledForRemoval(inVersion = "6.0")
@Nullable
public static <C, V> V getFieldValue(C classInstance, @Nullable Field field, @NotNull Class<V> fieldType) {
try {
@@ -345,6 +352,7 @@ public final class ReflectHelper {
* which returns a {@link Try} that can be used to handle the failure case more explicitly.
*/
@Deprecated(forRemoval = true, since = "5.7")
@ApiStatus.ScheduledForRemoval(inVersion = "6.0")
@Nullable
public static <C, V> V getFieldValue(C classInstance, @Nullable String fieldName, @NotNull Class<V> fieldType) {
return getFieldValue(classInstance, getField(classInstance, fieldName), fieldType);
@@ -118,6 +118,20 @@ public final class StringFormatter {
* @return The parsed args
*/
public static @NotNull Collection<String> parseQuotesInArgs(@NotNull String[] args) {
return parseQuotesInArgs(args, " ");
}
/**
* Parse quotes in args into a single string. E.g. ["\"my", "string\""] -> ["my string"]
*
* @param args The args to parse
* @param separator The separator to use between args when joining them
* @return The parsed args
*
* @since 5.8
*/
@ApiStatus.AvailableSince("5.8")
public static @NotNull Collection<String> parseQuotesInArgs(@NotNull String[] args, @NotNull String separator) {
List<String> result = new ArrayList<>(args.length);
StringBuilder current = new StringBuilder();
boolean inQuotes = false;
@@ -131,13 +145,13 @@ public final class StringFormatter {
quoteStartIndex = i;
current.append(arg.substring(1));
} else if (inQuotes && arg.endsWith("\"")) {
current.append(" ").append(arg, 0, arg.length() - 1);
current.append(separator).append(arg, 0, arg.length() - 1);
result.add(current.toString());
current.setLength(0);
inQuotes = false;
quoteStartIndex = -1;
} else if (inQuotes) {
current.append(" ").append(arg);
current.append(separator).append(arg);
} else if (arg.startsWith("\"") && arg.endsWith("\"") && arg.length() > 1) {
// Fully quoted in one token
result.add(arg.substring(1, arg.length() - 1));
@@ -0,0 +1,40 @@
package org.mvplugins.multiverse.core.utils.compatibility;
import org.bukkit.entity.Entity;
import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.mvplugins.multiverse.core.utils.ReflectHelper;
/**
* Compatibility class used to handle API changes in {@link Entity} class.
*/
// TODO: Consider making this part of the public API in v5.9.
@ApiStatus.Internal
public final class EntityCompatibility {
private static final boolean HAS_GET_ENTITY_SPAWN_REASON_METHOD;
static {
HAS_GET_ENTITY_SPAWN_REASON_METHOD = ReflectHelper.hasMethod(Entity.class, "getEntitySpawnReason");
}
/**
* Gets the reason that initially spawned the entity when supported by the server.
*
* @param entity The entity to query.
* @return The entity's spawn reason, or null when the API is unavailable.
*/
@Nullable
public static SpawnReason getEntitySpawnReason(@NotNull Entity entity) {
if (HAS_GET_ENTITY_SPAWN_REASON_METHOD) {
return entity.getEntitySpawnReason();
}
return null;
}
private EntityCompatibility() {
throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");
}
}
@@ -49,7 +49,7 @@ public sealed interface Attempt<T, F extends FailureReason> permits Attempt.Succ
@ApiStatus.AvailableSince("5.7")
static <T, F extends FailureReason> Attempt.Failure<T, F> failureRef(
F failureReason, MessageReplacement... messageReplacements) {
return new Failure<>(failureReason, Message.of(failureReason, "Failed!", messageReplacements));
return new Failure<>(failureReason, Message.of(failureReason, messageReplacements));
}
/**
@@ -220,7 +220,7 @@ public sealed interface Result<S extends SuccessReason, F extends FailureReason>
Success(S successReason, MessageReplacement[] replacements) {
this.successReason = successReason;
this.message = Message.of(successReason, "Success!", replacements);
this.message = Message.of(successReason, replacements);
}
@Override
@@ -273,7 +273,7 @@ public sealed interface Result<S extends SuccessReason, F extends FailureReason>
Failure(F failureReason, MessageReplacement[] replacements) {
this.failureReason = failureReason;
this.message = Message.of(failureReason, "Failed!", replacements);
this.message = Message.of(failureReason, replacements);
}
@Override
@@ -55,6 +55,7 @@ public final class LoadedMultiverseWorld extends MultiverseWorld {
worldConfig.load();
worldConfig.setEnvironment(world.getEnvironment());
worldConfig.setSeed(world.getSeed());
worldConfig.setLegacyWorldName(world.getName());
}
private void setupSpawnLocation(World world) {
@@ -49,7 +49,7 @@ final class WorldConfig {
@NotNull ConfigurationSection configSection,
@NotNull MultiverseCore multiverseCore) {
this.keyOrName = keyOrName;
this.configNodes = new WorldConfigNodes(multiverseCore);
this.configNodes = new WorldConfigNodes(multiverseCore, keyOrName);
this.configHandle = MemoryConfigurationHandle.builder(configSection, configNodes.getNodes())
.logger(Logging.getLogger())
.migrator(migrator())
@@ -6,6 +6,7 @@ import java.util.Objects;
import com.dumptruckman.minecraft.util.Logging;
import io.vavr.control.Option;
import io.vavr.control.Try;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Difficulty;
@@ -17,6 +18,7 @@ import org.bukkit.configuration.MemoryConfiguration;
import org.jetbrains.annotations.NotNull;
import org.mvplugins.multiverse.core.MultiverseCore;
import org.mvplugins.multiverse.core.command.MVCommandManager;
import org.mvplugins.multiverse.core.config.CoreConfig;
import org.mvplugins.multiverse.core.config.node.MapConfigNode;
import org.mvplugins.multiverse.core.config.node.serializer.NodeSerializer;
@@ -25,13 +27,20 @@ import org.mvplugins.multiverse.core.config.node.ConfigNode;
import org.mvplugins.multiverse.core.config.node.ListConfigNode;
import org.mvplugins.multiverse.core.config.node.NodeGroup;
import org.mvplugins.multiverse.core.economy.MVEconomist;
import org.mvplugins.multiverse.core.exceptions.MultiverseException;
import org.mvplugins.multiverse.core.locale.MVCorei18n;
import org.mvplugins.multiverse.core.locale.message.Message;
import org.mvplugins.multiverse.core.utils.MaterialConverter;
import org.mvplugins.multiverse.core.utils.text.ChatTextFormatter;
import org.mvplugins.multiverse.core.world.helpers.AliasNameConflictChecker;
import org.mvplugins.multiverse.core.world.helpers.EnforcementHandler;
import org.mvplugins.multiverse.core.world.key.WorldKeyOrName;
import org.mvplugins.multiverse.core.world.location.NullSpawnLocation;
import org.mvplugins.multiverse.core.world.location.SpawnLocation;
import org.mvplugins.multiverse.core.world.entity.EntitySpawnConfig;
import static org.mvplugins.multiverse.core.locale.message.MessageReplacement.replace;
/**
* Represents nodes in a world configuration.
*/
@@ -42,12 +51,18 @@ final class WorldConfigNodes {
private WorldManager worldManager;
private EnforcementHandler enforcementHandler;
private CoreConfig config;
private AliasNameConflictChecker aliasNameConflictChecker;
private MVCommandManager commandManager;
private WorldKeyOrName keyOrName;
private MultiverseWorld world = null;
WorldConfigNodes(@NotNull MultiverseCore multiverseCore) {
WorldConfigNodes(@NotNull MultiverseCore multiverseCore, @NotNull WorldKeyOrName keyOrName) {
this.worldManager = multiverseCore.getServiceLocator().getService(WorldManager.class);
this.enforcementHandler = multiverseCore.getServiceLocator().getService(EnforcementHandler.class);
this.config = multiverseCore.getServiceLocator().getService(CoreConfig.class);
this.aliasNameConflictChecker = multiverseCore.getServiceLocator().getService(AliasNameConflictChecker.class);
this.commandManager = multiverseCore.getServiceLocator().getService(MVCommandManager.class);
this.keyOrName = keyOrName;
}
MultiverseWorld getWorld() {
@@ -90,11 +105,18 @@ final class WorldConfigNodes {
final ConfigNode<String> alias = node(ConfigNode.builder("alias", String.class)
.defaultValue("")
.onLoadAndChange((oldValue, newValue) -> {
.onLoadAndChange((sender, oldValue, newValue) -> {
worldManager.getWorldStore().changeAlias(
ChatTextFormatter.removeColor(oldValue),
ChatTextFormatter.removeColor(newValue),
keyOrName.usableKey()
);
if (world == null) return;
world.updateColourlessAlias();
worldManager.getWorldStore().changeAlias(
ChatTextFormatter.removeColor(oldValue), ChatTextFormatter.removeColor(newValue), world);
if (config.getWarnAliasConflicts()) {
aliasNameConflictChecker.checkDuplicateFor(world)
.sendConflictMessage(commandManager.getCommandIssuer(sender));
}
}));
final ConfigNode<Boolean> allowAdvancementGrant = node(ConfigNode.builder("allow-advancement-grant", Boolean.class)
@@ -153,11 +175,22 @@ final class WorldConfigNodes {
final ConfigNode<Material> entryFeeCurrency = node(ConfigNode.builder("entry-fee.currency", Material.class)
.defaultValue(MVEconomist.VAULT_ECONOMY_MATERIAL)
.name("entryfee-currency")
.stringParser((value, clazz) -> {
if (value != null && value.equalsIgnoreCase(MVEconomist.VAULT_ECONOMY_CODE)) {
return Try.success(MVEconomist.VAULT_ECONOMY_MATERIAL);
}
return Try.of(() -> MaterialConverter.stringToMaterial(value))
.filter(Objects::nonNull)
.orElse(Try.failure(new MultiverseException(Message.of(
MVCorei18n.CONFIG_STRING_PARSER_INVALIDMATERIAL,
replace("{input}").with(value)))));
})
.serializer(new NodeSerializer<>() {
@Override
public Material deserialize(Object object, Class<Material> type) {
return Option.of(object)
.map(String::valueOf)
.filter(s -> !s.isBlank())
.map(materialStr -> {
if (materialStr.equalsIgnoreCase(MVEconomist.VAULT_ECONOMY_CODE)) {
return MVEconomist.VAULT_ECONOMY_MATERIAL;
@@ -290,6 +323,8 @@ final class WorldConfigNodes {
.hidden());
final ConfigNode<String> legacyWorldName = node(ConfigNode.builder("read-only.legacy-world-name", String.class)
.onLoadAndChange((oldValue, newValue) -> worldManager.getWorldStore().changeLegacyWorldName(
oldValue, newValue, keyOrName.usableKey()))
.hidden());
final ConfigNode<Long> seed = node(ConfigNode.builder("read-only.seed", Long.class)
@@ -54,6 +54,7 @@ import org.mvplugins.multiverse.core.utils.compatibility.WorldCreatorCompatibili
import org.mvplugins.multiverse.core.utils.result.Attempt;
import org.mvplugins.multiverse.core.utils.result.FailureReason;
import org.mvplugins.multiverse.core.utils.FileUtils;
import org.mvplugins.multiverse.core.utils.text.ChatTextFormatter;
import org.mvplugins.multiverse.core.world.biomeprovider.BiomeProviderFactory;
import org.mvplugins.multiverse.core.world.entity.EntityPurger;
import org.mvplugins.multiverse.core.world.generators.GeneratorProvider;
@@ -154,10 +155,14 @@ public final class WorldManager {
*/
@ApiStatus.Internal
public Try<Void> initAllWorlds() {
return updateWorldsFromConfig().andThenTry(() -> {
importExistingWorlds();
autoLoadWorlds();
}).flatMap(ignore -> saveWorldsConfig());
return updateWorldsFromConfig()
.andThenTry(this::importExistingWorlds)
.andThenTry(this::autoLoadWorlds)
.flatMap(ignore -> saveWorldsConfig())
.onFailure(ex -> {
Logging.severe("Failed to load worlds from config: %s", ex.getMessage());
ex.printStackTrace();
});
}
/**
@@ -186,7 +191,7 @@ public final class WorldManager {
private void removeWorldsNotInConfigs(Collection<WorldKeyOrName> removedWorlds) {
removedWorlds.forEach(keyOrName -> getWorld(keyOrName.usableName())
.map(world -> removeWorld(RemoveWorldOptions.world(world)))
.getOrElse(() -> worldActionResult(RemoveFailureReason.WORLD_NON_EXISTENT, keyOrName.toString()))
.getOrElse(() -> worldActionResult(RemoveFailureReason.WORLD_NON_EXISTENT, keyOrName))
.onFailure(failure ->
Logging.severe("Failed to unload world %s: %s", keyOrName, failure))
.onSuccess(success ->
@@ -240,7 +245,10 @@ public final class WorldManager {
.filter(world -> !isLoadedWorld(world) && world.isAutoLoad())
.forEach(world -> loadWorld(LoadWorldOptions.world(world))
.onFailure(failure ->
Logging.severe("Failed to autoload world %s: %s", world.getName(), failure))
Logging.severe("Failed to autoload world '%s': %s: %s",
world.getName(),
failure,
ChatTextFormatter.removeColor(failure.getFailureMessage().formatted())))
.onSuccess(newMVWorld ->
Logging.fine("Autoloaded world %s", newMVWorld.getName())));
}
@@ -278,15 +286,24 @@ public final class WorldManager {
WorldKeyOrName keyOrName = keyOrNameWithOptions.keyOrName();
CreateWorldOptions options = keyOrNameWithOptions.options();
String generatorString = generatorProvider.parseGeneratorString(keyOrName.usableName(), options.generator());
WorldCreator worldCreator = WorldCreatorCompatibility.ofKeyOrName(keyOrName)
.environment(options.environment())
.generateStructures(options.generateStructures())
.generatorSettings(options.generatorSettings())
.seed(options.seed())
.type(options.worldType());
WorldCreatorCompatibility.setBonusChest(worldCreator, options.bonusChest());
options.forcedSpawnPosition().peek(position -> WorldCreatorCompatibility.setForcedSpawnPosition(worldCreator, position));
return addBiomeProviderToCreator(worldCreator, keyOrName.usableName(), options.biome())
return Try.of(() -> {
WorldCreator creator = WorldCreatorCompatibility.ofKeyOrName(keyOrName)
.environment(options.environment())
.generateStructures(options.generateStructures())
.generatorSettings(options.generatorSettings())
.seed(options.seed())
.type(options.worldType());
WorldCreatorCompatibility.setBonusChest(creator, options.bonusChest());
options.forcedSpawnPosition()
.peek(position -> WorldCreatorCompatibility.setForcedSpawnPosition(creator, position));
return creator;
})
.fold(throwable -> Attempt.<WorldCreator, WorldCreatorFailureReason>failure(WorldCreatorFailureReason.BUKKIT_CREATION_FAILED,
Replace.WORLD.with(keyOrName.usableName()),
Replace.ERROR.with(throwable)),
Attempt::<WorldCreator, WorldCreatorFailureReason>success)
.mapAttempt(creator -> addBiomeProviderToCreator(creator, keyOrName.usableName(), options.biome()))
.mapAttempt(creator -> addGeneratorToCreator(creator, generatorString))
.mapAttempt(this::createBukkitWorld)
.transform(CreateFailureReason.WORLD_CREATOR_FAILED)
@@ -355,11 +372,15 @@ public final class WorldManager {
WorldKeyOrName keyOrName = keyOrNameWithOptions.keyOrName();
ImportWorldOptions options = keyOrNameWithOptions.options();
String generatorString = generatorProvider.parseGeneratorString(keyOrName.usableName(), options.generator());
WorldCreator worldCreator = WorldCreatorCompatibility.ofKeyOrName(keyOrName)
.environment(options.environment())
.generatorSettings(options.generatorSettings());
return addBiomeProviderToCreator(worldCreator, keyOrName.usableName(), options.biome())
return Try.of(() -> WorldCreatorCompatibility.ofKeyOrName(keyOrName)
.environment(options.environment())
.generatorSettings(options.generatorSettings()))
.fold(throwable -> Attempt.<WorldCreator, WorldCreatorFailureReason>failure(WorldCreatorFailureReason.BUKKIT_CREATION_FAILED,
Replace.WORLD.with(keyOrName.usableName()),
Replace.ERROR.with(throwable)),
Attempt::<WorldCreator, WorldCreatorFailureReason>success)
.mapAttempt(creator -> addBiomeProviderToCreator(creator, keyOrName.usableName(), options.biome()))
.mapAttempt(creator -> addGeneratorToCreator(creator, generatorString))
.mapAttempt(this::createBukkitWorld)
.transform(ImportFailureReason.WORLD_CREATOR_FAILED)
@@ -524,10 +545,14 @@ public final class WorldManager {
}
}
WorldCreator worldCreator = WorldCreatorCompatibility.ofNameAndKey(mvWorld.getKey(), mvWorld.getName())
.environment(mvWorld.getEnvironment())
.seed(mvWorld.getSeed());
return addBiomeProviderToCreator(worldCreator, mvWorld.getName(), mvWorld.getBiome())
return Try.of(() -> WorldCreatorCompatibility.ofNameAndKey(mvWorld.getKey(), mvWorld.getName())
.environment(mvWorld.getEnvironment())
.seed(mvWorld.getSeed()))
.fold(throwable -> Attempt.<WorldCreator, WorldCreatorFailureReason>failure(WorldCreatorFailureReason.BUKKIT_CREATION_FAILED,
Replace.WORLD.with(mvWorld.getName()),
Replace.ERROR.with(throwable)),
Attempt::<WorldCreator, WorldCreatorFailureReason>success)
.mapAttempt(creator -> addBiomeProviderToCreator(creator, mvWorld.getName(), mvWorld.getBiome()))
.mapAttempt(creator -> addGeneratorToCreator(creator, mvWorld.getGenerator()))
.mapAttempt(this::createBukkitWorld)
.transform(LoadFailureReason.WORLD_CREATOR_FAILED)
@@ -1068,7 +1093,8 @@ public final class WorldManager {
* @return The world if it exists.
*/
public Option<MultiverseWorld> getWorldByNameOrAlias(@Nullable String worldNameOrAlias) {
return getWorld(worldStore.translateAlias(worldNameOrAlias));
return getWorld(worldNameOrAlias)
.orElse(() -> getWorld(worldStore.translateAlias(worldNameOrAlias)));
}
/**
@@ -1116,7 +1142,8 @@ public final class WorldManager {
* @return The world if it exists.
*/
public Option<MultiverseWorld> getUnloadedWorldByNameOrAlias(@Nullable String worldNameOrAlias) {
return getUnloadedWorld(worldStore.translateAlias(worldNameOrAlias));
return getUnloadedWorld(worldNameOrAlias)
.orElse(() -> getUnloadedWorld(worldStore.translateAlias(worldNameOrAlias)));
}
/**
@@ -1177,7 +1204,8 @@ public final class WorldManager {
* @return The multiverse world if it exists.
*/
public Option<LoadedMultiverseWorld> getLoadedWorldByNameOrAlias(@Nullable String worldNameOrAlias) {
return getLoadedWorld(worldStore.translateAlias(worldNameOrAlias));
return getLoadedWorld(worldNameOrAlias)
.orElse(() -> getLoadedWorld(worldStore.translateAlias(worldNameOrAlias)));
}
/**
@@ -15,6 +15,7 @@ import org.mvplugins.multiverse.core.world.key.WorldKeyOrName;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
@@ -119,16 +120,39 @@ final class WorldStore {
unloadedList.add(unloadedMap.get(world.getKey().toString()));
}
void changeAlias(@Nullable String oldAlias, @Nullable String newAlias, @NotNull MultiverseWorld world) {
void changeLegacyWorldName(@Nullable String oldName, @Nullable String newName, @NotNull NamespacedKey worldKey) {
if (Objects.equals(oldName, newName)) {
return;
}
String key = worldKey.toString();
updateLegacyWorldName(loadedMap, oldName, newName, key);
updateLegacyWorldName(unloadedMap, oldName, newName, key);
}
private <T extends MultiverseWorld> void updateLegacyWorldName(
Map<String, T> map, @Nullable String oldName, @Nullable String newName, String key) {
T world = map.get(key);
if (world == null) {
return;
}
if (oldName != null && !key.equalsIgnoreCase(oldName)) {
map.remove(oldName, world);
}
if (newName != null) {
map.put(newName, world);
}
}
void changeAlias(@Nullable String oldAlias, @Nullable String newAlias, @NotNull NamespacedKey worldKey) {
if (Objects.equals(oldAlias, newAlias)) {
// nothing changed, ignore
return;
}
if (!Strings.isNullOrEmpty(oldAlias)) {
aliasMap.remove(oldAlias, world.getKey().toString());
aliasMap.remove(oldAlias.toLowerCase(Locale.ROOT), worldKey.toString());
}
if (!Strings.isNullOrEmpty(newAlias)) {
aliasMap.put(newAlias, world.getKey().toString());
aliasMap.put(newAlias.toLowerCase(Locale.ROOT), worldKey.toString());
}
}
@@ -263,7 +287,7 @@ final class WorldStore {
return null;
}
//TODO: Not sure if we should fail if there is multiple alias of the same name, but for now just return the first one
return aliasMap.get(worldNameOrAlias).stream()
return aliasMap.get(worldNameOrAlias.toLowerCase(Locale.ROOT)).stream()
.findFirst()
.orElse(worldNameOrAlias);
}
@@ -18,7 +18,9 @@ import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Service;
import org.mvplugins.multiverse.core.MultiverseCore;
import org.mvplugins.multiverse.core.utils.ServerProperties;
import org.mvplugins.multiverse.core.utils.result.Attempt;
import org.mvplugins.multiverse.core.world.helpers.DimensionFinder;
import org.mvplugins.multiverse.core.world.key.WorldKeyOrName;
import org.mvplugins.multiverse.core.world.key.WorldKeyParseFailReason;
@@ -34,16 +36,18 @@ final class WorldsConfigManager {
private final SortedMap<WorldKeyOrName, WorldConfig> worldConfigMap;
private final File worldConfigFile;
private final ServerProperties serverProperties;
private YamlConfiguration worldsConfig;
private final MultiverseCore multiverseCore;
@Inject
WorldsConfigManager(@NotNull MultiverseCore core, @NotNull MultiverseCore multiverseCore) {
WorldsConfigManager(@NotNull MultiverseCore multiverseCore, @NotNull ServerProperties serverProperties) {
worldConfigMap = new TreeMap<>();
worldConfigFile = core.getDataFolder().toPath().resolve(CONFIG_FILENAME).toFile();
worldConfigFile = multiverseCore.getDataFolder().toPath().resolve(CONFIG_FILENAME).toFile();
this.multiverseCore = multiverseCore;
this.serverProperties = serverProperties;
}
/**
@@ -202,9 +206,59 @@ final class WorldsConfigManager {
worldConfigMap.remove(s);
}
migrateLevelNameChange();
return new NewAndRemovedWorlds(newWorldsAdded, worldsRemoved);
}
private void migrateLevelNameChange() {
serverProperties.getLevelName()
.peek(levelName -> {
boolean didMigrate = migrateLevelNameChange(
WorldKeyOrName.parseKey(NamespacedKey.minecraft("overworld")),
levelName
);
didMigrate = migrateLevelNameChange(
WorldKeyOrName.parseKey(NamespacedKey.minecraft("the_nether")),
DimensionFinder.DEFAULT_NETHER_FORMAT.replaceOverworld(levelName)
) || didMigrate;
didMigrate = migrateLevelNameChange(
WorldKeyOrName.parseKey(NamespacedKey.minecraft("the_end")),
DimensionFinder.DEFAULT_END_FORMAT.replaceOverworld(levelName)
) || didMigrate;
if (didMigrate) {
Logging.warning("We have detected and migrated your default world's name due to a level-name change.");
Logging.warning("Please check your worlds.yml file and world list to ensure everything is correct.");
Logging.warning("Additionally, all references to the old world name will no longer work. " +
"Please update all references within your configuration files/plugins.");
}
});
}
private boolean migrateLevelNameChange(WorldKeyOrName keyOrName, String newWorldName) {
return getWorldConfig(keyOrName).map(worldConfig -> {
if (newWorldName.equals(worldConfig.getLegacyWorldName())) {
return false;
}
boolean hasConflictKey = WorldKeyOrName.parseKey(newWorldName).map(this::getWorldConfig)
.fold(failure -> false, Option::isDefined);
if (hasConflictKey) {
Logging.severe("Unable to migrate default world's name caused by level-name change.");
Logging.severe("We have detected a clash between the default name '%s' and an existing non-default world in your worlds.yml file.", newWorldName);
Logging.severe("Please change the level-name in server.properties to something else and restart the server.");
return false;
}
Logging.info("Updating legacy world name for %s from %s to %s due to level-name change.",
keyOrName, worldConfig.getLegacyWorldName(), newWorldName);
worldConfig.setLegacyWorldName(newWorldName);
worldConfig.save();
return true;
}).getOrElse(false);
}
/**
* Whether the worlds.yml file has been loaded.
*
@@ -227,7 +281,7 @@ final class WorldsConfigManager {
worldsConfig = new YamlConfiguration();
worldConfigMap.forEach((keyOrName, worldConfig) -> {
worldConfig.save().onFailure(e -> {
throw new RuntimeException("Failed to save world %s in config: " + keyOrName, e);
throw new RuntimeException("Failed to save world " + keyOrName + " in config: " + e.getMessage(), e);
});
worldsConfig.set(encodeConfigKey(keyOrName), worldConfig.getConfigurationSection());
});
@@ -7,58 +7,35 @@ import org.jvnet.hk2.annotations.Service;
import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Predicate;
@Service
public final class EntityPurger {
public int purgeEntities(LoadedMultiverseWorld world) {
AtomicInteger purgeCount = new AtomicInteger(0);
world.getBukkitWorld().peek(bukkitWorld -> {
for (Entity entity : bukkitWorld.getEntities()) {
if (!world.getEntitySpawnConfig().shouldAllowSpawn(entity)) {
entity.remove();
purgeCount.incrementAndGet();
}
}
});
return purgeCount.get();
return purgeEntitiesWithCondition(world, entity -> !world.getEntitySpawnConfig().shouldAllowSpawn(entity));
}
public int purgeEntities(LoadedMultiverseWorld world, SpawnCategory spawnCategory) {
AtomicInteger purgeCount = new AtomicInteger(0);
world.getBukkitWorld().peek(bukkitWorld -> {
for (Entity entity : bukkitWorld.getEntities()) {
if (entity.getSpawnCategory() == spawnCategory) {
entity.remove();
purgeCount.incrementAndGet();
}
}
});
return purgeCount.get();
return purgeEntitiesWithCondition(world, entity -> entity.getSpawnCategory().equals(spawnCategory));
}
public int purgeEntities(LoadedMultiverseWorld world, SpawnCategory... spawnCategories) {
Set<SpawnCategory> spawnCategoriesSet = Set.of(spawnCategories);
AtomicInteger purgeCount = new AtomicInteger(0);
world.getBukkitWorld().peek(bukkitWorld -> bukkitWorld.getEntities().stream()
.filter(entity -> !(entity instanceof Player))
.filter(entity -> spawnCategoriesSet.contains(entity.getSpawnCategory()))
.forEach(entity -> {
entity.remove();
purgeCount.incrementAndGet();
}));
return purgeCount.get();
return purgeEntitiesWithCondition(world, entity -> spawnCategoriesSet.contains(entity.getSpawnCategory()));
}
public int purgeAllEntities(LoadedMultiverseWorld world) {
AtomicInteger purgeCount = new AtomicInteger(0);
world.getBukkitWorld().peek(bukkitWorld -> bukkitWorld.getEntities().stream()
.filter(entity -> !(entity instanceof Player))
.forEach(entity -> {
entity.remove();
purgeCount.incrementAndGet();
}));
return purgeCount.get();
return purgeEntitiesWithCondition(world, entity -> true);
}
private int purgeEntitiesWithCondition(LoadedMultiverseWorld world, Predicate<Entity> condition) {
return Math.toIntExact(world.getBukkitWorld()
.map(bukkitWorld -> bukkitWorld.getEntities().stream()
.filter(entity -> !(entity instanceof Player))
.filter(condition)
.peek(Entity::remove)
.count())
.getOrElse(0L));
}
}
@@ -7,6 +7,7 @@ import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.SpawnCategory;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.mvplugins.multiverse.core.config.CoreConfig;
import org.mvplugins.multiverse.core.utils.StringFormatter;
import org.mvplugins.multiverse.core.world.MultiverseWorld;
@@ -20,12 +21,13 @@ public final class EntitySpawnConfig {
private final CoreConfig config;
private final Map<SpawnCategory, SpawnCategoryConfig> spawnCategoriesConfig;
EntitySpawnConfig(CoreConfig config, Map<SpawnCategory, SpawnCategoryConfig> spawnCategoriesConfig) {
EntitySpawnConfig(@NotNull CoreConfig config,
@NotNull Map<SpawnCategory, SpawnCategoryConfig> spawnCategoriesConfig) {
this.config = config;
this.spawnCategoriesConfig = spawnCategoriesConfig;
}
public SpawnCategoryConfig getSpawnCategoryConfig(SpawnCategory spawnCategory) {
public @NotNull SpawnCategoryConfig getSpawnCategoryConfig(@NotNull SpawnCategory spawnCategory) {
return spawnCategoriesConfig.computeIfAbsent(spawnCategory,
computeSpawnCategory -> new SpawnCategoryConfig(
config,
@@ -35,12 +37,16 @@ public final class EntitySpawnConfig {
}
@ApiStatus.AvailableSince("5.7")
public boolean shouldAllowSpawn(EntityType entityType) {
return getSpawnCategoryConfig(SpawnCategoryMapper.getSpawnCategory(entityType)).shouldAllowSpawn(entityType);
public boolean shouldAllowSpawn(@NotNull EntityType entityType) {
// Ensure it defaults to true if SpawnCategoryMapper fails to find a spawn category for the entity type,
// to avoid accidentally breaking mob spawns.
return SpawnCategoryMapper.getSpawnCategory(entityType)
.map(spawnCategory -> getSpawnCategoryConfig(spawnCategory).shouldAllowSpawn(entityType))
.getOrElse(true);
}
public boolean shouldAllowSpawn(Entity entity) {
return getSpawnCategoryConfig(entity.getSpawnCategory()).shouldAllowSpawn(entity);
public boolean shouldAllowSpawn(@NotNull Entity entity) {
return shouldAllowSpawn(entity.getType());
}
public void applyConfigToWorld() {
@@ -57,16 +63,19 @@ public final class EntitySpawnConfig {
}
@ApiStatus.Internal
public ConfigurationSection toSection() {
public @NotNull ConfigurationSection toSection() {
MemoryConfiguration section = new MemoryConfiguration();
spawnCategoriesConfig.forEach((spawnCategory, spawnCategoryConfig) -> {
section.set(spawnCategory.toString().toLowerCase(Locale.ENGLISH), spawnCategoryConfig.saveSection());
section.set(String.valueOf(spawnCategory).toLowerCase(Locale.ENGLISH), spawnCategoryConfig.saveSection());
});
return section;
}
@ApiStatus.Internal
public static EntitySpawnConfig fromSection(CoreConfig config, ConfigurationSection section) {
public static @NotNull EntitySpawnConfig fromSection(
@NotNull CoreConfig config,
@NotNull ConfigurationSection section
) {
Map<SpawnCategory, SpawnCategoryConfig> spawnCategoriesConfig = new LinkedHashMap<>();
Map<String, Object> existingCategories = section.getValues(false);
for (SpawnCategory category : SpawnCategory.values()) {
@@ -8,6 +8,8 @@ import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.SpawnCategory;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.mvplugins.multiverse.core.config.CoreConfig;
import org.mvplugins.multiverse.core.config.handle.MemoryConfigurationHandle;
import org.mvplugins.multiverse.core.config.handle.StringPropertyHandle;
@@ -15,13 +17,19 @@ import org.mvplugins.multiverse.core.config.node.ConfigNode;
import org.mvplugins.multiverse.core.config.node.ListConfigNode;
import org.mvplugins.multiverse.core.config.node.Node;
import org.mvplugins.multiverse.core.config.node.NodeGroup;
import org.mvplugins.multiverse.core.config.node.functions.NodeStringParser;
import org.mvplugins.multiverse.core.config.node.serializer.NodeSerializer;
import org.mvplugins.multiverse.core.exceptions.MultiverseException;
import org.mvplugins.multiverse.core.locale.MVCorei18n;
import org.mvplugins.multiverse.core.locale.message.Message;
import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld;
import org.mvplugins.multiverse.core.world.MultiverseWorld;
import java.util.ArrayList;
import java.util.List;
import static org.mvplugins.multiverse.core.locale.message.MessageReplacement.replace;
public final class SpawnCategoryConfig {
private final CoreConfig config;
@@ -32,7 +40,11 @@ public final class SpawnCategoryConfig {
private MultiverseWorld world;
SpawnCategoryConfig(CoreConfig config, SpawnCategory spawnCategory, ConfigurationSection section) {
SpawnCategoryConfig(
@NotNull CoreConfig config,
@NotNull SpawnCategory spawnCategory,
@NotNull ConfigurationSection section
) {
this.config = config;
this.spawnCategory = spawnCategory;
this.nodes = new Nodes();
@@ -67,8 +79,8 @@ public final class SpawnCategoryConfig {
});
}
private void applyTickPerSpawns(World bukkitWorld) {
if (!config.getApplyEntitySpawnLimit()) {
private void applyTickPerSpawns(@NotNull World bukkitWorld) {
if (!config.getApplyEntitySpawnRate()) {
Logging.finer("World %s %s skipping setTicksPerSpawns due to core config", world.getName(), spawnCategory);
return;
}
@@ -85,7 +97,7 @@ public final class SpawnCategoryConfig {
bukkitWorld.setTicksPerSpawns(spawnCategory, getTickRate());
}
private void applySpawnLimit(World bukkitWorld) {
private void applySpawnLimit(@NotNull World bukkitWorld) {
if (!config.getApplyEntitySpawnLimit()) {
Logging.finer("Skipping World %s %s setSpawnLimit due to core config", world.getName(), spawnCategory);
return;
@@ -98,11 +110,11 @@ public final class SpawnCategoryConfig {
bukkitWorld.setSpawnLimit(spawnCategory, getSpawnLimit());
}
public StringPropertyHandle getStringPropertyHandle() {
public @NotNull StringPropertyHandle getStringPropertyHandle() {
return stringPropertyHandle;
}
public SpawnCategory getSpawnCategory() {
public @NotNull SpawnCategory getSpawnCategory() {
return spawnCategory;
}
@@ -110,7 +122,7 @@ public final class SpawnCategoryConfig {
return handle.get(nodes.spawn);
}
public Try<Void> setSpawn(boolean spawn) {
public @NotNull Try<Void> setSpawn(boolean spawn) {
return handle.set(nodes.spawn, spawn);
}
@@ -118,7 +130,7 @@ public final class SpawnCategoryConfig {
return handle.get(nodes.tickRate);
}
public Try<Void> setTickRate(int tickRate) {
public @NotNull Try<Void> setTickRate(int tickRate) {
return handle.set(nodes.tickRate, tickRate);
}
@@ -126,23 +138,23 @@ public final class SpawnCategoryConfig {
return handle.get(nodes.spawnLimit);
}
public Try<Void> setSpawnLimit(int spawnLimit) {
public @NotNull Try<Void> setSpawnLimit(int spawnLimit) {
return handle.set(nodes.spawnLimit, spawnLimit);
}
public List<EntityType> getExceptions() {
public @NotNull List<EntityType> getExceptions() {
return handle.get(nodes.exceptions);
}
public Try<Void> setExceptions(List<EntityType> exceptions) {
public @NotNull Try<Void> setExceptions(@NotNull List<EntityType> exceptions) {
return handle.set(nodes.exceptions, exceptions);
}
public boolean shouldAllowSpawn(Entity entity) {
public boolean shouldAllowSpawn(@NotNull Entity entity) {
return shouldAllowSpawn(entity.getType());
}
public boolean shouldAllowSpawn(EntityType entityType) {
public boolean shouldAllowSpawn(@NotNull EntityType entityType) {
return isSpawn() != getExceptions().contains(entityType);
}
@@ -172,6 +184,7 @@ public final class SpawnCategoryConfig {
final ConfigNode<Integer> tickRate = node(ConfigNode.builder("tick-rate", Integer.class)
.defaultValue(-2)
.suggester(input -> List.of("@unset", "@bukkit", "10", "100", "400", "1000"))
.stringParser(SpawnValueStringParser.INSTANCE)
.serializer(SpawnValueSerializer.INSTANCE)
.onLoadAndChange((oldValue, newValue) -> applyConfigToWorld())
.onChange((sender, oldValue, newValue) -> {
@@ -188,6 +201,7 @@ public final class SpawnCategoryConfig {
final ConfigNode<Integer> spawnLimit = node(ConfigNode.builder("spawn-limit", Integer.class)
.defaultValue(-2)
.suggester(input -> List.of("@unset", "@bukkit", "10", "100", "400", "1000"))
.stringParser(SpawnValueStringParser.INSTANCE)
.serializer(SpawnValueSerializer.INSTANCE)
.onLoadAndChange((oldValue, newValue) -> applyConfigToWorld())
.onChange((sender, oldValue, newValue) -> {
@@ -210,6 +224,27 @@ public final class SpawnCategoryConfig {
.build());
}
private static class SpawnValueStringParser implements NodeStringParser<Integer> {
private static final SpawnValueStringParser INSTANCE = new SpawnValueStringParser();
@Override
public @NotNull Try<Integer> parse(@Nullable String input, @NotNull Class<Integer> type) {
String str = String.valueOf(input).trim();
if (str.equalsIgnoreCase("@unset")) {
return Try.success(-2);
}
if (str.equalsIgnoreCase("@bukkit")) {
return Try.success(-1);
}
try {
return Try.success(Integer.parseInt(str));
} catch (NumberFormatException e) {
return Try.failure(new MultiverseException(Message.of(MVCorei18n.CONFIG_STRING_PARSER_INVALIDINTEGER,
replace("{input}").with(input))));
}
}
}
private static class SpawnValueSerializer implements NodeSerializer<Integer> {
private static final SpawnValueSerializer INSTANCE = new SpawnValueSerializer();
@@ -1,11 +1,15 @@
package org.mvplugins.multiverse.core.world.entity;
import com.dumptruckman.minecraft.util.Logging;
import io.vavr.control.Option;
import io.vavr.control.Try;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.SpawnCategory;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.mvplugins.multiverse.core.utils.ReflectHelper;
import javax.annotation.Nullable;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
@@ -17,23 +21,30 @@ import java.util.Map;
/**
* Uses reflection into nms to map entity types to spawn categories.
*/
final class SpawnCategoryMapper {
@ApiStatus.Internal
public final class SpawnCategoryMapper {
private static final Map<EntityType, SpawnCategory> entityTypeToSpawnCategoryMap;
private static final Map<SpawnCategory, List<EntityType>> spawnCategoryMap;
private static final @NotNull Map<EntityType, SpawnCategory> entityTypeToSpawnCategoryMap;
private static final @NotNull Map<SpawnCategory, List<EntityType>> spawnCategoryMap;
static {
entityTypeToSpawnCategoryMap = new HashMap<>();
spawnCategoryMap = new HashMap<>();
buildSpawnCategoryMap();
}
private static void buildSpawnCategoryMap() {
public static void buildSpawnCategoryMap() {
Class<?> entityTypeClass = ReflectHelper.tryGetClass("net.minecraft.world.entity.EntityType").getOrNull();
if (entityTypeClass == null) {
Logging.warning("Failed to find EntityType class. SpawnCategoryMapper will not work.");
return;
}
// In 26.2, the list of static fields has been moved from EntityType to EntityTypes class.
Class<?> entityTypesListClass = ReflectHelper.tryGetClass("net.minecraft.world.entity.EntityTypes").getOrNull();
if (entityTypesListClass == null) {
// Assume it's the older mc versions that has list of static fields in EntityType class instead.
Logging.finer("Failed to find EntityTypes class. falling back to EntityType.");
entityTypesListClass = entityTypeClass;
}
Method getCategoryMethod = ReflectHelper.tryGetMethod(entityTypeClass, "getCategory").getOrNull();
if (getCategoryMethod == null) {
Logging.warning("Failed to find getCategory method. SpawnCategoryMapper will not work.");
@@ -52,7 +63,10 @@ final class SpawnCategoryMapper {
Logging.warning("Failed to find toBukkit method. SpawnCategoryMapper will not work.");
return;
}
Arrays.stream(entityTypeClass.getFields()).forEach(entityTypeField -> Try.of(() -> EntityType.valueOf(entityTypeField.getName()))
Logging.finer("Mapping entities to its spawn category...");
Arrays.stream(entityTypesListClass.getFields()).forEach(entityTypeField -> Try.of(() -> EntityType.valueOf(entityTypeField.getName()))
.peek(entityType -> ReflectHelper.tryGetStaticFieldValue(entityTypeField, entityTypeClass)
.flatMap(nmsEntityType -> ReflectHelper.tryInvokeMethod(nmsEntityType, getCategoryMethod))
.flatMap(nsmMobCategory -> ReflectHelper.tryInvokeStaticMethod(toBukkitMethod, nsmMobCategory))
@@ -61,7 +75,22 @@ final class SpawnCategoryMapper {
.peek(bukkitSpawnCategory -> entityTypeToSpawnCategoryMap.put(entityType, bukkitSpawnCategory))
.peek(bukkitSpawnCategory -> spawnCategoryMap
.computeIfAbsent(bukkitSpawnCategory, ignore -> new ArrayList<>())
.add(entityType))));
.add(entityType))
.onFailure(throwable -> Logging.warning("Failed to map entity type "
+ entityTypeField.getName() + " to its spawn category. " + throwable.getLocalizedMessage())))
.onFailure(throwable -> Logging.fine("Failed to map entity type "
+ entityTypeField.getName() + " to its spawn category. " + throwable.getLocalizedMessage())));
if (entityTypeToSpawnCategoryMap.isEmpty()) {
Logging.warning("entityTypeToSpawnCategoryMap map is empty after building... " +
"SpawnCategoryMapper will not work.");
}
if (spawnCategoryMap.isEmpty()) {
Logging.warning("spawnCategoryMap map is empty after building... " +
"SpawnCategoryMapper will not work.");
}
Logging.finer("Mapped " + entityTypeToSpawnCategoryMap.size() + " entities to its spawn category.");
}
/**
@@ -70,8 +99,8 @@ final class SpawnCategoryMapper {
* @param entityType The entity type
* @return The spawn category that the given entity type is in, or null if it cannot be determined
*/
static SpawnCategory getSpawnCategory(EntityType entityType) {
return entityTypeToSpawnCategoryMap.get(entityType);
static @NotNull Option<SpawnCategory> getSpawnCategory(@Nullable EntityType entityType) {
return Option.of(entityTypeToSpawnCategoryMap.get(entityType));
}
/**
@@ -80,10 +109,11 @@ final class SpawnCategoryMapper {
* @param spawnCategory The spawn category
* @return The entity types associated with the spawn category
*/
static List<EntityType> getEntityTypes(SpawnCategory spawnCategory) {
if (spawnCategoryMap == null) {
return Collections.emptyList();
}
return spawnCategoryMap.get(spawnCategory);
static @NotNull List<EntityType> getEntityTypes(@Nullable SpawnCategory spawnCategory) {
return spawnCategoryMap.getOrDefault(spawnCategory, Collections.emptyList());
}
private SpawnCategoryMapper() {
throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");
}
}
@@ -0,0 +1,157 @@
package org.mvplugins.multiverse.core.world.helpers;
import com.google.common.base.Strings;
import jakarta.inject.Inject;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Service;
import org.mvplugins.multiverse.core.command.MVCommandIssuer;
import org.mvplugins.multiverse.core.locale.MVCorei18n;
import org.mvplugins.multiverse.core.locale.message.MessageReplacement.Replace;
import org.mvplugins.multiverse.core.utils.text.ChatTextFormatter;
import org.mvplugins.multiverse.core.world.MultiverseWorld;
import org.mvplugins.multiverse.core.world.WorldManager;
import java.util.ArrayList;
import java.util.List;
import static org.mvplugins.multiverse.core.locale.message.MessageReplacement.replace;
/**
* Checks world aliases for conflicts with other world aliases and names.
*
* @since 5.8
*/
@ApiStatus.AvailableSince("5.8")
@Service
public class AliasNameConflictChecker {
private final WorldManager worldManager;
@Inject
private AliasNameConflictChecker(@NotNull WorldManager worldManager) {
this.worldManager = worldManager;
}
/**
* Checks whether the target world's alias conflicts with another world's alias or name.
* Color formatting and letter case are ignored when aliases are compared.
*
* @param targetWorld The world whose alias should be checked.
* @return The detected alias conflicts.
*
* @since 5.8
*/
@ApiStatus.AvailableSince("5.8")
public @NotNull AliasNameConflictResult checkDuplicateFor(MultiverseWorld targetWorld) {
AliasNameConflictResult aliasNameConflictResult = new AliasNameConflictResult(targetWorld);
String targetWorldAlias = ChatTextFormatter.removeColor(targetWorld.getAlias());
if (Strings.isNullOrEmpty(targetWorldAlias)) {
return aliasNameConflictResult;
}
for (MultiverseWorld otherWorld : worldManager.getWorlds()) {
if (otherWorld.getKey().equals(targetWorld.getKey())) {
continue;
}
if (targetWorldAlias.equalsIgnoreCase(ChatTextFormatter.removeColor(otherWorld.getAlias()))) {
aliasNameConflictResult.getDuplicateAliases().add(otherWorld);
}
if (targetWorldAlias.equalsIgnoreCase(otherWorld.getName())) {
aliasNameConflictResult.getDuplicateWorldNames().add(otherWorld);
}
}
return aliasNameConflictResult;
}
/**
* Contains the alias conflicts detected for a world.
*
* @since 5.8
*/
@ApiStatus.AvailableSince("5.8")
public static class AliasNameConflictResult {
private final MultiverseWorld targetWorld;
private final List<MultiverseWorld> duplicateAliases;
private final List<MultiverseWorld> duplicateWorldNames;
private AliasNameConflictResult(@NotNull MultiverseWorld targetWorld) {
this.targetWorld = targetWorld;
duplicateAliases = new ArrayList<>();
duplicateWorldNames = new ArrayList<>();
}
/**
* Sends localized messages describing the detected conflicts to the command issuer.
* No message is sent when there are no conflicts.
*
* @param issuer The command issuer to notify.
*
* @since 5.8
*/
@ApiStatus.AvailableSince("5.8")
public void sendConflictMessage(MVCommandIssuer issuer) {
if (!hasConflict()) {
return;
}
issuer.sendError(MVCorei18n.ALIASNAMECONFLICT_DETECTED,
Replace.WORLD.with(targetWorld.getName()),
replace("{alias}").with(targetWorld.getColourlessAlias()));
duplicateAliases.forEach(conflictingWorld ->
issuer.sendError(MVCorei18n.ALIASNAMECONFLICT_DUPLICATEALIAS,
Replace.WORLD.with(conflictingWorld.getName()),
replace("{alias}").with(conflictingWorld.getColourlessAlias())));
duplicateWorldNames.forEach(conflictingWorld ->
issuer.sendError(MVCorei18n.ALIASNAMECONFLICT_DUPLICATEWORLDNAME,
Replace.WORLD.with(conflictingWorld.getName())));
}
/**
* Gets whether any alias or world name conflicts were detected.
*
* @return {@code true} if at least one conflict was detected, otherwise {@code false}.
*
* @since 5.8
*/
@ApiStatus.AvailableSince("5.8")
public boolean hasConflict() {
return !duplicateAliases.isEmpty() || !duplicateWorldNames.isEmpty();
}
/**
* Gets the world whose alias was checked.
*
* @return The checked world.
*
* @since 5.8
*/
@ApiStatus.AvailableSince("5.8")
public @NotNull MultiverseWorld getTargetWorld() {
return targetWorld;
}
/**
* Gets the worlds whose aliases match the target world's alias.
*
* @return The worlds with conflicting aliases.
*
* @since 5.8
*/
@ApiStatus.AvailableSince("5.8")
public @NotNull List<MultiverseWorld> getDuplicateAliases() {
return duplicateAliases;
}
/**
* Gets the worlds whose names match the target world's alias.
*
* @return The worlds with names that conflict with the target world's alias.
*
* @since 5.8
*/
@ApiStatus.AvailableSince("5.8")
public @NotNull List<MultiverseWorld> getDuplicateWorldNames() {
return duplicateWorldNames;
}
}
}
@@ -27,6 +27,9 @@ import java.util.stream.Stream;
@Service
public final class PotentialWorldFinder {
private static final int MAX_RECURSION_DEPTH = 3;
private static final int MAX_FOLDER_TRAVERSAL_LIMIT = 100;
private final WorldManager worldManager;
private final WorldNameChecker worldNameChecker;
@@ -56,6 +59,7 @@ public final class PotentialWorldFinder {
private Stream<String> findFromRootFolder() {
Path worldContainer = Bukkit.getWorldContainer().toPath();
return Arrays.stream(listFolders(worldContainer))
.limit(MAX_FOLDER_TRAVERSAL_LIMIT)
.filter(worldNameChecker::isValidWorldFolder)
.map(File::getName)
.filter(worldNameChecker::isValidWorldName)
@@ -65,15 +69,29 @@ public final class PotentialWorldFinder {
private Stream<String> findFromDimensionsFolder() {
Path worldContainer = BukkitCompatibility.getWorldFoldersDirectory();
return Arrays.stream(listFolders(worldContainer))
.map(File::getName)
.flatMap(namespace -> Arrays.stream(listFolders(worldContainer.resolve(namespace)))
.filter(worldNameChecker::isValidWorldFolder)
.map(File::getName)
.map(key -> namespace + ":" + key))
.limit(MAX_FOLDER_TRAVERSAL_LIMIT)
.flatMap(namespaceFile -> recursiveFindFromNamespaceFolder("", namespaceFile.toPath(), 0)
.map(worldKey -> namespaceFile.getName() + ":" + worldKey))
.filter(namespacedKey -> NamespacedKey.fromString(namespacedKey) != null)
.filter(namespacedKey -> !worldManager.isWorld(namespacedKey));
}
private Stream<String> recursiveFindFromNamespaceFolder(String prefix, Path subWorldContainer, int depth) {
if (depth >= MAX_RECURSION_DEPTH) {
return Stream.empty();
}
return Arrays.stream(listFolders(subWorldContainer))
.limit(MAX_FOLDER_TRAVERSAL_LIMIT)
.flatMap(folder -> worldNameChecker.isValidWorldFolder(folder)
? Stream.of(concatPath(prefix, folder.getName()))
: recursiveFindFromNamespaceFolder(
concatPath(prefix, folder.getName()), folder.toPath(), depth + 1));
}
private String concatPath(String prefix, String folderName) {
return prefix.isEmpty() ? folderName : prefix + "/" + folderName;
}
private @NotNull File[] listFolders(Path folder) {
return Option.of(folder.toFile().listFiles(File::isDirectory))
.getOrElse(() -> new File[0]);
@@ -126,6 +126,8 @@ public final class CloneWorldOptions implements KeepWorldSettingsOptions {
* Gets the name of the new world.
*
* @return The name of the new world.
*
* @deprecated Use {@link #newWorldKeyOrName()} instead.
*/
@Deprecated(forRemoval = true, since = "5.7")
@ApiStatus.ScheduledForRemoval(inVersion = "6.0")
@@ -101,10 +101,13 @@ public final class CreateWorldOptions {
* Gets the name of the world to create.
*
* @return The name of the world to create.
*
* @deprecated Use {@link #keyOrName()} instead.
*/
@Deprecated(forRemoval = true, since = "5.7")
@ApiStatus.ScheduledForRemoval(inVersion = "6.0")
public @NotNull String worldName() {
return keyOrName.fold(name -> name ,WorldKeyOrName::usableName);
return keyOrName.fold(name -> name, WorldKeyOrName::usableName);
}
/**
@@ -85,11 +85,13 @@ public final class ImportWorldOptions {
* Gets the name of the world to create.
*
* @return The name of the world to create.
*
* @deprecated Use {@link #keyOrName()} instead.
*/
@Deprecated(forRemoval = true, since = "5.7")
@ApiStatus.ScheduledForRemoval(inVersion = "6.0")
public @NotNull String worldName() {
return keyOrName.fold(name -> name ,WorldKeyOrName::usableName);
return keyOrName.fold(name -> name, WorldKeyOrName::usableName);
}
/**
@@ -1,6 +1,26 @@
# configuration
mv-core.config.save.failed=Unable to save Multiverse-Core config.yml. Your changes will be temporary!
mv-core.config.node.notfound=Node not found in config: {node}
mv-core.config.node.invalidyamlkey=Invalid yaml key: '{key}'. Keys can only contain alphanumeric characters, underscores and hyphens.
mv-core.config.string.parser.invalidboolean=Unable to convert '{input}' to boolean. Please use 'true' or 'false'
mv-core.config.string.parser.invalidinteger=Unable to convert '{input}' to number. (integer)
mv-core.config.string.parser.invaliddouble=Unable to convert '{input}' to number. (double)
mv-core.config.string.parser.invalidfloat=Unable to convert '{input}' to number. (float)
mv-core.config.string.parser.invalidlong=Unable to convert '{input}' to number. (long)
mv-core.config.string.parser.invalidmaterial='{input}' is not a valid material.
# core config
mv-core.core.config.customportalsearchradius.nonnegative=The value must be greater than or equal to 0.
mv-core.core.config.confirmtimeout.positive=Confirm timeout must be a positive number!
mv-core.core.config.debuglevel.range=Debug level must be between 0 and 3.
# /mv anchor
mv-core.anchor.delete.success=&aAnchor &f{anchor}&a deleted.
mv-core.anchor.delete.failure=&cFailed to delete anchor.
mv-core.anchor.list.header=&3==== [ Multiverse Anchors ] ====
mv-core.anchor.list.entry=&a{anchor}&7 - &f{location}
mv-core.anchor.set.success=&aAnchor &f{anchor}&a set to &f{location}
mv-core.anchor.set.failure=&cFailed to set anchor &f{anchor}.
# /mv check
mv-core.check.description=Checks if a player can teleport themselves to a destination.
@@ -13,7 +33,7 @@ mv-core.check.location=The destination's location is: &f{location}
# /mv clone
mv-core.clone.description=Clones a world.
mv-core.clone.world.description=The target world to clone.
mv-core.clone.newWorld.description=The new cloned world name.
mv-core.clone.newworld.description=The new cloned world name.
mv-core.clone.cloning=Cloning world '{world}' to '{newworld}'...
mv-core.clone.success=&aWorld cloned to '{world}'!
@@ -34,7 +54,7 @@ mv-core.coordinates.description=Simply sends your coordinates
mv-core.coordinates.info.title=&b--- Location Information ---
mv-core.coordinates.info.world=&bWorld: &f{world}
mv-core.coordinates.info.alias=&bAlias: &f{alias}
mv-core.coordinates.info.worldScale=&bWorld Scale: &f{scale}
mv-core.coordinates.info.worldscale=&bWorld Scale: &f{scale}
mv-core.coordinates.info.coordinates=&bCoordinates: &f{coordinates}
mv-core.coordinates.info.direction=&bDirection: &f{direction}
@@ -75,6 +95,15 @@ mv-core.dumps.description=Dumps version info to the console or paste services
mv-core.dumps.starting=Gathering logs and debug info...
mv-core.dumps.url.list=&aUploaded with {service} backend: &6{link}
# /mv entity-spawn-config
mv-core.entityspawnconfig.info.header===== [ Entity Spawn Config '{world}' ] ====
mv-core.entityspawnconfig.info.category={category}:\u0020
mv-core.entityspawnconfig.info.spawn=\ \ spawn: {spawn}
mv-core.entityspawnconfig.info.tickrate=\ \ tick-rate: {tickRate}
mv-core.entityspawnconfig.info.exceptions=\ \ exceptions: {exceptions}
mv-core.entityspawnconfig.modify.success=Successfully set {property} to {value} for {category} in {world}
mv-core.entityspawnconfig.modify.failure=Unable to set {property} to {value} for {category} in {world}: {error}
# /mv gamerule set
mv-core.gamerule.set.description=Changes a gamerule in one or more worlds.
mv-core.gamerule.set.gamerule.description=Gamerule to set.
@@ -101,7 +130,12 @@ mv-core.gamerule.list.title= --- Gamerules for {world} ---
# /mv generators
mv-core.generators.description=Lists generators known to Multiverse
mv-core.generators.description.flags=Filter - only shows entries matching this. Page - the page to show
mv-core.generators.header=&b====[ Multiverse Generator List ]====
mv-core.generators.empty=&cNo Generator Plugins found.
mv-core.generators.info.plugin=&rGenerator Plugin: {plugin}
mv-core.generators.info.exampleusages=&rExample usages:\u0020
mv-core.generators.info.usages=&r{usages}
mv-core.generators.info.infolink=&rLink to more info: {link}
# /mv import
mv-core.import.description=Imports an existing world folder.
@@ -158,6 +192,13 @@ mv-core.modify.success=&aSuccessfully {action} '&9{property}&a' to '&9{value}&a'
mv-core.modify.failure=&cFailed to {action} '&9{property}&c' to '&9{value}&c' in world &9{world}&c.\n&c{error}
mv-core.modify.failure.novalue=&cFailed to {action} '&9{property}&c' in world &9{world}&c.\n&c{error}
# /mv purge-all-entities
mv-core.purgeallentities.success=Successfully purged {count} entities in world {world}.
mv-core.purgeallentities.success.categories=Successfully purged {count} entities in world {world} for spawn categories {categories}.
# /mv purge-entities
mv-core.purgeentities.success=Successfully purged {count} entities in world {world}.
# /mv regen
mv-core.regen.description=Regenerates a world on your server. The previous state will be lost PERMANENTLY.
mv-core.regen.world.description=World that you want to regen.
@@ -192,6 +233,8 @@ mv-core.setspawn.notmvworld=&cUnable to set spawn for &c{world} as it is not a M
# /mv spawn
mv-core.spawn.description=Teleports the specified player to the spawn of the world they are in
mv-core.spawn.player.description=The player
mv-core.spawn.notmvworld=&cThe world '{world}' is not a multiverse world!
mv-core.spawn.nopermission=&cYou do not have permission to use this command in this world!
mv-core.spawn.success=Teleported {player} to '{world}' spawn!
mv-core.spawn.failed=Failed to teleport {player} to '{world}' spawn. {reason}
@@ -200,6 +243,9 @@ mv-core.teleport.description=Allows you to teleport to a location on your server
mv-core.teleport.player.description=Target player to teleport.
mv-core.teleport.destination.description=Location, can be a world name.
mv-core.teleport.toomanyplayers=&cYou cannot teleport more than {count} players at once.
mv-core.teleport.nopermission.self=&cYou do not have permission to teleport yourself!
mv-core.teleport.nopermission.other=&cYou do not have permission to teleport other players!
mv-core.teleport.nopermission.all=&cYou do not have permission to teleport all these players!
mv-core.teleport.success=Teleported {player} to {destination}.
mv-core.teleport.failed=Failed to teleport {player} to {destination}. {reason}
@@ -255,6 +301,21 @@ mv-core.worldborder.roundoff.warning=&cYour server version does not support sett
# commands error
mv-core.commands.error.playersonly=&cThis command can only be used by players
mv-core.commands.error.multiverseworldonly=&cThis can only be used in multiverse worlds
mv-core.commands.error.loadedmultiverseworld.playersonly=This command can only be used by a player in a loaded Multiverse World.
mv-core.commands.error.loadedmultiverseworld.issuer=You are not in a loaded multiverse world. Either specify a multiverse world name or use this command in a loaded multiverse world.
mv-core.commands.error.loadedmultiverseworld.inputconsole=World '{world}' is not a loaded multiverse world. Remember to specify the world name when using this command in console.
mv-core.commands.error.loadedmultiverseworld.input=World {world} is not a loaded multiverse world.
mv-core.commands.error.multiverseworld.playersonly=This command can only be used by a player in a Multiverse World.
mv-core.commands.error.multiverseworld.issuer=You are not in a multiverse world. Either specify a multiverse world name or use this command in a multiverse world.
mv-core.commands.error.multiverseworld.inputconsole=World '{world}' is not a multiverse world. Remember to specify the world name when using this command in console.
mv-core.commands.error.multiverseworld.input=World {world} is not a multiverse world.
mv-core.commands.error.player.issuerinput=Invalid player: {player}. Either specify an online player or use this command as a player.
mv-core.commands.error.player.input=Player {player} not found.
mv-core.commands.error.player.selector=No player(s) matched selector: {player}.
mv-core.commands.error.players.input=Player(s) {player} not found.
mv-core.commands.error.resolve.player=Unable to resolve context for player '{player}'.
mv-core.commands.error.resolve.input=Unable to resolve context for input '{input}'.
mv-core.commands.error.removeplayers.nodefault=No default world found, so the --remove-players flag requires a destination argument.
# entry check
mv-core.entrycheck.blacklisted=The world '{world}' is blacklisted.
@@ -271,7 +332,10 @@ mv-core.economy.vault.withdraw=&fYou have been charged &2{price}.
# multiverse parse destination failure reason
mv-core.destination.anchor.failurereason.anchornotfound=&cAnchor '&6{anchor}&c' does not exist!
mv-core.destination.anchor.display=anchor '{anchor}'
mv-core.destination.bed.failurereason.playernotfound=&cPlayer '&6{player}&c' does not exist or is not online! To teleport to own's bed, use 'playerbed'.
mv-core.destination.bed.display.own=your bed/respawn point
mv-core.destination.bed.display.other={player}'s bed/respawn point
mv-core.destination.cannon.failurereason.invalidformat=&cCannon destination format is: &6ca:worldname:x,y,z:pitch:yaw:speed
mv-core.destination.exact.failurereason.invalidformat=&cExact destination format is: &6e:worldname:x,y,z:pitch:yaw
mv-core.destination.player.failurereason.playernotfound=&cPlayer '&6{player}&c' does not exist or is not online!
@@ -293,6 +357,11 @@ mv-core.teleportfailurereason.teleport.failed=The teleport was rejected. Please
mv-core.teleportfailurereason.teleport.failed.exception=An error occurred during the teleport. Check server logs for more details.
mv-core.teleportfailurereason.event.cancelled=The teleport was cancelled by another plugin.
# alias name conflict
mv-core.aliasnameconflict.detected=Alias conflict detected for world '{world}' with alias '{alias}'. This may cause world list and selection to be confusing!
mv-core.aliasnameconflict.duplicatealias= - Conflicts with '{world}' world's alias: '{alias}'
mv-core.aliasnameconflict.duplicateworldname= - Conflicts with world name: '{world}'
# world manager result
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.
@@ -316,14 +385,14 @@ mv-core.importworld.worldexistunloaded=World '{world}' already exists, but it's
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.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.importworld.bukkitnamespacemismatch=Namespace mismatch detected!&f The world '{world}' is already loaded with namespace '{bukkitNamespace}', but Multiverse is trying to import it with namespace '{mvNamespace}'.
mv-core.importworld.bukkitnamespacedkeymismatch=Namespace mismatch detected!&f The world '{world}' is already loaded with namespace '{bukkitNamespace}', but Multiverse is trying to import it with namespace '{mvNamespace}'.
mv-core.loadworld.worldalreadyloading=World '{world}' is already loading! Please wait...
mv-core.loadworld.worldnonexistent=World '{world}' not found! Use '&a/mv create {world} <environment>&f' to create it.
mv-core.loadworld.worldexistfolder=World '{world}' exists in server folders, but it's not known to Multiverse!&f Type '&a/mv import {world} <environment>&f' if you wish to import it.
mv-core.loadworld.worldexistloaded=World '{world}' is already loaded!
mv-core.loadworld.bukkitenvironmentmismatch=Environment mismatch detected!&f The world '{world}' is already loaded with environment '{bukkitEnvironment}', but Multiverse is trying to load it with environment '{mvEnvironment}'.
mv-core.loadworld.bukkitnamespacemismatch=Namespace mismatch detected!&f The world '{world}' is already loaded with namespace '{bukkitNamespace}', but Multiverse is trying to load it with namespace '{mvNamespace}'.
mv-core.loadworld.bukkitnamespacedkeymismatch=Namespace mismatch detected!&f The world '{world}' is already loaded with namespace '{bukkitNamespace}', but Multiverse is trying to load it with namespace '{mvNamespace}'.
mv-core.removeworld.worldnonexistent=World '{world}' not found!
@@ -337,6 +406,9 @@ mv-core.worldcreator.invalidchunkgenerator=&cInvalid chunk generator '&6{generat
mv-core.worldcreator.bukkitcreationfailed=&cBukkit failed to create world '{world}': {error}\n&cSee console for more details.
# queue command result
mv-core.queuecommand.defaultprompt=The command you are trying to run is deemed dangerous.
mv-core.queuecommand.prompt=Run &a{command} &fto continue. This will expire in {timeout} seconds.
mv-core.queuecommand.expired=Your queued command has expired.
mv-core.queuecommand.nocommandinqueue=&cYou do not have any commands in queue.
mv-core.queuecommand.invalidotp=&cInvalid OTP number '&6{otp}&c'. Please try again...
mv-core.queuecommand.commandexecutionerror=&cError executing queued command: {error}\n&fSee console for more details.
@@ -345,6 +417,7 @@ mv-core.queuecommand.commandexecutionerror=&cError executing queued command: {er
mv-core.contentdisplay.nocontent=&cThere is no content to display.
mv-core.contentdisplay.filter=&7[Filter '{filter}']
mv-core.contentdisplay.page=&7[Page {current} of {total}]
mv-core.contentdisplay.pagefilter={page} {filter}
mv-core.contentdisplay.invalidpage=&cInvalid page number. Please enter a page number between &31&c and &3{total}&c.
mv-core.contentdisplay.empty=&7&oempty
mv-core.contentdisplay.null=&7&onull
@@ -370,5 +443,8 @@ mv-core.worldkeyparse.namespacedkeyunsupported=&cYour server software does not s
mv-core.generic.success=Success!
mv-core.generic.failure=Failed!
mv-core.generic.error=Error!
mv-core.generic.error.details=Error: {error}
mv-core.generic.null=Null!
mv-core.generic.you=you
mv-core.generic.playercount={count} players
mv-core.generic.teleportplayers.failed=Failed to teleport one or more players out of the world!
@@ -1,6 +1,25 @@
# configuration
mv-core.config.save.failed=No se han podido guardar los cambios que se han hecho al archivo config.yml de Multiverse-Core. ¡Estos camios serán temporales!
mv-core.config.node.notfound=Nodo no encontrado en la configuración: {node}
mv-core.config.node.invalidyamlkey=Clave yaml inválida: '{key}'. Las claves solo pueden contener caracteres alfanuméricos, guiones bajos y guiones.
mv-core.config.string.parser.invalidboolean=No se ha podido convertir '{input}' a booleano. Usa 'true' o 'false'
mv-core.config.string.parser.invalidinteger=No se ha podido convertir '{input}' a número. (entero)
mv-core.config.string.parser.invaliddouble=No se ha podido convertir '{input}' a número. (double)
mv-core.config.string.parser.invalidfloat=No se ha podido convertir '{input}' a número. (float)
mv-core.config.string.parser.invalidlong=No se ha podido convertir '{input}' a número. (long)
# core config
mv-core.core.config.customportalsearchradius.nonnegative=El valor debe ser mayor o igual a 0.
mv-core.core.config.confirmtimeout.positive=¡El tiempo de espera de confirmación debe ser un número positivo!
mv-core.core.config.debuglevel.range=El nivel de depuración debe estar entre 0 y 3.
# /mv anchor
mv-core.anchor.delete.success=&aAnclaje &f{anchor}&a eliminado.
mv-core.anchor.delete.failure=&cNo se ha podido eliminar el anclaje.
mv-core.anchor.list.header=&3==== [ Anclajes de Multiverse ] ====
mv-core.anchor.list.entry=&a{anchor}&7 - &f{location}
mv-core.anchor.set.success=&aAnclaje &f{anchor}&a establecido en &f{location}
mv-core.anchor.set.failure=&cNo se ha podido establecer el anclaje &f{anchor}.
# /mv check
mv-core.check.description=Comprueba si un jugador puede teletransportarse a algún sitio.
@@ -13,7 +32,7 @@ mv-core.check.location=La localización de su destino es: &f{location}
# /mv clone
mv-core.clone.description=Clona un mundo.
mv-core.clone.world.description=El mundo a clonar.
mv-core.clone.newWorld.description=El nombre del nuevo mundo clonado.
mv-core.clone.newworld.description=El nombre del nuevo mundo clonado.
mv-core.clone.cloning=Clonando mundo '{world}' a '{newworld}'...
mv-core.clone.success=&a¡Mundo clonado a '{world}'!
@@ -34,7 +53,7 @@ mv-core.coordinates.description=Simple y llanamente te muestra tus coordenadas.
mv-core.coordinates.info.title=&b--- Información de la localización ---
mv-core.coordinates.info.world=&bMundo: &f{world}
mv-core.coordinates.info.alias=&bAlias: &f{alias}
mv-core.coordinates.info.worldScale=&bEscala del mundo: &f{scale}
mv-core.coordinates.info.worldscale=&bEscala del mundo: &f{scale}
mv-core.coordinates.info.coordinates=&bCoordenadas: &f{coordinates}
mv-core.coordinates.info.direction=&bDirección: &f{direction}
@@ -74,6 +93,15 @@ mv-core.delete.success=&a¡Se ha eliminado el mundo '{world}'!
mv-core.dumps.description=Vuelca la información de la versión a la consola o a servicios de copiado y pegado («paste services» en inglés).
mv-core.dumps.url.list={service} : {link}
# /mv entity-spawn-config
mv-core.entityspawnconfig.info.header===== [ Configuración de aparición de entidades '{world}' ] ====
mv-core.entityspawnconfig.info.category={category}:\u0020
mv-core.entityspawnconfig.info.spawn=\ \ spawn: {spawn}
mv-core.entityspawnconfig.info.tickrate=\ \ tick-rate: {tickRate}
mv-core.entityspawnconfig.info.exceptions=\ \ excepciones: {exceptions}
mv-core.entityspawnconfig.modify.success=Se ha establecido {property} a {value} para {category} en {world}
mv-core.entityspawnconfig.modify.failure=No se ha podido establecer {property} a {value} para {category} en {world}: {error}
# /mv gamerule set
mv-core.gamerule.set.description=Cambia una «regla de juego» (gamerule) en uno o más mundos.
mv-core.gamerule.set.gamerule.description=Gamerule que se desea cambiar.
@@ -100,7 +128,12 @@ mv-core.gamerule.list.title= --- Valor de las gamerules de {world} ---
# /mv generators
mv-core.generators.description=Muestra los generadores conocidos por Multiverse
mv-core.generators.description.flags=Filtro - solo muestra las entradas que coincidan. Página - la página a mostrar.
mv-core.generators.header=&b====[ Lista de generadores de Multiverse ]====
mv-core.generators.empty=&cNo se ha encontrado ningún Plugin de Generación.
mv-core.generators.info.plugin=&rPlugin generador: {plugin}
mv-core.generators.info.exampleusages=&rUsos de ejemplo:\u0020
mv-core.generators.info.usages=&r{usages}
mv-core.generators.info.infolink=&rEnlace para más información: {link}
# /mv import
mv-core.import.description=Importa la carpeta de un mundo ya existente.
@@ -138,6 +171,13 @@ mv-core.modify.success=&aSe ha {action} '&9{property}&a' a '&9{value}&a' en el m
mv-core.modify.failure=&cNo se ha podido {action} '&9{property}&c' a '&9{value}&c' en el mundo &9{world}&c.\n&c{error}
mv-core.modify.failure.novalue=&cNo se ha podido {action} '&9{property}&c' en el mundo &9{world}&c.\n&c{error}
# /mv purge-all-entities
mv-core.purgeallentities.success=Se han purgado correctamente {count} entidades en el mundo {world}.
mv-core.purgeallentities.success.categories=Se han purgado correctamente {count} entidades en el mundo {world} para las categorías de aparición {categories}.
# /mv purge-entities
mv-core.purgeentities.success=Se han purgado correctamente {count} entidades en el mundo {world}.
# /mv regen
mv-core.regen.description=Regenera un mundo en tu servidor. El estado previo se perderá PERMANENTEMENTE.
mv-core.regen.world.description=Mundo que quieres regenerar.
@@ -172,6 +212,8 @@ mv-core.setspawn.notmvworld=&cNo se ha podido establecer el punto de aparición
# /mv spawn
mv-core.spawn.description=Teletransporta el jugador especificado al punto de aparición del mundo en el que están.
mv-core.spawn.player.description=El jugador
mv-core.spawn.notmvworld=&c¡El mundo '{world}' no es un mundo Multiverse!
mv-core.spawn.nopermission=&c¡No tienes permiso para usar este comando en este mundo!
mv-core.spawn.success=¡Se ha teletransportado a {player} al punto de aparición de '{world}'!
mv-core.spawn.failed=No se ha podido teletransportar a {player} al punto de aparición de '{world}'. {reason}
@@ -180,6 +222,9 @@ mv-core.teleport.description=¡Te permite teletransportarte a un lugar en concre
mv-core.teleport.player.description=Jugador sobre el que se va a ejecutar la teletransportación.
mv-core.teleport.destination.description=Localización, puede ser el nombre de un mundo.
mv-core.teleport.toomanyplayers=&cNo puedes teletransportar más de {count} jugador al mismo tiempo.
mv-core.teleport.nopermission.self=&c¡No tienes permiso para teletransportarte!
mv-core.teleport.nopermission.other=&c¡No tienes permiso para teletransportar a otros jugadores!
mv-core.teleport.nopermission.all=&c¡No tienes permiso para teletransportar a todos estos jugadores!
mv-core.teleport.success=Se ha teletransportado a {player} a {destination}.
mv-core.teleport.failed=No se ha podido teletransportar a {player} a {destination}. {reason}
@@ -233,6 +278,21 @@ mv-core.worldborder.warningtime.success=&rSe ha establecido el tiempo de duraci
# commands error
mv-core.commands.error.playersonly=&cEste comando solo puede ser usado por jugadores.
mv-core.commands.error.multiverseworldonly=&cEsto solo puede ser usado en mundos Multiverse.
mv-core.commands.error.loadedmultiverseworld.playersonly=Este comando solo puede ser usado por un jugador en un mundo Multiverse cargado.
mv-core.commands.error.loadedmultiverseworld.issuer=No estás en un mundo Multiverse cargado. Especifica el nombre de un mundo Multiverse o usa este comando en un mundo Multiverse cargado.
mv-core.commands.error.loadedmultiverseworld.inputconsole=El mundo '{world}' no es un mundo Multiverse cargado. Recuerda especificar el nombre del mundo al usar este comando desde la consola.
mv-core.commands.error.loadedmultiverseworld.input=El mundo {world} no es un mundo Multiverse cargado.
mv-core.commands.error.multiverseworld.playersonly=Este comando solo puede ser usado por un jugador en un mundo Multiverse.
mv-core.commands.error.multiverseworld.issuer=No estás en un mundo Multiverse. Especifica el nombre de un mundo Multiverse o usa este comando en un mundo Multiverse.
mv-core.commands.error.multiverseworld.inputconsole=El mundo '{world}' no es un mundo Multiverse. Recuerda especificar el nombre del mundo al usar este comando desde la consola.
mv-core.commands.error.multiverseworld.input=El mundo {world} no es un mundo Multiverse.
mv-core.commands.error.player.issuerinput=Jugador inválido: {player}. Especifica un jugador en línea o usa este comando como jugador.
mv-core.commands.error.player.input=Jugador {player} no encontrado.
mv-core.commands.error.player.selector=Ningún jugador coincide con el selector: {player}.
mv-core.commands.error.players.input=Jugador(es) {player} no encontrados.
mv-core.commands.error.resolve.player=No se ha podido resolver el contexto para el jugador '{player}'.
mv-core.commands.error.resolve.input=No se ha podido resolver el contexto para la entrada '{input}'.
mv-core.commands.error.removeplayers.nodefault=No se ha encontrado un mundo predeterminado, por lo que el ajuste --remove-players requiere un argumento de destino.
# entry check
mv-core.entrycheck.blacklisted=El mundo '{world}' está en la lista negra («blacklist» en inglés).
@@ -249,7 +309,10 @@ mv-core.economy.vault.withdraw=&fSe te ha cobrado &2{price}.
# multiverse parse destination failure reason
mv-core.destination.anchor.failurereason.anchornotfound=&c¡El anclaje '&6{anchor}&c' no existe!
mv-core.destination.anchor.display=anclaje '{anchor}'
mv-core.destination.bed.failurereason.playernotfound=&c¡El jugador '&6{player}&c' no existe o no está en línea! Para teletransportarte a su cama, usa 'playerbed'.
mv-core.destination.bed.display.own=tu cama/punto de reaparición
mv-core.destination.bed.display.other=cama/punto de reaparición de {player}
mv-core.destination.cannon.failurereason.invalidformat=&cEl formato canon del destino es: &6ca:nombredelmundo:x,y,z:inclinación:aceleración:velocidad
mv-core.destination.exact.failurereason.invalidformat=&cEl formato de destino exacto es: &6e:nombredelmundo:x,y,z:inclinación:aceleración
mv-core.destination.player.failurereason.playernotfound=&c¡El jugador '&6{player}&c' no existe o no está en línea!
@@ -267,6 +330,11 @@ mv-core.teleportfailurereason.teleport.failed=Algo ha evitado la teletransportac
mv-core.teleportfailurereason.teleport.failed.exception=Un error ha ocurrido durante la teletransportación. Mira la consola para ver los detalles.
mv-core.teleportfailurereason.event.cancelled=El teletransporte fue cancelado por otro plugin.
# alias name conflict
mv-core.aliasnameconflict.detected=Se ha detectado un conflicto de alias para el mundo '{world}' con alias '{alias}'. Esto puede hacer que la lista y selección de mundos sea confusa.
mv-core.aliasnameconflict.duplicatealias= - Entra en conflicto con el alias del mundo '{world}': '{alias}'
mv-core.aliasnameconflict.duplicateworldname= - Entra en conflicto con el nombre del mundo: '{world}'
# world manager result
mv-core.cloneworld.invalidworldname=¡El mundo '{world}' contiene caracteres inválidos!
mv-core.cloneworld.worldexistfolder=¡El mundo '{world}' ya existe en los archivos del servidor! Necesitas eliminarlo antes de clonarlo.
@@ -306,6 +374,9 @@ mv-core.worldcreator.invalidchunkgenerator=&c¡Generador de chunks inválido '&6
mv-core.worldcreator.bukkitcreationfailed=&cBukkit ha fallado al crear el mundo '{world}': {error}\n&cSMira la consola para ver los detalles.
# queue command result
mv-core.queuecommand.defaultprompt=El comando que intentas ejecutar se considera peligroso.
mv-core.queuecommand.prompt=Ejecuta &a{command} &fpara continuar. Esto expirará en {timeout} segundos.
mv-core.queuecommand.expired=Tu comando en cola ha expirado.
mv-core.queuecommand.nocommandinqueue=&cNo tienes ningún comando en la cola.
mv-core.queuecommand.invalidotp=&cNúmero OTP inválido '&6{otp}&c'. Por favor, inténtalo de nuevo...
mv-core.queuecommand.commandexecutionerror=&cError ejecutando el comando en la cola: {error}\n&fMira la consola para ver los detalles.
@@ -314,6 +385,7 @@ mv-core.queuecommand.commandexecutionerror=&cError ejecutando el comando en la c
mv-core.contentdisplay.nocontent=&cNo hay ningún contenido para mostrar.
mv-core.contentdisplay.filter=&7[Filtro '{filter}']
mv-core.contentdisplay.page=&7[Página {current} de {total}]
mv-core.contentdisplay.pagefilter={page} {filter}
mv-core.contentdisplay.invalidpage=&cNúmero de página inválido. Por favor, introduce el número de una página que esté entre &31&c y &3{total}&c.
mv-core.contentdisplay.empty=&7&ovacío
mv-core.contentdisplay.null=&7&onull
@@ -324,9 +396,17 @@ mv-core.exception.multiverseworld.unloaddefaultworld=&c¡No puedes descargar (en
mv-core.exception.multiverseworld.unloadplayersinworld=&c¡Aún hay &6{count}&c jugador(es) en el mundo! Usa el ajuste («flag») '&6--remove-players&c' al ejecutar el comando para teletransportar a todos los jugadores fuera del mundo.
mv-core.exception.multiverseworld.unloaderror=&cUn error desconocido ha ocurrido cuando se estaba descargando el mundo: &6{world}&c.\n&cMira la consola para ver los detalles.
# multiverse position parse exception
mv-core.exception.positionparse.invaliddirection=&cFormato de dirección inválido: {format}. Formato esperado: <pitch>:<yaw>
mv-core.exception.positionparse.invalidcoordinates=&cFormato de coordenadas inválido: {format}. Formato esperado: <x>,<y>,<z>
mv-core.exception.positionparse.invalidnumber=&cFormato de número inválido: {number}. Se espera un valor numérico.
# generic
mv-core.generic.success=¡Correctamente!
mv-core.generic.failure=¡Fallido!
mv-core.generic.error=¡Error!
mv-core.generic.error.details=Error: {error}
mv-core.generic.null=¡Null!
mv-core.generic.you=tú
mv-core.generic.playercount={count} jugadores
mv-core.generic.teleportplayers.failed=¡No se ha podido teletransportar a uno o más jugadores fuera del mundo!
@@ -0,0 +1,458 @@
# configuration
mv-core.config.save.failed=&c&l✘ &cNie udało się zapisać configu Multiverse-Core. Zmiany będą tymczasowe!
mv-core.config.node.notfound=&c&l✘ &cNie znaleziono node'a w konfigu: &a{node}
mv-core.config.node.invalidyamlkey=&c&l✘ &cNiepoprawny klucz yaml: '&a{key}&c'. Klucze mogą zawierać tylko znaki alfanumeryczne, podkreślenia i myślniki.
mv-core.config.string.parser.invalidboolean=&c&l✘ &cNie można przekonwertować '&a{input}&c' na wartość boolean. Użyj 'true' albo 'false'
mv-core.config.string.parser.invalidinteger=&c&l✘ &cNie można przekonwertować '&a{input}&c' na liczbę. (integer)
mv-core.config.string.parser.invaliddouble=&c&l✘ &cNie można przekonwertować '&a{input}&c' na liczbę. (double)
mv-core.config.string.parser.invalidfloat=&c&l✘ &cNie można przekonwertować '&a{input}&c' na liczbę. (float)
mv-core.config.string.parser.invalidlong=&c&l✘ &cNie można przekonwertować '&a{input}&c' na liczbę. (long)
# core config
mv-core.core.config.customportalsearchradius.nonnegative=&c&l✘ &cWartość musi być większa lub równa 0.
mv-core.core.config.confirmtimeout.positive=&c&l✘ &cCzas potwierdzenia musi być liczbą dodatnią!
mv-core.core.config.debuglevel.range=&c&l✘ &cPoziom debugowania musi być od 0 do 3.
# /mv anchor
mv-core.anchor.delete.success=&a&l✔ &fAnchor '&a{anchor}&f' został usunięty.
mv-core.anchor.delete.failure=&c&l✘ &cNie udało się usunąć anchora.
mv-core.anchor.list.header=&a&l==== [ Anchory Multiverse ] ====
mv-core.anchor.list.entry=&a{anchor}&7 - &f{location}
mv-core.anchor.set.success=&a&l✔ &fAnchor '&a{anchor}&f' ustawiono na &a{location}
mv-core.anchor.set.failure=&c&l✘ &cNie udało się ustawić anchora '&a{anchor}&c'.
# /mv check
mv-core.check.description=Sprawdza czy gracz może teleportować się do celu.
mv-core.check.player.description=Gracz do sprawdzenia teleportacji.
mv-core.check.destination.description=Lokalizacja docelowa, np. nazwa świata.
mv-core.check.haspermission=&a&l✔ &a{player} &fmoże teleportować się do &a{destination}
mv-core.check.nopermission=&c&l✘ &a{player} &fnie może teleportować się do &a{destination}
mv-core.check.location=&fLokalizacja celu: &a{location}
# /mv clone
mv-core.clone.description=Klonuje świat.
mv-core.clone.world.description=Świat do sklonowania.
mv-core.clone.newworld.description=Nazwa nowego sklonowanego świata.
mv-core.clone.cloning=&e&l! &fTrwa klonowanie świata '&a{world}&f' do '&a{newworld}&f'...
mv-core.clone.success=&a&l✔ &fŚwiat został sklonowany jako '&a{world}&f'!
# /mv config
mv-core.config.description=Pokazuje lub ustawia wartość konfigu.
mv-core.config.name.description=Nazwa konfigu do ustawienia lub wyświetlenia.
mv-core.config.value.description=Wartość do ustawienia. Jeśli nie podano, wyświetli aktualną wartość.
mv-core.config.show.success=&a&l✔ &fKonfig '&a{name}&f' ma aktualnie wartość '&a{value}&f'.
mv-core.config.show.error=&c&l✘ &cNie udało się pobrać wartości konfigu '&a{name}&c'.\n&7{error}
mv-core.config.set.success=&a&l✔ &fUstawiono konfig '&a{name}&f' na '&a{value}&f'.
mv-core.config.set.error=&c&l✘ &cNie udało się ustawić konfigu '&a{name}&c' na '&a{value}&c'.\n&7{error}
# /mv confirm
mv-core.confirm.description=Potwierdza niebezpieczne komendy przed wykonaniem.
# /mv coordinates
mv-core.coordinates.description=Wysyła twoje koordynaty.
mv-core.coordinates.info.title=&a&l--- Informacje o Lokalizacji ---
mv-core.coordinates.info.world=&fŚwiat: &a{world}
mv-core.coordinates.info.alias=&fAlias: &a{alias}
mv-core.coordinates.info.worldscale=&fSkala świata: &a{scale}
mv-core.coordinates.info.coordinates=&fKoordynaty: &a{coordinates}
mv-core.coordinates.info.direction=&fKierunek: &a{direction}
# /mv create
mv-core.create.description=Tworzy nowy świat i ładuje go.
mv-core.create.name.description=Nazwa nowego świata.
mv-core.create.environment.description=Środowisko świata. Zobacz: /mv environments
mv-core.create.flags.description=Dodatkowe ustawienia świata. Zobacz https://gg.gg/nn8bl aby sprawdzić wszystkie flagi.
mv-core.create.properties=&e&l! &fTworzenie świata &a{worldName} &fz następującymi ustawieniami:
mv-core.create.properties.environment=&7- &fŚrodowisko: &a{environment}
mv-core.create.properties.seed=&7- &fSeed: &a{seed}
mv-core.create.properties.worldtype=&7- &fTyp świata: &a{worldType}
mv-core.create.properties.adjustspawn=&7- &fDostosowanie spawnu: &a{adjustSpawn}
mv-core.create.properties.biome=&7- &fBiom: &a{biome}
mv-core.create.properties.generator=&7- &fGenerator: &a{generator}
mv-core.create.properties.generatorsettings=&7- &fUstawienia generatora: &a{generatorSettings}
mv-core.create.properties.structures=&7- &fStruktury: &a{structures}
mv-core.create.loading=&e&l! &fTworzenie świata...
mv-core.create.success=&a&l✔ &fŚwiat '&a{world}&f' został utworzony!
# /mv debug
mv-core.debug.info.description=Pokazuje aktualny poziom debugowania.
mv-core.debug.info.off=&fTryb debugowania Multiverse jest &cWYŁĄCZONY&f.
mv-core.debug.info.on=&fTryb debugowania Multiverse jest ustawiony na &apoziom {level}&f.
mv-core.debug.change.description=Zmienia poziom debugowania.
mv-core.debug.change.syntax=level
mv-core.debug.change.level.description=Poziom debugowania do ustawienia.
# /mv delete
mv-core.delete.description=Usuwa świat z serwera NA ZAWSZE.
mv-core.delete.deleting=&e&l! &fUsuwanie świata '&a{world}&f'...
mv-core.delete.prompt=&c&l✘ &cCzy na pewno chcesz usunąć świat '&a{world}&c'?
mv-core.delete.success=&a&l✔ &fŚwiat '&a{world}&f' został usunięty!
# /mv dumps
mv-core.dumps.description=Wysyła informacje o wersji do konsoli lub serwisów paste.
mv-core.dumps.starting=&e&l! &fZbieranie logów i informacji debugowania...
mv-core.dumps.url.list=&a{service} &f: &a{link}
# /mv entity-spawn-config
mv-core.entityspawnconfig.info.header=&a&l==== [ Konfig spawnu encji '{world}' ] ====
mv-core.entityspawnconfig.info.category={category}:\u0020
mv-core.entityspawnconfig.info.spawn=\ \ spawn: {spawn}
mv-core.entityspawnconfig.info.tickrate=\ \ tick-rate: {tickRate}
mv-core.entityspawnconfig.info.exceptions=\ \ wyjątki: {exceptions}
mv-core.entityspawnconfig.modify.success=&a&l✔ &fUstawiono &a{property} &fna &a{value} &fdla &a{category} &fw świecie &a{world}&f.
mv-core.entityspawnconfig.modify.failure=&c&l✘ &cNie udało się ustawić &a{property} &cna &a{value} &cdla &a{category} &cw świecie &a{world}&c: {error}
# /mv gamerule set
mv-core.gamerule.set.description=Zmienia gamerule w jednym lub wielu światach.
mv-core.gamerule.set.gamerule.description=Gamerule do ustawienia.
mv-core.gamerule.set.value.description=Wartość gamerule.
mv-core.gamerule.set.world.description=Świat, w którym zastosować gamerule. Domyślnie aktualny świat gracza.
mv-core.gamerule.set.failed=&c&l✘ &cNie udało się ustawić gamerule &a{gamerule} &cna &a{value} &cw świecie &a{world}&c. &fPowinien to być typ &a{type}&f.
mv-core.gamerule.set.success.single=&a&l✔ &fUstawiono &a{gamerule} &fna &a{value} &fw świecie &a{world}&f.
mv-core.gamerule.set.success.multiple=&a&l✔ &fUstawiono &a{gamerule} &fna &a{value} &fw &a{count} &fświatach.
# /mv gamerule reset
mv-core.gamerule.reset.description=Resetuje gamerule do wartości domyślnej w jednym lub wielu światach.
mv-core.gamerule.reset.gamerule.description=Gamerule do zresetowania.
mv-core.gamerule.reset.world.description=Świat, w którym zresetować gamerule. Domyślnie aktualny świat gracza.
mv-core.gamerule.reset.failed=&c&l✘ &cNie udało się zresetować gamerule &a{gamerule} &cw świecie &a{world}&c.
mv-core.gamerule.reset.success.single=&a&l✔ &fZresetowano &a{gamerule} &fw świecie &a{world}&f.
mv-core.gamerule.reset.success.multiple=&a&l✔ &fZresetowano &a{gamerule} &fw &a{count} &fświatach.
# /mv gamerule list
mv-core.gamerule.list.description=Pokazuje listę gamerule dla wybranego świata.
mv-core.gamerule.list.description.page=Strona do wyświetlenia.
mv-core.gamerule.list.description.world=Świat, dla którego wyświetlić gamerule.
mv-core.gamerule.list.title=&a&l--- Gamerule świata {world} ---
# /mv generators
mv-core.generators.description=Pokazuje listę generatorów znanych przez Multiverse.
mv-core.generators.description.flags=Filter - pokazuje tylko pasujące wpisy. Page - numer strony do wyświetlenia.
mv-core.generators.header=&a&l====[ Lista Generatorów Multiverse ]====
mv-core.generators.empty=&c&l✘ &cNie znaleziono pluginów generatorów.
mv-core.generators.info.plugin=&fPlugin generatora: &a{plugin}
mv-core.generators.info.exampleusages=&fPrzykładowe użycia:
mv-core.generators.info.usages=&f{usages}
mv-core.generators.info.infolink=&fLink do informacji: &a{link}
# /mv import
mv-core.import.description=Importuje istniejący folder świata.
mv-core.import.name.description=Nazwa folderu świata.
mv-core.import.env.description=Środowisko świata. Zobacz: /mv env
mv-core.import.other.description=Inne ustawienia świata. Zobacz: https://gg.gg/nn8c2
mv-core.import.importing=&e&l! &fRozpoczynanie importowania świata '&a{world}&f'...
mv-core.import.success=&a&l✔ &fŚwiat '&a{world}&f' został zaimportowany!
# /mv info
mv-core.info.description=Pokazuje wszystkie ustawienia skonfigurowane dla świata.
mv-core.info.description.world=Świat, dla którego wyświetlić informacje.
mv-core.info.header=&a&l---- Informacje o Świecie: &f&l{world}&a&l ----
mv-core.info.nocontent=&c&l✘ &cNie znaleziono takiej właściwości świata!
# /mv list
mv-core.list.description=Pokazuje listę wszystkich światów, do których masz dostęp.
mv-core.list.header=&a&l====[ Lista Światów Multiverse ]====
mv-core.list.nocontent=&c&l✘ &cNie znaleziono żadnych światów lub nie masz do nich dostępu!
# /mv load
mv-core.load.description=Ładuje świat. Świat musi być już wpisany w worlds.yml, w przeciwnym razie użyj /mv import.
mv-core.load.world.description=Nazwa świata do załadowania.
mv-core.load.loading=&e&l! &fŁadowanie świata '&a{world}&f'...
mv-core.load.success=&a&l✔ &fŚwiat '&a{world}&f' został załadowany!
# /mv meta info
mv-core.meta.info.description=Pokazuje wszystkie metadane świata.
mv-core.meta.info.world.description=Świat, dla którego wyświetlić metadane.
mv-core.meta.info.header=&a&l---- Metadane Świata: &f&l{world}&a&l ----
mv-core.meta.info.nocontent=&c&l✘ &cNie znaleziono metadanych dla świata '&a{world}&c'. Możesz ustawić metadane komendą '&a/mv meta modify {world} set <klucz> <wartość>&c'!
# /mv meta modify
mv-core.meta.modify.description=Modyfikuje metadane wybranego świata.
mv-core.meta.modify.world.description=Nazwa świata, którego metadane chcesz zmodyfikować.
mv-core.meta.modify.action.description=Akcja do wykonania na metadanych. Może być 'set', 'remove' lub 'reset'.
mv-core.meta.modify.key.description=Klucz metadanych do zmodyfikowania.
mv-core.meta.modify.value.description=Wartość metadanych do ustawienia. Używane tylko przy akcji 'set'.
mv-core.meta.modify.set.success=&a&l✔ &fUstawiono klucz metadanych '&a{key}&f' na '&a{value}&f' dla świata &a{world}&f.
mv-core.meta.modify.set.failure=&c&l✘ &cNie udało się ustawić klucza metadanych '&a{key}&c' na '&a{value}&c' dla świata &a{world}&c.\n&7{error}
mv-core.meta.modify.remove.success=&a&l✔ &fUsunięto klucz metadanych '&a{key}&f' dla świata &a{world}&f.
mv-core.meta.modify.remove.failure=&c&l✘ &cNie udało się usunąć klucza metadanych '&a{key}&c' dla świata &a{world}&c.\n&7{error}
mv-core.meta.modify.invalidaction=&c&l✘ &cNiepoprawna akcja '&a{action}&c'! Poprawne akcje to tylko 'set' i 'remove'.
# /mv modify
mv-core.modify.description=Modyfikuje właściwość wybranego świata.
mv-core.modify.world.description=Nazwa świata do modyfikacji.
mv-core.modify.property.description=Właściwość do zmiany.
mv-core.modify.value.description=Wartość właściwości.
mv-core.modify.propertyremoved=&c&l✘ &cNie można zmodyfikować '&a{property}&c', ponieważ została zmieniona lub usunięta. Użyj '&a{replacement}&c' zamiast tego.
mv-core.modify.cannothavevalue=&c&l✘ &cNie możesz podać wartości dla {action} '&a{property}&c'.
mv-core.modify.specifyvalue=&c&l✘ &cMusisz podać wartość dla {action} '&a{property}&c'.
mv-core.modify.success=&a&l✔ &fPomyślnie wykonano {action} '&a{property}&f' na '&a{value}&f' w świecie &a{world}&f.
mv-core.modify.failure=&c&l✘ &cNie udało się wykonać {action} '&a{property}&c' na '&a{value}&c' w świecie &a{world}&c.\n&7{error}
mv-core.modify.failure.novalue=&c&l✘ &cNie udało się wykonać {action} '&a{property}&c' w świecie &a{world}&c.\n&7{error}
# /mv purge-all-entities
mv-core.purgeallentities.success=&a&l✔ &fWyczyszczono &a{count} &fencji w świecie &a{world}&f.
mv-core.purgeallentities.success.categories=&a&l✔ &fWyczyszczono &a{count} &fencji w świecie &a{world} &fdla kategorii spawnu &a{categories}&f.
# /mv purge-entities
mv-core.purgeentities.success=&a&l✔ &fWyczyszczono &a{count} &fencji w świecie &a{world}&f.
# /mv regen
mv-core.regen.description=Regeneruje świat na serwerze. Poprzedni stan zostanie UTRACONY NA ZAWSZE.
mv-core.regen.world.description=Świat do regeneracji.
mv-core.regen.other.description=Inne ustawienia świata. Zobacz: http://gg.gg/nn8lk
mv-core.regen.regenerating=&e&l! &fRegenerowanie świata '&a{world}&f'...
mv-core.regen.prompt=&c&l✘ &cCzy na pewno chcesz zregenerować świat '&a{world}&c'?
mv-core.regen.success=&a&l✔ &fŚwiat '&a{world}&f' został zregenerowany!
# /mv reload
mv-core.reload.description=Przeładowuje pliki konfigów wszystkich modułów Multiverse.
mv-core.reload.reloading=&e&l! &fPrzeładowywanie wszystkich konfigów pluginu Multiverse...
mv-core.reload.success=&a&l✔ &fPrzeładowanie zakończone!
# /mv remove
mv-core.remove.description=Wyładowuje świat z Multiverse i usuwa go z worlds.yml. NIE usuwa folderu świata.
mv-core.remove.world.description=Świat do usunięcia z wiedzy Multiverse.
mv-core.remove.success=&a&l✔ &fŚwiat '&a{world}&f' został usunięty!
# /mv
mv-core.root.title=&a{name} &fwersja &a{version}
mv-core.root.help=&aZobacz &f/mv help&a aby sprawdzić dostępne komendy.
# /mv setspawn
mv-core.setspawn.description=Ustawia spawn wybranego świata.
mv-core.setspawn.location.description=Lokalizacja nowego spawnu.
mv-core.setspawn.world.description=Docelowy świat do ustawienia spawnu (domyślnie aktualny świat gracza).
mv-core.setspawn.unsafe=&c&l✘ &cNowa lokalizacja spawnu jest niebezpieczna! Jeśli ma tak być, możesz wyłączyć sprawdzanie bezpieczeństwa flagą &6--unsafe&c.
mv-core.setspawn.success=&a&l✔ &fUstawiono spawn w &a{world} &fna &a{location}&f!
mv-core.setspawn.failed=&c&l✘ &cNie udało się ustawić spawnu w &a{world} &cna &a{location}&c. &7{error}
mv-core.setspawn.notmvworld=&c&l✘ &cNie można ustawić spawnu dla &a{world}&c, ponieważ nie jest to świat Multiverse.
# /mv spawn
mv-core.spawn.description=Teleportuje wybranego gracza na spawn świata, w którym się znajduje.
mv-core.spawn.player.description=Gracz.
mv-core.spawn.notmvworld=&c&l✘ &cŚwiat '&a{world}&c' nie jest światem Multiverse!
mv-core.spawn.nopermission=&c&l✘ &cNie masz uprawnień, aby użyć tej komendy w tym świecie!
mv-core.spawn.success=&a&l✔ &fTeleportowano &a{player} &fna spawn świata '&a{world}&f'!
mv-core.spawn.failed=&c&l✘ &cNie udało się teleportować &a{player} &cna spawn świata '&a{world}&c'. &7{reason}
# /mv tp
mv-core.teleport.description=Pozwala teleportować się do lokalizacji na serwerze!
mv-core.teleport.player.description=Docelowy gracz do teleportacji.
mv-core.teleport.destination.description=Lokalizacja, może być nazwa świata.
mv-core.teleport.toomanyplayers=&c&l✘ &cNie możesz teleportować więcej niż &a{count} &cgraczy naraz.
mv-core.teleport.nopermission.self=&c&l✘ &cNie masz uprawnień, aby teleportować siebie!
mv-core.teleport.nopermission.other=&c&l✘ &cNie masz uprawnień, aby teleportować innych graczy!
mv-core.teleport.nopermission.all=&c&l✘ &cNie masz uprawnień, aby teleportować wszystkich tych graczy!
mv-core.teleport.success=&a&l✔ &fTeleportowano &a{player} &fdo &a{destination}&f.
mv-core.teleport.failed=&c&l✘ &cNie udało się teleportować &a{player} &cdo &a{destination}&c. &7{reason}
# /mv unload
mv-core.unload.description=Wyładowuje świat z Multiverse. NIE usuwa folderu świata. NIE usuwa go z konfigu.
mv-core.unload.world.description=Nazwa świata do wyładowania.
mv-core.unload.unloading=&e&l! &fWyładowywanie świata '&a{world}&f'...
mv-core.unload.success=&a&l✔ &fŚwiat '&a{world}&f' został wyładowany!
# /mv usage
mv-core.usage.description=Pokazuje użycie komend Multiverse-Core.
# /mv version
mv-core.version.description=Pokazuje wersję oraz autorów.
mv-core.version.mv=&aMultiverse Core &fwersja &av{version}
mv-core.version.authors=&aAutorzy Multiverse Core: &f{authors}
mv-core.version.secretcode=&eSekretny Kod: &fFRN002
# /mv who
# /mv whoall
mv-core.who.description=Pokazuje listę graczy w wybranym świecie.
mv-core.who.all.description=Pokazuje listę graczy we wszystkich światach.
mv-core.who.world.description=Nazwa świata, z którego chcesz wyświetlić graczy.
mv-core.who.flags.description=Filtr - pokazuje tylko wpisy pasujące do filtra. Page - numer strony.
mv-core.who.empty=&7&oPusto
mv-core.who.header=&a====[ Lista Graczy Multiverse ]====
# /mv worldborder
mv-core.worldborder.center.nothingchanged=&c&l✘ &cNic nie zmieniono. World border jest już ustawiony w tym miejscu dla świata '&a{world}&c'.
mv-core.worldborder.center.success=&a&l✔ &fUstawiono środek world bordera na &a{x}&f, &a{z}&f dla świata '&a{world}&f'.
mv-core.worldborder.damageamount.nothingchanged=&c&l✘ &cNic nie zmieniono. Obrażenia world bordera są już ustawione na tę wartość dla świata '&a{world}&c'.
mv-core.worldborder.damageamount.success=&a&l✔ &fUstawiono obrażenia world bordera na &a{amount} &fza blok co sekundę dla świata '&a{world}&f'.
mv-core.worldborder.damagebuffer.nothingchanged=&c&l✘ &cNic nie zmieniono. Bufor obrażeń world bordera jest już ustawiony na tę odległość dla świata '&a{world}&c'.
mv-core.worldborder.damagebuffer.success=&a&l✔ &fUstawiono bufor obrażeń world bordera na &a{distance} &fblok(ów) dla świata '&a{world}&f'.
mv-core.worldborder.get.size=&fWorld border ma aktualnie szerokość &a{size} &fblok(ów) dla świata '&a{world}&f'.
mv-core.worldborder.set.nothingchanged=&c&l✘ &cNic nie zmieniono. World border ma już taki rozmiar dla świata '&a{world}&c'.
mv-core.worldborder.set.immediate=&a&l✔ &fUstawiono world border na szerokość &a{size} &fblok(ów) dla świata '&a{world}&f'.
mv-core.worldborder.set.shrinking=&e&l! &fZmniejszanie world bordera do &a{size} &fblok(ów) przez &a{time} &fsekundy dla świata '&a{world}&f'.
mv-core.worldborder.set.growing=&e&l! &fPowiększanie world bordera do &a{size} &fblok(ów) przez &a{time} &fsekundy dla świata '&a{world}&f'.
mv-core.worldborder.warningdistance.nothingchanged=&c&l✘ &cNic nie zmieniono. Ostrzeżenie world bordera jest już ustawione na tę odległość dla świata '&a{world}&c'.
mv-core.worldborder.warningdistance.success=&a&l✔ &fUstawiono odległość ostrzeżenia world bordera na &a{distance} &fblok(ów) dla świata '&a{world}&f'.
mv-core.worldborder.warningtime.nothingchanged=&c&l✘ &cNic nie zmieniono. Ostrzeżenie world bordera jest już ustawione na ten czas dla świata '&a{world}&c'.
mv-core.worldborder.warningtime.success=&a&l✔ &fUstawiono czas ostrzeżenia world bordera na &a{time} &fsekundy dla świata '&a{world}&f'.
mv-core.worldborder.roundoff.warning=&c&l✘ &cWersja twojego serwera nie obsługuje ustawiania czasu w tickach. Czas zostanie zaokrąglony do najbliższej sekundy z &a{fromTime}&c do &a{toTime}&c sekund.
# commands error
mv-core.commands.error.playersonly=&c&l✘ &cTa komenda może być używana tylko przez graczy.
mv-core.commands.error.multiverseworldonly=&c&l✘ &cTo może być używane tylko w światach Multiverse.
mv-core.commands.error.loadedmultiverseworld.playersonly=&c&l✘ &cTa komenda może być używana tylko przez gracza w załadowanym świecie Multiverse.
mv-core.commands.error.loadedmultiverseworld.issuer=&c&l✘ &cNie jesteś w załadowanym świecie Multiverse. Podaj nazwę świata Multiverse albo użyj tej komendy w załadowanym świecie Multiverse.
mv-core.commands.error.loadedmultiverseworld.inputconsole=&c&l✘ &cŚwiat '&a{world}&c' nie jest załadowanym światem Multiverse. Pamiętaj, aby podać nazwę świata, gdy używasz tej komendy w konsoli.
mv-core.commands.error.loadedmultiverseworld.input=&c&l✘ &cŚwiat &a{world} &cnie jest załadowanym światem Multiverse.
mv-core.commands.error.multiverseworld.playersonly=&c&l✘ &cTa komenda może być używana tylko przez gracza w świecie Multiverse.
mv-core.commands.error.multiverseworld.issuer=&c&l✘ &cNie jesteś w świecie Multiverse. Podaj nazwę świata Multiverse albo użyj tej komendy w świecie Multiverse.
mv-core.commands.error.multiverseworld.inputconsole=&c&l✘ &cŚwiat '&a{world}&c' nie jest światem Multiverse. Pamiętaj, aby podać nazwę świata, gdy używasz tej komendy w konsoli.
mv-core.commands.error.multiverseworld.input=&c&l✘ &cŚwiat &a{world} &cnie jest światem Multiverse.
mv-core.commands.error.player.issuerinput=&c&l✘ &cNiepoprawny gracz: &a{player}&c. Podaj gracza online albo użyj tej komendy jako gracz.
mv-core.commands.error.player.input=&c&l✘ &cNie znaleziono gracza &a{player}&c.
mv-core.commands.error.player.selector=&c&l✘ &cŻaden gracz nie pasuje do selektora: &a{player}&c.
mv-core.commands.error.players.input=&c&l✘ &cNie znaleziono gracza/graczy &a{player}&c.
mv-core.commands.error.resolve.player=&c&l✘ &cNie można rozwiązać kontekstu dla gracza '&a{player}&c'.
mv-core.commands.error.resolve.input=&c&l✘ &cNie można rozwiązać kontekstu dla wejścia '&a{input}&c'.
mv-core.commands.error.removeplayers.nodefault=&c&l✘ &cNie znaleziono domyślnego świata, więc flaga --remove-players wymaga argumentu celu.
# entry check
mv-core.entrycheck.blacklisted=&c&l✘ &cŚwiat '&a{world}&c' jest na czarnej liście.
mv-core.entrycheck.notenoughmoney=&c&l✘ &cNie masz wystarczającej ilości pieniędzy na opłatę za wejście. Wymagana kwota: &a{amount}
mv-core.entrycheck.cannotpayentryfee=&c&l✘ &cNie masz możliwości zapłacenia opłaty za wejście.
mv-core.entrycheck.exceedplayerlimit=&c&l✘ &cŚwiat osiągnął limit graczy.
mv-core.entrycheck.noworldaccess=&c&l✘ &cNie masz dostępu do tego świata.
# economy
mv-core.economy.item.deposit=&a&l✔ &fDo twojego ekwipunku dodano &a{price}&f.
mv-core.economy.item.withdraw=&e&l! &fPobrano od ciebie &a{price}&f.
mv-core.economy.vault.deposit=&a&l✔ &fDodano &a{price} &fna twoje konto.
mv-core.economy.vault.withdraw=&e&l! &fPobrano z twojego konta &a{price}&f.
# multiverse parse destination failure reason
mv-core.destination.anchor.failurereason.anchornotfound=&c&l✘ &cAnchor '&a{anchor}&c' nie istnieje!
mv-core.destination.anchor.display=anchor '{anchor}'
mv-core.destination.bed.failurereason.playernotfound=&c&l✘ &cGracz '&a{player}&c' nie istnieje lub nie jest online! Aby teleportować się do własnego łóżka, użyj 'playerbed'.
mv-core.destination.bed.display.own=twoje łóżko/punkt odrodzenia
mv-core.destination.bed.display.other=łóżko/punkt odrodzenia gracza {player}
mv-core.destination.cannon.failurereason.invalidformat=&c&l✘ &cPoprawny format teleportu cannon to: &aca:worldname:x,y,z:pitch:yaw:speed
mv-core.destination.exact.failurereason.invalidformat=&c&l✘ &cPoprawny format teleportu exact to: &ae:worldname:x,y,z:pitch:yaw
mv-core.destination.player.failurereason.playernotfound=&c&l✘ &cGracz '&a{player}&c' nie istnieje lub nie jest online!
mv-core.destination.shared.failurereason.invalidcoordinatesformat=&c&l✘ &cMusisz podać poprawne koordynaty w formacie: &a{x},{y},{z}
mv-core.destination.shared.failurereason.invalidnumberformat=&c&l✘ &cNiepoprawne dane teleportu. {error}
mv-core.destination.shared.failurereason.worldnotfound=&c&l✘ &cŚwiat '&a{world}&c' nie istnieje lub nie jest załadowany!
mv-core.destination.parse.failurereason.invaliddestinationid=&c&l✘ &cNiepoprawne ID destynacji: &a{id}&c. Poprawne ID: &a{ids}
# portal form disabled
mv-core.portalform.disabled.end=&c&l✘ &cTworzenie portali Endu jest wyłączone w tym świecie.
mv-core.portalform.disabled.nether=&c&l✘ &cTworzenie portali Netheru jest wyłączone w tym świecie.
# teleport failure reason
mv-core.teleportfailurereason.null.destination=&c&l✘ &cDestynacja jest niepoprawna!
mv-core.teleportfailurereason.null.location=&c&l✘ &cLokalizacja jest niepoprawna!
mv-core.teleportfailurereason.null.world=&c&l✘ &cDocelowy świat jest wyładowany lub nie istnieje!
mv-core.teleportfailurereason.unsafe.location=&e&l! &eTa lokalizacja jest uznawana za niebezpieczną! Jeśli robisz to celowo, użyj flagi &a--unsafe&e.
mv-core.teleportfailurereason.teleport.failed=&c&l✘ &cTeleportacja została odrzucona. Upewnij się, że inny plugin nie blokuje teleportacji i sprawdź logi serwera.
mv-core.teleportfailurereason.teleport.failed.exception=&c&l✘ &cWystąpił błąd podczas teleportacji. Sprawdź logi serwera po więcej informacji.
mv-core.teleportfailurereason.event.cancelled=&c&l✘ &cTeleportacja została anulowana przez inny plugin.
# alias name conflict
mv-core.aliasnameconflict.detected=&c&l✘ &cWykryto konflikt aliasu dla świata '&a{world}&c' z aliasem '&a{alias}&c'. Może to utrudnić czytelność listy światów i ich wybieranie!
mv-core.aliasnameconflict.duplicatealias= - Konflikt z aliasem świata '&a{world}&r': '&a{alias}&r'
mv-core.aliasnameconflict.duplicateworldname= - Konflikt z nazwą świata: '&a{world}&r'
# world manager result
mv-core.cloneworld.invalidworldname=&c&l✘ &cŚwiat '&a{world}&c' zawiera niedozwolone znaki!
mv-core.cloneworld.worldexistfolder=&c&l✘ &cŚwiat '&a{world}&c' istnieje już w folderach serwera! Musisz go najpierw usunąć przed klonowaniem.
mv-core.cloneworld.worldexistunloaded=&c&l✘ &cŚwiat '&a{world}&c' już istnieje i jest wyładowany! Musisz go najpierw usunąć przed klonowaniem.
mv-core.cloneworld.worldexistloaded=&c&l✘ &cŚwiat '&a{world}&c' już istnieje! Musisz go najpierw usunąć przed klonowaniem.
mv-core.cloneworld.fromworldfolderinvalid=&c&l✘ &cFolder źródłowego świata '&a{world}&c' nie wygląda na poprawny świat! Upewnij się, że zawiera foldery data oraz region.
mv-core.cloneworld.copyfailed=&c&l✘ &cNie udało się skopiować świata do '&a{world}&c': {error}\n&fSprawdź konsolę po więcej informacji.
mv-core.createworld.invalidworldname=&c&l✘ &cŚwiat '&a{world}&c' zawiera niedozwolone znaki!
mv-core.createworld.worldexistfolder=&c&l✘ &cŚwiat '&a{world}&c' już istnieje w folderach serwera! &fWpisz '&a/mv import {world} <environment>&f', jeśli chcesz go zaimportować.
mv-core.createworld.worldexistunloaded=&e&l! &eŚwiat '&a{world}&e' już istnieje, ale nie jest załadowany! &fWpisz '&a/mv load {world}&f', jeśli chcesz go załadować.
mv-core.createworld.worldexistloaded=&c&l✘ &cŚwiat '&a{world}&c' już istnieje!
mv-core.deleteworld.worldnonexistent=&c&l✘ &cNie znaleziono świata '&a{world}&c'!
mv-core.deleteworld.loadfailed=&c&l✘ &cNie można załadować świata '&a{world}&c'. Czy folder świata został usunięty lub uszkodzony? &fMożesz wykonać '&a/mv remove {world}&f', aby usunąć go z Multiverse lub spróbować ponownie '&a/mv load {world}&f'.
mv-core.deleteworld.worldfoldernotfound=&c&l✘ &cNie można znaleźć folderu '&a{world}&c'! Możesz wykonać '&a/mv remove {world}&c', aby usunąć świat z Multiverse i ręcznie usunąć folder, jeśli istnieje.
mv-core.deleteworld.failedtodeletefolder=&c&l✘ &cNie udało się usunąć folderu świata '&a{world}&c': {error}\n&fSprawdź konsolę po więcej informacji.
mv-core.importworld.invalidworldname=&c&l✘ &cŚwiat '&a{world}&c' zawiera niedozwolone znaki!
mv-core.importworld.worldexistunloaded=&e&l! &eŚwiat '&a{world}&e' już istnieje, ale nie jest załadowany! &fWpisz '&a/mv load {world}&f', jeśli chcesz go załadować.
mv-core.importworld.worldexistloaded=&c&l✘ &cŚwiat '&a{world}&c' już istnieje!
mv-core.importworld.worldfolderinvalid=&c&l✘ &cFolder świata '&a{world}&c' nie wygląda na poprawny świat! Upewnij się, że zawiera poprawną strukturę folderów data oraz region.\n&eJeśli oprogramowanie serwera działa inaczej lub masz pewność, że świat jest poprawny, użyj flagi '&a--skip-folder-check&e'.
mv-core.importworld.bukkitenvironmentmismatch=&c&l✘ &cWykryto niezgodność environment! &fŚwiat '&a{world}&f' jest już załadowany z environment '&a{bukkitEnvironment}&f', ale Multiverse próbuje zaimportować go jako '&a{mvEnvironment}&f'.
mv-core.importworld.bukkitnamespacedkeymismatch=&c&l✘ &cWykryto niezgodność namespace! &fŚwiat '&a{world}&f' jest już załadowany z namespace '&a{bukkitNamespace}&f', ale Multiverse próbuje zaimportować go jako '&a{mvNamespace}&f'.
mv-core.loadworld.worldalreadyloading=&e&l! &eŚwiat '&a{world}&e' jest już ładowany! Poczekaj...
mv-core.loadworld.worldnonexistent=&c&l✘ &cŚwiat '&a{world}&c' nie istnieje! Użyj '&a/mv create {world} <environment>&f', aby go stworzyć.
mv-core.loadworld.worldexistfolder=&e&l! &eŚwiat '&a{world}&e' istnieje w folderach serwera, ale nie jest znany Multiverse! &fWpisz '&a/mv import {world} <environment>&f', jeśli chcesz go zaimportować.
mv-core.loadworld.worldexistloaded=&e&l! &eŚwiat '&a{world}&e' jest już załadowany!
mv-core.loadworld.bukkitenvironmentmismatch=&c&l✘ &cWykryto niezgodność environment! &fŚwiat '&a{world}&f' jest już załadowany z environment '&a{bukkitEnvironment}&f', ale Multiverse próbuje załadować go jako '&a{mvEnvironment}&f'.
mv-core.loadworld.bukkitnamespacedkeymismatch=&c&l✘ &cWykryto niezgodność namespace! &fŚwiat '&a{world}&f' jest już załadowany z namespace '&a{bukkitNamespace}&f', ale Multiverse próbuje załadować go jako '&a{mvNamespace}&f'.
mv-core.removeworld.worldnonexistent=&c&l✘ &cNie znaleziono świata '&a{world}&c'!
mv-core.unloadworld.worldalreadyunloading=&e&l! &eŚwiat '&a{world}&e' jest już wyładowywany! Poczekaj...
mv-core.unloadworld.worldnonexistent=&c&l✘ &cŚwiat '&a{world}&c' nie istnieje!
mv-core.unloadworld.worldunloaded=&e&l! &eŚwiat '&a{world}&e' jest już wyładowany!
mv-core.unloadworld.bukkitunloadfailed=&c&l✘ &cBukkit nie mógł wyładować świata '&a{world}&c': {error}
mv-core.worldcreator.invalidbiomeprovider=&c&l✘ &cNiepoprawny provider biomu '&a{biome}&c'! {error}\n&fSprawdź konsolę po więcej informacji.
mv-core.worldcreator.invalidchunkgenerator=&c&l✘ &cNiepoprawny generator chunków '&a{generator}&c'! {error}\n&eTo najprawdopodobniej problem z pluginem generatora, a NIE z Multiverse. Sprawdź konsolę po więcej informacji.
mv-core.worldcreator.bukkitcreationfailed=&c&l✘ &cBukkit nie mógł stworzyć świata '&a{world}&c': {error}\n&fSprawdź konsolę po więcej informacji.
# queue command result
mv-core.queuecommand.defaultprompt=&e&l! &fKomenda, którą próbujesz uruchomić, jest uznawana za niebezpieczną.
mv-core.queuecommand.prompt=&e&l! &fUruchom komendę &a{command} &faby kontynuować. Wygaśnie za {timeout} sekund.
mv-core.queuecommand.expired=&c&l✘ &cTwoja komenda w kolejce wygasła.
mv-core.queuecommand.nocommandinqueue=&c&l✘ &cNie masz żadnych komend w kolejce.
mv-core.queuecommand.invalidotp=&c&l✘ &cNiepoprawny kod OTP '&a{otp}&c'. Spróbuj ponownie...
mv-core.queuecommand.commandexecutionerror=&c&l✘ &cWystąpił błąd podczas wykonywania komendy z kolejki: {error}\n&fSprawdź konsolę po więcej informacji.
# content display messages
mv-core.contentdisplay.nocontent=&c&l✘ &cBrak treści do wyświetlenia.
mv-core.contentdisplay.filter=&7[Filtr '{filter}']
mv-core.contentdisplay.page=&7[Strona {current} z {total}]
mv-core.contentdisplay.pagefilter={page} {filter}
mv-core.contentdisplay.invalidpage=&c&l✘ &cNiepoprawny numer strony. Podaj numer od &a1&c do &a{total}&c.
mv-core.contentdisplay.empty=&7&oPusto
mv-core.contentdisplay.null=&7&oBrak
# multiverse world exception
mv-core.exception.multiverseworld.createnull=&c&l✘ &cTworzenie świata zwróciło null!
mv-core.exception.multiverseworld.unloaddefaultworld=&c&l✘ &cNie możesz wyładować domyślnego świata! Świat &a{world}&c jest ustawiony jako domyślny w server.properties pod '&alevel-name&c'.
mv-core.exception.multiverseworld.unloadplayersinworld=&c&l✘ &cW świecie nadal znajduje się &a{count}&c gracz(y)! Użyj flagi '&a--remove-players&c', aby teleportować wszystkich graczy poza świat.
mv-core.exception.multiverseworld.unloaderror=&c&l✘ &cWystąpił nieznany błąd podczas wyładowywania świata: &a{world}&c.\n&fSprawdź konsolę po więcej informacji.
# multiverse position parse exception
mv-core.exception.positionparse.invaliddirection=&c&l✘ &cNiepoprawny format kierunku: {format}. Oczekiwany format: <pitch>:<yaw>
mv-core.exception.positionparse.invalidcoordinates=&c&l✘ &cNiepoprawny format koordynatów: {format}. Oczekiwany format: <x>,<y>,<z>
mv-core.exception.positionparse.invalidnumber=&c&l✘ &cNiepoprawny format liczby: {number}. Oczekiwano wartości liczbowej.
# world key parse failures
mv-core.worldkeyparse.empty=&c&l✘ &cNazwa/klucz świata nie może być pusta!
mv-core.worldkeyparse.invalidworldname=&c&l✘ &cNazwa świata '{world}' zawiera niedozwolone znaki. Dozwolone znaki to: a-z, A-Z, 0-9, _, - oraz . (kropka)
mv-core.worldkeyparse.invalidnamespacedkey=&c&l✘ &cKlucz namespace '{namespace}' zawiera niedozwolone znaki. Oczekiwany format klucza namespace to 'namespace:key'. Dozwolone znaki to: a-z (tylko małe litery), 0-9, _, - oraz . (kropka), z jednym znakiem ':'.
mv-core.worldkeyparse.namespacedkeyunsupported=&c&l✘ &cOprogramowanie twojego serwera nie obsługuje kluczy namespace, ale nazwa świata '{namespace}' jest w formacie klucza namespace! Zmień klucz świata na stary format bez ':' albo przejdź na PaperMC, który obsługuje klucze namespace.
# generic
mv-core.generic.success=&a&l✔ &aSukces!
mv-core.generic.failure=&c&l✘ &cNiepowodzenie!
mv-core.generic.error=&c&l✘ &cBłąd!
mv-core.generic.error.details=&c&l✘ &cBłąd: {error}
mv-core.generic.null=&7Brak!
mv-core.generic.you=ty
mv-core.generic.playercount={count} gracz(y)
mv-core.generic.teleportplayers.failed=&c&l✘ &cNie udało się teleportować jednego lub więcej graczy poza świat!
@@ -1,6 +1,25 @@
# configuration
mv-core.config.save.failed=Не удалось сохранить файл config.yml Multiverse-Core. Ваши изменения будут временными!
mv-core.config.node.notfound=Узел не найден в конфигурации: {node}
mv-core.config.node.invalidyamlkey=Недопустимый ключ yaml: '{key}'. Ключи могут содержать только буквы, цифры, подчеркивания и дефисы.
mv-core.config.string.parser.invalidboolean=Не удалось преобразовать '{input}' в логическое значение. Используйте 'true' или 'false'
mv-core.config.string.parser.invalidinteger=Не удалось преобразовать '{input}' в число. (целое число)
mv-core.config.string.parser.invaliddouble=Не удалось преобразовать '{input}' в число. (double)
mv-core.config.string.parser.invalidfloat=Не удалось преобразовать '{input}' в число. (float)
mv-core.config.string.parser.invalidlong=Не удалось преобразовать '{input}' в число. (long)
# core config
mv-core.core.config.customportalsearchradius.nonnegative=Значение должно быть больше или равно 0.
mv-core.core.config.confirmtimeout.positive=Время подтверждения должно быть положительным числом!
mv-core.core.config.debuglevel.range=Уровень отладки должен быть от 0 до 3.
# /mv anchor
mv-core.anchor.delete.success=&aЯкорь &f{anchor}&a удален.
mv-core.anchor.delete.failure=&cНе удалось удалить якорь.
mv-core.anchor.list.header=&3==== [ Якоря Multiverse ] ====
mv-core.anchor.list.entry=&a{anchor}&7 - &f{location}
mv-core.anchor.set.success=&aЯкорь &f{anchor}&a установлен на &f{location}
mv-core.anchor.set.failure=&cНе удалось установить якорь &f{anchor}.
# /mv check
mv-core.check.description=Проверяет, может ли игрок телепортироваться в указанное место.
@@ -13,7 +32,7 @@ mv-core.check.location=Местоположение пункта назначе
# /mv clone
mv-core.clone.description=Клонирует мир.
mv-core.clone.world.description=Целевой мир для клонирования.
mv-core.clone.newWorld.description=Название нового клонированного мира.
mv-core.clone.newworld.description=Название нового клонированного мира.
mv-core.clone.cloning=Клонирование мира '{world}' в '{newworld}'...
mv-core.clone.success=&aМир клонирован в '{world}'!
@@ -34,7 +53,7 @@ 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.worldscale=&bМасштаб мира: &f{scale}
mv-core.coordinates.info.coordinates=&bКоординаты: &f{coordinates}
mv-core.coordinates.info.direction=&bНаправление: &f{direction}
@@ -73,6 +92,15 @@ mv-core.delete.success=&aМир '{world}' удален!
mv-core.dumps.description=Выводит информацию о версии в консоль или заливает в paste-сервисы
mv-core.dumps.url.list={service} : {link}
# /mv entity-spawn-config
mv-core.entityspawnconfig.info.header===== [ Настройка спавна сущностей '{world}' ] ====
mv-core.entityspawnconfig.info.category={category}:\u0020
mv-core.entityspawnconfig.info.spawn=\ \ spawn: {spawn}
mv-core.entityspawnconfig.info.tickrate=\ \ tick-rate: {tickRate}
mv-core.entityspawnconfig.info.exceptions=\ \ исключения: {exceptions}
mv-core.entityspawnconfig.modify.success=Успешно установлено {property} на {value} для {category} в {world}
mv-core.entityspawnconfig.modify.failure=Не удалось установить {property} на {value} для {category} в {world}: {error}
# /mv gamerule set
mv-core.gamerule.set.description=Изменяет игровое правило в одном или нескольких мирах.
mv-core.gamerule.set.gamerule.description=Игровое правило для установки.
@@ -99,7 +127,12 @@ mv-core.gamerule.list.title= --- Игровые правила для {world} --
# /mv generators
mv-core.generators.description=Выводит список генераторов, известных Multiverse
mv-core.generators.description.flags=Фильтр - показывает только соответствующие записи. Страница - страница для показа
mv-core.generators.header=&b====[ Список генераторов Multiverse ]====
mv-core.generators.empty=&cПлагины-генераторы не найдены.
mv-core.generators.info.plugin=&rПлагин генератора: {plugin}
mv-core.generators.info.exampleusages=&rПримеры использования:\u0020
mv-core.generators.info.usages=&r{usages}
mv-core.generators.info.infolink=&rСсылка на подробности: {link}
# /mv import
mv-core.import.description=Импортирует существующую папку мира.
@@ -137,6 +170,13 @@ mv-core.modify.success=&aУспешно {action} '&9{property}&a' на '&9{value
mv-core.modify.failure=&cНе удалось {action} '&9{property}&c' на '&9{value}&c' в мире &9{world}&c.\n&c{error}
mv-core.modify.failure.novalue=&cНе удалось {action} '&9{property}&c' в мире &9{world}&c.\n&c{error}
# /mv purge-all-entities
mv-core.purgeallentities.success=Успешно удалено {count} сущностей в мире {world}.
mv-core.purgeallentities.success.categories=Успешно удалено {count} сущностей в мире {world} для категорий спавна {categories}.
# /mv purge-entities
mv-core.purgeentities.success=Успешно удалено {count} сущностей в мире {world}.
# /mv regen
mv-core.regen.description=Регенерирует мир на вашем сервере. Предыдущее состояние будет потеряно НАВСЕГДА.
mv-core.regen.world.description=Мир, который вы хотите регенерировать.
@@ -167,6 +207,8 @@ mv-core.setspawn.world.description=Целевой мир для установк
# /mv spawn
mv-core.spawn.description=Телепортирует указанного игрока на спавн мира, в котором он находится
mv-core.spawn.player.description=Игрок
mv-core.spawn.notmvworld=&cМир '{world}' не является миром Multiverse!
mv-core.spawn.nopermission=&cУ вас нет прав использовать эту команду в этом мире!
mv-core.spawn.success=Телепортирован {player} на спавн '{world}'!
mv-core.spawn.failed=Не удалось телепортировать {player} на спавн '{world}'. {reason}
@@ -175,6 +217,9 @@ mv-core.teleport.description=Позволяет телепортироватьс
mv-core.teleport.player.description=Целевой игрок для телепортации.
mv-core.teleport.destination.description=Местоположение, может быть название мира.
mv-core.teleport.toomanyplayers=&cВы не можете телепортировать более {count} игроков одновременно.
mv-core.teleport.nopermission.self=&cУ вас нет прав телепортировать себя!
mv-core.teleport.nopermission.other=&cУ вас нет прав телепортировать других игроков!
mv-core.teleport.nopermission.all=&cУ вас нет прав телепортировать всех этих игроков!
mv-core.teleport.success=Телепортирован {player} в {destination}.
mv-core.teleport.failed=Не удалось телепортировать {player} в {destination}. {reason}
@@ -228,6 +273,21 @@ mv-core.worldborder.warningtime.success=&rУстановлено время пр
# commands error
mv-core.commands.error.playersonly=&cЭта команда может использоваться только игроками
mv-core.commands.error.multiverseworldonly=&cЭто может использоваться только в мирах multiverse
mv-core.commands.error.loadedmultiverseworld.playersonly=Эта команда может использоваться только игроком в загруженном мире Multiverse.
mv-core.commands.error.loadedmultiverseworld.issuer=Вы не находитесь в загруженном мире Multiverse. Укажите имя мира Multiverse или используйте эту команду в загруженном мире Multiverse.
mv-core.commands.error.loadedmultiverseworld.inputconsole=Мир '{world}' не является загруженным миром Multiverse. Не забудьте указать имя мира при использовании команды из консоли.
mv-core.commands.error.loadedmultiverseworld.input=Мир {world} не является загруженным миром Multiverse.
mv-core.commands.error.multiverseworld.playersonly=Эта команда может использоваться только игроком в мире Multiverse.
mv-core.commands.error.multiverseworld.issuer=Вы не находитесь в мире Multiverse. Укажите имя мира Multiverse или используйте эту команду в мире Multiverse.
mv-core.commands.error.multiverseworld.inputconsole=Мир '{world}' не является миром Multiverse. Не забудьте указать имя мира при использовании команды из консоли.
mv-core.commands.error.multiverseworld.input=Мир {world} не является миром Multiverse.
mv-core.commands.error.player.issuerinput=Недопустимый игрок: {player}. Укажите игрока онлайн или используйте эту команду как игрок.
mv-core.commands.error.player.input=Игрок {player} не найден.
mv-core.commands.error.player.selector=Ни один игрок не соответствует селектору: {player}.
mv-core.commands.error.players.input=Игрок(и) {player} не найдены.
mv-core.commands.error.resolve.player=Не удалось определить контекст для игрока '{player}'.
mv-core.commands.error.resolve.input=Не удалось определить контекст для ввода '{input}'.
mv-core.commands.error.removeplayers.nodefault=Основной мир не найден, поэтому флаг --remove-players требует аргумент назначения.
# entry check
mv-core.entrycheck.blacklisted=Мир '{world}' в черном списке.
@@ -244,7 +304,10 @@ 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.anchor.display=якорь '{anchor}'
mv-core.destination.bed.failurereason.playernotfound=&cИгрок '&6{player}&c' не существует или не в сети! Для телепортации к собственной кровати используйте 'playerbed'.
mv-core.destination.bed.display.own=ваша кровать/точка возрождения
mv-core.destination.bed.display.other=кровать/точка возрождения игрока {player}
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' не существует или не в сети!
@@ -262,6 +325,11 @@ mv-core.teleportfailurereason.teleport.failed=Что-то отклонило т
mv-core.teleportfailurereason.teleport.failed.exception=Произошла ошибка во время телепортации. См. консоль для подробностей.
mv-core.teleportfailurereason.event.cancelled=Телепортация была отменена другим плагином.
# alias name conflict
mv-core.aliasnameconflict.detected=Обнаружен конфликт псевдонима для мира '{world}' с псевдонимом '{alias}'. Это может затруднить понимание списка миров и их выбор!
mv-core.aliasnameconflict.duplicatealias= - Конфликтует с псевдонимом мира '{world}': '{alias}'
mv-core.aliasnameconflict.duplicateworldname= - Конфликтует с названием мира: '{world}'
# world manager result
mv-core.cloneworld.invalidworldname=Мир '{world}' содержит недопустимые символы!
mv-core.cloneworld.worldexistfolder=Мир '{world}' существует в папках сервера! Вам нужно удалить его перед клонированием.
@@ -301,6 +369,9 @@ mv-core.worldcreator.invalidchunkgenerator=&cНедопустимый генер
mv-core.worldcreator.bukkitcreationfailed=&cBukkit не удалось создать мир '{world}': {error}\n&cСм. консоль для подробностей.
# queue command result
mv-core.queuecommand.defaultprompt=Команда, которую вы пытаетесь выполнить, считается опасной.
mv-core.queuecommand.prompt=Выполните &a{command} &fдля продолжения. Команда истечет через {timeout} секунд.
mv-core.queuecommand.expired=Ваша команда в очереди истекла.
mv-core.queuecommand.nocommandinqueue=&cУ вас нет команд в очереди.
mv-core.queuecommand.invalidotp=&cНеверный OTP номер '&6{otp}&c'. Пожалуйста, попробуйте снова...
mv-core.queuecommand.commandexecutionerror=&cОшибка выполнения команды из очереди: {error}\n&fСм. консоль для подробностей.
@@ -309,6 +380,7 @@ mv-core.queuecommand.commandexecutionerror=&cОшибка выполнения
mv-core.contentdisplay.nocontent=&cНет содержимого для отображения.
mv-core.contentdisplay.filter=&7[Фильтр '{filter}']
mv-core.contentdisplay.page=&7[Страница {current} из {total}]
mv-core.contentdisplay.pagefilter={page} {filter}
mv-core.contentdisplay.invalidpage=&cНеверный номер страницы. Пожалуйста, введите номер страницы между &31&c и &3{total}&c.
mv-core.contentdisplay.empty=&7&oпусто
mv-core.contentdisplay.null=&7&onull
@@ -319,9 +391,17 @@ mv-core.exception.multiverseworld.unloaddefaultworld=&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}. Ожидается числовое значение.
# generic
mv-core.generic.success=Успешно!
mv-core.generic.failure=Неудача!
mv-core.generic.error=Ошибка!
mv-core.generic.error.details=Ошибка: {error}
mv-core.generic.null=Null!
mv-core.generic.you=вы
mv-core.generic.playercount={count} игроков
mv-core.generic.teleportplayers.failed=Не удалось телепортировать одного или нескольких игроков из мира!
@@ -0,0 +1,449 @@
# configuration
mv-core.config.save.failed=Multiverse-Core config.yml dosyası kaydedilemedi. Değişiklikleriniz geçici olacak!
mv-core.config.node.notfound=Yapılandırmada düğüm bulunamadı: {node}
mv-core.config.node.invalidyamlkey=Geçersiz yaml anahtarı: '{key}'. Anahtarlar yalnızca alfanümerik karakterler, alt çizgi ve tire içerebilir.
mv-core.config.string.parser.invalidboolean='{input}' boolean değerine dönüştürülemiyor. Lütfen 'true' veya 'false' kullanın.
mv-core.config.string.parser.invalidinteger='{input}' sayıya dönüştürülemiyor. (tam sayı)
mv-core.config.string.parser.invaliddouble='{input}' sayıya dönüştürülemiyor. (double)
mv-core.config.string.parser.invalidfloat='{input}' sayıya dönüştürülemiyor. (float)
mv-core.config.string.parser.invalidlong='{input}' sayıya dönüştürülemiyor. (long)
# core config
mv-core.core.config.customportalsearchradius.nonnegative=Değer 0'dan büyük veya 0'a eşit olmalıdır.
mv-core.core.config.confirmtimeout.positive=Onay zaman aşımı pozitif bir sayı olmalıdır!
mv-core.core.config.debuglevel.range=Hata ayıklama seviyesi 0 ile 3 arasında olmalıdır.
# /mv anchor
mv-core.anchor.delete.success=&a&f{anchor}&a çapası silindi.
mv-core.anchor.delete.failure=&cÇapa silinemedi.
mv-core.anchor.list.header=&3==== [ Multiverse Çapaları ] ====
mv-core.anchor.list.entry=&a{anchor}&7 - &f{location}
mv-core.anchor.set.success=&a&f{anchor}&a çapası &f{location}&a olarak ayarlandı.
mv-core.anchor.set.failure=&c&f{anchor} çapası ayarlanamadı.
# /mv check
mv-core.check.description=Bir oyuncunun belirtilen hedefe ışınlanıp ışınlanamayacağını kontrol eder.
mv-core.check.player.description=Hedef kontrolü yapılacak oyuncu.
mv-core.check.destination.description=Bir hedef konum, örneğin bir dünya adı.
mv-core.check.haspermission=&a{player} {destination} konumuna ışınlanabilir
mv-core.check.nopermission=&c{player} {destination} konumuna ışınlanamaz
mv-core.check.location=Hedefin konumu: &f{location}
# /mv clone
mv-core.clone.description=Bir dünyayı kopyalar.
mv-core.clone.world.description=Kopyalanacak hedef dünya.
mv-core.clone.newworld.description=Yeni kopyalanan dünyanın adı.
mv-core.clone.cloning='{world}' dünyası '{newworld}' olarak kopyalanıyor...
mv-core.clone.success=&aDünya '{world}' olarak kopyalandı!
# /mv config
mv-core.config.description=Bir yapılandırma değerini gösterir veya ayarlar.
mv-core.config.name.description=Ayarlanacak veya gösterilecek yapılandırmanın adı.
mv-core.config.value.description=Yapılandırmanın ayarlanacağı değer. Belirtilmezse mevcut değer gösterilir.
mv-core.config.show.success=&a'&6{name}&a' yapılandırması şu anda '&6{value}&a' olarak ayarlı.
mv-core.config.show.error=&c'&6{name}&c' için yapılandırma değeri alınamadı.\n&c{error}
mv-core.config.set.success=&a'&6{name}&a' yapılandırması başarıyla '&6{value}&a' olarak ayarlandı.
mv-core.config.set.error=&c'&6{name}&c' yapılandırması '&6{value}&c' olarak ayarlanamadı.\n&c{error}
# /mv confirm
mv-core.confirm.description=Tehlikeli komutları çalıştırmadan önce onaylar.
# /mv coordinates
mv-core.coordinates.description=Koordinatlarınızı gösterir
mv-core.coordinates.info.title=&b--- Konum Bilgisi ---
mv-core.coordinates.info.world=&bDünya: &f{world}
mv-core.coordinates.info.alias=&bTakma Ad: &f{alias}
mv-core.coordinates.info.worldscale=&bDünya Ölçeği: &f{scale}
mv-core.coordinates.info.coordinates=&bKoordinatlar: &f{coordinates}
mv-core.coordinates.info.direction=&bYön: &f{direction}
# /mv create
mv-core.create.description=Yeni bir dünya oluşturur ve yükler.
mv-core.create.name.description=Yeni dünya adı.
mv-core.create.environment.description=Dünyanın ortamı. Bkz: /mv environments
mv-core.create.flags.description=Ek dünya ayarları. Tüm olası seçenekler için bkz: https://gg.gg/nn8bl
mv-core.create.properties={worldName} dünyası şu özelliklerle oluşturuluyor:
mv-core.create.properties.environment=- Ortam: &f{environment}
mv-core.create.properties.seed=- Tohum: &f{seed}
mv-core.create.properties.worldtype=- Dünya Türü: &f{worldType}
mv-core.create.properties.adjustspawn=- Doğuş Noktasını Ayarla: &f{adjustSpawn}
mv-core.create.properties.biome=- Biyom: &f{biome}
mv-core.create.properties.generator=- Üretici: &f{generator}
mv-core.create.properties.generatorsettings=- Üretici Ayarları: &f{generatorSettings}
mv-core.create.properties.structures=- Yapılar: &f{structures}
mv-core.create.loading=Dünya oluşturuluyor...
mv-core.create.success=&a'{world}' dünyası oluşturuldu!
# /mv debug
mv-core.debug.info.description=Mevcut hata ayıklama seviyesini gösterir.
mv-core.debug.info.off=&fMultiverse Hata Ayıklama modu &cKAPALI&f.
mv-core.debug.info.on=&fMultiverse Hata Ayıklama modu &aseviye {level}&f konumunda.
mv-core.debug.change.description=Hata ayıklama seviyesini değiştirir.
mv-core.debug.change.syntax=seviye
mv-core.debug.change.level.description=Ayarlanacak hata ayıklama seviyesi.
# /mv delete
mv-core.delete.description=Sunucunuzdaki bir dünyayı KALICI OLARAK siler.
mv-core.delete.deleting='{world}' dünyası siliniyor...
mv-core.delete.prompt='{world}' dünyasını silmek istediğinizden emin misiniz?
mv-core.delete.success=&a'{world}' dünyası silindi!
# /mv dumps
mv-core.dumps.description=Sürüm bilgilerini konsola veya yapıştırma servislerine aktarır
mv-core.dumps.starting=Günlükler ve hata ayıklama bilgileri toplanıyor...
mv-core.dumps.url.list=&a{service} servisiyle yüklendi: &6{link}
# /mv entity-spawn-config
mv-core.entityspawnconfig.info.header===== [ Varlık Doğuş Yapılandırması '{world}' ] ====
mv-core.entityspawnconfig.info.category={category}:
mv-core.entityspawnconfig.info.spawn=\ \ doğuş: {spawn}
mv-core.entityspawnconfig.info.tickrate=\ \ tik-hızı: {tickRate}
mv-core.entityspawnconfig.info.exceptions=\ \ istisnalar: {exceptions}
mv-core.entityspawnconfig.modify.success={world} dünyasında {category} için {property} başarıyla {value} olarak ayarlandı
mv-core.entityspawnconfig.modify.failure={world} dünyasında {category} için {property}, {value} olarak ayarlanamadı: {error}
# /mv gamerule set
mv-core.gamerule.set.description=Bir veya daha fazla dünyada oyun kuralını değiştirir.
mv-core.gamerule.set.gamerule.description=Ayarlanacak oyun kuralı.
mv-core.gamerule.set.value.description=Oyun kuralının değeri.
mv-core.gamerule.set.world.description=Oyun kuralının uygulanacağı dünya, varsayılan olarak oyuncunun bulunduğu dünya.
mv-core.gamerule.set.failed={world} dünyasında {gamerule} oyun kuralı {value} olarak ayarlanamadı. &fBu {type} türünde olmalıdır.
mv-core.gamerule.set.success.single=&a{world} dünyasında {gamerule} başarıyla {value} olarak ayarlandı.
mv-core.gamerule.set.success.multiple=&a{count} dünyada {gamerule} başarıyla {value} olarak ayarlandı.
# /mv gamerule reset
mv-core.gamerule.reset.description=Bir veya daha fazla dünyada oyun kuralını varsayılan değerine sıfırlar.
mv-core.gamerule.reset.gamerule.description=Sıfırlanacak oyun kuralı.
mv-core.gamerule.reset.world.description=Oyun kuralının sıfırlanacağı dünya, varsayılan olarak oyuncunun bulunduğu dünya.
mv-core.gamerule.reset.failed={world} dünyasında {gamerule} oyun kuralı sıfırlanamadı.
mv-core.gamerule.reset.success.single=&a{world} dünyasında {gamerule} başarıyla sıfırlandı.
mv-core.gamerule.reset.success.multiple=&a{count} dünyada {gamerule} başarıyla sıfırlandı.
# /mv gamerule list
mv-core.gamerule.list.description=Belirtilen dünyanın oyun kurallarını listeler.
mv-core.gamerule.list.description.page=Görüntülenecek sayfa.
mv-core.gamerule.list.description.world=Oyun kuralları listelenecek dünya.
mv-core.gamerule.list.title= --- {world} Dünyasının Oyun Kuralları ---
# /mv generators
mv-core.generators.description=Multiverse tarafından bilinen üreticileri listeler
mv-core.generators.description.flags=Filtre - yalnızca eşleşen girişleri gösterir. Sayfa - gösterilecek sayfa
mv-core.generators.header=&b====[ Multiverse Üretici Listesi ]====
mv-core.generators.empty=&cHiçbir Üretici Eklentisi bulunamadı.
mv-core.generators.info.plugin=&rÜretici Eklentisi: {plugin}
mv-core.generators.info.exampleusages=&rÖrnek kullanımlar:
mv-core.generators.info.usages=&r{usages}
mv-core.generators.info.infolink=&rDaha fazla bilgi için bağlantı: {link}
# /mv import
mv-core.import.description=Mevcut bir dünya klasörünü içe aktarır.
mv-core.import.name.description=Dünya klasörünün adı.
mv-core.import.env.description=Dünyanın ortamı. Bkz: /mv env
mv-core.import.other.description=Diğer dünya ayarları. Bkz: https://gg.gg/nn8c2
mv-core.import.importing='{world}' dünyasının içe aktarımı başlatılıyor...
mv-core.import.success=&a'{world}' dünyası içe aktarıldı!
# /mv info
mv-core.info.description=Bir dünya için yapılandırılmış tüm özellikleri gösterir.
mv-core.info.description.world=Bilgi gösterilecek dünya.
mv-core.info.header=&a&l---- Dünya Bilgisi: &f&l{world}&a&l ----
mv-core.info.nocontent=&cBöyle bir dünya özelliği bulunamadı!
# /mv list
mv-core.list.description=Girebileceğiniz tüm dünyaların listesini gösterir.
mv-core.list.header=&6====[ Multiverse Dünya Listesi ]====
mv-core.list.nocontent=&cHiçbir dünya bulunamadı veya bunlara erişim izniniz yok!
# /mv load
mv-core.load.description=Bir dünyayı yükler. Dünya zaten worlds.yml içinde olmalıdır, aksi takdirde lütfen /mv import kullanın.
mv-core.load.world.description=Yüklemek istediğiniz dünyanın adı.
mv-core.load.loading='{world}' dünyası yükleniyor...
mv-core.load.success=&a'{world}' dünyası yüklendi!
# /mv meta info
mv-core.meta.info.description=Bir dünya için tüm meta verileri gösterir.
mv-core.meta.info.world.description=Meta verileri gösterilecek dünya.
mv-core.meta.info.header=&a&l---- Dünya Meta Verisi: &f&l{world}&a&l ----
mv-core.meta.info.nocontent=&c'{world}' dünyası için meta veri bulunamadı. '&6/mv meta modify {world} set <anahtar> <değer>&c' komutuyla meta veri ayarlayabilirsiniz!
# /mv meta modify
mv-core.meta.modify.description=Belirtilen bir dünyanın meta verisini değiştirir.
mv-core.meta.modify.world.description=Meta verisini değiştirmek istediğiniz dünyanın adı.
mv-core.meta.modify.action.description=Meta veri üzerinde gerçekleştirilecek işlem. 'set', 'remove' veya 'reset' olabilir.
mv-core.meta.modify.key.description=Değiştirilecek meta veri anahtarı.
mv-core.meta.modify.value.description=Meta verinin ayarlanacağı değer. Yalnızca işlem 'set' olduğunda kullanılır.
mv-core.meta.modify.set.success=&a&9{world}&a dünyası için '&9{key}&a' meta veri anahtarı başarıyla '&9{value}&a' olarak ayarlandı.
mv-core.meta.modify.set.failure=&c&9{world}&c dünyası için '&9{key}&c' meta veri anahtarı '&9{value}&c' olarak ayarlanamadı.\n&c{error}
mv-core.meta.modify.remove.success=&a&9{world}&a dünyası için '&9{key}&a' meta veri anahtarı başarıyla kaldırıldı.
mv-core.meta.modify.remove.failure=&c&9{world}&c dünyası için '&9{key}&c' meta veri anahtarı kaldırılamadı.\n&c{error}
mv-core.meta.modify.invalidaction=&c'&6{action}&c' geçersiz bir işlem! Geçerli işlemler yalnızca 'set' ve 'remove' seçenekleridir.
# /mv modify
mv-core.modify.description=Belirtilen bir dünyanın özelliğini değiştirir.
mv-core.modify.world.description=Değiştirmek istediğiniz dünyanın adı.
mv-core.modify.property.description=Değiştirmek istediğiniz özellik.
mv-core.modify.value.description=Özelliğin değeri.
mv-core.modify.propertyremoved=&c'&3{property}&c' değiştirilmiş veya kaldırılmış olduğundan düzenlenemiyor. Lütfen '&a{replacement}&c' kullanın.
mv-core.modify.cannothavevalue={action} '{property}' için bir değer belirtemezsiniz.
mv-core.modify.specifyvalue={action} '{property}' için bir değer belirtmeniz gerekiyor.
mv-core.modify.success=&a&9{world}&a dünyasında '&9{property}&a' başarıyla '&9{value}&a' olarak {action}.
mv-core.modify.failure=&c&9{world}&c dünyasında '&9{property}&c' '&9{value}&c' olarak {action} yapılamadı.\n&c{error}
mv-core.modify.failure.novalue=&c&9{world}&c dünyasında '&9{property}&c' üzerinde {action} yapılamadı.\n&c{error}
# /mv purge-all-entities
mv-core.purgeallentities.success={world} dünyasında {count} varlık başarıyla temizlendi.
mv-core.purgeallentities.success.categories={world} dünyasında {categories} doğuş kategorileri için {count} varlık başarıyla temizlendi.
# /mv purge-entities
mv-core.purgeentities.success={world} dünyasında {count} varlık başarıyla temizlendi.
# /mv regen
mv-core.regen.description=Sunucunuzdaki bir dünyayı yeniden oluşturur. Önceki durum KALICI OLARAK kaybolacaktır.
mv-core.regen.world.description=Yeniden oluşturmak istediğiniz dünya.
mv-core.regen.other.description=Diğer dünya ayarları. Bkz: http://gg.gg/nn8lk
mv-core.regen.regenerating='{world}' dünyası yeniden oluşturuluyor...
mv-core.regen.prompt='{world}' dünyasını yeniden oluşturmak istediğinizden emin misiniz?
mv-core.regen.success=&a'{world}' dünyası yeniden oluşturuldu!
# /mv reload
mv-core.reload.description=Tüm Multiverse modülleri için yapılandırma dosyalarını yeniden yükler.
mv-core.reload.reloading=&6Tüm Multiverse Eklenti yapılandırmaları yeniden yükleniyor...
mv-core.reload.success=&aYeniden yükleme tamamlandı!
# /mv remove
mv-core.remove.description=Bir dünyayı Multiverse'den kaldırır ve worlds.yml dosyasından siler. Bu işlem dünya klasörünü SİLMEZ.
mv-core.remove.world.description=Multiverse'den kaldırmak istediğiniz dünya.
mv-core.remove.success=&a'{world}' dünyası kaldırıldı!
# /mv
mv-core.root.title=&a{name} sürüm {version}
mv-core.root.help=&aMevcut komutlar için &f/mv help&a komutuna bakın.
# /mv setspawn
mv-core.setspawn.description=Belirtilen dünyanın doğuş noktasını ayarlar
mv-core.setspawn.location.description=Yeni doğuş noktasının konumu
mv-core.setspawn.world.description=Doğuş noktası ayarlanacak hedef dünya (varsayılan olarak oyuncunun bulunduğu dünya)
mv-core.setspawn.unsafe=&cYeni doğuş noktası güvensiz! Bu kasıtlıysa, güvenlik kontrollerini &6--unsafe&c bayrağıyla devre dışı bırakabilirsiniz.
mv-core.setspawn.success=&a&6{world}&a dünyasındaki doğuş noktası başarıyla &6{location}&a olarak ayarlandı!
mv-core.setspawn.failed=&c&6{world}&c dünyasındaki doğuş noktası &6{location}&c olarak ayarlanamadı. {error}
mv-core.setspawn.notmvworld=&c{world} için doğuş noktası ayarlanamıyor çünkü bu bir Multiverse dünyası değil.
# /mv spawn
mv-core.spawn.description=Belirtilen oyuncuyu bulunduğu dünyanın doğuş noktasına ışınlar
mv-core.spawn.player.description=Oyuncu
mv-core.spawn.notmvworld=&c'{world}' dünyası bir Multiverse dünyası değil!
mv-core.spawn.nopermission=&cBu dünyada bu komutu kullanma izniniz yok!
mv-core.spawn.success={player}, '{world}' dünyasının doğuş noktasına ışınlandı!
mv-core.spawn.failed={player}, '{world}' dünyasının doğuş noktasına ışınlanamadı. {reason}
# /mv tp
mv-core.teleport.description=Sunucunuzdaki bir konuma ışınlanmanızı sağlar!
mv-core.teleport.player.description=Işınlanacak hedef oyuncu.
mv-core.teleport.destination.description=Konum, bir dünya adı olabilir.
mv-core.teleport.toomanyplayers=&cAynı anda {count}'den fazla oyuncu ışınlayamazsınız.
mv-core.teleport.nopermission.self=&cKendinizi ışınlama izniniz yok!
mv-core.teleport.nopermission.other=&cDiğer oyuncuları ışınlama izniniz yok!
mv-core.teleport.nopermission.all=&cBu oyuncuların tamamını ışınlama izniniz yok!
mv-core.teleport.success={player}, {destination} konumuna ışınlandı.
mv-core.teleport.failed={player}, {destination} konumuna ışınlanamadı. {reason}
# /mv unload
mv-core.unload.description=Bir dünyayı Multiverse'den kaldırır. Bu işlem dünya klasörünü SİLMEZ. Bu işlem onu yapılandırma dosyasından da KALDIRMAZ.
mv-core.unload.world.description=Kaldırmak istediğiniz dünyanın adı.
mv-core.unload.unloading='{world}' dünyası kaldırılıyor...
mv-core.unload.success=&a'{world}' dünyası kaldırıldı!
# /mv usage
mv-core.usage.description=Multiverse-Core komut kullanımını gösterir.
# /mv version
mv-core.version.description=Sürümü ve yazarları gösterir
mv-core.version.mv=Multiverse Core Sürümü &fv{version}
mv-core.version.authors=Multiverse Core Yazarları &f{authors}
mv-core.version.secretcode=Özel Kod: &fFRN002
# /mv who
# /mv whoall
mv-core.who.description=Belirtilen dünyadaki oyuncuları listeler
mv-core.who.all.description=Tüm dünyalardaki oyuncuları listeler
mv-core.who.world.description=Oyuncuları listelemek istediğiniz dünyanın adı
mv-core.who.flags.description=Filtre - yalnızca eşleşen girişleri gösterir. Sayfa - gösterilecek sayfa
mv-core.who.empty=&7&oboş
mv-core.who.header=&b====[ Multiverse Dünya Oyuncu Listesi ]====
# /mv worldborder
mv-core.worldborder.center.nothingchanged=&cHiçbir şey değişmedi. Dünya sınırı zaten '&6{world}&c' dünyası için orada ortalanmış.
mv-core.worldborder.center.success=&r'&6{world}&r' dünyası için dünya sınırının merkezi &6{x}&r, &6{z}&r olarak ayarlandı.
mv-core.worldborder.damageamount.nothingchanged=&cHiçbir şey değişmedi. '&6{world}&c' dünyası için dünya sınırı hasarı zaten o miktarda.
mv-core.worldborder.damageamount.success=&r'&6{world}&r' dünyası için dünya sınırı hasarı saniyede blok başına {amount} olarak ayarlandı.
mv-core.worldborder.damagebuffer.nothingchanged=&cHiçbir şey değişmedi. '&6{world}&c' dünyası için dünya sınırı hasar tamponu zaten o mesafede.
mv-core.worldborder.damagebuffer.success=&r'&6{world}&r' dünyası için dünya sınırı hasar tamponu {distance} blok olarak ayarlandı.
mv-core.worldborder.get.size=&r'&6{world}&r' dünyası için dünya sınırı şu anda {size} blok genişliğinde.
mv-core.worldborder.set.nothingchanged=&cHiçbir şey değişmedi. '&6{world}&c' dünyası için dünya sınırı zaten o boyutta.
mv-core.worldborder.set.immediate=&f'&6{world}&f' dünyası için dünya sınırı &6{size}&f blok genişliğine ayarlandı.
mv-core.worldborder.set.shrinking=&f'&6{world}&f' dünyası için dünya sınırı &6{time}&f saniye içinde &6{size}&f blok genişliğine küçültülüyor.
mv-core.worldborder.set.growing=&f'&6{world}&f' dünyası için dünya sınırı &6{time}&f saniye içinde &6{size} blok genişliğine büyütülüyor.
mv-core.worldborder.warningdistance.nothingchanged=&cHiçbir şey değişmedi. '&6{world}&c' dünyası için dünya sınırı uyarısı zaten o mesafede.
mv-core.worldborder.warningdistance.success=&f'&6{world}&f' dünyası için dünya sınırı uyarı mesafesi &6{distance}&f blok olarak ayarlandı.
mv-core.worldborder.warningtime.nothingchanged=&cHiçbir şey değişmedi. '&6{world}&c' dünyası için dünya sınırı uyarısı zaten o süre için ayarlı.
mv-core.worldborder.warningtime.success=&f'&6{world}&f' dünyası için dünya sınırı uyarı süresi &6{time}&f saniye olarak ayarlandı.
mv-core.worldborder.roundoff.warning=&cSunucu sürümünüz tik cinsinden süre ayarlamayı desteklemiyor. Bu nedenle &6{fromTime}&c saniyeden &6{toTime}&c saniyeye yuvarlanacak.
# commands error
mv-core.commands.error.playersonly=&cBu komut yalnızca oyuncular tarafından kullanılabilir
mv-core.commands.error.multiverseworldonly=&cBu yalnızca Multiverse dünyalarında kullanılabilir
mv-core.commands.error.loadedmultiverseworld.playersonly=Bu komut yalnızca yüklü bir Multiverse Dünyasındaki bir oyuncu tarafından kullanılabilir.
mv-core.commands.error.loadedmultiverseworld.issuer=Yüklü bir Multiverse dünyasında değilsiniz. Bir Multiverse dünya adı belirtin ya da bu komutu yüklü bir Multiverse dünyasında kullanın.
mv-core.commands.error.loadedmultiverseworld.inputconsole='{world}' dünyası yüklü bir Multiverse dünyası değil. Konsolda bu komutu kullanırken dünya adını belirtmeyi unutmayın.
mv-core.commands.error.loadedmultiverseworld.input={world} dünyası yüklü bir Multiverse dünyası değil.
mv-core.commands.error.multiverseworld.playersonly=Bu komut yalnızca bir Multiverse Dünyasındaki bir oyuncu tarafından kullanılabilir.
mv-core.commands.error.multiverseworld.issuer=Bir Multiverse dünyasında değilsiniz. Bir Multiverse dünya adı belirtin ya da bu komutu bir Multiverse dünyasında kullanın.
mv-core.commands.error.multiverseworld.inputconsole='{world}' dünyası bir Multiverse dünyası değil. Konsolda bu komutu kullanırken dünya adını belirtmeyi unutmayın.
mv-core.commands.error.multiverseworld.input={world} dünyası bir Multiverse dünyası değil.
mv-core.commands.error.player.issuerinput=Geçersiz oyuncu: {player}. Çevrimiçi bir oyuncu belirtin ya da bu komutu bir oyuncu olarak kullanın.
mv-core.commands.error.player.input={player} oyuncusu bulunamadı.
mv-core.commands.error.player.selector={player} seçicisiyle eşleşen oyuncu bulunamadı.
mv-core.commands.error.players.input={player} oyuncusu/oyuncuları bulunamadı.
mv-core.commands.error.resolve.player='{player}' oyuncusu için bağlam çözümlenemiyor.
mv-core.commands.error.resolve.input='{input}' girdisi için bağlam çözümlenemiyor.
mv-core.commands.error.removeplayers.nodefault=Varsayılan dünya bulunamadı, bu nedenle --remove-players bayrağı bir hedef argümanı gerektirir.
# entry check
mv-core.entrycheck.blacklisted='{world}' dünyası kara listede.
mv-core.entrycheck.notenoughmoney=Giriş ücretini ödemek için yeterli paran yok. {amount} ödemen gerekiyor.
mv-core.entrycheck.cannotpayentryfee=Giriş ücretini ödeme imkânınız yok.
mv-core.entrycheck.exceedplayerlimit=Dünya oyuncu limitine ulaştı.
mv-core.entrycheck.noworldaccess=Bu dünyaya erişim izniniz yok.
# economy
mv-core.economy.item.deposit=&a{price}&f envanterinize yatırıldı.
mv-core.economy.item.withdraw=&f&a{price}&f tutarında ücret alındı.
mv-core.economy.vault.deposit=&2{price}&f hesabınıza yatırıldı.
mv-core.economy.vault.withdraw=&f&2{price}&f tutarında ücret alındı.
# multiverse parse destination failure reason
mv-core.destination.anchor.failurereason.anchornotfound=&c'&6{anchor}&c' çapası mevcut değil!
mv-core.destination.anchor.display='{anchor}' çapası
mv-core.destination.bed.failurereason.playernotfound=&c'&6{player}&c' oyuncusu mevcut değil veya çevrimiçi değil! Kendi yatağınıza ışınlanmak için 'playerbed' kullanın.
mv-core.destination.bed.display.own=kendi yatağınız/yeniden doğuş noktanız
mv-core.destination.bed.display.other={player} adlı oyuncunun yatağı/yeniden doğuş noktası
mv-core.destination.cannon.failurereason.invalidformat=&cTop hedef biçimi şöyle olmalıdır: &6ca:dunyaadi:x,y,z:egim:sapma:hiz
mv-core.destination.exact.failurereason.invalidformat=&cKesin hedef biçimi şöyle olmalıdır: &6e:dunyaadi:x,y,z:egim:sapma
mv-core.destination.player.failurereason.playernotfound=&c'&6{player}&c' oyuncusu mevcut değil veya çevrimiçi değil!
mv-core.destination.shared.failurereason.invalidcoordinatesformat=Bu biçimde geçerli bir koordinat belirtmelisiniz: &6{x},{y},{z}
mv-core.destination.shared.failurereason.invalidnumberformat=&cGeçersiz hedef girişi. {error}
mv-core.destination.shared.failurereason.worldnotfound=&c'&6{world}&c' dünyası mevcut değil veya yüklü değil!
mv-core.destination.parse.failurereason.invaliddestinationid=Geçersiz hedef kimliği: {id}. Geçerli hedef kimlikleri: {ids}
# portal form disabled
mv-core.portalform.disabled.end=&cBu dünyada son portal oluşturma devre dışı.
mv-core.portalform.disabled.nether=&cBu dünyada nether portal oluşturma devre dışı.
# teleport failure reason
mv-core.teleportfailurereason.null.destination=Hedef geçersiz!
mv-core.teleportfailurereason.null.location=Konum geçersiz!
mv-core.teleportfailurereason.null.world=&cHedef dünya yüklü değil veya mevcut değil!
mv-core.teleportfailurereason.unsafe.location=Konum güvensiz kabul ediliyor! Bu kasıtlıysa, geçersiz kılmak için `--unsafe` bayrağını kullanın.
mv-core.teleportfailurereason.teleport.failed=Işınlama reddedildi. Başka bir eklentinin ışınlamayı engellemediğinden emin olun ve hatalar için sunucu günlüklerini kontrol edin.
mv-core.teleportfailurereason.teleport.failed.exception=Işınlama sırasında bir hata oluştu. Daha fazla ayrıntı için sunucu günlüklerini kontrol edin.
mv-core.teleportfailurereason.event.cancelled=Işınlama başka bir eklenti tarafından iptal edildi.
# alias name conflict
mv-core.aliasnameconflict.detected='{alias}' takma adına sahip '{world}' dünyası için takma ad çakışması algılandı. Bu, dünya listesini ve seçimini kafa karıştırıcı hale getirebilir!
mv-core.aliasnameconflict.duplicatealias= - '{world}' dünyasının takma adıyla çakışıyor: '{alias}'
mv-core.aliasnameconflict.duplicateworldname= - Dünya adıyla çakışıyor: '{world}'
# world manager result
mv-core.cloneworld.invalidworldname='{world}' dünyası geçersiz karakterler içeriyor!
mv-core.cloneworld.worldexistfolder='{world}' dünyası sunucu klasörlerinde mevcut! Kopyalamadan önce silmeniz gerekiyor.
mv-core.cloneworld.worldexistunloaded='{world}' dünyası zaten mevcut ve yüklü değil! Kopyalamadan önce silmeniz gerekiyor.
mv-core.cloneworld.worldexistloaded='{world}' dünyası zaten mevcut! Kopyalamadan önce silmeniz gerekiyor.
mv-core.cloneworld.fromworldfolderinvalid=Kaynak dünya '{world}' klasörünün içeriği geçerli bir dünya gibi görünmüyor! Doğru dünya yapısına sahip olduğundan emin olun.
mv-core.cloneworld.copyfailed=Dünya '{world}' konumuna kopyalanamadı: {error}\n&fDaha fazla ayrıntı için konsola bakın.
mv-core.createworld.invalidworldname='{world}' dünyası geçersiz karakterler içeriyor!
mv-core.createworld.worldexistfolder='{world}' dünyası sunucu klasörlerinde zaten mevcut!&f İçe aktarmak istiyorsanız '&a/mv import {world} <ortam>&f' yazın.
mv-core.createworld.worldexistunloaded='{world}' dünyası zaten mevcut, ancak yüklü değil!&f Yüklemek istiyorsanız '&a/mv load {world}&f' yazın.
mv-core.createworld.worldexistloaded='{world}' dünyası zaten mevcut!
mv-core.deleteworld.worldnonexistent='{world}' dünyası bulunamadı!
mv-core.deleteworld.loadfailed='{world}' dünyası yüklenemiyor, dünya klasörü silinmiş veya bozulmuş mu?&f Multiverse'den kaldırmak için '&a/mv remove {world}&f' çalıştırabilir ya da '&a/mv load {world}&f' ile yeniden yüklemeyi deneyebilirsiniz.
mv-core.deleteworld.worldfoldernotfound=&c'{world}' klasörü bulunamıyor! Multiverse'den kaldırmak için '&a/mv remove {world}&c' yapabilir ve varsa klasörü manuel olarak silebilirsiniz.
mv-core.deleteworld.failedtodeletefolder='{world}' dünya klasörü silinemedi: {error}\n&fDaha fazla ayrıntı için konsola bakın.
mv-core.importworld.invalidworldname='{world}' dünyası geçersiz karakterler içeriyor!
mv-core.importworld.worldexistunloaded='{world}' dünyası zaten mevcut, ancak yüklü değil!&f Yüklemek istiyorsanız '&a/mv load {world}&f' yazın.
mv-core.importworld.worldexistloaded='{world}' dünyası zaten mevcut!
mv-core.importworld.worldfolderinvalid='{world}' dünya klasörünün içeriği geçerli bir dünya gibi görünmüyor! Doğru veri ve bölge klasörleri içerdiğinden emin olun.\n&cSunucu yazılımı dünya klasörleriyle farklı bir şey yapıyorsa veya dünyanın geçerli olduğundan çok eminseniz, '&3--skip-folder-check&c' bayrağını kullanın.
mv-core.importworld.bukkitenvironmentmismatch=Ortam uyuşmazlığı tespit edildi!&f '{world}' dünyası '{bukkitEnvironment}' ortamıyla zaten yüklü, ancak Multiverse onu '{mvEnvironment}' ortamıyla içe aktarmaya çalışıyor.
mv-core.importworld.bukkitnamespacedkeymismatch=Ad alanı uyuşmazlığı tespit edildi!&f '{world}' dünyası '{bukkitNamespace}' ad alanıyla zaten yüklü, ancak Multiverse onu '{mvNamespace}' ad alanıyla içe aktarmaya çalışıyor.
mv-core.loadworld.worldalreadyloading='{world}' dünyası zaten yükleniyor! Lütfen bekleyin...
mv-core.loadworld.worldnonexistent='{world}' dünyası bulunamadı! Oluşturmak için '&a/mv create {world} <ortam>&f' kullanın.
mv-core.loadworld.worldexistfolder='{world}' dünyası sunucu klasörlerinde mevcut, ancak Multiverse tarafından bilinmiyor!&f İçe aktarmak istiyorsanız '&a/mv import {world} <ortam>&f' yazın.
mv-core.loadworld.worldexistloaded='{world}' dünyası zaten yüklü!
mv-core.loadworld.bukkitenvironmentmismatch=Ortam uyuşmazlığı tespit edildi!&f '{world}' dünyası '{bukkitEnvironment}' ortamıyla zaten yüklü, ancak Multiverse onu '{mvEnvironment}' ortamıyla yüklemeye çalışıyor.
mv-core.loadworld.bukkitnamespacedkeymismatch=Ad alanı uyuşmazlığı tespit edildi!&f '{world}' dünyası '{bukkitNamespace}' ad alanıyla zaten yüklü, ancak Multiverse onu '{mvNamespace}' ad alanıyla yüklemeye çalışıyor.
mv-core.removeworld.worldnonexistent='{world}' dünyası bulunamadı!
mv-core.unloadworld.worldalreadyunloading='{world}' dünyası zaten kaldırılıyor! Lütfen bekleyin...
mv-core.unloadworld.worldnonexistent='{world}' dünyası mevcut değil!
mv-core.unloadworld.worldunloaded='{world}' dünyası zaten kaldırılmış!
mv-core.unloadworld.bukkitunloadfailed=Bukkit '{world}' dünyasını kaldıramadı: {error}
mv-core.worldcreator.invalidbiomeprovider=&cGeçersiz biyom sağlayıcısı '&6{biome}&c'! {error}\n&cDaha fazla ayrıntı için konsola bakın.
mv-core.worldcreator.invalidchunkgenerator=&cGeçersiz yığın üreticisi '&6{generator}&c'! {error}\n&cBu büyük olasılıkla Multiverse değil, üretici eklentinizle ilgili bir sorundur. Daha fazla ayrıntı için konsola bakın.
mv-core.worldcreator.bukkitcreationfailed=&cBukkit '{world}' dünyasını oluşturamadı: {error}\n&cDaha fazla ayrıntı için konsola bakın.
# queue command result
mv-core.queuecommand.defaultprompt=Çalıştırmaya çalıştığınız komut tehlikeli kabul edilmektedir.
mv-core.queuecommand.prompt=Devam etmek için &a{command}&f komutunu çalıştırın. Bu işlem {timeout} saniye içinde sona erecek.
mv-core.queuecommand.expired=Kuyruktaki komutunuzun süresi doldu.
mv-core.queuecommand.nocommandinqueue=&cKuyrukta bekleyen komutunuz yok.
mv-core.queuecommand.invalidotp=&cGeçersiz OTP numarası '&6{otp}&c'. Lütfen tekrar deneyin...
mv-core.queuecommand.commandexecutionerror=&cKuyruktaki komut çalıştırılırken hata: {error}\n&fDaha fazla ayrıntı için konsola bakın.
# content display messages
mv-core.contentdisplay.nocontent=&cGösterilecek içerik yok.
mv-core.contentdisplay.filter=&7[Filtre '{filter}']
mv-core.contentdisplay.page=&7[Sayfa {current} / {total}]
mv-core.contentdisplay.pagefilter={page} {filter}
mv-core.contentdisplay.invalidpage=&cGeçersiz sayfa numarası. Lütfen &31&c ile &3{total}&c arasında bir sayfa numarası girin.
mv-core.contentdisplay.empty=&7&oboş
mv-core.contentdisplay.null=&7&onull
# multiverse world exception
mv-core.exception.multiverseworld.createnull=Oluşturulan dünya null döndü!
mv-core.exception.multiverseworld.unloaddefaultworld=&cVarsayılan dünyayı kaldıramazsınız! &6{world}&c dünyası, server.properties dosyasındaki '&6level-name&c' olarak tanımlanmış varsayılan dünyadır.
mv-core.exception.multiverseworld.unloadplayersinworld=&cDünyada hâlâ &6{count}&c oyuncu var! Tüm oyuncuları dünyadan çıkarmak için komutunuza '&6--remove-players&c' bayrağını ekleyin.
mv-core.exception.multiverseworld.unloaderror=&c&6{world}&c dünyası kaldırılırken bilinmeyen bir hata oluştu.\n&cDaha fazla ayrıntı için konsola bakın.
# multiverse position parse exception
mv-core.exception.positionparse.invaliddirection=&cGeçersiz yön dizgisi biçimi: {format}. Beklenen biçim: <egim>:<sapma>
mv-core.exception.positionparse.invalidcoordinates=&cGeçersiz koordinat biçimi: {format}. Beklenen biçim: <x>,<y>,<z>
mv-core.exception.positionparse.invalidnumber=&cGeçersiz sayı biçimi: {number}. Sayısal bir değer bekleniyor.
# world key parse failures
mv-core.worldkeyparse.empty=&cDünya adı/anahtarı boş olamaz!
mv-core.worldkeyparse.invalidworldname=&c'{world}' dünya adı geçersiz karakterler içeriyor. İzin verilen karakterler: a-z, A-Z, 0-9, _, - ve . (nokta)
mv-core.worldkeyparse.invalidnamespacedkey=&c'{namespace}' ad alanı anahtarı geçersiz karakterler içeriyor. Beklenen biçim 'ad-alani:anahtar' şeklindedir. İzin verilen karakterler: a-z (yalnızca küçük harf), 0-9, _, - ve . (nokta) ile tek bir ':'.
mv-core.worldkeyparse.namespacedkeyunsupported=&cSunucu yazılımınız ad alanı anahtarlarını desteklemiyor, ancak '{namespace}' dünya adı ad alanı anahtarı biçiminde! Lütfen dünya anahtarını ':' içermeyen eski biçime değiştirin veya ad alanı anahtarlarını destekleyen PaperMC'ye geçin.
# generic
mv-core.generic.success=Başarılı!
mv-core.generic.failure=Başarısız!
mv-core.generic.error=Hata!
mv-core.generic.error.details=Hata: {error}
mv-core.generic.null=Null!
mv-core.generic.you=sen
mv-core.generic.playercount={count} oyuncu
mv-core.generic.teleportplayers.failed=Bir veya daha fazla oyuncu dünyadan ışınlanamadı!
@@ -1,6 +1,25 @@
# configuration
mv-core.config.save.failed=保存 Multiverse-Core 的 config.yml 文件失败,所有修改都将是临时的!
mv-core.config.node.notfound=在配置文件中无法找到节点: {node}
mv-core.config.node.invalidyamlkey=无效的 yaml 键:'{key}'。键只能包含字母、数字、下划线和连字符。
mv-core.config.string.parser.invalidboolean=无法将 '{input}' 转换为布尔值。请使用 'true' 或 'false'
mv-core.config.string.parser.invalidinteger=无法将 '{input}' 转换为数字。(整数)
mv-core.config.string.parser.invaliddouble=无法将 '{input}' 转换为数字。(double)
mv-core.config.string.parser.invalidfloat=无法将 '{input}' 转换为数字。(float)
mv-core.config.string.parser.invalidlong=无法将 '{input}' 转换为数字。(long)
# core config
mv-core.core.config.customportalsearchradius.nonnegative=值必须大于或等于 0。
mv-core.core.config.confirmtimeout.positive=确认超时时间必须是正数!
mv-core.core.config.debuglevel.range=调试等级必须在 0 到 3 之间。
# /mv anchor
mv-core.anchor.delete.success=&a锚点 &f{anchor}&a 已删除。
mv-core.anchor.delete.failure=&c删除锚点失败。
mv-core.anchor.list.header=&3==== [ Multiverse 锚点 ] ====
mv-core.anchor.list.entry=&a{anchor}&7 - &f{location}
mv-core.anchor.set.success=&a锚点 &f{anchor}&a 已设置到 &f{location}
mv-core.anchor.set.failure=&c设置锚点 &f{anchor} 失败。
# /mv check
mv-core.check.description=检查某位玩家是否可以将自己传送至某个地方。
@@ -13,7 +32,7 @@ mv-core.check.location=目标的地址是: &f{location}
# /mv clone
mv-core.clone.description=复制一个世界。
mv-core.clone.world.description=需要复制的原世界。
mv-core.clone.newWorld.description=新世界的名称。
mv-core.clone.newworld.description=新世界的名称。
mv-core.clone.cloning=正在复制 '{world}' 到 '{newworld}'……
mv-core.clone.success=&a已将世界复制到 '{world}'!
@@ -34,7 +53,7 @@ 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.worldscale=&b世界比例: &f{scale}
mv-core.coordinates.info.coordinates=&b坐标: &f{coordinates}
mv-core.coordinates.info.direction=&b方向: &f{direction}
@@ -74,6 +93,15 @@ mv-core.delete.success=&a世界 '{world}' 已经删除!
mv-core.dumps.description=导出版本信息到控制台或者粘贴服务。
mv-core.dumps.url.list={service}:{link}
# /mv entity-spawn-config
mv-core.entityspawnconfig.info.header===== [ 实体生成配置 '{world}' ] ====
mv-core.entityspawnconfig.info.category={category}:\u0020
mv-core.entityspawnconfig.info.spawn=\ \ spawn: {spawn}
mv-core.entityspawnconfig.info.tickrate=\ \ tick-rate: {tickRate}
mv-core.entityspawnconfig.info.exceptions=\ \ 例外: {exceptions}
mv-core.entityspawnconfig.modify.success=成功在 {world} 中将 {category} 的 {property} 设置为 {value}
mv-core.entityspawnconfig.modify.failure=在 {world} 中将 {category} 的 {property} 设置为 {value} 失败: {error}
# /mv gamerule set
mv-core.gamerule.set.description=修改某个世界或某些世界的游戏规则。
mv-core.gamerule.set.gamerule.description=需要设置的游戏规则。
@@ -100,7 +128,12 @@ mv-core.gamerule.list.title= --- {world} 的游戏规则 ---
# /mv generators
mv-core.generators.description=列出 Multiverse 已知的生成器。
mv-core.generators.description.flags=筛选 - 仅显示匹配的条目。页码 - 查看的页面序号。
mv-core.generators.header=&b====[ Multiverse 生成器列表 ]====
mv-core.generators.empty=&c未找到生成器插件。
mv-core.generators.info.plugin=&r生成器插件: {plugin}
mv-core.generators.info.exampleusages=&r示例用法:\u0020
mv-core.generators.info.usages=&r{usages}
mv-core.generators.info.infolink=&r更多信息链接: {link}
# /mv import
mv-core.import.description=导入一个已经存在的世界文件夹。
@@ -139,6 +172,13 @@ mv-core.modify.success=&a成功在世界 &9{world}&a 中 {action} '&9{property}&
mv-core.modify.failure=&c在世界 &9{world}&c 中 {action} '&9{property}&c' 为 '&9{value}&c' 失败。\n&c{error}
mv-core.modify.failure.novalue=&c在世界 &9{world}&c 中 {action} '&9{property}&c' 失败。\n&c{error}
# /mv purge-all-entities
mv-core.purgeallentities.success=成功清除了世界 {world} 中的 {count} 个实体。
mv-core.purgeallentities.success.categories=成功清除了世界 {world} 中生成类别 {categories} 的 {count} 个实体。
# /mv purge-entities
mv-core.purgeentities.success=成功清除了世界 {world} 中的 {count} 个实体。
# /mv regen
mv-core.regen.description=在你的服务器上重新生成一个世界。之前的状态将会“永久”丢失。
mv-core.regen.world.description=你想要重新生成的世界。
@@ -173,6 +213,8 @@ mv-core.setspawn.notmvworld=&c无法为 &c{world} 设置出生点,因为它不
# /mv spawn
mv-core.spawn.description=传送某个特定的玩家到他们所在世界的出生点。
mv-core.spawn.player.description=玩家
mv-core.spawn.notmvworld=&c世界 '{world}' 不是 Multiverse 世界!
mv-core.spawn.nopermission=&c你没有权限在这个世界中使用此命令!
mv-core.spawn.success=已传送 {player} 到 '{world}' 的出生点!
mv-core.spawn.failed=传送 {player} 到 '{world}' 的出生点失败。{reason}
@@ -181,6 +223,9 @@ mv-core.teleport.description=允许你传送到服务器上的某个位置!
mv-core.teleport.player.description=传送的玩家。
mv-core.teleport.destination.description=位置,可以是一个世界的名字。
mv-core.teleport.toomanyplayers=&c你一次不能传送超过 {count} 个玩家。
mv-core.teleport.nopermission.self=&c你没有权限传送自己!
mv-core.teleport.nopermission.other=&c你没有权限传送其他玩家!
mv-core.teleport.nopermission.all=&c你没有权限传送所有这些玩家!
mv-core.teleport.success=已传送 {player} 到 {destination}。
mv-core.teleport.failed=传送 {player} 到 {destination}失败。{reason}
@@ -234,6 +279,21 @@ 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.loadedmultiverseworld.playersonly=该指令仅可由已加载 Multiverse 世界中的玩家使用。
mv-core.commands.error.loadedmultiverseworld.issuer=你不在已加载的 Multiverse 世界中。请指定一个 Multiverse 世界名称,或在已加载的 Multiverse 世界中使用此命令。
mv-core.commands.error.loadedmultiverseworld.inputconsole=世界 '{world}' 不是已加载的 Multiverse 世界。从控制台使用此命令时,请记得指定世界名称。
mv-core.commands.error.loadedmultiverseworld.input=世界 {world} 不是已加载的 Multiverse 世界。
mv-core.commands.error.multiverseworld.playersonly=该指令仅可由 Multiverse 世界中的玩家使用。
mv-core.commands.error.multiverseworld.issuer=你不在 Multiverse 世界中。请指定一个 Multiverse 世界名称,或在 Multiverse 世界中使用此命令。
mv-core.commands.error.multiverseworld.inputconsole=世界 '{world}' 不是 Multiverse 世界。从控制台使用此命令时,请记得指定世界名称。
mv-core.commands.error.multiverseworld.input=世界 {world} 不是 Multiverse 世界。
mv-core.commands.error.player.issuerinput=无效玩家: {player}。请指定在线玩家,或作为玩家使用此命令。
mv-core.commands.error.player.input=找不到玩家 {player}。
mv-core.commands.error.player.selector=没有玩家匹配选择器: {player}。
mv-core.commands.error.players.input=找不到玩家 {player}。
mv-core.commands.error.resolve.player=无法为玩家 '{player}' 解析上下文。
mv-core.commands.error.resolve.input=无法为输入 '{input}' 解析上下文。
mv-core.commands.error.removeplayers.nodefault=未找到默认世界,因此 --remove-players 标志需要一个目标参数。
# entry check
mv-core.entrycheck.blacklisted='{world}' 在黑名单中。
@@ -250,7 +310,10 @@ 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.anchor.display=锚点 '{anchor}'
mv-core.destination.bed.failurereason.playernotfound=&玩家 '&6{player}&c' 不存在或不在线!如果想传送到自己的床,使用 'playerbed'。
mv-core.destination.bed.display.own=你的床/重生点
mv-core.destination.bed.display.other={player} 的床/重生点
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' 不存在或不在线!
@@ -268,6 +331,11 @@ mv-core.teleportfailurereason.teleport.failed=传送被拒绝。请确保其他
mv-core.teleportfailurereason.teleport.failed.exception=在传送过程中出现了一个错误。查看服务器日志获取更多信息。
mv-core.teleportfailurereason.event.cancelled=传送被另一个插件取消了。
# alias name conflict
mv-core.aliasnameconflict.detected=检测到世界 '{world}' 的别名 '{alias}' 存在冲突。这可能会让世界列表和选择变得混乱!
mv-core.aliasnameconflict.duplicatealias= - 与世界 '{world}' 的别名冲突:'{alias}'
mv-core.aliasnameconflict.duplicateworldname= - 与世界名称冲突:'{world}'
# world manager result
mv-core.cloneworld.invalidworldname=世界 '{world}' 包含无效的字符!
mv-core.cloneworld.worldexistfolder=世界 '{world}' 已经存在于服务器的文件中!在复制前你需要先删除它。
@@ -310,6 +378,9 @@ mv-core.worldcreator.invalidchunkgenerator=&c无效的区块生成器 '&6{genera
mv-core.worldcreator.bukkitcreationfailed=&cBukkit 创建世界 '{world}' 失败: {error}\n&c查看控制台获取更多信息。
# queue command result
mv-core.queuecommand.defaultprompt=你尝试运行的指令被认为是危险的。
mv-core.queuecommand.prompt=执行 &a{command} &f以继续。它将在 {timeout} 秒后过期。
mv-core.queuecommand.expired=你列队中的指令已过期。
mv-core.queuecommand.nocommandinqueue=&c你没有任何指令在列队中。
mv-core.queuecommand.invalidotp=&c无效的 OTP 数字 '&6{otp}&c'。请重试……
mv-core.queuecommand.commandexecutionerror=&c在处理列队中命令时发生了错误: {error}\n&f查看控制台获取更多信息。
@@ -318,6 +389,7 @@ mv-core.queuecommand.commandexecutionerror=&c在处理列队中命令时发生
mv-core.contentdisplay.nocontent=&c没有可显示的内容
mv-core.contentdisplay.filter=&7[筛选 '{filter}']
mv-core.contentdisplay.page=&7[第 {current} 页,共 {total} 页]
mv-core.contentdisplay.pagefilter={page} {filter}
mv-core.contentdisplay.invalidpage=&c无效的页码。请输入一个在 &31&c 到 &3{total}&c 之间的页码。
mv-core.contentdisplay.empty=&7&o空
mv-core.contentdisplay.null=&7&o错误
@@ -337,5 +409,8 @@ mv-core.exception.positionparse.invalidnumber=&c无效的数字格式: {number
mv-core.generic.success=成功!
mv-core.generic.failure=失败!
mv-core.generic.error=错误!
mv-core.generic.error.details=错误: {error}
mv-core.generic.null=无效!
mv-core.generic.you=你
mv-core.generic.playercount={count} 个玩家
mv-core.generic.teleportplayers.failed=无法将一个或多个玩家传送出该世界!
@@ -18,6 +18,7 @@ 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 java.util.Properties
import kotlin.test.*
class LocalizationTest : TestWithMockBukkit() {
@@ -31,6 +32,18 @@ class LocalizationTest : TestWithMockBukkit() {
locales = commandManager.locales
}
@Test
fun `Default locale bundle contains every message key`() {
val properties = Properties()
assertNotNull(javaClass.getResourceAsStream("/multiverse-core_en.properties")).use {
properties.load(it)
}
MVCorei18n.entries.forEach { message ->
assertTrue(properties.containsKey(message.messageKey.key), "Missing locale key: ${message.messageKey.key}")
}
}
@Nested
@DisplayName("Given a Message with only a non-localized message")
inner class BasicMessage {
@@ -45,7 +58,7 @@ class LocalizationTest : TestWithMockBukkit() {
@Test
fun `The formatted message should be the same as the original`() {
assertEquals(messageString, message.formatted())
assertEquals(messageString, message.rawFormatted())
}
@Test
@@ -97,7 +110,7 @@ class LocalizationTest : TestWithMockBukkit() {
@Test
fun `The formatted message should be the replaced message string`() {
assertEquals(replacedMessageString, message.formatted())
assertEquals(replacedMessageString, message.rawFormatted())
}
@Test
@@ -156,7 +169,7 @@ class LocalizationTest : TestWithMockBukkit() {
@Test
fun `The formatted message should be the replaced message string`() {
assertEquals(replacedMessageString, message.formatted())
assertEquals(replacedMessageString, message.rawFormatted())
}
@Test
@@ -209,8 +222,13 @@ class LocalizationTest : TestWithMockBukkit() {
}
@Test
fun `The formatted message should be the replaced original string`() {
assertEquals(replacedMessageString, message.formatted())
fun `The raw formatted message should be the replaced original string`() {
assertEquals(replacedMessageString, message.rawFormatted())
}
@Test
fun `The formatted message should be different from the replaced original string`() {
assertNotEquals(replacedMessageString, message.formatted())
}
@Test
@@ -271,7 +289,7 @@ class LocalizationTest : TestWithMockBukkit() {
@Test
fun `The formatted message should be the replaced original string`() {
assertEquals(replacedMessageString, message.formatted())
assertEquals(replacedMessageString, message.rawFormatted())
}
@Test
@@ -49,6 +49,38 @@ class WorldManagerTest : TestWithMockBukkit() {
assertNotNull(world2)
}
@Test
fun `Legacy world name changes update loaded and unloaded lookups`() {
val worldKey = world.key.toString()
assertTrue(world.worldConfig.setLegacyWorldName("renamed_world").isSuccess)
assertFalse(worldManager.getWorld("world").isDefined)
assertSame(world, worldManager.getLoadedWorld("RENAMED_WORLD").get())
assertSame(world, worldManager.getLoadedWorld(worldKey).get())
assertTrue(worldManager.unloadWorld(UnloadWorldOptions.world(world)).isSuccess)
assertFalse(worldManager.getWorld("world").isDefined)
assertFalse(worldManager.getLoadedWorld("renamed_world").isDefined)
assertEquals(world.key, worldManager.getUnloadedWorld("RENAMED_WORLD").get().key)
assertEquals(world.key, worldManager.getUnloadedWorld(worldKey).get().key)
}
@Test
fun `Setting up loaded world config refreshes legacy world name lookups`() {
assertTrue(world.worldConfig.setLegacyWorldName("stale_name").isSuccess)
assertTrue(world.worldConfig.save().isSuccess)
world.setWorldConfig(world.worldConfig)
assertEquals("world", world.name)
assertSame(world, worldManager.getLoadedWorld("world").get())
assertFalse(worldManager.getWorld("stale_name").isDefined)
assertTrue(worldManager.unloadWorld(UnloadWorldOptions.world(world)).isSuccess)
assertTrue(worldManager.getUnloadedWorld("world").isDefined)
assertFalse(worldManager.getWorld("stale_name").isDefined)
}
@Test
fun `Create world with custom options`() {
assertTrue(worldManager.createWorld(
@@ -0,0 +1,50 @@
package org.mvplugins.multiverse.core.world.helper
import org.mvplugins.multiverse.core.TestWithMockBukkit
import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld
import org.mvplugins.multiverse.core.world.WorldManager
import org.mvplugins.multiverse.core.world.helpers.AliasNameConflictChecker
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 AliasNameConflictCheckerTest : TestWithMockBukkit() {
private lateinit var conflictChecker: AliasNameConflictChecker
private lateinit var firstWorld: LoadedMultiverseWorld
private lateinit var secondWorld: LoadedMultiverseWorld
@BeforeTest
fun setUp() {
val worldManager = requireNotNull(serviceLocator.getActiveService(WorldManager::class.java))
conflictChecker = requireNotNull(serviceLocator.getService(AliasNameConflictChecker::class.java))
assertTrue(worldManager.createWorld(CreateWorldOptions.worldName("FirstWorld")).isSuccess)
assertTrue(worldManager.createWorld(CreateWorldOptions.worldName("SecondWorld")).isSuccess)
firstWorld = worldManager.getLoadedWorld("FirstWorld").get()
secondWorld = worldManager.getLoadedWorld("SecondWorld").get()
}
@Test
fun `Aliases differing only by case conflict`() {
assertTrue(firstWorld.setAlias("SharedAlias").isSuccess)
assertTrue(secondWorld.setAlias("sharedalias").isSuccess)
val result = conflictChecker.checkDuplicateFor(secondWorld)
assertEquals(listOf(firstWorld), result.duplicateAliases)
assertTrue(result.duplicateWorldNames.isEmpty())
}
@Test
fun `Alias and world name differing only by case conflict`() {
assertTrue(firstWorld.setAlias("SECONDWORLD").isSuccess)
val result = conflictChecker.checkDuplicateFor(firstWorld)
assertTrue(result.duplicateAliases.isEmpty())
assertEquals(listOf(secondWorld), result.duplicateWorldNames)
}
}
@@ -11,6 +11,7 @@ world:
world-name-format:
nether: '%overworld%_nether'
end: '%overworld%_the_end'
warn-alias-conflicts: true
teleport:
use-finer-teleport-permissions: true