mirror of
https://github.com/Multiverse/Multiverse-Core.git
synced 2026-11-04 16:50:03 -05:00
Compare commits
30
Commits
5.7.3
...
feat/world-ref
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8f19be1671 | ||
|
|
744450cbbb | ||
|
|
ecf107563d | ||
|
|
f7ea31d949 | ||
|
|
1bf4b99ecf | ||
|
|
4de4e5a68e | ||
|
|
f6a8a6b3b4 | ||
|
|
d485238c56 | ||
|
|
5234445746 | ||
|
|
0b626d9eaf | ||
|
|
2ff321e475 | ||
|
|
58dbe55be4 | ||
|
|
a2045b52bd | ||
|
|
396ece4d27 | ||
|
|
e25ac0e531 | ||
|
|
7cf53ee7a8 | ||
|
|
4a46c13754 | ||
|
|
5449ade6f3 | ||
|
|
a9f8deeb01 | ||
|
|
1ce7eb6d3e | ||
|
|
d11d664020 | ||
|
|
abd86f81e5 | ||
|
|
0faec79250 | ||
|
|
9264f5e2ce | ||
|
|
28240c3649 | ||
|
|
c8e4c3239a | ||
|
|
b23735dc72 | ||
|
|
1ccfa2b24b | ||
|
|
68bbb6ab90 | ||
|
|
16738f181a |
No files matched your search
@@ -0,0 +1,84 @@
|
||||
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
|
||||
|
||||
The build and tests passed. This PR build is ready to try.
|
||||
|
||||
**[⬇️ Download test build](${{ steps.artifact.outputs.url }})**
|
||||
Extract the downloaded ZIP to get the plugin JAR.
|
||||
|
||||
| Build details | |
|
||||
| --- | --- |
|
||||
| Commit | `${{ github.event.workflow_run.head_sha }}` |
|
||||
| Workflow | [Run #${{ github.event.workflow_run.run_number }}](${{ github.event.workflow_run.html_url }}) |
|
||||
|
||||
<sub>Sign in to GitHub to download. Artifacts expire after the repository's retention period. This comment updates after successful builds.</sub>
|
||||
@@ -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.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
|
||||
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.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
|
||||
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.2"]}'
|
||||
platform_dependencies: '{"PAPER": ["1.18.2-26.3"]}'
|
||||
plugin_dependencies: ${{ inputs.hangar_plugin_dependencies }}
|
||||
@@ -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 }}
|
||||
@@ -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
|
||||
+5
-5
@@ -37,7 +37,7 @@ 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'
|
||||
@@ -48,7 +48,7 @@ dependencies {
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
@@ -102,11 +102,12 @@ public class MultiverseCore extends MultiverseModule {
|
||||
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();
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()))));
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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()))));
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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}
|
||||
*/
|
||||
|
||||
@@ -200,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("")
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
+11
-6
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -22,11 +22,13 @@ 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;
|
||||
@@ -96,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;
|
||||
}
|
||||
|
||||
@@ -127,10 +126,7 @@ 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;
|
||||
}
|
||||
|
||||
@@ -168,6 +164,7 @@ final class MVEntityListener implements CoreListener {
|
||||
.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();
|
||||
@@ -177,4 +174,11 @@ final class MVEntityListener implements CoreListener {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private boolean isAlwaysAllowedSpawnReason(@Nullable SpawnReason reason) {
|
||||
return reason == SpawnReason.CUSTOM
|
||||
|| reason == SpawnReason.COMMAND
|
||||
|| reason == SpawnReason.BREEDING
|
||||
|| reason == SpawnReason.SPAWNER_EGG;
|
||||
}
|
||||
}
|
||||
@@ -182,7 +182,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,
|
||||
|
||||
@@ -370,6 +370,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,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
|
||||
+40
@@ -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
|
||||
|
||||
@@ -13,7 +13,7 @@ import org.bukkit.WorldType;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import org.mvplugins.multiverse.core.config.CoreConfig;
|
||||
import org.mvplugins.multiverse.core.MultiverseCore;
|
||||
import org.mvplugins.multiverse.core.teleportation.BlockSafety;
|
||||
import org.mvplugins.multiverse.core.teleportation.LocationManipulation;
|
||||
import org.mvplugins.multiverse.core.world.entity.EntityPurger;
|
||||
@@ -22,6 +22,11 @@ import org.mvplugins.multiverse.core.world.location.SpawnLocation;
|
||||
|
||||
/**
|
||||
* Extension of {@link MultiverseWorld} that represents a world that is currently loaded with bukkit world object.
|
||||
* <p>
|
||||
* Do not store instances of this class for future use during runtime. Worlds can be unloaded, removed, reloaded,
|
||||
* or regenerated, making stored instances stale. Instead, store a {@link MultiverseWorldRef} obtained from
|
||||
* {@link #asRef()} and call {@link MultiverseWorldRef#getLoaded()} when a loaded world is needed. The reference
|
||||
* returns an empty Option when the world is unloaded or no longer managed by Multiverse.
|
||||
*/
|
||||
public final class LoadedMultiverseWorld extends MultiverseWorld {
|
||||
|
||||
@@ -34,16 +39,13 @@ public final class LoadedMultiverseWorld extends MultiverseWorld {
|
||||
LoadedMultiverseWorld(
|
||||
@NotNull World world,
|
||||
@NotNull WorldConfig worldConfig,
|
||||
@NotNull CoreConfig config,
|
||||
@NotNull BlockSafety blockSafety,
|
||||
@NotNull LocationManipulation locationManipulation,
|
||||
@NotNull EntityPurger entityPurger
|
||||
) {
|
||||
super(worldConfig, config);
|
||||
@NotNull MultiverseCore multiverseCore
|
||||
) {
|
||||
super(worldConfig, multiverseCore);
|
||||
this.worldUid = world.getUID();
|
||||
this.blockSafety = blockSafety;
|
||||
this.locationManipulation = locationManipulation;
|
||||
this.entityPurger = entityPurger;
|
||||
this.blockSafety = multiverseCore.getServiceLocator().getService(BlockSafety.class);
|
||||
this.locationManipulation = multiverseCore.getServiceLocator().getService(LocationManipulation.class);
|
||||
this.entityPurger = multiverseCore.getServiceLocator().getService(EntityPurger.class);
|
||||
|
||||
setupWorldConfig(world);
|
||||
setupSpawnLocation(world);
|
||||
|
||||
@@ -20,6 +20,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import org.jetbrains.annotations.UnmodifiableView;
|
||||
import org.mvplugins.multiverse.core.MultiverseCore;
|
||||
import org.mvplugins.multiverse.core.config.CoreConfig;
|
||||
import org.mvplugins.multiverse.core.config.handle.StringPropertyHandle;
|
||||
import org.mvplugins.multiverse.core.utils.text.ChatTextFormatter;
|
||||
@@ -29,6 +30,11 @@ import org.mvplugins.multiverse.core.world.entity.EntitySpawnConfig;
|
||||
|
||||
/**
|
||||
* Represents a world handled by Multiverse which has all the custom properties provided by Multiverse.
|
||||
* <p>
|
||||
* Do not store instances of this class for future use during runtime. Worlds can be unloaded, removed, reloaded,
|
||||
* or regenerated, making stored instances stale. Instead, store a {@link MultiverseWorldRef} obtained from
|
||||
* {@link #asRef()} and retrieve the current world instance when needed. The reference returns an empty Option
|
||||
* when the world is no longer available.
|
||||
*/
|
||||
public sealed class MultiverseWorld permits LoadedMultiverseWorld {
|
||||
/**
|
||||
@@ -37,11 +43,13 @@ public sealed class MultiverseWorld permits LoadedMultiverseWorld {
|
||||
WorldConfig worldConfig;
|
||||
|
||||
protected final CoreConfig config;
|
||||
private final WorldStore worldStore;
|
||||
private String colourlessAlias = "";
|
||||
|
||||
MultiverseWorld(WorldConfig worldConfig, CoreConfig config) {
|
||||
MultiverseWorld(@NotNull WorldConfig worldConfig, @NotNull MultiverseCore multiverseCore) {
|
||||
this.worldConfig = worldConfig;
|
||||
this.config = config;
|
||||
this.config = multiverseCore.getServiceLocator().getService(CoreConfig.class);
|
||||
this.worldStore = multiverseCore.getServiceLocator().getService(WorldStore.class);
|
||||
this.worldConfig.setMVWorld(this);
|
||||
updateColourlessAlias();
|
||||
}
|
||||
@@ -125,6 +133,22 @@ public sealed class MultiverseWorld permits LoadedMultiverseWorld {
|
||||
.map(world -> (LoadedMultiverseWorld) world);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a reference to this world's key for storing and retrieving the current world instance at runtime.
|
||||
* <p>
|
||||
* Store this reference when retaining a world across reloads or regeneration, which make stored
|
||||
* {@link MultiverseWorld} and {@link LoadedMultiverseWorld} instances stale. Use {@link MultiverseWorldRef#get()}
|
||||
* or {@link MultiverseWorldRef#getLoaded()} to retrieve the current instance when needed.
|
||||
*
|
||||
* @return A reference that looks up the current world instance using this world's key.
|
||||
*
|
||||
* @since 5.9
|
||||
*/
|
||||
@ApiStatus.AvailableSince("5.9")
|
||||
public MultiverseWorldRef asRef() {
|
||||
return new MultiverseWorldRef(getKey(), worldStore);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the properties handler of this world.
|
||||
*
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
package org.mvplugins.multiverse.core.world;
|
||||
|
||||
import io.vavr.control.Option;
|
||||
import org.bukkit.NamespacedKey;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* A reference to a world by its key that can be stored and used to retrieve the current world instance at runtime.
|
||||
* <p>
|
||||
* Stored {@link MultiverseWorld} and {@link LoadedMultiverseWorld} instances become stale when a world is reloaded
|
||||
* or regenerated. This reference looks up the world on each call to {@link #get()} or {@link #getLoaded()}, allowing
|
||||
* callers to keep the reference across those operations and retrieve the current instance when needed.
|
||||
* Store this reference rather than caching the world instances returned by its methods.
|
||||
* <p>
|
||||
* Obtain a reference using {@link MultiverseWorld#asRef()}. Resolving a reference does not load the world.
|
||||
*
|
||||
* @since 5.9
|
||||
*/
|
||||
@ApiStatus.AvailableSince("5.9")
|
||||
public class MultiverseWorldRef {
|
||||
|
||||
private final NamespacedKey worldKey;
|
||||
private final WorldStore worldStore;
|
||||
|
||||
MultiverseWorldRef(@NotNull NamespacedKey worldKey, @NotNull WorldStore worldStore) {
|
||||
this.worldKey = worldKey;
|
||||
this.worldStore = worldStore;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current world instance for this reference's key, whether loaded or unloaded.
|
||||
* The world is looked up on each call so the returned instance reflects reloads and regeneration.
|
||||
*
|
||||
* @return The current world instance wrapped in an Option, or None if the world is no longer managed by Multiverse.
|
||||
*
|
||||
* @since 5.9
|
||||
*/
|
||||
@ApiStatus.AvailableSince("5.9")
|
||||
public Option<MultiverseWorld> get() {
|
||||
return worldStore.getWorld(worldKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current loaded world instance for this reference's key without loading the world.
|
||||
* The world is looked up on each call so the returned instance reflects reloads and regeneration.
|
||||
*
|
||||
* @return The current loaded world instance wrapped in an Option, or None if the world is unloaded or no longer
|
||||
* managed by Multiverse.
|
||||
*
|
||||
* @since 5.9
|
||||
*/
|
||||
@ApiStatus.AvailableSince("5.9")
|
||||
public Option<LoadedMultiverseWorld> getLoaded() {
|
||||
return worldStore.getLoadedWorld(worldKey);
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,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;
|
||||
@@ -27,6 +28,7 @@ import org.mvplugins.multiverse.core.config.node.NodeGroup;
|
||||
import org.mvplugins.multiverse.core.economy.MVEconomist;
|
||||
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;
|
||||
@@ -41,15 +43,21 @@ final class WorldConfigNodes {
|
||||
|
||||
private final NodeGroup nodes = new NodeGroup();
|
||||
private WorldManager worldManager;
|
||||
private WorldStore worldStore;
|
||||
private EnforcementHandler enforcementHandler;
|
||||
private CoreConfig config;
|
||||
private AliasNameConflictChecker aliasNameConflictChecker;
|
||||
private MVCommandManager commandManager;
|
||||
private WorldKeyOrName keyOrName;
|
||||
private MultiverseWorld world = null;
|
||||
|
||||
WorldConfigNodes(@NotNull MultiverseCore multiverseCore, @NotNull WorldKeyOrName keyOrName) {
|
||||
this.worldManager = multiverseCore.getServiceLocator().getService(WorldManager.class);
|
||||
this.worldStore = multiverseCore.getServiceLocator().getService(WorldStore.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;
|
||||
}
|
||||
|
||||
@@ -93,14 +101,18 @@ final class WorldConfigNodes {
|
||||
|
||||
final ConfigNode<String> alias = node(ConfigNode.builder("alias", String.class)
|
||||
.defaultValue("")
|
||||
.onLoadAndChange((oldValue, newValue) -> {
|
||||
worldManager.getWorldStore().changeAlias(
|
||||
.onLoadAndChange((sender, oldValue, newValue) -> {
|
||||
worldStore.changeAlias(
|
||||
ChatTextFormatter.removeColor(oldValue),
|
||||
ChatTextFormatter.removeColor(newValue),
|
||||
keyOrName.usableKey()
|
||||
);
|
||||
if (world == null) return;
|
||||
world.updateColourlessAlias();
|
||||
if (config.getWarnAliasConflicts()) {
|
||||
aliasNameConflictChecker.checkDuplicateFor(world)
|
||||
.sendConflictMessage(commandManager.getCommandIssuer(sender));
|
||||
}
|
||||
}));
|
||||
|
||||
final ConfigNode<Boolean> allowAdvancementGrant = node(ConfigNode.builder("allow-advancement-grant", Boolean.class)
|
||||
|
||||
@@ -31,6 +31,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.Unmodifiable;
|
||||
import org.jvnet.hk2.annotations.Service;
|
||||
|
||||
import org.mvplugins.multiverse.core.MultiverseCore;
|
||||
import org.mvplugins.multiverse.core.config.CoreConfig;
|
||||
import org.mvplugins.multiverse.core.event.world.MVWorldClonedEvent;
|
||||
import org.mvplugins.multiverse.core.event.world.MVWorldCreatedEvent;
|
||||
@@ -45,8 +46,6 @@ import org.mvplugins.multiverse.core.locale.MVCorei18n;
|
||||
import org.mvplugins.multiverse.core.locale.message.Message;
|
||||
import org.mvplugins.multiverse.core.locale.message.MessageReplacement.Replace;
|
||||
import org.mvplugins.multiverse.core.permissions.CorePermissions;
|
||||
import org.mvplugins.multiverse.core.teleportation.BlockSafety;
|
||||
import org.mvplugins.multiverse.core.teleportation.LocationManipulation;
|
||||
import org.mvplugins.multiverse.core.utils.ServerProperties;
|
||||
import org.mvplugins.multiverse.core.utils.compatibility.BukkitCompatibility;
|
||||
import org.mvplugins.multiverse.core.utils.compatibility.WorldCompatibility;
|
||||
@@ -54,8 +53,8 @@ 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;
|
||||
import org.mvplugins.multiverse.core.world.helpers.DataStore.GameRulesStore;
|
||||
import org.mvplugins.multiverse.core.world.helpers.DataTransfer;
|
||||
@@ -95,6 +94,7 @@ public final class WorldManager {
|
||||
"data/paper/metadata.dat" // New papermc format for 26.1+
|
||||
);
|
||||
|
||||
private final MultiverseCore multiverseCore;
|
||||
private final WorldStore worldStore;
|
||||
private final List<String> unloadTracker;
|
||||
private final List<String> loadTracker;
|
||||
@@ -103,44 +103,37 @@ public final class WorldManager {
|
||||
private final BiomeProviderFactory biomeProviderFactory;
|
||||
private final GeneratorProvider generatorProvider;
|
||||
private final FileUtils fileUtils;
|
||||
private final BlockSafety blockSafety;
|
||||
private final LocationManipulation locationManipulation;
|
||||
private final PluginManager pluginManager;
|
||||
private final CorePermissions corePermissions;
|
||||
private final ServerProperties serverProperties;
|
||||
private final CoreConfig config;
|
||||
private final EntityPurger entityPurger;
|
||||
private final Provider<PotentialWorldFinder> potentialWorldFinder;
|
||||
|
||||
@Inject
|
||||
WorldManager(
|
||||
@NotNull MultiverseCore multiverseCore,
|
||||
@NotNull WorldStore worldStore,
|
||||
@NotNull WorldsConfigManager worldsConfigManager,
|
||||
@NotNull WorldNameChecker worldNameChecker,
|
||||
@NotNull BiomeProviderFactory biomeProviderFactory,
|
||||
@NotNull GeneratorProvider generatorProvider,
|
||||
@NotNull FileUtils fileUtils,
|
||||
@NotNull BlockSafety blockSafety,
|
||||
@NotNull LocationManipulation locationManipulation,
|
||||
@NotNull PluginManager pluginManager,
|
||||
@NotNull CorePermissions corePermissions,
|
||||
@NotNull ServerProperties serverProperties,
|
||||
@NotNull CoreConfig config,
|
||||
@NotNull EntityPurger entityPurger,
|
||||
@NotNull Provider<PotentialWorldFinder> potentialWorldFinder) {
|
||||
this.multiverseCore = multiverseCore;
|
||||
this.worldStore = worldStore;
|
||||
this.worldsConfigManager = worldsConfigManager;
|
||||
this.worldNameChecker = worldNameChecker;
|
||||
this.biomeProviderFactory = biomeProviderFactory;
|
||||
this.generatorProvider = generatorProvider;
|
||||
this.fileUtils = fileUtils;
|
||||
this.blockSafety = blockSafety;
|
||||
this.locationManipulation = locationManipulation;
|
||||
this.pluginManager = pluginManager;
|
||||
this.corePermissions = corePermissions;
|
||||
this.serverProperties = serverProperties;
|
||||
this.config = config;
|
||||
this.entityPurger = entityPurger;
|
||||
this.potentialWorldFinder = potentialWorldFinder;
|
||||
|
||||
this.unloadTracker = new ArrayList<>();
|
||||
@@ -154,10 +147,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();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -240,7 +237,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 +278,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 +364,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)
|
||||
@@ -396,7 +409,7 @@ public final class WorldManager {
|
||||
}
|
||||
|
||||
private MultiverseWorld newMultiverseWorld(WorldConfig worldConfig) {
|
||||
MultiverseWorld mvWorld = new MultiverseWorld(worldConfig, config);
|
||||
MultiverseWorld mvWorld = new MultiverseWorld(worldConfig, multiverseCore);
|
||||
worldStore.putUnloadedWorld(mvWorld);
|
||||
corePermissions.addWorldPermissions(mvWorld);
|
||||
return mvWorld;
|
||||
@@ -435,10 +448,7 @@ public final class WorldManager {
|
||||
LoadedMultiverseWorld loadedWorld = new LoadedMultiverseWorld(
|
||||
world,
|
||||
worldConfig,
|
||||
config,
|
||||
blockSafety,
|
||||
locationManipulation,
|
||||
entityPurger
|
||||
multiverseCore
|
||||
);
|
||||
worldStore.putLoadedWorld(loadedWorld);
|
||||
saveWorldsConfig();
|
||||
@@ -524,10 +534,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)
|
||||
@@ -567,10 +581,7 @@ public final class WorldManager {
|
||||
LoadedMultiverseWorld loadedWorld = new LoadedMultiverseWorld(
|
||||
bukkitWorld,
|
||||
worldConfig,
|
||||
config,
|
||||
blockSafety,
|
||||
locationManipulation,
|
||||
entityPurger
|
||||
multiverseCore
|
||||
);
|
||||
worldStore.putLoadedWorld(loadedWorld);
|
||||
saveWorldsConfig();
|
||||
@@ -1253,10 +1264,6 @@ public final class WorldManager {
|
||||
});
|
||||
}
|
||||
|
||||
WorldStore getWorldStore() {
|
||||
return worldStore;
|
||||
}
|
||||
|
||||
/**
|
||||
* A simple pair wrapper for convenience to pass both the world key or name and the options together between methods
|
||||
* when parsing world options.
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -73,7 +73,7 @@ public final class SpawnCategoryConfig {
|
||||
}
|
||||
|
||||
private void applyTickPerSpawns(@NotNull World bukkitWorld) {
|
||||
if (!config.getApplyEntitySpawnLimit()) {
|
||||
if (!config.getApplyEntitySpawnRate()) {
|
||||
Logging.finer("World %s %s skipping setTicksPerSpawns due to core config", world.getName(), spawnCategory);
|
||||
return;
|
||||
}
|
||||
|
||||
+157
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -32,7 +32,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}'!
|
||||
|
||||
@@ -53,7 +53,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}
|
||||
|
||||
@@ -356,6 +356,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.
|
||||
@@ -379,14 +384,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!
|
||||
|
||||
|
||||
@@ -32,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}'!
|
||||
|
||||
@@ -53,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}
|
||||
|
||||
@@ -330,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.
|
||||
|
||||
@@ -32,7 +32,7 @@ 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.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'!
|
||||
|
||||
@@ -56,7 +56,7 @@ 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.worldscale=&fSkala świata: &a{scale}
|
||||
mv-core.coordinates.info.coordinates=&fKoordynaty: &a{coordinates}
|
||||
mv-core.coordinates.info.direction=&fKierunek: &a{direction}
|
||||
|
||||
@@ -365,6 +365,11 @@ mv-core.teleportfailurereason.teleport.failed=&c&l✘ &cTeleportacja została od
|
||||
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.
|
||||
@@ -388,14 +393,14 @@ mv-core.importworld.worldexistunloaded=&e&l! &eŚwiat '&a{world}&e' już istniej
|
||||
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.bukkitnamespacemismatch=&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.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.bukkitnamespacemismatch=&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.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'!
|
||||
|
||||
|
||||
@@ -32,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}'!
|
||||
|
||||
@@ -53,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}
|
||||
|
||||
@@ -325,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}' существует в папках сервера! Вам нужно удалить его перед клонированием.
|
||||
|
||||
@@ -32,7 +32,7 @@ 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.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ı!
|
||||
|
||||
@@ -53,7 +53,7 @@ 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.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}
|
||||
|
||||
@@ -356,6 +356,11 @@ mv-core.teleportfailurereason.teleport.failed=Işınlama reddedildi. Başka bir
|
||||
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.
|
||||
@@ -379,14 +384,14 @@ mv-core.importworld.worldexistunloaded='{world}' dünyası zaten mevcut, ancak y
|
||||
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.bukkitnamespacemismatch=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.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.bukkitnamespacemismatch=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.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ı!
|
||||
|
||||
@@ -441,4 +446,4 @@ 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ı!
|
||||
mv-core.generic.teleportplayers.failed=Bir veya daha fazla oyuncu dünyadan ışınlanamadı!
|
||||
@@ -32,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}'!
|
||||
|
||||
@@ -53,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}
|
||||
|
||||
@@ -331,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}' 已经存在于服务器的文件中!在复制前你需要先删除它。
|
||||
|
||||
@@ -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,48 @@ class WorldManagerTest : TestWithMockBukkit() {
|
||||
assertNotNull(world2)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `World reference follows unload and reload`() {
|
||||
val ref = world2.asRef()
|
||||
assertSame(world2, ref.get().get())
|
||||
assertSame(world2, ref.getLoaded().get())
|
||||
|
||||
assertTrue(worldManager.unloadWorld(UnloadWorldOptions.world(world2)).isSuccess)
|
||||
assertTrue(ref.getLoaded().isEmpty)
|
||||
val unloaded = ref.get().get()
|
||||
assertFalse(unloaded.isLoaded)
|
||||
assertSame(worldManager.getWorld(world2.name).get(), unloaded)
|
||||
|
||||
val unloadedRef = unloaded.asRef()
|
||||
assertTrue(worldManager.loadWorld(LoadWorldOptions.world(unloaded)).isSuccess)
|
||||
val reloaded = worldManager.getLoadedWorld(world2.name).get()
|
||||
assertNotSame(world2, reloaded)
|
||||
assertSame(reloaded, ref.get().get())
|
||||
assertSame(reloaded, ref.getLoaded().get())
|
||||
assertSame(reloaded, unloadedRef.getLoaded().get())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `World reference follows regeneration`() {
|
||||
val ref = world2.asRef()
|
||||
assertTrue(worldManager.regenWorld(RegenWorldOptions.world(world2).seed(4321L)).isSuccess)
|
||||
|
||||
val regenerated = ref.getLoaded().get()
|
||||
assertNotSame(world2, regenerated)
|
||||
assertSame(worldManager.getLoadedWorld(world2.name).get(), regenerated)
|
||||
assertSame(regenerated, ref.get().get())
|
||||
assertEquals(4321L, regenerated.seed)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `World reference is empty after removal`() {
|
||||
val ref = world2.asRef()
|
||||
assertTrue(worldManager.removeWorld(RemoveWorldOptions.world(world2)).isSuccess)
|
||||
|
||||
assertTrue(ref.get().isEmpty)
|
||||
assertTrue(ref.getLoaded().isEmpty)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Create world with custom options`() {
|
||||
assertTrue(worldManager.createWorld(
|
||||
|
||||
+50
@@ -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
|
||||
|
||||
Reference in new issue
Block a user