From 368541b09c72c8e9d6c95fbe0f4d471b12ced5cd Mon Sep 17 00:00:00 2001 From: Cailin Smith Date: Sun, 25 Jun 2023 22:22:23 +0200 Subject: [PATCH] Fix small bugs + make level map concurrent to prevent exceptions --- .../structure/ClientOnlySaveStructure.java | 84 +++++++++---------- .../distanthorizons/core/level/DhLevel.java | 11 ++- .../core/world/DhClientWorld.java | 30 +++---- 3 files changed, 65 insertions(+), 60 deletions(-) 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 3186a54a5..3ad53c5ca 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 @@ -18,7 +18,7 @@ import java.util.stream.Stream; /** * Designed for the Client_Only environment. - * + * * @version 12-17-2022 */ public class ClientOnlySaveStructure extends AbstractSaveStructure @@ -26,17 +26,17 @@ public class ClientOnlySaveStructure extends AbstractSaveStructure final File folder; private static final IMinecraftClientWrapper MC_CLIENT = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class); public static final String INVALID_FILE_CHARACTERS_REGEX = "[\\\\/:*?\"<>|]"; - + SubDimensionLevelMatcher fileMatcher = null; final HashMap levelToFileMap = new HashMap<>(); - - - + + + public ClientOnlySaveStructure() { this.folder = new File(MC_CLIENT.getGameDirectory().getPath() + File.separatorChar + "Distant_Horizons_server_data" + File.separatorChar + getServerFolderName()); - + if (!this.folder.exists()) { if (!this.folder.mkdirs()) @@ -46,13 +46,13 @@ public class ClientOnlySaveStructure extends AbstractSaveStructure } } } - - - + + + //================// // folder methods // //================// - + @Override public File getLevelFolder(ILevelWrapper level) { @@ -67,14 +67,14 @@ public class ClientOnlySaveStructure extends AbstractSaveStructure } return this.getLevelFolderWithoutSimilarityMatching(newLevel); } - + if (this.fileMatcher == null || !this.fileMatcher.isFindingLevel(newLevel)) { LOGGER.info("Loading level for world " + newLevel.getDimensionType().getDimensionName()); this.fileMatcher = new SubDimensionLevelMatcher(newLevel, this.folder, this.getMatchingLevelFolders(newLevel).toArray(new File[0] /* surprisingly we don't need to create an array of any specific size for this to work */)); } - + File levelFile = this.fileMatcher.tryGetLevel(); if (levelFile != null) { @@ -84,23 +84,23 @@ public class ClientOnlySaveStructure extends AbstractSaveStructure return levelFile; }); } - + private File getLevelFolderWithoutSimilarityMatching(ILevelWrapper level) { List folders = this.getMatchingLevelFolders(level); - if (folders.size() > 0 && folders.get(0) == null) + if (!folders.isEmpty() && folders.get(0) != null) { LOGGER.info("Default Sub Dimension set to: [" + LodUtil.shortenString(folders.get(0).getName(), 8) + "...]"); return folders.get(0); } else - { + { // if no valid sub dimension was found, create a new one LOGGER.info("Default Sub Dimension not found. Creating: [" + level.getDimensionType().getDimensionName() + "]"); return new File(this.folder, level.getDimensionType().getDimensionName()); } } - + public List getMatchingLevelFolders(@Nullable ILevelWrapper level) { File[] folders = this.folder.listFiles(); @@ -108,9 +108,9 @@ public class ClientOnlySaveStructure extends AbstractSaveStructure { return new ArrayList<>(0); } - + Stream fileStream = Arrays.stream(folders).filter( - (folder) -> + (folder) -> { if (!isValidLevelFolder(folder)) { @@ -122,10 +122,10 @@ public class ClientOnlySaveStructure extends AbstractSaveStructure } } ).sorted(); - + return fileStream.collect(Collectors.toList()); } - + @Override public File getRenderCacheFolder(ILevelWrapper level) { @@ -134,10 +134,10 @@ public class ClientOnlySaveStructure extends AbstractSaveStructure { return null; } - + return new File(levelFolder, RENDER_CACHE_FOLDER); } - + @Override public File getFullDataFolder(ILevelWrapper level) { @@ -146,16 +146,16 @@ public class ClientOnlySaveStructure extends AbstractSaveStructure { return null; } - + return new File(levelFolder, DATA_FOLDER); } - - - + + + //================// // helper methods // //================// - + /** Returns true if the given folder holds valid Lod Dimension data */ private static boolean isValidLevelFolder(File potentialFolder) { @@ -164,16 +164,16 @@ public class ClientOnlySaveStructure extends AbstractSaveStructure // a valid level folder needs to be a folder return false; } - + // filter out any non-DH folders File[] files = potentialFolder.listFiles((file) -> file.isDirectory() && (file.getName().equalsIgnoreCase(RENDER_CACHE_FOLDER) || file.getName().equalsIgnoreCase(DATA_FOLDER))); - + // a valid level folder needs to have DH specific folders in it return files != null && files.length != 0; } - + /** Generated from the server the client is currently connected to. */ private static String getServerFolderName() { @@ -181,14 +181,14 @@ public class ClientOnlySaveStructure extends AbstractSaveStructure ParsedIp parsedIp = new ParsedIp(MC_CLIENT.getCurrentServerIp()); String serverIpCleaned = parsedIp.ip.replaceAll(INVALID_FILE_CHARACTERS_REGEX, ""); String serverPortCleaned = parsedIp.port != null ? parsedIp.port.replaceAll(INVALID_FILE_CHARACTERS_REGEX, "") : ""; - - + + // determine the auto folder name format EServerFolderNameMode folderNameMode = Config.Client.Advanced.Multiplayer.serverFolderNameMode.get(); String serverName = MC_CLIENT.getCurrentServerName().replaceAll(INVALID_FILE_CHARACTERS_REGEX, ""); String serverMcVersion = MC_CLIENT.getCurrentServerVersion().replaceAll(INVALID_FILE_CHARACTERS_REGEX, ""); - - + + // generate the folder name String folderName; switch (folderNameMode) @@ -197,7 +197,7 @@ public class ClientOnlySaveStructure extends AbstractSaveStructure case NAME_ONLY: folderName = serverName; break; - + case NAME_IP: folderName = serverName + ", IP " + serverIpCleaned; break; @@ -208,22 +208,22 @@ public class ClientOnlySaveStructure extends AbstractSaveStructure folderName = serverName + ", IP " + serverIpCleaned + (serverPortCleaned.length() != 0 ? ("-" + serverPortCleaned) : "") + ", GameVersion " + serverMcVersion; break; } - + // PercentEscaper makes the characters all part of the standard alphameric character set // This fixes some issues when the server is named something in other languages return new PercentEscaper("", true).escape(folderName); } - - - + + + //==================// // override methods // //==================// - + @Override public void close() { this.fileMatcher.close(); } - + @Override public String toString() { return "[" + this.getClass().getSimpleName() + "@" + this.folder.getName() + "]"; } - + } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhLevel.java index 2c5dcef77..94af05afb 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhLevel.java @@ -28,8 +28,13 @@ public abstract class DhLevel implements IDhLevel { CompletableFuture future = this.chunkToLodBuilder.tryGenerateData(chunk); if (future != null) { - future.thenAccept((chunkSizedFullDataAccessor) -> - { + future.thenAccept((chunkSizedFullDataAccessor) -> + { + if (chunkSizedFullDataAccessor == null) { + // This can happen if, among other reasons, a chunk save is superceded by a later event + return; + } + this.saveWrites(chunkSizedFullDataAccessor); ApiEventInjector.INSTANCE.fireAllEvents( DhApiChunkModifiedEvent.class, @@ -40,5 +45,5 @@ public abstract class DhLevel implements IDhLevel { @Override public void close() { this.chunkToLodBuilder.close(); } - + } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientWorld.java b/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientWorld.java index 08b12a129..1c9126f19 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientWorld.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientWorld.java @@ -9,31 +9,31 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapp import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; import java.io.File; -import java.util.HashMap; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutorService; public class DhClientWorld extends AbstractDhWorld implements IDhClientWorld { - private final HashMap levels; + private final ConcurrentHashMap levels; public final ClientOnlySaveStructure saveStructure; - + // TODO why does this executor have 2 threads? public ExecutorService dhTickerThread = ThreadUtil.makeSingleThreadPool("DH Client World Ticker Thread", 2); public EventLoop eventLoop = new EventLoop(this.dhTickerThread, this::_clientTick); - - - + + + public DhClientWorld() { super(EWorldEnvironment.Client_Only); this.saveStructure = new ClientOnlySaveStructure(); - this.levels = new HashMap<>(); + this.levels = new ConcurrentHashMap<>(); LOGGER.info("Started DhWorld of type "+this.environment); } - - - + + + @Override public DhClientLevel getOrLoadLevel(ILevelWrapper wrapper) { @@ -61,10 +61,10 @@ public class DhClientWorld extends AbstractDhWorld implements IDhClientWorld { return null; } - + return this.levels.get(wrapper); } - + @Override public Iterable getAllLoadedLevels() { return this.levels.values(); } @@ -75,7 +75,7 @@ public class DhClientWorld extends AbstractDhWorld implements IDhClientWorld { return; } - + if (this.levels.containsKey(wrapper)) { LOGGER.info("Unloading level "+this.levels.get(wrapper)); @@ -105,10 +105,10 @@ public class DhClientWorld extends AbstractDhWorld implements IDhClientWorld LOGGER.info("Unloading level " + dhClientLevel.getLevelWrapper().getDimensionType().getDimensionName()); dhClientLevel.close(); } - + this.levels.clear(); this.eventLoop.close(); LOGGER.info("Closed DhWorld of type "+this.environment); } - + }