Improve logging and potentially fix a few substring crashes

This commit is contained in:
James Seibel
2022-03-23 20:58:50 -05:00
parent 4820c11f8e
commit 621bf7341d
7 changed files with 302 additions and 73 deletions
@@ -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)
{
@@ -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)
{
@@ -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");
}
@@ -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() + "]";
}
}
}
@@ -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();
}
@@ -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. <br>
* 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));
}
}
}
@@ -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());
}
}