From 9990132db25cdabc775894562ff59bed771bab99 Mon Sep 17 00:00:00 2001 From: Leonardo Date: Fri, 27 Aug 2021 14:31:13 +0200 Subject: [PATCH] small changes --- .../seibel/lod/builders/LodBufferBuilder.java | 2 +- .../com/seibel/lod/objects/LodDimension.java | 192 ++++--- .../com/seibel/lod/proxy/ClientProxy.java | 496 +++++++++--------- 3 files changed, 370 insertions(+), 320 deletions(-) diff --git a/src/main/java/com/seibel/lod/builders/LodBufferBuilder.java b/src/main/java/com/seibel/lod/builders/LodBufferBuilder.java index 56998d7d8..59afd9853 100644 --- a/src/main/java/com/seibel/lod/builders/LodBufferBuilder.java +++ b/src/main/java/com/seibel/lod/builders/LodBufferBuilder.java @@ -60,7 +60,7 @@ public class LodBufferBuilder /** * This holds the threads used to generate buffers. */ - private ExecutorService bufferBuilderThreads = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors(), new LodThreadFactory(this.getClass().getSimpleName() + " - builder")); + private ExecutorService bufferBuilderThreads = Executors.newFixedThreadPool(LodConfig.CLIENT.numberOfWorldGenerationThreads.get(), new LodThreadFactory(this.getClass().getSimpleName() + " - builder")); /** * The buffers that are used to create LODs using far fog diff --git a/src/main/java/com/seibel/lod/objects/LodDimension.java b/src/main/java/com/seibel/lod/objects/LodDimension.java index f93990ac7..8ba339ae0 100644 --- a/src/main/java/com/seibel/lod/objects/LodDimension.java +++ b/src/main/java/com/seibel/lod/objects/LodDimension.java @@ -21,13 +21,13 @@ import java.io.File; import java.io.IOException; import java.security.InvalidParameterException; import java.util.*; -import java.util.concurrent.ConcurrentNavigableMap; -import java.util.stream.Collectors; +import java.util.concurrent.*; import com.seibel.lod.enums.DistanceGenerationMode; import com.seibel.lod.handlers.LodDimensionFileHandler; import com.seibel.lod.objects.LevelPos.LevelPos; import com.seibel.lod.util.DetailDistanceUtil; +import com.seibel.lod.util.LodThreadFactory; import com.seibel.lod.util.LodUtil; import net.minecraft.client.Minecraft; @@ -36,6 +36,7 @@ import net.minecraft.world.DimensionType; import net.minecraft.world.server.ServerChunkProvider; import net.minecraft.world.server.ServerWorld; + /** * This object holds all loaded LOD regions * for a given dimension. @@ -63,8 +64,10 @@ public class LodDimension public volatile boolean isRegionDirty[][]; private volatile RegionPos center; - + private volatile ChunkPos lastGenChunk; + private volatile ChunkPos lastCutChunk; private LodDimensionFileHandler fileHandler; + private ExecutorService cutAndGenThreads = Executors.newSingleThreadExecutor(new LodThreadFactory(this.getClass().getSimpleName() + " - cutAndGen")); /** * Creates the dimension centered at (0,0) @@ -73,6 +76,8 @@ public class LodDimension */ public LodDimension(DimensionType newDimension, LodWorld lodWorld, int newWidth) { + lastCutChunk = null; + lastGenChunk = null; dimension = newDimension; width = newWidth; halfWidth = (int) Math.floor(width / 2); @@ -237,7 +242,8 @@ public class LodDimension for (int z = 0; z < regions.length; z++) { region = regions[x][z]; - if(region != null){ + if (region != null) + { count += region.getMinMemoryNeeded(); } } @@ -304,92 +310,124 @@ public class LodDimension /** + * */ public void treeCutter(int playerPosX, int playerPosZ) { - int regionX; - int regionZ; - LevelPos levelPos = new LevelPos(); - - for (int x = 0; x < regions.length; x++) + ChunkPos newPlayerChunk = (new LevelPos((byte) 0, playerPosX, playerPosZ)).getChunkPos(); + if (lastCutChunk == null) + lastCutChunk = new ChunkPos(newPlayerChunk.x + 1, newPlayerChunk.z - 1); + if (newPlayerChunk.x != lastCutChunk.x || newPlayerChunk.z != lastCutChunk.z) { - for (int z = 0; z < regions.length; z++) + lastCutChunk = newPlayerChunk; + Thread thread = new Thread(() -> { - regionX = (x + center.x) - halfWidth; - regionZ = (z + center.z) - halfWidth; - levelPos.changeParameters(LodUtil.REGION_DETAIL_LEVEL, regionX, regionZ); - //we start checking from the first circle. If the whole region is in the circle - //we proceed to cut all the level lower than the level of circle 1 and we break - //if this is not the case w - for(byte index = LodUtil.BLOCK_DETAIL_LEVEL; index <= LodUtil.DETAIL_OPTIONS; index++){ - if(DetailDistanceUtil.getDistanceTreeCut(index + 1) > levelPos.minDistance(playerPosX, playerPosZ)){ + int regionX; + int regionZ; + LevelPos levelPos = new LevelPos(); - byte cutDetailLevel = DetailDistanceUtil.getCutLodDetail(index); - - if(regions[x][z] != null) + for (int x = 0; x < regions.length; x++) + { + for (int z = 0; z < regions.length; z++) + { + regionX = (x + center.x) - halfWidth; + regionZ = (z + center.z) - halfWidth; + levelPos.changeParameters(LodUtil.REGION_DETAIL_LEVEL, regionX, regionZ); + //we start checking from the first circle. If the whole region is in the circle + //we proceed to cut all the level lower than the level of circle 1 and we break + //if this is not the case w + for (byte index = LodUtil.BLOCK_DETAIL_LEVEL; index <= LodUtil.DETAIL_OPTIONS; index++) { - if(regions[x][z].getMinDetailLevel() > cutDetailLevel){ - regions[x][z].cutTree(cutDetailLevel); + if (DetailDistanceUtil.getDistanceTreeCut(index + 1) > levelPos.minDistance(playerPosX, playerPosZ)) + { + + byte cutDetailLevel = DetailDistanceUtil.getCutLodDetail(index); + + if (regions[x][z] != null) + { + if (regions[x][z].getMinDetailLevel() > cutDetailLevel) + { + regions[x][z].cutTree(cutDetailLevel); + } + } + //once we + break; } } - //once we - break; - } - } - } + }// region z + }// region z + + }); + cutAndGenThreads.execute(thread); } } /** + * */ public void treeGenerator(int playerPosX, int playerPosZ) { - int regionX; - int regionZ; - LevelPos levelPos = new LevelPos(); - RegionPos regionPos; - LodRegion region; - byte targetDetailLevel; - for (int x = 0; x < regions.length; x++) + + ChunkPos newPlayerChunk = (new LevelPos((byte) 0, playerPosX, playerPosZ)).getChunkPos(); + + if (lastGenChunk == null) + lastGenChunk = new ChunkPos(newPlayerChunk.x + 1, newPlayerChunk.z - 1); + if (newPlayerChunk.x != lastGenChunk.x || newPlayerChunk.z != lastGenChunk.z) { - for (int z = 0; z < regions.length; z++) + lastGenChunk = newPlayerChunk; + Thread thread = new Thread(() -> { - regionX = (x + center.x) - halfWidth; - regionZ = (z + center.z) - halfWidth; - levelPos.changeParameters(LodUtil.REGION_DETAIL_LEVEL, regionX, regionZ); - regionPos = new RegionPos(regionX, regionZ); - - for(byte index = LodUtil.BLOCK_DETAIL_LEVEL; index <= LodUtil.REGION_DETAIL_LEVEL; index++){ - - //As soon as we find in which circle the region should be we analyze it - if(DetailDistanceUtil.getDistanceTreeGen(index + 1) > levelPos.minDistance(playerPosX, playerPosZ)){ - - region = regions[x][z]; - //We require that the region we are checking is loaded with at least this level - targetDetailLevel = DetailDistanceUtil.getLodDetail(index).detailLevel; - - if (region == null) + int regionX; + int regionZ; + LodRegion region; + LevelPos levelPos = new LevelPos(); + List> genThreads = new ArrayList<>(); + for (int x = 0; x < regions.length; x++) + { + for (int z = 0; z < regions.length; z++) + { + regionX = (x + center.x) - halfWidth; + regionZ = (z + center.z) - halfWidth; + levelPos.changeParameters(LodUtil.REGION_DETAIL_LEVEL, regionX, regionZ); + final RegionPos regionPos = new RegionPos(regionX, regionZ); + for (byte index = LodUtil.BLOCK_DETAIL_LEVEL; index <= LodUtil.REGION_DETAIL_LEVEL; index++) { - //First case, region has to be initialized - //We check if there is a file at the target level - regions[x][z] = getRegionFromFile(regionPos, targetDetailLevel); - - //if there is no file we initialize the region - if (regions[x][z] == null) + //As soon as we find in which circle the region should be we analyze it + if (DetailDistanceUtil.getDistanceTreeGen(index + 1) > levelPos.minDistance(playerPosX, playerPosZ)) { - regions[x][z] = new LodRegion(targetDetailLevel, regionPos); - } - }else if(region.getMinDetailLevel() > targetDetailLevel){ - //Second case, region has been initialized but at a higher level - //We expand the region by introducing the missing layer - region.expand(targetDetailLevel); + region = regions[x][z]; + //We require that the region we are checking is loaded with at least this level + byte targetDetailLevel = DetailDistanceUtil.getLodDetail(index).detailLevel; + + if (region == null) + { + //First case, region has to be initialized + + //We check if there is a file at the target level + regions[x][z] = getRegionFromFile(regionPos, targetDetailLevel); + + //if there is no file we initialize the region + if (regions[x][z] == null) + { + regions[x][z] = new LodRegion(targetDetailLevel, regionPos); + } + + } else if (region.getMinDetailLevel() > targetDetailLevel) + { + //Second case, region has been initialized but at a higher level + //We expand the region by introducing the missing layer + region.expand(targetDetailLevel); + } + break; + } } - break; } } - } + ; + }); + cutAndGenThreads.execute(thread); } } @@ -461,7 +499,8 @@ public class LodDimension region = getRegion(new LevelPos(LodUtil.REGION_DETAIL_LEVEL, regionPos.x, regionPos.z).getConvertedLevelPos(detailLevel)); listOfData.addAll(region.getDataToGenerate(playerPosX, playerPosZ, start, end, generation, detailLevel, dataNumber)); } - }catch (Exception e){ + } catch (Exception e) + { //e.printStackTrace(); } } @@ -470,9 +509,9 @@ public class LodDimension List levelMinPosList = new ArrayList<>(); dataNumber = Math.min(dataNumber, listOfData.size()); - for(int i=0; i> dataToRender, RegionPos regionPos, int playerPosX, int playerPosZ, int start, int end, byte detailLevel, boolean zFix) + public void getDataToRender(ConcurrentNavigableMap> dataToRender, RegionPos regionPos, int playerPosX, int playerPosZ, int start, int end, byte detailLevel, boolean zFix) { LevelPos regionLevelPos = new LevelPos(LodUtil.REGION_DETAIL_LEVEL, regionPos.x, regionPos.z); try @@ -493,9 +532,10 @@ public class LodDimension start <= regionLevelPos.maxDistance(playerPosX, playerPosZ)) { LodRegion region = getRegion(new LevelPos(LodUtil.REGION_DETAIL_LEVEL, regionPos.x, regionPos.z).getConvertedLevelPos(detailLevel)); - region.getDataToRender(dataToRender,playerPosX, playerPosZ, start, end, detailLevel,zFix); + region.getDataToRender(dataToRender, playerPosX, playerPosZ, start, end, detailLevel, zFix); } - }catch (Exception e){ + } catch (Exception e) + { e.printStackTrace(); } } @@ -536,7 +576,8 @@ public class LodDimension return region.getData(levelPos); - }catch (Exception e){ + } catch (Exception e) + { return null; } } @@ -595,7 +636,8 @@ public class LodDimension } return region.doesDataExist(levelPos.clone()); - }catch (Exception e){ + } catch (Exception e) + { return false; } } @@ -704,12 +746,12 @@ public class LodDimension for (int z = 0; z < regions.length; z++) { region = regions[x][z]; - if(region == null) + if (region == null) { stringBuilder.append("n"); stringBuilder.append("\t"); - }else + } else { stringBuilder.append(region.getMinDetailLevel()); stringBuilder.append("\t"); diff --git a/src/main/java/com/seibel/lod/proxy/ClientProxy.java b/src/main/java/com/seibel/lod/proxy/ClientProxy.java index 84d8121af..18c1321bf 100644 --- a/src/main/java/com/seibel/lod/proxy/ClientProxy.java +++ b/src/main/java/com/seibel/lod/proxy/ClientProxy.java @@ -57,258 +57,266 @@ import net.minecraftforge.eventbus.api.SubscribeEvent; */ public class ClientProxy { - public static final Logger LOGGER = LogManager.getLogger("LOD"); - - private static LodWorld lodWorld = new LodWorld(); - private static LodBuilder lodBuilder = new LodBuilder(); - private static LodBufferBuilder lodBufferBuilder = new LodBufferBuilder(); - private static LodRenderer renderer = new LodRenderer(lodBufferBuilder); - private static LodWorldGenerator lodWorldGenerator = LodWorldGenerator.INSTANCE; + public static final Logger LOGGER = LogManager.getLogger("LOD"); - private boolean configOverrideReminderPrinted = false; - - Minecraft mc = Minecraft.getInstance(); + private static LodWorld lodWorld = new LodWorld(); + private static LodBuilder lodBuilder = new LodBuilder(); + private static LodBufferBuilder lodBufferBuilder = new LodBufferBuilder(); + private static LodRenderer renderer = new LodRenderer(lodBufferBuilder); + private static LodWorldGenerator lodWorldGenerator = LodWorldGenerator.INSTANCE; + + 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 previousLodRenderDistance = 0; - - /** can be set if we want to recalculate variables related - * to the LOD view distance */ - private boolean recalculateWidths = false; + /** + * 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 previousLodRenderDistance = 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; - applyConfigOverrides(); - - viewDistanceChangedEvent(); - - LodDimension lodDim = lodWorld.getLodDimension(mc.player.level.dimensionType()); - if (lodDim == null) - return; + /** + * can be set if we want to recalculate variables related + * to the LOD view distance + */ + private boolean recalculateWidths = false; - playerMoveEvent(lodDim); - //System.out.println("memory needed " + lodDim.getMinMemoryNeeded() + " byte"); - //System.out.println(lodDim); + public ClientProxy() + { - lodDim.treeCutter((int) mc.player.getX(),(int) mc.player.getZ()); - lodDim.treeGenerator((int) mc.player.getX(),(int) mc.player.getZ()); - // comment out when creating a release - - - // 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; - previousLodRenderDistance = LodConfig.CLIENT.lodChunkRenderDistance.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); + //==============// + // render event // + //==============// - 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.lodChunkRenderDistance.set(128); - LodConfig.CLIENT.lodDistanceCalculatorType.set(DistanceCalculatorType.LINEAR); - LodConfig.CLIENT.lodQuality.set(2); - LodConfig.CLIENT.allowUnstableFeatureGeneration.set(false); - LodConfig.CLIENT.numberOfWorldGenerationThreads.set(Runtime.getRuntime().availableProcessors()); - - // has to be set in the config file - // LodConfig.CLIENT.numberOfWorldGenerationThreads.set(16); - } - - - //==============// - // forge events // - //==============// - - @SubscribeEvent - public void serverTickEvent(TickEvent.ServerTickEvent event) - { - if (mc == null || mc.player == null || !lodWorld.getIsWorldLoaded()) - return; - - LodDimension lodDim = lodWorld.getLodDimension(mc.player.level.dimensionType()); - if (lodDim == null) - return; - - lodWorldGenerator.queueGenerationRequests(lodDim, renderer, lodBuilder); - } - - @SubscribeEvent - public void chunkLoadEvent(ChunkEvent.Load event) - { - lodBuilder.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(); - - LodWorldGenerator.INSTANCE.numberOfChunksWaitingToGenerate.set(0); - // the player has disconnected from a server - lodWorld.deselectWorld(); - - - // hopefully this should reduce issues related to the buffer builder - // breaking when changing worlds. - renderer.destroyBuffers(); - recalculateWidths = true; - } - } - - - @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 - lodBuilder.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 = LodConfig.CLIENT.lodChunkRenderDistance.get() * 2 + 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 (lodBuilder.defaultDimensionWidthInRegions != newWidth || recalculateWidths) - { - // update the dimensions to fit the new width - lodWorld.resizeDimensionRegionWidth(newWidth); - lodBuilder.defaultDimensionWidthInRegions = newWidth; - renderer.setupBuffers(newWidth); - - recalculateWidths = false; - //LOGGER.info("new dimension width in regions: " + newWidth + "\t potential: " + newWidth ); - } - } - - - //================// - // public getters // - //================// - - public static LodWorld getLodWorld() - { - return lodWorld; - } - - public static LodBuilder getLodBuilder() - { - return lodBuilder; - } - - public static LodRenderer getRenderer() - { - return renderer; - } + /** + * 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); + //System.out.println("memory needed " + lodDim.getMinMemoryNeeded() + " byte"); + //System.out.println(lodDim); + + lodDim.treeCutter((int) mc.player.getX(), (int) mc.player.getZ()); + lodDim.treeGenerator((int) mc.player.getX(), (int) mc.player.getZ()); + + + // 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; + previousLodRenderDistance = LodConfig.CLIENT.lodChunkRenderDistance.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.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.lodChunkRenderDistance.set(128); + LodConfig.CLIENT.lodDistanceCalculatorType.set(DistanceCalculatorType.LINEAR); + LodConfig.CLIENT.lodQuality.set(2); + LodConfig.CLIENT.allowUnstableFeatureGeneration.set(false); + LodConfig.CLIENT.numberOfWorldGenerationThreads.set(Runtime.getRuntime().availableProcessors()); + + // has to be set in the config file + // LodConfig.CLIENT.numberOfWorldGenerationThreads.set(16); + } + + + //==============// + // forge events // + //==============// + + @SubscribeEvent + public void serverTickEvent(TickEvent.ServerTickEvent event) + { + if (mc == null || mc.player == null || !lodWorld.getIsWorldLoaded()) + return; + + LodDimension lodDim = lodWorld.getLodDimension(mc.player.level.dimensionType()); + if (lodDim == null) + return; + + lodWorldGenerator.queueGenerationRequests(lodDim, renderer, lodBuilder); + } + + @SubscribeEvent + public void chunkLoadEvent(ChunkEvent.Load event) + { + lodBuilder.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(); + + LodWorldGenerator.INSTANCE.numberOfChunksWaitingToGenerate.set(0); + // the player has disconnected from a server + lodWorld.deselectWorld(); + + + // hopefully this should reduce issues related to the buffer builder + // breaking when changing worlds. + renderer.destroyBuffers(); + recalculateWidths = true; + } + } + + + @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 + lodBuilder.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 = LodConfig.CLIENT.lodChunkRenderDistance.get() * 2 + 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 (lodBuilder.defaultDimensionWidthInRegions != newWidth || recalculateWidths) + { + // update the dimensions to fit the new width + lodWorld.resizeDimensionRegionWidth(newWidth); + lodBuilder.defaultDimensionWidthInRegions = newWidth; + renderer.setupBuffers(newWidth); + + recalculateWidths = false; + //LOGGER.info("new dimension width in regions: " + newWidth + "\t potential: " + newWidth ); + } + } + + + //================// + // public getters // + //================// + + public static LodWorld getLodWorld() + { + return lodWorld; + } + + public static LodBuilder getLodBuilder() + { + return lodBuilder; + } + + public static LodRenderer getRenderer() + { + return renderer; + } }