Add hashed seed to server level folders to replace multiverse similarity
This commit is contained in:
@@ -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)
|
||||
|
||||
+1
-1
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -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.");
|
||||
}
|
||||
|
||||
+17
-17
@@ -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<String> 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<CompletableFuture<Void>> 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."
|
||||
);
|
||||
}
|
||||
|
||||
+1
-1
@@ -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
|
||||
|
||||
+4
-4
@@ -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
|
||||
|
||||
-100
@@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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<SubDimCompare>
|
||||
{
|
||||
/**
|
||||
* 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; }
|
||||
|
||||
}
|
||||
-386
@@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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. <br/>
|
||||
* 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<File> potentialLevelFolders;
|
||||
private final File levelsFolder;
|
||||
|
||||
|
||||
|
||||
//=============//
|
||||
// constructor //
|
||||
//=============//
|
||||
|
||||
public SubDimensionLevelMatcher(IClientLevelWrapper targetLevel, File levelsFolder, List<File> 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(); }
|
||||
|
||||
}
|
||||
-145
@@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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() + "]";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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()+"]"
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
+1
-1
@@ -291,7 +291,7 @@ public abstract class AbstractFullDataNetworkRequestQueue implements IDebugRende
|
||||
|
||||
public void addDebugMenuStringsToList(List<String> 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() + ")");
|
||||
}
|
||||
|
||||
|
||||
@@ -147,7 +147,7 @@ public class LodQuadTree extends QuadTree<LodRenderSection> 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
|
||||
{
|
||||
|
||||
@@ -110,7 +110,7 @@ public abstract class AbstractDhServerWorld<TDhServerLevel extends AbstractDhSer
|
||||
{
|
||||
for (TDhServerLevel level : this.dhLevelByLevelWrapper.values())
|
||||
{
|
||||
LOGGER.info("Unloading level [" + level.getLevelWrapper().getDimensionName() + "].");
|
||||
LOGGER.info("Unloading level [" + level.getLevelWrapper().getLevelIdString() + "].");
|
||||
|
||||
// level wrapper shouldn't be null, but just in case
|
||||
IServerLevelWrapper serverLevelWrapper = level.getServerLevelWrapper();
|
||||
|
||||
@@ -79,7 +79,7 @@ public class DhClientServerWorld extends AbstractDhServerWorld<DhClientServerLev
|
||||
LodUtil.assertTrue(serverLevelWrapper != null);
|
||||
if (!clientLevelWrapper.getDimensionType().equals(serverLevelWrapper.getDimensionType()))
|
||||
{
|
||||
LodUtil.assertNotReach("tryGetServerSideWrapper returned a level for a different dimension. ClientLevelWrapper dim: " + clientLevelWrapper.getDimensionName() + " ServerLevelWrapper dim: " + serverLevelWrapper.getDimensionName());
|
||||
LodUtil.assertNotReach("tryGetServerSideWrapper returned a level for a different dimension. ClientLevelWrapper dim: [" + clientLevelWrapper.getDimensionName() + "] ServerLevelWrapper dim: [" + serverLevelWrapper.getDimensionName() + "].");
|
||||
}
|
||||
|
||||
|
||||
@@ -148,7 +148,7 @@ public class DhClientServerWorld extends AbstractDhServerWorld<DhClientServerLev
|
||||
// close each level
|
||||
for (DhClientServerLevel level : this.dhLevels)
|
||||
{
|
||||
LOGGER.info("Unloading level " + level.getServerLevelWrapper().getDimensionName());
|
||||
LOGGER.info("Unloading level [" + level.getServerLevelWrapper().getLevelIdString() + "].");
|
||||
|
||||
// level wrapper shouldn't be null, but just in case
|
||||
IServerLevelWrapper serverLevelWrapper = level.getServerLevelWrapper();
|
||||
|
||||
@@ -131,7 +131,7 @@ public class DhClientWorld extends AbstractDhWorld implements IDhClientWorld
|
||||
|
||||
for (DhClientLevel dhClientLevel : this.levels.values())
|
||||
{
|
||||
LOGGER.info("Unloading level [" + dhClientLevel.getLevelWrapper().getDimensionName() + "].");
|
||||
LOGGER.info("Unloading level [" + dhClientLevel.getLevelWrapper().getLevelIdString() + "].");
|
||||
|
||||
// level wrapper shouldn't be null, but just in case
|
||||
IClientLevelWrapper clientLevelWrapper = dhClientLevel.getClientLevelWrapper();
|
||||
|
||||
+11
-2
@@ -26,6 +26,7 @@ import com.seibel.distanthorizons.core.pos.DhChunkPos;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
|
||||
import com.seibel.distanthorizons.coreapi.interfaces.dependencyInjection.IBindable;
|
||||
import com.seibel.distanthorizons.coreapi.util.StringUtil;
|
||||
|
||||
/** Can be either a Server world or a Client world. */
|
||||
public interface ILevelWrapper extends IDhApiLevelWrapper, IBindable
|
||||
@@ -36,7 +37,15 @@ public interface ILevelWrapper extends IDhApiLevelWrapper, IBindable
|
||||
|
||||
@Override
|
||||
String getDimensionName();
|
||||
|
||||
|
||||
long getHashedSeed();
|
||||
|
||||
/**
|
||||
* A combination of the dimension name and hashed seed
|
||||
* intended to uniquely identify this level.
|
||||
*/
|
||||
default String getLevelIdString() { return this.getDimensionName() + "_" + this.getHashedSeed(); }
|
||||
|
||||
@Override
|
||||
boolean hasCeiling();
|
||||
|
||||
@@ -61,7 +70,7 @@ public interface ILevelWrapper extends IDhApiLevelWrapper, IBindable
|
||||
/** Fired when the level is being unloaded. Doesn't unload the level. */
|
||||
void onUnload();
|
||||
|
||||
// TODO I don't like the circular reference, can we merge the level wrapper and DhLevels?
|
||||
// TODO James doesn't like this circular reference, can we merge the level wrapper and DhLevels?
|
||||
@Deprecated
|
||||
void setParentLevel(IDhLevel parentLevel);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user