diff --git a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/world/IDhApiLevelWrapper.java b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/world/IDhApiLevelWrapper.java index 3bc48f29c..658a483b0 100644 --- a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/world/IDhApiLevelWrapper.java +++ b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/world/IDhApiLevelWrapper.java @@ -23,6 +23,8 @@ import com.seibel.distanthorizons.api.interfaces.IDhApiUnsafeWrapper; import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiLevelType; import com.seibel.distanthorizons.api.interfaces.render.IDhApiCustomRenderRegister; +import java.io.File; + /** * Can be either a Server or Client level.
* A level is equivalent to a dimension in vanilla Minecraft. @@ -72,4 +74,15 @@ public interface IDhApiLevelWrapper extends IDhApiUnsafeWrapper */ IDhApiCustomRenderRegister getRenderRegister(); + /** + * Returns the folder Distant Horizons uses to save + * data associated with this level. + * Will return null if the level is not loaded. + * + * @since API 4.0.0 + */ + File getDhSaveFolder(); + + + } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/structure/ClientOnlySaveStructure.java b/core/src/main/java/com/seibel/distanthorizons/core/file/structure/ClientOnlySaveStructure.java index 610f504df..c90e21408 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/structure/ClientOnlySaveStructure.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/structure/ClientOnlySaveStructure.java @@ -22,7 +22,6 @@ package com.seibel.distanthorizons.core.file.structure; import com.google.common.net.PercentEscaper; import com.seibel.distanthorizons.api.interfaces.override.levelHandling.IDhApiSaveStructure; import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; -import com.seibel.distanthorizons.core.file.subDimMatching.SubDimensionLevelMatcher; import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.api.enums.config.EDhApiServerFolderNameMode; import com.seibel.distanthorizons.core.level.IServerKeyedClientLevel; @@ -30,7 +29,6 @@ import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.util.objects.ParsedIp; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftSharedWrapper; -import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; import com.seibel.distanthorizons.coreapi.DependencyInjection.OverrideInjector; import com.seibel.distanthorizons.coreapi.util.StringUtil; @@ -38,6 +36,7 @@ import org.apache.logging.log4j.Logger; import java.io.File; import java.util.*; +import java.util.concurrent.ConcurrentHashMap; /** * Designed for the Client_Only environment. @@ -54,8 +53,7 @@ public class ClientOnlySaveStructure implements ISaveStructure private static final IMinecraftSharedWrapper MC_SHARED = SingletonInjector.INSTANCE.get(IMinecraftSharedWrapper.class); - private final File folder; - private final HashMap levelWrapperToFileMap = new HashMap<>(); + private final ConcurrentHashMap levelWrapperToFileMap = new ConcurrentHashMap<>(); @@ -63,21 +61,7 @@ public class ClientOnlySaveStructure implements ISaveStructure // constructor // //=============// - public ClientOnlySaveStructure() - { - - - this.folder = new File(getSaveStructureFolderPath()); - - if (!this.folder.exists()) - { - if (!this.folder.mkdirs()) - { - LOGGER.warn("Unable to create folder [" + this.folder.getPath() + "]"); - //TODO: Deal with errors - } - } - } + public ClientOnlySaveStructure() { } @@ -85,47 +69,6 @@ public class ClientOnlySaveStructure implements ISaveStructure // folder methods // //================// - private File getLevelFolderWithoutSimilarityMatching(ILevelWrapper level) - { - List folders = this.getDhDataFoldersForLevel(level); - if (!folders.isEmpty() && folders.get(0) != null) - { - // use the first existing sub-dimension - String folderName = folders.get(0).getName(); - LOGGER.info("Default Sub Dimension set to: [" + StringUtil.shortenString(folderName, 8) + "...]"); - return folders.get(0); - } - else - { - // no valid sub dimension was found, create a new one - LOGGER.info("Default Sub Dimension not found. Creating: [" + level.getDimensionName() + "]"); - return new File(this.folder, level.getDimensionName().replaceAll(":", "@@")); - } - } - - public List getDhDataFoldersForLevel(ILevelWrapper level) - { - File[] folders = this.folder.listFiles(); - if (folders == null) - { - return new ArrayList<>(0); - } - - // filter by dimension name - String expectedDimName = level.getDimensionName(); - ArrayList possibleDimFolders = new ArrayList<>(); - for (File dimFolder : folders) - { - if (dimFolder.isDirectory() && dimFolder.getName().equals(expectedDimName)) - { - possibleDimFolders.addAll(getValidDhDimensionFolders(dimFolder)); - } - } - - return possibleDimFolders; - } - - @Override public File getSaveFolder(ILevelWrapper levelWrapper) { @@ -139,12 +82,12 @@ public class ClientOnlySaveStructure implements ISaveStructure IServerKeyedClientLevel keyedClientLevel = (IServerKeyedClientLevel) newLevelWrapper; LOGGER.info("Loading level [" + newLevelWrapper.getDimensionName() + "] with key: [" + keyedClientLevel.getServerLevelKey() + "]."); // This world was identified by the server directly, so we can know for sure which folder to use. - saveFolder = new File(getSaveStructureFolderPath() + File.separatorChar + keyedClientLevel.getServerLevelKey().replaceAll(":", "@@")); + saveFolder = new File(getDefaultSaveStructureFolderPath() + File.separatorChar + keyedClientLevel.getServerLevelKey().replaceAll(":", "@@")); } else { // get the default folder - saveFolder = this.getLevelFolderWithoutSimilarityMatching(newLevelWrapper);; + saveFolder = new File(getDefaultSaveStructureFolderPath()); } // Allow API users to override the save folder @@ -217,7 +160,7 @@ public class ClientOnlySaveStructure implements ISaveStructure } - private static String getSaveStructureFolderPath() + private static String getDefaultSaveStructureFolderPath() { String path = MC_SHARED.getInstallationDirectory().getPath() + File.separatorChar + SERVER_DATA_FOLDER_NAME + File.separatorChar @@ -286,6 +229,6 @@ public class ClientOnlySaveStructure implements ISaveStructure public void close() { } @Override - public String toString() { return "[" + this.getClass().getSimpleName() + "@" + this.folder.getName() + "]"; } + public String toString() { return "[" + this.getClass().getSimpleName() + "@(" + StringUtil.join(";", this.levelWrapperToFileMap.values()) + ")]"; } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/structure/LocalSaveStructure.java b/core/src/main/java/com/seibel/distanthorizons/core/file/structure/LocalSaveStructure.java index 9889f7e73..2e35b775a 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/structure/LocalSaveStructure.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/structure/LocalSaveStructure.java @@ -25,20 +25,20 @@ import com.seibel.distanthorizons.core.world.EWorldEnvironment; import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper; import com.seibel.distanthorizons.coreapi.DependencyInjection.OverrideInjector; +import com.seibel.distanthorizons.coreapi.util.StringUtil; import org.apache.logging.log4j.Logger; import java.io.File; +import java.util.concurrent.ConcurrentHashMap; /** * Designed for {@link EWorldEnvironment#CLIENT_SERVER} & {@link EWorldEnvironment#SERVER_ONLY} environments. - * - * @version 2022-12-17 */ public class LocalSaveStructure implements ISaveStructure { private static final Logger LOGGER = DhLoggerBuilder.getLogger(); - private File debugPath = new File(""); + private final ConcurrentHashMap levelWrapperToFileMap = new ConcurrentHashMap<>(); @@ -53,24 +53,26 @@ public class LocalSaveStructure implements ISaveStructure @Override public File getSaveFolder(ILevelWrapper levelWrapper) { - IServerLevelWrapper serverLevelWrapper = (IServerLevelWrapper) levelWrapper; - this.debugPath = serverLevelWrapper.getSaveFolder(); - File saveFolder = serverLevelWrapper.getSaveFolder(); - - - // Allow API users to override the save folder - IDhApiSaveStructure saveStructureOverride = OverrideInjector.INSTANCE.get(IDhApiSaveStructure.class); - if (saveStructureOverride != null) + return this.levelWrapperToFileMap.computeIfAbsent(levelWrapper, (newLevelWrapper) -> { - File overrideFile = saveStructureOverride.overrideFilePath(saveFolder, levelWrapper); - if (overrideFile != null) + IServerLevelWrapper serverLevelWrapper = (IServerLevelWrapper) levelWrapper; + File saveFolder = serverLevelWrapper.getMcSaveFolder(); + + + // Allow API users to override the save folder + IDhApiSaveStructure saveStructureOverride = OverrideInjector.INSTANCE.get(IDhApiSaveStructure.class); + if (saveStructureOverride != null) { - LOGGER.info("Save folder overridden from ["+saveFolder.getPath()+"] -> ["+overrideFile.getPath()+"]."); - saveFolder = overrideFile; + File overrideFile = saveStructureOverride.overrideFilePath(saveFolder, levelWrapper); + if (overrideFile != null) + { + LOGGER.info("Save folder overridden from [" + saveFolder.getPath() + "] -> [" + overrideFile.getPath() + "]."); + saveFolder = overrideFile; + } } - } - - return saveFolder; + + return saveFolder; + }); } @@ -83,6 +85,7 @@ public class LocalSaveStructure implements ISaveStructure public void close() throws Exception { } @Override - public String toString() { return "[" + this.getClass().getSimpleName() + "@" + this.debugPath + "]"; } + public String toString() + { return "[" + this.getClass().getSimpleName() + "@(" + StringUtil.join(";", this.levelWrapperToFileMap.values()) + ")]"; } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/IServerLevelWrapper.java b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/IServerLevelWrapper.java index dcc1ed1e5..64577497d 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/IServerLevelWrapper.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/IServerLevelWrapper.java @@ -24,7 +24,7 @@ import java.io.File; public interface IServerLevelWrapper extends ILevelWrapper { - File getSaveFolder(); + File getMcSaveFolder(); default String getKeyedLevelDimensionName() { diff --git a/core/src/test/java/testItems/worldGeneratorInjection/objects/LevelWrapperTest.java b/core/src/test/java/testItems/worldGeneratorInjection/objects/LevelWrapperTest.java index 0bcbcaa8d..334670bdc 100644 --- a/core/src/test/java/testItems/worldGeneratorInjection/objects/LevelWrapperTest.java +++ b/core/src/test/java/testItems/worldGeneratorInjection/objects/LevelWrapperTest.java @@ -24,6 +24,8 @@ import com.seibel.distanthorizons.api.interfaces.render.IDhApiCustomRenderRegist import com.seibel.distanthorizons.api.interfaces.world.IDhApiDimensionTypeWrapper; import com.seibel.distanthorizons.api.interfaces.world.IDhApiLevelWrapper; +import java.io.File; + /** * Stub implementation of a Level wrapper for basic unit testing. * @@ -59,6 +61,10 @@ public class LevelWrapperTest implements IDhApiLevelWrapper @Override public IDhApiCustomRenderRegister getRenderRegister() { return null; } + @Override + public File getDhSaveFolder() + { return null; } + }