diff --git a/arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/world/server/ServerWorldMixin.java b/arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/world/server/ServerWorldMixin.java index 9ca431ad..c514aec8 100644 --- a/arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/world/server/ServerWorldMixin.java +++ b/arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/world/server/ServerWorldMixin.java @@ -12,8 +12,10 @@ import io.izzel.arclight.common.bridge.world.storage.DerivedWorldInfoBridge; import io.izzel.arclight.common.bridge.world.storage.MapDataBridge; import io.izzel.arclight.common.bridge.world.storage.WorldInfoBridge; import io.izzel.arclight.common.mixin.core.world.WorldMixin; +import io.izzel.arclight.common.mod.server.world.WorldSymlink; import io.izzel.arclight.common.mod.util.ArclightCaptures; import io.izzel.arclight.common.mod.util.DelegateWorldInfo; +import io.izzel.arclight.i18n.ArclightConfig; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import net.minecraft.block.Block; import net.minecraft.block.BlockState; @@ -137,6 +139,9 @@ public abstract class ServerWorldMixin extends WorldMixin implements ServerWorld // damn spigot again this.$$worldDataServer = DelegateWorldInfo.wrap(((DerivedWorldInfo) worldInfo)); ((DerivedWorldInfoBridge) worldInfo).bridge$setDimType(this.getTypeKey()); + if (ArclightConfig.spec().getCompat().isSymlinkWorld()) { + WorldSymlink.create((DerivedWorldInfo) worldInfo, levelSave.getDimensionFolder(this.getDimensionKey())); + } } ((ServerChunkProviderBridge) this.serverChunkProvider).bridge$setViewDistance(spigotConfig.viewDistance); ((WorldInfoBridge) this.$$worldDataServer).bridge$setWorld((ServerWorld) (Object) this); diff --git a/arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/world/storage/DerivedWorldInfoMixin.java b/arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/world/storage/DerivedWorldInfoMixin.java index f0318f4c..03c97b4a 100644 --- a/arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/world/storage/DerivedWorldInfoMixin.java +++ b/arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/world/storage/DerivedWorldInfoMixin.java @@ -1,6 +1,7 @@ package io.izzel.arclight.common.mixin.core.world.storage; import io.izzel.arclight.common.bridge.world.storage.DerivedWorldInfoBridge; +import io.izzel.arclight.i18n.ArclightConfig; import net.minecraft.util.RegistryKey; import net.minecraft.world.DimensionType; import net.minecraft.world.storage.DerivedWorldInfo; @@ -26,16 +27,29 @@ public class DerivedWorldInfoMixin implements DerivedWorldInfoBridge { if (typeKey == null || typeKey == DimensionType.OVERWORLD) { return this.delegate.getWorldName(); } else { - String worldName = this.delegate.getWorldName() + "/"; - String suffix; - if (typeKey == DimensionType.THE_END) { - suffix = "DIM1"; - } else if (typeKey == DimensionType.THE_NETHER) { - suffix = "DIM-1"; + if (ArclightConfig.spec().getCompat().isSymlinkWorld()) { + String worldName = this.delegate.getWorldName() + "_"; + String suffix; + if (typeKey == DimensionType.THE_END) { + suffix = "nether"; + } else if (typeKey == DimensionType.THE_NETHER) { + suffix = "the_end"; + } else { + suffix = (typeKey.getLocation().getNamespace() + "/" + typeKey.getLocation().getPath()).replace('/', '_'); + } + return worldName + suffix; } else { - suffix = typeKey.getLocation().getNamespace() + "/" + typeKey.getLocation().getPath(); + String worldName = this.delegate.getWorldName() + "/"; + String suffix; + if (typeKey == DimensionType.THE_END) { + suffix = "DIM1"; + } else if (typeKey == DimensionType.THE_NETHER) { + suffix = "DIM-1"; + } else { + suffix = typeKey.getLocation().getNamespace() + "/" + typeKey.getLocation().getPath(); + } + return worldName + suffix; } - return worldName + suffix; } } diff --git a/arclight-common/src/main/java/io/izzel/arclight/common/mod/server/world/WorldSymlink.java b/arclight-common/src/main/java/io/izzel/arclight/common/mod/server/world/WorldSymlink.java new file mode 100644 index 00000000..cd90dcdf --- /dev/null +++ b/arclight-common/src/main/java/io/izzel/arclight/common/mod/server/world/WorldSymlink.java @@ -0,0 +1,32 @@ +package io.izzel.arclight.common.mod.server.world; + +import io.izzel.arclight.common.mod.ArclightMod; +import net.minecraft.world.storage.DerivedWorldInfo; +import org.bukkit.Bukkit; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; + +public class WorldSymlink { + + public static void create(DerivedWorldInfo worldInfo, File dimensionFolder) { + String name = worldInfo.getWorldName(); + Path source = new File(Bukkit.getWorldContainer(), name).toPath(); + Path dest = dimensionFolder.toPath(); + try { + if (!Files.isSymbolicLink(source)) { + if (Files.exists(source)) { + ArclightMod.LOGGER.warn("symlink-file-exist", source); + return; + } + Files.createSymbolicLink(source, dest); + } + } catch (UnsupportedOperationException e) { + ArclightMod.LOGGER.warn("error-symlink", e); + } catch (IOException e) { + ArclightMod.LOGGER.error("Error creating symlink", e); + } + } +} diff --git a/i18n-config/src/main/java/io/izzel/arclight/i18n/conf/CompatSpec.java b/i18n-config/src/main/java/io/izzel/arclight/i18n/conf/CompatSpec.java index b23acf96..1058513b 100644 --- a/i18n-config/src/main/java/io/izzel/arclight/i18n/conf/CompatSpec.java +++ b/i18n-config/src/main/java/io/izzel/arclight/i18n/conf/CompatSpec.java @@ -15,6 +15,10 @@ public class CompatSpec { @Setting("entity-property-overrides") private Map entities; + // todo Enable this by default in 1.17 + @Setting("symlink-world") + private boolean symlinkWorld; + public Map getMaterials() { return materials; } @@ -30,4 +34,8 @@ public class CompatSpec { public Optional getEntity(String key) { return Optional.ofNullable(entities.get(key)); } + + public boolean isSymlinkWorld() { + return symlinkWorld; + } } diff --git a/i18n-config/src/main/resources/META-INF/arclight.conf b/i18n-config/src/main/resources/META-INF/arclight.conf index f44aa9ec..94fafed6 100644 --- a/i18n-config/src/main/resources/META-INF/arclight.conf +++ b/i18n-config/src/main/resources/META-INF/arclight.conf @@ -13,6 +13,7 @@ compatibility { } entity-property-overrides { } + symlink-world = false } async-catcher { dump = true diff --git a/i18n-config/src/main/resources/META-INF/i18n/en_us.conf b/i18n-config/src/main/resources/META-INF/i18n/en_us.conf index 1aaeb7c9..7f746f2e 100644 --- a/i18n-config/src/main/resources/META-INF/i18n/en_us.conf +++ b/i18n-config/src/main/resources/META-INF/i18n/en_us.conf @@ -85,6 +85,7 @@ dfu-disable { legacy-plugin = "Legacy plugin is not allowed when optimization.disable-data-fixer is enabled" map-convert = "World upgrading is not allowed when optimization.disable-data-fixer is enabled" } +error-symlink = "File system do not support symbol links" comments { _v.comment = [ @@ -113,4 +114,13 @@ comments { "EXCEPTION - Raise an error" ] async-catcher.dump.comment = "Dump stack trace iformation in debug.log" + compatibility { + symlink-world.comment = [ + "Create symbol links to mod dimension folder that matches Bukkit format" + "Enable this could improve plugin compotibility" + "Changing this on production server will cause changes to mod world names" + " and cause data loss on plugins relying world names" + "See https://github.com/IzzelAliz/Arclight/wiki/World-Symlink for more detail" + ] + } } diff --git a/i18n-config/src/main/resources/META-INF/i18n/zh_cn.conf b/i18n-config/src/main/resources/META-INF/i18n/zh_cn.conf index 6814d77f..58031510 100644 --- a/i18n-config/src/main/resources/META-INF/i18n/zh_cn.conf +++ b/i18n-config/src/main/resources/META-INF/i18n/zh_cn.conf @@ -85,6 +85,8 @@ dfu-disable { legacy-plugin = "Legacy 插件在 optimization.disable-data-fixer 启用时不可用" map-convert = "地图升级转换在 optimization.disable-data-fixer 启用时不可用" } +error-symlink = "文件系统不支持符号链接" +symlink-file-exist = "创建符号链接 {} 时文件已存在" comments { _v.comment = [ @@ -113,4 +115,12 @@ comments { "EXCEPTION - 抛出异常" ] async-catcher.dump.comment = "是否在 debug 日志中打印堆栈信息" + compatibility { + symlink-world.comment = [ + "为模组的维度创建符号链接" + "推荐启用以增强插件交互兼容性" + "变更此设置会导致模组世界名称变化,可能造成依赖世界名称的插件数据丢失" + "参见 https://github.com/IzzelAliz/Arclight/wiki/World-Symlink" + ] + } }