From 1dffedccb91b5f78d6057ef3d6782ad484d0f315 Mon Sep 17 00:00:00 2001 From: James Seibel Date: Mon, 4 Nov 2024 18:29:03 -0600 Subject: [PATCH] Add hashed seed to server level folders to replace multiverse similarity --- .../core/api/internal/ClientApi.java | 4 +- .../api/internal/ClientPluginChannelApi.java | 2 +- .../FullDataToRenderDataTransformer.java | 4 +- .../FullDataSourceProviderV2.java | 34 +- .../GeneratedFullDataSourceProvider.java | 2 +- .../structure/ClientOnlySaveStructure.java | 8 +- .../file/subDimMatching/SubDimCompare.java | 100 ----- .../SubDimensionLevelMatcher.java | 386 ------------------ .../SubDimensionPlayerData.java | 145 ------- .../core/level/AbstractDhServerLevel.java | 10 +- .../core/level/DhClientLevel.java | 2 +- .../AbstractFullDataNetworkRequestQueue.java | 2 +- .../core/render/LodQuadTree.java | 2 +- .../core/world/AbstractDhServerWorld.java | 2 +- .../core/world/DhClientServerWorld.java | 4 +- .../core/world/DhClientWorld.java | 2 +- .../world/ILevelWrapper.java | 13 +- 17 files changed, 50 insertions(+), 672 deletions(-) delete mode 100644 core/src/main/java/com/seibel/distanthorizons/core/file/subDimMatching/SubDimCompare.java delete mode 100644 core/src/main/java/com/seibel/distanthorizons/core/file/subDimMatching/SubDimensionLevelMatcher.java delete mode 100644 core/src/main/java/com/seibel/distanthorizons/core/file/subDimMatching/SubDimensionPlayerData.java diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java index 0abcede4e..00897f8e4 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java @@ -207,7 +207,7 @@ public class ClientApi { try { - LOGGER.info("Unloading client level [" + level + "]-["+level.getDimensionName()+"]."); + LOGGER.info("Unloading client level [" + level.getClass().getSimpleName() + "]-["+level.getLevelIdString()+"]."); if (level instanceof IServerKeyedClientLevel) { @@ -253,7 +253,7 @@ public class ClientApi try { - LOGGER.info("Loading client level [" + levelWrapper + "]-["+levelWrapper.getDimensionName()+"]."); + LOGGER.info("Loading client level [" + levelWrapper + "]-["+levelWrapper.getLevelIdString()+"]."); AbstractDhWorld world = SharedApi.getAbstractDhWorld(); if (world != null) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java index 9bc607734..cf8fd1b91 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java @@ -101,7 +101,7 @@ public class ClientPluginChannelApi } else { - LOGGER.info("Unloading non-keyed level: [" + clientLevel.getDimensionName() + "]."); + LOGGER.info("Unloading non-keyed level: [" + clientLevel.getLevelIdString() + "]."); this.levelUnloadHandler.accept(clientLevel); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/FullDataToRenderDataTransformer.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/FullDataToRenderDataTransformer.java index bbfba806d..8af8bf9eb 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/FullDataToRenderDataTransformer.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/FullDataToRenderDataTransformer.java @@ -236,10 +236,10 @@ public class FullDataToRenderDataTransformer if (!brokenPos.contains(fullDataMapping.getPos())) { brokenPos.add(fullDataMapping.getPos()); - String dimName = level.getLevelWrapper().getDimensionName(); + String levelId = level.getLevelWrapper().getLevelIdString(); LOGGER.warn("Unable to get data point with id ["+id+"] " + "(Max possible ID: ["+fullDataMapping.getMaxValidId()+"]) " + - "for pos ["+fullDataMapping.getPos()+"] in dimension ["+dimName+"]. " + + "for pos ["+fullDataMapping.getPos()+"] in level ["+levelId+"]. " + "Error: ["+e.getMessage()+"]. " + "Further errors for this position won't be logged."); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataSourceProviderV2.java b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataSourceProviderV2.java index b9360801c..cb8538c51 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataSourceProviderV2.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataSourceProviderV2.java @@ -130,16 +130,16 @@ public class FullDataSourceProviderV2 DebugRenderer.register(this, Config.Client.Advanced.Debugging.DebugWireframe.showFullDataUpdateStatus); - String dimensionName = level.getLevelWrapper().getDimensionName(); + String levelId = level.getLevelWrapper().getLevelIdString(); // start migrating any legacy data sources present in the background - this.migrationThreadPool = ThreadUtil.makeRateLimitedThreadPool(1, MIGRATION_THREAD_NAME_PREFIX + "["+dimensionName+"]", Config.Common.MultiThreading.runTimeRatioForUpdatePropagatorThreads.get(), Thread.MIN_PRIORITY, (Semaphore) null); + this.migrationThreadPool = ThreadUtil.makeRateLimitedThreadPool(1, MIGRATION_THREAD_NAME_PREFIX + "["+levelId+"]", Config.Common.MultiThreading.runTimeRatioForUpdatePropagatorThreads.get(), Thread.MIN_PRIORITY, (Semaphore) null); this.migrationThreadPool.execute(this::convertLegacyDataSources); // update propagation doesn't need to be run on the server since only the highest detail level is needed if (SharedApi.getEnvironment() != EWorldEnvironment.SERVER_ONLY) { - this.updateQueueProcessor = ThreadUtil.makeSingleThreadPool("Parent Update Queue ["+dimensionName+"]"); + this.updateQueueProcessor = ThreadUtil.makeSingleThreadPool("Parent Update Queue ["+levelId+"]"); this.updateQueueProcessor.execute(this::runUpdateQueue); } else @@ -346,8 +346,8 @@ public class FullDataSourceProviderV2 private void convertLegacyDataSources() { - String dimensionName = this.level.getLevelWrapper().getDimensionName(); - LOGGER.info("Attempting to migrate data sources for: ["+dimensionName+"]-["+this.saveDir+"]..."); + String levelId = this.level.getLevelWrapper().getLevelIdString(); + LOGGER.info("Attempting to migrate data sources for: ["+levelId+"]-["+this.saveDir+"]..."); @@ -366,7 +366,7 @@ public class FullDataSourceProviderV2 this.showMigrationStartMessage(); - LOGGER.info("deleting [" + dimensionName + "] - ["+totalDeleteCount+"] unused data sources..."); + LOGGER.info("deleting [" + levelId + "] - ["+totalDeleteCount+"] unused data sources..."); this.legacyDeletionCount = totalDeleteCount; ArrayList unusedDataPosList = this.legacyFileHandler.repo.getUnusedDataSourcePositionStringList(50); @@ -384,7 +384,7 @@ public class FullDataSourceProviderV2 long endStart = System.currentTimeMillis(); long deleteTime = endStart - startTime; - LOGGER.info("Deleting [" + dimensionName + "] - [" + unusedCount + "/" + totalDeleteCount + "] in ["+deleteTime+"]ms ..."); + LOGGER.info("Deleting [" + levelId + "] - [" + unusedCount + "/" + totalDeleteCount + "] in ["+deleteTime+"]ms ..."); // a slight delay is added to prevent accidentally locking the database when deleting a lot of rows @@ -397,7 +397,7 @@ public class FullDataSourceProviderV2 } catch (InterruptedException ignore){} } - LOGGER.info("Done deleting [" + dimensionName + "] - ["+totalDeleteCount+"] unused data sources."); + LOGGER.info("Done deleting [" + levelId + "] - ["+totalDeleteCount+"] unused data sources."); } @@ -422,7 +422,7 @@ public class FullDataSourceProviderV2 int progressCount = 0; while (!legacyDataSourceList.isEmpty() && this.migrationThreadRunning.get()) { - LOGGER.info("Migrating [" + dimensionName + "] - [" + progressCount + "/" + totalMigrationCount + "]..."); + LOGGER.info("Migrating [" + levelId + "] - [" + progressCount + "/" + totalMigrationCount + "]..."); ArrayList> updateFutureList = new ArrayList<>(); for (int i = 0; i < legacyDataSourceList.size() && this.migrationThreadRunning.get(); i++) @@ -484,7 +484,7 @@ public class FullDataSourceProviderV2 } catch (Exception e) { - LOGGER.info("migration stopped due to error for: ["+dimensionName+"]-["+this.saveDir+"], error: ["+e.getMessage()+"].", e); + LOGGER.info("migration stopped due to error for: ["+levelId+"]-["+this.saveDir+"], error: ["+e.getMessage()+"].", e); this.showMigrationEndMessage(false); this.migrationStoppedWithError = true; } @@ -492,13 +492,13 @@ public class FullDataSourceProviderV2 { if (this.migrationThreadRunning.get()) { - LOGGER.info("migration complete for: ["+dimensionName+"]-["+this.saveDir+"]."); + LOGGER.info("migration complete for: ["+levelId+"]-["+this.saveDir+"]."); this.showMigrationEndMessage(true); this.migrationCount = 0; } else { - LOGGER.info("migration stopped for: ["+dimensionName+"]-["+this.saveDir+"]."); + LOGGER.info("migration stopped for: ["+levelId+"]-["+this.saveDir+"]."); this.showMigrationEndMessage(false); this.migrationStoppedWithError = true; } @@ -525,9 +525,9 @@ public class FullDataSourceProviderV2 } this.migrationStartMessageQueued = true; - String dimName = this.level.getLevelWrapper().getDimensionName(); + String levelId = this.level.getLevelWrapper().getLevelIdString(); ClientApi.INSTANCE.showChatMessageNextFrame( - "Old Distant Horizons data is being migrated for ["+dimName+"]. \n" + + "Old Distant Horizons data is being migrated for ["+levelId+"]. \n" + "While migrating LODs may load slowly \n" + "and DH world gen will be disabled. \n" + "You can see migration progress in the F3 menu." @@ -536,16 +536,16 @@ public class FullDataSourceProviderV2 private void showMigrationEndMessage(boolean success) { - String dimName = this.level.getLevelWrapper().getDimensionName(); + String levelId = this.level.getLevelWrapper().getLevelIdString(); if (success) { - ClientApi.INSTANCE.showChatMessageNextFrame("Distant Horizons data migration for ["+dimName+"] completed."); + ClientApi.INSTANCE.showChatMessageNextFrame("Distant Horizons data migration for ["+levelId+"] completed."); } else { ClientApi.INSTANCE.showChatMessageNextFrame( - "Distant Horizons data migration for ["+dimName+"] stopped. \n" + + "Distant Horizons data migration for ["+levelId+"] stopped. \n" + "Some data may not have been migrated." ); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/GeneratedFullDataSourceProvider.java b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/GeneratedFullDataSourceProvider.java index 65fb7b59a..37b6ac504 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/GeneratedFullDataSourceProvider.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/GeneratedFullDataSourceProvider.java @@ -145,7 +145,7 @@ public class GeneratedFullDataSourceProvider extends FullDataSourceProviderV2 im { boolean oldQueueExists = this.worldGenQueueRef.compareAndSet(null, newWorldGenQueue); LodUtil.assertTrue(oldQueueExists, "previous world gen queue is still here!"); - LOGGER.info("Set world gen queue for level [" + this.level.getLevelWrapper().getDimensionName() + "]."); + LOGGER.info("Set world gen queue for level [" + this.level.getLevelWrapper().getLevelIdString() + "]."); } @Override diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/structure/ClientOnlySaveStructure.java b/core/src/main/java/com/seibel/distanthorizons/core/file/structure/ClientOnlySaveStructure.java index a6feccefb..fb4db25ce 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/structure/ClientOnlySaveStructure.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/structure/ClientOnlySaveStructure.java @@ -80,14 +80,14 @@ public class ClientOnlySaveStructure implements ISaveStructure if (newLevelWrapper instanceof IServerKeyedClientLevel) { IServerKeyedClientLevel keyedClientLevel = (IServerKeyedClientLevel) newLevelWrapper; - LOGGER.info("Loading level [" + newLevelWrapper.getDimensionName() + "] with key: [" + keyedClientLevel.getServerLevelKey() + "]."); + LOGGER.info("Loading level [" + newLevelWrapper.getLevelIdString() + "] with key: [" + keyedClientLevel.getServerLevelKey() + "]."); // This world was identified by the server directly, so we can know for sure which folder to use. - saveFolder = getSaveFolderFromDimensionName(keyedClientLevel.getServerLevelKey()); + saveFolder = getSaveFolderByLevelId(keyedClientLevel.getServerLevelKey()); } else { // get the default folder - saveFolder = getSaveFolderFromDimensionName(levelWrapper.getDimensionName()); + saveFolder = getSaveFolderByLevelId(levelWrapper.getLevelIdString()); } // Allow API users to override the save folder @@ -160,7 +160,7 @@ public class ClientOnlySaveStructure implements ISaveStructure } - private static File getSaveFolderFromDimensionName(String dimensionName) + private static File getSaveFolderByLevelId(String dimensionName) { String path = MC_SHARED.getInstallationDirectory().getPath() + File.separatorChar + SERVER_DATA_FOLDER_NAME + File.separatorChar diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/subDimMatching/SubDimCompare.java b/core/src/main/java/com/seibel/distanthorizons/core/file/subDimMatching/SubDimCompare.java deleted file mode 100644 index 3b73031aa..000000000 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/subDimMatching/SubDimCompare.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * This file is part of the Distant Horizons mod - * licensed under the GNU LGPL v3 License. - * - * Copyright (C) 2020-2023 James Seibel - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.seibel.distanthorizons.core.file.subDimMatching; - -import com.seibel.distanthorizons.core.config.Config; -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-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; - 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 (no equal datapoint) and 1 (totally equal) */ - public double getPercentEqual() - { - // its possible the comparison didn't find any data points - if (this.totalDataPoints != 0) - { - return (double) this.equalDataPoints / (double) this.totalDataPoints; - } - else - { - return 0; - } - } - - - @Override - public int compareTo(@NotNull SubDimCompare other) - { - if (this.equalDataPoints != other.equalDataPoints) - { - // compare based on data points - return Double.compare(this.getPercentEqual(), other.getPercentEqual()); - } - 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 = 0.5;//Config.Client.Advanced.Multiplayer.multiverseSimilarityRequiredPercent.get(); - return this.getPercentEqual() >= minimumSimilarityRequired - || this.playerPosDist <= MAX_SIMILAR_PLAYER_POS_DISTANCE_IN_BLOCKS; - } - - @Override - public String toString() { return this.equalDataPoints + "/" + this.totalDataPoints + ": " + this.getPercentEqual() + " playerPos: " + this.playerPosDist; } - -} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/subDimMatching/SubDimensionLevelMatcher.java b/core/src/main/java/com/seibel/distanthorizons/core/file/subDimMatching/SubDimensionLevelMatcher.java deleted file mode 100644 index d98287125..000000000 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/subDimMatching/SubDimensionLevelMatcher.java +++ /dev/null @@ -1,386 +0,0 @@ -/* - * This file is part of the Distant Horizons mod - * licensed under the GNU LGPL v3 License. - * - * Copyright (C) 2020-2023 James Seibel - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.seibel.distanthorizons.core.file.subDimMatching; - -import com.seibel.distanthorizons.core.config.Config; -import com.seibel.distanthorizons.core.dataObjects.fullData.FullDataPointIdMap; -import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2; -import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; -import com.seibel.distanthorizons.core.file.structure.ClientOnlySaveStructure; -import com.seibel.distanthorizons.core.generation.DhLightingEngine; -import com.seibel.distanthorizons.core.level.DhClientLevel; -import com.seibel.distanthorizons.core.level.IDhLevel; -import com.seibel.distanthorizons.core.logging.ConfigBasedLogger; -import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; -import com.seibel.distanthorizons.core.pos.DhChunkPos; -import com.seibel.distanthorizons.core.pos.DhSectionPos; -import com.seibel.distanthorizons.core.util.FullDataPointUtil; -import com.seibel.distanthorizons.core.util.LodUtil; -import com.seibel.distanthorizons.core.util.ThreadUtil; -import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper; -import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper; -import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; -import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper; -import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper; -import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; -import com.seibel.distanthorizons.coreapi.util.StringUtil; -import it.unimi.dsi.fastutil.longs.LongArrayList; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import java.io.File; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; -import java.util.UUID; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.atomic.AtomicBoolean; - -/** - * 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 - */ -@Deprecated -public class SubDimensionLevelMatcher implements AutoCloseable -{ - private static final IMinecraftClientWrapper MC_CLIENT = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class); - public static final Logger LOGGER = DhLoggerBuilder.getLogger(); - //new ConfigBasedLogger(LogManager.getLogger(), - //() -> Config.Common.Logging.logFileSubDimEvent.get()); - - private final ExecutorService matcherThread = ThreadUtil.makeSingleThreadPool("Sub Dimension Matcher"); - - private SubDimensionPlayerData playerData = null; - private SubDimensionPlayerData firstSeenPlayerData = null; - - /** If true the LodDimensionFileHelper is attempting to determine the folder for this dimension */ - private final AtomicBoolean determiningWorldFolder = new AtomicBoolean(false); - private final IClientLevelWrapper currentClientLevel; - private volatile File foundLevelFile = null; - private final List potentialLevelFolders; - private final File levelsFolder; - - - - //=============// - // constructor // - //=============// - - public SubDimensionLevelMatcher(IClientLevelWrapper targetLevel, File levelsFolder, List potentialLevelFolders) - { - this.currentClientLevel = targetLevel; - this.potentialLevelFolders = potentialLevelFolders; - this.levelsFolder = levelsFolder; - - if (potentialLevelFolders.size() == 0) - { - String newId = UUID.randomUUID().toString(); - LOGGER.info("No potential level files found. Creating a new sub dimension with the ID ["+ StringUtil.shortenString(newId, 8)+"]..."); - this.foundLevelFile = this.CreateSubDimFolder(newId); - } - } - - - - //==============// - // level finder // - //==============// - - public boolean isFindingLevel(ILevelWrapper level) { return Objects.equals(level, this.currentClientLevel); } - - /** May return null if the level isn't known yet */ - public File tryGetLevel() - { - this.tryGetLevelInternalAsync(); - return this.foundLevelFile; - } - private void tryGetLevelInternalAsync() - { - if (this.foundLevelFile != null) - { - return; - } - - // prevent multiple threads running at the same time - if (this.determiningWorldFolder.getAndSet(true)) - { - return; - } - - - this.matcherThread.submit(() -> - { - try - { - // attempt to get the file handler - File saveDir = this.attemptToDetermineSubDimensionFolder(); - if (saveDir != null) - { - this.foundLevelFile = saveDir; - } - } - catch (IOException e) - { - LOGGER.error("Unable to set the dimension file handler for level [" + this.currentClientLevel + "]. Error: ", e); - } - finally - { - // make sure we unlock this method - this.determiningWorldFolder.set(false); - } - }); - } - - /** - * 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. - * - * @throws IOException if the folder doesn't exist or can't be accessed - */ - public File attemptToDetermineSubDimensionFolder() throws IOException - { - // Update PlayerData - SubDimensionPlayerData newPlayerData = SubDimensionPlayerData.tryGetPlayerData(MC_CLIENT); - if (newPlayerData != null) - { - if (this.firstSeenPlayerData == null) - { - this.firstSeenPlayerData = newPlayerData; - } - this.playerData = newPlayerData; - } - - - - //================================// - // generate a LOD to test against // - //================================// - - // attempt to get a chunk at the player's pos - IChunkWrapper newlyLoadedChunk = MC_CLIENT.getWrappedClientLevel().tryGetChunk(new DhChunkPos(this.playerData.playerBlockPos)); - if (newlyLoadedChunk == null) - { - return null; - } - DhLightingEngine.INSTANCE.lightChunk(newlyLoadedChunk, new ArrayList<>(), MC_CLIENT.getWrappedClientLevel().hasSkyLight() ? LodUtil.MAX_MC_LIGHT : LodUtil.MIN_MC_LIGHT); - - // build the chunk LOD - FullDataSourceV2 newChunkSizedFullDataView = FullDataSourceV2.createFromChunk(newlyLoadedChunk); - // convert to a data source for easier comparing - FullDataSourceV2 newDataSource = FullDataSourceV2.createEmpty(DhSectionPos.encodeContaining(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, this.playerData.playerBlockPos)); - newDataSource.update(newChunkSizedFullDataView); - - - - //================================// - // test each known sub-dim folder // - //================================// - - // log the start of this attempt - LOGGER.info("Attempting to determine sub-dimension for [" + MC_CLIENT.getWrappedClientLevel().getDimensionName() + "]"); - LOGGER.info("Player block pos in dimension: [" + this.playerData.playerBlockPos.getX() + "," + this.playerData.playerBlockPos.getY() + "," + this.playerData.playerBlockPos.getZ() + "]"); - LOGGER.info("Potential Sub Dimension folders: [" + this.potentialLevelFolders.size() + "]"); - - SubDimCompare mostSimilarSubDim = null; - for (File testLevelFolder : this.potentialLevelFolders) - { - LOGGER.info("Testing level folder: [" + StringUtil.shortenString(testLevelFolder.getName(), 8) + "]"); - - FullDataSourceV2 testFullDataSource = null; - try - { - // get the data source to compare against - try (IDhLevel tempLevel = new DhClientLevel(new ClientOnlySaveStructure(), this.currentClientLevel, testLevelFolder, false, null)) - { - testFullDataSource = tempLevel.getFullDataProvider().getAsync(DhSectionPos.encodeContaining(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, this.playerData.playerBlockPos)).join(); - if (testFullDataSource == null) - { - continue; - } - } - - - // confirm both data sources have the same section pos - long newSectionChunkPos = DhSectionPos.convertToDetailLevel(newDataSource.getPos(), DhSectionPos.SECTION_CHUNK_DETAIL_LEVEL); - long testSectionChunkPos = DhSectionPos.convertToDetailLevel(testFullDataSource.getPos(), DhSectionPos.SECTION_CHUNK_DETAIL_LEVEL); - LodUtil.assertTrue(newSectionChunkPos == testSectionChunkPos, "data source positions don't match"); - - - - // compare the data sources - int equalDataPoints = 0; - int totalDataPointCount = 0; - for (int x = 0; x < FullDataSourceV2.WIDTH; x++) - { - for (int z = 0; z < FullDataSourceV2.WIDTH; z++) - { - LongArrayList newColumn = newDataSource.get(x, z); - LongArrayList testColumn = testFullDataSource.get(x, z); - - if (newColumn != null && testColumn != null) - { - // compare each data point in the column - - FullDataPointIdMap newDataMap = newDataSource.mapping; - FullDataPointIdMap testDataMap = testFullDataSource.mapping; - - // use min to prevent going out of bounds - int minColumnIndex = Math.min(newColumn.size(), testColumn.size()); - for (int i = 0; i < minColumnIndex; i++) - { - long newDataPoint = newColumn.getLong(i); - long testDataPoint = testColumn.getLong(i); - - int newId = FullDataPointUtil.getId(newDataPoint); - int testId = FullDataPointUtil.getId(testDataPoint); - - - // bottom Y - int newBottom = FullDataPointUtil.getBottomY(newDataPoint); - int testBottom = FullDataPointUtil.getBottomY(testDataPoint); - if (newBottom == testBottom) - { - equalDataPoints++; - } - totalDataPointCount++; - - // height - int newHeight = FullDataPointUtil.getHeight(newDataPoint); - int testHeight = FullDataPointUtil.getHeight(testDataPoint); - if (newHeight == testHeight) - { - equalDataPoints++; - } - totalDataPointCount++; - - // biome - IBiomeWrapper newBiome = newDataMap.getBiomeWrapper(newId); - IBiomeWrapper testBiome = testDataMap.getBiomeWrapper(testId); - if (newBiome.equals(testBiome)) - { - equalDataPoints++; - } - totalDataPointCount++; - - // block - IBlockStateWrapper newBlock = newDataMap.getBlockStateWrapper(newId); - IBlockStateWrapper testBlock = testDataMap.getBlockStateWrapper(testId); - if (newBlock.equals(testBlock)) - { - equalDataPoints++; - } - totalDataPointCount++; - - // ignore light values - // since we are using the DH lighting engine and only 1 chunk the values will never be the same - } - } - else if (newColumn != null) - { - // missing test column - totalDataPointCount += newColumn.size(); - } - else - { - // new column present, test absent, can't compare - } - } - } - - - - // get the player data for this dimension folder - SubDimensionPlayerData testPlayerData = new SubDimensionPlayerData(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(this.playerData.playerBlockPos); - LOGGER.info("Player block position distance between saved sub dimension and first seen is [" + playerBlockDist + "]"); - - // 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; - } - - - String subDimShortName = StringUtil.shortenString(testLevelFolder.getName(), 8); // variables are separated out for easier debugging - String equalPercent = StringUtil.shortenString(mostSimilarSubDim.getPercentEqual()+"", 5); - LOGGER.info("Sub dimension ["+subDimShortName+"...] is current dimension probability: "+equalPercent+" ("+equalDataPoints+"/"+totalDataPointCount+")"); - } - catch (Exception e) - { - // this sub dimension isn't formatted correctly - // for now we are just assuming it is an unrelated file - LOGGER.warn("Error checking level: "+e.getMessage(), e); - } - finally - { - if (testFullDataSource != null) - { - try { testFullDataSource.close(); } catch (Exception ignore) {} - } - } - } - - - - //================================// - // return the found sub dimension // - //================================// - - // the first seen player data is no longer needed, the sub dimension has been determined - this.firstSeenPlayerData = null; - if (mostSimilarSubDim != null && mostSimilarSubDim.isValidSubDim()) - { - // we found a sub dim folder that is similar, use it - - LOGGER.info("Sub Dimension set to: [" + StringUtil.shortenString(mostSimilarSubDim.folder.getName(), 8) + "...] with an equality of [" + mostSimilarSubDim.getPercentEqual() + "]"); - return mostSimilarSubDim.folder; - } - else - { - // no sub dim folder, create a new one - - String newId = UUID.randomUUID().toString(); - - double highestEqualityPercent = mostSimilarSubDim != null ? mostSimilarSubDim.getPercentEqual() : 0; - String message = "No suitable sub dimension found. The highest equality was [" + StringUtil.shortenString(highestEqualityPercent + "", 5) + "]. Creating a new sub dimension with ID: " + StringUtil.shortenString(newId, 8) + "..."; - LOGGER.info(message); - - File folder = this.CreateSubDimFolder(newId); - folder.mkdirs(); - return folder; - } - } - - - private File CreateSubDimFolder(String subDimId) { return new File(this.levelsFolder.getPath() + File.separatorChar + this.currentClientLevel.getDimensionName(), subDimId); } - - @Override - public void close() { this.matcherThread.shutdownNow(); } - -} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/subDimMatching/SubDimensionPlayerData.java b/core/src/main/java/com/seibel/distanthorizons/core/file/subDimMatching/SubDimensionPlayerData.java deleted file mode 100644 index e62e25bc3..000000000 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/subDimMatching/SubDimensionPlayerData.java +++ /dev/null @@ -1,145 +0,0 @@ -/* - * This file is part of the Distant Horizons mod - * licensed under the GNU LGPL v3 License. - * - * Copyright (C) 2020-2023 James Seibel - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.seibel.distanthorizons.core.file.subDimMatching; - - -import com.electronwill.nightconfig.core.file.CommentedFileConfig; -import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; -import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos; -import com.seibel.distanthorizons.core.wrapperInterfaces.IWrapperFactory; -import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; - -import org.jetbrains.annotations.Nullable; -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 SubDimensionPlayerData -{ - public static final IWrapperFactory FACTORY = SingletonInjector.INSTANCE.get(IWrapperFactory.class); - - public static final String PLAYER_DATA_FILE_NAME = "_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 DhBlockPos 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 DhBlockPos worldSpawnPointBlockPos; - - - - @Nullable - public static SubDimensionPlayerData tryGetPlayerData(IMinecraftClientWrapper mcClient) - { - if (!mcClient.playerExists()) - { - return null; - } - - try - { - return new SubDimensionPlayerData(mcClient); - } - catch (RuntimeException e) - { - // Player no longer exists due to concurrency. FIXME: Remember here is called not on main thread!!! - return null; - } - } - - private SubDimensionPlayerData(IMinecraftClientWrapper mc) - { - this.updateData(mc); - } - - public SubDimensionPlayerData(File dimensionFolder) - { - File file = getFileForDimensionFolder(dimensionFolder); - try (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 = new DhBlockPos(x, y, z); - } - else - { - this.playerBlockPos = new DhBlockPos(0, 0, 0); - } - } - } - - - - public static File getFileForDimensionFolder(File file) { return new File(file.getPath() + File.separatorChar + PLAYER_DATA_FILE_NAME); } - - - /** 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(); - - if (this.playerBlockPos == null) - { - throw new RuntimeException("No player block pos!"); - } - } - - /** Writes everything from this object to the file given. */ - public void toTomlFile(CommentedFileConfig toml) - { - // player block pos - toml.add(PLAYER_BLOCK_POS_X_PATH, this.playerBlockPos.getX()); - toml.add(PLAYER_BLOCK_POS_Y_PATH, this.playerBlockPos.getY()); - toml.add(PLAYER_BLOCK_POS_Z_PATH, this.playerBlockPos.getZ()); - - toml.save(); - } - - - @Override - public String toString() - { - return "PlayerBlockPos: [" + this.playerBlockPos.getX() + "," + this.playerBlockPos.getY() + "," + this.playerBlockPos.getZ() + "]"; - } - -} - diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/AbstractDhServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/AbstractDhServerLevel.java index dfb21ed31..d08e8070c 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/AbstractDhServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/AbstractDhServerLevel.java @@ -114,7 +114,7 @@ public abstract class AbstractDhServerLevel extends AbstractDhLevel implements I continue; } - NETWORK_LOGGER.debug("["+this.serverLevelWrapper.getDimensionName()+"] Fulfilled request group ["+entry.getKey()+"]"); + NETWORK_LOGGER.debug("["+this.serverLevelWrapper.getLevelIdString()+"] Fulfilled request group ["+entry.getKey()+"]"); // Make this group unavailable for adding into this.requestGroupByPos.remove(entry.getKey()); @@ -228,7 +228,7 @@ public abstract class AbstractDhServerLevel extends AbstractDhLevel implements I FullDataSourceRequestMessage requestMessage = requestGroup.requestMessages.remove(msg.futureId); if (requestGroup.requestMessages.isEmpty()) { - NETWORK_LOGGER.debug("["+this.serverLevelWrapper.getDimensionName()+"] Cancelled request group ["+DhSectionPos.toString(requestMessage.sectionPos)+"]."); + NETWORK_LOGGER.debug("["+this.serverLevelWrapper.getLevelIdString()+"] Cancelled request group ["+DhSectionPos.toString(requestMessage.sectionPos)+"]."); this.requestGroupByPos.remove(requestMessage.sectionPos); this.serverside.fullDataFileHandler.removeRetrievalRequestIf(pos -> pos == requestMessage.sectionPos); } @@ -307,7 +307,7 @@ public abstract class AbstractDhServerLevel extends AbstractDhLevel implements I { DataSourceRequestGroup newGroup = new DataSourceRequestGroup(); this.tryFulfillDataSourceRequestGroup(newGroup, pos); - NETWORK_LOGGER.debug("["+this.serverLevelWrapper.getDimensionName()+"] Created request group for pos ["+DhSectionPos.toString(pos)+"]."); + NETWORK_LOGGER.debug("["+this.serverLevelWrapper.getLevelIdString()+"] Created request group for pos ["+DhSectionPos.toString(pos)+"]."); return newGroup; }); @@ -353,8 +353,8 @@ public abstract class AbstractDhServerLevel extends AbstractDhLevel implements I new InvalidLevelException( "Generation not allowed. " + "Requested dimension: ["+((ILevelRelatedMessage) message).getLevelName()+"], " + - "player dimension: ["+message.getSession().serverPlayer.getLevel().getDimensionName()+"], " + - "handler dimension: ["+this.getLevelWrapper().getDimensionName()+"]" + "player dimension: ["+message.getSession().serverPlayer.getLevel().getLevelIdString()+"], " + + "handler dimension: ["+this.getLevelWrapper().getLevelIdString()+"]" ) ); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java index b5823ddda..4fd2ee944 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java @@ -338,7 +338,7 @@ public class DhClientLevel extends AbstractDhLevel implements IDhClientLevel //================// @Override - public String toString() { return "DhClientLevel{"+this.getClientLevelWrapper().getDimensionName()+"}"; } + public String toString() { return "DhClientLevel{"+this.getClientLevelWrapper().getLevelIdString()+"}"; } @Override public void close() diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataNetworkRequestQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataNetworkRequestQueue.java index 6c762a0ab..dfb9e9e62 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataNetworkRequestQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataNetworkRequestQueue.java @@ -291,7 +291,7 @@ public abstract class AbstractFullDataNetworkRequestQueue implements IDebugRende public void addDebugMenuStringsToList(List messageList) { - messageList.add(this.getQueueName() + " [" + this.level.getClientLevelWrapper().getDimensionName() + "]"); + messageList.add(this.getQueueName() + " [" + this.level.getClientLevelWrapper().getLevelIdString() + "]"); messageList.add("Requests: " + this.finishedRequests + " / " + (this.getWaitingTaskCount() + this.finishedRequests.get()) + " (failed: " + this.failedRequests + ", rate limit: " + this.getRequestRateLimit() + ")"); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/LodQuadTree.java b/core/src/main/java/com/seibel/distanthorizons/core/render/LodQuadTree.java index c71bd2c03..2d7eac30d 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/LodQuadTree.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/LodQuadTree.java @@ -147,7 +147,7 @@ public class LodQuadTree extends QuadTree implements IDebugRen } catch (Exception e) { - LOGGER.error("Quad Tree tick exception for dimension: " + this.level.getLevelWrapper().getDimensionName() + ", exception: " + e.getMessage(), e); + LOGGER.error("Quad Tree tick exception for level: [" + this.level.getLevelWrapper().getLevelIdString() + "], error: [" + e.getMessage() + "].", e); } finally { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/world/AbstractDhServerWorld.java b/core/src/main/java/com/seibel/distanthorizons/core/world/AbstractDhServerWorld.java index 780c68a5f..eefa32141 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/world/AbstractDhServerWorld.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/world/AbstractDhServerWorld.java @@ -110,7 +110,7 @@ public abstract class AbstractDhServerWorld