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 7659aa7b4..ec43884e6 100644
--- a/src/main/java/com/seibel/lod/core/api/ClientApi.java
+++ b/src/main/java/com/seibel/lod/core/api/ClientApi.java
@@ -27,7 +27,7 @@ import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
-import com.seibel.lod.core.handlers.LodSubDimensionFolderFinder;
+import com.seibel.lod.core.handlers.LodDimensionFinder;
import org.lwjgl.glfw.GLFW;
import com.seibel.lod.core.ModInfo;
@@ -54,7 +54,7 @@ import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper;
* Specifically for the client.
*
* @author James Seibel
- * @version 12-8-2021
+ * @version 2022-3-26
*/
public class ClientApi
{
@@ -78,6 +78,8 @@ public class ClientApi
public static final long SPAM_LOGGER_FLUSH_NS = TimeUnit.NANOSECONDS.convert(1, TimeUnit.SECONDS);
+ public static LodDimensionFinder DIMENSION_FINDER = new LodDimensionFinder();;
+
public static class LagSpikeCatcher {
long timer = System.nanoTime();
@@ -104,6 +106,8 @@ public class ClientApi
+
+
private ClientApi()
{
@@ -170,19 +174,21 @@ public class ClientApi
LodDimension lodDim = ApiShared.lodWorld.getLodDimension(world.getDimensionType());
// Make sure the player's data is up-to-date
- LodSubDimensionFolderFinder.updatePlayerData();
+ DIMENSION_FINDER.updatePlayerData();
// Make the LodDim if it does not exist
if (lodDim == null)
{
- lodDim = new LodDimension(world.getDimensionType(), ApiShared.lodWorld,
- ApiShared.lodBuilder.defaultDimensionWidthInRegions);
- ApiShared.lodWorld.addLodDimension(lodDim);
- }
- // if necessary attempt to get the world file handler
- if (lodDim.isFileHandlerNull())
- {
- lodDim.attemptToSetWorldFileHandler();
+ if (DIMENSION_FINDER.isDone())
+ {
+ lodDim = DIMENSION_FINDER.getAndClearFoundLodDimension();
+ ApiShared.lodWorld.addLodDimension(lodDim);
+ }
+ else
+ {
+ DIMENSION_FINDER.AttemptToDetermineSubDimensionAsync(MC.getCurrentDimension());
+ return;
+ }
}
if (prefLoggerEnabled) {
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 717f585b7..9c6c863c5 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.api.ClientApi;
import org.apache.commons.compress.compressors.xz.XZCompressorInputStream;
import org.apache.commons.compress.compressors.xz.XZCompressorOutputStream;
@@ -58,7 +59,7 @@ import org.apache.logging.log4j.Logger;
*
* @author James Seibel
* @author Cola
- * @version 3-7-2022
+ * @version 2022-3-26
*/
public class LodDimensionFileHandler
{
@@ -415,7 +416,7 @@ public class LodDimensionFileHandler
}
// save the dimension data
- LodSubDimensionFolderFinder.saveDimensionPlayerData(this.dimensionDataSaveFolder);
+ ClientApi.DIMENSION_FINDER.saveDimensionPlayerData(this.dimensionDataSaveFolder);
trySaveRegionsToBeSaved();
// wait for the saving to finish if requested
diff --git a/src/main/java/com/seibel/lod/core/handlers/LodSubDimensionFolderFinder.java b/src/main/java/com/seibel/lod/core/handlers/LodDimensionFinder.java
similarity index 66%
rename from src/main/java/com/seibel/lod/core/handlers/LodSubDimensionFolderFinder.java
rename to src/main/java/com/seibel/lod/core/handlers/LodDimensionFinder.java
index a1c1c9c5f..7cdfcbafc 100644
--- a/src/main/java/com/seibel/lod/core/handlers/LodSubDimensionFolderFinder.java
+++ b/src/main/java/com/seibel/lod/core/handlers/LodDimensionFinder.java
@@ -2,6 +2,8 @@ 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.handlers.dimensionFinder.PlayerData;
+import com.seibel.lod.core.handlers.dimensionFinder.SubDimCompare;
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;
@@ -12,14 +14,11 @@ 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;
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import com.seibel.lod.core.wrapperInterfaces.world.IDimensionTypeWrapper;
import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper;
-import org.jetbrains.annotations.NotNull;
import java.io.File;
import java.io.IOException;
@@ -27,21 +26,88 @@ import java.util.UUID;
/**
* Used to guess the world folder for the player's current dimension.
+ *
* @author James Seibel
- * @version 3-23-2022
+ * @version 2022-3-26
*/
-public class LodSubDimensionFolderFinder
+public class LodDimensionFinder
{
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;
+ public static final String THREAD_NAME = "Sub-Dimension-Finder";
- private static LodSubDimensionFolderFinder.PlayerData PLAYER_DATA = new LodSubDimensionFolderFinder.PlayerData(MC);
- private static LodSubDimensionFolderFinder.PlayerData FIRST_SEEN_PLAYER_DATA = null;
+ private PlayerData playerData = new PlayerData(MC);
+ private PlayerData firstSeenPlayerData = null;
+
+ private volatile LodDimension foundLodDimension = null;
+
+ /** If true the LodDimensionFileHelper is attempting to determine the folder for this dimension */
+ private boolean determiningWorldFolder = false;
+
+
+
+ public LodDimensionFinder()
+ {
+
+ }
+
+
+
+ /** Returns true if a LodDimension has been found */
+ public boolean isDone()
+ {
+ return foundLodDimension != null;
+ }
+
+ /** Returns the found LodDimension */
+ public LodDimension getAndClearFoundLodDimension()
+ {
+ // clear the found dimension
+ LodDimension returnDim = this.foundLodDimension;
+ this.foundLodDimension = null;
+
+ return returnDim;
+ }
+
+
+
+ public void AttemptToDetermineSubDimensionAsync(IDimensionTypeWrapper dimensionTypeWrapper)
+ {
+ // prevent multiple threads running at the same time
+ if (determiningWorldFolder && !isDone())
+ return;
+ determiningWorldFolder = true;
+
+
+ // run asynchronously since this could take a while
+ Thread thread = new Thread(() ->
+ {
+ try
+ {
+ // attempt to get the file handler
+ File saveDir = attemptToDetermineSubDimensionFolder();
+ if (saveDir == null)
+ return;
+
+ foundLodDimension = new LodDimension(dimensionTypeWrapper, ApiShared.lodBuilder.defaultDimensionWidthInRegions, saveDir);
+ }
+ catch (IOException e)
+ {
+ ApiShared.LOGGER.error("Unable to set the dimension file handler for dimension type [" + dimensionTypeWrapper.getDimensionName() + "]. Error: " + e.getMessage(), e);
+ }
+ finally
+ {
+ // make sure we unlock this method
+ determiningWorldFolder = false;
+ }
+ });
+ thread.setName(THREAD_NAME);
+ thread.start();
+ }
@@ -49,20 +115,23 @@ public class LodSubDimensionFolderFinder
* Currently this method checks a single chunk (where the player is)
* and compares it against the same chunk position in the other dimension worlds to
* guess which world the player is in.
- * @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 determineSubDimensionFolder() throws IOException
+ public File attemptToDetermineSubDimensionFolder() throws IOException
{
- if (FIRST_SEEN_PLAYER_DATA == null)
+ if (firstSeenPlayerData == null)
{
- FIRST_SEEN_PLAYER_DATA = PLAYER_DATA;
- PLAYER_DATA = new LodSubDimensionFolderFinder.PlayerData(MC);
+ firstSeenPlayerData = playerData;
+ playerData = new PlayerData(MC);
}
+ // TODO check based on the dimension's last seen location instead of the first seen player data
+ // hopefully this should fix entering a dimension in two different locations
+
+
// relevant positions
- AbstractChunkPosWrapper playerChunkPos = FACTORY.createChunkPos(FIRST_SEEN_PLAYER_DATA.playerBlockPos);
+ AbstractChunkPosWrapper playerChunkPos = FACTORY.createChunkPos(firstSeenPlayerData.playerBlockPos);
int startingBlockPosX = playerChunkPos.getMinBlockX();
int startingBlockPosZ = playerChunkPos.getMinBlockZ();
RegionPos playerRegionPos = new RegionPos(playerChunkPos);
@@ -71,11 +140,11 @@ public class LodSubDimensionFolderFinder
// chunk from the newly loaded dimension
IChunkWrapper newlyLoadedChunk = MC.getWrappedClientWorld().tryGetChunk(playerChunkPos);
// check if this chunk is valid to test
- if (!LodSubDimensionFolderFinder.CanDetermineDimensionFolder(newlyLoadedChunk))
+ if (!CanDetermineDimensionFolder(newlyLoadedChunk))
return null;
// create a temporary dimension to store the test LOD
- LodDimension newlyLoadedDim = new LodDimension(MC.getCurrentDimension(), null, 1);
+ LodDimension newlyLoadedDim = new LodDimension(MC.getCurrentDimension(), 1, null, false);
newlyLoadedDim.move(playerRegionPos);
newlyLoadedDim.regions.set(playerRegionPos.x, playerRegionPos.z, new LodRegion(LodUtil.BLOCK_DETAIL_LEVEL, playerRegionPos, VERTICAL_QUALITY_TO_TEST_WITH));
@@ -87,7 +156,7 @@ public class LodSubDimensionFolderFinder
// 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() + "]");
+ ApiShared.LOGGER.info("First seen player block pos in dimension: [" + firstSeenPlayerData.playerBlockPos.getX() + "," + firstSeenPlayerData.playerBlockPos.getY() + "," + firstSeenPlayerData.playerBlockPos.getZ() + "]");
// new chunk data
@@ -150,7 +219,7 @@ public class LodSubDimensionFolderFinder
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);
+ LodDimension tempLodDim = new LodDimension(null, 1, null, false);
tempLodDim.move(playerRegionPos);
LodDimensionFileHandler tempFileHandler = new LodDimensionFileHandler(testDimFolder, tempLodDim);
LodRegion testRegion = tempFileHandler.loadRegionFromFile(LodUtil.BLOCK_DETAIL_LEVEL, playerRegionPos, VERTICAL_QUALITY_TO_TEST_WITH);
@@ -171,7 +240,7 @@ public class LodSubDimensionFolderFinder
ApiShared.LOGGER.info("Last known player pos: [" + testPlayerData.playerBlockPos.getX() + "," + testPlayerData.playerBlockPos.getY() + "," + testPlayerData.playerBlockPos.getZ() + "]");
// check if the block positions are close
- int playerBlockDist = testPlayerData.playerBlockPos.getManhattanDistance(FIRST_SEEN_PLAYER_DATA.playerBlockPos);
+ int playerBlockDist = testPlayerData.playerBlockPos.getManhattanDistance(firstSeenPlayerData.playerBlockPos);
ApiShared.LOGGER.info("Player block position distance between saved sub dimension and first seen is [" + playerBlockDist + "]");
@@ -217,7 +286,7 @@ public class LodSubDimensionFolderFinder
}
// the first seen player data is no longer needed, the sub dimension has been determined
- FIRST_SEEN_PLAYER_DATA = null;
+ firstSeenPlayerData = null;
if (mostSimilarSubDim != null && mostSimilarSubDim.isValidSubDim())
@@ -248,7 +317,7 @@ public class LodSubDimensionFolderFinder
* If the worldId is empty or null this returns the dimension parent folder
* Example folder names: "dim_overworld/worldId", "dim_the_nether/worldId"
*/
- public static File GetDimensionFolder(IDimensionTypeWrapper newDimensionType, String worldId)
+ public File GetDimensionFolder(IDimensionTypeWrapper newDimensionType, String worldId)
{
// prevent null pointers
if (worldId == null)
@@ -278,7 +347,7 @@ public class LodSubDimensionFolderFinder
/** Returns true if the given chunk is valid to test */
- public static boolean CanDetermineDimensionFolder(IChunkWrapper chunk)
+ public boolean CanDetermineDimensionFolder(IChunkWrapper chunk)
{
// we can only guess if the given chunk can be converted into a LOD
return LodBuilder.canGenerateLodFromChunk(chunk);
@@ -301,9 +370,11 @@ public class LodSubDimensionFolderFinder
}
}
}
-
+
return false;
}
+
+
@@ -311,15 +382,13 @@ public class LodSubDimensionFolderFinder
- private static final String playerDataFileName = "_playerData.toml";
-
- public static void updatePlayerData()
+ public void updatePlayerData()
{
- PLAYER_DATA.updateData(MC);
+ playerData.updateData(MC);
}
/** saves any necessary player data to the given world folder */
- public static void saveDimensionPlayerData(File worldFolder)
+ public void saveDimensionPlayerData(File worldFolder)
{
// get and create the file and path if they don't exist
File file = PlayerData.getFileForDimensionFolder(worldFolder);
@@ -347,139 +416,4 @@ public class LodSubDimensionFolderFinder
}
- /** 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();
-
- toml.load();
-
-
- // get the player block pos if it is specified
- if (toml.contains(PLAYER_BLOCK_POS_X_PATH)
- && toml.contains(PLAYER_BLOCK_POS_Y_PATH)
- && toml.contains(PLAYER_BLOCK_POS_Z_PATH))
- {
- int x = toml.getIntOrElse(PLAYER_BLOCK_POS_X_PATH, 0);
- int y = toml.getIntOrElse(PLAYER_BLOCK_POS_Y_PATH, 0);
- int z = toml.getIntOrElse(PLAYER_BLOCK_POS_Z_PATH, 0);
- this.playerBlockPos = FACTORY.createBlockPos(x, y, z);
- }
- else
- {
- this.playerBlockPos = FACTORY.createBlockPos(0, 0, 0);
- }
- }
-
-
-
- public static File getFileForDimensionFolder(File file)
- {
- return new File(file.getPath() + File.separatorChar + playerDataFileName);
- }
-
-
- /** Should be called often to make sure this object is up to date with the player's info */
- 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() + "]";
- }
- }
-
-
-
-
- private static class SubDimCompare implements Comparable
- {
- public int equalDataPoints = 0;
- public int totalDataPoints = 0;
- public int playerPosDist = 0;
- public File folder = null;
-
-
- public SubDimCompare(int newEqualDataPoints, int newTotalDataPoints, int newPlayerPosDistance, File newSubDimFolder)
- {
- this.equalDataPoints = newEqualDataPoints;
- this.totalDataPoints = newTotalDataPoints;
- this.playerPosDist = newPlayerPosDistance;
-
- this.folder = newSubDimFolder;
- }
-
- /** returns a number between 0 (not equal) and 1 (totally equal) */
- public double getPercentEqual()
- {
- return (double) equalDataPoints / (double) totalDataPoints;
- }
-
-
- @Override
- public int compareTo(@NotNull LodSubDimensionFolderFinder.SubDimCompare other)
- {
- if (this.equalDataPoints != other.equalDataPoints)
- {
- // compare based on data points
- return Integer.compare(this.equalDataPoints, other.equalDataPoints);
- }
- else
- {
- // break ties based on player position
- return Integer.compare(this.playerPosDist, other.playerPosDist);
- }
- }
-
- /** Returns true if this sub dimension is close enough to be considered a valid sub dimension */
- public boolean isValidSubDim()
- {
- double minimumSimilarityRequired = CONFIG.client().multiplayer().getMultiDimensionRequiredSimilarity();
- return this.getPercentEqual() >= minimumSimilarityRequired || this.playerPosDist <= 3;
- }
- }
-
-
}
diff --git a/src/main/java/com/seibel/lod/core/handlers/dimensionFinder/PlayerData.java b/src/main/java/com/seibel/lod/core/handlers/dimensionFinder/PlayerData.java
new file mode 100644
index 000000000..1d9cfef4d
--- /dev/null
+++ b/src/main/java/com/seibel/lod/core/handlers/dimensionFinder/PlayerData.java
@@ -0,0 +1,106 @@
+package com.seibel.lod.core.handlers.dimensionFinder;
+
+
+import com.electronwill.nightconfig.core.file.CommentedFileConfig;
+import com.seibel.lod.core.handlers.dependencyInjection.SingletonHandler;
+import com.seibel.lod.core.wrapperInterfaces.IWrapperFactory;
+import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
+import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
+
+import java.io.File;
+
+/**
+ * Data container for any player data we can use to differentiate one dimension from another.
+ *
+ * @author James Seibel
+ * @version 2022-3-26
+ */
+public class PlayerData
+{
+ public static final IWrapperFactory FACTORY = SingletonHandler.get(IWrapperFactory.class);
+
+ private static final String playerDataFileName = "_playerData.toml";
+
+
+ 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";
+ public 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.
+ */
+ public AbstractBlockPosWrapper worldSpawnPointBlockPos;
+
+
+
+ public PlayerData(IMinecraftClientWrapper mc)
+ {
+ updateData(mc);
+ }
+
+ public PlayerData(File dimensionFolder)
+ {
+ File file = getFileForDimensionFolder(dimensionFolder);
+ CommentedFileConfig toml = CommentedFileConfig.builder(file).build();
+
+ toml.load();
+
+
+ // get the player block pos if it is specified
+ if (toml.contains(PLAYER_BLOCK_POS_X_PATH)
+ && toml.contains(PLAYER_BLOCK_POS_Y_PATH)
+ && toml.contains(PLAYER_BLOCK_POS_Z_PATH))
+ {
+ int x = toml.getIntOrElse(PLAYER_BLOCK_POS_X_PATH, 0);
+ int y = toml.getIntOrElse(PLAYER_BLOCK_POS_Y_PATH, 0);
+ int z = toml.getIntOrElse(PLAYER_BLOCK_POS_Z_PATH, 0);
+ this.playerBlockPos = FACTORY.createBlockPos(x, y, z);
+ }
+ else
+ {
+ this.playerBlockPos = FACTORY.createBlockPos(0, 0, 0);
+ }
+ }
+
+
+
+ public static File getFileForDimensionFolder(File file)
+ {
+ return new File(file.getPath() + File.separatorChar + playerDataFileName);
+ }
+
+
+ /** Should be called often to make sure this object is up to date with the player's info */
+ public void updateData(IMinecraftClientWrapper mc)
+ {
+ if (mc.playerExists())
+ {
+ 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/handlers/dimensionFinder/SubDimCompare.java b/src/main/java/com/seibel/lod/core/handlers/dimensionFinder/SubDimCompare.java
new file mode 100644
index 000000000..a2e5e34ee
--- /dev/null
+++ b/src/main/java/com/seibel/lod/core/handlers/dimensionFinder/SubDimCompare.java
@@ -0,0 +1,66 @@
+package com.seibel.lod.core.handlers.dimensionFinder;
+
+import com.seibel.lod.core.handlers.dependencyInjection.SingletonHandler;
+import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.File;
+
+/**
+ * Contains data used to compare different sub LodDimensions.
+ * Sub Dimensions are the different folders under a dimension.
+ * For example: "\Distant_Horizons_server_data\server_1\dim_the_nether\6fb97c01-e4c7-4634-87db-36b1e490e4c3"
+ * is a sub dimension for the server "server_1" in the nether.
+ *
+ * @author James Seibel
+ * @version 2022-3-26
+ */
+public class SubDimCompare implements Comparable
+{
+ private static final ILodConfigWrapperSingleton CONFIG = SingletonHandler.get(ILodConfigWrapperSingleton.class);
+
+
+ public int equalDataPoints = 0;
+ public int totalDataPoints = 0;
+ public int playerPosDist = 0;
+ public File folder = null;
+
+
+ public SubDimCompare(int newEqualDataPoints, int newTotalDataPoints, int newPlayerPosDistance, File newSubDimFolder)
+ {
+ this.equalDataPoints = newEqualDataPoints;
+ this.totalDataPoints = newTotalDataPoints;
+ this.playerPosDist = newPlayerPosDistance;
+
+ this.folder = newSubDimFolder;
+ }
+
+ /** returns a number between 0 (not equal) and 1 (totally equal) */
+ public double getPercentEqual()
+ {
+ return (double) equalDataPoints / (double) totalDataPoints;
+ }
+
+
+ @Override
+ public int compareTo(@NotNull SubDimCompare other)
+ {
+ if (this.equalDataPoints != other.equalDataPoints)
+ {
+ // compare based on data points
+ return Integer.compare(this.equalDataPoints, other.equalDataPoints);
+ }
+ else
+ {
+ // break ties based on player position
+ return Integer.compare(this.playerPosDist, other.playerPosDist);
+ }
+ }
+
+ /** Returns true if this sub dimension is close enough to be considered a valid sub dimension */
+ public boolean isValidSubDim()
+ {
+ double minimumSimilarityRequired = CONFIG.client().multiplayer().getMultiDimensionRequiredSimilarity();
+ return this.getPercentEqual() >= minimumSimilarityRequired || this.playerPosDist <= 3;
+ }
+}
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 9236c1fa2..cc684f04e 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
@@ -26,7 +26,6 @@ import com.seibel.lod.core.enums.config.DropoffQuality;
import com.seibel.lod.core.enums.config.GenerationPriority;
import com.seibel.lod.core.enums.config.VerticalQuality;
import com.seibel.lod.core.handlers.LodDimensionFileHandler;
-import com.seibel.lod.core.handlers.LodSubDimensionFolderFinder;
import com.seibel.lod.core.handlers.dependencyInjection.SingletonHandler;
import com.seibel.lod.core.objects.Pos2D;
import com.seibel.lod.core.objects.PosToGenerateContainer;
@@ -37,7 +36,6 @@ import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import com.seibel.lod.core.wrapperInterfaces.world.IDimensionTypeWrapper;
import java.io.File;
-import java.io.IOException;
import java.util.Arrays;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@@ -56,7 +54,7 @@ import java.util.concurrent.TimeUnit;
*
* @author Leonardo Amato
* @author James Seibel
- * @version 3-17-2022
+ * @version 2022-3-26
*/
public class LodDimension
{
@@ -89,75 +87,49 @@ public class LodDimension
private final ExecutorService cutAndExpandThread = Executors.newSingleThreadExecutor(
new LodThreadFactory(this.getClass().getSimpleName() + " - Cut and Expand", Thread.NORM_PRIORITY - 1));
- /** If true the LodDimensionFileHelper is attempting to determine the folder for this dimension */
- private boolean determiningWorldFolder = false;
+ private boolean logEvents = true;
+
/**
- * Creates the dimension centered at (0,0)
- * @param newWidth in regions
+ * Creates the dimension centered at (0,0), with event logging, and no file saving/loading.
+ *
+ * @param newWidth measured in regions
*/
- public LodDimension(IDimensionTypeWrapper newDimension, LodWorld lodWorld, int newWidth)
+ public LodDimension(IDimensionTypeWrapper newDimension, int newWidth)
{
- dimension = newDimension;
- width = newWidth; // FIXME any width besides 1 causes an indexOutOfBounds Exception
- halfWidth = width / 2;
-
- if (newDimension != null && lodWorld != null)
- {
- attemptToSetWorldFileHandler();
- }
-
-
- regions = new MovableGridRingList(halfWidth, 0, 0);
- generateIteratorList();
+ this(newDimension, newWidth, null, true);
}
/**
- * Attempts to determine and set the file handler based on
- * the chunk the player is currently in.
- * @returns true if the fileHandler has been set, false otherwise
+ * Creates the dimension centered at (0,0)
+ *
+ * @param newWidth measured in regions
+ * @param saveDir can be null. If null regions will not be saved or loaded from file.
*/
- public void attemptToSetWorldFileHandler()
+ public LodDimension(IDimensionTypeWrapper newDimension, int newWidth, File saveDir)
{
- // check if we need to get the file handler
- if (this.fileHandler != null)
- return;
+ this(newDimension, newWidth, saveDir, true);
+ }
+
+ /**
+ * Creates the dimension centered at (0,0)
+ *
+ * @param newWidth measured in regions
+ * @param saveDir can be null. If null regions will not be saved or loaded from file.
+ */
+ public LodDimension(IDimensionTypeWrapper newDimension, int newWidth, File saveDir, boolean newLogEvents)
+ {
+ this.dimension = newDimension;
+ this.width = newWidth; // FIXME any width besides 1 causes an indexOutOfBounds Exception
+ this.halfWidth = width / 2;
+ this.logEvents = newLogEvents;
- // prevent multiple threads running at the same time
- if (this.determiningWorldFolder)
- return;
- this.determiningWorldFolder = true;
+ if (saveDir != null)
+ this.fileHandler = new LodDimensionFileHandler(saveDir, this);
-
- // run asynchronously since this could take a while
- Thread thread =new Thread(() ->
- {
- try
- {
- // attempt to get the file handler
- File saveDir = LodSubDimensionFolderFinder.determineSubDimensionFolder();
- if (saveDir == null)
- return;
-
- this.fileHandler = new LodDimensionFileHandler(saveDir, this);
-
- // clear the previous regions so we can load the LODs from file
- if (this.regions != null)
- this.regions.clear();
- }
- catch (IOException e)
- {
- ApiShared.LOGGER.error("Unable to set the dimension file handler for dimension type [" + this.dimension.getDimensionName() + "]. Error: " + e.getMessage(), e);
- }
- finally
- {
- // make sure we unlock this method
- this.determiningWorldFolder = false;
- }
- });
- thread.setName("Sub-Dimension-Finder");
- thread.start();
+ this.regions = new MovableGridRingList(halfWidth, 0, 0);
+ generateIteratorList();
}
@@ -193,11 +165,15 @@ public class LodDimension
*/
public synchronized void move(RegionPos regionOffset)
{
- ApiShared.LOGGER.info("LodDim MOVE. Offset: "+regionOffset);
+ if (this.logEvents)
+ ApiShared.LOGGER.info("LodDim MOVE. Offset: "+regionOffset);
+
saveDirtyRegionsToFile(false); //async add dirty regions to be saved.
Pos2D p = regions.getCenter();
regions.move(p.x+regionOffset.x, p.y+regionOffset.z);
- ApiShared.LOGGER.info("LodDim MOVE complete. Offset: "+regionOffset);
+
+ if (this.logEvents)
+ ApiShared.LOGGER.info("LodDim MOVE complete. Offset: "+regionOffset);
}
@@ -343,9 +319,13 @@ public class LodDimension
if (isExpanding) return;
// If we have less than 20% or 128MB ram left. Don't expend.
- if (expandOrLoadPaused) {
- if (LodUtil.checkRamUsage(0.2, 128)) {
- ApiShared.LOGGER.info("Enough ram for expandOrLoadThread. Restarting...");
+ if (expandOrLoadPaused)
+ {
+ if (LodUtil.checkRamUsage(0.2, 128))
+ {
+ if (this.logEvents)
+ ApiShared.LOGGER.info("Enough ram for expandOrLoadThread. Restarting...");
+
expandOrLoadPaused = false;
}
}
@@ -359,14 +339,20 @@ public class LodDimension
int dropoffSwitch = dropoffQuality.fastModeSwitch;
// don't run the expander multiple times
// for the same location
- Runnable thread = () -> {
+ Runnable thread = () ->
+ {
//ApiShared.LOGGER.info("LodDim expend Region: " + playerPosX + "," + playerPosZ);
Pos2D minPos = regions.getMinInRange();
- iterateWithSpiral((int x, int z) -> {
- if (!expandOrLoadPaused && !LodUtil.checkRamUsage(0.02, 64)) {
+ iterateWithSpiral((int x, int z) ->
+ {
+ if (!expandOrLoadPaused && !LodUtil.checkRamUsage(0.02, 64))
+ {
Runtime.getRuntime().gc();
- if (!LodUtil.checkRamUsage(0.2, 128)) {
- ApiShared.LOGGER.warn("Not enough ram for expandOrLoadThread. Pausing until Ram is freed...");
+ if (!LodUtil.checkRamUsage(0.2, 128))
+ {
+ if (this.logEvents)
+ ApiShared.LOGGER.warn("Not enough ram for expandOrLoadThread. Pausing until Ram is freed...");
+
// We have less than 10% or 64MB ram left. Don't expend.
expandOrLoadPaused = true;
saveDirtyRegionsToFile(false);
@@ -396,10 +382,15 @@ public class LodDimension
double deltaRPosX = debugRPosX - playerPosX;
double deltaRPosZ = debugRPosZ - playerPosZ;
double debugDistance = Math.sqrt(deltaRPosX*deltaRPosX + deltaRPosZ*deltaRPosZ);
- if (minDistance > debugDistance || maxDistance < debugDistance || minDistance > maxDistance) {
- ApiShared.LOGGER.error("MinDistance/MaxDistance is WRONG!!! minDist: [{}], maxDist: [{}], centerDist: [{}]\n"
- + "At center block pos: {} {}, region pos: {}",
- minDistance, maxDistance, debugDistance, debugRPosX, debugRPosZ, regionPos);
+ if (minDistance > debugDistance || maxDistance < debugDistance || minDistance > maxDistance)
+ {
+ if (this.logEvents)
+ {
+ ApiShared.LOGGER.error("MinDistance/MaxDistance is WRONG!!! minDist: [{}], maxDist: [{}], centerDist: [{}]\n"
+ + "At center block pos: {} {}, region pos: {}",
+ minDistance, maxDistance, debugDistance, debugRPosX, debugRPosZ, regionPos);
+ }
+
return;
}
}
@@ -742,13 +733,18 @@ public class LodDimension
return stringBuilder.toString();
}
- public void shutdown() {
+ public void shutdown()
+ {
cutAndExpandThread.shutdown();
- try {
+ try
+ {
boolean worked = cutAndExpandThread.awaitTermination(5, TimeUnit.SECONDS);
+
if (!worked)
ApiShared.LOGGER.error("Cut And Expend threads timed out! May cause crash on game exit due to cleanup failure.");
- } catch (InterruptedException e) {
+ }
+ catch (InterruptedException e)
+ {
ApiShared.LOGGER.error("Cut And Expend threads shutdown is interrupted! May cause crash on game exit due to cleanup failure: ", e);
}