From c0c4cf8b566953527b39153b3f7a16ae887acaaf Mon Sep 17 00:00:00 2001 From: James Seibel Date: Sat, 17 Dec 2022 09:44:14 -0600 Subject: [PATCH] Partially update LevelToFileMatcher (File reading incomplete) --- .../lod/core/datatype/full/FullDataPoint.java | 3 + .../lod/core/file/LevelToFileMatcher.java | 212 +++++++++++------- .../seibel/lod/core/file/SubDimCompare.java | 18 +- 3 files changed, 145 insertions(+), 88 deletions(-) diff --git a/core/src/main/java/com/seibel/lod/core/datatype/full/FullDataPoint.java b/core/src/main/java/com/seibel/lod/core/datatype/full/FullDataPoint.java index 781c0a9ad..e9ba872ed 100644 --- a/core/src/main/java/com/seibel/lod/core/datatype/full/FullDataPoint.java +++ b/core/src/main/java/com/seibel/lod/core/datatype/full/FullDataPoint.java @@ -33,6 +33,9 @@ import static com.seibel.lod.core.datatype.column.accessor.ColumnFormat.MAX_WORL */ public class FullDataPoint { + /** Represents the data held by an empty data point */ + public static final int EMPTY_DATA_POINT = 0; + public static final int ID_WIDTH = 32; public static final int DP_WIDTH = 12; public static final int Y_WIDTH = 12; diff --git a/core/src/main/java/com/seibel/lod/core/file/LevelToFileMatcher.java b/core/src/main/java/com/seibel/lod/core/file/LevelToFileMatcher.java index d32419baf..e51052d54 100644 --- a/core/src/main/java/com/seibel/lod/core/file/LevelToFileMatcher.java +++ b/core/src/main/java/com/seibel/lod/core/file/LevelToFileMatcher.java @@ -1,12 +1,24 @@ package com.seibel.lod.core.file; import com.seibel.lod.core.config.Config; +import com.seibel.lod.core.datatype.ILodDataSource; +import com.seibel.lod.core.datatype.full.ChunkSizedData; +import com.seibel.lod.core.datatype.full.FullDataPoint; +import com.seibel.lod.core.datatype.full.accessor.SingleFullArrayView; +import com.seibel.lod.core.datatype.transform.LodDataBuilder; import com.seibel.lod.core.dependencyInjection.SingletonInjector; +import com.seibel.lod.core.file.datafile.DataFileHandler; +import com.seibel.lod.core.file.datafile.IDataSourceProvider; +import com.seibel.lod.core.file.structure.ClientOnlySaveStructure; +import com.seibel.lod.core.level.DhClientLevel; +import com.seibel.lod.core.level.IDhLevel; import com.seibel.lod.core.logging.ConfigBasedLogger; import com.seibel.lod.core.pos.DhChunkPos; +import com.seibel.lod.core.pos.DhSectionPos; import com.seibel.lod.core.util.LodUtil; import com.seibel.lod.core.wrapperInterfaces.chunk.IChunkWrapper; import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; +import com.seibel.lod.core.wrapperInterfaces.world.IClientLevelWrapper; import com.seibel.lod.core.wrapperInterfaces.world.ILevelWrapper; import org.apache.logging.log4j.LogManager; @@ -14,12 +26,16 @@ import java.io.File; import java.io.IOException; import java.util.Objects; import java.util.UUID; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutorService; import java.util.concurrent.atomic.AtomicBoolean; /** - * Used to support multiple worlds using the same dimension type.
+ * Used to allow multiple levels using the same dimension type.
* This is specifically needed for servers running the Multiverse plugin (or similar). + * + * @author James Seibel + * @version 12-17-2022 */ public class LevelToFileMatcher implements AutoCloseable { @@ -137,27 +153,31 @@ public class LevelToFileMatcher implements AutoCloseable } //TODO: Compute a ChunkData from current chunk. - /* + // generate a LOD to test against - boolean lodGenerated = InternalApiShared.lodBuilder.generateLodNodeFromChunk(newlyLoadedDim, newlyLoadedChunk, new LodBuilderConfig(EDistanceGenerationMode.FULL), true, true); + boolean lodGenerated = LodDataBuilder.canGenerateLodFromChunk(newlyLoadedChunk); if (!lodGenerated) return null; // log the start of this attempt - LOGGER.info("Attempting to determine sub-dimension for [" + MC_CLIENT.getCurrentDimension().getDimensionName() + "]"); + LOGGER.info("Attempting to determine sub-dimension for [" + MC_CLIENT.getWrappedClientWorld().getDimensionType().getDimensionName() + "]"); LOGGER.info("Player block pos in dimension: [" + playerData.playerBlockPos.getX() + "," + playerData.playerBlockPos.getY() + "," + playerData.playerBlockPos.getZ() + "]"); // new chunk data - long[][][] newChunkData = new long[LodUtil.CHUNK_WIDTH][LodUtil.CHUNK_WIDTH][]; - for (int x = 0; x < LodUtil.CHUNK_WIDTH; x++) - { - for (int z = 0; z < LodUtil.CHUNK_WIDTH; z++) - { - long[] array = newlyLoadedDim.getRegion(playerRegionPos.x, playerRegionPos.z).getAllData(LodUtil.BLOCK_DETAIL_LEVEL, x + startingBlockPosX, z + startingBlockPosZ); - newChunkData[x][z] = array; - } - } - boolean newChunkHasData = !isDataEmpty(newChunkData); + ChunkSizedData newChunkSizedData = LodDataBuilder.createChunkData(newlyLoadedChunk); + long[][][] newChunkData = new long[LodUtil.CHUNK_WIDTH][LodUtil.CHUNK_WIDTH][]; + if (newChunkSizedData != null) + { + for (int x = 0; x < LodUtil.CHUNK_WIDTH; x++) + { + for (int z = 0; z < LodUtil.CHUNK_WIDTH; z++) + { + long[] array = newChunkSizedData.get(x, z).getRaw(); + newChunkData[x][z] = array; + } + } + } + boolean newChunkHasData = newChunkSizedData != null && newChunkSizedData.nonEmptyCount() != 0; // check if the chunk is actually empty if (!newChunkHasData) @@ -175,7 +195,7 @@ public class LevelToFileMatcher implements AutoCloseable LOGGER.warn(message); } return null; - }*/ + } // compare each world with the newly loaded one @@ -190,70 +210,100 @@ public class LevelToFileMatcher implements AutoCloseable { // TODO: Try load a data file overlapping the playerChunkPos from ClientOnlySaveStructure, // and then use it to compare chunk data to current chunk. - - /* - // get a LOD from this dimension folder - LodDimension tempLodDim = new LodDimension(null, 1, null, false); - tempLodDim.move(playerRegionPos); - LodDimensionFileHandler tempFileHandler = new LodDimensionFileHandler(testLevelFolder, tempLodDim); - LodRegion testRegion = tempFileHandler.loadRegionFromFile(LodUtil.BLOCK_DETAIL_LEVEL, playerRegionPos, VERTICAL_QUALITY_TO_TEST_WITH); - // get data from this LOD - long[][][] testChunkData = new long[LodUtil.CHUNK_WIDTH][LodUtil.CHUNK_WIDTH][]; - for (int x = 0; x < LodUtil.CHUNK_WIDTH; x++) - { - for (int z = 0; z < LodUtil.CHUNK_WIDTH; z++) - { - long[] array = testRegion.getAllData(LodUtil.BLOCK_DETAIL_LEVEL, x + startingBlockPosX, z + startingBlockPosZ); - testChunkData[x][z] = array; - } - } - - // get the player data for this dimension folder - PlayerData testPlayerData = new PlayerData(testLevelFolder); - 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(playerData.playerBlockPos); - LOGGER.info("Player block position distance between saved sub dimension and first seen is [" + playerBlockDist + "]"); - - // check if the chunk is actually empty - if (isDataEmpty(testChunkData)) - { - String message = "The test chunk for dimension folder [" + LodUtil.shortenString(testLevelFolder.getName(), 8) + "] and chunk pos (" + playerChunkPos.getX() + "," + playerChunkPos.getZ() + ") is empty. This is expected if the position is outside the sub-dimension's generated area."; - LOGGER.info(message); - continue; - } - - // compare the two LODs - int equalDataPoints = 0; - int totalDataPointCount = 0; - for (int x = 0; x < LodUtil.CHUNK_WIDTH; x++) - { - for (int z = 0; z < LodUtil.CHUNK_WIDTH; z++) - { - for (int y = 0; y < newChunkData[x][z].length; y++) - { - if (newChunkData[x][z][y] == testChunkData[x][z][y]) - { - equalDataPoints++; - } - totalDataPointCount++; - - if (!DataPointUtil.doesItExist(newChunkData[x][z][y]) || !DataPointUtil.doesItExist(testChunkData[x][z][y])) - break; - } - } - } - - // determine if this world is closer to the newly loaded world - SubDimCompare subDimCompare = new SubDimCompare(equalDataPoints, totalDataPointCount, playerBlockDist, testLevelFolder); - if (mostSimilarSubDim == null || subDimCompare.compareTo(mostSimilarSubDim) > 0) - { - mostSimilarSubDim = subDimCompare; - } - - LOGGER.info("Sub dimension [" + LodUtil.shortenString(testLevelFolder.getName(), 8) + "...] is current dimension probability: " + LodUtil.shortenString(subDimCompare.getPercentEqual() + "", 5) + " (" + equalDataPoints + "/" + totalDataPointCount + ")"); -*/ + + // get a data source for this dimension + IClientLevelWrapper clientLevelWrapper = null; + if (clientLevelWrapper == null) + { + // TODO level shouldn't be null, continuing would probably cause a null pointer crash + LOGGER.info(this.getClass().getSimpleName() + " implementation incomplete. Unable to get LOD data file from generic folder without [" + IClientLevelWrapper.class.getSimpleName() + "]."); + break; + } + IDhLevel tempLevel = new DhClientLevel(new ClientOnlySaveStructure(), clientLevelWrapper); + IDataSourceProvider fileHandler = new DataFileHandler(tempLevel, testLevelFolder); + CompletableFuture testDataSource = fileHandler.read(new DhSectionPos(playerChunkPos)); + ILodDataSource lodDataSource = testDataSource.get(); + + + // convert the data source into a raw LOD data array + long[][][] testChunkData = new long[LodUtil.CHUNK_WIDTH][LodUtil.CHUNK_WIDTH][]; + boolean testLodDataExists = false; + for (int x = 0; x < LodUtil.CHUNK_WIDTH; x++) + { + for (int z = 0; z < LodUtil.CHUNK_WIDTH; z++) + { + SingleFullArrayView singleDataColumn = lodDataSource.tryGet(x, z); + if (singleDataColumn != null) + { + long[] rawSingleColumn = singleDataColumn.getRaw(); + testChunkData[x][z] = rawSingleColumn; + + + // does any LOD data exist in this chunk? + // if we have found at least one datapoint, don't check again + if (!testLodDataExists) + { + // does any data exist in this column? + for (long dataPoint : rawSingleColumn) + { + if (dataPoint != FullDataPoint.EMPTY_DATA_POINT) + { + // at least one datapoint exists in this chunk + testLodDataExists = true; + break; + } + } + } + } + } + } + + + // stop if the test chunk doesn't contain any data + if (!testLodDataExists) + { + String message = "The test chunk for dimension folder [" + LodUtil.shortenString(testLevelFolder.getName(), 8) + "] and chunk pos (" + playerChunkPos.getX() + "," + playerChunkPos.getZ() + ") is empty. This is expected if the position is outside the sub-dimension's generated area."; + LOGGER.info(message); + continue; + } + + + // get the player data for this dimension folder + PlayerData testPlayerData = new PlayerData(testLevelFolder); + 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(playerData.playerBlockPos); + LOGGER.info("Player block position distance between saved sub dimension and first seen is [" + playerBlockDist + "]"); + + + + // compare the two LODs + int equalDataPoints = 0; + int totalDataPointCount = 0; + for (int x = 0; x < LodUtil.CHUNK_WIDTH; x++) + { + for (int z = 0; z < LodUtil.CHUNK_WIDTH; z++) + { + for (int y = 0; y < newChunkData[x][z].length; y++) + { + if (newChunkData[x][z][y] == testChunkData[x][z][y]) + { + equalDataPoints++; + } + totalDataPointCount++; + } + } + } + + // determine if this world is closer to the newly loaded world + SubDimCompare subDimCompare = new SubDimCompare(equalDataPoints, totalDataPointCount, playerBlockDist, testLevelFolder); + if (mostSimilarSubDim == null || subDimCompare.compareTo(mostSimilarSubDim) > 0) + { + mostSimilarSubDim = subDimCompare; + } + + LOGGER.info("Sub dimension [" + LodUtil.shortenString(testLevelFolder.getName(), 8) + "...] is current dimension probability: " + LodUtil.shortenString(subDimCompare.getPercentEqual() + "", 5) + " (" + equalDataPoints + "/" + totalDataPointCount + ")"); } catch (Exception e) { @@ -293,10 +343,10 @@ public class LevelToFileMatcher implements AutoCloseable public boolean CanDetermineLevelFolder(IChunkWrapper chunk) { // we can only guess if the given chunk can be converted into a LOD - return false; //FIXME: Fix this after LodBUilder is done. - //return LodBuilder.canGenerateLodFromChunk(chunk); + return LodDataBuilder.canGenerateLodFromChunk(chunk); } + @Override public void close() { diff --git a/core/src/main/java/com/seibel/lod/core/file/SubDimCompare.java b/core/src/main/java/com/seibel/lod/core/file/SubDimCompare.java index e66feff80..e20be1806 100644 --- a/core/src/main/java/com/seibel/lod/core/file/SubDimCompare.java +++ b/core/src/main/java/com/seibel/lod/core/file/SubDimCompare.java @@ -31,10 +31,16 @@ import java.io.File; * is a sub dimension for the server "server_1" in the nether. * * @author James Seibel - * @version 2022-3-26 + * @version 2022-12-17 */ public class SubDimCompare implements Comparable { + /** + * the maximum distance in blocks a player can be away from the + * given dimension and still be considered in the same place. + */ + public static int MAX_SIMILAR_PLAYER_POS_DISTANCE_IN_BLOCKS = 3; + public int equalDataPoints = 0; public int totalDataPoints = 0; public int playerPosDist = 0; @@ -50,11 +56,8 @@ public class SubDimCompare implements Comparable this.folder = newSubDimFolder; } - /** returns a number between 0 (not equal) and 1 (totally equal) */ - public double getPercentEqual() - { - return (double) equalDataPoints / (double) totalDataPoints; - } + /** returns a number between 0 (no equal datapoint) and 1 (totally equal) */ + public double getPercentEqual() { return (double) equalDataPoints / (double) totalDataPoints; } @Override @@ -76,6 +79,7 @@ public class SubDimCompare implements Comparable public boolean isValidSubDim() { double minimumSimilarityRequired = Config.Client.Multiplayer.multiDimensionRequiredSimilarity.get(); - return this.getPercentEqual() >= minimumSimilarityRequired || this.playerPosDist <= 3; + return this.getPercentEqual() >= minimumSimilarityRequired + || this.playerPosDist <= MAX_SIMILAR_PLAYER_POS_DISTANCE_IN_BLOCKS; } }