Skip to content
Open

V5.5-2 #3410

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
5270857
Add world border check for spawn safety
benwoo1110 Jan 1, 2026
cf70b3b
Add configurable delay for gamemode and flight enforcement on world c…
benwoo1110 Jan 1, 2026
6152620
Merge pull request #3409 from Multiverse/fix/worldborder-safety
benwoo1110 Jan 1, 2026
c505da7
Fix config test
benwoo1110 Jan 1, 2026
2db55fa
Add event priority configuration for PlayerChangedWorldEvent
benwoo1110 Jan 1, 2026
7819438
Merge pull request #3411 from Multiverse/feat/enforce-delay
benwoo1110 Jan 1, 2026
815637e
Merge pull request #3412 from Multiverse/feat/world-change-priority
benwoo1110 Jan 1, 2026
5bd37d1
Fix spelling error for addOnToCommaSeparated method name
benwoo1110 Jan 1, 2026
588a8e1
Merge pull request #3413 from Multiverse/fix/seperated-typo
benwoo1110 Jan 2, 2026
77382da
Add support for customising world properties during world creation
benwoo1110 Jan 2, 2026
8a77dc8
Add `--properties` syntax to create command
benwoo1110 Jan 2, 2026
86e2112
Merge pull request #3414 from Multiverse/feat/properties-flag
benwoo1110 Jan 2, 2026
c1ef8f6
Enhance StringFormatter methods to handle null inputs and improve typ…
benwoo1110 Jan 2, 2026
e078c5e
Add a logging message when plugin shutdown
benwoo1110 Jan 2, 2026
1220998
Merge pull request #3416 from Multiverse/chore/logging
benwoo1110 Jan 2, 2026
0822b14
Merge pull request #3415 from Multiverse/refactor/string-formatter
benwoo1110 Jan 3, 2026
87571e7
Add @since annotation for worldPropertyStrings method in CreateWorldO…
benwoo1110 Jan 3, 2026
b6c309a
Merge pull request #3417 from Multiverse/fix/since-annotation
benwoo1110 Jan 3, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ public void onDisable() {
MultiverseCoreApi.shutdown();
shutdownDependencyInjection();
PluginServiceLocatorFactory.get().shutdown();
Logging.info("- Disabled");
Logging.shutdown();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@
import org.mvplugins.multiverse.core.world.generators.GeneratorPlugin;
import org.mvplugins.multiverse.core.world.generators.GeneratorProvider;

import static org.mvplugins.multiverse.core.utils.StringFormatter.addonToCommaSeperated;
import static org.mvplugins.multiverse.core.utils.StringFormatter.addOnToCommaSeparated;

@Service
public class MVCommandCompletions extends PaperCommandCompletions {
Expand Down Expand Up @@ -163,7 +163,7 @@ private Collection<String> completeWithPreconditions(
}
}
if (context.hasConfig("multiple")) {
return addonToCommaSeperated(context.getInput(), handler.getCompletions(context));
return addOnToCommaSeparated(context.getInput(), handler.getCompletions(context));
}
return handler.getCompletions(context);
}
Expand Down Expand Up @@ -334,7 +334,7 @@ private Collection<String> suggestPlayersArray(BukkitCommandCompletionContext co
matchedPlayers.add(name);
}
}
return addonToCommaSeperated(context.getInput(), matchedPlayers);
return addOnToCommaSeparated(context.getInput(), matchedPlayers);
}

private Collection<String> suggestSpawnCategoryPropsName(BukkitCommandCompletionContext context) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,14 @@

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;
import org.mvplugins.multiverse.core.command.flag.FlagBuilder;
import org.mvplugins.multiverse.core.command.flag.ParsedCommandFlags;
import org.mvplugins.multiverse.core.locale.MVCorei18n;
import org.mvplugins.multiverse.core.locale.message.MessageReplacement.Replace;
import org.mvplugins.multiverse.core.utils.StringFormatter;
import org.mvplugins.multiverse.core.utils.result.Attempt.Failure;
import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld;
import org.mvplugins.multiverse.core.world.WorldManager;
Expand All @@ -53,7 +53,7 @@ class CreateCommand extends CoreCommand {
@CommandPermission("multiverse.core.create")
@CommandCompletion("@empty @environments @flags:groupName=" + Flags.NAME)
@Syntax("<name> <environment> [--seed <seed> --generator <generator[:id]> --world-type <worldtype> --adjust-spawn "
+ "--no-structures --biome <biome>]")
+ "--no-structures --biome <biome> --properties <prop1=value1,prop2=value2,...>]")
@Description("{@@mv-core.create.description}")
void onCreateCommand(
MVCommandIssuer issuer,
Expand All @@ -68,7 +68,7 @@ void onCreateCommand(

@Optional
@Syntax("[--seed <seed> --generator <generator[:id]> --world-type <worldtype> --adjust-spawn "
+ "--no-structures --biome <biome>]")
+ "--no-structures --biome <biome> --properties <prop1=value1,prop2=value2,...>]")
@Description("{@@mv-core.create.flags.description}")
String[] flagArray) {
ParsedCommandFlags parsedFlags = flags.parse(flagArray);
Expand All @@ -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));
}
Expand Down Expand Up @@ -180,6 +181,10 @@ private Flags(
.addAlias("-b")
.completion(input -> biomeProviderFactory.suggestBiomeString(input))
.build());

private final CommandValueFlag<String> properties = flag(CommandValueFlag.builder("--properties", String.class)
.addAlias("-p")
.build());
}

@Service
Expand Down
20 changes: 20 additions & 0 deletions src/main/java/org/mvplugins/multiverse/core/config/CoreConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,16 @@ public boolean getEnforceFlight() {
return configHandle.get(configNodes.enforceFlight);
}

@ApiStatus.AvailableSince("5.5")
public Try<Void> setGamemodeAndFlightEnforceDelay(int delayTicks) {
return configHandle.set(configNodes.gamemodeAndFlightEnforceDelay, delayTicks);
}

@ApiStatus.AvailableSince("5.5")
public int getGamemodeAndFlightEnforceDelay() {
return configHandle.get(configNodes.gamemodeAndFlightEnforceDelay);
}

@ApiStatus.AvailableSince("5.3")
public Try<Void> setApplyEntitySpawnRate(boolean applyEntitySpawnRate) {
return configHandle.set(configNodes.applyEntitySpawnRate, applyEntitySpawnRate);
Expand Down Expand Up @@ -641,6 +651,16 @@ public EventPriority getEventPriorityPlayerTeleport() {
return configHandle.get(configNodes.eventPriorityPlayerTeleport);
}

@ApiStatus.AvailableSince("5.5")
public Try<Void> setEventPriorityPlayerWorldChange(EventPriority eventPriorityPlayerWorldChange) {
return configHandle.set(configNodes.eventPriorityPlayerWorldChange, eventPriorityPlayerWorldChange);
}

@ApiStatus.AvailableSince("5.5")
public EventPriority getEventPriorityPlayerWorldChange() {
return configHandle.get(configNodes.eventPriorityPlayerWorldChange);
}

public Try<Void> setBukkitYmlPath(String bukkitYmlPath) {
return configHandle.set(configNodes.bukkitYmlPath, bukkitYmlPath);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,16 @@
.name("enforce-flight")
.build());

final ConfigNode<Integer> gamemodeAndFlightEnforceDelay = node(ConfigNode.builder("world.gamemode-and-flight-enforce-delay", Integer.class)

Check warning on line 138 in src/main/java/org/mvplugins/multiverse/core/config/CoreConfigNodes.java

View workflow job for this annotation

GitHub Actions / checkstyle / checkstyle

[checkstyle] reported by reviewdog 🐶 Line is longer than 120 characters (found 143). Raw Output: /github/workspace/./src/main/java/org/mvplugins/multiverse/core/config/CoreConfigNodes.java:138:0: warning: Line is longer than 120 characters (found 143). (com.puppycrawl.tools.checkstyle.checks.sizes.LineLengthCheck)
.comment("")
.comment("Sets the delay in ticks before Multiverse enforces gamemode and flight ability on world change.")
.comment("Increase this value if you are experiencing issues with other plugins overriding gamemode or flight ability.")

Check warning on line 141 in src/main/java/org/mvplugins/multiverse/core/config/CoreConfigNodes.java

View workflow job for this annotation

GitHub Actions / checkstyle / checkstyle

[checkstyle] reported by reviewdog 🐶 Line is longer than 120 characters (found 132). Raw Output: /github/workspace/./src/main/java/org/mvplugins/multiverse/core/config/CoreConfigNodes.java:141:0: warning: Line is longer than 120 characters (found 132). (com.puppycrawl.tools.checkstyle.checks.sizes.LineLengthCheck)
.comment("Or set to 0 to enforce immediately during world change event.")
.defaultValue(1)
.name("gamemode-and-flight-enforce-delay")
.suggester((sender -> List.of("0", "1", "2", "5", "10")))

Check warning on line 145 in src/main/java/org/mvplugins/multiverse/core/config/CoreConfigNodes.java

View workflow job for this annotation

GitHub Actions / checkstyle / checkstyle

[checkstyle] reported by reviewdog 🐶 Unnecessary parentheses around assignment right-hand side. Raw Output: /github/workspace/./src/main/java/org/mvplugins/multiverse/core/config/CoreConfigNodes.java:145:24: warning: Unnecessary parentheses around assignment right-hand side. (com.puppycrawl.tools.checkstyle.checks.coding.UnnecessaryParenthesesCheck)
.build());

final ConfigNode<Boolean> applyEntitySpawnRate = node(ConfigNode.builder("world.apply-entity-spawn-rate", Boolean.class)
.comment("")
.comment("Sets whether Multiverse will apply the world's entity `tick-rate` config in worlds.yml.")
Expand Down Expand Up @@ -545,6 +555,18 @@
"full effect after a server restart."))
.build());

final ConfigNode<EventPriority> eventPriorityPlayerWorldChange = node(ConfigNode.builder("event-priority.player-changed-world", EventPriority.class)

Check warning on line 558 in src/main/java/org/mvplugins/multiverse/core/config/CoreConfigNodes.java

View workflow job for this annotation

GitHub Actions / checkstyle / checkstyle

[checkstyle] reported by reviewdog 🐶 Line is longer than 120 characters (found 152). Raw Output: /github/workspace/./src/main/java/org/mvplugins/multiverse/core/config/CoreConfigNodes.java:558:0: warning: Line is longer than 120 characters (found 152). (com.puppycrawl.tools.checkstyle.checks.sizes.LineLengthCheck)
.defaultValue(EventPriority.NORMAL)
.name("event-priority-player-changed-world")
.comment("")
.comment("This config option defines the priority for the PlayerChangedWorldEvent.")
.onLoadAndChange((oldValue, newValue) ->
eventPriorityMapper.get().setPriority("mvcore-player-changed-world", newValue))
.onChange((sender, oldValue, newValue) ->
sender.sendMessage(ChatColor.YELLOW + "'event-priority.player-changed-world' config option will only take " +
"full effect after a server restart."))
.build());

private final ConfigHeaderNode miscHeader = node(ConfigHeaderNode.builder("misc")
.comment("")
.comment("")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,9 +120,9 @@ private void setDefaults() {
private void setDefaultSuggester() {
if (itemSuggester instanceof SenderNodeSuggester senderItemSuggester) {
this.suggester = (SenderNodeSuggester)(sender, input) ->
StringFormatter.addonToCommaSeperated(input, senderItemSuggester.suggest(sender, input));
StringFormatter.addOnToCommaSeparated(input, senderItemSuggester.suggest(sender, input));
} else {
this.suggester = input -> StringFormatter.addonToCommaSeperated(input, itemSuggester.suggest(input));
this.suggester = input -> StringFormatter.addOnToCommaSeparated(input, itemSuggester.suggest(input));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,7 @@

package org.mvplugins.multiverse.core.listeners;

import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

import com.dumptruckman.minecraft.util.Logging;
import io.vavr.control.Option;
Expand Down Expand Up @@ -257,9 +255,9 @@
* @param event The Event that was fired.
*/
@EventMethod
@DefaultEventPriority(EventPriority.MONITOR)
@EventPriorityKey("mvcore-player-changed-world")
@DefaultEventPriority(EventPriority.NORMAL)
void playerChangedWorld(PlayerChangedWorldEvent event) {
// Permissions now determine whether or not to handle a gamemode.
this.handleGameModeAndFlight(event.getPlayer(), event.getPlayer().getWorld());
}

Expand Down Expand Up @@ -402,21 +400,31 @@
}

/**
* Handles the gamemode for the specified {@link Player}.
* Handles the gamemode for the specified {@link Player}. Delays the enforcement if configured to do so
* to ensure multiverse has final say over other plugins changing gamemodes.
*
* @param player The {@link Player}.
* @param world The {@link World} the player is supposed to be in.
*/
private void handleGameModeAndFlight(final Player player, World world) {
// We perform this task one tick later to MAKE SURE that the player actually reaches the
// destination world, otherwise we'd be changing the player mode if they havent moved anywhere.
this.server.getScheduler().runTaskLater(this.plugin, () -> {
if (!player.isOnline() || !player.getWorld().equals(world)) {
return;
}
Logging.finer("Handling gamemode and flight for player %s in world '%s'", player.getName(), world.getName());
enforcementHandler.handleFlightEnforcement(player);
enforcementHandler.handleGameModeEnforcement(player);
}, 1L);
if (config.getGamemodeAndFlightEnforceDelay() <= 0) {
doGameModeAndFlightEnforcement(player, world);
return;
}
server.getScheduler().runTaskLater(
this.plugin,
() -> doGameModeAndFlightEnforcement(player, world),
config.getGamemodeAndFlightEnforceDelay()
);

Check warning on line 418 in src/main/java/org/mvplugins/multiverse/core/listeners/MVPlayerListener.java

View workflow job for this annotation

GitHub Actions / checkstyle / checkstyle

[checkstyle] reported by reviewdog 🐶 ')' should be on the previous line. Raw Output: /github/workspace/./src/main/java/org/mvplugins/multiverse/core/listeners/MVPlayerListener.java:418:9: warning: ')' should be on the previous line. (SeparatorWrapEol)
}

private void doGameModeAndFlightEnforcement(Player player, World world) {
if (!player.isOnline() || !player.getWorld().equals(world)) {
Logging.finer("Player %s is no longer online or not in the expected world '%s'", player.getName(), world.getName());

Check warning on line 423 in src/main/java/org/mvplugins/multiverse/core/listeners/MVPlayerListener.java

View workflow job for this annotation

GitHub Actions / checkstyle / checkstyle

[checkstyle] reported by reviewdog 🐶 Line is longer than 120 characters (found 128). Raw Output: /github/workspace/./src/main/java/org/mvplugins/multiverse/core/listeners/MVPlayerListener.java:423:0: warning: Line is longer than 120 characters (found 128). (com.puppycrawl.tools.checkstyle.checks.sizes.LineLengthCheck)
return;
}
Logging.finer("Handling gamemode and flight for player %s in world '%s'", player.getName(), world.getName());
enforcementHandler.handleFlightEnforcement(player);
enforcementHandler.handleGameModeEnforcement(player);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,10 @@ public boolean canSpawnAtLocationSafely(@NotNull Location location) {
*/
public boolean canSpawnAtBlockSafely(@NotNull Block block) {
Logging.finest("Checking spawn safety for location: %s, %s, %s", block.getX(), block.getY(), block.getZ());
if (!block.getWorld().getWorldBorder().isInside(block.getLocation())) {
Logging.finest("Location is outside world border.");
return false;
}
if (isUnsafeSpawnBody(block)) {
// Player body will be stuck in solid
Logging.finest("Unsafe location for player's body: " + block);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,17 @@

import com.google.common.base.Strings;
import com.google.common.collect.Sets;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.Unmodifiable;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

Expand All @@ -30,15 +34,15 @@
* @param list the list of strings to join. If the list is empty, an empty string is returned.
* @return the concatenated string
*/
public static @NotNull String joinAnd(List<String> list) {
public static @NotNull String joinAnd(@Nullable List<String> list) {
return join(list, ", ", " and ");
}

public static @NotNull String join(Collection list, String separator) {
public static @NotNull String join(@Nullable Collection<?> list, @NotNull String separator) {

Check warning on line 41 in src/main/java/org/mvplugins/multiverse/core/utils/StringFormatter.java

View workflow job for this annotation

GitHub Actions / checkstyle / checkstyle

[checkstyle] reported by reviewdog 🐶 Missing a Javadoc comment. Raw Output: /github/workspace/./src/main/java/org/mvplugins/multiverse/core/utils/StringFormatter.java:41:5: warning: Missing a Javadoc comment. (com.puppycrawl.tools.checkstyle.checks.javadoc.MissingJavadocMethodCheck)
if (list == null || list.isEmpty()) {
return "";
}
return list.stream().map(String::valueOf).collect(Collectors.joining(separator)).toString();
return list.stream().map(String::valueOf).collect(Collectors.joining(separator));
}

/**
Expand All @@ -50,7 +54,7 @@
* @param lastSeparator the separator to use before the last element. For example, " and ".
* @return the concatenated string
*/
public static @NotNull String join(List<String> list, String separator, String lastSeparator) {
public static @NotNull String join(@Nullable List<String> list, @NotNull String separator, @NotNull String lastSeparator) {

Check warning on line 57 in src/main/java/org/mvplugins/multiverse/core/utils/StringFormatter.java

View workflow job for this annotation

GitHub Actions / checkstyle / checkstyle

[checkstyle] reported by reviewdog 🐶 Line is longer than 120 characters (found 127). Raw Output: /github/workspace/./src/main/java/org/mvplugins/multiverse/core/utils/StringFormatter.java:57:0: warning: Line is longer than 120 characters (found 127). (com.puppycrawl.tools.checkstyle.checks.sizes.LineLengthCheck)
if (list == null || list.isEmpty()) {
return "";
}
Expand All @@ -71,11 +75,30 @@

/**
* Appends a list of suggestions to the end of the input string, separated by commas.
*
* @param input The current input
* @param addons The autocomplete suggestions
* @return A collection of suggestions with the next suggestion appended
*
* @deprecated Method name has a spelling error. Use {@link #addOnToCommaSeparated(String, Collection)} instead.
*/
@Deprecated(forRemoval = true, since = "5.5")
@ApiStatus.ScheduledForRemoval(inVersion = "6.0")
public static Collection<String> addonToCommaSeperated(@Nullable String input, @NotNull Collection<String> addons) {
return addOnToCommaSeparated(input, addons);
}

/**
* Appends a list of suggestions to the end of the input string, separated by commas.
*
* @param input The current input
* @param addons The autocomplete suggestions
* @return A collection of suggestions with the next suggestion appended
*
* @since 5.5
*/
@ApiStatus.AvailableSince("5.5")
public static Collection<String> addOnToCommaSeparated(@Nullable String input, @NotNull Collection<String> addons) {
if (Strings.isNullOrEmpty(input)) {
return addons;
}
Expand All @@ -94,7 +117,7 @@
* @param args The args to parse
* @return The parsed args
*/
public static Collection<String> parseQuotesInArgs(String[] args) {
public static @NotNull Collection<String> parseQuotesInArgs(@NotNull String[] args) {

Check warning on line 120 in src/main/java/org/mvplugins/multiverse/core/utils/StringFormatter.java

View workflow job for this annotation

GitHub Actions / checkstyle / checkstyle

[checkstyle] reported by reviewdog 🐶 Cyclomatic Complexity is 12 (max allowed is 7). Raw Output: /github/workspace/./src/main/java/org/mvplugins/multiverse/core/utils/StringFormatter.java:120:5: warning: Cyclomatic Complexity is 12 (max allowed is 7). (com.puppycrawl.tools.checkstyle.checks.metrics.CyclomaticComplexityCheck)
List<String> result = new ArrayList<>(args.length);
StringBuilder current = new StringBuilder();
boolean inQuotes = false;
Expand Down Expand Up @@ -139,7 +162,28 @@
* @param input The string to add quotes to
* @return The quoted string
*/
public static String quoteMultiWordString(String input) {
return input.contains(" ") ? "\"" + input + "\"" : input;
@Contract("null -> null")
public static @Nullable String quoteMultiWordString(@Nullable String input) {
return input != null && input.contains(" ") ? "\"" + input + "\"" : input;
}

/**
* Parses a CSV string of key=value pairs into a map.
* E.g. "key1=value1,key2=value2" -> {key1=value1, key2=value2}
*
* @param input The CSV string to parse
* @return The parsed map
*
* @since 5.5
*/
@ApiStatus.AvailableSince("5.5")
public static @Unmodifiable Map<String, String> parseCSVMap(@Nullable String input) {

Check warning on line 180 in src/main/java/org/mvplugins/multiverse/core/utils/StringFormatter.java

View workflow job for this annotation

GitHub Actions / checkstyle / checkstyle

[checkstyle] reported by reviewdog 🐶 Abbreviation in name 'parseCSVMap' must contain no more than '1' consecutive capital letters. Raw Output: /github/workspace/./src/main/java/org/mvplugins/multiverse/core/utils/StringFormatter.java:180:53: warning: Abbreviation in name 'parseCSVMap' must contain no more than '1' consecutive capital letters. (com.puppycrawl.tools.checkstyle.checks.naming.AbbreviationAsWordInNameCheck)
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]));
}
}
Loading
Loading