Partially fix SubDimensionFinder
This commit is contained in:
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
+97
-163
@@ -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 <br>
|
||||
* 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<SubDimCompare>
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -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() + "]";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<SubDimCompare>
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -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<LodRegion>(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<LodRegion>(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);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user