Fix small bugs + make level map concurrent to prevent exceptions

This commit is contained in:
Cailin Smith
2023-06-25 22:22:23 +02:00
parent d04b4c0d55
commit 368541b09c
3 changed files with 65 additions and 60 deletions
@@ -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<ILevelWrapper, File> 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<File> 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<File> getMatchingLevelFolders(@Nullable ILevelWrapper level)
{
File[] folders = this.folder.listFiles();
@@ -108,9 +108,9 @@ public class ClientOnlySaveStructure extends AbstractSaveStructure
{
return new ArrayList<>(0);
}
Stream<File> 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() + "]"; }
}
@@ -28,8 +28,13 @@ public abstract class DhLevel implements IDhLevel {
CompletableFuture<ChunkSizedFullDataAccessor> 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(); }
}
@@ -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<IClientLevelWrapper, DhClientLevel> levels;
private final ConcurrentHashMap<IClientLevelWrapper, DhClientLevel> 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<? extends IDhLevel> 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);
}
}