Set up per world LOD file reading and writing

Note: this doesn't change the LoadedRegions upon world change.
This commit is contained in:
James Seibel
2021-01-30 14:50:41 -06:00
parent ca6e165bbb
commit c22efa762c
3 changed files with 177 additions and 96 deletions
@@ -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 */}
// });
}