Set up per world LOD file reading and writing
Note: this doesn't change the LoadedRegions upon world change.
This commit is contained in:
@@ -1,9 +1,7 @@
|
||||
package backsun.lod.objects;
|
||||
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import backsun.lod.util.LodRegionFileHandler;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.world.DimensionType;
|
||||
|
||||
/**
|
||||
@@ -21,33 +19,28 @@ public class LoadedRegions
|
||||
private int halfWidth;
|
||||
|
||||
public LodRegion regions[][];
|
||||
private boolean isRegionDirty[][];
|
||||
private long regionLastWriteTime[][];
|
||||
public boolean isRegionDirty[][];
|
||||
|
||||
private int centerX;
|
||||
private int centerZ;
|
||||
|
||||
private LodRegionFileHandler rfHandler = new LodRegionFileHandler();;
|
||||
private ExecutorService fileHandlerPool = Executors.newFixedThreadPool(1);
|
||||
private LodRegionFileHandler rfHandler;
|
||||
|
||||
public LoadedRegions(DimensionType newDimension, int newMaxWidth)
|
||||
{
|
||||
dimension = newDimension;
|
||||
width = newMaxWidth;
|
||||
|
||||
// TODO what can be done if connected to a server?
|
||||
rfHandler = new LodRegionFileHandler(Minecraft.getMinecraft().getIntegratedServer().getWorld(0).getSaveHandler(), this);
|
||||
|
||||
regions = new LodRegion[width][width];
|
||||
isRegionDirty = new boolean[width][width];
|
||||
regionLastWriteTime = new long[width][width];
|
||||
|
||||
// populate isRegionDirty and regionLastWriteTime
|
||||
// populate isRegionDirty
|
||||
for(int i = 0; i < width; i++)
|
||||
{
|
||||
for(int j = 0; j < width; j++)
|
||||
{
|
||||
isRegionDirty[i][j] = false;
|
||||
regionLastWriteTime[i][j] = -1;
|
||||
}
|
||||
}
|
||||
|
||||
centerX = 0;
|
||||
centerZ = 0;
|
||||
@@ -241,7 +234,9 @@ public class LoadedRegions
|
||||
int zIndex = (centerZ - regionZ) + halfWidth;
|
||||
isRegionDirty[xIndex][zIndex] = true;
|
||||
|
||||
fileHandlerPool.execute(saveDirtyRegionsAsync);
|
||||
|
||||
|
||||
rfHandler.saveDirtyRegionsToFile();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -279,43 +274,6 @@ public class LoadedRegions
|
||||
return rfHandler.loadRegionFromFile(regionX, regionZ);
|
||||
}
|
||||
|
||||
public void saveAllRegionsToFile()
|
||||
{
|
||||
Thread task = new Thread(() -> {
|
||||
for (LodRegion regionArray[] : regions)
|
||||
for (LodRegion region : regionArray)
|
||||
rfHandler.saveRegionToDisk(region);
|
||||
});
|
||||
|
||||
for(int i = 0; i < width; i++)
|
||||
{
|
||||
for(int j = 0; j < width; j++)
|
||||
{
|
||||
isRegionDirty[i][j] = false;
|
||||
regionLastWriteTime[i][j] = System.currentTimeMillis();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fileHandlerPool.execute(task);
|
||||
}
|
||||
|
||||
|
||||
private Thread saveDirtyRegionsAsync = new Thread(() -> {
|
||||
for(int i = 0; i < width; i++)
|
||||
{
|
||||
for(int j = 0; j < width; j++)
|
||||
{
|
||||
if(isRegionDirty[i][j])
|
||||
{
|
||||
rfHandler.saveRegionToDisk(regions[i][j]);
|
||||
isRegionDirty[i][j] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether the region at the given X and Z coordinates
|
||||
@@ -328,6 +286,13 @@ public class LoadedRegions
|
||||
|
||||
return xIndex >= 0 && xIndex < width && zIndex >= 0 && zIndex < width;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public int getWidth()
|
||||
{
|
||||
return width;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import backsun.lod.objects.LodRegion;
|
||||
import backsun.lod.renderer.LodRenderer;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.multiplayer.WorldClient;
|
||||
import net.minecraft.world.DimensionType;
|
||||
import net.minecraft.world.chunk.Chunk;
|
||||
import net.minecraft.world.chunk.storage.ExtendedBlockStorage;
|
||||
import net.minecraftforge.client.event.RenderWorldLastEvent;
|
||||
@@ -131,17 +132,17 @@ public class ClientProxy extends CommonProxy
|
||||
// given a valid chunk object
|
||||
// (Minecraft often gives back empty
|
||||
// or null chunks in this method)
|
||||
if (chunk != null && isValidChunk(chunk) && Minecraft.getMinecraft().world != null)
|
||||
Minecraft mc = Minecraft.getMinecraft();
|
||||
if (mc != null && mc.world != null && chunk != null && isValidChunk(chunk))
|
||||
{
|
||||
Thread thread = new Thread(() ->
|
||||
{
|
||||
Minecraft mc = Minecraft.getMinecraft();
|
||||
|
||||
LodChunk lod = new LodChunk(chunk, mc.world);
|
||||
|
||||
if (regions == null)
|
||||
{
|
||||
regions = new LoadedRegions(null, regionWidth);
|
||||
DimensionType dim = DimensionType.getById(chunk.getWorld().provider.getDimension());
|
||||
regions = new LoadedRegions(dim, regionWidth);
|
||||
}
|
||||
|
||||
regions.addLod(lod);
|
||||
|
||||
@@ -5,9 +5,14 @@ import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import backsun.lod.objects.LoadedRegions;
|
||||
import backsun.lod.objects.LodChunk;
|
||||
import backsun.lod.objects.LodRegion;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.world.storage.ISaveHandler;
|
||||
|
||||
/**
|
||||
* This object handles creating LodRegions
|
||||
@@ -15,21 +20,49 @@ import backsun.lod.objects.LodRegion;
|
||||
* to file.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 09-28-2020
|
||||
* @version 01-30-2021
|
||||
*/
|
||||
public class LodRegionFileHandler
|
||||
{
|
||||
private LoadedRegions loadedRegion = null;
|
||||
public long regionLastWriteTime[][];
|
||||
|
||||
// String s = Minecraft.getMinecraftDir().getCanonicalPath() + "/saves/" + world.getSaveHandler().getSaveDirectoryName() + "/data/AA/World" + world.provider.dimensionId + ".dat";
|
||||
private final String SAVE_DIR = "C:/Users/James Seibel/Desktop/lod_save_folder/";
|
||||
private String save_dir;
|
||||
public ISaveHandler saveHandler;
|
||||
|
||||
private final String FILE_NAME_PREFIX = "lod";
|
||||
private final String FILE_NAME_DELIMITER = ".";
|
||||
private final String FILE_EXTENSION = ".txt";
|
||||
|
||||
private ExecutorService fileWritingThreadPool = Executors.newFixedThreadPool(1);
|
||||
/** Is true if the readyToReadAndWrite is false */
|
||||
private boolean waitingToSaveRegions = false;
|
||||
|
||||
public LodRegionFileHandler()
|
||||
|
||||
public LodRegionFileHandler(ISaveHandler newSaveHandler, LoadedRegions newLoadedRegion)
|
||||
{
|
||||
saveHandler = newSaveHandler;
|
||||
|
||||
if (saveHandler == null)
|
||||
{
|
||||
throw new NullPointerException("LodRegionFileHandler requires a world SaveHandler.");
|
||||
}
|
||||
|
||||
loadedRegion = newLoadedRegion;
|
||||
// these two variable are used in sync with the LoadedRegions
|
||||
regionLastWriteTime = new long[loadedRegion.getWidth()][loadedRegion.getWidth()];
|
||||
for(int i = 0; i < loadedRegion.getWidth(); i++)
|
||||
for(int j = 0; j < loadedRegion.getWidth(); j++)
|
||||
regionLastWriteTime[i][j] = -1;
|
||||
|
||||
|
||||
if (saveHandler != null && saveHandler.getWorldDirectory() != null)
|
||||
try {
|
||||
save_dir = saveHandler.getWorldDirectory().getCanonicalPath();
|
||||
} catch (IOException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -38,10 +71,14 @@ public class LodRegionFileHandler
|
||||
|
||||
|
||||
/**
|
||||
* Return the LodRegion that
|
||||
* Return the LodRegion at the given coordinates.
|
||||
* (null if the file doesn't exist)
|
||||
*/
|
||||
public LodRegion loadRegionFromFile(int regionX, int regionZ)
|
||||
{
|
||||
if (!readyToReadAndWrite())
|
||||
return null;
|
||||
|
||||
String fileName = getFileNameForRegion(regionX, regionZ);
|
||||
|
||||
File f = new File(fileName);
|
||||
@@ -94,8 +131,50 @@ public class LodRegionFileHandler
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public synchronized void saveDirtyRegionsToFile()
|
||||
{
|
||||
if (!readyToReadAndWrite())
|
||||
{
|
||||
// we aren't ready to read and write yet
|
||||
if(!waitingToSaveRegions)
|
||||
{
|
||||
waitingToSaveRegions = true;
|
||||
|
||||
// retry until we are able to read and write
|
||||
// then wake up the fileWritingThreadPool
|
||||
Thread retryReady = new Thread(() ->
|
||||
{
|
||||
try
|
||||
{
|
||||
// check once every so often so see
|
||||
// if anything has changed so we can
|
||||
// start reading and writing files
|
||||
while(!readyToReadAndWrite())
|
||||
{
|
||||
// TODO what can be done if connected to a server?
|
||||
saveHandler = Minecraft.getMinecraft().getIntegratedServer().getWorld(0).getSaveHandler();
|
||||
this.wait(1000);
|
||||
}
|
||||
|
||||
// we can start writing files now
|
||||
if (waitingToSaveRegions)
|
||||
{
|
||||
fileWritingThreadPool.execute(saveDirtyRegionsAsync);
|
||||
waitingToSaveRegions = false;
|
||||
}
|
||||
}
|
||||
catch (InterruptedException e)
|
||||
{ /* should never be called */}
|
||||
});
|
||||
|
||||
retryReady.run();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
fileWritingThreadPool.execute(saveDirtyRegionsAsync);
|
||||
}
|
||||
|
||||
public void saveRegionToDisk(LodRegion region)
|
||||
{
|
||||
@@ -106,11 +185,16 @@ public class LodRegionFileHandler
|
||||
|
||||
File f = new File(getFileNameForRegion(x, z));
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
if (!f.exists())
|
||||
{
|
||||
// make the LOD folder if it doesn't exist
|
||||
if(!f.getParentFile().exists())
|
||||
{
|
||||
f.getParentFile().mkdirs();
|
||||
}
|
||||
|
||||
f.createNewFile();
|
||||
}
|
||||
|
||||
@@ -131,51 +215,82 @@ public class LodRegionFileHandler
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
System.err.println("LOD ERROR: ");
|
||||
e.printStackTrace();
|
||||
System.err.println("LOD ERROR: " + e.getMessage());
|
||||
//e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Returns true if a file exists for the region
|
||||
* containing the given chunk.
|
||||
*/
|
||||
private boolean regionFileExistForChunk(int chunkX, int chunkZ)
|
||||
private Thread saveDirtyRegionsAsync = new Thread(() ->
|
||||
{
|
||||
// convert chunk coordinates to region
|
||||
// coordinates
|
||||
int regionX = chunkX / 32;
|
||||
int regionZ = chunkZ / 32;
|
||||
for(int i = 0; i < loadedRegion.getWidth(); i++)
|
||||
{
|
||||
for(int j = 0; j < loadedRegion.getWidth(); j++)
|
||||
{
|
||||
if(loadedRegion.isRegionDirty[i][j])
|
||||
{
|
||||
saveRegionToDisk(loadedRegion.regions[i][j]);
|
||||
loadedRegion.isRegionDirty[i][j] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new File(getFileNameForRegion(regionX, regionZ)).exists();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if a file exists
|
||||
* for the given region coordinates.
|
||||
*/
|
||||
private boolean regionFileExistForRegion(int regionX, int regionZ)
|
||||
{
|
||||
return new File(getFileNameForRegion(regionX, regionZ)).exists();
|
||||
}
|
||||
|
||||
|
||||
waitingToSaveRegions = false;
|
||||
});
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Return the name of the file that should contain the
|
||||
* region at the given x and z.
|
||||
* region at the given x and z. <br>
|
||||
* Returns null if this object isn't ready to read and write.
|
||||
* @param regionX
|
||||
* @param regionZ
|
||||
*/
|
||||
private String getFileNameForRegion(int regionX, int regionZ)
|
||||
{
|
||||
return SAVE_DIR +
|
||||
FILE_NAME_PREFIX + FILE_NAME_DELIMITER +
|
||||
regionX + FILE_NAME_DELIMITER + regionZ + FILE_EXTENSION;
|
||||
if (!readyToReadAndWrite())
|
||||
return null;
|
||||
|
||||
return save_dir + "\\lod_data\\DIM" + loadedRegion.dimension.getId() + "\\" +
|
||||
FILE_NAME_PREFIX + "." + regionX + "." + regionZ + FILE_EXTENSION;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns if this FileHandler is ready to read
|
||||
* and write files.
|
||||
*/
|
||||
public boolean readyToReadAndWrite()
|
||||
{
|
||||
return saveHandler != null && saveHandler.getWorldDirectory() != null &&
|
||||
save_dir != null && !save_dir.isEmpty();
|
||||
}
|
||||
|
||||
// /**
|
||||
// * Used to wake up the fileWritingThreadPool once
|
||||
// * we are able to read and write files.
|
||||
// */
|
||||
// private Thread retryReady = new Thread(() ->
|
||||
// {
|
||||
// try
|
||||
// {
|
||||
// // check once every so often so see
|
||||
// // if anything has changed so we can
|
||||
// // start reading and writing files
|
||||
// while(!readyToReadAndWrite())
|
||||
// {
|
||||
// this.wait(1000);
|
||||
// }
|
||||
//
|
||||
// // we can start writing files now
|
||||
// if (waitingToSaveRegions)
|
||||
// {
|
||||
// fileWritingThreadPool.execute(saveDirtyRegionsAsync);
|
||||
// waitingToSaveRegions = false;
|
||||
// }
|
||||
// }
|
||||
// catch (InterruptedException e)
|
||||
// { /* should never be called */}
|
||||
// });
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user