diff --git a/leaf-server/src/main/java/org/dreeam/leaf/config/WorldList.java b/leaf-server/src/main/java/org/dreeam/leaf/config/WorldList.java new file mode 100644 index 0000000000..600ab2e0dc --- /dev/null +++ b/leaf-server/src/main/java/org/dreeam/leaf/config/WorldList.java @@ -0,0 +1,72 @@ +package org.dreeam.leaf.config; + +import org.jspecify.annotations.NullMarked; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.regex.Pattern; + +/// A world name list read from the config, matching either exact names or `*` wildcard patterns. +/// +/// `*` matches any (possibly empty) sequence of characters, so `hub*` matches `hub01` and `hub02`, +/// and `*hub*` matches every world whose name contains `hub`. Entries without `*` are matched +/// exactly, as before. Matching is case sensitive, like world folder names. +@NullMarked +public final class WorldList { + + private static final Pattern[] NO_PATTERNS = {}; + + public static final WorldList EMPTY = new WorldList(List.of()); + + private final Set names = new HashSet<>(); + private final Pattern[] patterns; + + public WorldList(List entries) { + List patterns = new ArrayList<>(); + for (String entry : entries) { + if (entry == null || entry.isBlank()) { + continue; + } + String trimmed = entry.trim(); + if (trimmed.indexOf('*') == -1) { + this.names.add(trimmed); + } else { + patterns.add(compile(trimmed)); + } + } + this.patterns = patterns.toArray(NO_PATTERNS); + } + + public boolean isEmpty() { + return this.names.isEmpty() && this.patterns.length == 0; + } + + public boolean contains(String worldName) { + if (this.names.contains(worldName)) { + return true; + } + for (Pattern pattern : this.patterns) { + if (pattern.matcher(worldName).matches()) { + return true; + } + } + return false; + } + + private static Pattern compile(String glob) { + String[] literals = glob.split("\\*", -1); + StringBuilder regex = new StringBuilder(glob.length() + 8); + for (int i = 0; i < literals.length; i++) { + if (i != 0) { + regex.append(".*"); + } + if (!literals[i].isEmpty()) { + regex.append(Pattern.quote(literals[i])); + } + } + // DOTALL so '*' also covers line terminators, world names are not required to be single line + return Pattern.compile(regex.toString(), Pattern.DOTALL); + } +} diff --git a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/misc/DisableWorldDataSaving.java b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/misc/DisableWorldDataSaving.java index 36b790b7d1..a2ee530086 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/misc/DisableWorldDataSaving.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/misc/DisableWorldDataSaving.java @@ -3,13 +3,13 @@ import net.minecraft.server.level.ServerLevel; import org.dreeam.leaf.config.ConfigModule; import org.dreeam.leaf.config.ConfigCategory; +import org.dreeam.leaf.config.WorldList; import java.util.ArrayList; -import java.util.List; public class DisableWorldDataSaving extends ConfigModule { - public static List worlds = new ArrayList<>(); + public static WorldList worlds = WorldList.EMPTY; public String basePath() { return ConfigCategory.MISC.basePath() + ".disable-world-data-saving"; @@ -17,13 +17,17 @@ public String basePath() { @Override public void onLoaded() { - worlds = globalConfig.getList(basePath() + ".worlds", worlds, + worlds = new WorldList(globalConfig.getList(basePath() + ".worlds", new ArrayList<>(), globalConfig.pickStringRegionBased(""" Worlds listed here will skip world data persistence. - Changes in chunks/entities remain in memory until unload/restart and are not written to disk.""", + Changes in chunks/entities remain in memory until unload/restart and are not written to disk. + '*' works as a wildcard, e.g. 'hub*' matches hub01 and hub02, '*hub*' matches every world + whose name contains 'hub'. Entries without '*' are matched exactly.""", """ 此处列出的世界将跳过世界数据持久化。 - 区块/实体更改仅保留在内存中直到卸载或重启,不会写入磁盘。""")); + 区块/实体更改仅保留在内存中直到卸载或重启,不会写入磁盘。 + '*' 可作为通配符使用, 例如 'hub*' 匹配 hub01 和 hub02, '*hub*' 匹配名称中包含 'hub' 的所有世界。 + 不含 '*' 的条目按完整名称精确匹配。"""))); } public static boolean shouldSkipSave(ServerLevel serverLevel) {