--- a/net/minecraft/world/level/storage/LevelStorageSource.java
+++ b/net/minecraft/world/level/storage/LevelStorageSource.java
@@ -3,6 +3,7 @@
 import com.google.common.collect.Maps;
 import com.mojang.datafixers.DataFixer;
 import com.mojang.logging.LogUtils;
+import com.mojang.serialization.DataResult;
 import com.mojang.serialization.Dynamic;
 import com.mojang.serialization.Lifecycle;
 import java.io.BufferedOutputStream;
@@ -12,6 +13,7 @@
 import java.nio.file.FileVisitResult;
 import java.nio.file.Files;
 import java.nio.file.InvalidPathException;
+import java.nio.file.LinkOption;
 import java.nio.file.Path;
 import java.nio.file.PathMatcher;
 import java.nio.file.Paths;
@@ -65,7 +67,6 @@
 import net.minecraft.world.level.Level;
 import net.minecraft.world.level.LevelSettings;
 import net.minecraft.world.level.WorldDataConfiguration;
-import net.minecraft.world.level.dimension.DimensionType;
 import net.minecraft.world.level.dimension.LevelStem;
 import net.minecraft.world.level.levelgen.WorldDimensions;
 import net.minecraft.world.level.levelgen.WorldGenSettings;
@@ -76,10 +77,13 @@
 import org.slf4j.Logger;
 
 public class LevelStorageSource {
+
     static final Logger LOGGER = LogUtils.getLogger();
     static final DateTimeFormatter FORMATTER = FileNameDateFormatter.create();
     private static final String TAG_DATA = "Data";
-    private static final PathMatcher NO_SYMLINKS_ALLOWED = path -> false;
+    private static final PathMatcher NO_SYMLINKS_ALLOWED = (path) -> {
+        return false;
+    };
     public static final String ALLOWED_SYMLINKS_CONFIG_NAME = "allowed_symlinks.txt";
     private static final int UNCOMPRESSED_NBT_QUOTA = 104857600;
     private static final int DISK_SPACE_WARNING_THRESHOLD = 67108864;
@@ -93,8 +97,8 @@
 
         try {
             FileUtil.createDirectoriesSafe(savesDirectory);
-        } catch (IOException var6) {
-            throw new UncheckedIOException(var6);
+        } catch (IOException ioexception) {
+            throw new UncheckedIOException(ioexception);
         }
 
         this.baseDir = savesDirectory;
@@ -103,48 +107,68 @@
     }
 
     public static DirectoryValidator parseValidator(Path allowedSymlinksFile) {
-        if (Files.exists(allowedSymlinksFile)) {
+        if (Files.exists(allowedSymlinksFile, new LinkOption[0])) {
             try {
-                DirectoryValidator var2;
-                try (BufferedReader bufferedReader = Files.newBufferedReader(allowedSymlinksFile)) {
-                    var2 = new DirectoryValidator(PathAllowList.readPlain(bufferedReader));
+                BufferedReader bufferedreader = Files.newBufferedReader(allowedSymlinksFile);
+
+                DirectoryValidator directoryvalidator;
+
+                try {
+                    directoryvalidator = new DirectoryValidator(PathAllowList.readPlain(bufferedreader));
+                } catch (Throwable throwable) {
+                    if (bufferedreader != null) {
+                        try {
+                            bufferedreader.close();
+                        } catch (Throwable throwable1) {
+                            throwable.addSuppressed(throwable1);
+                        }
+                    }
+
+                    throw throwable;
                 }
 
-                return var2;
-            } catch (Exception var6) {
-                LOGGER.error("Failed to parse {}, disallowing all symbolic links", "allowed_symlinks.txt", var6);
+                if (bufferedreader != null) {
+                    bufferedreader.close();
+                }
+
+                return directoryvalidator;
+            } catch (Exception exception) {
+                LevelStorageSource.LOGGER.error("Failed to parse {}, disallowing all symbolic links", "allowed_symlinks.txt", exception);
             }
         }
 
-        return new DirectoryValidator(NO_SYMLINKS_ALLOWED);
+        return new DirectoryValidator(LevelStorageSource.NO_SYMLINKS_ALLOWED);
     }
 
     public static LevelStorageSource createDefault(Path path) {
-        DirectoryValidator directoryValidator = parseValidator(path.resolve("allowed_symlinks.txt"));
-        return new LevelStorageSource(path, path.resolve("../backups"), directoryValidator, DataFixers.getDataFixer());
+        DirectoryValidator directoryvalidator = LevelStorageSource.parseValidator(path.resolve("allowed_symlinks.txt"));
+
+        return new LevelStorageSource(path, path.resolve("../backups"), directoryvalidator, DataFixers.getDataFixer());
     }
 
     public static WorldDataConfiguration readDataConfig(Dynamic<?> dynamic) {
-        return WorldDataConfiguration.CODEC.parse(dynamic).resultOrPartial(LOGGER::error).orElse(WorldDataConfiguration.DEFAULT);
+        DataResult<WorldDataConfiguration> dataresult = WorldDataConfiguration.CODEC.parse(dynamic); // CraftBukkit - decompile error
+        Logger logger = LevelStorageSource.LOGGER;
+
+        Objects.requireNonNull(logger);
+        return (WorldDataConfiguration) dataresult.resultOrPartial(logger::error).orElse(WorldDataConfiguration.DEFAULT);
     }
 
     public static WorldLoader.PackConfig getPackConfig(Dynamic<?> dynamic, PackRepository dataPackManager, boolean safeMode) {
-        return new WorldLoader.PackConfig(dataPackManager, readDataConfig(dynamic), safeMode, false);
+        return new WorldLoader.PackConfig(dataPackManager, LevelStorageSource.readDataConfig(dynamic), safeMode, false);
     }
 
-    public static LevelDataAndDimensions getLevelDataAndDimensions(
-        Dynamic<?> dynamic, WorldDataConfiguration dataConfiguration, Registry<LevelStem> dimensionsRegistry, RegistryAccess.Frozen registryManager
-    ) {
-        Dynamic<?> dynamic2 = RegistryOps.injectRegistryContext(dynamic, registryManager);
-        Dynamic<?> dynamic3 = dynamic2.get("WorldGenSettings").orElseEmptyMap();
-        WorldGenSettings worldGenSettings = WorldGenSettings.CODEC.parse(dynamic3).getOrThrow();
-        LevelSettings levelSettings = LevelSettings.parse(dynamic2, dataConfiguration);
-        WorldDimensions.Complete complete = worldGenSettings.dimensions().bake(dimensionsRegistry);
-        Lifecycle lifecycle = complete.lifecycle().add(registryManager.allRegistriesLifecycle());
-        PrimaryLevelData primaryLevelData = PrimaryLevelData.parse(
-            dynamic2, levelSettings, complete.specialWorldProperty(), worldGenSettings.options(), lifecycle
-        );
-        return new LevelDataAndDimensions(primaryLevelData, complete);
+    public static LevelDataAndDimensions getLevelDataAndDimensions(Dynamic<?> dynamic, WorldDataConfiguration dataConfiguration, Registry<LevelStem> dimensionsRegistry, RegistryAccess.Frozen registryManager) {
+        Dynamic<?> dynamic1 = RegistryOps.injectRegistryContext(dynamic, registryManager);
+        Dynamic<?> dynamic2 = dynamic1.get("WorldGenSettings").orElseEmptyMap();
+        WorldGenSettings generatorsettings = (WorldGenSettings) WorldGenSettings.CODEC.parse(dynamic2).getOrThrow();
+        LevelSettings worldsettings = LevelSettings.parse(dynamic1, dataConfiguration);
+        WorldDimensions.Complete worlddimensions_b = generatorsettings.dimensions().bake(dimensionsRegistry);
+        Lifecycle lifecycle = worlddimensions_b.lifecycle().add(registryManager.allRegistriesLifecycle());
+        PrimaryLevelData worlddataserver = PrimaryLevelData.parse(dynamic1, worldsettings, worlddimensions_b.specialWorldProperty(), generatorsettings.options(), lifecycle);
+        worlddataserver.pdc = ((Dynamic<Tag>) dynamic1).getElement("BukkitValues", null); // CraftBukkit - Add PDC to world
+
+        return new LevelDataAndDimensions(worlddataserver, worlddimensions_b);
     }
 
     public String getName() {
@@ -152,65 +176,94 @@
     }
 
     public LevelStorageSource.LevelCandidates findLevelCandidates() throws LevelStorageException {
-        if (!Files.isDirectory(this.baseDir)) {
+        if (!Files.isDirectory(this.baseDir, new LinkOption[0])) {
             throw new LevelStorageException(Component.translatable("selectWorld.load_folder_access"));
         } else {
             try {
-                LevelStorageSource.LevelCandidates var3;
-                try (Stream<Path> stream = Files.list(this.baseDir)) {
-                    List<LevelStorageSource.LevelDirectory> list = stream.filter(path -> Files.isDirectory(path))
-                        .map(LevelStorageSource.LevelDirectory::new)
-                        .filter(levelSave -> Files.isRegularFile(levelSave.dataFile()) || Files.isRegularFile(levelSave.oldDataFile()))
-                        .toList();
-                    var3 = new LevelStorageSource.LevelCandidates(list);
+                Stream<Path> stream = Files.list(this.baseDir);
+
+                LevelStorageSource.LevelCandidates convertable_a;
+
+                try {
+                    List<LevelStorageSource.LevelDirectory> list = stream.filter((path) -> {
+                        return Files.isDirectory(path, new LinkOption[0]);
+                    }).map(LevelStorageSource.LevelDirectory::new).filter((convertable_b) -> {
+                        return Files.isRegularFile(convertable_b.dataFile(), new LinkOption[0]) || Files.isRegularFile(convertable_b.oldDataFile(), new LinkOption[0]);
+                    }).toList();
+
+                    convertable_a = new LevelStorageSource.LevelCandidates(list);
+                } catch (Throwable throwable) {
+                    if (stream != null) {
+                        try {
+                            stream.close();
+                        } catch (Throwable throwable1) {
+                            throwable.addSuppressed(throwable1);
+                        }
+                    }
+
+                    throw throwable;
                 }
 
-                return var3;
-            } catch (IOException var6) {
+                if (stream != null) {
+                    stream.close();
+                }
+
+                return convertable_a;
+            } catch (IOException ioexception) {
                 throw new LevelStorageException(Component.translatable("selectWorld.load_folder_access"));
             }
         }
     }
 
     public CompletableFuture<List<LevelSummary>> loadLevelSummaries(LevelStorageSource.LevelCandidates levels) {
-        List<CompletableFuture<LevelSummary>> list = new ArrayList<>(levels.levels.size());
+        List<CompletableFuture<LevelSummary>> list = new ArrayList(levels.levels.size());
+        Iterator iterator = levels.levels.iterator();
+
+        while (iterator.hasNext()) {
+            LevelStorageSource.LevelDirectory convertable_b = (LevelStorageSource.LevelDirectory) iterator.next();
 
-        for (LevelStorageSource.LevelDirectory levelDirectory : levels.levels) {
             list.add(CompletableFuture.supplyAsync(() -> {
-                boolean bl;
+                boolean flag;
+
                 try {
-                    bl = DirectoryLock.isLocked(levelDirectory.path());
-                } catch (Exception var13) {
-                    LOGGER.warn("Failed to read {} lock", levelDirectory.path(), var13);
+                    flag = DirectoryLock.isLocked(convertable_b.path());
+                } catch (Exception exception) {
+                    LevelStorageSource.LOGGER.warn("Failed to read {} lock", convertable_b.path(), exception);
                     return null;
                 }
 
                 try {
-                    return this.readLevelSummary(levelDirectory, bl);
-                } catch (OutOfMemoryError var12) {
+                    return this.readLevelSummary(convertable_b, flag);
+                } catch (OutOfMemoryError outofmemoryerror) {
                     MemoryReserve.release();
                     System.gc();
-                    String string = "Ran out of memory trying to read summary of world folder \"" + levelDirectory.directoryName() + "\"";
-                    LOGGER.error(LogUtils.FATAL_MARKER, string);
-                    OutOfMemoryError outOfMemoryError2 = new OutOfMemoryError("Ran out of memory reading level data");
-                    outOfMemoryError2.initCause(var12);
-                    CrashReport crashReport = CrashReport.forThrowable(outOfMemoryError2, string);
-                    CrashReportCategory crashReportCategory = crashReport.addCategory("World details");
-                    crashReportCategory.setDetail("Folder Name", levelDirectory.directoryName());
+                    String s = "Ran out of memory trying to read summary of world folder \"" + convertable_b.directoryName() + "\"";
+
+                    LevelStorageSource.LOGGER.error(LogUtils.FATAL_MARKER, s);
+                    OutOfMemoryError outofmemoryerror1 = new OutOfMemoryError("Ran out of memory reading level data");
+
+                    outofmemoryerror1.initCause(outofmemoryerror);
+                    CrashReport crashreport = CrashReport.forThrowable(outofmemoryerror1, s);
+                    CrashReportCategory crashreportsystemdetails = crashreport.addCategory("World details");
+
+                    crashreportsystemdetails.setDetail("Folder Name", (Object) convertable_b.directoryName());
 
                     try {
-                        long l = Files.size(levelDirectory.dataFile());
-                        crashReportCategory.setDetail("level.dat size", l);
-                    } catch (IOException var11) {
-                        crashReportCategory.setDetailError("level.dat size", var11);
+                        long i = Files.size(convertable_b.dataFile());
+
+                        crashreportsystemdetails.setDetail("level.dat size", (Object) i);
+                    } catch (IOException ioexception) {
+                        crashreportsystemdetails.setDetailError("level.dat size", ioexception);
                     }
 
-                    throw new ReportedException(crashReport);
+                    throw new ReportedException(crashreport);
                 }
             }, Util.backgroundExecutor()));
         }
 
-        return Util.sequenceFailFastAndCancel(list).thenApply(summaries -> summaries.stream().filter(Objects::nonNull).sorted().toList());
+        return Util.sequenceFailFastAndCancel(list).thenApply((list1) -> {
+            return list1.stream().filter(Objects::nonNull).sorted().toList();
+        });
     }
 
     private int getStorageVersion() {
@@ -222,46 +275,68 @@
     }
 
     static Dynamic<?> readLevelDataTagFixed(Path path, DataFixer dataFixer) throws IOException {
-        CompoundTag compoundTag = readLevelDataTagRaw(path);
-        CompoundTag compoundTag2 = compoundTag.getCompound("Data");
-        int i = NbtUtils.getDataVersion(compoundTag2, -1);
-        Dynamic<?> dynamic = DataFixTypes.LEVEL.updateToCurrentVersion(dataFixer, new Dynamic<>(NbtOps.INSTANCE, compoundTag2), i);
-        dynamic = dynamic.update("Player", dynamicx -> DataFixTypes.PLAYER.updateToCurrentVersion(dataFixer, dynamicx, i));
-        return dynamic.update("WorldGenSettings", dynamicx -> DataFixTypes.WORLD_GEN_SETTINGS.updateToCurrentVersion(dataFixer, dynamicx, i));
+        CompoundTag nbttagcompound = LevelStorageSource.readLevelDataTagRaw(path);
+        CompoundTag nbttagcompound1 = nbttagcompound.getCompound("Data");
+        int i = NbtUtils.getDataVersion(nbttagcompound1, -1); final int version = i; // Paper - obfuscation helpers
+        Dynamic<?> dynamic = DataFixTypes.LEVEL.updateToCurrentVersion(dataFixer, new Dynamic(NbtOps.INSTANCE, nbttagcompound1), i);
+
+        // Paper start - replace data conversion system
+        dynamic = dynamic.update("Player", (dynamic1) -> {
+            return new Dynamic<>(
+                NbtOps.INSTANCE,
+                ca.spottedleaf.dataconverter.minecraft.MCDataConverter.convertTag(
+                    ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry.PLAYER,
+                    (net.minecraft.nbt.CompoundTag)dynamic1.getValue(),
+                    version, net.minecraft.SharedConstants.getCurrentVersion().getDataVersion().getVersion()
+                )
+            );
+        });
+        // Paper end - replace data conversion system
+        dynamic = dynamic.update("WorldGenSettings", (dynamic1) -> {
+            return DataFixTypes.WORLD_GEN_SETTINGS.updateToCurrentVersion(dataFixer, dynamic1, i);
+        });
+        return dynamic;
     }
 
     private LevelSummary readLevelSummary(LevelStorageSource.LevelDirectory save, boolean locked) {
         Path path = save.dataFile();
-        if (Files.exists(path)) {
+
+        if (Files.exists(path, new LinkOption[0])) {
             try {
                 if (Files.isSymbolicLink(path)) {
                     List<ForbiddenSymlinkInfo> list = this.worldDirValidator.validateSymlink(path);
+
                     if (!list.isEmpty()) {
-                        LOGGER.warn("{}", ContentValidationException.getMessage(path, list));
+                        LevelStorageSource.LOGGER.warn("{}", ContentValidationException.getMessage(path, list));
                         return new LevelSummary.SymlinkLevelSummary(save.directoryName(), save.iconFile());
                     }
                 }
 
-                if (readLightweightData(path) instanceof CompoundTag compoundTag) {
-                    CompoundTag compoundTag2 = compoundTag.getCompound("Data");
-                    int i = NbtUtils.getDataVersion(compoundTag2, -1);
-                    Dynamic<?> dynamic = DataFixTypes.LEVEL.updateToCurrentVersion(this.fixerUpper, new Dynamic<>(NbtOps.INSTANCE, compoundTag2), i);
+                Tag nbtbase = LevelStorageSource.readLightweightData(path);
+
+                if (nbtbase instanceof CompoundTag) {
+                    CompoundTag nbttagcompound = (CompoundTag) nbtbase;
+                    CompoundTag nbttagcompound1 = nbttagcompound.getCompound("Data");
+                    int i = NbtUtils.getDataVersion(nbttagcompound1, -1);
+                    Dynamic<?> dynamic = DataFixTypes.LEVEL.updateToCurrentVersion(this.fixerUpper, new Dynamic(NbtOps.INSTANCE, nbttagcompound1), i);
+
                     return this.makeLevelSummary(dynamic, save, locked);
                 }
 
-                LOGGER.warn("Invalid root tag in {}", path);
-            } catch (Exception var9) {
-                LOGGER.error("Exception reading {}", path, var9);
+                LevelStorageSource.LOGGER.warn("Invalid root tag in {}", path);
+            } catch (Exception exception) {
+                LevelStorageSource.LOGGER.error("Exception reading {}", path, exception);
             }
         }
 
-        return new LevelSummary.CorruptedLevelSummary(save.directoryName(), save.iconFile(), getFileModificationTime(save));
+        return new LevelSummary.CorruptedLevelSummary(save.directoryName(), save.iconFile(), LevelStorageSource.getFileModificationTime(save));
     }
 
     private static long getFileModificationTime(LevelStorageSource.LevelDirectory save) {
-        Instant instant = getFileModificationTime(save.dataFile());
+        Instant instant = LevelStorageSource.getFileModificationTime(save.dataFile());
+
         if (instant == null) {
-            instant = getFileModificationTime(save.oldDataFile());
+            instant = LevelStorageSource.getFileModificationTime(save.oldDataFile());
         }
 
         return instant == null ? -1L : instant.toEpochMilli();
@@ -271,60 +346,62 @@
     static Instant getFileModificationTime(Path path) {
         try {
             return Files.getLastModifiedTime(path).toInstant();
-        } catch (IOException var2) {
+        } catch (IOException ioexception) {
             return null;
         }
     }
 
     LevelSummary makeLevelSummary(Dynamic<?> dynamic, LevelStorageSource.LevelDirectory save, boolean locked) {
-        LevelVersion levelVersion = LevelVersion.parse(dynamic);
-        int i = levelVersion.levelDataVersion();
+        LevelVersion levelversion = LevelVersion.parse(dynamic);
+        int i = levelversion.levelDataVersion();
+
         if (i != 19132 && i != 19133) {
             throw new NbtFormatException("Unknown data version: " + Integer.toHexString(i));
         } else {
-            boolean bl = i != this.getStorageVersion();
+            boolean flag1 = i != this.getStorageVersion();
             Path path = save.iconFile();
-            WorldDataConfiguration worldDataConfiguration = readDataConfig(dynamic);
-            LevelSettings levelSettings = LevelSettings.parse(dynamic, worldDataConfiguration);
-            FeatureFlagSet featureFlagSet = parseFeatureFlagsFromSummary(dynamic);
-            boolean bl2 = FeatureFlags.isExperimental(featureFlagSet);
-            return new LevelSummary(levelSettings, levelVersion, save.directoryName(), bl, locked, bl2, path);
+            WorldDataConfiguration worlddataconfiguration = LevelStorageSource.readDataConfig(dynamic);
+            LevelSettings worldsettings = LevelSettings.parse(dynamic, worlddataconfiguration);
+            FeatureFlagSet featureflagset = LevelStorageSource.parseFeatureFlagsFromSummary(dynamic);
+            boolean flag2 = FeatureFlags.isExperimental(featureflagset);
+
+            return new LevelSummary(worldsettings, levelversion, save.directoryName(), flag1, locked, flag2, path);
         }
     }
 
     private static FeatureFlagSet parseFeatureFlagsFromSummary(Dynamic<?> levelData) {
-        Set<ResourceLocation> set = levelData.get("enabled_features")
-            .asStream()
-            .flatMap(featureFlag -> featureFlag.asString().result().map(ResourceLocation::tryParse).stream())
-            .collect(Collectors.toSet());
-        return FeatureFlags.REGISTRY.fromNames(set, id -> {
+        Set<ResourceLocation> set = (Set) levelData.get("enabled_features").asStream().flatMap((dynamic1) -> {
+            return dynamic1.asString().result().map(ResourceLocation::tryParse).stream();
+        }).collect(Collectors.toSet());
+
+        return FeatureFlags.REGISTRY.fromNames(set, (minecraftkey) -> {
         });
     }
 
     @Nullable
     private static Tag readLightweightData(Path path) throws IOException {
-        SkipFields skipFields = new SkipFields(
-            new FieldSelector("Data", CompoundTag.TYPE, "Player"), new FieldSelector("Data", CompoundTag.TYPE, "WorldGenSettings")
-        );
-        NbtIo.parseCompressed(path, skipFields, NbtAccounter.create(104857600L));
-        return skipFields.getResult();
+        SkipFields skipfields = new SkipFields(new FieldSelector[]{new FieldSelector("Data", CompoundTag.TYPE, "Player"), new FieldSelector("Data", CompoundTag.TYPE, "WorldGenSettings")});
+
+        NbtIo.parseCompressed(path, skipfields, NbtAccounter.create(104857600L));
+        return skipfields.getResult();
     }
 
     public boolean isNewLevelIdAcceptable(String name) {
         try {
             Path path = this.getLevelPath(name);
+
             Files.createDirectory(path);
             Files.deleteIfExists(path);
             return true;
-        } catch (IOException var3) {
+        } catch (IOException ioexception) {
             return false;
         }
     }
 
     public boolean levelExists(String name) {
         try {
-            return Files.isDirectory(this.getLevelPath(name));
-        } catch (InvalidPathException var3) {
+            return Files.isDirectory(this.getLevelPath(name), new LinkOption[0]);
+        } catch (InvalidPathException invalidpathexception) {
             return false;
         }
     }
@@ -341,37 +418,54 @@
         return this.backupDir;
     }
 
-    public LevelStorageSource.LevelStorageAccess validateAndCreateAccess(String directoryName) throws IOException, ContentValidationException {
-        Path path = this.getLevelPath(directoryName);
+    public LevelStorageSource.LevelStorageAccess validateAndCreateAccess(String s, ResourceKey<LevelStem> dimensionType) throws IOException, ContentValidationException { // CraftBukkit
+        Path path = this.getLevelPath(s);
         List<ForbiddenSymlinkInfo> list = this.worldDirValidator.validateDirectory(path, true);
+
         if (!list.isEmpty()) {
             throw new ContentValidationException(path, list);
         } else {
-            return new LevelStorageSource.LevelStorageAccess(directoryName, path);
+            return new LevelStorageSource.LevelStorageAccess(s, path, dimensionType); // CraftBukkit
         }
     }
 
-    public LevelStorageSource.LevelStorageAccess createAccess(String directoryName) throws IOException {
-        Path path = this.getLevelPath(directoryName);
-        return new LevelStorageSource.LevelStorageAccess(directoryName, path);
+    public LevelStorageSource.LevelStorageAccess createAccess(String s, ResourceKey<LevelStem> dimensionType) throws IOException { // CraftBukkit
+        Path path = this.getLevelPath(s);
+
+        return new LevelStorageSource.LevelStorageAccess(s, path, dimensionType); // CraftBukkit
     }
 
     public DirectoryValidator getWorldDirValidator() {
         return this.worldDirValidator;
     }
 
+    // CraftBukkit start
+    public static Path getStorageFolder(Path path, ResourceKey<LevelStem> dimensionType) {
+        if (dimensionType == LevelStem.OVERWORLD) {
+            return path;
+        } else if (dimensionType == LevelStem.NETHER) {
+            return path.resolve("DIM-1");
+        } else if (dimensionType == LevelStem.END) {
+            return path.resolve("DIM1");
+        } else {
+            return path.resolve("dimensions").resolve(dimensionType.location().getNamespace()).resolve(dimensionType.location().getPath());
+        }
+    }
+    // CraftBukkit end
+
     public static record LevelCandidates(List<LevelStorageSource.LevelDirectory> levels) implements Iterable<LevelStorageSource.LevelDirectory> {
+
         public boolean isEmpty() {
             return this.levels.isEmpty();
         }
 
-        @Override
         public Iterator<LevelStorageSource.LevelDirectory> iterator() {
             return this.levels.iterator();
         }
     }
 
     public static record LevelDirectory(Path path) {
+
         public String directoryName() {
             return this.path.getFileName().toString();
         }
@@ -385,11 +479,17 @@
         }
 
         public Path corruptedDataFile(LocalDateTime dateTime) {
-            return this.path.resolve(LevelResource.LEVEL_DATA_FILE.getId() + "_corrupted_" + dateTime.format(LevelStorageSource.FORMATTER));
+            Path path = this.path;
+            String s = LevelResource.LEVEL_DATA_FILE.getId();
+
+            return path.resolve(s + "_corrupted_" + dateTime.format(LevelStorageSource.FORMATTER));
         }
 
         public Path rawDataFile(LocalDateTime dateTime) {
-            return this.path.resolve(LevelResource.LEVEL_DATA_FILE.getId() + "_raw_" + dateTime.format(LevelStorageSource.FORMATTER));
+            Path path = this.path;
+            String s = LevelResource.LEVEL_DATA_FILE.getId();
+
+            return path.resolve(s + "_raw_" + dateTime.format(LevelStorageSource.FORMATTER));
         }
 
         public Path iconFile() {
@@ -406,13 +506,18 @@
     }
 
     public class LevelStorageAccess implements AutoCloseable {
+
         final DirectoryLock lock;
         public final LevelStorageSource.LevelDirectory levelDirectory;
         private final String levelId;
         private final Map<LevelResource, Path> resources = Maps.newHashMap();
+        // CraftBukkit start
+        public final ResourceKey<LevelStem> dimensionType;
 
-        LevelStorageAccess(final String directoryName, final Path path) throws IOException {
-            this.levelId = directoryName;
+        LevelStorageAccess(final String s, final Path path, final ResourceKey<LevelStem> dimensionType) throws IOException {
+            this.dimensionType = dimensionType;
+            // CraftBukkit end
+            this.levelId = s;
             this.levelDirectory = new LevelStorageSource.LevelDirectory(path);
             this.lock = DirectoryLock.create(path);
         }
@@ -420,7 +525,7 @@
         public long estimateDiskSpace() {
             try {
                 return Files.getFileStore(this.levelDirectory.path).getUsableSpace();
-            } catch (Exception var2) {
+            } catch (Exception exception) {
                 return Long.MAX_VALUE;
             }
         }
@@ -432,9 +537,10 @@
         public void safeClose() {
             try {
                 this.close();
-            } catch (IOException var2) {
-                LevelStorageSource.LOGGER.warn("Failed to unlock access to level {}", this.getLevelId(), var2);
+            } catch (IOException ioexception) {
+                LevelStorageSource.LOGGER.warn("Failed to unlock access to level {}", this.getLevelId(), ioexception);
             }
+
         }
 
         public LevelStorageSource parent() {
@@ -450,11 +556,15 @@
         }
 
         public Path getLevelPath(LevelResource savePath) {
-            return this.resources.computeIfAbsent(savePath, this.levelDirectory::resourcePath);
+            Map<LevelResource, Path> map = this.resources; // CraftBukkit - decompile error
+            LevelStorageSource.LevelDirectory convertable_b = this.levelDirectory;
+
+            Objects.requireNonNull(this.levelDirectory);
+            return (Path) map.computeIfAbsent(savePath, convertable_b::resourcePath);
         }
 
         public Path getDimensionPath(ResourceKey<Level> key) {
-            return DimensionType.getStorageFolder(key, this.levelDirectory.path());
+            return LevelStorageSource.getStorageFolder(this.levelDirectory.path(), this.dimensionType); // CraftBukkit
         }
 
         private void checkLock() {
@@ -483,34 +593,36 @@
 
         private Dynamic<?> getDataTag(boolean old) throws IOException {
             this.checkLock();
-            return LevelStorageSource.readLevelDataTagFixed(
-                old ? this.levelDirectory.oldDataFile() : this.levelDirectory.dataFile(), LevelStorageSource.this.fixerUpper
-            );
+            return LevelStorageSource.readLevelDataTagFixed(old ? this.levelDirectory.oldDataFile() : this.levelDirectory.dataFile(), LevelStorageSource.this.fixerUpper);
         }
 
         public void saveDataTag(RegistryAccess registryManager, WorldData saveProperties) {
-            this.saveDataTag(registryManager, saveProperties, null);
+            this.saveDataTag(registryManager, saveProperties, (CompoundTag) null);
         }
 
         public void saveDataTag(RegistryAccess registryManager, WorldData saveProperties, @Nullable CompoundTag nbt) {
-            CompoundTag compoundTag = saveProperties.createTag(registryManager, nbt);
-            CompoundTag compoundTag2 = new CompoundTag();
-            compoundTag2.put("Data", compoundTag);
-            this.saveLevelData(compoundTag2);
+            CompoundTag nbttagcompound1 = saveProperties.createTag(registryManager, nbt);
+            CompoundTag nbttagcompound2 = new CompoundTag();
+
+            nbttagcompound2.put("Data", nbttagcompound1);
+            this.saveLevelData(nbttagcompound2);
         }
 
         private void saveLevelData(CompoundTag nbt) {
             Path path = this.levelDirectory.path();
 
             try {
-                Path path2 = Files.createTempFile(path, "level", ".dat");
-                NbtIo.writeCompressed(nbt, path2);
-                Path path3 = this.levelDirectory.oldDataFile();
-                Path path4 = this.levelDirectory.dataFile();
-                Util.safeReplaceFile(path4, path2, path3);
-            } catch (Exception var6) {
-                LevelStorageSource.LOGGER.error("Failed to save level {}", path, var6);
+                Path path1 = Files.createTempFile(path, "level", ".dat");
+
+                NbtIo.writeCompressed(nbt, path1);
+                Path path2 = this.levelDirectory.oldDataFile();
+                Path path3 = this.levelDirectory.dataFile();
+
+                Util.safeReplaceFile(path3, path1, path2);
+            } catch (Exception exception) {
+                LevelStorageSource.LOGGER.error("Failed to save level {}", path, exception);
             }
+
         }
 
         public Optional<Path> getIconFile() {
@@ -520,120 +632,136 @@
         public void deleteLevel() throws IOException {
             this.checkLock();
             final Path path = this.levelDirectory.lockFile();
+
             LevelStorageSource.LOGGER.info("Deleting level {}", this.levelId);
+            int i = 1;
 
-            for (int i = 1; i <= 5; i++) {
+            while (i <= 5) {
                 LevelStorageSource.LOGGER.info("Attempt {}...", i);
 
                 try {
                     Files.walkFileTree(this.levelDirectory.path(), new SimpleFileVisitor<Path>() {
-                        @Override
-                        public FileVisitResult visitFile(Path path, BasicFileAttributes basicFileAttributes) throws IOException {
-                            if (!path.equals(path)) {
-                                LevelStorageSource.LOGGER.debug("Deleting {}", path);
-                                Files.delete(path);
+                        public FileVisitResult visitFile(Path path1, BasicFileAttributes basicfileattributes) throws IOException {
+                            if (!path1.equals(path)) {
+                                LevelStorageSource.LOGGER.debug("Deleting {}", path1);
+                                Files.delete(path1);
                             }
 
                             return FileVisitResult.CONTINUE;
                         }
 
-                        @Override
-                        public FileVisitResult postVisitDirectory(Path path, @Nullable IOException iOException) throws IOException {
-                            if (iOException != null) {
-                                throw iOException;
+                        public FileVisitResult postVisitDirectory(Path path1, @Nullable IOException ioexception) throws IOException {
+                            if (ioexception != null) {
+                                throw ioexception;
                             } else {
-                                if (path.equals(LevelStorageAccess.this.levelDirectory.path())) {
+                                if (path1.equals(LevelStorageAccess.this.levelDirectory.path())) {
                                     LevelStorageAccess.this.lock.close();
                                     Files.deleteIfExists(path);
                                 }
 
-                                Files.delete(path);
+                                Files.delete(path1);
                                 return FileVisitResult.CONTINUE;
                             }
                         }
                     });
                     break;
-                } catch (IOException var6) {
+                } catch (IOException ioexception) {
                     if (i >= 5) {
-                        throw var6;
+                        throw ioexception;
                     }
 
-                    LevelStorageSource.LOGGER.warn("Failed to delete {}", this.levelDirectory.path(), var6);
+                    LevelStorageSource.LOGGER.warn("Failed to delete {}", this.levelDirectory.path(), ioexception);
 
                     try {
                         Thread.sleep(500L);
-                    } catch (InterruptedException var5) {
+                    } catch (InterruptedException interruptedexception) {
+                        ;
                     }
+
+                    ++i;
                 }
             }
+
         }
 
         public void renameLevel(String name) throws IOException {
-            this.modifyLevelDataWithoutDatafix(nbt -> nbt.putString("LevelName", name.trim()));
+            this.modifyLevelDataWithoutDatafix((nbttagcompound) -> {
+                nbttagcompound.putString("LevelName", name.trim());
+            });
         }
 
         public void renameAndDropPlayer(String name) throws IOException {
-            this.modifyLevelDataWithoutDatafix(nbt -> {
-                nbt.putString("LevelName", name.trim());
-                nbt.remove("Player");
+            this.modifyLevelDataWithoutDatafix((nbttagcompound) -> {
+                nbttagcompound.putString("LevelName", name.trim());
+                nbttagcompound.remove("Player");
             });
         }
 
         private void modifyLevelDataWithoutDatafix(Consumer<CompoundTag> nbtProcessor) throws IOException {
             this.checkLock();
-            CompoundTag compoundTag = LevelStorageSource.readLevelDataTagRaw(this.levelDirectory.dataFile());
-            nbtProcessor.accept(compoundTag.getCompound("Data"));
-            this.saveLevelData(compoundTag);
+            CompoundTag nbttagcompound = LevelStorageSource.readLevelDataTagRaw(this.levelDirectory.dataFile());
+
+            nbtProcessor.accept(nbttagcompound.getCompound("Data"));
+            this.saveLevelData(nbttagcompound);
         }
 
         public long makeWorldBackup() throws IOException {
             this.checkLock();
-            String string = LocalDateTime.now().format(LevelStorageSource.FORMATTER) + "_" + this.levelId;
+            String s = LocalDateTime.now().format(LevelStorageSource.FORMATTER);
+            String s1 = s + "_" + this.levelId;
             Path path = LevelStorageSource.this.getBackupPath();
 
             try {
                 FileUtil.createDirectoriesSafe(path);
-            } catch (IOException var9) {
-                throw new RuntimeException(var9);
+            } catch (IOException ioexception) {
+                throw new RuntimeException(ioexception);
             }
 
-            Path path2 = path.resolve(FileUtil.findAvailableName(path, string, ".zip"));
+            Path path1 = path.resolve(FileUtil.findAvailableName(path, s1, ".zip"));
+            final ZipOutputStream zipoutputstream = new ZipOutputStream(new BufferedOutputStream(Files.newOutputStream(path1)));
+
+            try {
+                final Path path2 = Paths.get(this.levelId);
 
-            try (final ZipOutputStream zipOutputStream = new ZipOutputStream(new BufferedOutputStream(Files.newOutputStream(path2)))) {
-                final Path path3 = Paths.get(this.levelId);
                 Files.walkFileTree(this.levelDirectory.path(), new SimpleFileVisitor<Path>() {
-                    @Override
-                    public FileVisitResult visitFile(Path path, BasicFileAttributes basicFileAttributes) throws IOException {
-                        if (path.endsWith("session.lock")) {
+                    public FileVisitResult visitFile(Path path3, BasicFileAttributes basicfileattributes) throws IOException {
+                        if (path3.endsWith("session.lock")) {
                             return FileVisitResult.CONTINUE;
                         } else {
-                            String string = path3.resolve(LevelStorageAccess.this.levelDirectory.path().relativize(path)).toString().replace('\\', '/');
-                            ZipEntry zipEntry = new ZipEntry(string);
-                            zipOutputStream.putNextEntry(zipEntry);
-                            com.google.common.io.Files.asByteSource(path.toFile()).copyTo(zipOutputStream);
-                            zipOutputStream.closeEntry();
+                            String s2 = path2.resolve(LevelStorageAccess.this.levelDirectory.path().relativize(path3)).toString().replace('\\', '/');
+                            ZipEntry zipentry = new ZipEntry(s2);
+
+                            zipoutputstream.putNextEntry(zipentry);
+                            com.google.common.io.Files.asByteSource(path3.toFile()).copyTo(zipoutputstream);
+                            zipoutputstream.closeEntry();
                             return FileVisitResult.CONTINUE;
                         }
                     }
                 });
+            } catch (Throwable throwable) {
+                try {
+                    zipoutputstream.close();
+                } catch (Throwable throwable1) {
+                    throwable.addSuppressed(throwable1);
+                }
+
+                throw throwable;
             }
 
-            return Files.size(path2);
+            zipoutputstream.close();
+            return Files.size(path1);
         }
 
         public boolean hasWorldData() {
-            return Files.exists(this.levelDirectory.dataFile()) || Files.exists(this.levelDirectory.oldDataFile());
+            return Files.exists(this.levelDirectory.dataFile(), new LinkOption[0]) || Files.exists(this.levelDirectory.oldDataFile(), new LinkOption[0]);
         }
 
-        @Override
         public void close() throws IOException {
             this.lock.close();
         }
 
         public boolean restoreLevelDataFromOld() {
-            return Util.safeReplaceOrMoveFile(
-                this.levelDirectory.dataFile(), this.levelDirectory.oldDataFile(), this.levelDirectory.corruptedDataFile(LocalDateTime.now()), true
-            );
+            return Util.safeReplaceOrMoveFile(this.levelDirectory.dataFile(), this.levelDirectory.oldDataFile(), this.levelDirectory.corruptedDataFile(LocalDateTime.now()), true);
         }
 
         @Nullable
