diff --git a/src/main/java/org/mvplugins/multiverse/core/commands/CreateCommand.java b/src/main/java/org/mvplugins/multiverse/core/commands/CreateCommand.java index acc880566..7be73b0a6 100644 --- a/src/main/java/org/mvplugins/multiverse/core/commands/CreateCommand.java +++ b/src/main/java/org/mvplugins/multiverse/core/commands/CreateCommand.java @@ -19,7 +19,6 @@ import org.mvplugins.multiverse.core.command.LegacyAliasCommand; import org.mvplugins.multiverse.core.command.MVCommandIssuer; -import org.mvplugins.multiverse.core.command.MVCommandManager; import org.mvplugins.multiverse.core.command.flag.CommandFlag; import org.mvplugins.multiverse.core.command.flag.CommandFlagsManager; import org.mvplugins.multiverse.core.command.flag.CommandValueFlag; @@ -27,6 +26,7 @@ import org.mvplugins.multiverse.core.command.flag.ParsedCommandFlags; import org.mvplugins.multiverse.core.locale.MVCorei18n; import org.mvplugins.multiverse.core.locale.message.MessageReplacement.Replace; +import org.mvplugins.multiverse.core.utils.StringFormatter; import org.mvplugins.multiverse.core.utils.result.Attempt.Failure; import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld; import org.mvplugins.multiverse.core.world.WorldManager; @@ -53,7 +53,7 @@ class CreateCommand extends CoreCommand { @CommandPermission("multiverse.core.create") @CommandCompletion("@empty @environments @flags:groupName=" + Flags.NAME) @Syntax(" [--seed --generator --world-type --adjust-spawn " - + "--no-structures --biome ]") + + "--no-structures --biome --properties ]") @Description("{@@mv-core.create.description}") void onCreateCommand( MVCommandIssuer issuer, @@ -68,7 +68,7 @@ void onCreateCommand( @Optional @Syntax("[--seed --generator --world-type --adjust-spawn " - + "--no-structures --biome ]") + + "--no-structures --biome --properties ]") @Description("{@@mv-core.create.flags.description}") String[] flagArray) { ParsedCommandFlags parsedFlags = flags.parse(flagArray); @@ -78,14 +78,15 @@ void onCreateCommand( issuer.sendInfo(MVCorei18n.CREATE_LOADING); worldManager.createWorld(CreateWorldOptions.worldName(worldName) - .biome(parsedFlags.flagValue(flags.biome, "")) - .environment(environment) - .seed(parsedFlags.flagValue(flags.seed)) - .worldType(parsedFlags.flagValue(flags.worldType, WorldType.NORMAL)) - .useSpawnAdjust(!parsedFlags.hasFlag(flags.noAdjustSpawn)) - .generator(parsedFlags.flagValue(flags.generator, "")) - .generatorSettings(parsedFlags.flagValue(flags.generatorSettings, "")) - .generateStructures(!parsedFlags.hasFlag(flags.noStructures))) + .biome(parsedFlags.flagValue(flags.biome, "")) + .environment(environment) + .seed(parsedFlags.flagValue(flags.seed)) + .worldType(parsedFlags.flagValue(flags.worldType, WorldType.NORMAL)) + .useSpawnAdjust(!parsedFlags.hasFlag(flags.noAdjustSpawn)) + .generator(parsedFlags.flagValue(flags.generator, "")) + .generatorSettings(parsedFlags.flagValue(flags.generatorSettings, "")) + .generateStructures(!parsedFlags.hasFlag(flags.noStructures)) + .worldPropertyStrings(StringFormatter.parseCSVMap(parsedFlags.flagValue(flags.properties)))) .onSuccess(newWorld -> messageSuccess(issuer, newWorld)) .onFailure(failure -> messageFailure(issuer, failure)); } @@ -180,6 +181,10 @@ private Flags( .addAlias("-b") .completion(input -> biomeProviderFactory.suggestBiomeString(input)) .build()); + + private final CommandValueFlag properties = flag(CommandValueFlag.builder("--properties", String.class) + .addAlias("-p") + .build()); } @Service diff --git a/src/main/java/org/mvplugins/multiverse/core/utils/StringFormatter.java b/src/main/java/org/mvplugins/multiverse/core/utils/StringFormatter.java index b7f0c2291..6f93a88f9 100644 --- a/src/main/java/org/mvplugins/multiverse/core/utils/StringFormatter.java +++ b/src/main/java/org/mvplugins/multiverse/core/utils/StringFormatter.java @@ -5,11 +5,13 @@ import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.Unmodifiable; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.stream.Collectors; @@ -162,4 +164,24 @@ public static Collection parseQuotesInArgs(String[] args) { public static String quoteMultiWordString(String input) { return input.contains(" ") ? "\"" + input + "\"" : input; } + + /** + * Parses a CSV string of key=value pairs into a map. + * E.g. "key1=value1,key2=value2" -> {key1=value1, key2=value2} + * + * @param input The CSV string to parse + * @return The parsed map + * + * @since 5.5 + */ + @ApiStatus.AvailableSince("5.5") + public static @Unmodifiable Map parseCSVMap(@Nullable String input) { + if (Strings.isNullOrEmpty(input)) { + return Map.of(); + } + return REPatterns.COMMA.splitAsStream(input) + .map(s -> REPatterns.EQUALS.split(s, 2)) + .filter(parts -> parts.length == 2) + .collect(Collectors.toUnmodifiableMap(parts -> parts[0], parts -> parts[1])); + } } diff --git a/src/main/java/org/mvplugins/multiverse/core/world/WorldManager.java b/src/main/java/org/mvplugins/multiverse/core/world/WorldManager.java index 08e259661..64a20179f 100644 --- a/src/main/java/org/mvplugins/multiverse/core/world/WorldManager.java +++ b/src/main/java/org/mvplugins/multiverse/core/world/WorldManager.java @@ -6,9 +6,7 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; -import java.util.HashMap; import java.util.List; -import java.util.Locale; import java.util.Map; import java.util.Objects; import java.util.concurrent.atomic.AtomicReference; @@ -278,7 +276,16 @@ private Attempt doCreateWorld( generatorString, options.biome(), options.useSpawnAdjust())) - .peek(loadedWorld -> pluginManager.callEvent(new MVWorldCreatedEvent(loadedWorld))); + .peek(loadedWorld -> postCreateWorld(loadedWorld, options)); + } + + private void postCreateWorld(LoadedMultiverseWorld loadedWorld, CreateWorldOptions options) { + options.worldPropertyStrings().forEach((key, value) -> loadedWorld.getStringPropertyHandle() + .setPropertyString(key, value) + .onFailure(failure -> Logging.warning("Failed to set property '%s' to '%s' for world %s: %s", + key, value, loadedWorld.getName(), failure.getMessage()))); + pluginManager.callEvent(new MVWorldCreatedEvent(loadedWorld)); + saveWorldsConfig(); } /** diff --git a/src/main/java/org/mvplugins/multiverse/core/world/options/CreateWorldOptions.java b/src/main/java/org/mvplugins/multiverse/core/world/options/CreateWorldOptions.java index 9ea1d4ebf..68db0b636 100644 --- a/src/main/java/org/mvplugins/multiverse/core/world/options/CreateWorldOptions.java +++ b/src/main/java/org/mvplugins/multiverse/core/world/options/CreateWorldOptions.java @@ -3,8 +3,14 @@ import co.aikar.commands.ACFUtil; import org.bukkit.World; import org.bukkit.WorldType; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.UnmodifiableView; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; /** * Options for customizing the creation of a new world. @@ -31,6 +37,7 @@ public final class CreateWorldOptions { private boolean useSpawnAdjust = true; private WorldType worldType = WorldType.NORMAL; private boolean doFolderCheck = true; + private final Map worldPropertyStrings = new HashMap<>(); /** * Creates a new {@link CreateWorldOptions} instance with the given world name. @@ -260,4 +267,44 @@ public boolean useSpawnAdjust() { public boolean doFolderCheck() { return doFolderCheck; } + + /** + * Sets a world property string key-value pair. Overwrites any existing key. + * + * @param key The key of the world property string. + * @param value The value of the world property string. + * @return This {@link CreateWorldOptions} instance. + * + * @since 5.5 + */ + @ApiStatus.AvailableSince("5.5") + public @NotNull CreateWorldOptions worldPropertyString(@NotNull String key, @Nullable String value) { + this.worldPropertyStrings.put(key, value); + return this; + } + + /** + * Sets multiple world property string key-value pairs. Overwrites any existing keys. + * + * @param worldProperties A map of world property string key-value pairs. + * @return This {@link CreateWorldOptions} instance. + */ + @ApiStatus.AvailableSince("5.5") + public @NotNull CreateWorldOptions worldPropertyStrings(@NotNull Map<@NotNull String, @Nullable String> worldProperties) { + this.worldPropertyStrings.putAll(worldProperties); + return this; + } + + /** + * Gets an unmodifiable view of the world property strings. Use {@link #worldPropertyString(String, String)} and + * {@link #worldPropertyStrings(Map)} to modify the world property strings. + * + * @return An unmodifiable view of the world property strings. + * + * @since 5.5 + */ + @ApiStatus.AvailableSince("5.5") + public @UnmodifiableView @NotNull Map worldPropertyStrings() { + return Collections.unmodifiableMap(worldPropertyStrings); + } } diff --git a/src/test/java/org/mvplugins/multiverse/core/utils/StringFormatterTest.kt b/src/test/java/org/mvplugins/multiverse/core/utils/StringFormatterTest.kt index b59ac1808..ff1de1f5f 100644 --- a/src/test/java/org/mvplugins/multiverse/core/utils/StringFormatterTest.kt +++ b/src/test/java/org/mvplugins/multiverse/core/utils/StringFormatterTest.kt @@ -43,4 +43,20 @@ class StringFormatterTest { StringFormatter.quoteMultiWordString("test") ) } + + @Test + fun `StringFormatter parseCSVMap`() { + assertEquals( + emptyMap(), + StringFormatter.parseCSVMap("") + ) + assertEquals( + mapOf("key" to "value"), + StringFormatter.parseCSVMap("key=value") + ) + assertEquals( + mapOf("key1" to "value1", "key2" to "value2", "key3" to "value3"), + StringFormatter.parseCSVMap("key1=value1,key2=value2,key3=value3") + ) + } }