new line brackets

This commit is contained in:
Leonardo
2021-08-20 11:46:41 +02:00
parent 9f77bf4e61
commit 967aab3b3b
11 changed files with 2129 additions and 2075 deletions
@@ -58,486 +58,481 @@ import net.minecraft.world.gen.Heightmap;
*/
public class LodNodeBuilder
{
private ExecutorService lodGenThreadPool = Executors.newSingleThreadExecutor(new LodThreadFactory(this.getClass().getSimpleName()));
public static final int CHUNK_DATA_WIDTH = LodUtil.CHUNK_WIDTH;
public static final int CHUNK_SECTION_HEIGHT = CHUNK_DATA_WIDTH;
public static final Heightmap.Type DEFAULT_HEIGHTMAP = Heightmap.Type.WORLD_SURFACE_WG;
/** How wide LodDimensions should be in regions */
public int defaultDimensionWidthInRegions = 5;
public LodNodeBuilder()
{
}
public void generateLodNodeAsync(IChunk chunk, LodWorld lodWorld, IWorld world)
{
generateLodNodeAsync(chunk, lodWorld, world, DistanceGenerationMode.SERVER);
}
public void generateLodNodeAsync(IChunk chunk, LodWorld lodWorld, IWorld world, DistanceGenerationMode generationMode)
{
if (lodWorld == null || !lodWorld.getIsWorldLoaded())
return;
// don't try to create an LOD object
// if for some reason we aren't
// given a valid chunk object
if (chunk == null)
return;
Thread thread = new Thread(() ->
{
try
{
DimensionType dim = world.dimensionType();
LodDimension lodDim;
if (lodWorld.getLodDimension(dim) == null)
{
lodDim = new LodDimension(dim, lodWorld, defaultDimensionWidthInRegions);
lodWorld.addLodDimension(lodDim);
}
else
{
lodDim = lodWorld.getLodDimension(dim);
}
generateLodNodeFromChunk(lodDim ,chunk, new LodBuilderConfig(generationMode));
}
catch (IllegalArgumentException | NullPointerException e)
{
e.printStackTrace();
// if the world changes while LODs are being generated
// they will throw errors as they try to access things that no longer
// exist.
}
});
lodGenThreadPool.execute(thread);
return;
}
/**
* Creates a LodChunk for a chunk in the given world.
*
* @throws IllegalArgumentException thrown if either the chunk or world is null.
*/
public void generateLodNodeFromChunk(LodDimension lodDim, IChunk chunk) throws IllegalArgumentException
{
generateLodNodeFromChunk(lodDim, chunk, new LodBuilderConfig());
}
/**
* Creates a LodChunk for a chunk in the given world.
*
* @throws IllegalArgumentException thrown if either the chunk or world is null.
*/
public void generateLodNodeFromChunk(LodDimension lodDim, IChunk chunk, LodBuilderConfig config)
throws IllegalArgumentException
{
LodDetail detail = LodConfig.CLIENT.maxGenerationDetail.get();
if (chunk == null)
throw new IllegalArgumentException("generateLodFromChunk given a null chunk");
for (int i = 0; i < detail.dataPointLengthCount * detail.dataPointLengthCount; i++)
{
int startX = detail.startX[i];
int startZ = detail.startZ[i];
int endX = detail.endX[i];
int endZ = detail.endZ[i];
Color color = generateLodColorForArea(chunk, config, startX, startZ, endX, endZ);
short height;
short depth;
if (!config.useHeightmap)
{
height = determineHeightPointForArea(chunk.getSections(), startX, startZ, endX, endZ);
depth = determineBottomPointForArea(chunk.getSections(), startX, startZ, endX, endZ);
}
else
{
height = determineHeightPoint(chunk.getOrCreateHeightmapUnprimed(LodUtil.DEFAULT_HEIGHTMAP), startX,
startZ, endX, endZ);
depth = 0;
}
LevelPos levelPos = new LevelPos((byte)0 ,
chunk.getPos().x*16 + startX,
chunk.getPos().z*16 + startZ);
LodDataPoint data = new LodDataPoint(height, depth, color);
lodDim.addData(levelPos.convert((byte) detail.detailLevel),
data,
config.distanceGenerationMode,
true,
false);
}
}
// =====================//
// constructor helpers //
// =====================//
/**
* Find the lowest valid point from the bottom.
*
* @param chunkSections
* @param startX
* @param startZ
* @param endX
* @param endZ
*/
private short determineBottomPointForArea(ChunkSection[] chunkSections, int startX, int startZ, int endX, int endZ)
{
int numberOfBlocksRequired = ((endX - startX) * (endZ - startZ) / 2);
// search from the bottom up
for (int section = 0; section < CHUNK_DATA_WIDTH; section++)
{
for (int y = 0; y < CHUNK_SECTION_HEIGHT; y++)
{
int numberOfBlocksFound = 0;
for (int x = startX; x < endX; x++)
{
for (int z = startZ; z < endZ; z++)
{
if (isLayerValidLodPoint(chunkSections, section, y, x, z))
{
numberOfBlocksFound++;
if (numberOfBlocksFound >= numberOfBlocksRequired)
{
// we found
// enough blocks in this
// layer to count as an
// LOD point
return (short) (y + (section * CHUNK_SECTION_HEIGHT));
}
}
}
}
}
}
// we never found a valid LOD point
return -1;
}
/**
* Find the lowest valid point from the bottom.
*/
@SuppressWarnings("unused")
private short determineBottomPoint(Heightmap heightmap)
{
// the heightmap only shows how high the blocks go, it
// doesn't have any info about how low they go
return 0;
}
/**
* Find the highest valid point from the Top
*
* @param chunkSections
* @param startX
* @param startZ
* @param endX
* @param endZ
*/
private short determineHeightPointForArea(ChunkSection[] chunkSections, int startX, int startZ, int endX, int endZ)
{
int numberOfBlocksRequired = ((endX - startX) * (endZ - startZ) / 2);
// search from the top down
for (int section = chunkSections.length - 1; section >= 0; section--)
{
for (int y = CHUNK_DATA_WIDTH - 1; y >= 0; y--)
{
int numberOfBlocksFound = 0;
for (int x = startX; x < endX; x++)
{
for (int z = startZ; z < endZ; z++)
{
if (isLayerValidLodPoint(chunkSections, section, y, x, z))
{
numberOfBlocksFound++;
if (numberOfBlocksFound >= numberOfBlocksRequired)
{
// we found
// enough blocks in this
// layer to count as an
// LOD point
return (short) (y + 1 + (section * CHUNK_SECTION_HEIGHT));
}
}
}
}
}
}
// we never found a valid LOD point
return -1;
}
/**
* Find the highest point from the Top
*/
private short determineHeightPoint(Heightmap heightmap, int startX, int startZ, int endX, int endZ)
{
short highest = 0;
for (int x = startX; x < endX; x++)
{
for (int z = startZ; z < endZ; z++)
{
short newHeight = (short) heightmap.getFirstAvailable(x, z);
if (newHeight > highest)
highest = newHeight;
}
}
return highest;
}
/**
* Generate the color for the given chunk using biome water color, foliage
* color, and grass color.
*
* @param config_useSolidBlocksInColorGen <br>
* If true we look down from the top of
* the <br>
* chunk until we find a non-invisible
* block, and then use <br>
* its color. If false we generate the
* color immediately for <br>
* each x and z.
* @param config_useBiomeColors <br>
* If true use biome foliage, water, and
* grass colors, <br>
* otherwise only use the block's
* material color
*/
private Color generateLodColorForArea(IChunk chunk, LodBuilderConfig config, int startX, int startZ, int endX,
int endZ)
{
ChunkSection[] chunkSections = chunk.getSections();
int numbOfBlocks = 0;
int red = 0;
int green = 0;
int blue = 0;
for (int x = startX; x < endX; x++)
{
for (int z = startZ; z < endZ; z++)
{
boolean foundBlock = false;
// go top down
for (int i = chunkSections.length - 1; !foundBlock && i >= 0; i--)
{
if (!foundBlock && (chunkSections[i] != null || !config.useSolidBlocksInColorGen))
{
for (int y = CHUNK_SECTION_HEIGHT - 1; !foundBlock && y >= 0; y--)
{
int colorInt = 0;
BlockState blockState = null;
if (chunkSections[i] != null)
{
blockState = chunkSections[i].getBlockState(x, y, z);
colorInt = blockState.materialColor.col;
}
if (colorInt == 0 && config.useSolidBlocksInColorGen)
{
// skip air or invisible blocks
continue;
}
if (config.useBiomeColors)
{
// I have no idea why I need to bit shift to the right, but
// if I don't the biomes don't show up correctly.
Biome biome = chunk.getBiomes().getNoiseBiome(x >> 2, y + 1 * chunkSections.length >> 2,
z >> 2);
colorInt = getColorForBiome(x, z, biome);
}
else
{
// the bit shift is equivalent to dividing by 4
Biome biome = chunk.getBiomes().getNoiseBiome(x >> 2, y + i * chunkSections.length >> 2,
z >> 2);
colorInt = getColorForBlock(x, z, blockState, biome);
}
Color c = LodUtil.intToColor(colorInt);
red += c.getRed();
green += c.getGreen();
blue += c.getBlue();
numbOfBlocks++;
// we found a valid block, skip to the
// next x and z
foundBlock = true;
}
}
}
}
}
if (numbOfBlocks == 0)
numbOfBlocks = 1;
red /= numbOfBlocks;
green /= numbOfBlocks;
blue /= numbOfBlocks;
return new Color(red, green, blue);
}
/**
* Returns a color int for a given block.
*/
private int getColorForBlock(int x, int z, BlockState blockState, Biome biome)
{
int colorInt = 0;
// block special cases
if (blockState == Blocks.AIR.defaultBlockState())
{
Color tmp = LodUtil.intToColor(biome.getGrassColor(x, z));
tmp = tmp.darker();
colorInt = LodUtil.colorToInt(tmp);
}
else if (blockState == Blocks.MYCELIUM.defaultBlockState())
{
colorInt = MaterialColor.COLOR_LIGHT_GRAY.col;
}
// plant life
else if (blockState.getBlock() instanceof LeavesBlock)
{
Color leafColor = LodUtil.intToColor(biome.getFoliageColor()).darker();
colorInt = LodUtil.colorToInt(leafColor);
}
else if (blockState.getBlock() instanceof GrassBlock || blockState.getBlock() instanceof AbstractPlantBlock
|| blockState.getBlock() instanceof BushBlock || blockState.getBlock() instanceof IGrowable)
{
Color tmp = LodUtil.intToColor(biome.getGrassColor(x, z));
tmp = tmp.darker();
colorInt = LodUtil.colorToInt(tmp);
}
// water
else if (blockState.getBlock() == Blocks.WATER)
{
colorInt = biome.getWaterColor();
}
// everything else
else
{
colorInt = blockState.materialColor.col;
}
return colorInt;
}
/**
* Returns a color int for the given biome.
*/
private int getColorForBiome(int x, int z, Biome biome)
{
int colorInt = 0;
switch (biome.getBiomeCategory())
{
case NETHER:
colorInt = Blocks.BEDROCK.defaultBlockState().materialColor.col;
break;
case THEEND:
colorInt = Blocks.END_STONE.defaultBlockState().materialColor.col;
break;
case BEACH:
case DESERT:
colorInt = Blocks.SAND.defaultBlockState().materialColor.col;
break;
case EXTREME_HILLS:
colorInt = Blocks.STONE.defaultMaterialColor().col;
break;
case MUSHROOM:
colorInt = MaterialColor.COLOR_LIGHT_GRAY.col;
break;
case ICY:
colorInt = Blocks.SNOW.defaultMaterialColor().col;
break;
case MESA:
colorInt = Blocks.RED_SAND.defaultMaterialColor().col;
break;
case OCEAN:
case RIVER:
colorInt = biome.getWaterColor();
break;
case NONE:
case FOREST:
case TAIGA:
case JUNGLE:
case PLAINS:
case SAVANNA:
case SWAMP:
default:
Color tmp = LodUtil.intToColor(biome.getGrassColor(x, z));
tmp = tmp.darker();
colorInt = LodUtil.colorToInt(tmp);
break;
}
return colorInt;
}
/**
* Is the layer between the given X, Z, and dataIndex values a valid LOD point?
*/
private boolean isLayerValidLodPoint(ChunkSection[] chunkSections, int sectionIndex, int y, int x, int z)
{
if (chunkSections[sectionIndex] == null)
{
// this section doesn't have any blocks,
// it is not a valid section
return false;
}
else
{
if (chunkSections[sectionIndex].getBlockState(x, y, z) != null
&& chunkSections[sectionIndex].getBlockState(x, y, z).getBlock() != Blocks.AIR)
{
return true;
}
}
return false;
}
private ExecutorService lodGenThreadPool = Executors.newSingleThreadExecutor(new LodThreadFactory(this.getClass().getSimpleName()));
public static final int CHUNK_DATA_WIDTH = LodUtil.CHUNK_WIDTH;
public static final int CHUNK_SECTION_HEIGHT = CHUNK_DATA_WIDTH;
public static final Heightmap.Type DEFAULT_HEIGHTMAP = Heightmap.Type.WORLD_SURFACE_WG;
/**
* How wide LodDimensions should be in regions
*/
public int defaultDimensionWidthInRegions = 5;
public LodNodeBuilder()
{
}
public void generateLodNodeAsync(IChunk chunk, LodWorld lodWorld, IWorld world)
{
generateLodNodeAsync(chunk, lodWorld, world, DistanceGenerationMode.SERVER);
}
public void generateLodNodeAsync(IChunk chunk, LodWorld lodWorld, IWorld world, DistanceGenerationMode generationMode)
{
if (lodWorld == null || !lodWorld.getIsWorldLoaded())
return;
// don't try to create an LOD object
// if for some reason we aren't
// given a valid chunk object
if (chunk == null)
return;
Thread thread = new Thread(() ->
{
try
{
DimensionType dim = world.dimensionType();
LodDimension lodDim;
if (lodWorld.getLodDimension(dim) == null)
{
lodDim = new LodDimension(dim, lodWorld, defaultDimensionWidthInRegions);
lodWorld.addLodDimension(lodDim);
} else
{
lodDim = lodWorld.getLodDimension(dim);
}
generateLodNodeFromChunk(lodDim, chunk, new LodBuilderConfig(generationMode));
} catch (IllegalArgumentException | NullPointerException e)
{
e.printStackTrace();
// if the world changes while LODs are being generated
// they will throw errors as they try to access things that no longer
// exist.
}
});
lodGenThreadPool.execute(thread);
return;
}
/**
* Creates a LodChunk for a chunk in the given world.
*
* @throws IllegalArgumentException thrown if either the chunk or world is null.
*/
public void generateLodNodeFromChunk(LodDimension lodDim, IChunk chunk) throws IllegalArgumentException
{
generateLodNodeFromChunk(lodDim, chunk, new LodBuilderConfig());
}
/**
* Creates a LodChunk for a chunk in the given world.
*
* @throws IllegalArgumentException thrown if either the chunk or world is null.
*/
public void generateLodNodeFromChunk(LodDimension lodDim, IChunk chunk, LodBuilderConfig config)
throws IllegalArgumentException
{
LodDetail detail = LodConfig.CLIENT.maxGenerationDetail.get();
if (chunk == null)
throw new IllegalArgumentException("generateLodFromChunk given a null chunk");
for (int i = 0; i < detail.dataPointLengthCount * detail.dataPointLengthCount; i++)
{
int startX = detail.startX[i];
int startZ = detail.startZ[i];
int endX = detail.endX[i];
int endZ = detail.endZ[i];
Color color = generateLodColorForArea(chunk, config, startX, startZ, endX, endZ);
short height;
short depth;
if (!config.useHeightmap)
{
height = determineHeightPointForArea(chunk.getSections(), startX, startZ, endX, endZ);
depth = determineBottomPointForArea(chunk.getSections(), startX, startZ, endX, endZ);
} else
{
height = determineHeightPoint(chunk.getOrCreateHeightmapUnprimed(LodUtil.DEFAULT_HEIGHTMAP), startX,
startZ, endX, endZ);
depth = 0;
}
LevelPos levelPos = new LevelPos((byte) 0,
chunk.getPos().x * 16 + startX,
chunk.getPos().z * 16 + startZ);
LodDataPoint data = new LodDataPoint(height, depth, color);
lodDim.addData(levelPos.convert((byte) detail.detailLevel),
data,
config.distanceGenerationMode,
true,
false);
}
}
// =====================//
// constructor helpers //
// =====================//
/**
* Find the lowest valid point from the bottom.
*
* @param chunkSections
* @param startX
* @param startZ
* @param endX
* @param endZ
*/
private short determineBottomPointForArea(ChunkSection[] chunkSections, int startX, int startZ, int endX, int endZ)
{
int numberOfBlocksRequired = ((endX - startX) * (endZ - startZ) / 2);
// search from the bottom up
for (int section = 0; section < CHUNK_DATA_WIDTH; section++)
{
for (int y = 0; y < CHUNK_SECTION_HEIGHT; y++)
{
int numberOfBlocksFound = 0;
for (int x = startX; x < endX; x++)
{
for (int z = startZ; z < endZ; z++)
{
if (isLayerValidLodPoint(chunkSections, section, y, x, z))
{
numberOfBlocksFound++;
if (numberOfBlocksFound >= numberOfBlocksRequired)
{
// we found
// enough blocks in this
// layer to count as an
// LOD point
return (short) (y + (section * CHUNK_SECTION_HEIGHT));
}
}
}
}
}
}
// we never found a valid LOD point
return -1;
}
/**
* Find the lowest valid point from the bottom.
*/
@SuppressWarnings("unused")
private short determineBottomPoint(Heightmap heightmap)
{
// the heightmap only shows how high the blocks go, it
// doesn't have any info about how low they go
return 0;
}
/**
* Find the highest valid point from the Top
*
* @param chunkSections
* @param startX
* @param startZ
* @param endX
* @param endZ
*/
private short determineHeightPointForArea(ChunkSection[] chunkSections, int startX, int startZ, int endX, int endZ)
{
int numberOfBlocksRequired = ((endX - startX) * (endZ - startZ) / 2);
// search from the top down
for (int section = chunkSections.length - 1; section >= 0; section--)
{
for (int y = CHUNK_DATA_WIDTH - 1; y >= 0; y--)
{
int numberOfBlocksFound = 0;
for (int x = startX; x < endX; x++)
{
for (int z = startZ; z < endZ; z++)
{
if (isLayerValidLodPoint(chunkSections, section, y, x, z))
{
numberOfBlocksFound++;
if (numberOfBlocksFound >= numberOfBlocksRequired)
{
// we found
// enough blocks in this
// layer to count as an
// LOD point
return (short) (y + 1 + (section * CHUNK_SECTION_HEIGHT));
}
}
}
}
}
}
// we never found a valid LOD point
return -1;
}
/**
* Find the highest point from the Top
*/
private short determineHeightPoint(Heightmap heightmap, int startX, int startZ, int endX, int endZ)
{
short highest = 0;
for (int x = startX; x < endX; x++)
{
for (int z = startZ; z < endZ; z++)
{
short newHeight = (short) heightmap.getFirstAvailable(x, z);
if (newHeight > highest)
highest = newHeight;
}
}
return highest;
}
/**
* Generate the color for the given chunk using biome water color, foliage
* color, and grass color.
*
* @param config_useSolidBlocksInColorGen <br>
* If true we look down from the top of
* the <br>
* chunk until we find a non-invisible
* block, and then use <br>
* its color. If false we generate the
* color immediately for <br>
* each x and z.
* @param config_useBiomeColors <br>
* If true use biome foliage, water, and
* grass colors, <br>
* otherwise only use the block's
* material color
*/
private Color generateLodColorForArea(IChunk chunk, LodBuilderConfig config, int startX, int startZ, int endX,
int endZ)
{
ChunkSection[] chunkSections = chunk.getSections();
int numbOfBlocks = 0;
int red = 0;
int green = 0;
int blue = 0;
for (int x = startX; x < endX; x++)
{
for (int z = startZ; z < endZ; z++)
{
boolean foundBlock = false;
// go top down
for (int i = chunkSections.length - 1; !foundBlock && i >= 0; i--)
{
if (!foundBlock && (chunkSections[i] != null || !config.useSolidBlocksInColorGen))
{
for (int y = CHUNK_SECTION_HEIGHT - 1; !foundBlock && y >= 0; y--)
{
int colorInt = 0;
BlockState blockState = null;
if (chunkSections[i] != null)
{
blockState = chunkSections[i].getBlockState(x, y, z);
colorInt = blockState.materialColor.col;
}
if (colorInt == 0 && config.useSolidBlocksInColorGen)
{
// skip air or invisible blocks
continue;
}
if (config.useBiomeColors)
{
// I have no idea why I need to bit shift to the right, but
// if I don't the biomes don't show up correctly.
Biome biome = chunk.getBiomes().getNoiseBiome(x >> 2, y + 1 * chunkSections.length >> 2,
z >> 2);
colorInt = getColorForBiome(x, z, biome);
} else
{
// the bit shift is equivalent to dividing by 4
Biome biome = chunk.getBiomes().getNoiseBiome(x >> 2, y + i * chunkSections.length >> 2,
z >> 2);
colorInt = getColorForBlock(x, z, blockState, biome);
}
Color c = LodUtil.intToColor(colorInt);
red += c.getRed();
green += c.getGreen();
blue += c.getBlue();
numbOfBlocks++;
// we found a valid block, skip to the
// next x and z
foundBlock = true;
}
}
}
}
}
if (numbOfBlocks == 0)
numbOfBlocks = 1;
red /= numbOfBlocks;
green /= numbOfBlocks;
blue /= numbOfBlocks;
return new Color(red, green, blue);
}
/**
* Returns a color int for a given block.
*/
private int getColorForBlock(int x, int z, BlockState blockState, Biome biome)
{
int colorInt = 0;
// block special cases
if (blockState == Blocks.AIR.defaultBlockState())
{
Color tmp = LodUtil.intToColor(biome.getGrassColor(x, z));
tmp = tmp.darker();
colorInt = LodUtil.colorToInt(tmp);
} else if (blockState == Blocks.MYCELIUM.defaultBlockState())
{
colorInt = MaterialColor.COLOR_LIGHT_GRAY.col;
}
// plant life
else if (blockState.getBlock() instanceof LeavesBlock)
{
Color leafColor = LodUtil.intToColor(biome.getFoliageColor()).darker();
colorInt = LodUtil.colorToInt(leafColor);
} else if (blockState.getBlock() instanceof GrassBlock || blockState.getBlock() instanceof AbstractPlantBlock
|| blockState.getBlock() instanceof BushBlock || blockState.getBlock() instanceof IGrowable)
{
Color tmp = LodUtil.intToColor(biome.getGrassColor(x, z));
tmp = tmp.darker();
colorInt = LodUtil.colorToInt(tmp);
}
// water
else if (blockState.getBlock() == Blocks.WATER)
{
colorInt = biome.getWaterColor();
}
// everything else
else
{
colorInt = blockState.materialColor.col;
}
return colorInt;
}
/**
* Returns a color int for the given biome.
*/
private int getColorForBiome(int x, int z, Biome biome)
{
int colorInt = 0;
switch (biome.getBiomeCategory())
{
case NETHER:
colorInt = Blocks.BEDROCK.defaultBlockState().materialColor.col;
break;
case THEEND:
colorInt = Blocks.END_STONE.defaultBlockState().materialColor.col;
break;
case BEACH:
case DESERT:
colorInt = Blocks.SAND.defaultBlockState().materialColor.col;
break;
case EXTREME_HILLS:
colorInt = Blocks.STONE.defaultMaterialColor().col;
break;
case MUSHROOM:
colorInt = MaterialColor.COLOR_LIGHT_GRAY.col;
break;
case ICY:
colorInt = Blocks.SNOW.defaultMaterialColor().col;
break;
case MESA:
colorInt = Blocks.RED_SAND.defaultMaterialColor().col;
break;
case OCEAN:
case RIVER:
colorInt = biome.getWaterColor();
break;
case NONE:
case FOREST:
case TAIGA:
case JUNGLE:
case PLAINS:
case SAVANNA:
case SWAMP:
default:
Color tmp = LodUtil.intToColor(biome.getGrassColor(x, z));
tmp = tmp.darker();
colorInt = LodUtil.colorToInt(tmp);
break;
}
return colorInt;
}
/**
* Is the layer between the given X, Z, and dataIndex values a valid LOD point?
*/
private boolean isLayerValidLodPoint(ChunkSection[] chunkSections, int sectionIndex, int y, int x, int z)
{
if (chunkSections[sectionIndex] == null)
{
// this section doesn't have any blocks,
// it is not a valid section
return false;
} else
{
if (chunkSections[sectionIndex].getBlockState(x, y, z) != null
&& chunkSections[sectionIndex].getBlockState(x, y, z).getBlock() != Blocks.AIR)
{
return true;
}
}
return false;
}
}
@@ -24,27 +24,29 @@ package com.seibel.lod.enums;
* SURFACE <br>
* FEATURES <br>
* SERVER <br><br>
*
* <p>
* In order of fastest to slowest.
*
*
* @author James Seibel
* @author Leonardo Amato
* @version 8-7-2021
*/
public enum LodQuality
{
LOW(1),
LOW(1),
MEDIUM(2),
MEDIUM(2),
HIGH(3);
HIGH(3);
/** The higher the number the more complete the generation is. */
public final int quality;
/**
* The higher the number the more complete the generation is.
*/
public final int quality;
LodQuality(int quality)
{
this.quality = quality;
}
LodQuality(int quality)
{
this.quality = quality;
}
}
@@ -40,238 +40,236 @@ import net.minecraftforge.fml.config.ModConfig;
/**
* This handles any configuration the user has access to.
*
*
* @author James Seibel
* @version 8-10-2021
*/
@Mod.EventBusSubscriber
public class LodConfig
{
public static class Client
{
public ForgeConfigSpec.BooleanValue drawLODs;
public ForgeConfigSpec.EnumValue<FogDistance> fogDistance;
public ForgeConfigSpec.EnumValue<FogDrawOverride> fogDrawOverride;
public ForgeConfigSpec.BooleanValue debugMode;
public ForgeConfigSpec.EnumValue<LodTemplate> lodTemplate;
public ForgeConfigSpec.EnumValue<LodDetail> maxDrawDetail;
public ForgeConfigSpec.EnumValue<LodDetail> maxGenerationDetail;
public ForgeConfigSpec.EnumValue<DistanceGenerationMode> distanceGenerationMode;
public ForgeConfigSpec.BooleanValue allowUnstableFeatureGeneration;
public ForgeConfigSpec.IntValue numberOfWorldGenerationThreads;
public ForgeConfigSpec.EnumValue<ShadingMode> shadingMode;
/** this is multiplied by the default view distance
* to determine how far out to generate/render LODs */
public ForgeConfigSpec.IntValue lodChunkRadiusMultiplier;
public static class Client
{
public ForgeConfigSpec.BooleanValue drawLODs;
public ForgeConfigSpec.IntValue lodQuality;
public ForgeConfigSpec.EnumValue<FogDistance> fogDistance;
public ForgeConfigSpec.IntValue lodChunkRenderDistane;
public ForgeConfigSpec.DoubleValue brightnessMultiplier;
public ForgeConfigSpec.DoubleValue saturationMultiplier;
Client(ForgeConfigSpec.Builder builder)
{
builder.comment(ModInfo.MODNAME + " configuration settings").push("client");
drawLODs = builder
.comment("\n\n"
+ " If false LODs will not be drawn, \n"
+ " however they will still be generated \n"
+ " and saved to file for later use. \n")
.define("drawLODs", true);
fogDistance = builder
.comment("\n\n"
+ " At what distance should Fog be drawn on the LODs? \n"
+ " If the fog cuts off ubruptly or you are using Optifine's \"fast\" \n"
+ " fog option set this to " + FogDistance.NEAR.toString() + " or " + FogDistance.FAR.toString() + ". \n")
.defineEnum("fogDistance", FogDistance.NEAR_AND_FAR);
fogDrawOverride = builder
.comment("\n\n"
+ " When should fog be drawn? \n"
+ " " + FogDrawOverride.USE_OPTIFINE_FOG_SETTING.toString() + ": Use whatever Fog setting Optifine is using. If Optifine isn't installed this defaults to " + FogDrawOverride.ALWAYS_DRAW_FOG_FANCY.toString() + ". \n"
+ " " + FogDrawOverride.NEVER_DRAW_FOG.toString() + ": Never draw fog on the LODs \n"
+ " " + FogDrawOverride.ALWAYS_DRAW_FOG_FAST.toString() + ": Always draw fast fog on the LODs \n"
+ " " + FogDrawOverride.ALWAYS_DRAW_FOG_FANCY.toString() + ": Always draw fancy fog on the LODs (if your graphics card supports it) \n")
.defineEnum("fogDrawOverride", FogDrawOverride.USE_OPTIFINE_FOG_SETTING);
debugMode = builder
.comment("\n\n"
+ " If false the LODs will draw with their normal colors. \n"
+ " If true LODs colors will be based on their detail \n"
+ " level. \n")
.define("debugMode", false);
lodTemplate = builder
.comment("\n\n"
+ " How should the LODs be drawn? \n"
+ " NOTE: Currently only " + LodTemplate.CUBIC.toString() + " is implemented! \n"
+ " \n"
+ " " + LodTemplate.CUBIC.toString() + ": LOD Chunks are drawn as rectangular prisms (boxes). \n"
+ " " + LodTemplate.TRIANGULAR.toString() + ": LOD Chunks smoothly transition between other. \n"
+ " " + LodTemplate.DYNAMIC.toString() + ": LOD Chunks smoothly transition between other, \n"
+ " " + " unless a neighboring chunk is at a significantly different height. \n")
.defineEnum("lodTemplate", LodTemplate.CUBIC);
maxDrawDetail = builder
.comment("\n\n"
+ " What is the maximum detail level that LODs should be drawn at? \n"
+ " " + LodDetail.SINGLE.toString() + ": render 1 LOD for each Chunk. \n"
+ " " + LodDetail.DOUBLE.toString() + ": render 4 LODs for each Chunk. \n"
+ " " + LodDetail.QUAD.toString() + ": render 16 LODs for each Chunk. \n"
+ " " + LodDetail.HALF.toString() + ": render 64 LODs for each Chunk. \n"
+ " " + LodDetail.FULL.toString() + ": render 256 LODs for each Chunk. \n")
.defineEnum("lodDrawQuality", LodDetail.DOUBLE);
maxGenerationDetail = builder
.comment("\n\n"
+ " What is the maximum detail level that LODs should be generated at? \n"
+ " " + LodDetail.SINGLE.toString() + ": render 1 LOD for each Chunk. \n"
+ " " + LodDetail.DOUBLE.toString() + ": render 4 LODs for each Chunk. \n"
+ " " + LodDetail.QUAD.toString() + ": render 16 LODs for each Chunk. \n"
+ " " + LodDetail.HALF.toString() + ": render 64 LODs for each Chunk. \n"
+ " " + LodDetail.FULL.toString() + ": render 256 LODs for each Chunk. \n")
.defineEnum("lodGenerationQuality", LodDetail.DOUBLE);
public ForgeConfigSpec.EnumValue<FogDrawOverride> fogDrawOverride;
public ForgeConfigSpec.BooleanValue debugMode;
public ForgeConfigSpec.EnumValue<LodTemplate> lodTemplate;
public ForgeConfigSpec.EnumValue<LodDetail> maxDrawDetail;
public ForgeConfigSpec.EnumValue<LodDetail> maxGenerationDetail;
public ForgeConfigSpec.EnumValue<DistanceGenerationMode> distanceGenerationMode;
public ForgeConfigSpec.BooleanValue allowUnstableFeatureGeneration;
public ForgeConfigSpec.IntValue numberOfWorldGenerationThreads;
public ForgeConfigSpec.EnumValue<ShadingMode> shadingMode;
/**
* this is multiplied by the default view distance
* to determine how far out to generate/render LODs
*/
public ForgeConfigSpec.IntValue lodChunkRadiusMultiplier;
public ForgeConfigSpec.IntValue lodQuality;
public ForgeConfigSpec.IntValue lodChunkRenderDistane;
public ForgeConfigSpec.DoubleValue brightnessMultiplier;
public ForgeConfigSpec.DoubleValue saturationMultiplier;
lodChunkRadiusMultiplier = builder
.comment("\n\n"
+ " This is multiplied by the default view distance \n"
+ " to determine how far out to generate/render LODs. \n"
+ " A value of 2 means that there is 1 render distance worth \n"
+ " of LODs in each cardinal direction. \n")
.defineInRange("lodChunkRadiusMultiplier", 8, 2, 16);
Client(ForgeConfigSpec.Builder builder)
{
builder.comment(ModInfo.MODNAME + " configuration settings").push("client");
drawLODs = builder
.comment("\n\n"
+ " If false LODs will not be drawn, \n"
+ " however they will still be generated \n"
+ " and saved to file for later use. \n")
.define("drawLODs", true);
fogDistance = builder
.comment("\n\n"
+ " At what distance should Fog be drawn on the LODs? \n"
+ " If the fog cuts off ubruptly or you are using Optifine's \"fast\" \n"
+ " fog option set this to " + FogDistance.NEAR.toString() + " or " + FogDistance.FAR.toString() + ". \n")
.defineEnum("fogDistance", FogDistance.NEAR_AND_FAR);
fogDrawOverride = builder
.comment("\n\n"
+ " When should fog be drawn? \n"
+ " " + FogDrawOverride.USE_OPTIFINE_FOG_SETTING.toString() + ": Use whatever Fog setting Optifine is using. If Optifine isn't installed this defaults to " + FogDrawOverride.ALWAYS_DRAW_FOG_FANCY.toString() + ". \n"
+ " " + FogDrawOverride.NEVER_DRAW_FOG.toString() + ": Never draw fog on the LODs \n"
+ " " + FogDrawOverride.ALWAYS_DRAW_FOG_FAST.toString() + ": Always draw fast fog on the LODs \n"
+ " " + FogDrawOverride.ALWAYS_DRAW_FOG_FANCY.toString() + ": Always draw fancy fog on the LODs (if your graphics card supports it) \n")
.defineEnum("fogDrawOverride", FogDrawOverride.USE_OPTIFINE_FOG_SETTING);
debugMode = builder
.comment("\n\n"
+ " If false the LODs will draw with their normal colors. \n"
+ " If true LODs colors will be based on their detail \n"
+ " level. \n")
.define("debugMode", false);
lodTemplate = builder
.comment("\n\n"
+ " How should the LODs be drawn? \n"
+ " NOTE: Currently only " + LodTemplate.CUBIC.toString() + " is implemented! \n"
+ " \n"
+ " " + LodTemplate.CUBIC.toString() + ": LOD Chunks are drawn as rectangular prisms (boxes). \n"
+ " " + LodTemplate.TRIANGULAR.toString() + ": LOD Chunks smoothly transition between other. \n"
+ " " + LodTemplate.DYNAMIC.toString() + ": LOD Chunks smoothly transition between other, \n"
+ " " + " unless a neighboring chunk is at a significantly different height. \n")
.defineEnum("lodTemplate", LodTemplate.CUBIC);
maxDrawDetail = builder
.comment("\n\n"
+ " What is the maximum detail level that LODs should be drawn at? \n"
+ " " + LodDetail.SINGLE.toString() + ": render 1 LOD for each Chunk. \n"
+ " " + LodDetail.DOUBLE.toString() + ": render 4 LODs for each Chunk. \n"
+ " " + LodDetail.QUAD.toString() + ": render 16 LODs for each Chunk. \n"
+ " " + LodDetail.HALF.toString() + ": render 64 LODs for each Chunk. \n"
+ " " + LodDetail.FULL.toString() + ": render 256 LODs for each Chunk. \n")
.defineEnum("lodDrawQuality", LodDetail.DOUBLE);
maxGenerationDetail = builder
.comment("\n\n"
+ " What is the maximum detail level that LODs should be generated at? \n"
+ " " + LodDetail.SINGLE.toString() + ": render 1 LOD for each Chunk. \n"
+ " " + LodDetail.DOUBLE.toString() + ": render 4 LODs for each Chunk. \n"
+ " " + LodDetail.QUAD.toString() + ": render 16 LODs for each Chunk. \n"
+ " " + LodDetail.HALF.toString() + ": render 64 LODs for each Chunk. \n"
+ " " + LodDetail.FULL.toString() + ": render 256 LODs for each Chunk. \n")
.defineEnum("lodGenerationQuality", LodDetail.DOUBLE);
lodChunkRadiusMultiplier = builder
.comment("\n\n"
+ " This is multiplied by the default view distance \n"
+ " to determine how far out to generate/render LODs. \n"
+ " A value of 2 means that there is 1 render distance worth \n"
+ " of LODs in each cardinal direction. \n")
.defineInRange("lodChunkRadiusMultiplier", 8, 2, 16);
distanceGenerationMode = builder
.comment("\n\n"
+ " Note: The times listed here are the amount of time it took \n"
+ " the developer's PC to generate 1 chunk, \n"
+ " and are included so you can compare the \n"
+ " different generation options. Your mileage may vary. \n"
+ "\n"
+ " " + DistanceGenerationMode.BIOME_ONLY.toString() + " \n"
+ " Only generate the biomes and use biome \n"
+ " grass/foliage color, water color, or snow color \n"
+ " to generate the color. \n"
+ " Doesn't generate height, everything is shown at sea level. \n"
+ " Multithreaded - Fastest (2-5 ms) \n"
+ "\n"
+ " " + DistanceGenerationMode.BIOME_ONLY_SIMULATE_HEIGHT.toString() + " \n"
+ " Same as BIOME_ONLY, except instead \n"
+ " of always using sea level as the LOD height \n"
+ " different biome types (mountain, ocean, forest, etc.) \n"
+ " use predetermined heights to simulate having height data. \n"
+ " Multithreaded - Fastest (2-5 ms) \n"
+ "\n"
+ " " + DistanceGenerationMode.SURFACE.toString() + " \n"
+ " Generate the world surface, \n"
+ " this does NOT include caves, trees, \n"
+ " or structures. \n"
+ " Multithreaded - Faster (10-20 ms) \n"
+ "\n"
+ " " + DistanceGenerationMode.FEATURES.toString() + " \n"
+ " Generate everything except structures. \n"
+ " WARNING: This may cause world generation bugs or instability! \n"
+ " Multithreaded - Fast (15-20 ms) \n"
+ "\n"
+ " " + DistanceGenerationMode.SERVER.toString() + " \n"
+ " Ask the server to generate/load each chunk. \n"
+ " This is the most compatible, but causes server/simulation lag. \n"
+ " This will also show player made structures if you \n"
+ " are adding the mod to a pre-existing world. \n"
+ " Singlethreaded - Slow (15-50 ms, with spikes up to 200 ms) \n")
.defineEnum("distanceGenerationMode", DistanceGenerationMode.SURFACE);
allowUnstableFeatureGeneration = builder
.comment("\n\n"
+ " When using the " + DistanceGenerationMode.FEATURES.toString() + " generation mode \n"
+ " some features may not be thread safe, which could \n"
+ " cause instability and crashes. \n"
+ " By default (false) those features are skipped, \n"
+ " improving stability, but decreasing how many features are \n"
+ " actually generated. \n"
+ " (for example: some tree generation is unstable, \n"
+ " so some trees may not be generated.) \n"
+ " By setting this to true, all features will be generated, \n"
+ " but your game will be more unstable and crashes may occur. \n"
+ " \n"
+ " I would love to remove this option and always generate everything, \n"
+ " but I'm not sure how to do that. \n"
+ " If you are a Java wizard, check out the git issue here: \n"
+ " https://gitlab.com/jeseibel/minecraft-lod-mod/-/issues/35 \n")
.define("allowUnstableFeatureGeneration", false);
numberOfWorldGenerationThreads = builder
.comment("\n\n"
+ " This is how many threads are used when generating terrain. \n"
+ " If you experience stuttering when generating terrain, decrease \n"
+ " this number. If you want to increase LOD generation speed, \n"
+ " increase the number. \n"
+ " The max is the number of processors on your CPU. \n"
+ "\n"
+ " Requires a restart to take effect. \n"
)
.defineInRange("numberOfWorldGenerationThreads", Runtime.getRuntime().availableProcessors(), 1, Runtime.getRuntime().availableProcessors());
shadingMode = builder
.comment("\n\n"
+ " What kind of shading should LODs have? \n"
+ " " + ShadingMode.NONE.toString() + " \n"
+ " " + "LODs will have the same lighting on every side. \n"
+ " " + "Can make large similarly colored areas hard to differentiate. \n"
+ " " + "Fastest"
+ "/n"
+ " " + ShadingMode.DARKEN_SIDES.toString() + " \n"
+ " " + "LODs will have darker sides and bottoms to simulate top down lighting."
+ " " + "Fastest /n")
.defineEnum("lightingMode", ShadingMode.DARKEN_SIDES);
brightnessMultiplier = builder
.comment("\n\n"
+ " Change how bright LOD colors are. \n"
+ " 0 = black \n"
+ " 1 = normal color value \n"
+ " 2 = washed out colors \n")
.defineInRange("brightnessMultiplier", 1.0, 0, 2);
saturationMultiplier = builder
.comment("\n\n"
+ " Change how saturated LOD colors are. \n"
+ " 0 = black and white \n"
+ " 1 = normal saturation \n"
+ " 2 = very saturated \n")
.defineInRange("saturationMultiplier", 1.0, 0, 2);
builder.pop();
}
}
distanceGenerationMode = builder
.comment("\n\n"
+ " Note: The times listed here are the amount of time it took \n"
+ " the developer's PC to generate 1 chunk, \n"
+ " and are included so you can compare the \n"
+ " different generation options. Your mileage may vary. \n"
+ "\n"
+ " " + DistanceGenerationMode.BIOME_ONLY.toString() + " \n"
+ " Only generate the biomes and use biome \n"
+ " grass/foliage color, water color, or snow color \n"
+ " to generate the color. \n"
+ " Doesn't generate height, everything is shown at sea level. \n"
+ " Multithreaded - Fastest (2-5 ms) \n"
+ "\n"
+ " " + DistanceGenerationMode.BIOME_ONLY_SIMULATE_HEIGHT.toString() + " \n"
+ " Same as BIOME_ONLY, except instead \n"
+ " of always using sea level as the LOD height \n"
+ " different biome types (mountain, ocean, forest, etc.) \n"
+ " use predetermined heights to simulate having height data. \n"
+ " Multithreaded - Fastest (2-5 ms) \n"
+ "\n"
+ " " + DistanceGenerationMode.SURFACE.toString() + " \n"
+ " Generate the world surface, \n"
+ " this does NOT include caves, trees, \n"
+ " or structures. \n"
+ " Multithreaded - Faster (10-20 ms) \n"
+ "\n"
+ " " + DistanceGenerationMode.FEATURES.toString() + " \n"
+ " Generate everything except structures. \n"
+ " WARNING: This may cause world generation bugs or instability! \n"
+ " Multithreaded - Fast (15-20 ms) \n"
+ "\n"
+ " " + DistanceGenerationMode.SERVER.toString() + " \n"
+ " Ask the server to generate/load each chunk. \n"
+ " This is the most compatible, but causes server/simulation lag. \n"
+ " This will also show player made structures if you \n"
+ " are adding the mod to a pre-existing world. \n"
+ " Singlethreaded - Slow (15-50 ms, with spikes up to 200 ms) \n")
.defineEnum("distanceGenerationMode", DistanceGenerationMode.SURFACE);
allowUnstableFeatureGeneration = builder
.comment("\n\n"
+ " When using the " + DistanceGenerationMode.FEATURES.toString() + " generation mode \n"
+ " some features may not be thread safe, which could \n"
+ " cause instability and crashes. \n"
+ " By default (false) those features are skipped, \n"
+ " improving stability, but decreasing how many features are \n"
+ " actually generated. \n"
+ " (for example: some tree generation is unstable, \n"
+ " so some trees may not be generated.) \n"
+ " By setting this to true, all features will be generated, \n"
+ " but your game will be more unstable and crashes may occur. \n"
+ " \n"
+ " I would love to remove this option and always generate everything, \n"
+ " but I'm not sure how to do that. \n"
+ " If you are a Java wizard, check out the git issue here: \n"
+ " https://gitlab.com/jeseibel/minecraft-lod-mod/-/issues/35 \n")
.define("allowUnstableFeatureGeneration", false);
numberOfWorldGenerationThreads = builder
.comment("\n\n"
+ " This is how many threads are used when generating terrain. \n"
+ " If you experience stuttering when generating terrain, decrease \n"
+ " this number. If you want to increase LOD generation speed, \n"
+ " increase the number. \n"
+ " The max is the number of processors on your CPU. \n"
+ "\n"
+ " Requires a restart to take effect. \n"
)
.defineInRange("numberOfWorldGenerationThreads", Runtime.getRuntime().availableProcessors(), 1, Runtime.getRuntime().availableProcessors());
shadingMode = builder
.comment("\n\n"
+ " What kind of shading should LODs have? \n"
+ " " + ShadingMode.NONE.toString() + " \n"
+ " " + "LODs will have the same lighting on every side. \n"
+ " " + "Can make large similarly colored areas hard to differentiate. \n"
+ " " + "Fastest"
+ "/n"
+ " " + ShadingMode.DARKEN_SIDES.toString() + " \n"
+ " " + "LODs will have darker sides and bottoms to simulate top down lighting."
+ " " + "Fastest /n")
.defineEnum("lightingMode", ShadingMode.DARKEN_SIDES);
brightnessMultiplier = builder
.comment("\n\n"
+ " Change how bright LOD colors are. \n"
+ " 0 = black \n"
+ " 1 = normal color value \n"
+ " 2 = washed out colors \n")
.defineInRange("brightnessMultiplier", 1.0, 0, 2);
saturationMultiplier = builder
.comment("\n\n"
+ " Change how saturated LOD colors are. \n"
+ " 0 = black and white \n"
+ " 1 = normal saturation \n"
+ " 2 = very saturated \n")
.defineInRange("saturationMultiplier", 1.0, 0, 2);
builder.pop();
}
}
/**
* {@link Path} to the configuration file of this mod
@@ -281,11 +279,13 @@ public class LodConfig
public static final ForgeConfigSpec clientSpec;
public static final Client CLIENT;
static {
static
{
final Pair<Client, ForgeConfigSpec> specPair = new ForgeConfigSpec.Builder().configure(Client::new);
clientSpec = specPair.getRight();
CLIENT = specPair.getLeft();
// setup the config file
CommentedFileConfig config = CommentedFileConfig.builder(CONFIG_PATH)
.writingMode(WritingMode.REPLACE)
@@ -294,21 +294,19 @@ public class LodConfig
config.save();
clientSpec.setConfig(config);
}
@SubscribeEvent
public static void onLoad(final ModConfig.Loading configEvent)
{
LogManager.getLogger().debug(ModInfo.MODNAME, "Loaded forge config file {}", configEvent.getConfig().getFileName());
}
@SubscribeEvent
public static void onFileChange(final ModConfig.Reloading configEvent)
{
LogManager.getLogger().debug(ModInfo.MODNAME, "Forge config just got changed on the file system!");
}
}
@@ -41,7 +41,8 @@ import java.util.concurrent.Executors;
* @author James Seibel
* @version 8-14-2021
*/
public class LodDimensionFileHandler {
public class LodDimensionFileHandler
{
/**
* This is what separates each piece of data
*/
@@ -89,7 +90,8 @@ public class LodDimensionFileHandler {
private ExecutorService fileWritingThreadPool = Executors.newSingleThreadExecutor(new LodThreadFactory(this.getClass().getSimpleName()));
public LodDimensionFileHandler(File newSaveFolder, LodDimension newLoadedDimension) {
public LodDimensionFileHandler(File newSaveFolder, LodDimension newLoadedDimension)
{
if (newSaveFolder == null)
throw new IllegalArgumentException("LodDimensionFileHandler requires a valid File location to read and write to.");
@@ -113,36 +115,43 @@ public class LodDimensionFileHandler {
* Return the LodRegion region at the given coordinates.
* (null if the file doesn't exist)
*/
public LodRegion loadRegionFromFile(RegionPos regionPos) {
public LodRegion loadRegionFromFile(RegionPos regionPos)
{
int regionX = regionPos.x;
int regionZ = regionPos.z;
String fileName = getFileNameAndPathForRegion(regionX, regionZ);
File f = new File(fileName);
if (!f.exists()) {
if (!f.exists())
{
// there wasn't a file, don't
// return anything
return null;
}
String data = "";
try {
try
{
BufferedReader bufferedReader = new BufferedReader(new FileReader(f));
data = bufferedReader.readLine();
int fileVersion = -1;
if (data != null && !data.isEmpty()) {
if (data != null && !data.isEmpty())
{
// try to get the file version
try {
try
{
fileVersion = Integer.parseInt(data.substring(data.indexOf(' ')).trim());
} catch (NumberFormatException | StringIndexOutOfBoundsException e) {
} catch (NumberFormatException | StringIndexOutOfBoundsException e)
{
// this file doesn't have a version
// keep the version as -1
fileVersion = -1;
}
// check if this file can be read by this file handler
if (fileVersion < LOD_SAVE_FILE_VERSION) {
if (fileVersion < LOD_SAVE_FILE_VERSION)
{
// the file we are reading is an older version,
// close the reader and delete the file.
bufferedReader.close();
@@ -152,7 +161,8 @@ public class LodDimensionFileHandler {
" File was been deleted.");
return null;
} else if (fileVersion > LOD_SAVE_FILE_VERSION) {
} else if (fileVersion > LOD_SAVE_FILE_VERSION)
{
// the file we are reading is a newer version,
// close the reader and ignore the file, we don't
// want to accidently delete anything the user may want.
@@ -163,7 +173,8 @@ public class LodDimensionFileHandler {
return null;
}
} else {
} else
{
// there is no data in this file
bufferedReader.close();
return null;
@@ -174,7 +185,8 @@ public class LodDimensionFileHandler {
data = bufferedReader.readLine();
bufferedReader.close();
} catch (IOException e) {
} catch (IOException e)
{
// the buffered reader encountered a
// problem reading the file
return null;
@@ -190,15 +202,19 @@ public class LodDimensionFileHandler {
/**
* Save all dirty regions in this LodDimension to file.
*/
public void saveDirtyRegionsToFileAsync() {
public void saveDirtyRegionsToFileAsync()
{
fileWritingThreadPool.execute(saveDirtyRegionsThread);
}
private Thread saveDirtyRegionsThread = new Thread(() ->
{
for (int i = 0; i < loadedDimension.getWidth(); i++) {
for (int j = 0; j < loadedDimension.getWidth(); j++) {
if (loadedDimension.isRegionDirty[i][j] && loadedDimension.regions[i][j] != null) {
for (int i = 0; i < loadedDimension.getWidth(); i++)
{
for (int j = 0; j < loadedDimension.getWidth(); j++)
{
if (loadedDimension.isRegionDirty[i][j] && loadedDimension.regions[i][j] != null)
{
saveRegionToFile(loadedDimension.regions[i][j]);
loadedDimension.isRegionDirty[i][j] = false;
}
@@ -214,7 +230,8 @@ public class LodDimensionFileHandler {
* 2. This will save to the LodDimension that this
* handler is associated with.
*/
private void saveRegionToFile(LodRegion region) {
private void saveRegionToFile(LodRegion region)
{
// convert to region coordinates
int x = region.regionPosX;
int z = region.regionPosZ;
@@ -224,15 +241,18 @@ public class LodDimensionFileHandler {
byte minDetailLevel = region.getMinDetailLevel();
File oldFile = new File(getFileNameAndPathForRegion(x, z));
try {
try
{
// make sure the file and folder exists
if (!oldFile.exists()) {
if (!oldFile.exists())
{
// the file doesn't exist,
// create it and the folder if need be
if (!oldFile.getParentFile().exists())
oldFile.getParentFile().mkdirs();
oldFile.createNewFile();
} else {
} else
{
// the file exists, make sure it
// is the correct version.
// (to make sure we don't overwrite a newer
@@ -242,11 +262,14 @@ public class LodDimensionFileHandler {
String s = br.readLine();
int fileVersion = LOD_SAVE_FILE_VERSION;
if (s != null && !s.isEmpty()) {
if (s != null && !s.isEmpty())
{
// try to get the file version
try {
try
{
fileVersion = Integer.parseInt(s.substring(s.indexOf(' ')).trim());
} catch (NumberFormatException | StringIndexOutOfBoundsException e) {
} catch (NumberFormatException | StringIndexOutOfBoundsException e)
{
// this file doesn't have a correctly formated version
// just overwrite the file
}
@@ -254,7 +277,8 @@ public class LodDimensionFileHandler {
br.close();
// check if this file can be written to by the file handler
if (fileVersion <= LOD_SAVE_FILE_VERSION) {
if (fileVersion <= LOD_SAVE_FILE_VERSION)
{
// we are good to continue and overwrite the old file
} else //if(fileVersion > LOD_SAVE_FILE_VERSION)
{
@@ -279,7 +303,8 @@ public class LodDimensionFileHandler {
// overwrite the old file with the new one
Files.move(newFile.toPath(), oldFile.toPath(), StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING);
} catch (Exception e) {
} catch (Exception e)
{
ClientProxy.LOGGER.error("LOD file write error: ");
e.printStackTrace();
}
@@ -298,15 +323,18 @@ public class LodDimensionFileHandler {
* <p>
* example: "lod.0.0.txt"
*/
private String getFileNameAndPathForRegion(int regionX, int regionZ) {
try {
private String getFileNameAndPathForRegion(int regionX, int regionZ)
{
try
{
// saveFolder is something like
// ".\Super Flat\DIM-1\data"
// or
// ".\Super Flat\data"
return dimensionDataSaveFolder.getCanonicalPath() + File.separatorChar +
FILE_NAME_PREFIX + "." + regionX + "." + regionZ + FILE_EXTENSION;
} catch (IOException e) {
} catch (IOException e)
{
return null;
}
}
@@ -4,7 +4,8 @@ import java.io.Serializable;
import com.seibel.lod.util.LodUtil;
public class LevelContainer implements Serializable {
public class LevelContainer implements Serializable
{
public static final char DATA_DELIMITER = ',';
@@ -20,7 +21,8 @@ public class LevelContainer implements Serializable {
public final boolean[][] dataExistence;
public LevelContainer(byte detailLevel, byte[][][] colors, short[][] height, short[][] depth, byte[][] generationType, boolean[][] dataExistence){
public LevelContainer(byte detailLevel, byte[][][] colors, short[][] height, short[][] depth, byte[][] generationType, boolean[][] dataExistence)
{
this.detailLevel = detailLevel;
this.colors = colors;
this.height = height;
@@ -29,14 +31,15 @@ public class LevelContainer implements Serializable {
this.dataExistence = dataExistence;
}
public LevelContainer(String data){
public LevelContainer(String data)
{
int index = 0;
int lastIndex = 0;
index = data.indexOf(DATA_DELIMITER, 0);
this.detailLevel = (byte) Integer.parseInt(data.substring(0,index));
this.detailLevel = (byte) Integer.parseInt(data.substring(0, index));
int size = (int) Math.pow(2, LodUtil.REGION_DETAIL_LEVEL - detailLevel);
this.colors = new byte[size][size][3];
@@ -45,46 +48,51 @@ public class LevelContainer implements Serializable {
this.generationType = new byte[size][size];
this.dataExistence = new boolean[size][size];
int intCol;
for (int x = 0; x < size; x++) {
for (int z = 0; z < size; z++) {
for (int x = 0; x < size; x++)
{
for (int z = 0; z < size; z++)
{
lastIndex = index;
index = data.indexOf(DATA_DELIMITER, lastIndex+1);
intCol = Integer.parseInt(data.substring(lastIndex+1,index), 16);
index = data.indexOf(DATA_DELIMITER, lastIndex + 1);
intCol = Integer.parseInt(data.substring(lastIndex + 1, index), 16);
colors[x][z][0] = (byte) ((intCol >> 16) - 128);
colors[x][z][1] = (byte) ((intCol >> 8) - 128);
colors[x][z][2] = (byte) (intCol - 128);
lastIndex = index;
index = data.indexOf(DATA_DELIMITER, lastIndex+1);
height[x][z] = Short.parseShort(data.substring(lastIndex+1,index), 16);
index = data.indexOf(DATA_DELIMITER, lastIndex + 1);
height[x][z] = Short.parseShort(data.substring(lastIndex + 1, index), 16);
lastIndex = index;
index = data.indexOf(DATA_DELIMITER, lastIndex+1);
depth[x][z] = Short.parseShort(data.substring(lastIndex+1,index), 16);
index = data.indexOf(DATA_DELIMITER, lastIndex + 1);
depth[x][z] = Short.parseShort(data.substring(lastIndex + 1, index), 16);
lastIndex = index;
index = data.indexOf(DATA_DELIMITER, lastIndex+1);
generationType[x][z] = Byte.parseByte(data.substring(lastIndex+1,index), 16);
index = data.indexOf(DATA_DELIMITER, lastIndex + 1);
generationType[x][z] = Byte.parseByte(data.substring(lastIndex + 1, index), 16);
lastIndex = index;
index = data.indexOf(DATA_DELIMITER, lastIndex+1);
dataExistence[x][z] = Boolean.parseBoolean(data.substring(lastIndex+1,index));
index = data.indexOf(DATA_DELIMITER, lastIndex + 1);
dataExistence[x][z] = Boolean.parseBoolean(data.substring(lastIndex + 1, index));
}
}
}
@Override
public String toString() {
public String toString()
{
StringBuilder stringBuilder = new StringBuilder();
int combinedCol;
int size = (int) Math.pow(2, LodUtil.REGION_DETAIL_LEVEL - detailLevel);
stringBuilder.append(detailLevel);
stringBuilder.append(DATA_DELIMITER);
for (int x = 0; x < size; x++) {
for (int z = 0; z < size; z++) {
for (int x = 0; x < size; x++)
{
for (int z = 0; z < size; z++)
{
//Converting the colors to intColor and then to HEX
combinedCol = ((colors[x][z][0] + 128) << 16) | ((colors[x][z][1] + 128) << 8) | ((colors[x][z][2] + 128) & 0xFF);
combinedCol = ((colors[x][z][0] + 128) << 16) | ((colors[x][z][1] + 128) << 8) | ((colors[x][z][2] + 128) & 0xFF);
stringBuilder.append(Integer.toHexString(combinedCol));
stringBuilder.append(DATA_DELIMITER);
stringBuilder.append(Integer.toHexString(height[x][z]));
@@ -27,108 +27,120 @@ import com.seibel.lod.util.LodUtil;
/**
* This stores the height and color
* for a specific area in a LodChunk.
*
*
* @author James Seibel
* @version 8-8-2021
*/
public class LodDataPoint implements Serializable
{
/** This is what separates each piece of data in the toData method */
private static final char DATA_DELIMITER = LodDimensionFileHandler.DATA_DELIMITER;
/** this is how many pieces of data are exported when toData is called */
public static final int NUMBER_OF_DELIMITERS = 5;
/** a empty data point that can be used for comparisons */
public static final LodDataPoint EMPTY_DATA_POINT = new LodDataPoint();
/** highest point */
public short height;
/** lowest point */
public short depth;
/** The average color for the 6 cardinal directions */
public Color color;
/**
* Creates an empty LodDataPoint
*/
public LodDataPoint()
{
height = -1;
depth = -1;
color = LodUtil.COLOR_INVISIBLE;
}
public LodDataPoint(short newHeight, short newDepth, Color newColor)
{
height = newHeight;
depth = newDepth;
color = newColor;
}
public LodDataPoint(int newHeight, int newDepth, Color newColor)
{
height = (short) newHeight;
depth = (short) newDepth;
color = newColor;
}
/**
* This is what separates each piece of data in the toData method
*/
private static final char DATA_DELIMITER = LodDimensionFileHandler.DATA_DELIMITER;
@Override
public int hashCode()
{
return Objects.hash(this.height, this.depth, this.color);
}
/**
* this is how many pieces of data are exported when toData is called
*/
public static final int NUMBER_OF_DELIMITERS = 5;
public boolean equals(LodDataPoint other)
{
return (this.height == other.height
&& this.depth == other.depth
&& this.color == other.color);
}
public boolean isEmpty()
{
return this.equals(EMPTY_DATA_POINT);
}
/**
* a empty data point that can be used for comparisons
*/
public static final LodDataPoint EMPTY_DATA_POINT = new LodDataPoint();
/**
* Outputs all data in a csv format
* with the given delimiter.
* <br>
* Exports data in the form:
* <br>
* height, depth, rgb color data
*
* <br>
* example output:
* <br>
* 4, 0, 255,255,255,
*/
public String toData()
{
String s = Short.toString(height) + DATA_DELIMITER;
s += Short.toString(depth) + DATA_DELIMITER;
s += Integer.toString(color.getRed()) + DATA_DELIMITER + Integer.toString(color.getGreen()) + DATA_DELIMITER + Integer.toString(color.getBlue()) + DATA_DELIMITER;
return s;
}
@Override
public String toString()
{
String s = Short.toString(height) + DATA_DELIMITER;
s += Short.toString(depth) + DATA_DELIMITER;
s += Integer.toString(color.getRed()) + DATA_DELIMITER + Integer.toString(color.getGreen()) + DATA_DELIMITER + Integer.toString(color.getBlue()) + DATA_DELIMITER;
return s;
}
/**
* highest point
*/
public short height;
/**
* lowest point
*/
public short depth;
/**
* The average color for the 6 cardinal directions
*/
public Color color;
/**
* Creates an empty LodDataPoint
*/
public LodDataPoint()
{
height = -1;
depth = -1;
color = LodUtil.COLOR_INVISIBLE;
}
public LodDataPoint(short newHeight, short newDepth, Color newColor)
{
height = newHeight;
depth = newDepth;
color = newColor;
}
public LodDataPoint(int newHeight, int newDepth, Color newColor)
{
height = (short) newHeight;
depth = (short) newDepth;
color = newColor;
}
@Override
public int hashCode()
{
return Objects.hash(this.height, this.depth, this.color);
}
public boolean equals(LodDataPoint other)
{
return (this.height == other.height
&& this.depth == other.depth
&& this.color == other.color);
}
public boolean isEmpty()
{
return this.equals(EMPTY_DATA_POINT);
}
/**
* Outputs all data in a csv format
* with the given delimiter.
* <br>
* Exports data in the form:
* <br>
* height, depth, rgb color data
*
* <br>
* example output:
* <br>
* 4, 0, 255,255,255,
*/
public String toData()
{
String s = Short.toString(height) + DATA_DELIMITER;
s += Short.toString(depth) + DATA_DELIMITER;
s += Integer.toString(color.getRed()) + DATA_DELIMITER + Integer.toString(color.getGreen()) + DATA_DELIMITER + Integer.toString(color.getBlue()) + DATA_DELIMITER;
return s;
}
@Override
public String toString()
{
String s = Short.toString(height) + DATA_DELIMITER;
s += Short.toString(depth) + DATA_DELIMITER;
s += Integer.toString(color.getRed()) + DATA_DELIMITER + Integer.toString(color.getGreen()) + DATA_DELIMITER + Integer.toString(color.getBlue()) + DATA_DELIMITER;
return s;
}
}
@@ -42,9 +42,13 @@ public class LodDimension
public final DimensionType dimension;
/** measured in regions */
/**
* measured in regions
*/
private volatile int width;
/** measured in regions */
/**
* measured in regions
*/
private volatile int halfWidth;
@@ -65,9 +69,9 @@ public class LodDimension
{
dimension = newDimension;
width = newWidth;
halfWidth = (int)Math.floor(width / 2);
halfWidth = (int) Math.floor(width / 2);
if(newDimension != null && lodWorld != null)
if (newDimension != null && lodWorld != null)
{
try
{
@@ -84,8 +88,7 @@ public class LodDimension
// the compiler from complaining
ServerChunkProvider provider = serverWorld.getChunkSource();
saveDir = new File(provider.dataStorage.dataFolder.getCanonicalFile().getPath() + File.separatorChar + "lod");
}
else
} else
{
// connected to server
@@ -94,8 +97,7 @@ public class LodDimension
}
fileHandler = new LodDimensionFileHandler(saveDir, this);
}
catch (IOException e)
} catch (IOException e)
{
// the file handler wasn't able to be created
// we won't be able to read or write any files
@@ -103,23 +105,22 @@ public class LodDimension
}
regions = new LodRegion[width][width];
isRegionDirty = new boolean[width][width];
// populate isRegionDirty
for(int i = 0; i < width; i++)
for(int j = 0; j < width; j++)
for (int i = 0; i < width; i++)
for (int j = 0; j < width; j++)
isRegionDirty[i][j] = false;
center = new RegionPos(0,0);
center = new RegionPos(0, 0);
}
/**
* Move the center of this LodDimension and move all owned
* regions over by the given x and z offset. <br><br>
*
* <p>
* Synchronized to prevent multiple moves happening on top of each other.
*/
public synchronized void move(RegionPos regionOffset)
@@ -132,9 +133,9 @@ public class LodDimension
// and update the centerX and/or centerZ
if (Math.abs(xOffset) >= width || Math.abs(zOffset) >= width)
{
for(int x = 0; x < width; x++)
for (int x = 0; x < width; x++)
{
for(int z = 0; z < width; z++)
for (int z = 0; z < width; z++)
{
regions[x][z] = null;
}
@@ -149,28 +150,27 @@ public class LodDimension
// X
if(xOffset > 0)
if (xOffset > 0)
{
// move everything over to the left (as the center moves to the right)
for(int x = 0; x < width; x++)
for (int x = 0; x < width; x++)
{
for(int z = 0; z < width; z++)
for (int z = 0; z < width; z++)
{
if(x + xOffset < width)
if (x + xOffset < width)
regions[x][z] = regions[x + xOffset][z];
else
regions[x][z] = null;
}
}
}
else
} else
{
// move everything over to the right (as the center moves to the left)
for(int x = width - 1; x >= 0; x--)
for (int x = width - 1; x >= 0; x--)
{
for(int z = 0; z < width; z++)
for (int z = 0; z < width; z++)
{
if(x + xOffset >= 0)
if (x + xOffset >= 0)
regions[x][z] = regions[x + xOffset][z];
else
regions[x][z] = null;
@@ -179,30 +179,28 @@ public class LodDimension
}
// Z
if(zOffset > 0)
if (zOffset > 0)
{
// move everything up (as the center moves down)
for(int x = 0; x < width; x++)
for (int x = 0; x < width; x++)
{
for(int z = 0; z < width; z++)
for (int z = 0; z < width; z++)
{
if(z + zOffset < width)
if (z + zOffset < width)
regions[x][z] = regions[x][z + zOffset];
else
regions[x][z] = null;
}
}
}
else
} else
{
// move everything down (as the center moves up)
for(int x = 0; x < width; x++)
for (int x = 0; x < width; x++)
{
for(int z = width - 1; z >= 0; z--)
for (int z = width - 1; z >= 0; z--)
{
if(z + zOffset >= 0)
if (z + zOffset >= 0)
regions[x][z] = regions[x][z + zOffset];
else
regions[x][z] = null;
@@ -211,17 +209,12 @@ public class LodDimension
}
// update the new center
center.x += xOffset;
center.z += zOffset;
}
/**
* Gets the region at the given X and Z
* <br>
@@ -244,7 +237,7 @@ public class LodDimension
if (regions[xIndex][zIndex] == null)
{
/**TODO the value is currently 0 but should be determinated by the distance of the player)*/
regions[xIndex][zIndex] = new LodRegion((byte) 0,regionPos);
regions[xIndex][zIndex] = new LodRegion((byte) 0, regionPos);
}
}
@@ -270,7 +263,7 @@ public class LodDimension
/**
*this method creates all null regions
* this method creates all null regions
*/
public void initializeNullRegions()
{
@@ -279,19 +272,19 @@ public class LodDimension
RegionPos regionPos;
LodRegion region;
for(int x = 0; x < regions.length; x++)
for (int x = 0; x < regions.length; x++)
{
for(int z = 0; z < regions.length; z++)
for (int z = 0; z < regions.length; z++)
{
regionX = (x + center.x) - halfWidth;
regionZ = (z + center.z) - halfWidth;
regionPos = new RegionPos(regionX,regionZ);
regionPos = new RegionPos(regionX, regionZ);
region = getRegion(regionPos);
if (region == null)
{
// if no region exists, create it
region = new LodRegion((byte) 0,regionPos);
region = new LodRegion((byte) 0, regionPos);
addOrOverwriteRegion(region);
}
}
@@ -318,10 +311,10 @@ public class LodDimension
if (region == null)
{
// if no region exists, create it
region = new LodRegion((byte) 0,regionPos);
region = new LodRegion((byte) 0, regionPos);
addOrOverwriteRegion(region);
}
boolean nodeAdded = region.setData(levelPos,lodDataPoint,(byte) generationMode.complexity,true);
boolean nodeAdded = region.setData(levelPos, lodDataPoint, (byte) generationMode.complexity, true);
// only save valid LODs to disk
if (!dontSave && fileHandler != null)
{
@@ -331,8 +324,7 @@ public class LodDimension
int xIndex = (regionPos.x - center.x) + halfWidth;
int zIndex = (regionPos.z - center.z) + halfWidth;
isRegionDirty[xIndex][zIndex] = true;
}
catch(ArrayIndexOutOfBoundsException e)
} catch (ArrayIndexOutOfBoundsException e)
{
// This method was probably called when the dimension was changing size.
// Hopefully this shouldn't be an issue.
@@ -370,7 +362,7 @@ public class LodDimension
LodRegion region = getRegion(levelPos.getRegionPos());
if(region == null)
if (region == null)
{
return null;
}
@@ -385,7 +377,7 @@ public class LodDimension
{
LodRegion region = getRegion(LodUtil.convertGenericPosToRegionPos(chunkPos.x, chunkPos.z, LodUtil.CHUNK_DETAIL_LEVEL));
if(region == null)
if (region == null)
{
return false;
}
@@ -401,7 +393,7 @@ public class LodDimension
{
LodRegion region = getRegion(levelPos.getRegionPos());
if(region == null)
if (region == null)
{
return false;
}
@@ -416,7 +408,7 @@ public class LodDimension
{
LodRegion region = getRegion(levelPos.getRegionPos());
if(region == null)
if (region == null)
{
return false;
}
@@ -431,7 +423,7 @@ public class LodDimension
{
LodRegion region = getRegion(levelPos.getRegionPos());
if(region == null)
if (region == null)
{
return DistanceGenerationMode.NONE;
}
@@ -473,11 +465,6 @@ public class LodDimension
}
public int getCenterX()
{
return center.x;
@@ -491,7 +478,7 @@ public class LodDimension
/**
* TODO Double check that this method works as expected
*
* <p>
* Returns how many non-null LodChunks
* are stored in this LodDimension.
*/
@@ -511,8 +498,7 @@ public class LodDimension
// source to make sure it is in sync with region
// and isRegionDirty
return regions.length;
}
else
} else
{
return width;
}
@@ -521,14 +507,14 @@ public class LodDimension
public void setRegionWidth(int newWidth)
{
width = newWidth;
halfWidth = (int)Math.floor(width / 2);
halfWidth = (int) Math.floor(width / 2);
regions = new LodRegion[width][width];
isRegionDirty = new boolean[width][width];
// populate isRegionDirty
for(int i = 0; i < width; i++)
for(int j = 0; j < width; j++)
for (int i = 0; i < width; i++)
for (int j = 0; j < width; j++)
isRegionDirty[i][j] = false;
}
@@ -16,7 +16,8 @@ import java.io.Serializable;
* 0 for x, 1 for y, 2 for z in 3D
*/
public class LodRegion implements Serializable {
public class LodRegion implements Serializable
{
//x coord,
private byte minDetailLevel;
private static final byte POSSIBLE_LOD = 10;
@@ -41,8 +42,8 @@ public class LodRegion implements Serializable {
public final int regionPosX;
public final int regionPosZ;
public LodRegion(LevelContainer levelContainer, RegionPos regionPos) {
/**TODO there is some error here in the update*/
public LodRegion(LevelContainer levelContainer, RegionPos regionPos)
{
this.regionPosX = regionPos.x;
this.regionPosZ = regionPos.z;
this.minDetailLevel = levelContainer.detailLevel;
@@ -63,7 +64,8 @@ public class LodRegion implements Serializable {
dataExistence[minDetailLevel] = levelContainer.dataExistence;
//Initialize all the different matrices
for (byte lod = (byte) (minDetailLevel + 1); lod <= LodUtil.REGION_DETAIL_LEVEL; lod++) {
for (byte lod = (byte) (minDetailLevel + 1); lod <= LodUtil.REGION_DETAIL_LEVEL; lod++)
{
int size = (short) Math.pow(2, LodUtil.REGION_DETAIL_LEVEL - lod);
colors[lod] = new byte[size][size][3];
height[lod] = new short[size][size];
@@ -73,10 +75,13 @@ public class LodRegion implements Serializable {
}
int sizeDiff;
LevelPos levelPos;
for (byte tempLod = (byte) (minDetailLevel + 1); tempLod <= LodUtil.REGION_DETAIL_LEVEL; tempLod++) {
sizeDiff = (int) Math.pow(2,LodUtil.REGION_DETAIL_LEVEL - tempLod);
for (int x = 0; x < sizeDiff; x++) {
for (int z = 0; z < sizeDiff; z++) {
for (byte tempLod = (byte) (minDetailLevel + 1); tempLod <= LodUtil.REGION_DETAIL_LEVEL; tempLod++)
{
sizeDiff = (int) Math.pow(2, LodUtil.REGION_DETAIL_LEVEL - tempLod);
for (int x = 0; x < sizeDiff; x++)
{
for (int z = 0; z < sizeDiff; z++)
{
levelPos = new LevelPos(tempLod, x, z);
update(levelPos);
}
@@ -84,7 +89,8 @@ public class LodRegion implements Serializable {
}
}
public LodRegion(byte minDetailLevel, RegionPos regionPos) {
public LodRegion(byte minDetailLevel, RegionPos regionPos)
{
this.minDetailLevel = minDetailLevel;
this.regionPosX = regionPos.x;
this.regionPosZ = regionPos.z;
@@ -100,7 +106,8 @@ public class LodRegion implements Serializable {
//Initialize all the different matrices
for (byte lod = minDetailLevel; lod <= LodUtil.REGION_DETAIL_LEVEL; lod++) {
for (byte lod = minDetailLevel; lod <= LodUtil.REGION_DETAIL_LEVEL; lod++)
{
int size = (short) Math.pow(2, LodUtil.REGION_DETAIL_LEVEL - lod);
colors[lod] = new byte[size][size][3];
height[lod] = new short[size][size];
@@ -120,12 +127,14 @@ public class LodRegion implements Serializable {
* @param update
* @return
*/
public boolean setData(LevelPos levelPos, LodDataPoint dataPoint, byte generationType, boolean update) {
public boolean setData(LevelPos levelPos, LodDataPoint dataPoint, byte generationType, boolean update)
{
levelPos = levelPos.regionModule();
if ((this.generationType[levelPos.detailLevel][levelPos.posX][levelPos.posZ] == 0) || (generationType >= this.generationType[levelPos.detailLevel][levelPos.posX][levelPos.posZ])) {
if ((this.generationType[levelPos.detailLevel][levelPos.posX][levelPos.posZ] == 0) || (generationType >= this.generationType[levelPos.detailLevel][levelPos.posX][levelPos.posZ]))
{
//update the number of node present
//if (this.generationType[lod][posX][posZ] == 0) numberOfPoints++;
if (this.dataExistence[levelPos.detailLevel][levelPos.posX][levelPos.posZ]) numberOfPoints++;
//add the node data
this.colors[levelPos.detailLevel][levelPos.posX][levelPos.posZ][0] = (byte) (dataPoint.color.getRed() - 128);
@@ -138,20 +147,24 @@ public class LodRegion implements Serializable {
//update could be stopped and a single big update could be done at the end
LevelPos tempLevelPos = levelPos;
if (update) {
for (byte tempLod = (byte) (levelPos.detailLevel + 1); tempLod <= LodUtil.REGION_DETAIL_LEVEL; tempLod++) {
if (update)
{
for (byte tempLod = (byte) (levelPos.detailLevel + 1); tempLod <= LodUtil.REGION_DETAIL_LEVEL; tempLod++)
{
tempLevelPos = tempLevelPos.convert(tempLod);
update(tempLevelPos);
}
}
return true;
} else {
} else
{
return false;
}
}
public LodDataPoint getData(ChunkPos chunkPos) {
public LodDataPoint getData(ChunkPos chunkPos)
{
return getData(new LevelPos(LodUtil.CHUNK_DETAIL_LEVEL, chunkPos.x, chunkPos.z));
}
@@ -161,7 +174,8 @@ public class LodRegion implements Serializable {
* @param lod
* @return the data at the relative pos and level
*/
public LodDataPoint getData(byte lod, BlockPos blockPos) {
public LodDataPoint getData(byte lod, BlockPos blockPos)
{
int posX = Math.floorMod(blockPos.getX(), (int) Math.pow(2, lod));
int posZ = Math.floorMod(blockPos.getZ(), (int) Math.pow(2, lod));
return getData(new LevelPos(lod, posX, posZ));
@@ -173,7 +187,8 @@ public class LodRegion implements Serializable {
* @param levelPos
* @return the data at the relative pos and level
*/
public LodDataPoint getData(LevelPos levelPos) {
public LodDataPoint getData(LevelPos levelPos)
{
levelPos = levelPos.regionModule();
return new LodDataPoint(
height[levelPos.detailLevel][levelPos.posX][levelPos.posZ],
@@ -188,37 +203,29 @@ public class LodRegion implements Serializable {
/**
* @return
*/
/*
public List<LevelPos> getDataToGenerate(int playerPosX, int playerPosZ, int start, int end, byte generetion, byte detailLevel) {
if(detailLevel < minDetailLevel) detailLevel = minDetailLevel;
LevelPos levelPos
/*
public List<LevelPos> getDataToGenerate(int playerPosX, int playerPosZ, int start, int end, byte generetion, byte detailLevel)
{
if (detailLevel < minDetailLevel) detailLevel = minDetailLevel;
LevelPos levelPos;
int size;
int width;
int posX;
int posZ;
int distance;
for(int tempLod = detailLevel; tempLod <= LodUtil.REGION_DETAIL_LEVEL; tempLod++){
size = (int) Math.pow(2,LodUtil.REGION_DETAIL_LEVEL-tempLod);
width = (int) Math.pow(2,tempLod);
for(int x = 0; x < size; x++){
for(int z = 0; z < size; z++){
posX = regionPosX * 512 + x * width + width/2;
posZ = regionPosZ * 512 + z * width + width/2;
distance = (int) Math.sqrt(Math.pow(playerPosX - posX, 2) + Math.pow(playerPosZ - posZ, 2))
if(distance >= start && distance <= end){
}
}
}
}
size = (int) Math.pow(2, LodUtil.REGION_DETAIL_LEVEL - tempLod);
width = (int) Math.pow(2, tempLod);
posX = regionPosX * 512 + width / 2;
posZ = regionPosZ * 512 + width / 2;
distance = (int) Math.sqrt(Math.pow(playerPosX - posX, 2) + Math.pow(playerPosZ - posZ, 2))
}
*/
*/
/**
* @return
*/
/*
public List<LevelPos> getDataToRender(int playerPosX, int playerPosZ, int start, int end, byte detailLevel) {
*//*
public List<LevelPos> getDataToRender(int playerPosX, int playerPosZ, int start, int end, byte detailLevel)
{
if(detailLevel < minDetailLevel) detailLevel = minDetailLevel;
int size;
int width;
@@ -234,13 +241,14 @@ public class LodRegion implements Serializable {
}
}
}
*/
*/
/**TODO a method to update a whole area, to be used as a single big update*/
/**
* @param levelPos
*/
private void updateArea(LevelPos levelPos) {
private void updateArea(LevelPos levelPos)
{
/*
LevelPos tempLevelPos = levelPos;
int sizeDiff;
@@ -259,13 +267,25 @@ public class LodRegion implements Serializable {
}
int sizeDiff;
LevelPos levelPos;
for (byte tempLod = (byte) (minDetailLevel + 1); tempLod <= LodUtil.REGION_DETAIL_LEVEL; tempLod++) {
sizeDiff = (int) Math.pow(2,LodUtil.REGION_DETAIL_LEVEL - tempLod);
for (int x = 0; x < sizeDiff; x++) {
for (int z = 0; z < sizeDiff; z++) {
levelPos = new LevelPos(tempLod, x, z);
update(levelPos);
}
}
}
*/
}
/**
* @param levelPos
*/
private void update(LevelPos levelPos) {
private void update(LevelPos levelPos)
{
levelPos = levelPos.regionModule();
boolean[][] children = getChildren(levelPos);
@@ -282,13 +302,16 @@ public class LodRegion implements Serializable {
int newPosZ;
byte newLod;
LevelPos childPos;
for (int x = 0; x <= 1; x++) {
for (int z = 0; z <= 1; z++) {
for (int x = 0; x <= 1; x++)
{
for (int z = 0; z <= 1; z++)
{
newPosX = 2 * levelPos.posX + x;
newPosZ = 2 * levelPos.posZ + z;
newLod = (byte) (levelPos.detailLevel - 1);
childPos = new LevelPos(newLod, newPosX, newPosZ);
if (hasDataBeenGenerated(childPos)) {
if (hasDataBeenGenerated(childPos))
{
numberOfChildren++;
tempRed += colors[newLod][newPosX][newPosZ][0];
@@ -301,7 +324,8 @@ public class LodRegion implements Serializable {
}
}
if (numberOfChildren > 0) {
if (numberOfChildren > 0)
{
colors[levelPos.detailLevel][levelPos.posX][levelPos.posZ][0] = (byte) (tempRed / numberOfChildren);
colors[levelPos.detailLevel][levelPos.posX][levelPos.posZ][1] = (byte) (tempGreen / numberOfChildren);
colors[levelPos.detailLevel][levelPos.posX][levelPos.posZ][2] = (byte) (tempBlue / numberOfChildren);
@@ -316,15 +340,19 @@ public class LodRegion implements Serializable {
* @param levelPos
* @return
*/
private boolean[][] getChildren(LevelPos levelPos) {
private boolean[][] getChildren(LevelPos levelPos)
{
levelPos = levelPos.regionModule();
boolean[][] children = new boolean[2][2];
int numberOfChild = 0;
if (minDetailLevel == levelPos.detailLevel) {
if (minDetailLevel == levelPos.detailLevel)
{
return children;
}
for (int x = 0; x <= 1; x++) {
for (int z = 0; z <= 1; z++) {
for (int x = 0; x <= 1; x++)
{
for (int z = 0; z <= 1; z++)
{
children[x][z] = (dataExistence[levelPos.detailLevel - 1][2 * levelPos.posX + x][2 * levelPos.posZ + z]);
}
}
@@ -335,7 +363,8 @@ public class LodRegion implements Serializable {
* @param chunkPos
* @return
*/
public boolean doesDataExist(ChunkPos chunkPos) {
public boolean doesDataExist(ChunkPos chunkPos)
{
return doesDataExist(new LevelPos(LodUtil.CHUNK_DETAIL_LEVEL, chunkPos.x, chunkPos.z));
}
@@ -343,7 +372,8 @@ public class LodRegion implements Serializable {
* @param levelPos
* @return
*/
public boolean doesDataExist(LevelPos levelPos) {
public boolean doesDataExist(LevelPos levelPos)
{
levelPos = levelPos.regionModule();
return dataExistence[levelPos.detailLevel][levelPos.posX][levelPos.posZ];
}
@@ -352,10 +382,12 @@ public class LodRegion implements Serializable {
* @param levelPos
* @return
*/
public DistanceGenerationMode getGenerationMode(LevelPos levelPos) {
public DistanceGenerationMode getGenerationMode(LevelPos levelPos)
{
levelPos = levelPos.regionModule();
DistanceGenerationMode generationMode = DistanceGenerationMode.NONE;
switch (generationType[levelPos.detailLevel][levelPos.posX][levelPos.posZ]) {
switch (generationType[levelPos.detailLevel][levelPos.posX][levelPos.posZ])
{
case 0:
generationMode = DistanceGenerationMode.NONE;
break;
@@ -386,12 +418,14 @@ public class LodRegion implements Serializable {
* @param levelPos
* @return
*/
public boolean hasDataBeenGenerated(LevelPos levelPos) {
public boolean hasDataBeenGenerated(LevelPos levelPos)
{
levelPos = levelPos.regionModule();
return (generationType[levelPos.detailLevel][levelPos.posX][levelPos.posZ] != 0);
}
public byte getMinDetailLevel() {
public byte getMinDetailLevel()
{
return minDetailLevel;
}
@@ -401,15 +435,18 @@ public class LodRegion implements Serializable {
* @param lod
* @return
*/
public LevelContainer getLevel(byte lod) {
public LevelContainer getLevel(byte lod)
{
return new LevelContainer(lod, colors[lod], height[lod], depth[lod], generationType[lod], dataExistence[lod]);
}
/**
* @param levelContainer
*/
public void addLevel(LevelContainer levelContainer) {
if (levelContainer.detailLevel < minDetailLevel - 1) {
public void addLevel(LevelContainer levelContainer)
{
if (levelContainer.detailLevel < minDetailLevel - 1)
{
throw new IllegalArgumentException("addLevel requires a level that is at least the minimum level of the region -1 ");
}
if (levelContainer.detailLevel == minDetailLevel - 1) minDetailLevel = levelContainer.detailLevel;
@@ -424,8 +461,10 @@ public class LodRegion implements Serializable {
/**
* @param lod
*/
public void removeDetailLevel(byte lod) {
for (byte tempLod = 0; tempLod <= lod; tempLod++) {
public void removeDetailLevel(byte lod)
{
for (byte tempLod = 0; tempLod <= lod; tempLod++)
{
colors[tempLod] = new byte[0][0][0];
height[tempLod] = new short[0][0];
depth[tempLod] = new short[0][0];
@@ -434,7 +473,8 @@ public class LodRegion implements Serializable {
}
}
public String toString(){
public String toString()
{
return getLevel(LodUtil.REGION_DETAIL_LEVEL).toString();
}
}
+123 -122
View File
@@ -26,133 +26,134 @@ import net.minecraft.world.DimensionType;
/**
* This stores all LODs for a given world.
*
*
* @author James Seibel
* @author Leonardo Amato
* @version 8-17-2021
*/
public class LodWorld
{
private String worldName;
private Map<DimensionType, LodDimension> lodDimensions;
/** If true then the LOD world is setup and ready to use */
private boolean isWorldLoaded = false;
public static final String NO_WORLD_LOADED = "No world loaded";
public LodWorld()
{
worldName = NO_WORLD_LOADED;
}
/**
* Set up the LodQuadTreeWorld with the given newWorldName. <br>
* This should be done whenever loading a new world.
*
* @param newWorldName name of the world
*/
public void selectWorld(String newWorldName)
{
if (newWorldName.isEmpty())
{
deselectWorld();
return;
}
if (worldName.equals(newWorldName))
// don't recreate everything if we
// didn't actually change worlds
return;
worldName = newWorldName;
lodDimensions = new Hashtable<DimensionType, LodDimension>();
isWorldLoaded = true;
}
/**
* Set the worldName to "No world loaded"
* and clear the lodDimensions Map. <br>
* This should be done whenever unloaded a world.
*/
public void deselectWorld()
{
worldName = NO_WORLD_LOADED;
lodDimensions = null;
isWorldLoaded = false;
}
/**
* Adds newStorage to this world, if a LodQuadTreeDimension
* already exists for the given dimension it is replaced.
*/
public void addLodDimension(LodDimension newStorage)
{
if (lodDimensions == null)
return;
lodDimensions.put(newStorage.dimension, newStorage);
}
/**
* Returns null if no LodQuadTreeDimension exists for the given dimension
*/
public LodDimension getLodDimension(DimensionType dimension)
{
if (lodDimensions == null)
return null;
return lodDimensions.get(dimension);
}
/**
* Resizes the max width in regions that each LodDimension
* should use.
*/
public void resizeDimensionRegionWidth(int newWidth)
{
if (lodDimensions == null)
return;
saveAllDimensions();
for (DimensionType key : lodDimensions.keySet())
lodDimensions.get(key).setRegionWidth(newWidth);
}
/**
* Requests all dimensions save any dirty regions they may have.
*/
public void saveAllDimensions()
{
if (lodDimensions == null)
return;
ClientProxy.LOGGER.info("Saving LODs");
for (DimensionType key : lodDimensions.keySet())
lodDimensions.get(key).saveDirtyRegionsToFileAsync();
}
public boolean getIsWorldLoaded()
{
return isWorldLoaded;
}
public String getWorldName()
{
return worldName;
}
@Override
public String toString()
{
return "World name: " + worldName;
}
private String worldName;
private Map<DimensionType, LodDimension> lodDimensions;
/**
* If true then the LOD world is setup and ready to use
*/
private boolean isWorldLoaded = false;
public static final String NO_WORLD_LOADED = "No world loaded";
public LodWorld()
{
worldName = NO_WORLD_LOADED;
}
/**
* Set up the LodQuadTreeWorld with the given newWorldName. <br>
* This should be done whenever loading a new world.
*
* @param newWorldName name of the world
*/
public void selectWorld(String newWorldName)
{
if (newWorldName.isEmpty())
{
deselectWorld();
return;
}
if (worldName.equals(newWorldName))
// don't recreate everything if we
// didn't actually change worlds
return;
worldName = newWorldName;
lodDimensions = new Hashtable<DimensionType, LodDimension>();
isWorldLoaded = true;
}
/**
* Set the worldName to "No world loaded"
* and clear the lodDimensions Map. <br>
* This should be done whenever unloaded a world.
*/
public void deselectWorld()
{
worldName = NO_WORLD_LOADED;
lodDimensions = null;
isWorldLoaded = false;
}
/**
* Adds newStorage to this world, if a LodQuadTreeDimension
* already exists for the given dimension it is replaced.
*/
public void addLodDimension(LodDimension newStorage)
{
if (lodDimensions == null)
return;
lodDimensions.put(newStorage.dimension, newStorage);
}
/**
* Returns null if no LodQuadTreeDimension exists for the given dimension
*/
public LodDimension getLodDimension(DimensionType dimension)
{
if (lodDimensions == null)
return null;
return lodDimensions.get(dimension);
}
/**
* Resizes the max width in regions that each LodDimension
* should use.
*/
public void resizeDimensionRegionWidth(int newWidth)
{
if (lodDimensions == null)
return;
saveAllDimensions();
for (DimensionType key : lodDimensions.keySet())
lodDimensions.get(key).setRegionWidth(newWidth);
}
/**
* Requests all dimensions save any dirty regions they may have.
*/
public void saveAllDimensions()
{
if (lodDimensions == null)
return;
ClientProxy.LOGGER.info("Saving LODs");
for (DimensionType key : lodDimensions.keySet())
lodDimensions.get(key).saveDirtyRegionsToFileAsync();
}
public boolean getIsWorldLoaded()
{
return isWorldLoaded;
}
public String getWorldName()
{
return worldName;
}
@Override
public String toString()
{
return "World name: " + worldName;
}
}
@@ -46,241 +46,240 @@ import net.minecraftforge.eventbus.api.SubscribeEvent;
/**
* This handles all events sent to the client,
* and is the starting point for most of this program.
*
*
* @author James_Seibel
* @version 8-17-2021
*/
public class ClientProxy
{
public static final Logger LOGGER = LogManager.getLogger("LOD");
private static LodWorld lodWorld = new LodWorld();
private static LodNodeBuilder lodNodeBuilder = new LodNodeBuilder();
private static LodNodeBufferBuilder lodBufferBuilder = new LodNodeBufferBuilder(lodNodeBuilder);
private static LodNodeRenderer renderer = new LodNodeRenderer(lodBufferBuilder);
private boolean configOverrideReminderPrinted = false;
Minecraft mc = Minecraft.getInstance();
/** This is used to determine if the LODs should be regenerated */
public static int previousChunkRenderDistance = 0;
/** This is used to determine if the LODs should be regenerated */
public static int previousLodMultiplierDistance = 0;
public ClientProxy()
{
}
//==============//
// render event //
//==============//
/**
* Do any setup that is required to draw LODs
* and then tell the LodRenderer to draw.
*/
public void renderLods(float partialTicks)
{
if (mc == null || mc.player == null || !lodWorld.getIsWorldLoaded())
return;
public static final Logger LOGGER = LogManager.getLogger("LOD");
private static LodWorld lodWorld = new LodWorld();
private static LodNodeBuilder lodNodeBuilder = new LodNodeBuilder();
private static LodNodeBufferBuilder lodBufferBuilder = new LodNodeBufferBuilder(lodNodeBuilder);
private static LodNodeRenderer renderer = new LodNodeRenderer(lodBufferBuilder);
private boolean configOverrideReminderPrinted = false;
Minecraft mc = Minecraft.getInstance();
/**
* This is used to determine if the LODs should be regenerated
*/
public static int previousChunkRenderDistance = 0;
/**
* This is used to determine if the LODs should be regenerated
*/
public static int previousLodMultiplierDistance = 0;
viewDistanceChangedEvent();
public ClientProxy()
{
LodDimension lodDim = lodWorld.getLodDimension(mc.player.level.dimensionType());
if (lodDim == null)
return;
playerMoveEvent(lodDim);
// comment out when creating a release
applyConfigOverrides();
// Note to self:
// if "unspecified" shows up in the pie chart, it is
// possibly because the amount of time between sections
// is too small for the profiler to measure
IProfiler profiler = mc.getProfiler();
profiler.pop(); // get out of "terrain"
profiler.push("LOD");
renderer.drawLODs(lodDim, partialTicks, mc.getProfiler());
profiler.pop(); // end LOD
profiler.push("terrain"); // restart "terrain"
// these can't be set until after the buffers are built (in renderer.drawLODs)
// otherwise the buffers may be set to the wrong size, or not changed at all
previousChunkRenderDistance = mc.options.renderDistance;
previousLodMultiplierDistance = LodConfig.CLIENT.lodChunkRadiusMultiplier.get();
}
}
private void applyConfigOverrides()
{
// remind the developer(s). that config override is active
if (!configOverrideReminderPrinted)
{
mc.player.sendMessage(new StringTextComponent("Debug settings enabled!"), mc.player.getUUID());
configOverrideReminderPrinted = true;
}
//==============//
// render event //
//==============//
/**
* Do any setup that is required to draw LODs
* and then tell the LodRenderer to draw.
*/
public void renderLods(float partialTicks)
{
if (mc == null || mc.player == null || !lodWorld.getIsWorldLoaded())
return;
viewDistanceChangedEvent();
LodDimension lodDim = lodWorld.getLodDimension(mc.player.level.dimensionType());
if (lodDim == null)
return;
playerMoveEvent(lodDim);
// comment out when creating a release
applyConfigOverrides();
// Note to self:
// if "unspecified" shows up in the pie chart, it is
// possibly because the amount of time between sections
// is too small for the profiler to measure
IProfiler profiler = mc.getProfiler();
profiler.pop(); // get out of "terrain"
profiler.push("LOD");
renderer.drawLODs(lodDim, partialTicks, mc.getProfiler());
profiler.pop(); // end LOD
profiler.push("terrain"); // restart "terrain"
// these can't be set until after the buffers are built (in renderer.drawLODs)
// otherwise the buffers may be set to the wrong size, or not changed at all
previousChunkRenderDistance = mc.options.renderDistance;
previousLodMultiplierDistance = LodConfig.CLIENT.lodChunkRadiusMultiplier.get();
}
private void applyConfigOverrides()
{
// remind the developer(s). that config override is active
if (!configOverrideReminderPrinted)
{
mc.player.sendMessage(new StringTextComponent("Debug settings enabled!"), mc.player.getUUID());
configOverrideReminderPrinted = true;
}
// LodConfig.CLIENT.drawLODs.set(true);
// LodConfig.CLIENT.debugMode.set(false);
LodConfig.CLIENT.maxDrawDetail.set(LodDetail.FULL);
LodConfig.CLIENT.maxGenerationDetail.set(LodDetail.FULL);
LodConfig.CLIENT.lodChunkRadiusMultiplier.set(16);
LodConfig.CLIENT.fogDistance.set(FogDistance.FAR);
LodConfig.CLIENT.fogDrawOverride.set(FogDrawOverride.ALWAYS_DRAW_FOG_FANCY);
LodConfig.CLIENT.shadingMode.set(ShadingMode.DARKEN_SIDES);
LodConfig.CLIENT.maxDrawDetail.set(LodDetail.FULL);
LodConfig.CLIENT.maxGenerationDetail.set(LodDetail.FULL);
LodConfig.CLIENT.lodChunkRadiusMultiplier.set(16);
LodConfig.CLIENT.fogDistance.set(FogDistance.FAR);
LodConfig.CLIENT.fogDrawOverride.set(FogDrawOverride.ALWAYS_DRAW_FOG_FANCY);
LodConfig.CLIENT.shadingMode.set(ShadingMode.DARKEN_SIDES);
// LodConfig.CLIENT.brightnessMultiplier.set(1.0);
// LodConfig.CLIENT.saturationMultiplier.set(1.0);
LodConfig.CLIENT.distanceGenerationMode.set(DistanceGenerationMode.SURFACE);
LodConfig.CLIENT.allowUnstableFeatureGeneration.set(false);
LodConfig.CLIENT.numberOfWorldGenerationThreads.set(2);
}
//==============//
// forge events //
//==============//
@SubscribeEvent
public void chunkLoadEvent(ChunkEvent.Load event)
{
lodNodeBuilder.generateLodNodeAsync(event.getChunk(), lodWorld, event.getWorld(), DistanceGenerationMode.SERVER);
}
@SubscribeEvent
public void worldSaveEvent(WorldEvent.Save event)
{
if (lodWorld != null)
lodWorld.saveAllDimensions();
}
@SubscribeEvent
public void worldLoadEvent(WorldEvent.Load event)
{
// the player just loaded a new world/dimension
lodWorld.selectWorld(LodUtil.getWorldID(event.getWorld()));
// make sure the correct LODs are being rendered
// (if this isn't done the previous world's LODs may be drawn)
renderer.regenerateLODsNextFrame();
}
@SubscribeEvent
public void worldUnloadEvent(WorldEvent.Unload event)
{
// the player just unloaded a world/dimension
if(mc.getConnection().getLevel() == null)
{
// if this isn't done unfinished tasks may be left in the queue
// preventing new LodChunks form being generated
LodNodeGenWorker.restartExecuterService();
lodBufferBuilder.numberOfChunksWaitingToGenerate.set(0);
// the player has disconnected from a server
lodWorld.deselectWorld();
}
}
@SubscribeEvent
public void blockChangeEvent(BlockEvent event)
{
if (event.getClass() == BlockEvent.BreakEvent.class ||
event.getClass() == BlockEvent.EntityPlaceEvent.class ||
event.getClass() == BlockEvent.EntityMultiPlaceEvent.class ||
event.getClass() == BlockEvent.FluidPlaceBlockEvent.class ||
event.getClass() == BlockEvent.PortalSpawnEvent.class)
{
// recreate the LOD where the blocks were changed
lodNodeBuilder.generateLodNodeAsync(event.getWorld().getChunk(event.getPos()), lodWorld, event.getWorld());
}
}
//==================//
// frame LOD events //
//==================//
/**
* Re-centers the given LodDimension if it needs to be.
*/
private void playerMoveEvent(LodDimension lodDim)
{
// make sure the dimension is centered
RegionPos playerRegionPos = new RegionPos(mc.player.blockPosition());
RegionPos worldRegionOffset = new RegionPos(playerRegionPos.x - lodDim.getCenterX(), playerRegionPos.z - lodDim.getCenterZ());
if (worldRegionOffset.x != 0 || worldRegionOffset.z != 0)
{
lodWorld.saveAllDimensions();
lodDim.move(worldRegionOffset);
//LOGGER.info("offset: " + worldRegionOffset.x + "," + worldRegionOffset.z + "\t center: " + lodDim.getCenterX() + "," + lodDim.getCenterZ());
}
}
LodConfig.CLIENT.distanceGenerationMode.set(DistanceGenerationMode.FEATURES);
LodConfig.CLIENT.allowUnstableFeatureGeneration.set(false);
LodConfig.CLIENT.numberOfWorldGenerationThreads.set(10);
}
/**
* Re-sizes all LodDimensions if they needs to be.
*/
private void viewDistanceChangedEvent()
{
// calculate how wide the dimension(s) should be in regions
//int chunksWide = (mc.options.renderDistance * 2) * LodConfig.CLIENT.lodChunkRadiusMultiplier.get();
int chunksWide = 8 * 2 * LodConfig.CLIENT.lodChunkRadiusMultiplier.get() + 1;
int newWidth = (int)Math.ceil(chunksWide / (float) LodUtil.REGION_WIDTH_IN_CHUNKS);
newWidth = (newWidth % 2 == 0) ? (newWidth += 1) : (newWidth += 2); // make sure we have a odd number of regions
// do the dimensions need to change in size?
if (lodNodeBuilder.defaultDimensionWidthInRegions != newWidth)
{
// TODO make this async
// update the dimensions to fit the new width
lodWorld.resizeDimensionRegionWidth(newWidth);
lodNodeBuilder.defaultDimensionWidthInRegions = newWidth;
renderer.setupBuffers(newWidth);
//LOGGER.info("new dimension width in regions: " + newWidth + "\t potential: " + newWidth );
}
}
//==============//
// forge events //
//==============//
//================//
// public getters //
//================//
public static LodWorld getLodWorld()
{
return lodWorld;
}
public static LodNodeBuilder getLodBuilder()
{
return lodNodeBuilder;
}
public static LodNodeRenderer getRenderer()
{
return renderer;
}
@SubscribeEvent
public void chunkLoadEvent(ChunkEvent.Load event)
{
lodNodeBuilder.generateLodNodeAsync(event.getChunk(), lodWorld, event.getWorld(), DistanceGenerationMode.SERVER);
}
@SubscribeEvent
public void worldSaveEvent(WorldEvent.Save event)
{
if (lodWorld != null)
lodWorld.saveAllDimensions();
}
@SubscribeEvent
public void worldLoadEvent(WorldEvent.Load event)
{
// the player just loaded a new world/dimension
lodWorld.selectWorld(LodUtil.getWorldID(event.getWorld()));
// make sure the correct LODs are being rendered
// (if this isn't done the previous world's LODs may be drawn)
renderer.regenerateLODsNextFrame();
}
@SubscribeEvent
public void worldUnloadEvent(WorldEvent.Unload event)
{
// the player just unloaded a world/dimension
if (mc.getConnection().getLevel() == null)
{
// if this isn't done unfinished tasks may be left in the queue
// preventing new LodChunks form being generated
LodNodeGenWorker.restartExecuterService();
lodBufferBuilder.numberOfChunksWaitingToGenerate.set(0);
// the player has disconnected from a server
lodWorld.deselectWorld();
}
}
@SubscribeEvent
public void blockChangeEvent(BlockEvent event)
{
if (event.getClass() == BlockEvent.BreakEvent.class ||
event.getClass() == BlockEvent.EntityPlaceEvent.class ||
event.getClass() == BlockEvent.EntityMultiPlaceEvent.class ||
event.getClass() == BlockEvent.FluidPlaceBlockEvent.class ||
event.getClass() == BlockEvent.PortalSpawnEvent.class)
{
// recreate the LOD where the blocks were changed
lodNodeBuilder.generateLodNodeAsync(event.getWorld().getChunk(event.getPos()), lodWorld, event.getWorld());
}
}
//==================//
// frame LOD events //
//==================//
/**
* Re-centers the given LodDimension if it needs to be.
*/
private void playerMoveEvent(LodDimension lodDim)
{
// make sure the dimension is centered
RegionPos playerRegionPos = new RegionPos(mc.player.blockPosition());
RegionPos worldRegionOffset = new RegionPos(playerRegionPos.x - lodDim.getCenterX(), playerRegionPos.z - lodDim.getCenterZ());
if (worldRegionOffset.x != 0 || worldRegionOffset.z != 0)
{
lodWorld.saveAllDimensions();
lodDim.move(worldRegionOffset);
//LOGGER.info("offset: " + worldRegionOffset.x + "," + worldRegionOffset.z + "\t center: " + lodDim.getCenterX() + "," + lodDim.getCenterZ());
}
}
/**
* Re-sizes all LodDimensions if they needs to be.
*/
private void viewDistanceChangedEvent()
{
// calculate how wide the dimension(s) should be in regions
//int chunksWide = (mc.options.renderDistance * 2) * LodConfig.CLIENT.lodChunkRadiusMultiplier.get();
int chunksWide = 8 * 2 * LodConfig.CLIENT.lodChunkRadiusMultiplier.get() + 1;
int newWidth = (int) Math.ceil(chunksWide / (float) LodUtil.REGION_WIDTH_IN_CHUNKS);
newWidth = (newWidth % 2 == 0) ? (newWidth += 1) : (newWidth += 2); // make sure we have a odd number of regions
// do the dimensions need to change in size?
if (lodNodeBuilder.defaultDimensionWidthInRegions != newWidth)
{
// TODO make this async
// update the dimensions to fit the new width
lodWorld.resizeDimensionRegionWidth(newWidth);
lodNodeBuilder.defaultDimensionWidthInRegions = newWidth;
renderer.setupBuffers(newWidth);
//LOGGER.info("new dimension width in regions: " + newWidth + "\t potential: " + newWidth );
}
}
//================//
// public getters //
//================//
public static LodWorld getLodWorld()
{
return lodWorld;
}
public static LodNodeBuilder getLodBuilder()
{
return lodNodeBuilder;
}
public static LodNodeRenderer getRenderer()
{
return renderer;
}
}
File diff suppressed because it is too large Load Diff