From 621bf7341dd435872c51fad17fdf6f9a5d5396c0 Mon Sep 17 00:00:00 2001 From: James Seibel Date: Wed, 23 Mar 2022 20:58:50 -0500 Subject: [PATCH] Improve logging and potentially fix a few substring crashes --- .../com/seibel/lod/core/api/ClientApi.java | 13 +- .../com/seibel/lod/core/api/EventApi.java | 6 +- .../handlers/LodDimensionFileHandler.java | 74 +++--- .../core/handlers/LodDimensionFileHelper.java | 250 +++++++++++++++--- .../lod/core/objects/lod/LodDimension.java | 8 +- .../com/seibel/lod/core/util/LodUtil.java | 18 ++ .../block/AbstractBlockPosWrapper.java | 6 + 7 files changed, 302 insertions(+), 73 deletions(-) diff --git a/src/main/java/com/seibel/lod/core/api/ClientApi.java b/src/main/java/com/seibel/lod/core/api/ClientApi.java index e2fc94eb9..fe69e3e4d 100644 --- a/src/main/java/com/seibel/lod/core/api/ClientApi.java +++ b/src/main/java/com/seibel/lod/core/api/ClientApi.java @@ -27,6 +27,7 @@ import java.util.List; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; +import com.seibel.lod.core.handlers.LodDimensionFileHelper; import org.lwjgl.glfw.GLFW; import com.seibel.lod.core.ModInfo; @@ -100,11 +101,17 @@ public class ClientApi public boolean rendererDisabledBecauseOfExceptions = false; + + + private ClientApi() { } + + + private void flushSpamLoggersState() { synchronized(spamReducedLoggers) { spamReducedLoggers.removeIf((logger) -> logger.get()==null); @@ -158,9 +165,13 @@ public class ClientApi return; IWorldWrapper world = MC.getWrappedClientWorld(); - if (world == null) return; + if (world == null) + return; LodDimension lodDim = ApiShared.lodWorld.getLodDimension(world.getDimensionType()); + // Make sure the player's data is up-to-date + LodDimensionFileHelper.updatePlayerData(); + // Make the LodDim if it does not exist if (lodDim == null) { diff --git a/src/main/java/com/seibel/lod/core/api/EventApi.java b/src/main/java/com/seibel/lod/core/api/EventApi.java index 857a639ea..2de0915fc 100644 --- a/src/main/java/com/seibel/lod/core/api/EventApi.java +++ b/src/main/java/com/seibel/lod/core/api/EventApi.java @@ -20,6 +20,7 @@ package com.seibel.lod.core.api; import com.seibel.lod.core.api.ClientApi.LagSpikeCatcher; +import com.seibel.lod.core.handlers.LodDimensionFileHelper; import com.seibel.lod.core.objects.opengl.builders.lodBuilding.LodBuilder; import com.seibel.lod.core.objects.opengl.builders.worldGeneration.BatchGenerator; import com.seibel.lod.core.enums.WorldType; @@ -59,6 +60,9 @@ public class EventApi */ private boolean recalculateWidths = false; + private boolean isCurrentlyOnSinglePlayerServer = false; + + private EventApi() { @@ -113,8 +117,6 @@ public class EventApi ApiShared.lodWorld.saveAllDimensions(false); // Do an async save. } - private boolean isCurrentlyOnSinglePlayerServer = false; - /** This is also called when a new dimension loads */ public void worldLoadEvent(IWorldWrapper world) { diff --git a/src/main/java/com/seibel/lod/core/handlers/LodDimensionFileHandler.java b/src/main/java/com/seibel/lod/core/handlers/LodDimensionFileHandler.java index 33e9a1efe..af99278e9 100644 --- a/src/main/java/com/seibel/lod/core/handlers/LodDimensionFileHandler.java +++ b/src/main/java/com/seibel/lod/core/handlers/LodDimensionFileHandler.java @@ -33,6 +33,7 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.locks.ReentrantLock; +import com.seibel.lod.core.ModInfo; import com.seibel.lod.core.api.ApiShared; import org.apache.commons.compress.compressors.xz.XZCompressorInputStream; import org.apache.commons.compress.compressors.xz.XZCompressorOutputStream; @@ -48,6 +49,8 @@ import com.seibel.lod.core.util.LodThreadFactory; import com.seibel.lod.core.util.LodUtil; import com.seibel.lod.core.util.SpamReducedLogger; import com.seibel.lod.core.util.UnitBytes; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; /** @@ -61,6 +64,8 @@ import com.seibel.lod.core.util.UnitBytes; */ public class LodDimensionFileHandler { + public static final Logger LOGGER = LogManager.getLogger("LodDimensionFileHandler"); + public static final boolean ENABLE_SAVE_THREAD_LOGGING = true; public static final boolean ENABLE_SAVE_REGION_LOGGING = false; @@ -147,26 +152,26 @@ public class LodDimensionFileHandler subFile.getName().equals(DistanceGenerationMode.BIOME_ONLY.toString()) || subFile.getName().equals(DistanceGenerationMode.NONE.toString())) { - ApiShared.LOGGER.info("Noticed old save structure files. Starting merge process..."); + LOGGER.info("Noticed old save structure files. Starting merge process..."); LodDimensionOldFileStructureHandler oldFileStructHandler = new LodDimensionOldFileStructureHandler(this); if (mergeOldFileLock.tryLock()) { // I got the lock to merge file. - ApiShared.LOGGER.info("Updating VerticalQuality LOW..."); + LOGGER.info("Updating VerticalQuality LOW..."); oldFileStructHandler.mergeOldFileStructureForVertQuality(VerticalQuality.LOW); - ApiShared.LOGGER.info("Updating VerticalQuality MEDIUM..."); + LOGGER.info("Updating VerticalQuality MEDIUM..."); oldFileStructHandler.mergeOldFileStructureForVertQuality(VerticalQuality.MEDIUM); - ApiShared.LOGGER.info("Updating VerticalQuality HIGH..."); + LOGGER.info("Updating VerticalQuality HIGH..."); oldFileStructHandler.mergeOldFileStructureForVertQuality(VerticalQuality.HIGH); - ApiShared.LOGGER.info("Update completed."); + LOGGER.info("Update completed."); } else { - // Someone is already doing it. I just need to wait until he is done. + // Someone is already doing it. I just need to wait until they are done. mergeOldFileLock.lock(); mergeOldFileLock.unlock(); } - ApiShared.LOGGER.info("Merge process completed."); + LOGGER.info("Merge process completed."); return; } } @@ -238,7 +243,7 @@ public class LodDimensionFileHandler // close the reader and delete the file. inputStream.close(); file.delete(); - ApiShared.LOGGER.info("Outdated LOD region file for region: (" + regionX + "," + regionZ + ")" + LOGGER.info("Outdated LOD region file for region: (" + regionX + "," + regionZ + ")" + " version found: " + fileVersion + ", version requested: " + LOD_SAVE_FILE_VERSION + ". File has been deleted."); @@ -251,7 +256,7 @@ public class LodDimensionFileHandler // close the reader and ignore the file, we don't // want to accidentally delete anything the user may want. inputStream.close(); - ApiShared.LOGGER.info("Newer LOD region file for region: (" + regionX + "," + regionZ + ")" + LOGGER.info("Newer LOD region file for region: (" + regionX + "," + regionZ + ")" + " version found: " + fileVersion + ", version requested: " + LOD_SAVE_FILE_VERSION + " this region will not be written to in order to protect the newer file."); @@ -260,7 +265,7 @@ public class LodDimensionFileHandler } else if (fileVersion < LOD_SAVE_FILE_VERSION) { - ApiShared.LOGGER.debug("Old LOD region file for region: (" + regionX + "," + regionZ + ")" + LOGGER.debug("Old LOD region file for region: (" + regionX + "," + regionZ + ")" + " version found: " + fileVersion + ", version requested: " + LOD_SAVE_FILE_VERSION + ". File will be loaded and updated to new format in next save."); @@ -283,7 +288,7 @@ public class LodDimensionFileHandler } catch (IOException ioEx) { - ApiShared.LOGGER.error("LOD file read error. Unable to read xz compressed file [" + file + "]: ", ioEx); + LOGGER.error("LOD file read error. Unable to read xz compressed file [" + file + "]: ", ioEx); region.addLevelContainer(new VerticalLevelContainer(tempDetailLevel)); } } // for each detail level @@ -303,7 +308,7 @@ public class LodDimensionFileHandler FILE_NAME_PREFIX + "." + posX + "." + posZ + FILE_EXTENSION); if (file.exists()) { - ApiShared.LOGGER.warn("LOD file write warn. Unable to write [" + file + "] because the newer version file already exist! Skipping this position..."); + LOGGER.warn("LOD file write warn. Unable to write [" + file + "] because the newer version file already exist! Skipping this position..."); return; } if (!file.getParentFile().exists()) @@ -314,7 +319,7 @@ public class LodDimensionFileHandler } catch (IOException e) { - ApiShared.LOGGER.error("LOD file write error. Unable to create parent directory for [" + file + "]: ", e); + LOGGER.error("LOD file write error. Unable to create parent directory for [" + file + "]: ", e); return; } try (FileOutputStream fileOutStream = new FileOutputStream(file)) @@ -330,7 +335,7 @@ public class LodDimensionFileHandler } catch (IOException e) { - ApiShared.LOGGER.error("LOD file write error. Unable to write to temp file [" + file + "]: ", e); + LOGGER.error("LOD file write error. Unable to write to temp file [" + file + "]: ", e); } } @@ -367,7 +372,7 @@ public class LodDimensionFileHandler LevelContainer[] container = r.debugGetDataContainers().clone(); if (container == null || container.length != LodUtil.DETAIL_OPTIONS) { - ApiShared.LOGGER.warn("DumpRamUsage encountered an invalid region!"); + LOGGER.warn("DumpRamUsage encountered an invalid region!"); continue; } for (int i = 0; i < LodUtil.DETAIL_OPTIONS; i++) @@ -395,6 +400,7 @@ public class LodDimensionFileHandler /** Save all dirty regions in this LodDimension to file */ public void saveDirtyRegionsToFile(boolean blockUntilFinished) { + // determine the regions to save for (int i = 0; i < lodDimension.getWidth(); i++) { for (int j = 0; j < lodDimension.getWidth(); j++) @@ -402,28 +408,34 @@ public class LodDimensionFileHandler LodRegion r = lodDimension.getRegionByArrayIndex(i, j); // FIXME: Note that the isWriting is a crude attempt at syncing. It won't work. - // It just reduce the chance of race condition + // It just reduces the chance of a race condition if (r != null && r.needSaving) { regionToSave.put(r.getRegionPos(), r); } } } + + // save the dimension data + LodDimensionFileHelper.saveDimensionPlayerData(this.dimensionDataSaveFolder); trySaveRegionsToBeSaved(); + + // wait for the saving to finish if requested if (blockUntilFinished) { if (ENABLE_SAVE_THREAD_LOGGING) - ApiShared.LOGGER.info("Blocking until lod file save finishes!"); + LOGGER.info("Blocking until lod file save finishes!"); + try { fileWritingThreadPool.shutdown(); boolean worked = fileWritingThreadPool.awaitTermination(30, TimeUnit.SECONDS); if (!worked) - ApiShared.LOGGER.error("File writing timed out! File data may not be saved correctly and may cause corruptions!!!"); + LOGGER.error("File writing timed out! File data may not be saved correctly and may cause corruptions!!!"); } catch (InterruptedException e) { - ApiShared.LOGGER.error("File writing wait is interrupted! File data may not be saved correctly and may cause corruptions!!!: ", e); + LOGGER.error("File writing wait is interrupted! File data may not be saved correctly and may cause corruptions!!!: ", e); } finally { @@ -461,7 +473,7 @@ public class LodDimensionFileHandler throw new ConcurrentModificationException("WriterMain Triggered but the thead state is not started!?"); if (ENABLE_SAVE_THREAD_LOGGING) - ApiShared.LOGGER.info("Lod File Writer started. To-be-written-regions: " + regionToSave.size()); + LOGGER.info("Lod File Writer started. To-be-written-regions: " + regionToSave.size()); Instant start = Instant.now(); // Note: Since regionToSave is a ConcurrentHashMap, and the .values() return one that support concurrency, @@ -481,16 +493,16 @@ public class LodDimensionFileHandler r.needSaving = false; Instant i = Instant.now(); if (ENABLE_SAVE_REGION_LOGGING) - ApiShared.LOGGER.info("Lod: Saving Region " + r.getRegionPos()); + LOGGER.info("Lod: Saving Region " + r.getRegionPos()); saveRegionToFile(r); Instant j = Instant.now(); Duration d = Duration.between(i, j); if (ENABLE_SAVE_REGION_LOGGING) - ApiShared.LOGGER.info("Lod: Region " + r.getRegionPos() + " save finish. Took " + d); + LOGGER.info("Lod: Region " + r.getRegionPos() + " save finish. Took " + d); } catch (Exception e) { - ApiShared.LOGGER.error("Lod: UNCAUGHT exception when saving region " + r.getRegionPos() + ": ", e); + LOGGER.error("Lod: UNCAUGHT exception when saving region " + r.getRegionPos() + ": ", e); } finally { @@ -501,7 +513,7 @@ public class LodDimensionFileHandler Instant end = Instant.now(); if (ENABLE_SAVE_THREAD_LOGGING) - ApiShared.LOGGER.info("Lod File Writer completed. Took " + Duration.between(start, end)); + LOGGER.info("Lod File Writer completed. Took " + Duration.between(start, end)); // Use Memory order Release to release any memory changes on setting this boolean // (Corresponding call is the this::saveRegions(...)::...compareAndExchangeAcquire(false, true);) @@ -525,7 +537,7 @@ public class LodDimensionFileHandler // Get the old file File oldFile = getRegionFile(region.regionPosX, region.regionPosZ, detailLevel, region.getVerticalQuality()); if (ENABLE_SAVE_REGION_LOGGING) - ApiShared.LOGGER.debug("saving region [" + region.regionPosX + ", " + region.regionPosZ + "] detail " + detailLevel + " to file."); + LOGGER.debug("saving region [" + region.regionPosX + ", " + region.regionPosZ + "] detail " + detailLevel + " to file."); boolean isFileFullyGened = false; // make sure the file and folder exists @@ -542,7 +554,7 @@ public class LodDimensionFileHandler } catch (IOException e) { - ApiShared.LOGGER.error("LOD file write error. Unable to create parent directory for [" + oldFile + "] error [" + e.getMessage() + "]: "); + LOGGER.error("LOD file write error. Unable to create parent directory for [" + oldFile + "] error [" + e.getMessage() + "]: "); e.printStackTrace(); continue; } @@ -564,7 +576,7 @@ public class LodDimensionFileHandler } catch (IOException e) { - ApiShared.LOGGER.warn("LOD file write warning. Unable to read existing file [" + oldFile + "] version. Treating it as latest version. [" + e.getMessage() + "]: "); + LOGGER.warn("LOD file write warning. Unable to read existing file [" + oldFile + "] version. Treating it as latest version. [" + e.getMessage() + "]: "); e.printStackTrace(); } @@ -598,7 +610,7 @@ public class LodDimensionFileHandler // existing file is complete while new one is only partially generate // this can happen is for some reason loading failed // this doesn't fix the bug, but at least protects old data - ApiShared.LOGGER.error("LOD file write error. Attempted to overwrite complete region with incomplete one [" + oldFile + "]"); + LOGGER.error("LOD file write error. Attempted to overwrite complete region with incomplete one [" + oldFile + "]"); try { tempFile.delete(); @@ -612,7 +624,7 @@ public class LodDimensionFileHandler } catch (IOException e) { - ApiShared.LOGGER.error("LOD file write error. Unable to write to temp file [" + tempFile + "]: ", e); + LOGGER.error("LOD file write error. Unable to write to temp file [" + tempFile + "]: ", e); continue; } @@ -623,7 +635,7 @@ public class LodDimensionFileHandler } catch (IOException e) { - ApiShared.LOGGER.error("LOD file write error. Unable to update file [" + oldFile + "]: ", e); + LOGGER.error("LOD file write error. Unable to update file [" + oldFile + "]: ", e); } } } @@ -645,7 +657,7 @@ public class LodDimensionFileHandler } catch (IOException e) { - ApiShared.LOGGER.warn("Unable to get the base save file path. One possible cause is that" + LOGGER.warn("Unable to get the base save file path. One possible cause is that" + " the process failed to read the current path location due to security configs."); throw new RuntimeException("DistantHorizons Get Save File Path Failure"); } diff --git a/src/main/java/com/seibel/lod/core/handlers/LodDimensionFileHelper.java b/src/main/java/com/seibel/lod/core/handlers/LodDimensionFileHelper.java index 9a243cbf0..1c83fb8d0 100644 --- a/src/main/java/com/seibel/lod/core/handlers/LodDimensionFileHelper.java +++ b/src/main/java/com/seibel/lod/core/handlers/LodDimensionFileHelper.java @@ -1,8 +1,9 @@ package com.seibel.lod.core.handlers; +import com.electronwill.nightconfig.core.file.CommentedFileConfig; import com.seibel.lod.core.api.ApiShared; -import com.seibel.lod.core.builders.lodBuilding.LodBuilder; -import com.seibel.lod.core.builders.lodBuilding.LodBuilderConfig; +import com.seibel.lod.core.objects.opengl.builders.lodBuilding.LodBuilder; +import com.seibel.lod.core.objects.opengl.builders.lodBuilding.LodBuilderConfig; import com.seibel.lod.core.enums.config.DistanceGenerationMode; import com.seibel.lod.core.enums.config.VerticalQuality; import com.seibel.lod.core.handlers.dependencyInjection.SingletonHandler; @@ -10,6 +11,8 @@ import com.seibel.lod.core.objects.lod.LodDimension; import com.seibel.lod.core.objects.lod.LodRegion; import com.seibel.lod.core.objects.lod.RegionPos; import com.seibel.lod.core.util.LodUtil; +import com.seibel.lod.core.wrapperInterfaces.IWrapperFactory; +import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper; import com.seibel.lod.core.wrapperInterfaces.chunk.AbstractChunkPosWrapper; import com.seibel.lod.core.wrapperInterfaces.chunk.IChunkWrapper; import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton; @@ -30,9 +33,15 @@ public class LodDimensionFileHelper { private static final IMinecraftClientWrapper MC = SingletonHandler.get(IMinecraftClientWrapper.class); private static final ILodConfigWrapperSingleton CONFIG = SingletonHandler.get(ILodConfigWrapperSingleton.class); + private static final IWrapperFactory FACTORY = SingletonHandler.get(IWrapperFactory.class); /** Increasing this will increase accuracy but increase calculation time */ - private static final VerticalQuality VERTICAL_QUALITY_TO_TEST_WITH = VerticalQuality.LOW; + private static final VerticalQuality VERTICAL_QUALITY_TO_TEST_WITH = VerticalQuality.MEDIUM; + + + private static LodDimensionFileHelper.PlayerData PLAYER_DATA = new LodDimensionFileHelper.PlayerData(MC); + private static LodDimensionFileHelper.PlayerData FIRST_SEEN_PLAYER_DATA = null; + /** @@ -42,13 +51,21 @@ public class LodDimensionFileHelper * @return the new or existing folder for this dimension, null if there was a problem * @throws IOException if the folder doesn't exist or can't be accessed */ - public static File determineSaveFolder() throws IOException + public static File determineSubDimensionFolder() throws IOException { + if (FIRST_SEEN_PLAYER_DATA == null) + { + FIRST_SEEN_PLAYER_DATA = PLAYER_DATA; + PLAYER_DATA = new LodDimensionFileHelper.PlayerData(MC); + } + + // relevant positions - AbstractChunkPosWrapper playerChunkPos = MC.getPlayerChunkPos(); + AbstractChunkPosWrapper playerChunkPos = FACTORY.createChunkPos(FIRST_SEEN_PLAYER_DATA.playerBlockPos); int startingBlockPosX = playerChunkPos.getMinBlockX(); int startingBlockPosZ = playerChunkPos.getMinBlockZ(); - RegionPos playerRegionPos = new RegionPos(MC.getPlayerChunkPos()); + RegionPos playerRegionPos = new RegionPos(playerChunkPos); + // chunk from the newly loaded dimension IChunkWrapper newlyLoadedChunk = MC.getWrappedClientWorld().tryGetChunk(playerChunkPos); @@ -56,7 +73,7 @@ public class LodDimensionFileHelper if (!LodDimensionFileHelper.CanDetermineDimensionFolder(newlyLoadedChunk)) return null; - // create a temporary dimension to store the new LOD + // create a temporary dimension to store the test LOD LodDimension newlyLoadedDim = new LodDimension(MC.getCurrentDimension(), null, 1); newlyLoadedDim.move(playerRegionPos); newlyLoadedDim.regions.set(playerRegionPos.x, playerRegionPos.z, new LodRegion(LodUtil.BLOCK_DETAIL_LEVEL, playerRegionPos, VERTICAL_QUALITY_TO_TEST_WITH)); @@ -66,6 +83,12 @@ public class LodDimensionFileHelper if (!lodGenerated) return null; + + // log the start of this attempt + ApiShared.LOGGER.info("Attempting to determine sub-dimension for [" + MC.getCurrentDimension().getDimensionName() + "]"); + ApiShared.LOGGER.info("First seen player block pos in dimension: [" + FIRST_SEEN_PLAYER_DATA.playerBlockPos.getX() + "," + FIRST_SEEN_PLAYER_DATA.playerBlockPos.getY() + "," + FIRST_SEEN_PLAYER_DATA.playerBlockPos.getZ() + "]"); + + // new chunk data long[][][] newChunkData = new long[LodUtil.CHUNK_WIDTH][LodUtil.CHUNK_WIDTH][]; for (int x = 0; x < LodUtil.CHUNK_WIDTH; x++) @@ -77,9 +100,6 @@ public class LodDimensionFileHelper } } boolean newChunkHasData = isDataEmpty(newChunkData); -// String message = "new chunk data " + (newChunkHasData ? newChunkData[0][0][0] : "[NULL]"); -// MC.sendChatMessage(message); -// ApiShared.LOGGER.info(message); // check if the chunk is actually empty if (!newChunkHasData) @@ -88,16 +108,16 @@ public class LodDimensionFileHelper { // the chunk isn't empty but the LOD is... -// String message = "Error: the chunk at (" + playerChunkPos.getX() + "," + playerChunkPos.getZ() + ") has a height of [" + newlyLoadedChunk.getHeight() + "] but the LOD generated is empty!"; -// MC.sendChatMessage(message); -// ApiShared.LOGGER.info(message); + String message = "Error: the chunk at (" + playerChunkPos.getX() + "," + playerChunkPos.getZ() + ") has a height of [" + newlyLoadedChunk.getHeight() + "] but the LOD generated is empty!"; + MC.sendChatMessage(message); + ApiShared.LOGGER.error(message); return null; } else { -// String message = "The chunk at (" + playerChunkPos.getX() + "," + playerChunkPos.getZ() + ") is empty."; + String message = "Warning: The chunk at (" + playerChunkPos.getX() + "," + playerChunkPos.getZ() + ") is empty."; // MC.sendChatMessage(message); -// ApiShared.LOGGER.info(message); + ApiShared.LOGGER.warn(message); } } @@ -114,21 +134,23 @@ public class LodDimensionFileHelper // create the directory since it doesn't exist dimensionFolder.mkdirs(); } - else - { - return null; - } } + // TODO move any old files if they exist + + // compare each world with the newly loaded one File mostSimilarWorldFolder = null; int mostEqualLines = 0; - boolean oneDimensionIsValid = false; + double highestEqualityPercent = 0.0; double minimumSimilarityRequired = CONFIG.client().multiplayer().getMultiDimensionRequiredSimilarity(); + ApiShared.LOGGER.info("Known Sub Dimensions: [" + dimensionFolder.listFiles().length + "]"); for (File testDimFolder : dimensionFolder.listFiles()) { + ApiShared.LOGGER.info("Testing sub dimension: [" + LodUtil.shortenString(testDimFolder.getName(), 8) + "]"); + // get a LOD from this dimension folder LodDimension tempLodDim = new LodDimension(null, null, 1); tempLodDim.move(playerRegionPos); @@ -145,15 +167,28 @@ public class LodDimensionFileHelper } } + + // get the player data for this dimension folder + PlayerData testPlayerData = new PlayerData(testDimFolder); + ApiShared.LOGGER.info("Last known player pos: [" + testPlayerData.playerBlockPos.getX() + "," + testPlayerData.playerBlockPos.getY() + "," + testPlayerData.playerBlockPos.getZ() + "]"); + + // check if the block positions are close + int distance = testPlayerData.playerBlockPos.getManhattanDistance(FIRST_SEEN_PLAYER_DATA.playerBlockPos); + ApiShared.LOGGER.info("Player block position distance between saved sub dimension and first seen is [" + distance + "]"); +// if (distance <= 2) // TODO make this number a config +// { +// // TODO do something with this information +// } + + // check if the chunk is actually empty if (!isDataEmpty(newChunkData)) { -// String message = "The test chunk for dimension folder [" + testDimFolder.getName() + "] and chunk pos (" + playerChunkPos.getX() + "," + playerChunkPos.getZ() + ") is empty. Is that correct?"; + String message = "The test chunk for dimension folder [" + LodUtil.shortenString(testDimFolder.getName(), 8) + "] and chunk pos (" + playerChunkPos.getX() + "," + playerChunkPos.getZ() + ") is empty. Is that correct?"; // MC.sendChatMessage(message); -// ApiShared.LOGGER.info(message); + ApiShared.LOGGER.info(message); continue; } - oneDimensionIsValid = true; // compare the two LODs @@ -177,28 +212,31 @@ public class LodDimensionFileHelper // determine if this world is closer to the newly loaded world double percentEqual = (double) equalLines / (double) totalLineCount; - if (equalLines > mostEqualLines && percentEqual >= minimumSimilarityRequired) + if (equalLines > mostEqualLines) { mostEqualLines = equalLines; - mostSimilarWorldFolder = testDimFolder; + highestEqualityPercent = percentEqual; + + if (percentEqual >= minimumSimilarityRequired) + { + mostSimilarWorldFolder = testDimFolder; + } } -// String message = "test data [" + testDimFolder.getName().substring(0, 6) + "...] " + testChunkData[0][0][0] + " equal lines: " + equalLines + "/" + totalLineCount + " = " + percentEqual; -// MC.sendChatMessage(message); -// ApiShared.LOGGER.info(message); + String message = "Sub dimension [" + LodUtil.shortenString(testDimFolder.getName(), 8) + "...] is current dimension probability: " + LodUtil.shortenString(percentEqual + "", 5) + " (" + equalLines + "/" + totalLineCount + ")"; + MC.sendChatMessage(message); + ApiShared.LOGGER.info(message); } - - if (!oneDimensionIsValid && dimensionFolder.listFiles().length != 0) - // all the world folders were empty, and there was at least one world folder that we tested - return null; + // the first seen player data is no longer needed, the sub dimension has been determined + FIRST_SEEN_PLAYER_DATA = null; if (mostSimilarWorldFolder != null) { // we found a world folder that is similar, use it - String message = "Dimension folder set to: [" + mostSimilarWorldFolder.getName().substring(0, 8) + "...]"; -// MC.sendChatMessage(message); + String message = "Sub Dimension set to: [" + LodUtil.shortenString(mostSimilarWorldFolder.getName(), 8) + "...] with an equality of [" + highestEqualityPercent + "]"; + MC.sendChatMessage(message); ApiShared.LOGGER.info(message); return mostSimilarWorldFolder; } @@ -207,8 +245,8 @@ public class LodDimensionFileHelper // no world folder was found, create a new one String newId = UUID.randomUUID().toString(); - String message = "No dimension folder found. Creating a new one with ID: " + newId.substring(0, 8) + "..."; -// MC.sendChatMessage(message); + String message = "No suitable sub dimension found. The highest equality was [" + highestEqualityPercent + "]. Creating a new sub dimension with ID: " + LodUtil.shortenString(newId, 8) + "..."; + MC.sendChatMessage(message); ApiShared.LOGGER.info(message); return GetDimensionFolder(newlyLoadedDim.dimension, newId); } @@ -276,4 +314,144 @@ public class LodDimensionFileHelper return false; } + + + + + + + private static final String playerDataFileName = "_playerData.toml"; + + private static PlayerData getPlayerData(File worldFolder) + { + File file = PlayerData.getFileForDimensionFolder(worldFolder); + if (!file.exists()) + { + return null; + } + + + CommentedFileConfig toml = CommentedFileConfig.builder(file).build(); + toml.add("path", "test"); + toml.save(); + + return null; + } + + public static void updatePlayerData() + { + PLAYER_DATA.updateData(MC); + } + + /** saves any necessary player data to the given world folder */ + public static void saveDimensionPlayerData(File worldFolder) + { + // get and create the file and path if they don't exist + File file = PlayerData.getFileForDimensionFolder(worldFolder); + if (!file.exists()) + { + try + { + file.getParentFile().mkdirs(); + file.createNewFile(); + } + catch (IOException e) + { + ApiShared.LOGGER.error("Unable to save player dimension data for world folder [" + worldFolder.getPath() + "].", e); + return; + } + } + + // determine the playerData + IMinecraftClientWrapper mc = SingletonHandler.get(IMinecraftClientWrapper.class); + PlayerData playerdata = new PlayerData(mc); + + // write the data to file + CommentedFileConfig toml = CommentedFileConfig.builder(file).build(); + playerdata.toTomlFile(toml); + } + + + /** Data container for any player data we can use to differentiate one dimension from another. */ + private static class PlayerData + { + public static final IWrapperFactory FACTORY = SingletonHandler.get(IWrapperFactory.class); + + public static final String PLAYER_BLOCK_POS_X_PATH = "playerBlockPosX"; + public static final String PLAYER_BLOCK_POS_Y_PATH = "playerBlockPosY"; + public static final String PLAYER_BLOCK_POS_Z_PATH = "playerBlockPosZ"; + AbstractBlockPosWrapper playerBlockPos; + + // not implemented yet + public static final String WORLD_SPAWN_POS_X_PATH = "worldSpawnBlockPosX"; + public static final String WORLD_SPAWN_POS_Y_PATH = "worldSpawnBlockPosY"; + public static final String WORLD_SPAWN_POS_Z_PATH = "worldSpawnBlockPosZ"; + /** + * The client world has access to a spawn point, so this should be possible to fill in. + * I'm not sure what this will look like for worlds that don't have a spawn point. + */ + AbstractBlockPosWrapper worldSpawnPointBlockPos; + + + + public PlayerData(IMinecraftClientWrapper mc) + { + updateData(mc); + } + + public PlayerData(File dimensionFolder) + { + File file = getFileForDimensionFolder(dimensionFolder); + CommentedFileConfig toml = CommentedFileConfig.builder(file).build(); + + // player block pos + try + { + toml.load(); + + // TODO this is crashing... + int x = toml.getInt(PLAYER_BLOCK_POS_X_PATH); + int y = toml.getInt(PLAYER_BLOCK_POS_Y_PATH); + int z = toml.getInt(PLAYER_BLOCK_POS_Z_PATH); + this.playerBlockPos = FACTORY.createBlockPos(x, y, z); + } + catch(Exception e) + { + ApiShared.LOGGER.error(e.getMessage(), e); + } + } + + + + public static File getFileForDimensionFolder(File file) + { + return new File(file.getPath() + File.separatorChar + playerDataFileName); + } + + + /** */ + public void updateData(IMinecraftClientWrapper mc) + { + this.playerBlockPos = mc.getPlayerBlockPos(); + } + + /** Writes everything from this object to the file given. */ + public void toTomlFile(CommentedFileConfig toml) + { + // player block pos + toml.add(PLAYER_BLOCK_POS_X_PATH, playerBlockPos.getX()); + toml.add(PLAYER_BLOCK_POS_Y_PATH, playerBlockPos.getY()); + toml.add(PLAYER_BLOCK_POS_Z_PATH, playerBlockPos.getZ()); + + + toml.save(); + } + + + @Override + public String toString() + { + return "PlayerBlockPos: [" + playerBlockPos.getX() + "," + playerBlockPos.getY() + "," + playerBlockPos.getZ() + "]"; + } + } } diff --git a/src/main/java/com/seibel/lod/core/objects/lod/LodDimension.java b/src/main/java/com/seibel/lod/core/objects/lod/LodDimension.java index 6f80bffb8..b90b5e4ff 100644 --- a/src/main/java/com/seibel/lod/core/objects/lod/LodDimension.java +++ b/src/main/java/com/seibel/lod/core/objects/lod/LodDimension.java @@ -131,12 +131,12 @@ public class LodDimension // run asynchronously since this could take a while - new Thread(() -> + Thread thread =new Thread(() -> { try { // attempt to get the file handler - File saveDir = LodDimensionFileHelper.determineSaveFolder(); + File saveDir = LodDimensionFileHelper.determineSubDimensionFolder(); if (saveDir == null) return; @@ -155,7 +155,9 @@ public class LodDimension // make sure we unlock this method this.determiningWorldFolder = false; } - }).start(); + }); + thread.setName("Sub-Dimension-Finder"); + thread.start(); } diff --git a/src/main/java/com/seibel/lod/core/util/LodUtil.java b/src/main/java/com/seibel/lod/core/util/LodUtil.java index ae3ea176a..fe2d11794 100644 --- a/src/main/java/com/seibel/lod/core/util/LodUtil.java +++ b/src/main/java/com/seibel/lod/core/util/LodUtil.java @@ -414,4 +414,22 @@ public class LodUtil public static void checkInterruptsUnchecked() { if (Thread.interrupted()) throw new RuntimeException(new InterruptedException()); } + + + /** + * Returns a shortened version of the given string that is no longer than maxLength.
+ * If null returns the empty string. + */ + public static String shortenString(String str, int maxLength) + { + if (str == null) + { + return ""; + } + else + { + return str.substring(0, Math.min(str.length(), maxLength)); + } + } + } diff --git a/src/main/java/com/seibel/lod/core/wrapperInterfaces/block/AbstractBlockPosWrapper.java b/src/main/java/com/seibel/lod/core/wrapperInterfaces/block/AbstractBlockPosWrapper.java index a8cf65f6d..2a0a8a420 100644 --- a/src/main/java/com/seibel/lod/core/wrapperInterfaces/block/AbstractBlockPosWrapper.java +++ b/src/main/java/com/seibel/lod/core/wrapperInterfaces/block/AbstractBlockPosWrapper.java @@ -45,4 +45,10 @@ public abstract class AbstractBlockPosWrapper /** returns itself */ public abstract AbstractBlockPosWrapper offset(int x, int y, int z); + + + public int getManhattanDistance(AbstractBlockPosWrapper otherPos) + { + return Math.abs(this.getX() - otherPos.getX()) + Math.abs(this.getY() - otherPos.getY()) + Math.abs(this.getZ() - otherPos.getZ()); + } }