Correct Java 26.2 Iris world lifecycle

This commit is contained in:
Brian Neumann-Fopiano committed 2026-08-15 11:27:46 -04:00
1 parent 32beca236c
commit 57072d5bcd
61 files changed
+2180 -695

No files matched your search

@@ -1,6 +1,7 @@
package art.arcane.iris.core;
import art.arcane.iris.BuildConstants;
import art.arcane.iris.core.lifecycle.BukkitWorldConfiguration.IrisGeneratorBinding;
import art.arcane.iris.core.loader.IrisData;
import art.arcane.iris.core.loader.ResourceLoader;
import art.arcane.iris.core.nms.datapack.DataVersion;
@@ -21,6 +22,7 @@ import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.StandardOpenOption;
import java.nio.file.attribute.BasicFileAttributes;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
@@ -37,9 +39,29 @@ import java.util.concurrent.atomic.AtomicIntegerArray;
import java.util.stream.Stream;
public final class IrisDatapackCompiler {
private static final int INPUT_FINGERPRINT_SCHEMA = 1;
private static final int INPUT_FINGERPRINT_SCHEMA = 2;
private static final int WORLD_PACK_SCAN_DEPTH = 8;
private static final List<String> INPUT_DIRECTORIES = List.of("dimensions", "biomes", "snippet");
private static final String FLAT_VOID_LEVEL_STEM = """
{
"type": "%s",
"generator": {
"type": "minecraft:flat",
"settings": {
"biome": "minecraft:the_void",
"features": false,
"lakes": false,
"layers": [
{
"block": "minecraft:air",
"height": 1
}
],
"structure_overrides": []
}
}
}
""";
private IrisDatapackCompiler() {
}
@@ -65,28 +87,37 @@ public final class IrisDatapackCompiler {
public static String computeInputFingerprint(
List<File> packRoots,
List<IrisGeneratorBinding> bindings,
IDataFixer fixer,
boolean adjustVanillaHeight
) throws IOException {
Objects.requireNonNull(fixer, "fixer");
return computeInputFingerprint(
packRoots,
bindings,
adjustVanillaHeight,
compilerIdentity(fixer));
}
static String computeInputFingerprint(
List<File> packRoots,
List<IrisGeneratorBinding> bindings,
boolean adjustVanillaHeight,
String compilerIdentity
) throws IOException {
Objects.requireNonNull(packRoots, "packRoots");
Objects.requireNonNull(compilerIdentity, "compilerIdentity");
List<IrisGeneratorBinding> normalizedBindings = normalizeBindings(bindings);
MessageDigest digest = sha256();
updateDigestString(digest, "iris-datapack-compiler-input");
updateDigestInt(digest, INPUT_FINGERPRINT_SCHEMA);
updateDigestString(digest, compilerIdentity);
digest.update((byte) (adjustVanillaHeight ? 1 : 0));
updateDigestInt(digest, normalizedBindings.size());
for (IrisGeneratorBinding binding : normalizedBindings) {
updateDigestString(digest, binding.worldKey().toString());
updateDigestString(digest, binding.dimension());
}
updateDigestInt(digest, packRoots.size());
for (int index = 0; index < packRoots.size(); index++) {
@@ -139,12 +170,14 @@ public final class IrisDatapackCompiler {
public static CompilationResult compile(
List<File> packRoots,
KList<File> datapackRoots,
List<IrisGeneratorBinding> bindings,
IDataFixer fixer,
boolean adjustVanillaHeight
) throws IOException {
Objects.requireNonNull(packRoots, "packRoots");
Objects.requireNonNull(datapackRoots, "datapackRoots");
Objects.requireNonNull(fixer, "fixer");
List<IrisGeneratorBinding> normalizedBindings = normalizeBindings(bindings);
if (datapackRoots.isEmpty()) {
throw new IOException("No Iris datapack output roots were provided");
}
@@ -154,6 +187,7 @@ public final class IrisDatapackCompiler {
DimensionHeight height = new DimensionHeight(fixer);
Map<String, KSet<String>> biomes = new LinkedHashMap<>();
Map<String, List<DimensionCandidate>> dimensions = new LinkedHashMap<>();
int packCount = 0;
int dimensionCount = 0;
for (File packRoot : packRoots) {
@@ -177,6 +211,12 @@ public final class IrisDatapackCompiler {
}
IrisLogging.debug(" Compiling Dimension " + dimension.getLoadFile().getPath());
height.merge(dimension);
dimensions.computeIfAbsent(dimension.getLoadKey(), ignored -> new ArrayList<>())
.add(new DimensionCandidate(
dimension,
packRoot.toPath().toAbsolutePath().normalize(),
dimension.getDimensionType().toJson(fixer)
));
KSet<String> seenBiomes = biomes.computeIfAbsent(dimension.getLoadKey(), ignored -> new KSet<>());
dimension.installBiomes(fixer, dimension::getLoader, datapackRoots, seenBiomes);
dimension.installDimensionType(fixer, datapackRoots);
@@ -192,10 +232,80 @@ public final class IrisDatapackCompiler {
}
IrisDimension.writeShared(datapackRoots, height, adjustVanillaHeight);
validateOutputs(datapackRoots, dimensionCount);
installLevelStemBindings(normalizedBindings, dimensions, datapackRoots);
validateOutputs(datapackRoots, dimensionCount, normalizedBindings.size());
return new CompilationResult(packCount, dimensionCount, countBiomes(biomes));
}
private static void installLevelStemBindings(
List<IrisGeneratorBinding> bindings,
Map<String, List<DimensionCandidate>> dimensions,
Collection<File> datapackRoots
) throws IOException {
for (IrisGeneratorBinding binding : bindings) {
DimensionCandidate selected = resolveDimension(binding, dimensions.get(binding.dimension()));
String typeKey = "iris:" + selected.dimension().getDimensionTypeKey();
String levelStem = FLAT_VOID_LEVEL_STEM.formatted(typeKey);
for (File datapackRoot : datapackRoots) {
Path output = datapackRoot.toPath()
.toAbsolutePath()
.normalize()
.resolve("data/iris/dimension")
.resolve(binding.worldKey().key() + ".json");
Files.createDirectories(output.getParent());
Files.writeString(
output,
levelStem,
StandardCharsets.UTF_8,
StandardOpenOption.CREATE_NEW,
StandardOpenOption.WRITE
);
}
}
}
private static DimensionCandidate resolveDimension(
IrisGeneratorBinding binding,
List<DimensionCandidate> candidates
) throws IOException {
if (candidates == null || candidates.isEmpty()) {
throw new IOException("Iris world " + binding.worldKey() + " selects missing dimension \""
+ binding.dimension() + "\".");
}
DimensionCandidate selected = candidates.getFirst();
for (int index = 1; index < candidates.size(); index++) {
DimensionCandidate candidate = candidates.get(index);
if (!selected.dimensionTypeJson().equals(candidate.dimensionTypeJson())) {
throw new IOException("Iris world " + binding.worldKey() + " selects ambiguous dimension \""
+ binding.dimension() + "\" from " + selected.packRoot() + " and "
+ candidate.packRoot() + ".");
}
}
return selected;
}
private static List<IrisGeneratorBinding> normalizeBindings(List<IrisGeneratorBinding> bindings)
throws IOException {
List<IrisGeneratorBinding> requiredBindings = List.copyOf(
Objects.requireNonNull(bindings, "bindings")
);
Map<WorldSlotKey, IrisGeneratorBinding> byWorld = new LinkedHashMap<>();
for (IrisGeneratorBinding binding : requiredBindings) {
IrisGeneratorBinding requiredBinding = Objects.requireNonNull(binding, "binding");
IrisGeneratorBinding previous = byWorld.putIfAbsent(
requiredBinding.worldKey(),
requiredBinding
);
if (previous != null) {
throw new IOException("Multiple Iris LevelStem bindings target "
+ requiredBinding.worldKey() + ".");
}
}
ArrayList<IrisGeneratorBinding> normalized = new ArrayList<>(byWorld.values());
normalized.sort(Comparator.comparing(binding -> binding.worldKey().toString()));
return List.copyOf(normalized);
}
private static void collectInstalledPackRoots(
Path packsRoot,
Map<Path, File> roots,
@@ -363,7 +473,11 @@ public final class IrisDatapackCompiler {
}
}
private static void validateOutputs(Collection<File> datapackRoots, int dimensionCount) throws IOException {
private static void validateOutputs(
Collection<File> datapackRoots,
int dimensionCount,
int levelStemCount
) throws IOException {
for (File datapackRoot : datapackRoots) {
Path root = datapackRoot.toPath();
if (!Files.isRegularFile(root.resolve("pack.mcmeta"))) {
@@ -372,6 +486,9 @@ public final class IrisDatapackCompiler {
if (dimensionCount > 0 && !Files.isDirectory(root.resolve("data/iris/dimension_type"))) {
throw new IOException("Iris dimension types were not generated at " + root);
}
if (levelStemCount > 0 && !Files.isDirectory(root.resolve("data/iris/dimension"))) {
throw new IOException("Iris LevelStem bindings were not generated at " + root);
}
}
}
@@ -389,6 +506,13 @@ public final class IrisDatapackCompiler {
private record CompilerInputEntry(Path source, String relativePath) {
}
private record DimensionCandidate(
IrisDimension dimension,
Path packRoot,
String dimensionTypeJson
) {
}
public static final class DimensionHeight {
private final IDataFixer fixer;
private final AtomicIntegerArray[] dimensions = new AtomicIntegerArray[3];
@@ -109,6 +109,16 @@ public final class IrisStartupValidation {
}
}
public static void requireWorldReplacementStagingReady() {
Snapshot current = snapshot;
if (current.enforced()
&& current.datapacks() == ValidationState.RESTART_REQUIRED
&& current.packs() == ValidationState.READY) {
return;
}
requireWorldCreationReady();
}
static Snapshot snapshot() {
return snapshot;
}
@@ -10,8 +10,9 @@ import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Path;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.Path;
import java.util.Locale;
import java.util.Objects;
import java.util.Optional;
@@ -125,6 +126,54 @@ public final class IrisWorldStorage {
return key;
}
public static NamespacedKey replacementKeyFromName(String requestedName, String levelName) {
String requested = Objects.requireNonNull(requestedName, "requestedName").trim();
String configuredLevelName = Objects.requireNonNull(levelName, "levelName").trim();
if (requested.isEmpty()) {
throw new IllegalArgumentException("World name cannot be empty.");
}
if (configuredLevelName.isEmpty()) {
throw new IllegalArgumentException("Configured level name cannot be empty.");
}
if (requested.contains("/") || requested.contains("\\") || requested.contains("..")) {
throw new IllegalArgumentException("World name must be a safe single path segment.");
}
String normalized = requested.toLowerCase(Locale.ENGLISH);
if (normalized.contains(":")) {
int separator = normalized.indexOf(':');
if (separator == 0
|| separator == normalized.length() - 1
|| separator != normalized.lastIndexOf(':')) {
throw new IllegalArgumentException("World identifier is invalid: " + requestedName);
}
NamespacedKey explicit = NamespacedKey.fromString(normalized);
if (explicit == null) {
throw new IllegalArgumentException("World identifier is invalid: " + requestedName);
}
return explicit;
}
String configuredIrisPrefix = configuredLevelName + "_" + IRIS_NAMESPACE + "_";
if (requested.startsWith(configuredIrisPrefix)) {
return keyFromConfiguredWorldName(requested, configuredLevelName);
}
if (requested.equalsIgnoreCase(configuredLevelName)) {
return NamespacedKey.minecraft("overworld");
}
if (requested.equalsIgnoreCase(configuredLevelName + "_nether")) {
return NamespacedKey.minecraft("the_nether");
}
if (requested.equalsIgnoreCase(configuredLevelName + "_the_end")) {
return NamespacedKey.minecraft("the_end");
}
return switch (normalized) {
case "main", "overworld" -> NamespacedKey.minecraft("overworld");
case "nether", "the_nether" -> NamespacedKey.minecraft("the_nether");
case "end", "the_end" -> NamespacedKey.minecraft("the_end");
default -> new NamespacedKey(IRIS_NAMESPACE, normalized.replace(' ', '_'));
};
}
static NamespacedKey keyFromName(String worldName, String levelName) {
String name = Objects.requireNonNull(worldName, "worldName").trim();
String mainLevelName = Objects.requireNonNull(levelName, "levelName").trim();
@@ -145,6 +194,53 @@ public final class IrisWorldStorage {
return new NamespacedKey(IRIS_NAMESPACE, key);
}
public static NamespacedKey keyFromConfiguredWorldName(String configuredWorldName, String levelName) {
String name = Objects.requireNonNull(configuredWorldName, "configuredWorldName").trim();
String mainLevelName = Objects.requireNonNull(levelName, "levelName").trim();
if (name.isEmpty()) {
throw new IllegalArgumentException("Configured world name cannot be empty.");
}
if (mainLevelName.isEmpty()) {
throw new IllegalArgumentException("Level name cannot be empty.");
}
String irisPrefix = mainLevelName + "_" + IRIS_NAMESPACE + "_";
if (name.startsWith(irisPrefix)) {
return managedKeyFromName(IRIS_NAMESPACE + ":" + name.substring(irisPrefix.length()), mainLevelName);
}
return keyFromName(name, mainLevelName);
}
public static String configuredWorldName(NamespacedKey key, String levelName) {
NamespacedKey worldKey = Objects.requireNonNull(key, "key");
String mainLevelName = Objects.requireNonNull(levelName, "levelName").trim();
if (mainLevelName.isEmpty()) {
throw new IllegalArgumentException("Level name cannot be empty.");
}
if (NamespacedKey.minecraft("overworld").equals(worldKey)) {
return mainLevelName;
}
if (NamespacedKey.minecraft("the_nether").equals(worldKey)) {
return mainLevelName + "_nether";
}
if (NamespacedKey.minecraft("the_end").equals(worldKey)) {
return mainLevelName + "_the_end";
}
if (!IRIS_NAMESPACE.equals(worldKey.getNamespace()) || !worldKey.getKey().matches("[a-z0-9_-]+")) {
throw new IllegalArgumentException("Only safe Iris-managed world keys have a Bukkit startup name.");
}
return mainLevelName + "_" + worldKey.getNamespace() + "_" + worldKey.getKey();
}
public static String configuredWorldName(WorldSlotKey key, String levelName) {
WorldSlotKey worldKey = Objects.requireNonNull(key, "key");
NamespacedKey namespacedKey = NamespacedKey.fromString(worldKey.toString());
if (namespacedKey == null) {
throw new IllegalArgumentException("World key is invalid: " + worldKey);
}
return configuredWorldName(namespacedKey, levelName);
}
public static String logicalName(WorldInfo world) {
return logicalName(WorldIdentity.key(world));
}
@@ -214,6 +310,15 @@ public final class IrisWorldStorage {
return target.toFile();
}
public static boolean isExistingManagedDimensionRoot(File levelRoot, NamespacedKey key) {
try {
Path target = requireSafeManagedDimensionRoot(levelRoot, key).toPath();
return Files.isDirectory(target, LinkOption.NOFOLLOW_LINKS);
} catch (IllegalArgumentException exception) {
return false;
}
}
public static File dimensionRoot(File levelRoot, NamespacedKey key) {
Path dimensionsRoot = Objects.requireNonNull(levelRoot, "levelRoot")
.toPath()
@@ -14,6 +14,7 @@ import art.arcane.volmlib.util.io.IO;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import org.bukkit.NamespacedKey;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.YamlConfiguration;
@@ -25,9 +26,11 @@ import java.nio.channels.FileChannel;
import java.nio.charset.StandardCharsets;
import java.nio.file.AtomicMoveNotSupportedException;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.nio.file.StandardOpenOption;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Stream;
@@ -35,34 +38,41 @@ public class IrisWorlds {
private static final AtomicCache<IrisWorlds> cache = new AtomicCache<>();
private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create();
private static final Type TYPE = TypeToken.getParameterized(KMap.class, String.class, String.class).getType();
private final Path levelRoot;
private final Path registryFile;
private final KMap<String, String> worlds;
private volatile boolean dirty = false;
private IrisWorlds(KMap<String, String> worlds) {
private IrisWorlds(Path levelRoot, KMap<String, String> worlds) {
this.levelRoot = Objects.requireNonNull(levelRoot, "levelRoot").toAbsolutePath().normalize();
registryFile = registryFile(this.levelRoot);
this.worlds = new KMap<>();
worlds.forEach((identity, type) -> this.worlds.put(WorldIdentity.parse(identity).toString(), type));
readBukkitWorlds().forEach((name, type) -> put0(IrisWorldStorage.keyFromName(name).toString(), type));
readBukkitWorlds(this.levelRoot).forEach((name, type) -> put0(
IrisWorldStorage.keyFromConfiguredWorldName(name, this.levelRoot.getFileName().toString()).toString(),
type));
save();
}
public static IrisWorlds get() {
return cache.aquire(() -> {
File file = IrisPlatforms.get().dataFile("worlds.json");
Path levelRoot = IrisWorldStorage.levelRoot().toPath().toAbsolutePath().normalize();
File file = registryFile(levelRoot).toFile();
if (!file.exists()) {
return new IrisWorlds(new KMap<>());
return new IrisWorlds(levelRoot, new KMap<>());
}
try {
String json = IO.readAll(file);
KMap<String, String> worlds = GSON.fromJson(json, TYPE);
return new IrisWorlds(Objects.requireNonNullElseGet(worlds, KMap::new));
return new IrisWorlds(levelRoot, Objects.requireNonNullElseGet(worlds, KMap::new));
} catch (Throwable e) {
IrisLogging.error("Failed to load worlds.json!");
IrisLogging.error("Failed to load worlds.json for level root " + levelRoot + "!");
e.printStackTrace();
IrisLogging.reportError(e);
}
return new IrisWorlds(new KMap<>());
return new IrisWorlds(levelRoot, new KMap<>());
});
}
@@ -114,7 +124,9 @@ public class IrisWorlds {
public synchronized KMap<String, String> getWorlds() {
clean();
KMap<String, String> result = new KMap<>();
readBukkitWorlds().forEach((name, type) -> result.put(IrisWorldStorage.keyFromName(name).toString(), type));
readBukkitWorlds(levelRoot).forEach((name, type) -> result.put(
IrisWorldStorage.keyFromConfiguredWorldName(name, levelRoot.getFileName().toString()).toString(),
type));
return result.put(worlds);
}
@@ -135,7 +147,7 @@ public class IrisWorlds {
public synchronized void clean() {
boolean removed = worlds.entrySet().removeIf(entry -> {
try {
File packRoot = IrisWorldStorage.packRoot(WorldIdentity.parse(entry.getKey()));
File packRoot = packRoot(entry.getKey());
return !new File(packRoot, "dimensions/" + entry.getValue() + ".json").exists();
} catch (IllegalArgumentException e) {
return true;
@@ -159,7 +171,7 @@ public class IrisWorlds {
return;
}
Path target = IrisPlatforms.get().dataFile("worlds.json").toPath().toAbsolutePath().normalize();
Path target = registryFile;
Path parent = target.getParent();
if (parent == null) {
throw new IOException("worlds.json target has no parent: " + target);
@@ -193,6 +205,49 @@ public class IrisWorlds {
}
public static KMap<String, String> readBukkitWorlds() {
return readBukkitWorlds(IrisWorldStorage.levelRoot().toPath());
}
static Path registryFile(Path levelRoot) {
return Objects.requireNonNull(levelRoot, "levelRoot")
.toAbsolutePath()
.normalize()
.resolve("iris")
.resolve("worlds.json");
}
static KMap<String, String> filterBukkitWorldsByStorage(
Path levelRoot,
Map<String, String> configuredWorlds
) {
Path root = Objects.requireNonNull(levelRoot, "levelRoot").toAbsolutePath().normalize();
Path levelNamePath = root.getFileName();
if (levelNamePath == null) {
throw new IllegalArgumentException("Selected level root has no name: " + root);
}
KMap<String, String> result = new KMap<>();
for (Map.Entry<String, String> entry : Objects.requireNonNull(configuredWorlds, "configuredWorlds").entrySet()) {
String configuredWorldName = entry.getKey();
NamespacedKey worldKey = IrisWorldStorage.keyFromConfiguredWorldName(
configuredWorldName,
levelNamePath.toString());
if (!IrisWorldStorage.configuredWorldName(worldKey, levelNamePath.toString())
.equals(configuredWorldName)) {
continue;
}
Path dimensionRoot = IrisWorldStorage.dimensionRoot(root.toFile(), worldKey)
.toPath()
.toAbsolutePath()
.normalize();
if (Files.isDirectory(dimensionRoot, LinkOption.NOFOLLOW_LINKS)) {
result.put(configuredWorldName, entry.getValue());
}
}
return result;
}
private static KMap<String, String> readBukkitWorlds(Path levelRoot) {
YamlConfiguration bukkit = YamlConfiguration.loadConfiguration(ServerProperties.BUKKIT_YML);
ConfigurationSection worlds = bukkit.getConfigurationSection("worlds");
if (worlds == null) return new KMap<>();
@@ -212,11 +267,16 @@ public class IrisWorlds {
result.put(world, loadKey);
}
return result;
return filterBukkitWorldsByStorage(levelRoot, result);
}
private static IrisDimension loadDimension(String worldIdentity, String id) {
File pack = IrisWorldStorage.packRoot(WorldIdentity.parse(worldIdentity));
private File packRoot(String worldIdentity) {
NamespacedKey worldKey = WorldIdentity.parse(worldIdentity);
return new File(IrisWorldStorage.dimensionRoot(levelRoot.toFile(), worldKey), "iris/pack");
}
private IrisDimension loadDimension(String worldIdentity, String id) {
File pack = packRoot(worldIdentity);
IrisDimension dimension = pack.isDirectory() ? IrisData.get(pack).getDimensionLoader().load(id) : null;
if (dimension == null) {
dimension = IrisData.loadAnyDimension(id, null);
@@ -24,6 +24,8 @@ import art.arcane.iris.core.datapack.DatapackIngestService;
import art.arcane.iris.core.datapack.DatapackIngestService.ReapplyOutcome;
import art.arcane.iris.core.loader.IrisData;
import art.arcane.iris.core.loader.ResourceLoader;
import art.arcane.iris.core.lifecycle.BukkitWorldConfiguration;
import art.arcane.iris.core.lifecycle.BukkitWorldConfiguration.IrisGeneratorBinding;
import art.arcane.iris.core.lifecycle.LifecycleOperationCoordinator;
import art.arcane.iris.core.nms.INMS;
import art.arcane.iris.core.nms.datapack.DataVersion;
@@ -276,10 +278,12 @@ public class ServerConfigurator {
IrisLogging.debug("Checking Data Packs...");
}
List<File> packRoots;
List<IrisGeneratorBinding> bindings;
try {
packRoots = collectCompilerPackRoots();
bindings = collectConfiguredLevelStemBindings();
} catch (IOException exception) {
IrisLogging.reportError("Unable to resolve Iris datapack compiler roots.", exception);
IrisLogging.reportError("Unable to resolve Iris datapack compiler inputs.", exception);
return DatapackInstallResult.failedResult();
}
@@ -303,6 +307,7 @@ public class ServerConfigurator {
IrisDatapackCompiler.compile(
packRoots,
stagedRoots,
bindings,
fixer,
IrisSettings.get().getGeneral().adjustVanillaHeight
);
@@ -445,11 +450,25 @@ public class ServerConfigurator {
IrisWorldStorage.levelRoot().toPath());
}
private static List<IrisGeneratorBinding> collectConfiguredLevelStemBindings() throws IOException {
File levelRoot = IrisWorldStorage.levelRoot();
String levelId = levelRoot.getName();
if (levelId.isBlank()) {
throw new IOException("Configured level root has no Paper startup level id: " + levelRoot);
}
return BukkitWorldConfiguration.readIrisGeneratorBindings(
ServerProperties.BUKKIT_YML,
levelId,
levelRoot.toPath()
);
}
private static String computeCurrentDatapackCompilerInputFingerprint(IDataFixer fixer) throws IOException {
return IrisDatapackCompiler.computeInputFingerprint(
IrisDatapackCompiler.collectCompilerInputRoots(
IrisPlatforms.get().dataFolder().toPath(),
IrisWorldStorage.levelRoot().toPath()),
collectConfiguredLevelStemBindings(),
Objects.requireNonNull(fixer, "Datapack fixer"),
IrisSettings.get().getGeneral().adjustVanillaHeight);
}
@@ -1,5 +1,8 @@
package art.arcane.iris.core.lifecycle;
import art.arcane.iris.core.IrisWorldStorage;
import art.arcane.iris.core.WorldSlotKey;
import org.bukkit.NamespacedKey;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.InvalidConfigurationException;
import org.bukkit.configuration.file.YamlConfiguration;
@@ -15,6 +18,10 @@ import java.nio.file.StandardCopyOption;
import java.nio.file.StandardOpenOption;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Predicate;
@@ -91,6 +98,95 @@ public final class BukkitWorldConfiguration {
}
}
public static List<IrisGeneratorBinding> readIrisGeneratorBindings(
File configurationFile,
String levelName,
Path levelRoot
) throws IOException {
File requiredConfigurationFile = Objects.requireNonNull(configurationFile, "configurationFile");
String requiredLevelName = requireName(levelName, "Level name");
Path requiredLevelRoot = Objects.requireNonNull(levelRoot, "levelRoot")
.toAbsolutePath()
.normalize();
Path configurationPath = requiredConfigurationFile.toPath();
if (!Files.exists(configurationPath, LinkOption.NOFOLLOW_LINKS)) {
return List.of();
}
synchronized (MUTATION_LOCK) {
YamlConfiguration configuration = load(requiredConfigurationFile);
Object rawWorlds = configuration.get("worlds");
ConfigurationSection worlds = configuration.getConfigurationSection("worlds");
if (rawWorlds != null && worlds == null) {
throw new IOException("bukkit.yml worlds entry is not a section.");
}
if (worlds == null) {
return List.of();
}
List<String> configuredNames = new ArrayList<>(worlds.getKeys(false));
configuredNames.sort(Comparator.naturalOrder());
Map<WorldSlotKey, IrisGeneratorBinding> bindings = new LinkedHashMap<>();
for (String configuredName : configuredNames) {
Object rawWorld = worlds.get(configuredName);
ConfigurationSection world = worlds.getConfigurationSection(configuredName);
if (rawWorld != null && world == null) {
throw new IOException("bukkit.yml world entry \"" + configuredName + "\" is not a section.");
}
if (world == null || !world.getKeys(false).contains("generator")) {
continue;
}
Object rawGenerator = world.get("generator");
if (!(rawGenerator instanceof String generator)) {
throw new IOException("bukkit.yml generator for world \"" + configuredName
+ "\" is not a string.");
}
String configuredGenerator = generator.trim();
if (!configuredGenerator.equalsIgnoreCase("Iris")
&& !configuredGenerator.regionMatches(true, 0, "Iris:", 0, 5)) {
continue;
}
NamespacedKey namespacedKey;
try {
namespacedKey = IrisWorldStorage.keyFromConfiguredWorldName(
configuredName,
requiredLevelName
);
} catch (IllegalArgumentException failure) {
throw new IOException("bukkit.yml contains an invalid Iris world name \""
+ configuredName + "\".", failure);
}
if (!"iris".equals(namespacedKey.getNamespace())) {
continue;
}
if (!configuredName.equals(IrisWorldStorage.configuredWorldName(
namespacedKey,
requiredLevelName
))) {
continue;
}
if (!IrisWorldStorage.isExistingManagedDimensionRoot(
requiredLevelRoot.toFile(),
namespacedKey
)) {
continue;
}
String dimension = selectedIrisDimension(configuredGenerator, configuredName);
WorldSlotKey worldKey = new WorldSlotKey(
namespacedKey.getNamespace(),
namespacedKey.getKey()
);
IrisGeneratorBinding binding = new IrisGeneratorBinding(configuredName, worldKey, dimension);
IrisGeneratorBinding previous = bindings.putIfAbsent(worldKey, binding);
if (previous != null) {
throw new IOException("bukkit.yml maps both \"" + previous.configuredWorldName()
+ "\" and \"" + configuredName + "\" to " + worldKey + ".");
}
}
return List.copyOf(bindings.values());
}
}
public static GeneratorReplacement replaceIfMatching(
File configurationFile,
String worldName,
@@ -396,11 +492,40 @@ public final class BukkitWorldConfiguration {
return value.trim();
}
private static String selectedIrisDimension(String configuredGenerator, String worldName) throws IOException {
if (configuredGenerator.equalsIgnoreCase("Iris")) {
throw new IOException("bukkit.yml Iris generator for custom world \"" + worldName
+ "\" must select a dimension with Iris:<dimension>.");
}
String dimension = configuredGenerator.substring(5).trim();
if (dimension.isEmpty()) {
throw new IOException("bukkit.yml Iris generator for world \"" + worldName
+ "\" does not select a dimension.");
}
return dimension;
}
public enum Registration {
CREATED,
UNCHANGED
}
public record IrisGeneratorBinding(
String configuredWorldName,
WorldSlotKey worldKey,
String dimension
) {
public IrisGeneratorBinding {
configuredWorldName = requireName(configuredWorldName, "Configured world name");
WorldSlotKey requiredWorldKey = Objects.requireNonNull(worldKey, "worldKey");
if (!"iris".equals(requiredWorldKey.namespace())
|| !requiredWorldKey.key().matches("[a-z0-9_-]+")) {
throw new IllegalArgumentException("Only safe Iris-managed keys can have LevelStem bindings.");
}
dimension = requireName(dimension, "Iris dimension");
}
}
public record WorldGeneratorSnapshot(
boolean worldsSectionPresent,
boolean worldSectionPresent,
@@ -18,6 +18,7 @@ import art.arcane.volmlib.util.bukkit.WorldIdentity;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Player;
@@ -695,6 +696,28 @@ public final class IrisWorldRemovalService {
}
}
static String bukkitConfigurationWorldName(WorldRemovalPathPolicy.Target target) {
WorldRemovalPathPolicy.Target requiredTarget = Objects.requireNonNull(target, "target");
Path levelName = requiredTarget.levelRoot().getFileName();
if (levelName == null || levelName.toString().isBlank()) {
throw new IllegalArgumentException("Level root must have a Bukkit startup name.");
}
return IrisWorldStorage.configuredWorldName(requiredTarget.worldKey(), levelName.toString());
}
static String bukkitGenerator(
YamlConfiguration configuration,
WorldRemovalPathPolicy.Target target
) {
ConfigurationSection worlds = Objects.requireNonNull(configuration, "configuration")
.getConfigurationSection("worlds");
if (worlds == null) {
return null;
}
ConfigurationSection world = worlds.getConfigurationSection(bukkitConfigurationWorldName(target));
return world == null ? null : world.getString("generator");
}
private static final class RemovalFailure extends CompletionException {
private final RemovalStatus status;
private final Path quarantineDirectory;
@@ -840,7 +863,7 @@ public final class IrisWorldRemovalService {
try {
boolean bukkitChanged = BukkitWorldConfiguration.remove(
ServerProperties.BUKKIT_YML,
target.logicalName()
bukkitConfigurationWorldName(target)
);
return new ConfigurationDisposition(multiverseChanged || bukkitChanged, null);
} catch (Throwable failure) {
@@ -931,7 +954,7 @@ public final class IrisWorldRemovalService {
);
boolean directoryPresent = Files.isDirectory(target.worldDirectory(), LinkOption.NOFOLLOW_LINKS);
YamlConfiguration configuration = YamlConfiguration.loadConfiguration(ServerProperties.BUKKIT_YML);
String generator = configuration.getString("worlds." + target.logicalName() + ".generator");
String generator = bukkitGenerator(configuration, target);
boolean configurationManaged = generator != null
&& (generator.equalsIgnoreCase("Iris") || generator.regionMatches(true, 0, "Iris:", 0, 5));
boolean conflictingConfiguration = generator != null && !configurationManaged;
@@ -180,7 +180,6 @@ public final class LifecycleOperationCoordinator {
WORLD_UNLOAD,
WORLD_REMOVE,
WORLD_REPLACE,
WORLD_PROMOTE,
STUDIO_OPEN,
STUDIO_CLOSE,
PACK_CREATE,
@@ -2,6 +2,7 @@ package art.arcane.iris.core.lifecycle;
import art.arcane.iris.core.ExactWorldSlotPathPolicy;
import art.arcane.iris.core.SnapshotDirectoryTreeDeleter;
import art.arcane.iris.core.pack.PackDirectoryResolver;
import java.io.IOException;
import java.io.InputStream;
@@ -27,6 +28,7 @@ import java.util.regex.Pattern;
import java.util.stream.Stream;
public final class WorldReplacementFilesystem {
private static final String CODE_WORKSPACE_SUFFIX = ".code-workspace";
private static final List<Path> PAPER_WORLD_METADATA = List.of(
Path.of("data/paper/metadata.dat"),
Path.of("data/paper/level_overrides.dat"),
@@ -55,15 +57,15 @@ public final class WorldReplacementFilesystem {
ReplacementPaths requiredPaths = Objects.requireNonNull(paths, "paths");
State state = inspect(requiredPaths);
if (!state.targetPresent()) {
throw new IOException("overwrite=true requires an existing exact world slot; use ordinary create for a new world.");
throw new IOException("/iris replace requires an existing exact world slot; use /iris create for a new world.");
}
if (state.stagePresent() || state.backupPresent()) {
throw new IOException("A replacement artifact already exists for this transaction.");
}
requireMigratedRetainedWorld(requiredPaths.target());
requireCurrentPaperWorld(requiredPaths.target());
}
public static void requireMigratedRetainedWorld(Path retainedWorld) throws IOException {
public static void requireCurrentPaperWorld(Path retainedWorld) throws IOException {
try {
requireDirectory(retainedWorld, "retained world");
requireDirectory(retainedWorld.resolve("data"), "retained world data");
@@ -249,13 +251,15 @@ public final class WorldReplacementFilesystem {
try (Stream<Path> stream = Files.walk(root)) {
files = stream
.filter(path -> !path.equals(root))
.filter(path -> !containsMetadataSegment(root.relativize(path)))
.sorted(Comparator.comparing(path -> root.relativize(path).toString()))
.toList();
}
for (Path file : files) {
BasicFileAttributes attributes = requireSafeEntry(file);
Path relative = root.relativize(file);
if (isGeneratedPackMetadata(relative, attributes)) {
continue;
}
update(digest, relative.toString().replace(file.getFileSystem().getSeparator(), "/"));
digest.update((byte) (attributes.isDirectory() ? 1 : 0));
if (!attributes.isRegularFile()) {
@@ -304,13 +308,10 @@ public final class WorldReplacementFilesystem {
return fingerprint;
}
private static boolean containsMetadataSegment(Path relative) {
for (Path component : relative) {
if (".iris".equals(component.toString())) {
return true;
}
}
return false;
private static boolean isGeneratedPackMetadata(Path relative, BasicFileAttributes attributes) {
return PackDirectoryResolver.isHiddenName(relative.getName(0).toString())
|| attributes.isRegularFile()
&& relative.getFileName().toString().endsWith(CODE_WORKSPACE_SUFFIX);
}
private static BasicFileAttributes requireSafeEntry(Path path) throws IOException {
@@ -347,7 +348,7 @@ public final class WorldReplacementFilesystem {
}
private static void preservePaperWorldMetadata(Path retainedWorld, Path replacementWorld) throws IOException {
requireMigratedRetainedWorld(retainedWorld);
requireCurrentPaperWorld(retainedWorld);
requireDirectory(replacementWorld, "replacement world");
ensureDirectory(replacementWorld.resolve("data"), "replacement world data");
ensureDirectory(replacementWorld.resolve("data/paper"), "replacement Paper data");
@@ -1,6 +1,7 @@
package art.arcane.iris.core.lifecycle;
import art.arcane.iris.core.ExactWorldSlotPathPolicy;
import art.arcane.iris.core.IrisWorldStorage;
import art.arcane.iris.core.WorldSlotKey;
import art.arcane.iris.core.lifecycle.BukkitWorldConfiguration.WorldGeneratorSnapshot;
@@ -152,15 +153,7 @@ public final class WorldReplacementJournal {
}
String levelName = fileName.toString();
if ("iris".equals(requiredWorldKey.namespace())) {
String logicalName = requiredWorldKey.key();
if (logicalName.equals(levelName)
|| logicalName.equals(levelName + "_nether")
|| logicalName.equals(levelName + "_the_end")) {
throw new IllegalArgumentException(
"An Iris-managed world cannot use a configured vanilla world alias."
);
}
return logicalName;
return IrisWorldStorage.configuredWorldName(requiredWorldKey, levelName);
}
if (WorldSlotKey.minecraft("overworld").equals(requiredWorldKey)) {
return levelName;
@@ -0,0 +1,119 @@
package art.arcane.iris.core.lifecycle;
import art.arcane.volmlib.util.nbt.io.NBTUtil;
import art.arcane.volmlib.util.nbt.io.NamedTag;
import art.arcane.volmlib.util.nbt.tag.CompoundTag;
import art.arcane.volmlib.util.nbt.tag.LongTag;
import art.arcane.volmlib.util.nbt.tag.Tag;
import java.io.IOException;
import java.nio.channels.FileChannel;
import java.nio.file.AtomicMoveNotSupportedException;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.nio.file.StandardOpenOption;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.Objects;
public final class WorldReplacementSeed {
private static final Path WORLD_GEN_SETTINGS = Path.of("data/minecraft/world_gen_settings.dat");
private WorldReplacementSeed() {
}
public static long readAuthoritativeSeed(Path worldDirectory) throws IOException {
Path requiredWorldDirectory = Objects.requireNonNull(worldDirectory, "worldDirectory")
.toAbsolutePath()
.normalize();
Path settings = requiredWorldDirectory.resolve(WORLD_GEN_SETTINGS);
NamedTag namedTag = readSettings(settings);
return requireData(namedTag, settings).getLongTag("seed").asLong();
}
public static void copyWithAuthoritativeSeed(
Path sourceWorldDirectory,
Path targetWorldDirectory,
long seed
) throws IOException {
Path sourceWorld = Objects.requireNonNull(sourceWorldDirectory, "sourceWorldDirectory")
.toAbsolutePath()
.normalize();
Path targetWorld = Objects.requireNonNull(targetWorldDirectory, "targetWorldDirectory")
.toAbsolutePath()
.normalize();
Path source = sourceWorld.resolve(WORLD_GEN_SETTINGS);
Path target = targetWorld.resolve(WORLD_GEN_SETTINGS);
NamedTag namedTag = readSettings(source);
CompoundTag data = requireData(namedTag, source);
data.putLong("seed", seed);
if (Files.exists(target, LinkOption.NOFOLLOW_LINKS) || Files.isSymbolicLink(target)) {
throw new IOException("Staged Paper world generation settings already exist: " + target);
}
Path parent = target.getParent();
if (parent == null) {
throw new IOException("Staged Paper world generation settings have no parent: " + target);
}
Files.createDirectories(parent);
Path staged = Files.createTempFile(parent, ".world-gen-settings-", ".dat");
try {
NBTUtil.write(namedTag, staged.toFile(), true);
try (FileChannel channel = FileChannel.open(staged, StandardOpenOption.WRITE)) {
channel.force(true);
}
try {
Files.move(staged, target, StandardCopyOption.ATOMIC_MOVE);
} catch (AtomicMoveNotSupportedException exception) {
Files.move(staged, target);
}
} finally {
Files.deleteIfExists(staged);
}
long writtenSeed = readAuthoritativeSeed(targetWorld);
if (writtenSeed != seed) {
throw new IOException("Staged Paper world generation settings did not retain the requested seed.");
}
}
private static NamedTag readSettings(Path settings) throws IOException {
BasicFileAttributes attributes = Files.readAttributes(
settings,
BasicFileAttributes.class,
LinkOption.NOFOLLOW_LINKS
);
if (attributes.isSymbolicLink() || !attributes.isRegularFile()) {
throw new IOException("Paper world generation settings are not a regular file: " + settings);
}
try {
return NBTUtil.read(settings.toFile());
} catch (IOException failure) {
throw new IOException("Could not read Paper world generation settings: " + settings, failure);
}
}
private static CompoundTag requireData(NamedTag namedTag, Path settings) throws IOException {
Tag<?> rootTag = namedTag.getTag();
if (!(rootTag instanceof CompoundTag root)) {
throw new IOException("Paper world generation settings must have a compound root: " + settings);
}
Tag<?> dataTag = root.get("data");
if (dataTag == null) {
throw new IOException("Paper world generation settings are missing data: " + settings);
}
if (!(dataTag instanceof CompoundTag data)) {
throw new IOException("Paper world generation settings data must be a compound tag: " + settings);
}
Tag<?> seedTag = data.get("seed");
if (seedTag == null) {
throw new IOException("Paper world generation settings are missing data.seed: " + settings);
}
if (!(seedTag instanceof LongTag seed)) {
throw new IOException("Paper world generation settings data.seed must be a long tag: " + settings);
}
return data;
}
}
@@ -175,9 +175,9 @@ public final class BukkitCommandMessagesExtended {
"iris.bukkit.commandiris.try_one_overworld_vanilla_flat_theend",
C.YELLOW + "Try one of: overworld, vanilla, flat, theend"
);
public static final TextKey COMMAND_IRIS_WORLD_STAGING_COMPLETED_RESTART_SERVER_GENERATE_LOAD = TextKey.of(
"iris.bukkit.commandiris.world_staging_completed_restart_server_generate_load",
C.GREEN + "World staging completed. Restart the server to generate/load \"" + "{worldName}" + "\"."
public static final TextKey COMMAND_IRIS_WORLD_STAGING_COMPLETED_RESTARTING_SERVER_GENERATE_LOAD = TextKey.of(
"iris.bukkit.commandiris.world_staging_completed_restarting_server_generate_load",
C.GREEN + "World staging completed. Iris is restarting the server to generate/load \"" + "{worldName}" + "\"."
);
public static final TextKey COMMAND_IRIS_EXCEPTION_RAISED_DURING_CREATION_SEE_CONSOLE_MORE_DETAILS = TextKey.of(
"iris.bukkit.commandiris.exception_raised_during_creation_see_console_more_details",
@@ -187,10 +187,6 @@ public final class BukkitCommandMessagesExtended {
"iris.bukkit.commandiris.successfully_created_your_world",
C.GREEN + "Successfully created your world!"
);
public static final TextKey COMMAND_IRIS_YOUR_WORLD_WILL_AUTOMATICALLY_BE_SET_AS_MAIN_WORLD_WHEN = TextKey.of(
"iris.bukkit.commandiris.your_world_will_automatically_be_set_as_main_world_when",
C.GREEN + "Your world will automatically be set as the main world when the server restarts."
);
public static final TextKey COMMAND_IRIS_RUNTIME_WORLD_CREATION_IS_DISABLED_ON_FOLIA = TextKey.of(
"iris.bukkit.commandiris.runtime_world_creation_is_disabled_on_folia",
C.YELLOW + "Runtime world creation is disabled on Folia."
@@ -203,22 +199,10 @@ public final class BukkitCommandMessagesExtended {
"iris.bukkit.commandiris.failed_stage_world_files_dimension",
C.RED + "Failed to stage world files for dimension \"" + "{value}" + "\"."
);
public static final TextKey COMMAND_IRIS_UPDATED_SERVER_PROPERTIES_LEVEL_NAME = TextKey.of(
"iris.bukkit.commandiris.updated_server_properties_level_name",
C.GREEN + "Updated server.properties level-name to \"" + "{name}" + "\"."
);
public static final TextKey COMMAND_IRIS_WORLD_WAS_STAGED_BUT_FAILED_UPDATE_SERVER_PROPERTIES_MAIN_WORLD = TextKey.of(
"iris.bukkit.commandiris.world_was_staged_but_failed_update_server_properties_main_world",
C.RED + "World was staged, but failed to update server.properties main world."
);
public static final TextKey COMMAND_IRIS_STAGED_IRIS_WORLD_WITH_GENERATOR_IRIS_SEED = TextKey.of(
"iris.bukkit.commandiris.staged_iris_world_with_generator_iris_seed",
C.GREEN + "Staged Iris world \"" + "{name}" + "\" with generator Iris:" + "{value}" + " and seed " + "{seed}" + "."
);
public static final TextKey COMMAND_IRIS_THIS_WORLD_IS_NOW_CONFIGURED_AS_MAIN_NEXT_RESTART = TextKey.of(
"iris.bukkit.commandiris.this_world_is_now_configured_as_main_next_restart",
C.GREEN + "This world is now configured as main for next restart."
);
public static final TextKey COMMAND_IRIS_FAILED_UPDATE_BUKKIT_YML = TextKey.of(
"iris.bukkit.commandiris.failed_update_bukkit_yml",
C.RED + "Failed to update bukkit.yml: " + "{value}"
@@ -878,17 +862,13 @@ public final class BukkitCommandMessagesExtended {
COMMAND_IRIS_MAY_WE_SUGGEST_NAME_IRISWORLD_INSTEAD_2,
COMMAND_IRIS_THAT_FOLDER_ALREADY_EXISTS,
COMMAND_IRIS_TRY_ONE_OVERWORLD_VANILLA_FLAT_THEEND,
COMMAND_IRIS_WORLD_STAGING_COMPLETED_RESTART_SERVER_GENERATE_LOAD,
COMMAND_IRIS_WORLD_STAGING_COMPLETED_RESTARTING_SERVER_GENERATE_LOAD,
COMMAND_IRIS_EXCEPTION_RAISED_DURING_CREATION_SEE_CONSOLE_MORE_DETAILS,
COMMAND_IRIS_SUCCESSFULLY_CREATED_YOUR_WORLD,
COMMAND_IRIS_YOUR_WORLD_WILL_AUTOMATICALLY_BE_SET_AS_MAIN_WORLD_WHEN,
COMMAND_IRIS_RUNTIME_WORLD_CREATION_IS_DISABLED_ON_FOLIA,
COMMAND_IRIS_PREPARING_WORLD_FILES_BUKKIT_YML_NEXT_STARTUP,
COMMAND_IRIS_FAILED_STAGE_WORLD_FILES_DIMENSION,
COMMAND_IRIS_UPDATED_SERVER_PROPERTIES_LEVEL_NAME,
COMMAND_IRIS_WORLD_WAS_STAGED_BUT_FAILED_UPDATE_SERVER_PROPERTIES_MAIN_WORLD,
COMMAND_IRIS_STAGED_IRIS_WORLD_WITH_GENERATOR_IRIS_SEED,
COMMAND_IRIS_THIS_WORLD_IS_NOW_CONFIGURED_AS_MAIN_NEXT_RESTART,
COMMAND_IRIS_FAILED_UPDATE_BUKKIT_YML,
COMMAND_IRIS_SPECIFIED_PLAYER_DOES_NOT_EXIST,
COMMAND_IRIS_IRIS_V_BY_VOLMIT_SOFTWARE,
@@ -282,10 +282,6 @@ public final class DirectorCommandMessages {
"iris.director.commandiris.param.seed_generate_world_with",
"The seed to generate the world with"
);
public static final TextKey COMMAND_IRIS_PARAM_WHETHER_NOT_AUTOMATICALLY_USE_THIS_WORLD_AS_MAIN_WORLD = TextKey.of(
"iris.director.commandiris.param.whether_not_automatically_use_this_world_as_main_world",
"Whether or not to automatically use this world as the main world"
);
public static final TextKey COMMAND_IRIS_PARAM_REPLACE_EXACT_EXISTING_WORLD_SLOT_NEXT_RESTART = TextKey.of(
"iris.director.commandiris.param.replace_exact_existing_world_slot_next_restart",
"Replace the exact existing world slot on the next restart"
@@ -941,7 +937,6 @@ public final class DirectorCommandMessages {
COMMAND_IRIS_PARAM_NAME_WORLD_CREATE,
COMMAND_IRIS_PARAM_DIMENSION_PACK_CREATE_WORLD_WITH,
COMMAND_IRIS_PARAM_SEED_GENERATE_WORLD_WITH,
COMMAND_IRIS_PARAM_WHETHER_NOT_AUTOMATICALLY_USE_THIS_WORLD_AS_MAIN_WORLD,
COMMAND_IRIS_PARAM_REPLACE_EXACT_EXISTING_WORLD_SLOT_NEXT_RESTART,
COMMAND_IRIS_DIRECTOR_TELEPORT_ANOTHER_WORLD,
COMMAND_IRIS_PARAM_WORLD_TELEPORT,
@@ -54,6 +54,8 @@ import org.bukkit.generator.ChunkGenerator;
import org.bukkit.inventory.ItemStack;
import java.awt.Color;
import java.io.IOException;
import java.nio.file.Path;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
@@ -269,6 +271,14 @@ public interface INMSBinding {
default void uninjectBukkit() {
}
default void writeCurrentPaperWorldData(
Path sourceWorldDirectory,
Path targetWorldDirectory,
long seed
) throws IOException {
throw new UnsupportedOperationException("The active NMS binding does not support current Paper world data staging.");
}
KMap<Material, List<BlockProperty>> getBlockProperties();
private void validateDimensionTypes(WorldCreator c) {
@@ -2,6 +2,7 @@ package art.arcane.iris.core.pack;
import java.io.IOException;
import java.nio.file.AtomicMoveNotSupportedException;
import java.nio.file.FileAlreadyExistsException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
@@ -44,6 +45,22 @@ public final class AtomicDirectoryPublisher {
}
}
public static Publication publishAbsent(Path stagedDirectory, Path targetDirectory) throws IOException {
Path staged = Objects.requireNonNull(stagedDirectory, "stagedDirectory").toAbsolutePath().normalize();
Path target = Objects.requireNonNull(targetDirectory, "targetDirectory").toAbsolutePath().normalize();
if (!Files.isDirectory(staged) || Files.isSymbolicLink(staged)) {
throw new IOException("Staged directory is missing or unsafe: " + staged);
}
if (!Objects.equals(staged.getParent(), target.getParent())) {
throw new IOException("Staged and target directories must have the same parent.");
}
if (Files.exists(target) || Files.isSymbolicLink(target)) {
throw new FileAlreadyExistsException("Publication target already exists: " + target);
}
move(staged, target);
return new Publication(target, null);
}
private static void move(Path source, Path target) throws IOException {
try {
Files.move(source, target, StandardCopyOption.ATOMIC_MOVE);
@@ -2,6 +2,8 @@ package art.arcane.iris.core.pack;
import art.arcane.iris.core.IrisDatapackCompiler;
import art.arcane.iris.core.lifecycle.BukkitStartupPaths;
import art.arcane.iris.core.lifecycle.BukkitWorldConfiguration;
import art.arcane.iris.core.lifecycle.BukkitWorldConfiguration.IrisGeneratorBinding;
import art.arcane.iris.core.nms.datapack.DataVersion;
import art.arcane.iris.core.nms.datapack.IDataFixer;
import art.arcane.volmlib.util.collection.KList;
@@ -45,7 +47,7 @@ import java.util.zip.ZipInputStream;
public final class DefaultPackBootstrapProvisioner {
private static final List<PackSpec> DEFAULT_PACKS = List.of();
private static final String WORLD_DATAPACK_DIRECTORY = "iris";
private static final int MARKER_SCHEMA = 5;
private static final int MARKER_SCHEMA = 6;
private static final int MAX_ARCHIVE_ENTRIES = 100_000;
private static final long MAX_ARCHIVE_BYTES = 512L * 1024L * 1024L;
private static final long MAX_EXPANDED_BYTES = 2L * 1024L * 1024L * 1024L;
@@ -71,13 +73,20 @@ public final class DefaultPackBootstrapProvisioner {
) throws IOException {
Objects.requireNonNull(dataDirectory, "dataDirectory");
Objects.requireNonNull(feedback, "feedback");
Path levelRoot = Objects.requireNonNull(startupPaths, "startupPaths").levelRoot();
BukkitStartupPaths requiredStartupPaths = Objects.requireNonNull(startupPaths, "startupPaths");
Path levelRoot = requiredStartupPaths.levelRoot();
List<IrisGeneratorBinding> bindings = BukkitWorldConfiguration.readIrisGeneratorBindings(
requiredStartupPaths.bukkitConfiguration().toFile(),
levelId(levelRoot),
requiredStartupPaths.levelRoot()
);
HttpClient client = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(15))
.followRedirects(HttpClient.Redirect.ALWAYS)
.build();
ProvisionOptions options = new ProvisionOptions(
DEFAULT_PACKS,
bindings,
client,
Clock.systemUTC(),
Duration.ofMinutes(30),
@@ -95,20 +104,24 @@ public final class DefaultPackBootstrapProvisioner {
return false;
}
try {
return isProvisioned(
dataDirectory,
BukkitStartupPaths.resolveCurrent().levelRoot()
BukkitStartupPaths startupPaths = BukkitStartupPaths.resolveCurrent();
List<IrisGeneratorBinding> bindings = BukkitWorldConfiguration.readIrisGeneratorBindings(
startupPaths.bukkitConfiguration().toFile(),
levelId(startupPaths.levelRoot()),
startupPaths.levelRoot()
);
return isProvisioned(dataDirectory, startupPaths.levelRoot(), DEFAULT_PACKS, bindings);
} catch (IOException | RuntimeException exception) {
return false;
}
}
static boolean isProvisioned(Path dataDirectory, Path levelRoot) {
return isProvisioned(dataDirectory, levelRoot, DEFAULT_PACKS);
}
static boolean isProvisioned(Path dataDirectory, Path levelRoot, List<PackSpec> requiredPacks) {
static boolean isProvisioned(
Path dataDirectory,
Path levelRoot,
List<PackSpec> requiredPacks,
List<IrisGeneratorBinding> bindings
) {
try {
Path normalizedData = dataDirectory.toAbsolutePath().normalize();
Path normalizedLevel = levelRoot.toAbsolutePath().normalize();
@@ -137,12 +150,19 @@ public final class DefaultPackBootstrapProvisioner {
return false;
}
}
List<File> packRoots = IrisDatapackCompiler.collectPackRoots(
normalizedData,
normalizedLevel
);
return directoryFingerprint(datapackRoot).equals(marker.getProperty("datapackFingerprint"))
&& datapackRoot.toString().equals(marker.getProperty("datapackPath"))
&& packRootsFingerprint(IrisDatapackCompiler.collectPackRoots(
normalizedData,
normalizedLevel
)).equals(marker.getProperty("aggregateFingerprint"));
&& packRootsFingerprint(packRoots).equals(marker.getProperty("aggregateFingerprint"))
&& IrisDatapackCompiler.computeInputFingerprint(
packRoots,
bindings,
fixer,
false
).equals(marker.getProperty("compilerInputFingerprint"));
} catch (IOException | RuntimeException exception) {
return false;
}
@@ -234,10 +254,17 @@ public final class DefaultPackBootstrapProvisioner {
}
String compilerIdentity = IrisDatapackCompiler.compilerIdentity(fixer);
String aggregateFingerprint = packRootsFingerprint(packRoots);
String compilerInputFingerprint = IrisDatapackCompiler.computeInputFingerprint(
packRoots,
options.bindings(),
fixer,
false
);
boolean anyPackReplaced = !packPublications.isEmpty();
boolean rebuildDatapack = anyPackReplaced
|| !existingDatapack
|| !aggregateFingerprint.equals(previousMarker.getProperty("aggregateFingerprint"))
|| !compilerInputFingerprint.equals(previousMarker.getProperty("compilerInputFingerprint"))
|| !compilerIdentity.equals(previousMarker.getProperty("compilerIdentity"))
|| !datapackRoot.toString().equals(previousMarker.getProperty("datapackPath"))
|| !directoryFingerprint(datapackRoot).equals(previousMarker.getProperty("datapackFingerprint"));
@@ -245,7 +272,7 @@ public final class DefaultPackBootstrapProvisioner {
compileContainer = datapacksRoot.resolve("." + WORLD_DATAPACK_DIRECTORY + "-stage-" + UUID.randomUUID());
Files.createDirectories(compileContainer);
KList<File> outputFolders = new KList<File>().qadd(compileContainer.toFile());
IrisDatapackCompiler.compile(packRoots, outputFolders, fixer, false);
IrisDatapackCompiler.compile(packRoots, outputFolders, options.bindings(), fixer, false);
if (!isDatapackRoot(compileContainer, !packRoots.isEmpty())) {
throw new IOException("Canonical Iris datapack compiler produced incomplete output at " + compileContainer);
}
@@ -266,6 +293,7 @@ public final class DefaultPackBootstrapProvisioner {
String finalAggregateFingerprint = packRootsFingerprint(finalPackRoots);
String finalCompilerInputFingerprint = IrisDatapackCompiler.computeInputFingerprint(
finalPackRoots,
options.bindings(),
fixer,
false);
String finalDatapackFingerprint = directoryFingerprint(datapackRoot);
@@ -280,6 +308,7 @@ public final class DefaultPackBootstrapProvisioner {
}
marker.setProperty("aggregateFingerprint", finalAggregateFingerprint);
marker.setProperty("compilerIdentity", compilerIdentity);
marker.setProperty("compilerInputFingerprint", finalCompilerInputFingerprint);
marker.setProperty("datapackFingerprint", finalDatapackFingerprint);
marker.setProperty("datapackPath", datapackRoot.toString());
marker.setProperty("completedAt", Long.toString(options.clock().millis()));
@@ -765,6 +794,14 @@ public final class DefaultPackBootstrapProvisioner {
}
}
private static String levelId(Path levelRoot) throws IOException {
Path fileName = Objects.requireNonNull(levelRoot, "levelRoot").getFileName();
if (fileName == null || fileName.toString().isBlank()) {
throw new IOException("Configured level root has no Paper startup level id: " + levelRoot);
}
return fileName.toString();
}
private static void deleteQuietly(Path path, Consumer<String> feedback) {
try {
delete(path);
@@ -880,6 +917,7 @@ public final class DefaultPackBootstrapProvisioner {
record ProvisionOptions(
List<PackSpec> packs,
List<IrisGeneratorBinding> bindings,
HttpClient client,
Clock clock,
Duration refreshInterval,
@@ -891,6 +929,7 @@ public final class DefaultPackBootstrapProvisioner {
) {
ProvisionOptions {
packs = List.copyOf(Objects.requireNonNull(packs, "packs"));
bindings = List.copyOf(Objects.requireNonNull(bindings, "bindings"));
Objects.requireNonNull(client, "client");
Objects.requireNonNull(clock, "clock");
Objects.requireNonNull(refreshInterval, "refreshInterval");
@@ -134,8 +134,11 @@ public class IrisCreator {
private BiConsumer<String, Long> studioTimingConsumer;
private DatapackPreparation datapackPreparation = DatapackPreparation.INSTALL_IF_CHANGED;
public static boolean removeFromBukkitYml(String name) throws IOException {
return BukkitWorldConfiguration.remove(BUKKIT_YML, name);
public static boolean removeFromBukkitYml(NamespacedKey worldKey) throws IOException {
return BukkitWorldConfiguration.remove(
BUKKIT_YML,
IrisWorldStorage.configuredWorldName(worldKey, IrisWorldStorage.levelRoot().getName())
);
}
public static int removeTransientStudioWorldsFromBukkitYml() throws IOException {
@@ -293,7 +296,12 @@ public class IrisCreator {
reportStudioProgress(0.86D, "create_world");
if (!studio && !benchmark) {
BukkitWorldConfiguration.register(BUKKIT_YML, name, dimension, seed);
BukkitWorldConfiguration.register(
BUKKIT_YML,
IrisWorldStorage.configuredWorldName(worldKey, IrisWorldStorage.levelRoot().getName()),
dimension,
seed
);
bukkitRegistered = true;
World createdWorld = world;
CompletableFuture<Void> multiverseRegistration = J.sfut(
@@ -730,7 +738,10 @@ public class IrisCreator {
failure.addSuppressed(rollbackFailure);
}
try {
BukkitWorldConfiguration.remove(BUKKIT_YML, name);
BukkitWorldConfiguration.remove(
BUKKIT_YML,
IrisWorldStorage.configuredWorldName(worldKey, IrisWorldStorage.levelRoot().getName())
);
} catch (Throwable rollbackFailure) {
failure.addSuppressed(rollbackFailure);
}
@@ -670,7 +670,7 @@ public class IrisToolbelt {
}
public static boolean removeWorld(World world) throws IOException {
return IrisCreator.removeFromBukkitYml(IrisWorldStorage.logicalName(world));
return IrisCreator.removeFromBukkitYml(WorldIdentity.key(world));
}
record PackReference(String pack, String dimension, boolean explicitDimension) {
+1 -6
View File
@@ -149,17 +149,13 @@
"iris.bukkit.commandiris.may_we_suggest_name_irisworld_instead_2": "§cWie wäre es stattdessen mit dem Namen \"IrisWorld\"?",
"iris.bukkit.commandiris.that_folder_already_exists": "§cDieser Ordner existiert bereits!",
"iris.bukkit.commandiris.try_one_overworld_vanilla_flat_theend": "§eVersuche eine der folgenden Optionen: overworld, vanilla, flat, theend",
"iris.bukkit.commandiris.world_staging_completed_restart_server_generate_load": "§aVorbereitung der Welt abgeschlossen. Starte den Server neu, um \"{worldName}\" zu generieren/zu laden.",
"iris.bukkit.commandiris.world_staging_completed_restarting_server_generate_load": "§aVorbereitung der Welt abgeschlossen. Iris startet den Server jetzt neu, um \"{worldName}\" zu generieren/zu laden.",
"iris.bukkit.commandiris.exception_raised_during_creation_see_console_more_details": "§cBei der Erstellung ist eine Ausnahme aufgetreten. Weitere Details findest du in der Konsole.",
"iris.bukkit.commandiris.successfully_created_your_world": "§aDeine Welt wurde erfolgreich erstellt!",
"iris.bukkit.commandiris.your_world_will_automatically_be_set_as_main_world_when": "§aIhre Welt wird automatisch als Hauptwelt festgelegt, wenn der Server neu gestartet wird.",
"iris.bukkit.commandiris.runtime_world_creation_is_disabled_on_folia": "§eDie Erstellung von Welten zur Laufzeit ist unter Folia deaktiviert.",
"iris.bukkit.commandiris.preparing_world_files_bukkit_yml_next_startup": "§eWeltdateien und bukkit.yml werden für den nächsten Start vorbereitet...",
"iris.bukkit.commandiris.failed_stage_world_files_dimension": "§cDie Weltdateien für Dimension \"{value}\" konnten nicht vorbereitet werden.",
"iris.bukkit.commandiris.updated_server_properties_level_name": "§aDie Eigenschaft level-name in server.properties wurde auf \"{name}\" gesetzt.",
"iris.bukkit.commandiris.world_was_staged_but_failed_update_server_properties_main_world": "§cDie Welt wurde vorbereitet, aber die Hauptwelt in server.properties konnte nicht aktualisiert werden.",
"iris.bukkit.commandiris.staged_iris_world_with_generator_iris_seed": "§aIris-Welt \"{name}\" mit Generator Iris:{value} und Seed {seed} vorbereitet.",
"iris.bukkit.commandiris.this_world_is_now_configured_as_main_next_restart": "§aDiese Welt ist für den nächsten Neustart als Hauptwelt konfiguriert.",
"iris.bukkit.commandiris.failed_update_bukkit_yml": "§cbukkit.yml konnte nicht aktualisiert werden: {value}",
"iris.bukkit.commandiris.specified_player_does_not_exist": "§cDer angegebene Spieler existiert nicht.",
"iris.bukkit.commandiris.iris_v_by_volmit_software": "§aIris v{value} von Volmit Software",
@@ -693,7 +689,6 @@
"iris.director.commandiris.param.name_world_create": "Name der zu erstellenden Welt",
"iris.director.commandiris.param.dimension_pack_create_world_with": "Dimension oder Pack, mit der bzw. dem die Welt erstellt wird",
"iris.director.commandiris.param.seed_generate_world_with": "Seed für die Generierung der Welt",
"iris.director.commandiris.param.whether_not_automatically_use_this_world_as_main_world": "Ob man diese Welt automatisch als Hauptwelt benutzt oder nicht",
"iris.director.commandiris.param.replace_exact_existing_world_slot_next_restart": "Den exakten vorhandenen Welt-Slot beim nächsten Neustart ersetzen",
"iris.director.commandiris.director.teleport_another_world": "Teleportieren in eine andere Welt",
"iris.director.commandiris.param.world_teleport": "Zielwelt der Teleportation",
+1 -6
View File
@@ -149,17 +149,13 @@
"iris.bukkit.commandiris.may_we_suggest_name_irisworld_instead_2": "§c¿Podemos sugerir el nombre \"IrisWorld\"?",
"iris.bukkit.commandiris.that_folder_already_exists": "§c¡Esa carpeta ya existe!",
"iris.bukkit.commandiris.try_one_overworld_vanilla_flat_theend": "§ePrueba una de estas opciones: overworld, vanilla, flat, theend",
"iris.bukkit.commandiris.world_staging_completed_restart_server_generate_load": "§aPreparación del mundo completada. Reinicia el servidor para generar/cargar \"{worldName}\".",
"iris.bukkit.commandiris.world_staging_completed_restarting_server_generate_load": "§aPreparación del mundo completada. Iris está reiniciando el servidor para generar/cargar \"{worldName}\".",
"iris.bukkit.commandiris.exception_raised_during_creation_see_console_more_details": "§cSe produjo una excepción durante la creación. Consulta la consola para obtener más detalles.",
"iris.bukkit.commandiris.successfully_created_your_world": "§a¡Tu mundo se creó correctamente!",
"iris.bukkit.commandiris.your_world_will_automatically_be_set_as_main_world_when": "§aTu mundo se establecerá automáticamente como mundo principal cuando se reinicie el servidor.",
"iris.bukkit.commandiris.runtime_world_creation_is_disabled_on_folia": "§eLa creación de mundos en tiempo de ejecución está desactivada en Folia.",
"iris.bukkit.commandiris.preparing_world_files_bukkit_yml_next_startup": "§ePreparando los archivos del mundo y bukkit.yml para el próximo inicio...",
"iris.bukkit.commandiris.failed_stage_world_files_dimension": "§cNo se pudieron preparar los archivos del mundo para la dimensión \"{value}\".",
"iris.bukkit.commandiris.updated_server_properties_level_name": "§aSe actualizó level-name en server.properties a \"{name}\".",
"iris.bukkit.commandiris.world_was_staged_but_failed_update_server_properties_main_world": "§cEl mundo se preparó, pero no se pudo actualizar el mundo principal en server.properties.",
"iris.bukkit.commandiris.staged_iris_world_with_generator_iris_seed": "§aSe preparó el mundo de Iris \"{name}\" con el generador Iris:{value} y la semilla {seed}.",
"iris.bukkit.commandiris.this_world_is_now_configured_as_main_next_restart": "§aEste mundo está configurado como principal para el próximo reinicio.",
"iris.bukkit.commandiris.failed_update_bukkit_yml": "§cNo se pudo actualizar bukkit.yml: {value}",
"iris.bukkit.commandiris.specified_player_does_not_exist": "§cEl jugador especificado no existe.",
"iris.bukkit.commandiris.iris_v_by_volmit_software": "§aIris v{value} por Volmit Software",
@@ -693,7 +689,6 @@
"iris.director.commandiris.param.name_world_create": "El nombre del mundo que se creará",
"iris.director.commandiris.param.dimension_pack_create_world_with": "La dimensión o el pack con el que se creará el mundo",
"iris.director.commandiris.param.seed_generate_world_with": "La semilla con la que se generará el mundo",
"iris.director.commandiris.param.whether_not_automatically_use_this_world_as_main_world": "Indica si este mundo debe usarse automáticamente como mundo principal",
"iris.director.commandiris.param.replace_exact_existing_world_slot_next_restart": "Reemplazar el espacio exacto del mundo existente en el próximo reinicio",
"iris.director.commandiris.director.teleport_another_world": "Teletransportarse a otro mundo",
"iris.director.commandiris.param.world_teleport": "El mundo al que se teletransportará",
+1 -6
View File
@@ -149,17 +149,13 @@
"iris.bukkit.commandiris.may_we_suggest_name_irisworld_instead_2": "§cVoimmeko ehdottaa nimeä \"IrisWorldSen sijaan?",
"iris.bukkit.commandiris.that_folder_already_exists": "§cSe kansio on jo olemassa!",
"iris.bukkit.commandiris.try_one_overworld_vanilla_flat_theend": "§eKokeile yksi: maapallo, vanilla, litteä, theend",
"iris.bukkit.commandiris.world_staging_completed_restart_server_generate_load": "§aMaailman valmistelu valmis. Käynnistä palvelin uudelleen, jotta \"{worldName}\" luodaan/ladataan.",
"iris.bukkit.commandiris.world_staging_completed_restarting_server_generate_load": "§aMaailman valmistelu valmis. Iris käynnistää palvelimen nyt uudelleen, jotta \"{worldName}\" luodaan/ladataan.",
"iris.bukkit.commandiris.exception_raised_during_creation_see_console_more_details": "§cLuomisen aikana nostettu poikkeus. Katso lisätietoja konsolista.",
"iris.bukkit.commandiris.successfully_created_your_world": "§aOnnistuneesti loin maailmasi!",
"iris.bukkit.commandiris.your_world_will_automatically_be_set_as_main_world_when": "§aMaailmasi asetetaan automaattisesti päämaailmaksi, kun palvelin käynnistyy uudelleen.",
"iris.bukkit.commandiris.runtime_world_creation_is_disabled_on_folia": "§eRuntime maailman luominen on pois käytöstä Folia.",
"iris.bukkit.commandiris.preparing_world_files_bukkit_yml_next_startup": "§eValmistellaan maailman tiedostoja ja bukkit.yml seuraavan käynnistyksen...",
"iris.bukkit.commandiris.failed_stage_world_files_dimension": "§cMaailmatiedostoja ei voitu lavastaa ulottuvuudeksi \"{value}\".",
"iris.bukkit.commandiris.updated_server_properties_level_name": "§aPäivitetty server.properties tason nimi \"{name}\".",
"iris.bukkit.commandiris.world_was_staged_but_failed_update_server_properties_main_world": "§cMaailma lavastettiin, mutta sitä ei päivitetty server.properties suurmaailma.",
"iris.bukkit.commandiris.staged_iris_world_with_generator_iris_seed": "§aIris-maailma \"{name}\" valmisteltiin generaattorilla Iris:{value} ja siemenellä {seed}.",
"iris.bukkit.commandiris.this_world_is_now_configured_as_main_next_restart": "§aTämä maailma on nyt asetettu pääohjelmaksi seuraavaan uudelleenkäynnistykseen.",
"iris.bukkit.commandiris.failed_update_bukkit_yml": "§cPäivitys epäonnistui bukkit.yml: {value}",
"iris.bukkit.commandiris.specified_player_does_not_exist": "§cMääriteltyä pelaajaa ei ole olemassa.",
"iris.bukkit.commandiris.iris_v_by_volmit_software": "§aIris{value} mennessä Volmit Software",
@@ -693,7 +689,6 @@
"iris.director.commandiris.param.name_world_create": "Luotavan maailman nimi",
"iris.director.commandiris.param.dimension_pack_create_world_with": "Ulottuvuus / paketti luoda maailma",
"iris.director.commandiris.param.seed_generate_world_with": "Siemenet tuottaa maailman kanssa",
"iris.director.commandiris.param.whether_not_automatically_use_this_world_as_main_world": "Käytetäänkö tätä maailmaa automaattisesti päämaailmana",
"iris.director.commandiris.param.replace_exact_existing_world_slot_next_restart": "Korvaa täsmällinen olemassa oleva maailmapaikka seuraavalla uudelleenkäynnistyksellä",
"iris.director.commandiris.director.teleport_another_world": "Teleporttautuminen toiseen maailmaan",
"iris.director.commandiris.param.world_teleport": "Maailman teleportata",
+1 -6
View File
@@ -149,17 +149,13 @@
"iris.bukkit.commandiris.may_we_suggest_name_irisworld_instead_2": "§cNous vous suggérons plutôt le nom \"IrisWorld\".",
"iris.bukkit.commandiris.that_folder_already_exists": "§cCe dossier existe déjà !",
"iris.bukkit.commandiris.try_one_overworld_vanilla_flat_theend": "§eEssayez l'une des options suivantes : overworld, vanilla, flat, theend",
"iris.bukkit.commandiris.world_staging_completed_restart_server_generate_load": "§aPréparation du monde terminée. Redémarrez le serveur pour générer/charger \"{worldName}\".",
"iris.bukkit.commandiris.world_staging_completed_restarting_server_generate_load": "§aPréparation du monde terminée. Iris redémarre maintenant le serveur pour générer/charger \"{worldName}\".",
"iris.bukkit.commandiris.exception_raised_during_creation_see_console_more_details": "§cUne exception s'est produite pendant la création. Consultez la console pour plus de détails.",
"iris.bukkit.commandiris.successfully_created_your_world": "§aVotre monde a été créé avec succès !",
"iris.bukkit.commandiris.your_world_will_automatically_be_set_as_main_world_when": "§aVotre monde deviendra automatiquement le monde principal au redémarrage du serveur.",
"iris.bukkit.commandiris.runtime_world_creation_is_disabled_on_folia": "§eLa création de mondes pendant l'exécution est désactivée sur Folia.",
"iris.bukkit.commandiris.preparing_world_files_bukkit_yml_next_startup": "§ePréparation des fichiers du monde et de bukkit.yml pour le prochain démarrage...",
"iris.bukkit.commandiris.failed_stage_world_files_dimension": "§cImpossible de préparer les fichiers du monde pour la dimension \"{value}\".",
"iris.bukkit.commandiris.updated_server_properties_level_name": "§aLa propriété level-name de server.properties a été définie sur \"{name}\".",
"iris.bukkit.commandiris.world_was_staged_but_failed_update_server_properties_main_world": "§cLe monde a été préparé, mais le monde principal n'a pas pu être mis à jour dans server.properties.",
"iris.bukkit.commandiris.staged_iris_world_with_generator_iris_seed": "§aLe monde Iris \"{name}\" a été préparé avec le générateur Iris:{value} et la graine {seed}.",
"iris.bukkit.commandiris.this_world_is_now_configured_as_main_next_restart": "§aCe monde est maintenant configuré comme monde principal pour le prochain redémarrage.",
"iris.bukkit.commandiris.failed_update_bukkit_yml": "§cImpossible de mettre à jour bukkit.yml : {value}",
"iris.bukkit.commandiris.specified_player_does_not_exist": "§cLe joueur indiqué n'existe pas.",
"iris.bukkit.commandiris.iris_v_by_volmit_software": "§aIris v{value} par Volmit Software",
@@ -693,7 +689,6 @@
"iris.director.commandiris.param.name_world_create": "Le nom du monde à créer",
"iris.director.commandiris.param.dimension_pack_create_world_with": "La dimension ou le pack avec lequel créer le monde",
"iris.director.commandiris.param.seed_generate_world_with": "La graine avec laquelle générer le monde",
"iris.director.commandiris.param.whether_not_automatically_use_this_world_as_main_world": "Indique si ce monde doit être utilisé automatiquement comme monde principal",
"iris.director.commandiris.param.replace_exact_existing_world_slot_next_restart": "Remplacer l’emplacement exact du monde existant au prochain redémarrage",
"iris.director.commandiris.director.teleport_another_world": "Se téléporter vers un autre monde",
"iris.director.commandiris.param.world_teleport": "Le monde vers lequel se téléporter",
+1 -6
View File
@@ -149,17 +149,13 @@
"iris.bukkit.commandiris.may_we_suggest_name_irisworld_instead_2": "§cאפשר להציע את השם \"IrisWorld\"במקום?",
"iris.bukkit.commandiris.that_folder_already_exists": "§cהתיקיה הזו כבר קיימת!",
"iris.bukkit.commandiris.try_one_overworld_vanilla_flat_theend": "§eנסה: Overworld vanilla, שטוח, TheendXTry",
"iris.bukkit.commandiris.world_staging_completed_restart_server_generate_load": "§aהכנת העולם הושלמה. הפעל מחדש את השרת כדי ליצור/לטעון את \"{worldName}\".",
"iris.bukkit.commandiris.world_staging_completed_restarting_server_generate_load": "§aהכנת העולם הושלמה. Iris מפעיל כעת מחדש את השרת כדי ליצור/לטעון את \"{worldName}\".",
"iris.bukkit.commandiris.exception_raised_during_creation_see_console_more_details": "§cלמעטים שגדלו במהלך הבריאה. ראו את הקונסולה לפרטים נוספים.",
"iris.bukkit.commandiris.successfully_created_your_world": "§aיצרתם בהצלחה את עולמכם!",
"iris.bukkit.commandiris.your_world_will_automatically_be_set_as_main_world_when": "§aעולמכם יקבע באופן אוטומטי כעולם הראשי כאשר השרת נפתח מחדש.",
"iris.bukkit.commandiris.runtime_world_creation_is_disabled_on_folia": "§eיצירת העולם של Runtime זמינה Folia.",
"iris.bukkit.commandiris.preparing_world_files_bukkit_yml_next_startup": "§eהכנת קבצים בעולם bukkit.yml עבור הסטארט-אפ הבא...",
"iris.bukkit.commandiris.failed_stage_world_files_dimension": "§cנכשלים בקבצי עולם הבמה לממד \"{value}\".",
"iris.bukkit.commandiris.updated_server_properties_level_name": "§aעדכון server.properties שם ברמה \"{name}\".",
"iris.bukkit.commandiris.world_was_staged_but_failed_update_server_properties_main_world": "§cהעולם הוטבע, אך לא הצליח לעדכן server.properties העולם הראשי",
"iris.bukkit.commandiris.staged_iris_world_with_generator_iris_seed": "§aעולם Iris ‏\"{name}\" הוכן עם המחולל Iris:{value} והזרע {seed}.",
"iris.bukkit.commandiris.this_world_is_now_configured_as_main_next_restart": "§aהעולם הזה מוגדר כעת כעיקרי לחידוש הבא.",
"iris.bukkit.commandiris.failed_update_bukkit_yml": "§cנכשל לעדכן bukkit.yml: {value}",
"iris.bukkit.commandiris.specified_player_does_not_exist": "§cהשחקן שצוין אינו קיים.",
"iris.bukkit.commandiris.iris_v_by_volmit_software": "§aאיריס v{value} על ידי Volmit Software",
@@ -693,7 +689,6 @@
"iris.director.commandiris.param.name_world_create": "שם העולם ליצור",
"iris.director.commandiris.param.dimension_pack_create_world_with": "המימד / החבילה ליצירת העולם עם",
"iris.director.commandiris.param.seed_generate_world_with": "הזרע ליצור את העולם עם",
"iris.director.commandiris.param.whether_not_automatically_use_this_world_as_main_world": "בין אם להשתמש בעולם באופן אוטומטי כעולם הראשי",
"iris.director.commandiris.param.replace_exact_existing_world_slot_next_restart": "החלפת משבצת העולם הקיימת המדויקת בהפעלה מחדש הבאה",
"iris.director.commandiris.director.teleport_another_world": "טלפורט לעולם אחר",
"iris.director.commandiris.param.world_teleport": "העולם לטלפורט",
+1 -6
View File
@@ -149,17 +149,13 @@
"iris.bukkit.commandiris.may_we_suggest_name_irisworld_instead_2": "§cPossiamo suggerire il nome \"IrisWorld\" invece?",
"iris.bukkit.commandiris.that_folder_already_exists": "§cQuella cartella esiste già!",
"iris.bukkit.commandiris.try_one_overworld_vanilla_flat_theend": "§eProva una delle seguenti opzioni: overworld, vanilla, flat, theend",
"iris.bukkit.commandiris.world_staging_completed_restart_server_generate_load": "§aPreparazione del mondo completata. Riavvia il server per generare/caricare \"{worldName}\".",
"iris.bukkit.commandiris.world_staging_completed_restarting_server_generate_load": "§aPreparazione del mondo completata. Iris sta riavviando il server per generare/caricare \"{worldName}\".",
"iris.bukkit.commandiris.exception_raised_during_creation_see_console_more_details": "§cSi è verificata un'eccezione durante la creazione. Consulta la console per maggiori dettagli.",
"iris.bukkit.commandiris.successfully_created_your_world": "§aCreato con successo il tuo mondo!",
"iris.bukkit.commandiris.your_world_will_automatically_be_set_as_main_world_when": "§aIl tuo mondo verrà impostato automaticamente come mondo principale al riavvio del server.",
"iris.bukkit.commandiris.runtime_world_creation_is_disabled_on_folia": "§eLa creazione di mondi durante l'esecuzione è disattivata su Folia.",
"iris.bukkit.commandiris.preparing_world_files_bukkit_yml_next_startup": "§ePreparazione dei file del mondo e di bukkit.yml per il prossimo avvio...",
"iris.bukkit.commandiris.failed_stage_world_files_dimension": "§cImpossibile preparare i file del mondo per la dimensione \"{value}\".",
"iris.bukkit.commandiris.updated_server_properties_level_name": "§aLa proprietà level-name in server.properties è stata impostata su \"{name}\".",
"iris.bukkit.commandiris.world_was_staged_but_failed_update_server_properties_main_world": "§cIl mondo è stato preparato, ma non è stato possibile aggiornare il mondo principale in server.properties.",
"iris.bukkit.commandiris.staged_iris_world_with_generator_iris_seed": "§aMondo Iris \"{name}\" preparato con il generatore Iris:{value} e il seed {seed}.",
"iris.bukkit.commandiris.this_world_is_now_configured_as_main_next_restart": "§aQuesto mondo è ora configurato come principale per il prossimo riavvio.",
"iris.bukkit.commandiris.failed_update_bukkit_yml": "§cImpossibile aggiornare bukkit.yml: {value}",
"iris.bukkit.commandiris.specified_player_does_not_exist": "§cIl giocatore specificato non esiste.",
"iris.bukkit.commandiris.iris_v_by_volmit_software": "§aIris v{value} di Volmit Software",
@@ -693,7 +689,6 @@
"iris.director.commandiris.param.name_world_create": "Il nome del mondo da creare",
"iris.director.commandiris.param.dimension_pack_create_world_with": "La dimensione o il Pack con cui creare il mondo",
"iris.director.commandiris.param.seed_generate_world_with": "Il seed con cui generare il mondo",
"iris.director.commandiris.param.whether_not_automatically_use_this_world_as_main_world": "Che sia o meno utilizzare automaticamente questo mondo come il mondo principale",
"iris.director.commandiris.param.replace_exact_existing_world_slot_next_restart": "Sostituisci lo slot esatto del mondo esistente al prossimo riavvio",
"iris.director.commandiris.director.teleport_another_world": "Teletrasporto in un altro mondo",
"iris.director.commandiris.param.world_teleport": "Il mondo verso cui teletrasportarsi",
+1 -6
View File
@@ -149,17 +149,13 @@
"iris.bukkit.commandiris.may_we_suggest_name_irisworld_instead_2": "§c代わりに \"IrisWorld\" という名前はいかがですか?",
"iris.bukkit.commandiris.that_folder_already_exists": "§cそのフォルダーはすでに存在します!",
"iris.bukkit.commandiris.try_one_overworld_vanilla_flat_theend": "§e次のいずれかを指定してください: overworld、vanilla、flat、theend",
"iris.bukkit.commandiris.world_staging_completed_restart_server_generate_load": "§aワールドの準備が完了しました。\"{worldName}\" を生成/読み込むにはサーバーを再起動してください。",
"iris.bukkit.commandiris.world_staging_completed_restarting_server_generate_load": "§aワールドの準備が完了しました。Iris は \"{worldName}\" を生成/読み込むためにサーバーを再起動しています。",
"iris.bukkit.commandiris.exception_raised_during_creation_see_console_more_details": "§c作成中に例外が発生しました。詳細はコンソールを確認してください。",
"iris.bukkit.commandiris.successfully_created_your_world": "§aワールドを作成しました!",
"iris.bukkit.commandiris.your_world_will_automatically_be_set_as_main_world_when": "§aサーバーを再起動すると、このワールドが自動的にメインワールドに設定されます。",
"iris.bukkit.commandiris.runtime_world_creation_is_disabled_on_folia": "§eFolia では実行中のワールド作成が無効です。",
"iris.bukkit.commandiris.preparing_world_files_bukkit_yml_next_startup": "§e次回起動用のワールドファイルと bukkit.yml を準備しています...",
"iris.bukkit.commandiris.failed_stage_world_files_dimension": "§cディメンション \"{value}\" のワールドファイルを準備できませんでした。",
"iris.bukkit.commandiris.updated_server_properties_level_name": "§aserver.properties の level-name を \"{name}\" に更新しました。",
"iris.bukkit.commandiris.world_was_staged_but_failed_update_server_properties_main_world": "§cワールドは準備されましたが、server.properties のメインワールド設定を更新できませんでした。",
"iris.bukkit.commandiris.staged_iris_world_with_generator_iris_seed": "§aIris ワールド \"{name}\" をジェネレーター Iris:{value}、シード {seed} で準備しました。",
"iris.bukkit.commandiris.this_world_is_now_configured_as_main_next_restart": "§aこのワールドは、次回再起動時のメインワールドとして設定されました。",
"iris.bukkit.commandiris.failed_update_bukkit_yml": "§cbukkit.yml を更新できませんでした: {value}",
"iris.bukkit.commandiris.specified_player_does_not_exist": "§c指定されたプレイヤーは存在しません。",
"iris.bukkit.commandiris.iris_v_by_volmit_software": "§aIris v{value} by Volmit Software",
@@ -693,7 +689,6 @@
"iris.director.commandiris.param.name_world_create": "作成するワールドの名前",
"iris.director.commandiris.param.dimension_pack_create_world_with": "ワールドの作成に使用するディメンションまたはパック",
"iris.director.commandiris.param.seed_generate_world_with": "ワールドの生成に使用するシード",
"iris.director.commandiris.param.whether_not_automatically_use_this_world_as_main_world": "このワールドをメインワールドとして自動設定するかどうか",
"iris.director.commandiris.param.replace_exact_existing_world_slot_next_restart": "次回の再起動時に既存の正確なワールドスロットを置き換える",
"iris.director.commandiris.director.teleport_another_world": "別のワールドへテレポートします",
"iris.director.commandiris.param.world_teleport": "テレポート先のワールド",
+1 -6
View File
@@ -149,17 +149,13 @@
"iris.bukkit.commandiris.may_we_suggest_name_irisworld_instead_2": "§c우리는 이름 \"을 제안 할 수있다IrisWorld\" 대신?",
"iris.bukkit.commandiris.that_folder_already_exists": "§c그 폴더는 이미 존재합니다!",
"iris.bukkit.commandiris.try_one_overworld_vanilla_flat_theend": "§e중 하나를 시도: overworld, 바닐라, 플랫, theend",
"iris.bukkit.commandiris.world_staging_completed_restart_server_generate_load": "§a월드 준비가 완료되었습니다. \"{worldName}\"을 생성/로드하려면 서버를 재시작하세요.",
"iris.bukkit.commandiris.world_staging_completed_restarting_server_generate_load": "§a월드 준비가 완료되었습니다. Iris가 \"{worldName}\"을 생성/로드하기 위해 서버를 재시작하고 있습니다.",
"iris.bukkit.commandiris.exception_raised_during_creation_see_console_more_details": "§c창조 중 발생한 예외. 자세한 내용은 콘솔을 참조하십시오.",
"iris.bukkit.commandiris.successfully_created_your_world": "§a세상을 성공적으로 만들었습니다!",
"iris.bukkit.commandiris.your_world_will_automatically_be_set_as_main_world_when": "§a서버가 재시작할 때 세계로 자동 설정됩니다.",
"iris.bukkit.commandiris.runtime_world_creation_is_disabled_on_folia": "§eRuntime 세계 생성은 비활성화됩니다 Folia.",
"iris.bukkit.commandiris.preparing_world_files_bukkit_yml_next_startup": "§e세계 파일 및 준비 bukkit.yml 다음 시작...",
"iris.bukkit.commandiris.failed_stage_world_files_dimension": "§c차원을 위한 단계 세계 파일에 실패 \"{value}\".",
"iris.bukkit.commandiris.updated_server_properties_level_name": "§a으로 server.properties \"에 수평 이름{name}\".",
"iris.bukkit.commandiris.world_was_staged_but_failed_update_server_properties_main_world": "§c세계는 무대로, 하지만 업데이트 실패 server.properties 주요 세계.",
"iris.bukkit.commandiris.staged_iris_world_with_generator_iris_seed": "§aIris 월드 \"{name}\"을 생성기 Iris:{value}, 시드 {seed}로 준비했습니다.",
"iris.bukkit.commandiris.this_world_is_now_configured_as_main_next_restart": "§a다음 재시작의 메인 월드로 설정했습니다.",
"iris.bukkit.commandiris.failed_update_bukkit_yml": "§c업데이트 실패 bukkit.yml: {value}",
"iris.bukkit.commandiris.specified_player_does_not_exist": "§c지정된 플레이어는 존재하지 않습니다.",
"iris.bukkit.commandiris.iris_v_by_volmit_software": "§a아이리스 v{value} Volmit 소프트웨어",
@@ -693,7 +689,6 @@
"iris.director.commandiris.param.name_world_create": "만들 월드 이름",
"iris.director.commandiris.param.dimension_pack_create_world_with": "월드 생성에 사용할 차원 또는 팩",
"iris.director.commandiris.param.seed_generate_world_with": "월드 생성에 사용할 시드",
"iris.director.commandiris.param.whether_not_automatically_use_this_world_as_main_world": "이 월드를 메인 월드로 자동 사용할지 여부",
"iris.director.commandiris.param.replace_exact_existing_world_slot_next_restart": "다음 재시작 시 기존의 정확한 월드 슬롯 교체",
"iris.director.commandiris.director.teleport_another_world": "다른 월드로 순간이동합니다",
"iris.director.commandiris.param.world_teleport": "순간이동할 월드",
+1 -6
View File
@@ -149,17 +149,13 @@
"iris.bukkit.commandiris.may_we_suggest_name_irisworld_instead_2": "§cAr galime pasiūlyti pavadinimą \"IrisWorld\"vietoj to?",
"iris.bukkit.commandiris.that_folder_already_exists": "§cTas aplankas jau egzistuoja!",
"iris.bukkit.commandiris.try_one_overworld_vanilla_flat_theend": "§ePabandykite vieną iš: per pasaulį, vanilla, butas, pabaiga",
"iris.bukkit.commandiris.world_staging_completed_restart_server_generate_load": "§aPasaulio paruošimas baigtas. Paleiskite serverį iš naujo, kad sugeneruotumėte / įkeltumėte „{worldName}“.",
"iris.bukkit.commandiris.world_staging_completed_restarting_server_generate_load": "§aPasaulio paruošimas baigtas. Iris dabar paleidžia serverį iš naujo, kad sugeneruotų / įkeltų „{worldName}“.",
"iris.bukkit.commandiris.exception_raised_during_creation_see_console_more_details": "§cSukūrimo metu iškelta išimtis. Daugiau informacijos rasite konsolėje.",
"iris.bukkit.commandiris.successfully_created_your_world": "§aSėkmingai sukūrė savo pasaulį!",
"iris.bukkit.commandiris.your_world_will_automatically_be_set_as_main_world_when": "§aJūsų pasaulis bus automatiškai nustatytas kaip pagrindinis pasaulis, kai serveris vėl paleidžiamas.",
"iris.bukkit.commandiris.runtime_world_creation_is_disabled_on_folia": "§eweather forecast Folia.",
"iris.bukkit.commandiris.preparing_world_files_bukkit_yml_next_startup": "§eRuošiami pasauliniai failai ir bukkit.yml kitam paleidimui...",
"iris.bukkit.commandiris.failed_stage_world_files_dimension": "§cNepavyko sukurti pasaulinių matmenų failų \"{value}\".",
"iris.bukkit.commandiris.updated_server_properties_level_name": "§aAtnaujinta server.properties Lyginamasis pavadinimas į \"{name}\".",
"iris.bukkit.commandiris.world_was_staged_but_failed_update_server_properties_main_world": "§cPasaulis buvo suskirstytas, bet nepavyko atnaujinti server.properties pagrindinis pasaulis.",
"iris.bukkit.commandiris.staged_iris_world_with_generator_iris_seed": "§aParuoštas Iris pasaulis „{name}“ su generatoriumi Iris:{value} ir pradine reikšme {seed}.",
"iris.bukkit.commandiris.this_world_is_now_configured_as_main_next_restart": "§aŠis pasaulis dabar yra sukonfigūruotas kaip pagrindinis kitą startą.",
"iris.bukkit.commandiris.failed_update_bukkit_yml": "§cNepavyko atnaujinti bukkit.yml: {value}",
"iris.bukkit.commandiris.specified_player_does_not_exist": "§cNurodytasis žaidėjas neegzistuoja.",
"iris.bukkit.commandiris.iris_v_by_volmit_software": "§aIris prieš{value} pagal Volmit programinė įranga",
@@ -693,7 +689,6 @@
"iris.director.commandiris.param.name_world_create": "Kuriamo pasaulio pavadinimas",
"iris.director.commandiris.param.dimension_pack_create_world_with": "dimensija / paketas sukurti pasaulį su",
"iris.director.commandiris.param.seed_generate_world_with": "Sėkla generuoti pasaulį su",
"iris.director.commandiris.param.whether_not_automatically_use_this_world_as_main_world": "Ar automatiškai naudoti šį pasaulį kaip pagrindinį pasaulį",
"iris.director.commandiris.param.replace_exact_existing_world_slot_next_restart": "Per kitą paleidimą iš naujo pakeisti tikslų esamą pasaulio lizdą",
"iris.director.commandiris.director.teleport_another_world": "Teleportas į kitą pasaulį",
"iris.director.commandiris.param.world_teleport": "Pasaulis teleportui į",
+1 -6
View File
@@ -149,17 +149,13 @@
"iris.bukkit.commandiris.may_we_suggest_name_irisworld_instead_2": "§cMogen we de naam voorstellen \"IrisWorldIn plaats daarvan?",
"iris.bukkit.commandiris.that_folder_already_exists": "§cDie map bestaat al!",
"iris.bukkit.commandiris.try_one_overworld_vanilla_flat_theend": "§eProbeer een van: overwereld, vanilla, plat, einde",
"iris.bukkit.commandiris.world_staging_completed_restart_server_generate_load": "§aVoorbereiding van de wereld voltooid. Herstart de server om \"{worldName}\" te genereren/laden.",
"iris.bukkit.commandiris.world_staging_completed_restarting_server_generate_load": "§aVoorbereiding van de wereld voltooid. Iris herstart de server nu om \"{worldName}\" te genereren/laden.",
"iris.bukkit.commandiris.exception_raised_during_creation_see_console_more_details": "§cUitzondering bij de schepping. Zie de console voor meer details.",
"iris.bukkit.commandiris.successfully_created_your_world": "§aSuccesvol jullie wereld gecreëerd!",
"iris.bukkit.commandiris.your_world_will_automatically_be_set_as_main_world_when": "§aUw wereld wordt automatisch ingesteld als de belangrijkste wereld wanneer de server herstart.",
"iris.bukkit.commandiris.runtime_world_creation_is_disabled_on_folia": "§eRuntime wereld creatie is uitgeschakeld op Folia.",
"iris.bukkit.commandiris.preparing_world_files_bukkit_yml_next_startup": "§eVoorbereiden van wereldbestanden en bukkit.yml voor volgende opstart...",
"iris.bukkit.commandiris.failed_stage_world_files_dimension": "§cKon wereldbestanden voor dimensie niet stagen \"{value}\".",
"iris.bukkit.commandiris.updated_server_properties_level_name": "§aBijgewerkt server.properties niveau-naam aan \"{name}\".",
"iris.bukkit.commandiris.world_was_staged_but_failed_update_server_properties_main_world": "§cWereld werd opgevoerd, maar kon niet bijwerken server.properties De belangrijkste wereld.",
"iris.bukkit.commandiris.staged_iris_world_with_generator_iris_seed": "§aIris-wereld \"{name}\" voorbereid met generator Iris:{value} en seed {seed}.",
"iris.bukkit.commandiris.this_world_is_now_configured_as_main_next_restart": "§aDeze wereld is nu geconfigureerd als hoofd voor volgende herstart.",
"iris.bukkit.commandiris.failed_update_bukkit_yml": "§cBijwerken is mislukt bukkit.yml: {value}",
"iris.bukkit.commandiris.specified_player_does_not_exist": "§cDe opgegeven speler bestaat niet.",
"iris.bukkit.commandiris.iris_v_by_volmit_software": "§aIris /{value} door Volmit Software",
@@ -693,7 +689,6 @@
"iris.director.commandiris.param.name_world_create": "De naam van de wereld te creëren",
"iris.director.commandiris.param.dimension_pack_create_world_with": "De dimensie/pack om de wereld te creëren met",
"iris.director.commandiris.param.seed_generate_world_with": "Het zaad om de wereld te genereren met",
"iris.director.commandiris.param.whether_not_automatically_use_this_world_as_main_world": "Of deze wereld automatisch gebruikt moet worden als de belangrijkste wereld",
"iris.director.commandiris.param.replace_exact_existing_world_slot_next_restart": "De exacte bestaande wereldsleuf bij de volgende herstart vervangen",
"iris.director.commandiris.director.teleport_another_world": "Teleporteren naar een andere wereld",
"iris.director.commandiris.param.world_teleport": "Wereld te teleporteren naar",
+1 -6
View File
@@ -149,17 +149,13 @@
"iris.bukkit.commandiris.may_we_suggest_name_irisworld_instead_2": "§cCzy możemy zasugerować nazwę \"IrisWorld\"zamiast?",
"iris.bukkit.commandiris.that_folder_already_exists": "§cTen folder już istnieje!",
"iris.bukkit.commandiris.try_one_overworld_vanilla_flat_theend": "§eSpróbuj jeden z: zaświaty, vanilla, płaski, koniec",
"iris.bukkit.commandiris.world_staging_completed_restart_server_generate_load": "§aPrzygotowanie świata ukończone. Uruchom serwer ponownie, aby wygenerować/wczytać „{worldName}”.",
"iris.bukkit.commandiris.world_staging_completed_restarting_server_generate_load": "§aPrzygotowanie świata ukończone. Iris uruchamia teraz serwer ponownie, aby wygenerować/wczytać „{worldName}“.",
"iris.bukkit.commandiris.exception_raised_during_creation_see_console_more_details": "§cWyjątek podniesiony podczas tworzenia. Więcej szczegółów znajdziesz w konsoli.",
"iris.bukkit.commandiris.successfully_created_your_world": "§aZ powodzeniem stworzyłeś swój świat!",
"iris.bukkit.commandiris.your_world_will_automatically_be_set_as_main_world_when": "§aTwój świat zostanie automatycznie ustawiony jako główny świat po ponownym uruchomieniu serwera.",
"iris.bukkit.commandiris.runtime_world_creation_is_disabled_on_folia": "§eRuntime world creation jest wyłączone na Folia.",
"iris.bukkit.commandiris.preparing_world_files_bukkit_yml_next_startup": "§ePrzygotowanie światowych plików i bukkit.yml dla następnego startup...",
"iris.bukkit.commandiris.failed_stage_world_files_dimension": "§cNie udało się sscenizować plików świata dla wymiaru \"{value}\".",
"iris.bukkit.commandiris.updated_server_properties_level_name": "§aAktualizacja server.properties level- name do \"{name}\".",
"iris.bukkit.commandiris.world_was_staged_but_failed_update_server_properties_main_world": "§cŚwiat został wystawiony, ale nie udało się zaktualizować server.properties Główny świat.",
"iris.bukkit.commandiris.staged_iris_world_with_generator_iris_seed": "§aPrzygotowano świat Iris „{name}” z generatorem Iris:{value} i ziarnem {seed}.",
"iris.bukkit.commandiris.this_world_is_now_configured_as_main_next_restart": "§aTen świat jest teraz skonfigurowany jako główny dla następnego restartu.",
"iris.bukkit.commandiris.failed_update_bukkit_yml": "§cNie udało się zaktualizować bukkit.yml: {value}",
"iris.bukkit.commandiris.specified_player_does_not_exist": "§cOkreślony gracz nie istnieje.",
"iris.bukkit.commandiris.iris_v_by_volmit_software": "§aIris v.{value} przez Volmit Software",
@@ -693,7 +689,6 @@
"iris.director.commandiris.param.name_world_create": "Nazwa świata do stworzenia",
"iris.director.commandiris.param.dimension_pack_create_world_with": "Wymiar / pakiet do tworzenia świata z",
"iris.director.commandiris.param.seed_generate_world_with": "Nasienie do generowania świata z",
"iris.director.commandiris.param.whether_not_automatically_use_this_world_as_main_world": "Czy automatycznie używać tego świata jako głównego świata",
"iris.director.commandiris.param.replace_exact_existing_world_slot_next_restart": "Zastąp dokładny istniejący slot świata przy następnym restarcie",
"iris.director.commandiris.director.teleport_another_world": "Teleport do innego świata",
"iris.director.commandiris.param.world_teleport": "Świat teleportować do",
+1 -6
View File
@@ -149,17 +149,13 @@
"iris.bukkit.commandiris.may_we_suggest_name_irisworld_instead_2": "§cPodemos sugerir o nome \"IrisWorld\" em vez disso?",
"iris.bukkit.commandiris.that_folder_already_exists": "§cEssa pasta já existe!",
"iris.bukkit.commandiris.try_one_overworld_vanilla_flat_theend": "§eExperimente um de: overworld, vanilla, flat, theend",
"iris.bukkit.commandiris.world_staging_completed_restart_server_generate_load": "§aPreparação do mundo concluída. Reinicie o servidor para gerar/carregar \"{worldName}\".",
"iris.bukkit.commandiris.world_staging_completed_restarting_server_generate_load": "§aPreparação do mundo concluída. O Iris está agora a reiniciar o servidor para gerar/carregar \"{worldName}\".",
"iris.bukkit.commandiris.exception_raised_during_creation_see_console_more_details": "§cExceção levantada durante a criação. Veja o console para mais detalhes.",
"iris.bukkit.commandiris.successfully_created_your_world": "§aCriou com sucesso o seu mundo!",
"iris.bukkit.commandiris.your_world_will_automatically_be_set_as_main_world_when": "§aSeu mundo será automaticamente definido como o mundo principal quando o servidor reiniciar.",
"iris.bukkit.commandiris.runtime_world_creation_is_disabled_on_folia": "§eA criação do mundo em execução está desactivada Folia.",
"iris.bukkit.commandiris.preparing_world_files_bukkit_yml_next_startup": "§ePreparando arquivos mundiais e bukkit.yml para a próxima inicialização...",
"iris.bukkit.commandiris.failed_stage_world_files_dimension": "§cFalha ao encenar arquivos mundiais para a dimensão \"{value}\".",
"iris.bukkit.commandiris.updated_server_properties_level_name": "§aAtualizado server.properties nível-nome para \"{name}\".",
"iris.bukkit.commandiris.world_was_staged_but_failed_update_server_properties_main_world": "§cO mundo foi encenado, mas falhou em atualizar server.properties mundo principal.",
"iris.bukkit.commandiris.staged_iris_world_with_generator_iris_seed": "§aMundo Iris \"{name}\" preparado com o gerador Iris:{value} e a seed {seed}.",
"iris.bukkit.commandiris.this_world_is_now_configured_as_main_next_restart": "§aEste mundo está agora configurado como principal para o próximo reinício.",
"iris.bukkit.commandiris.failed_update_bukkit_yml": "§cFalha ao atualizar bukkit.yml: {value}",
"iris.bukkit.commandiris.specified_player_does_not_exist": "§cO jogador especificado não existe.",
"iris.bukkit.commandiris.iris_v_by_volmit_software": "§aÍris v{value} por Volmit Software",
@@ -693,7 +689,6 @@
"iris.director.commandiris.param.name_world_create": "O nome do mundo para criar",
"iris.director.commandiris.param.dimension_pack_create_world_with": "A dimensão/pack para criar o mundo com",
"iris.director.commandiris.param.seed_generate_world_with": "A semente para gerar o mundo com",
"iris.director.commandiris.param.whether_not_automatically_use_this_world_as_main_world": "Se deve ou não usar automaticamente este mundo como o mundo principal",
"iris.director.commandiris.param.replace_exact_existing_world_slot_next_restart": "Substituir o espaço exato do mundo existente no próximo reinício",
"iris.director.commandiris.director.teleport_another_world": "Teletransporte para outro mundo",
"iris.director.commandiris.param.world_teleport": "Mundo para teletransportar",
+1 -6
View File
@@ -149,17 +149,13 @@
"iris.bukkit.commandiris.may_we_suggest_name_irisworld_instead_2": "§cМожно предложить название \"IrisWorldВместо этого?",
"iris.bukkit.commandiris.that_folder_already_exists": "§cЭта папка уже существует!",
"iris.bukkit.commandiris.try_one_overworld_vanilla_flat_theend": "§eПопробуйте один из вариантов: overworld, vanilla, flat, theend",
"iris.bukkit.commandiris.world_staging_completed_restart_server_generate_load": "§aПодготовка мира завершена. Перезапустите сервер, чтобы сгенерировать/загрузить «{worldName}».",
"iris.bukkit.commandiris.world_staging_completed_restarting_server_generate_load": "§aПодготовка мира завершена. Iris перезапускает сервер, чтобы сгенерировать/загрузить «{worldName}».",
"iris.bukkit.commandiris.exception_raised_during_creation_see_console_more_details": "§cИсключение, возникшее при сотворении. Смотрите консоль для более подробной информации.",
"iris.bukkit.commandiris.successfully_created_your_world": "§aУспешно создал свой мир!",
"iris.bukkit.commandiris.your_world_will_automatically_be_set_as_main_world_when": "§aВаш мир будет автоматически установлен в качестве основного мира при перезагрузке сервера.",
"iris.bukkit.commandiris.runtime_world_creation_is_disabled_on_folia": "Создание мира §eRuntime отключено на Folia.",
"iris.bukkit.commandiris.preparing_world_files_bukkit_yml_next_startup": "§eПодготовка мировых файлов и bukkit.yml Для следующего стартапа...",
"iris.bukkit.commandiris.failed_stage_world_files_dimension": "§cНе удалось создать мировые файлы для измерения\".{value}\".",
"iris.bukkit.commandiris.updated_server_properties_level_name": "§aОбновлено server.properties имя уровня \"{name}\".",
"iris.bukkit.commandiris.world_was_staged_but_failed_update_server_properties_main_world": "§cМир был инсценирован, но не удалось обновить server.properties Главный мир.",
"iris.bukkit.commandiris.staged_iris_world_with_generator_iris_seed": "§aМир Iris «{name}» подготовлен с генератором Iris:{value} и сидом {seed}.",
"iris.bukkit.commandiris.this_world_is_now_configured_as_main_next_restart": "§aЭтот мир теперь настроен как основной для следующего перезапуска.",
"iris.bukkit.commandiris.failed_update_bukkit_yml": "§cНе удалось обновить bukkit.yml: {value}",
"iris.bukkit.commandiris.specified_player_does_not_exist": "§cУказанного игрока не существует.",
"iris.bukkit.commandiris.iris_v_by_volmit_software": "§aИрис v{value} Разработчик Volmit Software",
@@ -693,7 +689,6 @@
"iris.director.commandiris.param.name_world_create": "Название мира для создания",
"iris.director.commandiris.param.dimension_pack_create_world_with": "Размер/пакет для создания мира",
"iris.director.commandiris.param.seed_generate_world_with": "Семя, чтобы создать мир с",
"iris.director.commandiris.param.whether_not_automatically_use_this_world_as_main_world": "Использовать или не использовать этот мир как основной",
"iris.director.commandiris.param.replace_exact_existing_world_slot_next_restart": "Заменить точный существующий слот мира при следующем перезапуске",
"iris.director.commandiris.director.teleport_another_world": "Телепорт в другой мир",
"iris.director.commandiris.param.world_teleport": "Телепортироваться в мир",
+1 -6
View File
@@ -149,17 +149,13 @@
"iris.bukkit.commandiris.may_we_suggest_name_irisworld_instead_2": "§cAdımı önerebiliriz \"IrisWorldBunun yerine?",
"iris.bukkit.commandiris.that_folder_already_exists": "§cBu klasör zaten var!",
"iris.bukkit.commandiris.try_one_overworld_vanilla_flat_theend": "§eBirini deneyin: Overworld, vanilla, düz, son",
"iris.bukkit.commandiris.world_staging_completed_restart_server_generate_load": "§aDünya hazırlığı tamamlandı. \"{worldName}\" dünyasını oluşturmak/yüklemek için sunucuyu yeniden başlatın.",
"iris.bukkit.commandiris.world_staging_completed_restarting_server_generate_load": "§aDünya hazırlığı tamamlandı. Iris, \"{worldName}\" dünyasını oluşturmak/yüklemek için sunucuyu yeniden başlatıyor.",
"iris.bukkit.commandiris.exception_raised_during_creation_see_console_more_details": "§cYaratılış sırasında ortaya çıktı. Konsolu daha fazla ayrıntı için görün.",
"iris.bukkit.commandiris.successfully_created_your_world": "§aBaşarılı bir şekilde dünyanızı yarattı!",
"iris.bukkit.commandiris.your_world_will_automatically_be_set_as_main_world_when": "§aDünyanız otomatik olarak sunucu yeniden başladığında ana dünya olarak belirlenecektir.",
"iris.bukkit.commandiris.runtime_world_creation_is_disabled_on_folia": "§eRuntime dünya yaratımı engellidir Folia.",
"iris.bukkit.commandiris.preparing_world_files_bukkit_yml_next_startup": "§eDünya dosyalarını hazırlamak ve bukkit.yml Bir sonraki başlangıç için...",
"iris.bukkit.commandiris.failed_stage_world_files_dimension": "§cBoyut için dünya dosyalarını sahnelemek için başarısız oldu \"{value}\".",
"iris.bukkit.commandiris.updated_server_properties_level_name": "§aGüncelleme Tarihi server.properties Seviye adı \"{name}\".",
"iris.bukkit.commandiris.world_was_staged_but_failed_update_server_properties_main_world": "§cDünya sahnelendi, ancak güncellemeyi başaramadı server.properties Ana dünya.",
"iris.bukkit.commandiris.staged_iris_world_with_generator_iris_seed": "§aIris dünyası \"{name}\", Iris:{value} jeneratörü ve {seed} tohumu ile hazırlandı.",
"iris.bukkit.commandiris.this_world_is_now_configured_as_main_next_restart": "§aBu dünya şimdi bir sonraki yeniden başlamak için ana olarak yapılandırılmıştır.",
"iris.bukkit.commandiris.failed_update_bukkit_yml": "§cgüncellemek için başarısız oldu bukkit.yml: {value}",
"iris.bukkit.commandiris.specified_player_does_not_exist": "§cBelirtilen oyuncu mevcut değildir.",
"iris.bukkit.commandiris.iris_v_by_volmit_software": "§aIris{value} Volmit Software",
@@ -693,7 +689,6 @@
"iris.director.commandiris.param.name_world_create": "Dünyanın adı yaratmak",
"iris.director.commandiris.param.dimension_pack_create_world_with": "Dünyayı yaratmak için boyut / paket",
"iris.director.commandiris.param.seed_generate_world_with": "Dünyayı üretmek için tohum",
"iris.director.commandiris.param.whether_not_automatically_use_this_world_as_main_world": "Bu dünyayı ana dünya olarak otomatik olarak kullanıp kullanmayalım",
"iris.director.commandiris.param.replace_exact_existing_world_slot_next_restart": "Bir sonraki yeniden başlatmada mevcut tam dünya yuvasını değiştir",
"iris.director.commandiris.director.teleport_another_world": "Teleport başka bir dünyaya",
"iris.director.commandiris.param.world_teleport": "Dünya teleport'a",
+1 -6
View File
@@ -149,17 +149,13 @@
"iris.bukkit.commandiris.may_we_suggest_name_irisworld_instead_2": "§cChúng ta có thể đề nghị cái tên \"IrisWorldThay vào đó?",
"iris.bukkit.commandiris.that_folder_already_exists": "§cThư mục đó đã có!",
"iris.bukkit.commandiris.try_one_overworld_vanilla_flat_theend": "§eHãy thử một trong những: ngoài thế giới, vanilla, bằng phẳng, kết thúc",
"iris.bukkit.commandiris.world_staging_completed_restart_server_generate_load": "§aĐã chuẩn bị xong thế giới. Hãy khởi động lại máy chủ để tạo/tải \"{worldName}\".",
"iris.bukkit.commandiris.world_staging_completed_restarting_server_generate_load": "§aĐã chuẩn bị xong thế giới. Iris đang khởi động lại máy chủ để tạo/tải \"{worldName}\".",
"iris.bukkit.commandiris.exception_raised_during_creation_see_console_more_details": "§cNgoại lệ được nuôi lớn trong suốt quá trình sáng tạo. Xem bảng điều khiển để biết thêm chi tiết.",
"iris.bukkit.commandiris.successfully_created_your_world": "§aThành công trong việc tạo ra thế giới của anh!",
"iris.bukkit.commandiris.your_world_will_automatically_be_set_as_main_world_when": "§aThế giới của bạn sẽ tự động được thiết lập như thế giới chính khi máy chủ khởi động lại.",
"iris.bukkit.commandiris.runtime_world_creation_is_disabled_on_folia": "§eComment Folia.",
"iris.bukkit.commandiris.preparing_world_files_bukkit_yml_next_startup": "§eĐang chuẩn bị tập tin thế giới và bukkit.yml cho lần khởi chạy tiếp theo...",
"iris.bukkit.commandiris.failed_stage_world_files_dimension": "§cKhông thể tạo tập tin cho chiều không gian{value}\".",
"iris.bukkit.commandiris.updated_server_properties_level_name": "§aCập nhật server.properties Cấp-tên là \"{name}\".",
"iris.bukkit.commandiris.world_was_staged_but_failed_update_server_properties_main_world": "§cThế giới đã được thiết lập, nhưng không cập nhật được server.properties thế giới chính.",
"iris.bukkit.commandiris.staged_iris_world_with_generator_iris_seed": "§aĐã chuẩn bị thế giới Iris \"{name}\" với trình tạo Iris:{value} và seed {seed}.",
"iris.bukkit.commandiris.this_world_is_now_configured_as_main_next_restart": "§aThế giới này bây giờ được cấu hình là chính cho lần khởi động lại tiếp theo.",
"iris.bukkit.commandiris.failed_update_bukkit_yml": "§cLỗi cập nhật bukkit.yml: {value}",
"iris.bukkit.commandiris.specified_player_does_not_exist": "§cNgười chơi đã ghi rõ không tồn tại.",
"iris.bukkit.commandiris.iris_v_by_volmit_software": "§aIris v.{value} by Volmit Phần mềm",
@@ -693,7 +689,6 @@
"iris.director.commandiris.param.name_world_create": "Tên của thế giới để tạo ra",
"iris.director.commandiris.param.dimension_pack_create_world_with": "Kích thước/ lốc tạo ra thế giới với",
"iris.director.commandiris.param.seed_generate_world_with": "Hạt giống để tạo ra thế giới với",
"iris.director.commandiris.param.whether_not_automatically_use_this_world_as_main_world": "Có nên tự động sử dụng thế giới này làm thế giới chính hay không",
"iris.director.commandiris.param.replace_exact_existing_world_slot_next_restart": "Thay thế đúng vị trí thế giới hiện có vào lần khởi động lại tiếp theo",
"iris.director.commandiris.director.teleport_another_world": "Name",
"iris.director.commandiris.param.world_teleport": "Thế giới có thể dịch chuyển",
+1 -6
View File
@@ -149,17 +149,13 @@
"iris.bukkit.commandiris.may_we_suggest_name_irisworld_instead_2": "§c建议改用名称 \"IrisWorld\"。",
"iris.bukkit.commandiris.that_folder_already_exists": "§c该文件夹已存在!",
"iris.bukkit.commandiris.try_one_overworld_vanilla_flat_theend": "§e可尝试:overworld、vanilla、flat 或 the_end",
"iris.bukkit.commandiris.world_staging_completed_restart_server_generate_load": "§a世界暂存完成。请重启服务器以生成并加载 \"{worldName}\"。",
"iris.bukkit.commandiris.world_staging_completed_restarting_server_generate_load": "§a世界暂存完成。Iris 正在重启服务器以生成并加载 \"{worldName}\"。",
"iris.bukkit.commandiris.exception_raised_during_creation_see_console_more_details": "§c创建期间发生异常。详情请查看控制台。",
"iris.bukkit.commandiris.successfully_created_your_world": "§a世界创建成功!",
"iris.bukkit.commandiris.your_world_will_automatically_be_set_as_main_world_when": "§a服务器重启时,此世界将自动设为主世界。",
"iris.bukkit.commandiris.runtime_world_creation_is_disabled_on_folia": "§eFolia 上已禁用运行时世界创建。",
"iris.bukkit.commandiris.preparing_world_files_bukkit_yml_next_startup": "§e正在为下次启动准备世界文件和 bukkit.yml...",
"iris.bukkit.commandiris.failed_stage_world_files_dimension": "§c无法为维度 \"{value}\" 暂存世界文件。",
"iris.bukkit.commandiris.updated_server_properties_level_name": "§a已将 server.properties 中的 level-name 更新为 \"{name}\"。",
"iris.bukkit.commandiris.world_was_staged_but_failed_update_server_properties_main_world": "§c世界已暂存,但无法更新 server.properties 中的主世界。",
"iris.bukkit.commandiris.staged_iris_world_with_generator_iris_seed": "§a已暂存 Iris 世界 \"{name}\",生成器为 Iris:{value},种子为 {seed}。",
"iris.bukkit.commandiris.this_world_is_now_configured_as_main_next_restart": "§a此世界现已设为下次重启后的主世界。",
"iris.bukkit.commandiris.failed_update_bukkit_yml": "§c无法更新 bukkit.yml:{value}",
"iris.bukkit.commandiris.specified_player_does_not_exist": "§c指定的玩家不存在。",
"iris.bukkit.commandiris.iris_v_by_volmit_software": "§aIris v.{value},由 Volmit Software 开发",
@@ -693,7 +689,6 @@
"iris.director.commandiris.param.name_world_create": "要创建的世界名称",
"iris.director.commandiris.param.dimension_pack_create_world_with": "用于创建世界的维度包",
"iris.director.commandiris.param.seed_generate_world_with": "用于生成世界的种子",
"iris.director.commandiris.param.whether_not_automatically_use_this_world_as_main_world": "是否自动将此世界设为主世界",
"iris.director.commandiris.param.replace_exact_existing_world_slot_next_restart": "在下次重启时替换指定的现有世界槽位",
"iris.director.commandiris.director.teleport_another_world": "传送到另一个世界",
"iris.director.commandiris.param.world_teleport": "要传送到的世界",
+1 -6
View File
@@ -149,17 +149,13 @@
"iris.bukkit.commandiris.may_we_suggest_name_irisworld_instead_2": "§c建議改用名稱 \"IrisWorld\"。",
"iris.bukkit.commandiris.that_folder_already_exists": "§c該資料夾已存在!",
"iris.bukkit.commandiris.try_one_overworld_vanilla_flat_theend": "§e可嘗試:overworld、vanilla、flat 或 the_end",
"iris.bukkit.commandiris.world_staging_completed_restart_server_generate_load": "§a世界暫存完成。請重新啟動伺服器以生成並載入 \"{worldName}\"。",
"iris.bukkit.commandiris.world_staging_completed_restarting_server_generate_load": "§a世界暫存完成。Iris 正在重新啟動伺服器以生成並載入 \"{worldName}\"。",
"iris.bukkit.commandiris.exception_raised_during_creation_see_console_more_details": "§c建立期間發生例外。詳細資訊請查看主控台。",
"iris.bukkit.commandiris.successfully_created_your_world": "§a世界建立成功!",
"iris.bukkit.commandiris.your_world_will_automatically_be_set_as_main_world_when": "§a伺服器重新啟動時,此世界將自動設為主世界。",
"iris.bukkit.commandiris.runtime_world_creation_is_disabled_on_folia": "§eFolia 上已停用執行階段世界建立。",
"iris.bukkit.commandiris.preparing_world_files_bukkit_yml_next_startup": "§e正在為下次啟動準備世界檔案和 bukkit.yml...",
"iris.bukkit.commandiris.failed_stage_world_files_dimension": "§c無法為維度 \"{value}\" 暫存世界檔案。",
"iris.bukkit.commandiris.updated_server_properties_level_name": "§a已將 server.properties 中的 level-name 更新為 \"{name}\"。",
"iris.bukkit.commandiris.world_was_staged_but_failed_update_server_properties_main_world": "§c世界已暫存,但無法更新 server.properties 中的主世界。",
"iris.bukkit.commandiris.staged_iris_world_with_generator_iris_seed": "§a已暫存 Iris 世界 \"{name}\",生成器為 Iris:{value},種子為 {seed}。",
"iris.bukkit.commandiris.this_world_is_now_configured_as_main_next_restart": "§a此世界現已設為下次重新啟動後的主世界。",
"iris.bukkit.commandiris.failed_update_bukkit_yml": "§c無法更新 bukkit.yml:{value}",
"iris.bukkit.commandiris.specified_player_does_not_exist": "§c指定的玩家不存在。",
"iris.bukkit.commandiris.iris_v_by_volmit_software": "§aIris v.{value},由 Volmit Software 開發",
@@ -693,7 +689,6 @@
"iris.director.commandiris.param.name_world_create": "要建立的世界名稱",
"iris.director.commandiris.param.dimension_pack_create_world_with": "用於建立世界的維度包",
"iris.director.commandiris.param.seed_generate_world_with": "用於生成世界的種子",
"iris.director.commandiris.param.whether_not_automatically_use_this_world_as_main_world": "是否自動將此世界設為主世界",
"iris.director.commandiris.param.replace_exact_existing_world_slot_next_restart": "在下次重新啟動時取代指定的現有世界槽位",
"iris.director.commandiris.director.teleport_another_world": "傳送到另一個世界",
"iris.director.commandiris.param.world_teleport": "要傳送到的世界",