Minor refactors for LevelToFileMatcher and PlayerData
This commit is contained in:
@@ -17,95 +17,126 @@ import java.util.UUID;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
public class LevelToFileMatcher implements AutoCloseable {
|
||||
private static final IMinecraftClientWrapper MC_CLIENT = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
|
||||
public static final ConfigBasedLogger LOGGER = new ConfigBasedLogger(LogManager.getLogger(),
|
||||
() -> Config.Client.Advanced.Debugging.DebugSwitch.logFileSubDimEvent.get());
|
||||
|
||||
private final ExecutorService matcherThread = LodUtil.makeSingleThreadPool("Level-To-File-Matcher");
|
||||
|
||||
private PlayerData playerData = null;
|
||||
private PlayerData firstSeenPlayerData = null;
|
||||
|
||||
/** If true the LodDimensionFileHelper is attempting to determine the folder for this dimension */
|
||||
private final AtomicBoolean determiningWorldFolder = new AtomicBoolean(false);
|
||||
private final ILevelWrapper currentLevel;
|
||||
private volatile File foundLevel = null;
|
||||
private final File[] potentialFiles;
|
||||
private final File levelsFolder;
|
||||
|
||||
public LevelToFileMatcher(ILevelWrapper targetWorld, File levelsFolder, File[] potentialFiles) {
|
||||
this.currentLevel = targetWorld;
|
||||
this.potentialFiles = potentialFiles;
|
||||
this.levelsFolder = levelsFolder;
|
||||
if (potentialFiles.length == 0) {
|
||||
String newId = UUID.randomUUID().toString();
|
||||
LOGGER.info("No potential level files found. Creating a new sub dimension with ID {}...",
|
||||
LodUtil.shortenString(newId, 8));
|
||||
foundLevel = new File(levelsFolder, newId);
|
||||
}
|
||||
}
|
||||
|
||||
// May return null, where at this moment the level is not yet known
|
||||
public File tryGetLevel() {
|
||||
tick();
|
||||
return foundLevel;
|
||||
}
|
||||
|
||||
public boolean isFindingLevel(ILevelWrapper level) {
|
||||
return Objects.equals(level, currentLevel);
|
||||
}
|
||||
|
||||
private void tick() {
|
||||
if (foundLevel != null) return;
|
||||
// prevent multiple threads running at the same time
|
||||
if (determiningWorldFolder.getAndSet(true)) return;
|
||||
matcherThread.submit(() ->
|
||||
{
|
||||
try {
|
||||
// attempt to get the file handler
|
||||
File saveDir = attemptToDetermineSubDimensionFolder();
|
||||
if (saveDir != null) foundLevel = saveDir;
|
||||
} catch (IOException e) {
|
||||
LOGGER.error("Unable to set the dimension file handler for level [" + currentLevel + "]. Error: ", e);
|
||||
} finally {
|
||||
// make sure we unlock this method
|
||||
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
|
||||
PlayerData data = PlayerData.tryGetPlayerData(MC_CLIENT);
|
||||
if (data != null) {
|
||||
if (firstSeenPlayerData == null) {
|
||||
firstSeenPlayerData = data;
|
||||
}
|
||||
playerData = data;
|
||||
}
|
||||
}
|
||||
|
||||
// relevant positions
|
||||
DhChunkPos playerChunkPos = new DhChunkPos(playerData.playerBlockPos);
|
||||
int startingBlockPosX = playerChunkPos.getMinBlockX();
|
||||
int startingBlockPosZ = playerChunkPos.getMinBlockZ();
|
||||
|
||||
// chunk from the newly loaded level
|
||||
IChunkWrapper newlyLoadedChunk = MC_CLIENT.getWrappedClientWorld().tryGetChunk(playerChunkPos);
|
||||
// check if this chunk is valid to test
|
||||
if (!CanDetermineLevelFolder(newlyLoadedChunk))
|
||||
return null;
|
||||
|
||||
//TODO: Compute a ChunkData from current chunk.
|
||||
/**
|
||||
* Used to support multiple worlds using the same dimension type. <br/>
|
||||
* This is specifically needed for servers running the Multiverse plugin (or similar).
|
||||
*/
|
||||
public class LevelToFileMatcher implements AutoCloseable
|
||||
{
|
||||
private static final IMinecraftClientWrapper MC_CLIENT = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
|
||||
public static final ConfigBasedLogger LOGGER = new ConfigBasedLogger(LogManager.getLogger(),
|
||||
() -> Config.Client.Advanced.Debugging.DebugSwitch.logFileSubDimEvent.get());
|
||||
|
||||
private final ExecutorService matcherThread = LodUtil.makeSingleThreadPool("Level-To-File-Matcher");
|
||||
|
||||
private PlayerData playerData = null;
|
||||
private PlayerData firstSeenPlayerData = null;
|
||||
|
||||
/** If true the LodDimensionFileHelper is attempting to determine the folder for this dimension */
|
||||
private final AtomicBoolean determiningWorldFolder = new AtomicBoolean(false);
|
||||
private final ILevelWrapper currentLevel;
|
||||
private volatile File foundLevel = null;
|
||||
private final File[] potentialFiles;
|
||||
private final File levelsFolder;
|
||||
|
||||
|
||||
|
||||
public LevelToFileMatcher(ILevelWrapper targetWorld, File levelsFolder, File[] potentialFiles)
|
||||
{
|
||||
this.currentLevel = targetWorld;
|
||||
this.potentialFiles = potentialFiles;
|
||||
this.levelsFolder = levelsFolder;
|
||||
|
||||
if (potentialFiles.length == 0)
|
||||
{
|
||||
String newId = UUID.randomUUID().toString();
|
||||
LOGGER.info("No potential level files found. Creating a new sub dimension with the ID [{}]...",
|
||||
LodUtil.shortenString(newId, 8));
|
||||
this.foundLevel = new File(levelsFolder, newId);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public boolean isFindingLevel(ILevelWrapper level) { return Objects.equals(level, this.currentLevel); }
|
||||
|
||||
/** May return null if the level isn't known yet */
|
||||
public File tryGetLevel()
|
||||
{
|
||||
this.tryGetLevelInternal();
|
||||
return this.foundLevel;
|
||||
}
|
||||
private void tryGetLevelInternal()
|
||||
{
|
||||
if (this.foundLevel != 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.foundLevel = saveDir;
|
||||
}
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
LOGGER.error("Unable to set the dimension file handler for level [" + this.currentLevel + "]. 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
|
||||
PlayerData data = PlayerData.tryGetPlayerData(MC_CLIENT);
|
||||
if (data != null)
|
||||
{
|
||||
if (this.firstSeenPlayerData == null)
|
||||
{
|
||||
this.firstSeenPlayerData = data;
|
||||
}
|
||||
this.playerData = data;
|
||||
}
|
||||
}
|
||||
|
||||
// relevant positions
|
||||
DhChunkPos playerChunkPos = new DhChunkPos(this.playerData.playerBlockPos);
|
||||
int startingBlockPosX = playerChunkPos.getMinBlockX();
|
||||
int startingBlockPosZ = playerChunkPos.getMinBlockZ();
|
||||
|
||||
// chunk from the newly loaded level
|
||||
IChunkWrapper newlyLoadedChunk = MC_CLIENT.getWrappedClientWorld().tryGetChunk(playerChunkPos);
|
||||
// check if this chunk is valid to test
|
||||
if (!this.CanDetermineLevelFolder(newlyLoadedChunk))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
//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);
|
||||
@@ -145,20 +176,20 @@ public class LevelToFileMatcher implements AutoCloseable {
|
||||
}
|
||||
return null;
|
||||
}*/
|
||||
|
||||
|
||||
// compare each world with the newly loaded one
|
||||
SubDimCompare mostSimilarSubDim = null;
|
||||
|
||||
File[] levelFolders = potentialFiles;
|
||||
LOGGER.info("Potential Sub Dimension folders: [" + levelFolders.length + "]");
|
||||
for (File testLevelFolder : levelFolders)
|
||||
{
|
||||
LOGGER.info("Testing level folder: [" + LodUtil.shortenString(testLevelFolder.getName(), 8) + "]");
|
||||
try
|
||||
{
|
||||
// TODO: Try load a data file overlapping the playerChunkPos from ClientOnlySaveStructure,
|
||||
// and then use it to compare chunk data to current chunk.
|
||||
|
||||
|
||||
// compare each world with the newly loaded one
|
||||
SubDimCompare mostSimilarSubDim = null;
|
||||
|
||||
File[] levelFolders = potentialFiles;
|
||||
LOGGER.info("Potential Sub Dimension folders: [" + levelFolders.length + "]");
|
||||
for (File testLevelFolder : levelFolders)
|
||||
{
|
||||
LOGGER.info("Testing level folder: [" + LodUtil.shortenString(testLevelFolder.getName(), 8) + "]");
|
||||
try
|
||||
{
|
||||
// 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
|
||||
@@ -223,51 +254,53 @@ public class LevelToFileMatcher implements AutoCloseable {
|
||||
|
||||
LOGGER.info("Sub dimension [" + LodUtil.shortenString(testLevelFolder.getName(), 8) + "...] is current dimension probability: " + LodUtil.shortenString(subDimCompare.getPercentEqual() + "", 5) + " (" + equalDataPoints + "/" + totalDataPointCount + ")");
|
||||
*/
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// this sub dimension isn't formatted correctly
|
||||
// for now we are just assuming it is an unrelated file
|
||||
}
|
||||
}
|
||||
|
||||
// TODO if two sub dimensions contain the same LODs merge them???
|
||||
|
||||
// the first seen player data is no longer needed, the sub dimension has been determined
|
||||
firstSeenPlayerData = null;
|
||||
|
||||
if (mostSimilarSubDim != null && mostSimilarSubDim.isValidSubDim())
|
||||
{
|
||||
// we found a world folder that is similar, use it
|
||||
|
||||
LOGGER.info("Sub Dimension set to: [" + LodUtil.shortenString(mostSimilarSubDim.folder.getName(), 8) + "...] with an equality of [" + mostSimilarSubDim.getPercentEqual() + "]");
|
||||
return mostSimilarSubDim.folder;
|
||||
}
|
||||
else
|
||||
{
|
||||
// no world folder was found, create a new one
|
||||
|
||||
double highestEqualityPercent = mostSimilarSubDim != null ? mostSimilarSubDim.getPercentEqual() : 0;
|
||||
|
||||
String newId = UUID.randomUUID().toString();
|
||||
String message = "No suitable sub dimension found. The highest equality was [" + LodUtil.shortenString(highestEqualityPercent + "", 5) + "]. Creating a new sub dimension with ID: " + LodUtil.shortenString(newId, 8) + "...";
|
||||
LOGGER.info(message);
|
||||
File folder = new File(levelsFolder, newId);
|
||||
folder.mkdirs();
|
||||
return folder;
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns true if the given chunk is valid to test */
|
||||
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);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
matcherThread.shutdownNow();
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// this sub dimension isn't formatted correctly
|
||||
// for now we are just assuming it is an unrelated file
|
||||
}
|
||||
}
|
||||
|
||||
// TODO if two sub dimensions contain the same LODs merge them???
|
||||
|
||||
// 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 world folder that is similar, use it
|
||||
|
||||
LOGGER.info("Sub Dimension set to: [" + LodUtil.shortenString(mostSimilarSubDim.folder.getName(), 8) + "...] with an equality of [" + mostSimilarSubDim.getPercentEqual() + "]");
|
||||
return mostSimilarSubDim.folder;
|
||||
}
|
||||
else
|
||||
{
|
||||
// no world folder was found, create a new one
|
||||
|
||||
double highestEqualityPercent = mostSimilarSubDim != null ? mostSimilarSubDim.getPercentEqual() : 0;
|
||||
|
||||
String newId = UUID.randomUUID().toString();
|
||||
String message = "No suitable sub dimension found. The highest equality was [" + LodUtil.shortenString(highestEqualityPercent + "", 5) + "]. Creating a new sub dimension with ID: " + LodUtil.shortenString(newId, 8) + "...";
|
||||
LOGGER.info(message);
|
||||
File folder = new File(this.levelsFolder, newId);
|
||||
folder.mkdirs();
|
||||
return folder;
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns true if the given chunk is valid to test */
|
||||
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);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close()
|
||||
{
|
||||
this.matcherThread.shutdownNow();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ public class PlayerData
|
||||
{
|
||||
public static final IWrapperFactory FACTORY = SingletonInjector.INSTANCE.get(IWrapperFactory.class);
|
||||
|
||||
private static final String playerDataFileName = "_playerData.toml";
|
||||
public static final String PLAYER_DATA_FILE_NAME = "_playerData.toml";
|
||||
|
||||
|
||||
public static final String PLAYER_BLOCK_POS_X_PATH = "playerBlockPosX";
|
||||
@@ -80,7 +80,7 @@ public class PlayerData
|
||||
|
||||
private PlayerData(IMinecraftClientWrapper mc)
|
||||
{
|
||||
updateData(mc);
|
||||
this.updateData(mc);
|
||||
}
|
||||
|
||||
public PlayerData(File dimensionFolder)
|
||||
@@ -108,26 +108,27 @@ public class PlayerData
|
||||
|
||||
|
||||
|
||||
public static File getFileForDimensionFolder(File file)
|
||||
{
|
||||
return new File(file.getPath() + File.separatorChar + playerDataFileName);
|
||||
}
|
||||
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 (playerBlockPos == null) throw new RuntimeException("No player block pos!");
|
||||
|
||||
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, playerBlockPos.getX());
|
||||
toml.add(PLAYER_BLOCK_POS_Y_PATH, playerBlockPos.getY());
|
||||
toml.add(PLAYER_BLOCK_POS_Z_PATH, playerBlockPos.getZ());
|
||||
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();
|
||||
}
|
||||
@@ -136,7 +137,7 @@ public class PlayerData
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return "PlayerBlockPos: [" + playerBlockPos.x + "," + playerBlockPos.y + "," + playerBlockPos.z + "]";
|
||||
return "PlayerBlockPos: [" + this.playerBlockPos.x + "," + this.playerBlockPos.y + "," + this.playerBlockPos.z + "]";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user