Merge branch 'batchGenRefactor'
This commit is contained in:
+1
-1
@@ -10,7 +10,7 @@ insert_final_newline = false
|
||||
max_line_length = 1000
|
||||
tab_width = 4
|
||||
trim_trailing_whitespace = false
|
||||
ij_continuation_indent_size = 8
|
||||
ij_continuation_indent_size = 4
|
||||
ij_formatter_off_tag = @formatter:off
|
||||
ij_formatter_on_tag = @formatter:on
|
||||
ij_formatter_tags_enabled = true
|
||||
|
||||
+11
-1
@@ -63,7 +63,17 @@ public enum EDhApiDataCompressionMode
|
||||
* Write Speed: 15.13 MS / DTO <br>
|
||||
* Compression ratio: 0.2606 <br>
|
||||
*/
|
||||
Z_STD(2),
|
||||
Z_STD(4),
|
||||
|
||||
/**
|
||||
* Similar to {@link EDhApiDataCompressionMode#Z_STD}
|
||||
* except slower.
|
||||
* <br>
|
||||
* This option is only provided for legacy support when processing old databases.
|
||||
*/
|
||||
@Deprecated
|
||||
@DisallowSelectingViaConfigGui
|
||||
Z_STD_STREAM(2),
|
||||
|
||||
|
||||
/**
|
||||
|
||||
+18
@@ -98,4 +98,22 @@ public class DhApiTerrainDataPoint
|
||||
|
||||
|
||||
|
||||
//================//
|
||||
// base overrides //
|
||||
//================//
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return "[Block:" + this.blockStateWrapper.getSerialString() +
|
||||
",Biome:" + this.biomeWrapper.getName() +
|
||||
",TopY:" + this.topYBlockPos +
|
||||
",BottomY:" + this.bottomYBlockPos +
|
||||
",BlockLight:" + this.blockLightLevel +
|
||||
",SkyLight:" + this.skyLightLevel +
|
||||
"]";
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -565,8 +565,13 @@ public class ClientApi
|
||||
{
|
||||
// only fade when DH is rendering
|
||||
if (Config.Client.Advanced.Debugging.rendererMode.get() == EDhApiRendererMode.DEFAULT
|
||||
// only fade when requested
|
||||
&& Config.Client.Advanced.Graphics.Quality.vanillaFadeMode.get() == EDhApiMcRenderingFadeMode.DOUBLE_PASS
|
||||
&&
|
||||
(
|
||||
// only fade when requested
|
||||
Config.Client.Advanced.Graphics.Quality.vanillaFadeMode.get() == EDhApiMcRenderingFadeMode.DOUBLE_PASS
|
||||
// or if LOD-only mode is enabled (fading is used to remove the MC render pass)
|
||||
|| Config.Client.Advanced.Debugging.lodOnlyMode.get()
|
||||
)
|
||||
// don't fade when Iris shaders are active, otherwise the rendering can get weird
|
||||
&& !DhApiRenderProxy.INSTANCE.getDeferTransparentRendering())
|
||||
{
|
||||
|
||||
+21
-1
@@ -8,6 +8,10 @@ import com.seibel.distanthorizons.core.pos.DhChunkPos;
|
||||
import com.seibel.distanthorizons.core.world.EWorldEnvironment;
|
||||
import com.seibel.distanthorizons.core.logging.DhLogger;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class ChunkUpdateQueueManager
|
||||
{
|
||||
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
|
||||
@@ -15,6 +19,7 @@ public class ChunkUpdateQueueManager
|
||||
|
||||
public final ChunkPosQueue updateQueue;
|
||||
public final ChunkPosQueue preUpdateQueue;
|
||||
public final Set<DhChunkPos> ignoredChunkPosSet = Collections.newSetFromMap(new ConcurrentHashMap<>());
|
||||
|
||||
public int maxSize = 500;
|
||||
|
||||
@@ -38,12 +43,18 @@ public class ChunkUpdateQueueManager
|
||||
// list/set methods //
|
||||
//==================//
|
||||
|
||||
public boolean contains(DhChunkPos pos) { return this.updateQueue.contains(pos) || this.preUpdateQueue.contains(pos); }
|
||||
public boolean contains(DhChunkPos pos)
|
||||
{
|
||||
return this.updateQueue.contains(pos)
|
||||
|| this.ignoredChunkPosSet.contains(pos)
|
||||
|| this.preUpdateQueue.contains(pos);
|
||||
}
|
||||
|
||||
public void clear()
|
||||
{
|
||||
this.updateQueue.clear();
|
||||
this.preUpdateQueue.clear();
|
||||
this.ignoredChunkPosSet.clear();
|
||||
}
|
||||
public int getQueuedCount() { return this.updateQueue.getQueuedCount() + this.preUpdateQueue.getQueuedCount(); }
|
||||
public boolean isEmpty()
|
||||
@@ -131,6 +142,15 @@ public class ChunkUpdateQueueManager
|
||||
|
||||
|
||||
|
||||
//=========//
|
||||
// ignores //
|
||||
//=========//
|
||||
|
||||
public void addPosToIgnore(DhChunkPos chunkPos) { this.ignoredChunkPosSet.add(chunkPos); }
|
||||
public void removePosToIgnore(DhChunkPos chunkPos) { this.ignoredChunkPosSet.remove(chunkPos); }
|
||||
|
||||
|
||||
|
||||
//==================//
|
||||
// position methods //
|
||||
//==================//
|
||||
|
||||
@@ -103,10 +103,7 @@ public class Config
|
||||
|
||||
public static ConfigUiLinkedEntry quickEnableWorldGenerator = new ConfigUiLinkedEntry(Common.WorldGenerator.enableDistantGeneration);
|
||||
|
||||
public static ConfigEntry<Boolean> quickShowWorldGenProgress = new ConfigEntry.Builder<Boolean>()
|
||||
.set(false) // TODO should be set by the underlying world gen progress button, not a static default
|
||||
.setAppearance(EConfigEntryAppearance.ONLY_IN_GUI)
|
||||
.build();
|
||||
public static ConfigUiLinkedEntry quickShowWorldGenProgress = new ConfigUiLinkedEntry(Common.WorldGenerator.showGenerationProgress);
|
||||
|
||||
public static ConfigUiLinkedEntry quickLodCloudRendering = new ConfigUiLinkedEntry(Advanced.Graphics.GenericRendering.enableCloudRendering);
|
||||
|
||||
@@ -1391,36 +1388,8 @@ public class Config
|
||||
|
||||
public static ConfigEntry<EDhApiDataCompressionMode> dataCompression = new ConfigEntry.Builder<EDhApiDataCompressionMode>()
|
||||
.set(EDhApiDataCompressionMode.Z_STD)
|
||||
.comment(""
|
||||
+ "What algorithm should be used to compress new LOD data? \n"
|
||||
+ "This setting will only affect new or updated LOD data, \n"
|
||||
+ "any data already generated when this setting is changed will be\n"
|
||||
+ "unaffected until it needs to be re-written to the database.\n"
|
||||
+ "\n"
|
||||
+ EDhApiDataCompressionMode.UNCOMPRESSED + " \n"
|
||||
+ "Should only be used for testing, is worse in every way vs ["+EDhApiDataCompressionMode.LZ4+"].\n"
|
||||
+ "Expected Compression Ratio: 1.0\n"
|
||||
+ "Estimated average DTO read speed: 6.09 milliseconds\n"
|
||||
+ "Estimated average DTO write speed: 6.01 milliseconds\n"
|
||||
+ "\n"
|
||||
+ EDhApiDataCompressionMode.LZ4 + " \n"
|
||||
+ "A good option if you're CPU limited and have plenty of hard drive space.\n"
|
||||
+ "Expected Compression Ratio: 0.4513\n"
|
||||
+ "Estimated average DTO read speed: 3.25 ms\n"
|
||||
+ "Estimated average DTO write speed: 5.99 ms\n"
|
||||
+ "\n"
|
||||
+ EDhApiDataCompressionMode.Z_STD + " \n"
|
||||
+ "A good option if you're CPU limited and have plenty of hard drive space.\n"
|
||||
+ "Expected Compression Ratio: 0.2606\n"
|
||||
+ "Estimated average DTO read speed: 9.31 ms\n"
|
||||
+ "Estimated average DTO write speed: 15.13 ms\n"
|
||||
+ "\n"
|
||||
+ EDhApiDataCompressionMode.LZMA2 + " \n"
|
||||
+ "Slow but very good compression.\n"
|
||||
+ "Expected Compression Ratio: 0.2\n"
|
||||
+ "Estimated average DTO read speed: 13.29 ms\n"
|
||||
+ "Estimated average DTO write speed: 70.95 ms\n"
|
||||
+ "")
|
||||
// only visible via the API since there is no reason to use any compressor except ZStandard as of 2025-11-24
|
||||
.setAppearance(EConfigEntryAppearance.ONLY_IN_API)
|
||||
.build();
|
||||
|
||||
public static ConfigEntry<EDhApiWorldCompressionMode> worldCompression = new ConfigEntry.Builder<EDhApiWorldCompressionMode>()
|
||||
@@ -1443,49 +1412,6 @@ public class Config
|
||||
+ "")
|
||||
.build();
|
||||
|
||||
public static ConfigEntry<Boolean> recalculateChunkHeightmaps = new ConfigEntry.Builder<Boolean>()
|
||||
.set(false)
|
||||
.comment(""
|
||||
+ "True: Recalculate chunk height maps before chunks can be used by DH.\n"
|
||||
+ " This can fix problems with worlds created by World Painter or \n"
|
||||
+ " other external tools where the heightmap format may be incorrect. \n"
|
||||
+ "False: Assume any height maps handled by Minecraft are correct. \n"
|
||||
+ "\n"
|
||||
+ "Fastest: False\n"
|
||||
+ "Most Compatible: True\n"
|
||||
+ "")
|
||||
.build();
|
||||
|
||||
public static ConfigEntry<Boolean> pullLightingForPregeneratedChunks = new ConfigEntry.Builder<Boolean>()
|
||||
.set(false)
|
||||
.comment(""
|
||||
+ "If true LOD generation for pre-existing chunks will attempt to pull the lighting data \n"
|
||||
+ "saved in Minecraft's Region files. \n"
|
||||
+ "If false DH will pull in chunks without lighting and re-light them. \n"
|
||||
+ " \n"
|
||||
+ "Setting this to true will result in faster LOD generation \n"
|
||||
+ "for already generated worlds, but is broken by most lighting mods. \n"
|
||||
+ " \n"
|
||||
+ "Set this to false if LODs are black. \n"
|
||||
+ "")
|
||||
.build();
|
||||
|
||||
public static ConfigEntry<Boolean> assumePreExistingChunksAreFinished = new ConfigEntry.Builder<Boolean>()
|
||||
.set(false)
|
||||
.comment(""
|
||||
+ "When DH pulls in pre-existing chunks it will attempt to \n"
|
||||
+ "run any missing world generation steps; for example: \n"
|
||||
+ "if a chunk has the status SURFACE, DH will skip BIOMES \n"
|
||||
+ "and SURFACE, but will run FEATURES. \n"
|
||||
+ " \n"
|
||||
+ "However if for some reason the chunks are malformed \n"
|
||||
+ "or there's some other issue that causes the status \n"
|
||||
+ "to be incorrect that can either cause world gen \n"
|
||||
+ "lock-ups and/or crashes. \n"
|
||||
+ "If either of those happen try setting this to True. \n"
|
||||
+ "")
|
||||
.build();
|
||||
|
||||
public static ConfigCategory experimental = new ConfigCategory.Builder().set(Experimental.class).build();
|
||||
|
||||
|
||||
@@ -1527,6 +1453,7 @@ public class Config
|
||||
+ "How many threads should be used by Distant Horizons? \n"
|
||||
+ "")
|
||||
.build();
|
||||
|
||||
public static final ConfigEntry<Double> threadRunTimeRatio = new ConfigEntry.Builder<Double>()
|
||||
.setChatCommandName("threading.threadRunTimeRatio")
|
||||
.setMinDefaultMax(0.01, ThreadPresetConfigEventHandler.getDefaultRunTimeRatio(), 1.0)
|
||||
@@ -1540,6 +1467,19 @@ public class Config
|
||||
"")
|
||||
.build();
|
||||
|
||||
public static final ConfigEntry<Integer> threadPriority = new ConfigEntry.Builder<Integer>()
|
||||
.setAppearance(EConfigEntryAppearance.ONLY_IN_FILE) // only in file since this requires a MC reboot to change
|
||||
.setMinDefaultMax(Thread.MIN_PRIORITY, // 1
|
||||
Thread.NORM_PRIORITY, // 5 (1 higher than C2ME's default priority of 4 which can help reduce issues with Chunky)
|
||||
Thread.MAX_PRIORITY) // 10
|
||||
.comment(""
|
||||
+ "What Java thread priority should DH's primary thread pools run with? \n"
|
||||
+ "\n"
|
||||
+ "You probably don't need to change this unless you are also \n"
|
||||
+ "running C2ME and are seeing thread starvation in either C2ME or DH. \n"
|
||||
+ "")
|
||||
.build();
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -1576,14 +1516,6 @@ public class Config
|
||||
+ "This can be useful for debugging.")
|
||||
.build();
|
||||
|
||||
public static ConfigEntry<EDhApiLoggerLevel> logWorldGenPerformanceToFile = new ConfigEntry.Builder<EDhApiLoggerLevel>()
|
||||
.setChatCommandName("logging.logWorldGenPerformance")
|
||||
.set(EDhApiLoggerLevel.INFO)
|
||||
.comment(""
|
||||
+ "If enabled, the mod will log performance about the world generation process. \n"
|
||||
+ "This can be useful for debugging.")
|
||||
.build();
|
||||
|
||||
public static ConfigEntry<EDhApiLoggerLevel> logWorldGenChunkLoadEventToFile = new ConfigEntry.Builder<EDhApiLoggerLevel>()
|
||||
.setChatCommandName("logging.logWorldGenLoadEvent")
|
||||
.set(EDhApiLoggerLevel.INFO)
|
||||
@@ -1669,6 +1601,14 @@ public class Config
|
||||
+ "")
|
||||
.build();
|
||||
|
||||
public static ConfigEntry<Boolean> showSlowWorldGenSettingWarnings = new ConfigEntry.Builder<Boolean>()
|
||||
.set(true)
|
||||
.comment(""
|
||||
+ "If enabled, a chat message will be displayed when DH has too many chunks \n"
|
||||
+ "queued for updating. \n"
|
||||
+ "")
|
||||
.build();
|
||||
|
||||
public static ConfigEntry<Boolean> showModCompatibilityWarningsOnStartup = new ConfigEntry.Builder<Boolean>()
|
||||
.set(true)
|
||||
.comment(""
|
||||
@@ -1892,7 +1832,6 @@ public class Config
|
||||
ThreadPresetConfigEventHandler.INSTANCE.setUiOnlyConfigValues();
|
||||
RenderQualityPresetConfigEventHandler.INSTANCE.setUiOnlyConfigValues();
|
||||
QuickRenderToggleConfigEventHandler.INSTANCE.setUiOnlyConfigValues();
|
||||
QuickShowWorldGenProgressConfigEventHandler.INSTANCE.setUiOnlyConfigValues();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
||||
-66
@@ -1,66 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizons mod
|
||||
* licensed under the GNU LGPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.seibel.distanthorizons.core.config.eventHandlers.presets;
|
||||
|
||||
import com.seibel.distanthorizons.api.enums.rendering.EDhApiRendererMode;
|
||||
import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiDistantGeneratorProgressDisplayLocation;
|
||||
import com.seibel.distanthorizons.core.config.Config;
|
||||
import com.seibel.distanthorizons.core.config.listeners.ConfigChangeListener;
|
||||
|
||||
public class QuickShowWorldGenProgressConfigEventHandler
|
||||
{
|
||||
public static QuickShowWorldGenProgressConfigEventHandler INSTANCE = new QuickShowWorldGenProgressConfigEventHandler();
|
||||
|
||||
private final ConfigChangeListener<Boolean> quickChangeListener;
|
||||
private final ConfigChangeListener<EDhApiDistantGeneratorProgressDisplayLocation> fullChangeListener;
|
||||
|
||||
|
||||
|
||||
/** private since we only ever need one handler at a time */
|
||||
private QuickShowWorldGenProgressConfigEventHandler()
|
||||
{
|
||||
this.quickChangeListener = new ConfigChangeListener<>(Config.Client.quickShowWorldGenProgress,
|
||||
(val) ->
|
||||
{
|
||||
boolean quickShowProgress = Config.Client.quickShowWorldGenProgress.get();
|
||||
Config.Common.WorldGenerator.showGenerationProgress.set(
|
||||
quickShowProgress
|
||||
? EDhApiDistantGeneratorProgressDisplayLocation.OVERLAY
|
||||
: EDhApiDistantGeneratorProgressDisplayLocation.DISABLED);
|
||||
});
|
||||
this.fullChangeListener = new ConfigChangeListener<>(Config.Common.WorldGenerator.showGenerationProgress,
|
||||
(val) ->
|
||||
{
|
||||
boolean showProgress = Config.Common.WorldGenerator.showGenerationProgress.get() != EDhApiDistantGeneratorProgressDisplayLocation.DISABLED;
|
||||
Config.Client.quickShowWorldGenProgress.setWithoutFiringEvents(showProgress);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the UI only config based on what is set in the file. <br>
|
||||
* This should only be called once.
|
||||
*/
|
||||
public void setUiOnlyConfigValues()
|
||||
{
|
||||
boolean showProgress = Config.Common.WorldGenerator.showGenerationProgress.get() != EDhApiDistantGeneratorProgressDisplayLocation.DISABLED;
|
||||
Config.Client.quickShowWorldGenProgress.set(showProgress);
|
||||
}
|
||||
|
||||
}
|
||||
+1
-1
@@ -268,7 +268,7 @@ public class FullDataPointIdMap
|
||||
// necessary to prevent issues with deserializing objects after the level has been closed
|
||||
if (Thread.interrupted())
|
||||
{
|
||||
throw new InterruptedException(FullDataPointIdMap.class.getSimpleName() + " task interrupted.");
|
||||
throw new InterruptedException("[" + FullDataPointIdMap.class.getSimpleName() + "] deserializing interrupted.");
|
||||
}
|
||||
|
||||
|
||||
|
||||
+4
-3
@@ -298,7 +298,7 @@ public class FullDataSourceV2
|
||||
* returns {@link FullDataPointUtil#EMPTY_DATA_POINT} if the given {@link DhBlockPos}
|
||||
* is outside this data source's boundaries.
|
||||
*/
|
||||
public long getDataPointAtBlockPos(int blockPosX, int relBlockPosY, int blockPosZ)
|
||||
public long getDataPointAtBlockPos(int blockPosX, int blockPosY, int blockPosZ, int levelMinY)
|
||||
{
|
||||
DhLodPos requestedPos = new DhLodPos(LodUtil.BLOCK_DETAIL_LEVEL, blockPosX, blockPosZ);
|
||||
|
||||
@@ -330,6 +330,7 @@ public class FullDataSourceV2
|
||||
|
||||
|
||||
// search for a datapoint that contains the given block y position
|
||||
int relBlockPosY = blockPosY - levelMinY;
|
||||
long dataPoint;
|
||||
for (int i = 0; i < dataColumn.size(); i++)
|
||||
{
|
||||
@@ -1306,13 +1307,13 @@ public class FullDataSourceV2
|
||||
{
|
||||
try
|
||||
{
|
||||
LodDataBuilder.correctDataColumnOrder(columnDataPoints);
|
||||
LodDataBuilder.putListInTopDownOrder(columnDataPoints);
|
||||
if (this.runApiChunkValidation)
|
||||
{
|
||||
LodDataBuilder.validateOrThrowApiDataColumn(columnDataPoints);
|
||||
}
|
||||
|
||||
LongArrayList packedDataPoints = LodDataBuilder.convertApiDataPointListToPackedLongArray(columnDataPoints, this, 0);
|
||||
LongArrayList packedDataPoints = LodDataBuilder.convertApiDataPointListToPackedLongArray(columnDataPoints, this, 0, true);
|
||||
|
||||
// TODO there should be an "unknown" compression and generation step, or be defined via the datapoints
|
||||
this.setSingleColumn(packedDataPoints, relX, relZ, EDhApiWorldGenerationStep.SURFACE, EDhApiWorldCompressionMode.MERGE_SAME_BLOCKS);
|
||||
|
||||
+2
-2
@@ -157,8 +157,8 @@ public final class BufferQuad
|
||||
return false;
|
||||
|
||||
// make sure these quads share the same perpendicular axis
|
||||
if ((mergeDirection == BufferMergeDirectionEnum.EastWest && this.y != quad.y) ||
|
||||
(mergeDirection == BufferMergeDirectionEnum.NorthSouthOrUpDown && this.x != quad.x))
|
||||
if ((mergeDirection == BufferMergeDirectionEnum.EastWest && this.y != quad.y)
|
||||
|| (mergeDirection == BufferMergeDirectionEnum.NorthSouthOrUpDown && this.x != quad.x))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
+20
-21
@@ -24,7 +24,6 @@ import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
|
||||
import com.seibel.distanthorizons.core.enums.EDhDirection;
|
||||
import com.seibel.distanthorizons.core.level.IDhClientLevel;
|
||||
import com.seibel.distanthorizons.core.pooling.PhantomArrayListCheckout;
|
||||
import com.seibel.distanthorizons.core.pooling.PhantomArrayListPool;
|
||||
import com.seibel.distanthorizons.core.util.ColorUtil;
|
||||
import com.seibel.distanthorizons.core.util.LodUtil;
|
||||
import com.seibel.distanthorizons.core.util.RenderDataPointUtil;
|
||||
@@ -46,7 +45,6 @@ public class ColumnBox
|
||||
|
||||
|
||||
|
||||
|
||||
//=========//
|
||||
// builder //
|
||||
//=========//
|
||||
@@ -249,10 +247,10 @@ public class ColumnBox
|
||||
}
|
||||
|
||||
private static void makeAdjVerticalQuad(
|
||||
LodQuadBuilder builder, PhantomArrayListCheckout phantomArrayCheckout,
|
||||
@NotNull ColumnArrayView adjColumnView, boolean adjacentIsSameDetailLevel, int caveCullingMaxY, EDhDirection direction,
|
||||
short x, short yMin, short z, short horizontalWidth, short ySize,
|
||||
int color, byte irisBlockMaterialId, byte blockLight)
|
||||
LodQuadBuilder builder, PhantomArrayListCheckout phantomArrayCheckout,
|
||||
@NotNull ColumnArrayView adjColumnView, boolean adjacentIsSameDetailLevel, int caveCullingMaxY, EDhDirection direction,
|
||||
short x, short yMin, short z, short horizontalWidth, short ySize,
|
||||
int color, byte irisBlockMaterialId, byte blockLight)
|
||||
{
|
||||
// pooled arrays
|
||||
LongArrayList segments = phantomArrayCheckout.getLongArray(0, 0);
|
||||
@@ -311,9 +309,10 @@ public class ColumnBox
|
||||
long adjBelowPoint = (adjIndex + 1 < adjCount) ? adjColumnView.get(adjIndex + 1) : RenderDataPointUtil.EMPTY_DATA;
|
||||
|
||||
boolean adjOverVoid = !RenderDataPointUtil.doesDataPointExist(adjBelowPoint);
|
||||
boolean adjTransparent = !adjOverVoid
|
||||
&& RenderDataPointUtil.getAlpha(adjPoint) < 255
|
||||
&& transparencyEnabled;
|
||||
boolean adjTransparent =
|
||||
!adjOverVoid
|
||||
&& RenderDataPointUtil.getAlpha(adjPoint) < 255
|
||||
&& transparencyEnabled;
|
||||
|
||||
byte adjSkyLight = RenderDataPointUtil.getLightSky(adjPoint);
|
||||
byte lightToApply;
|
||||
@@ -323,14 +322,14 @@ public class ColumnBox
|
||||
// Adjacent is opaque
|
||||
boolean adjacentCoversThis =
|
||||
!adjacentIsSameDetailLevel
|
||||
&& RenderDataPointUtil.getYMax(adjPoint) >= caveCullingMaxY
|
||||
&&
|
||||
(
|
||||
(x == 0 && direction == EDhDirection.WEST)
|
||||
|| (z == 0 && direction == EDhDirection.NORTH)
|
||||
|| (x == 256 && direction == EDhDirection.EAST)
|
||||
|| (z == 256 && direction == EDhDirection.SOUTH)
|
||||
);
|
||||
&& RenderDataPointUtil.getYMax(adjPoint) >= caveCullingMaxY
|
||||
&&
|
||||
(
|
||||
(x == 0 && direction == EDhDirection.WEST)
|
||||
|| (z == 0 && direction == EDhDirection.NORTH)
|
||||
|| (x == 256 && direction == EDhDirection.EAST)
|
||||
|| (z == 256 && direction == EDhDirection.SOUTH)
|
||||
);
|
||||
|
||||
lightToApply = adjacentCoversThis ? adjSkyLight : SKYLIGHT_COVERED;
|
||||
}
|
||||
@@ -363,10 +362,10 @@ public class ColumnBox
|
||||
{
|
||||
long segment = segments.getLong(i);
|
||||
tryAddVerticalFaceWithSkyLightToBuilder(
|
||||
builder, direction,
|
||||
x, z, horizontalWidth,
|
||||
color, irisBlockMaterialId, blockLight,
|
||||
YSegmentUtil.getSkyLight(segment), inputTransparent, YSegmentUtil.getEndY(segment), YSegmentUtil.getStartY(segment)
|
||||
builder, direction,
|
||||
x, z, horizontalWidth,
|
||||
color, irisBlockMaterialId, blockLight,
|
||||
YSegmentUtil.getSkyLight(segment), inputTransparent, YSegmentUtil.getEndY(segment), YSegmentUtil.getStartY(segment)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -112,7 +112,8 @@ public class LodBufferContainer implements AutoCloseable
|
||||
try
|
||||
{
|
||||
// skip this event if requested
|
||||
if (Thread.interrupted() || this.uploadFuture.isCancelled())
|
||||
if (Thread.interrupted()
|
||||
|| this.uploadFuture.isCancelled())
|
||||
{
|
||||
throw new InterruptedException();
|
||||
}
|
||||
|
||||
+11
-1
@@ -148,8 +148,18 @@ public class LodQuadBuilder
|
||||
throw new IllegalArgumentException("addQuadAdj() is only for adj direction! Not UP or Down!");
|
||||
}
|
||||
|
||||
|
||||
ArrayList<BufferQuad> quadList;
|
||||
if (this.doTransparency && ColorUtil.getAlpha(color) < 255)
|
||||
{
|
||||
quadList = this.transparentQuads[dir.ordinal()];
|
||||
}
|
||||
else
|
||||
{
|
||||
quadList = this.opaqueQuads[dir.ordinal()];
|
||||
}
|
||||
|
||||
BufferQuad quad = new BufferQuad(x, y, z, widthEastWest, widthNorthSouthOrUpDown, color, irisBlockMaterialId, skyLight, blockLight, dir);
|
||||
ArrayList<BufferQuad> quadList = (this.doTransparency && ColorUtil.getAlpha(color) < 255) ? this.transparentQuads[dir.ordinal()] : this.opaqueQuads[dir.ordinal()];
|
||||
if (!quadList.isEmpty()
|
||||
&& (
|
||||
quadList.get(quadList.size() - 1).tryMerge(quad, BufferMergeDirectionEnum.EastWest)
|
||||
|
||||
+35
-11
@@ -31,11 +31,9 @@ import com.seibel.distanthorizons.core.pooling.PhantomArrayListCheckout;
|
||||
import com.seibel.distanthorizons.core.pooling.PhantomArrayListPool;
|
||||
import com.seibel.distanthorizons.core.pos.DhSectionPos;
|
||||
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPosMutable;
|
||||
import com.seibel.distanthorizons.core.render.LodQuadTree;
|
||||
import com.seibel.distanthorizons.core.util.*;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.IWrapperFactory;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
|
||||
import com.seibel.distanthorizons.coreapi.util.BitShiftUtil;
|
||||
@@ -54,11 +52,11 @@ public class FullDataToRenderDataTransformer
|
||||
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
|
||||
|
||||
private static final IWrapperFactory WRAPPER_FACTORY = SingletonInjector.INSTANCE.get(IWrapperFactory.class);
|
||||
private static final IMinecraftClientWrapper MC = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
|
||||
|
||||
private static final LongOpenHashSet brokenPos = new LongOpenHashSet();
|
||||
private static final LongOpenHashSet BROKEN_POS_SET = new LongOpenHashSet();
|
||||
private static final PhantomArrayListPool ARRAY_LIST_POOL = new PhantomArrayListPool("Data Transformer");
|
||||
|
||||
public static final PhantomArrayListPool ARRAY_LIST_POOL = new PhantomArrayListPool("Data Transformer");
|
||||
private static HashSet<IBlockStateWrapper> snowLayerBlockStates = null;
|
||||
|
||||
|
||||
|
||||
@@ -198,6 +196,16 @@ public class FullDataToRenderDataTransformer
|
||||
HashSet<IBlockStateWrapper> blockStatesToIgnore = WRAPPER_FACTORY.getRendererIgnoredBlocks(levelWrapper);
|
||||
HashSet<IBlockStateWrapper> caveBlockStatesToIgnore = WRAPPER_FACTORY.getRendererIgnoredCaveBlocks(levelWrapper);
|
||||
|
||||
// build snow block cache if needed
|
||||
if (snowLayerBlockStates == null)
|
||||
{
|
||||
snowLayerBlockStates = new HashSet<>();
|
||||
// ignore snow layers 1-3, everything above should be considered a full block
|
||||
snowLayerBlockStates.add(WRAPPER_FACTORY.deserializeBlockStateWrapperOrGetDefault("minecraft:snow_STATE_{layers:1}", levelWrapper));
|
||||
snowLayerBlockStates.add(WRAPPER_FACTORY.deserializeBlockStateWrapperOrGetDefault("minecraft:snow_STATE_{layers:2}", levelWrapper));
|
||||
snowLayerBlockStates.add(WRAPPER_FACTORY.deserializeBlockStateWrapperOrGetDefault("minecraft:snow_STATE_{layers:3}", levelWrapper));
|
||||
}
|
||||
|
||||
int caveCullingMaxY = Config.Client.Advanced.Graphics.Culling.caveCullingHeight.get() - levelWrapper.getMinHeight();
|
||||
boolean caveCullingEnabled =
|
||||
Config.Client.Advanced.Graphics.Culling.enableCaveCulling.get()
|
||||
@@ -252,9 +260,9 @@ public class FullDataToRenderDataTransformer
|
||||
}
|
||||
catch (IndexOutOfBoundsException e)
|
||||
{
|
||||
if (!brokenPos.contains(fullDataMapping.getPos()))
|
||||
if (!BROKEN_POS_SET.contains(fullDataMapping.getPos()))
|
||||
{
|
||||
brokenPos.add(fullDataMapping.getPos());
|
||||
BROKEN_POS_SET.add(fullDataMapping.getPos());
|
||||
String levelId = levelWrapper.getDhIdentifier();
|
||||
LOGGER.warn("Unable to get data point with id ["+id+"] " +
|
||||
"(Max possible ID: ["+fullDataMapping.getMaxValidId()+"]) " +
|
||||
@@ -324,10 +332,26 @@ public class FullDataToRenderDataTransformer
|
||||
// non-solid block check //
|
||||
//=======================//
|
||||
|
||||
if (ignoreNonCollidingBlocks
|
||||
&& !block.isSolid()
|
||||
&& !block.isLiquid()
|
||||
&& block.getOpacity() != LodUtil.BLOCK_FULLY_OPAQUE)
|
||||
boolean ignoreNonSolidBlock =
|
||||
ignoreNonCollidingBlocks
|
||||
&& !block.isSolid()
|
||||
&& !block.isLiquid()
|
||||
&& block.getOpacity() != LodUtil.BLOCK_FULLY_OPAQUE;
|
||||
|
||||
// merge snow into the block below it
|
||||
if (snowLayerBlockStates.contains(block))
|
||||
{
|
||||
// sometimes a snow datapoint will be multiple blocks tall,
|
||||
// in that case we just want to drop the top by 1
|
||||
blockHeight -= 1;
|
||||
if (blockHeight == 0)
|
||||
{
|
||||
// this snow block was entirely removed, just color the block below it
|
||||
ignoreNonSolidBlock = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (ignoreNonSolidBlock)
|
||||
{
|
||||
if (colorBelowWithAvoidedBlocks)
|
||||
{
|
||||
|
||||
+109
-30
@@ -74,7 +74,7 @@ public class LodDataBuilder
|
||||
long pos = DhSectionPos.encode(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, sectionPosX, sectionPosZ);
|
||||
|
||||
FullDataSourceV2 dataSource = FullDataSourceV2.createEmpty(pos);
|
||||
dataSource.isEmpty = false;
|
||||
dataSource.isEmpty = false; // this will be set to "true" if any blocks are found
|
||||
// chunk updates always propagate up
|
||||
dataSource.applyToParent = true;
|
||||
|
||||
@@ -244,6 +244,15 @@ public class LodDataBuilder
|
||||
blockLight = newBlockLight;
|
||||
skyLight = newSkyLight;
|
||||
lastY = y;
|
||||
|
||||
|
||||
// mark the data source as non-empty if we find any non-air blocks
|
||||
if (dataSource.isEmpty
|
||||
&& newBlockState != null
|
||||
&& !newBlockState.isAir())
|
||||
{
|
||||
dataSource.isEmpty = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -261,7 +270,6 @@ public class LodDataBuilder
|
||||
return null;
|
||||
}
|
||||
|
||||
LodUtil.assertTrue(!dataSource.isEmpty);
|
||||
return dataSource;
|
||||
}
|
||||
|
||||
@@ -289,13 +297,29 @@ public class LodDataBuilder
|
||||
for (int relBlockX = 0; relBlockX < LodUtil.CHUNK_WIDTH; relBlockX++)
|
||||
{
|
||||
List<DhApiTerrainDataPoint> columnDataPoints = apiChunk.getDataPoints(relBlockX, relBlockZ);
|
||||
LodDataBuilder.correctDataColumnOrder(columnDataPoints);
|
||||
|
||||
// mark the data source as non-empty if we find any non-air blocks
|
||||
if (dataSource.isEmpty)
|
||||
{
|
||||
for (int i = 0; i < columnDataPoints.size(); i++)
|
||||
{
|
||||
DhApiTerrainDataPoint dataPoint = columnDataPoints.get(i);
|
||||
if (dataPoint.blockStateWrapper != null
|
||||
&& !dataPoint.blockStateWrapper.isAir())
|
||||
{
|
||||
dataSource.isEmpty = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LodDataBuilder.putListInTopDownOrder(columnDataPoints);
|
||||
if (runAdditionalValidation)
|
||||
{
|
||||
validateOrThrowApiDataColumn(columnDataPoints);
|
||||
}
|
||||
|
||||
LongArrayList packedDataPoints = convertApiDataPointListToPackedLongArray(columnDataPoints, dataSource, apiChunk.bottomYBlockPos);
|
||||
LongArrayList packedDataPoints = convertApiDataPointListToPackedLongArray(columnDataPoints, dataSource, apiChunk.bottomYBlockPos, runAdditionalValidation);
|
||||
|
||||
// TODO add the ability for API users to define a different compression mode
|
||||
// or add a "unkown" compression mode
|
||||
@@ -303,7 +327,6 @@ public class LodDataBuilder
|
||||
packedDataPoints,
|
||||
relBlockX + relSourceBlockX, relBlockZ + relSourceBlockZ,
|
||||
EDhApiWorldGenerationStep.LIGHT, EDhApiWorldCompressionMode.MERGE_SAME_BLOCKS);
|
||||
dataSource.isEmpty = false;
|
||||
}
|
||||
}
|
||||
return dataSource;
|
||||
@@ -317,41 +340,97 @@ public class LodDataBuilder
|
||||
|
||||
/** @see FullDataPointUtil */
|
||||
public static LongArrayList convertApiDataPointListToPackedLongArray(
|
||||
@Nullable List<DhApiTerrainDataPoint> columnDataPoints, FullDataSourceV2 dataSource,
|
||||
int bottomYBlockPos) throws DataCorruptedException
|
||||
@Nullable List<DhApiTerrainDataPoint> topDownColumnDataPoints, FullDataSourceV2 dataSource,
|
||||
int bottomYBlockPos, boolean runAdditionalValidation) throws DataCorruptedException
|
||||
{
|
||||
// this null check does 2 nice things at the same time:
|
||||
// if columnDataPoints is null,
|
||||
// then packedDataPoints will be of length 0
|
||||
// AND the below loop won't run.
|
||||
int size = (columnDataPoints != null) ? columnDataPoints.size() : 0;
|
||||
|
||||
// TODO make missing air LODs
|
||||
// TODO merge duplicate datapoints
|
||||
LongArrayList packedDataPoints = new LongArrayList(new long[size]);
|
||||
for (int index = 0; index < size; index++)
|
||||
if (topDownColumnDataPoints == null
|
||||
|| topDownColumnDataPoints.size() == 0)
|
||||
{
|
||||
DhApiTerrainDataPoint dataPoint = columnDataPoints.get(index);
|
||||
return new LongArrayList(0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// array to store data
|
||||
int size = topDownColumnDataPoints.size();
|
||||
LongArrayList packedDataPoints = new LongArrayList(size);
|
||||
packedDataPoints.clear();
|
||||
|
||||
|
||||
if (runAdditionalValidation)
|
||||
{
|
||||
// check for missing data
|
||||
int lastTopY = Integer.MAX_VALUE;
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
DhApiTerrainDataPoint apiDataPoint = topDownColumnDataPoints.get(i);
|
||||
|
||||
if (lastTopY != apiDataPoint.topYBlockPos
|
||||
// the first index won't have a lastTopY value
|
||||
&& i != 0)
|
||||
{
|
||||
throw new DataCorruptedException("LOD data has a gap between ["+lastTopY+"] and ["+apiDataPoint.bottomYBlockPos+"]. Empty areas should be filled with air datapoints so light propagates correctly.");
|
||||
}
|
||||
lastTopY = apiDataPoint.bottomYBlockPos;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// go through data from top down
|
||||
long lastDataPoint = FullDataPointUtil.EMPTY_DATA_POINT;
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
DhApiTerrainDataPoint apiDataPoint = topDownColumnDataPoints.get(i);
|
||||
|
||||
int id = dataSource.mapping.addIfNotPresentAndGetId(
|
||||
(IBiomeWrapper) (dataPoint.biomeWrapper),
|
||||
(IBlockStateWrapper) (dataPoint.blockStateWrapper)
|
||||
int thisId = dataSource.mapping.addIfNotPresentAndGetId(
|
||||
(IBiomeWrapper) (apiDataPoint.biomeWrapper),
|
||||
(IBlockStateWrapper) (apiDataPoint.blockStateWrapper)
|
||||
);
|
||||
int thisHeight = (apiDataPoint.topYBlockPos - apiDataPoint.bottomYBlockPos);
|
||||
|
||||
packedDataPoints.set(index, FullDataPointUtil.encode(
|
||||
id,
|
||||
dataPoint.topYBlockPos - dataPoint.bottomYBlockPos,
|
||||
dataPoint.bottomYBlockPos - bottomYBlockPos,
|
||||
(byte) (dataPoint.blockLightLevel),
|
||||
(byte) (dataPoint.skyLightLevel)
|
||||
));
|
||||
int lastId = FullDataPointUtil.getId(lastDataPoint);
|
||||
byte lastBlockLight = (byte)FullDataPointUtil.getBlockLight(lastDataPoint);
|
||||
byte lastSkyLight = (byte)FullDataPointUtil.getSkyLight(lastDataPoint);
|
||||
|
||||
|
||||
// if the ID and light are the same, merge the height
|
||||
if (thisId == lastId
|
||||
&& apiDataPoint.blockLightLevel == lastBlockLight
|
||||
&& apiDataPoint.skyLightLevel == lastSkyLight
|
||||
// the first index should always be added to the list
|
||||
&& i != 0 )
|
||||
{
|
||||
// add adjacent height
|
||||
int lastHeight = FullDataPointUtil.getHeight(lastDataPoint);
|
||||
int newHeight = (lastHeight + thisHeight);
|
||||
lastDataPoint = FullDataPointUtil.setHeight(lastDataPoint, newHeight);
|
||||
|
||||
// subtract bottom Y
|
||||
int lastBottomY = FullDataPointUtil.getBottomY(lastDataPoint);
|
||||
int newBottomY = lastBottomY - thisHeight;
|
||||
lastDataPoint = FullDataPointUtil.setBottomY(lastDataPoint, newBottomY);
|
||||
|
||||
packedDataPoints.set(packedDataPoints.size()-1, lastDataPoint);
|
||||
}
|
||||
else
|
||||
{
|
||||
// data changed, create a new datapoint
|
||||
long dataPoint = FullDataPointUtil.encode(
|
||||
thisId,
|
||||
thisHeight,
|
||||
apiDataPoint.bottomYBlockPos - bottomYBlockPos,
|
||||
(byte) (apiDataPoint.blockLightLevel),
|
||||
(byte) (apiDataPoint.skyLightLevel)
|
||||
);
|
||||
lastDataPoint = dataPoint;
|
||||
packedDataPoints.add(dataPoint);
|
||||
}
|
||||
}
|
||||
|
||||
return packedDataPoints;
|
||||
}
|
||||
|
||||
/** also corrects the order if it's backwards */
|
||||
public static void correctDataColumnOrder(List<DhApiTerrainDataPoint> dataPoints)
|
||||
public static void putListInTopDownOrder(List<DhApiTerrainDataPoint> dataPoints)
|
||||
{
|
||||
// order doesn't need to be checked if there is 0 or 1 items
|
||||
if (dataPoints.size() > 1)
|
||||
|
||||
+2
-2
@@ -134,14 +134,14 @@ public class FullDataSourceProviderV2 implements IDebugRenderable, AutoCloseable
|
||||
|
||||
public void addDataSourceUpdateListener(IDataSourceUpdateListenerFunc<FullDataSourceV2> listener)
|
||||
{
|
||||
synchronized (this.dataUpdater)
|
||||
synchronized (this.dataUpdater.dateSourceUpdateListeners)
|
||||
{
|
||||
this.dataUpdater.dateSourceUpdateListeners.add(listener);
|
||||
}
|
||||
}
|
||||
public void removeDataSourceUpdateListener(IDataSourceUpdateListenerFunc<FullDataSourceV2> listener)
|
||||
{
|
||||
synchronized (this.dataUpdater)
|
||||
synchronized (this.dataUpdater.dateSourceUpdateListeners)
|
||||
{
|
||||
this.dataUpdater.dateSourceUpdateListeners.add(listener);
|
||||
}
|
||||
|
||||
+1
@@ -82,6 +82,7 @@ public class FullDataUpdatePropagatorV2 implements IDebugRenderable, AutoCloseab
|
||||
}
|
||||
|
||||
|
||||
|
||||
//================//
|
||||
// parent updates //
|
||||
//================//
|
||||
|
||||
+6
-3
@@ -150,11 +150,14 @@ public class FullDataUpdaterV2 implements IDebugRenderable, AutoCloseable
|
||||
}
|
||||
|
||||
|
||||
for (IDataSourceUpdateListenerFunc<FullDataSourceV2> listener : this.dateSourceUpdateListeners)
|
||||
synchronized (this.dateSourceUpdateListeners)
|
||||
{
|
||||
if (listener != null)
|
||||
for (IDataSourceUpdateListenerFunc<FullDataSourceV2> listener : this.dateSourceUpdateListeners)
|
||||
{
|
||||
listener.OnDataSourceUpdated(recipientDataSource);
|
||||
if (listener != null)
|
||||
{
|
||||
listener.OnDataSourceUpdated(recipientDataSource);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,12 +24,13 @@ import com.seibel.distanthorizons.api.interfaces.override.worldGenerator.IDhApiW
|
||||
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
|
||||
import com.seibel.distanthorizons.core.level.IDhLevel;
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.core.util.ExceptionUtil;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.worldGeneration.IBatchGeneratorEnvironmentWrapper;
|
||||
import com.seibel.distanthorizons.coreapi.interfaces.dependencyInjection.IOverrideInjector;
|
||||
import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiDistantGeneratorMode;
|
||||
import com.seibel.distanthorizons.core.util.LodUtil;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.IWrapperFactory;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.worldGeneration.AbstractBatchGenerationEnvironmentWrapper;
|
||||
import com.seibel.distanthorizons.core.logging.DhLogger;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
@@ -42,10 +43,10 @@ import java.util.function.Consumer;
|
||||
*/
|
||||
public class BatchGenerator implements IDhApiWorldGenerator
|
||||
{
|
||||
private static final IWrapperFactory FACTORY = SingletonInjector.INSTANCE.get(IWrapperFactory.class);
|
||||
private static final IWrapperFactory WRAPPER_FACTORY = SingletonInjector.INSTANCE.get(IWrapperFactory.class);
|
||||
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
|
||||
|
||||
public AbstractBatchGenerationEnvironmentWrapper generationEnvironment;
|
||||
public IBatchGeneratorEnvironmentWrapper generationEnvironment;
|
||||
public IDhLevel targetDhLevel;
|
||||
|
||||
|
||||
@@ -57,7 +58,7 @@ public class BatchGenerator implements IDhApiWorldGenerator
|
||||
public BatchGenerator(IDhLevel targetDhLevel)
|
||||
{
|
||||
this.targetDhLevel = targetDhLevel;
|
||||
this.generationEnvironment = FACTORY.createBatchGenerator(targetDhLevel);
|
||||
this.generationEnvironment = WRAPPER_FACTORY.createBatchGenerator(targetDhLevel);
|
||||
LOGGER.info("Batch Chunk Generator initialized");
|
||||
}
|
||||
|
||||
@@ -83,26 +84,26 @@ public class BatchGenerator implements IDhApiWorldGenerator
|
||||
|
||||
|
||||
|
||||
|
||||
//===================//
|
||||
// generator methods //
|
||||
//===================//
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> generateChunks(
|
||||
int chunkPosMinX, int chunkPosMinZ, int generationRequestChunkWidthCount, byte targetDataDetail, EDhApiDistantGeneratorMode generatorMode,
|
||||
ExecutorService worldGeneratorThreadPool, Consumer<Object[]> resultConsumer)
|
||||
int chunkPosMinX,
|
||||
int chunkPosMinZ,
|
||||
int chunkWidthCount,
|
||||
byte targetDataDetail,
|
||||
EDhApiDistantGeneratorMode generatorMode,
|
||||
ExecutorService worldGeneratorThreadPool,
|
||||
Consumer<Object[]> resultConsumer)
|
||||
{
|
||||
EDhApiWorldGenerationStep targetStep = null;
|
||||
EDhApiWorldGenerationStep targetStep;
|
||||
switch (generatorMode)
|
||||
{
|
||||
case PRE_EXISTING_ONLY: // Only load in existing chunks. Note: this requires the biome generation step in order for biomes to be properly initialized.
|
||||
//case BIOME_ONLY: // No blocks. Require fake height in LodBuilder
|
||||
targetStep = EDhApiWorldGenerationStep.BIOMES;
|
||||
case PRE_EXISTING_ONLY: // Only load in existing chunks.
|
||||
targetStep = EDhApiWorldGenerationStep.EMPTY; // special logic
|
||||
break;
|
||||
//case BIOME_ONLY_SIMULATE_HEIGHT:
|
||||
// targetStep = EDhApiWorldGenerationStep.NOISE; // Stone only. Requires a fake surface
|
||||
// break;
|
||||
case SURFACE:
|
||||
targetStep = EDhApiWorldGenerationStep.SURFACE;
|
||||
break;
|
||||
@@ -112,20 +113,27 @@ public class BatchGenerator implements IDhApiWorldGenerator
|
||||
case INTERNAL_SERVER:
|
||||
targetStep = EDhApiWorldGenerationStep.LIGHT;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new IllegalArgumentException("no target step defined for generator mode: ["+generatorMode+"].");
|
||||
}
|
||||
|
||||
// the consumer needs to be wrapped like this because the API can't use DH core objects (and IChunkWrapper can't be easily put into the API project)
|
||||
Consumer<IChunkWrapper> consumerWrapper = (chunkWrapper) -> resultConsumer.accept(new Object[]{chunkWrapper});
|
||||
try
|
||||
{
|
||||
return this.generationEnvironment.generateChunks(
|
||||
chunkPosMinX, chunkPosMinZ, generationRequestChunkWidthCount,
|
||||
return this.generationEnvironment.queueGenEvent(
|
||||
chunkPosMinX, chunkPosMinZ, chunkWidthCount,
|
||||
generatorMode, targetStep,
|
||||
worldGeneratorThreadPool, consumerWrapper);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (!LodUtil.isInterruptOrReject(e)) LOGGER.error("Error starting future for chunk generation", e);
|
||||
if (!ExceptionUtil.isInterruptOrReject(e))
|
||||
{
|
||||
LOGGER.error("Error starting future for chunk generation, error: ["+e.getMessage()+"].", e);
|
||||
}
|
||||
|
||||
CompletableFuture<Void> future = new CompletableFuture<>();
|
||||
future.completeExceptionally(e);
|
||||
return future;
|
||||
@@ -144,9 +152,10 @@ public class BatchGenerator implements IDhApiWorldGenerator
|
||||
@Override
|
||||
public void close()
|
||||
{
|
||||
LOGGER.info(BatchGenerator.class.getSimpleName() + " shutting down...");
|
||||
this.generationEnvironment.stop();
|
||||
LOGGER.info("["+BatchGenerator.class.getSimpleName()+"] shutting down...");
|
||||
this.generationEnvironment.close();
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
+49
-36
@@ -154,14 +154,15 @@ public class DhLightingEngine
|
||||
// and get any necessary info from them
|
||||
for (int chunkIndex = 0; chunkIndex < nearbyChunkList.size(); chunkIndex++) // using iterators in high traffic areas can cause GC issues due to allocating a bunch of iterators, use an indexed for-loop instead
|
||||
{
|
||||
IChunkWrapper chunk = nearbyChunkList.get(chunkIndex);
|
||||
if (chunk != null && requestedAdjacentPositions.contains(chunk.getChunkPos()))
|
||||
IChunkWrapper neighborChunk = nearbyChunkList.get(chunkIndex);
|
||||
if (neighborChunk != null
|
||||
&& requestedAdjacentPositions.contains(neighborChunk.getChunkPos()))
|
||||
{
|
||||
// remove the newly found position
|
||||
requestedAdjacentPositions.remove(chunk.getChunkPos());
|
||||
requestedAdjacentPositions.remove(neighborChunk.getChunkPos());
|
||||
|
||||
// add the adjacent chunk
|
||||
adjacentChunkHolder.add(chunk);
|
||||
adjacentChunkHolder.add(neighborChunk);
|
||||
|
||||
// get and set the adjacent chunk's initial block lights
|
||||
final DhBlockPosMutable relLightBlockPos = PRIMARY_BLOCK_POS_REF.get();
|
||||
@@ -174,19 +175,19 @@ public class DhLightingEngine
|
||||
|
||||
if (updateBlockLight)
|
||||
{
|
||||
ArrayList<DhBlockPos> blockLightPosList = chunk.getWorldBlockLightPosList();
|
||||
ArrayList<DhBlockPos> blockLightPosList = neighborChunk.getWorldBlockLightPosList();
|
||||
for (int blockLightIndex = 0; blockLightIndex < blockLightPosList.size(); blockLightIndex++) // using iterators in high traffic areas can cause GC issues due to allocating a bunch of iterators, use an indexed for-loop instead
|
||||
{
|
||||
DhBlockPos blockLightPos = blockLightPosList.get(blockLightIndex);
|
||||
blockLightPos.mutateToChunkRelativePos(relLightBlockPos);
|
||||
|
||||
// get the light
|
||||
IBlockStateWrapper blockState = chunk.getBlockState(relLightBlockPos);
|
||||
IBlockStateWrapper blockState = neighborChunk.getBlockState(relLightBlockPos);
|
||||
int lightValue = blockState.getLightEmission();
|
||||
blockLightWorldPosQueue.push(blockLightPos.getX(), blockLightPos.getY(), blockLightPos.getZ(), lightValue);
|
||||
|
||||
// set the light
|
||||
chunk.setDhBlockLight(relLightBlockPos.getX(), relLightBlockPos.getY(), relLightBlockPos.getZ(), lightValue);
|
||||
neighborChunk.setDhBlockLight(relLightBlockPos.getX(), relLightBlockPos.getY(), relLightBlockPos.getZ(), lightValue);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -198,23 +199,24 @@ public class DhLightingEngine
|
||||
|
||||
// get and set the adjacent chunk's initial skylights,
|
||||
// if the dimension has skylights
|
||||
if (updateSkyLight && maxSkyLight > 0)
|
||||
if (updateSkyLight
|
||||
&& maxSkyLight > 0)
|
||||
{
|
||||
IMutableBlockPosWrapper mcBlockPos = chunk.getMutableBlockPosWrapper();
|
||||
IMutableBlockPosWrapper mcBlockPos = neighborChunk.getMutableBlockPosWrapper();
|
||||
IBlockStateWrapper previousBlockState = null;
|
||||
|
||||
int maxY = chunk.getMaxNonEmptyHeight();
|
||||
int minY = chunk.getInclusiveMinBuildHeight();
|
||||
int maxY = neighborChunk.getMaxNonEmptyHeight();
|
||||
int minY = neighborChunk.getInclusiveMinBuildHeight();
|
||||
|
||||
// get the adjacent chunk's sky lights
|
||||
for (int relX = 0; relX < LodUtil.CHUNK_WIDTH; relX++) // relative block pos
|
||||
{
|
||||
for (int relZ = 0; relZ < LodUtil.CHUNK_WIDTH; relZ++)
|
||||
{
|
||||
// set each pos' sky light all the way down until an opaque block is hit
|
||||
// set each pos sky light all the way down until an opaque block is hit
|
||||
for (int y = maxY; y >= minY; y--)
|
||||
{
|
||||
IBlockStateWrapper block = previousBlockState = chunk.getBlockState(relX, y, relZ, mcBlockPos, previousBlockState);
|
||||
IBlockStateWrapper block = previousBlockState = neighborChunk.getBlockState(relX, y, relZ, mcBlockPos, previousBlockState);
|
||||
if (block != null && block.getOpacity() != LodUtil.BLOCK_FULLY_TRANSPARENT)
|
||||
{
|
||||
// keep moving down until we find a non-transparent block
|
||||
@@ -223,12 +225,12 @@ public class DhLightingEngine
|
||||
|
||||
|
||||
// add sky light to the queue
|
||||
DhBlockPos skyLightPos = new DhBlockPos(chunk.getMinBlockX() + relX, y, chunk.getMinBlockZ() + relZ);
|
||||
DhBlockPos skyLightPos = new DhBlockPos(neighborChunk.getMinBlockX() + relX, y, neighborChunk.getMinBlockZ() + relZ);
|
||||
skyLightWorldPosQueue.push(skyLightPos.getX(), skyLightPos.getY(), skyLightPos.getZ(), maxSkyLight);
|
||||
|
||||
// set the chunk's sky light
|
||||
skyLightPos.mutateToChunkRelativePos(relLightBlockPos);
|
||||
chunk.setDhSkyLight(relLightBlockPos.getX(), relLightBlockPos.getY(), relLightBlockPos.getZ(), maxSkyLight);
|
||||
neighborChunk.setDhSkyLight(relLightBlockPos.getX(), relLightBlockPos.getY(), relLightBlockPos.getZ(), maxSkyLight);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -247,13 +249,12 @@ public class DhLightingEngine
|
||||
if (updateBlockLight)
|
||||
{
|
||||
// done to prevent a rare issue where the light values are incorrectly set to -1
|
||||
// TODO why could that happen?
|
||||
centerChunk.clearDhBlockLighting();
|
||||
|
||||
this.propagateChunkLightPosList(blockLightWorldPosQueue, adjacentChunkHolder,
|
||||
(neighbourChunk, relBlockPos) -> neighbourChunk.getDhBlockLight(relBlockPos.getX(), relBlockPos.getY(), relBlockPos.getZ()),
|
||||
(neighbourChunk, relBlockPos, newLightValue) -> neighbourChunk.setDhBlockLight(relBlockPos.getX(), relBlockPos.getY(), relBlockPos.getZ(), newLightValue),
|
||||
true);
|
||||
(neighbourChunk, relBlockPos) -> neighbourChunk.getDhBlockLight(relBlockPos.getX(), relBlockPos.getY(), relBlockPos.getZ()),
|
||||
(neighbourChunk, relBlockPos, newLightValue) -> neighbourChunk.setDhBlockLight(relBlockPos.getX(), relBlockPos.getY(), relBlockPos.getZ(), newLightValue),
|
||||
true);
|
||||
}
|
||||
|
||||
// sky light
|
||||
@@ -262,9 +263,9 @@ public class DhLightingEngine
|
||||
centerChunk.clearDhSkyLighting();
|
||||
|
||||
this.propagateChunkLightPosList(skyLightWorldPosQueue, adjacentChunkHolder,
|
||||
(neighbourChunk, relBlockPos) -> neighbourChunk.getDhSkyLight(relBlockPos.getX(), relBlockPos.getY(), relBlockPos.getZ()),
|
||||
(neighbourChunk, relBlockPos, newLightValue) -> neighbourChunk.setDhSkyLight(relBlockPos.getX(), relBlockPos.getY(), relBlockPos.getZ(), newLightValue),
|
||||
false);
|
||||
(neighbourChunk, relBlockPos) -> neighbourChunk.getDhSkyLight(relBlockPos.getX(), relBlockPos.getY(), relBlockPos.getZ()),
|
||||
(neighbourChunk, relBlockPos, newLightValue) -> neighbourChunk.setDhSkyLight(relBlockPos.getX(), relBlockPos.getY(), relBlockPos.getZ(), newLightValue),
|
||||
false);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
@@ -300,9 +301,25 @@ public class DhLightingEngine
|
||||
final DhBlockPosMutable neighbourBlockPos = PRIMARY_BLOCK_POS_REF.get();
|
||||
final DhBlockPosMutable relNeighbourBlockPos = SECONDARY_BLOCK_POS_REF.get();
|
||||
|
||||
// it doesn't matter what chunk we get the mutable block pos from
|
||||
IMutableBlockPosWrapper mcBlockPos = null;
|
||||
for (int i = 0; i < adjacentChunkHolder.chunkArray.length; i++)
|
||||
{
|
||||
IChunkWrapper chunkWrapper = adjacentChunkHolder.chunkArray[i];
|
||||
if (chunkWrapper != null)
|
||||
{
|
||||
mcBlockPos = chunkWrapper.getMutableBlockPosWrapper();
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (mcBlockPos == null)
|
||||
{
|
||||
LodUtil.assertNotReach("How did we try to light a chunk with no chunks?");
|
||||
}
|
||||
|
||||
IBlockStateWrapper previousBlockState = null;
|
||||
|
||||
|
||||
// update each light position
|
||||
while (!lightPosQueue.isEmpty())
|
||||
{
|
||||
@@ -346,15 +363,9 @@ public class DhLightingEngine
|
||||
}
|
||||
|
||||
|
||||
if (mcBlockPos == null)
|
||||
{
|
||||
// it doesn't matter what chunk we get the position object from
|
||||
// TODO move this getter logic out of ChunkWrapper
|
||||
mcBlockPos = neighbourChunk.getMutableBlockPosWrapper();
|
||||
}
|
||||
IBlockStateWrapper neighbourBlockState = neighbourChunk.getBlockState(relNeighbourBlockPos, mcBlockPos, previousBlockState);
|
||||
previousBlockState = neighbourBlockState;
|
||||
|
||||
|
||||
IBlockStateWrapper neighbourBlockState = previousBlockState = neighbourChunk.getBlockState(relNeighbourBlockPos, mcBlockPos, previousBlockState);
|
||||
// Math.max(1, ...) is used so that the propagated light level always drops by at least 1, preventing infinite cycles.
|
||||
int targetLevel = lightValue - Math.max(1, neighbourBlockState.getOpacity());
|
||||
if (targetLevel > currentBlockLight)
|
||||
@@ -370,12 +381,14 @@ public class DhLightingEngine
|
||||
}
|
||||
|
||||
|
||||
// can be enable if troubleshooting lighting issues
|
||||
if (RENDER_BLOCK_LIGHT_WIREFRAME && propagatingBlockLights)
|
||||
// can be enabled if troubleshooting lighting issues
|
||||
if (RENDER_BLOCK_LIGHT_WIREFRAME
|
||||
&& propagatingBlockLights)
|
||||
{
|
||||
RenderDhLightValuesAsWireframe(adjacentChunkHolder, true);
|
||||
}
|
||||
else if (RENDER_SKY_LIGHT_WIREFRAME && !propagatingBlockLights)
|
||||
else if (RENDER_SKY_LIGHT_WIREFRAME
|
||||
&& !propagatingBlockLights)
|
||||
{
|
||||
RenderDhLightValuesAsWireframe(adjacentChunkHolder, false);
|
||||
}
|
||||
@@ -462,7 +475,7 @@ public class DhLightingEngine
|
||||
point = FullDataPointUtil.setSkyLight(point, skylight);
|
||||
dataPoints.set(index, point);
|
||||
// now for the propagation.
|
||||
recursivelyLightAdjacentDataPoints(dataSource, airIDs, x, z, point);
|
||||
this.recursivelyLightAdjacentDataPoints(dataSource, airIDs, x, z, point);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -596,7 +609,7 @@ public class DhLightingEngine
|
||||
else if (!airIDs.get(FullDataPointUtil.getId(adjacentDataPoint)))
|
||||
{
|
||||
// assume for now that we cannot propagate into non-transparent data points.
|
||||
continue; // TODO how does this work with water? Do we care?
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -610,7 +623,7 @@ public class DhLightingEngine
|
||||
adjacentDataPoint = FullDataPointUtil.setSkyLight(adjacentDataPoint, lightLevel - 1);
|
||||
adjacentDataPoints.set(adjacentIndex, adjacentDataPoint);
|
||||
// if propagation succeeded, recursively propagate again starting at the adjacent data point.
|
||||
recursivelyLightAdjacentDataPoints(chunk, airIDs, adjacentX, adjacentZ, adjacentDataPoint);
|
||||
this.recursivelyLightAdjacentDataPoints(chunk, airIDs, adjacentX, adjacentZ, adjacentDataPoint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,7 +111,7 @@ public class PregenManager
|
||||
}
|
||||
|
||||
long timeSincePreviousTaskFinish = System.currentTimeMillis() - this.lastTaskFinishTime.getAndSet(System.currentTimeMillis());
|
||||
this.averageTaskCompletionIntervalMs.addValue(timeSincePreviousTaskFinish);
|
||||
this.averageTaskCompletionIntervalMs.add(timeSincePreviousTaskFinish);
|
||||
|
||||
PregenState.this.fillPendingQueue();
|
||||
})
|
||||
|
||||
+1
-1
@@ -69,7 +69,7 @@ public class RemoteWorldRetrievalQueue extends AbstractFullDataNetworkRequestQue
|
||||
int chunkWidth = DhSectionPos.getChunkWidth(sectionPos);
|
||||
int chunkCount = chunkWidth * chunkWidth;
|
||||
double timePerChunk = (double)totalGenTimeInMs / (double)chunkCount;
|
||||
this.rollingAverageChunkGenTimeInMs.addValue(timePerChunk);
|
||||
this.rollingAverageChunkGenTimeInMs.add(timePerChunk);
|
||||
|
||||
switch (requestResult)
|
||||
{
|
||||
|
||||
+15
-18
@@ -40,6 +40,7 @@ import com.seibel.distanthorizons.core.config.Config;
|
||||
import com.seibel.distanthorizons.core.dataObjects.transformers.LodDataBuilder;
|
||||
import com.seibel.distanthorizons.core.render.renderer.DebugRenderer;
|
||||
import com.seibel.distanthorizons.core.render.renderer.IDebugRenderable;
|
||||
import com.seibel.distanthorizons.core.util.ExceptionUtil;
|
||||
import com.seibel.distanthorizons.core.util.LodUtil.AssertFailureException;
|
||||
import com.seibel.distanthorizons.core.util.ThreadUtil;
|
||||
import com.seibel.distanthorizons.core.util.objects.DataCorruptedException;
|
||||
@@ -65,15 +66,6 @@ public class WorldGenerationQueue implements IFullDataSourceRetrievalQueue, IDeb
|
||||
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
|
||||
private static final IWrapperFactory WRAPPER_FACTORY = SingletonInjector.INSTANCE.get(IWrapperFactory.class);
|
||||
|
||||
/**
|
||||
* Defines how many tasks can be queued per thread. <br><br>
|
||||
*
|
||||
* TODO the multiplier here should change dynamically based on how fast the generator is vs the queuing thread,
|
||||
* if this is too high it may cause issues when moving,
|
||||
* but if it is too low the generator threads won't have enough tasks to work on
|
||||
*/
|
||||
private static final int MAX_QUEUED_TASKS_PER_THREAD = 3;
|
||||
|
||||
|
||||
private final IDhApiWorldGenerator generator;
|
||||
private final IDhServerLevel level;
|
||||
@@ -240,9 +232,9 @@ public class WorldGenerationQueue implements IFullDataSourceRetrievalQueue, IDeb
|
||||
return true;
|
||||
}
|
||||
|
||||
// queue more tasks if any of the threads are available
|
||||
int worldGenThreadCount = Math.max(Config.Common.MultiThreading.numberOfThreads.get(), 1);
|
||||
int maxWorldGenTaskCount = worldGenThreadCount * MAX_QUEUED_TASKS_PER_THREAD;
|
||||
return this.inProgressGenTasksByLodPos.size() > maxWorldGenTaskCount;
|
||||
return this.inProgressGenTasksByLodPos.size() > worldGenThreadCount;
|
||||
}
|
||||
/**
|
||||
* @param targetPos the position to center the generation around
|
||||
@@ -345,7 +337,7 @@ public class WorldGenerationQueue implements IFullDataSourceRetrievalQueue, IDeb
|
||||
long totalGenTimeInMs = System.currentTimeMillis() - generationStartMsTime;
|
||||
int chunkCount = generationRequestChunkWidthCount * generationRequestChunkWidthCount;
|
||||
double timePerChunk = (double)totalGenTimeInMs / (double)chunkCount;
|
||||
this.rollingAverageChunkGenTimeInMs.addValue(timePerChunk);
|
||||
this.rollingAverageChunkGenTimeInMs.add(timePerChunk);
|
||||
});
|
||||
|
||||
newTaskGroup.genFuture = generationFuture;
|
||||
@@ -358,7 +350,7 @@ public class WorldGenerationQueue implements IFullDataSourceRetrievalQueue, IDeb
|
||||
if (exception != null)
|
||||
{
|
||||
// don't log the shutdown exceptions
|
||||
if (!LodUtil.isInterruptOrReject(exception))
|
||||
if (!ExceptionUtil.isInterruptOrReject(exception))
|
||||
{
|
||||
LOGGER.error("Error generating data for pos: " + DhSectionPos.toString(taskPos), exception);
|
||||
}
|
||||
@@ -412,11 +404,16 @@ public class WorldGenerationQueue implements IFullDataSourceRetrievalQueue, IDeb
|
||||
{
|
||||
IChunkWrapper chunkWrapper = WRAPPER_FACTORY.createChunkWrapper(generatedObjectArray);
|
||||
|
||||
// TODO light data should be pulled (if possible) from the ChunkAccess object itself via ChunkFileReader.readLight
|
||||
// but this should work for now
|
||||
ArrayList<IChunkWrapper> nearbyChunkList = new ArrayList<IChunkWrapper>();
|
||||
nearbyChunkList.add(chunkWrapper);
|
||||
DhLightingEngine.INSTANCE.bakeChunkBlockLighting(chunkWrapper, nearbyChunkList, this.level.getLevelWrapper().hasSkyLight() ? LodUtil.MAX_MC_LIGHT : LodUtil.MIN_MC_LIGHT);
|
||||
// only light the chunk here if necessary,
|
||||
// lighting before this point is preferred but for potenial legacy API uses this
|
||||
// check should be done
|
||||
if (!chunkWrapper.isDhBlockLightingCorrect())
|
||||
{
|
||||
ArrayList<IChunkWrapper> nearbyChunkList = new ArrayList<>();
|
||||
nearbyChunkList.add(chunkWrapper);
|
||||
byte maxSkyLight = this.level.getLevelWrapper().hasSkyLight() ? LodUtil.MAX_MC_LIGHT : LodUtil.MIN_MC_LIGHT;
|
||||
DhLightingEngine.INSTANCE.bakeChunkBlockLighting(chunkWrapper, nearbyChunkList, maxSkyLight);
|
||||
}
|
||||
|
||||
try (FullDataSourceV2 dataSource = LodDataBuilder.createFromChunk(this.level.getLevelWrapper(), chunkWrapper))
|
||||
{
|
||||
|
||||
@@ -45,7 +45,6 @@ import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* Handles both single-player/server-side world gen and client side LOD requests.
|
||||
* TODO rename
|
||||
*/
|
||||
public class LodRequestModule implements Closeable
|
||||
{
|
||||
|
||||
+11
-1
@@ -121,6 +121,16 @@ public class VanillaFadeRenderer
|
||||
|
||||
public void render(Mat4f mcModelViewMatrix, Mat4f mcProjectionMatrix, float partialTicks, IClientLevelWrapper level)
|
||||
{
|
||||
int depthTextureId = LodRenderer.INSTANCE.getActiveDepthTextureId();
|
||||
if (depthTextureId == -1)
|
||||
{
|
||||
// the renderer hasn't been set up yet
|
||||
// trying to render fading may cause GL errors
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
IProfilerWrapper profiler = MC_CLIENT.getProfiler();
|
||||
profiler.pop(); // get out of "terrain"
|
||||
profiler.push("DH-Vanilla Fade");
|
||||
@@ -158,7 +168,7 @@ public class VanillaFadeRenderer
|
||||
|
||||
FadeApplyShader.INSTANCE.fadeTexture = this.fadeTexture;
|
||||
FadeApplyShader.INSTANCE.readFramebuffer = DhFadeShader.INSTANCE.frameBuffer;
|
||||
FadeApplyShader.INSTANCE.drawFramebuffer = LodRenderer.INSTANCE.getActiveFramebufferId();
|
||||
FadeApplyShader.INSTANCE.drawFramebuffer = MC_RENDER.getTargetFramebuffer();
|
||||
FadeApplyShader.INSTANCE.render(partialTicks);
|
||||
}
|
||||
|
||||
|
||||
@@ -69,8 +69,7 @@ public class FullDataSourceV1DTO implements IBaseDTO<Long>
|
||||
/** @return a stream for the data contained in this DTO. */
|
||||
public DhDataInputStream getInputStream() throws IOException
|
||||
{
|
||||
InputStream inputStream = new ByteArrayInputStream(this.dataArray);
|
||||
DhDataInputStream compressedStream = new DhDataInputStream(inputStream, EDhApiDataCompressionMode.LZ4); // LZ4 was used by DH before 2.1.0 and as such must be used until the render data format is changed to record the compressor
|
||||
DhDataInputStream compressedStream = DhDataInputStream.create(this.dataArray, EDhApiDataCompressionMode.LZ4); // LZ4 was used by DH before 2.1.0 and as such must be used until the render data format is changed to record the compressor
|
||||
return compressedStream;
|
||||
}
|
||||
|
||||
|
||||
+13
-49
@@ -19,6 +19,7 @@
|
||||
|
||||
package com.seibel.distanthorizons.core.sql.dto;
|
||||
|
||||
import com.github.luben.zstd.Zstd;
|
||||
import com.google.common.base.MoreObjects;
|
||||
import com.seibel.distanthorizons.api.enums.config.EDhApiDataCompressionMode;
|
||||
import com.seibel.distanthorizons.api.enums.config.EDhApiWorldCompressionMode;
|
||||
@@ -317,12 +318,7 @@ public class FullDataSourceV2DTO
|
||||
LongArrayList[] inputDataArray, ByteArrayList outputByteArray,
|
||||
EDhApiDataCompressionMode compressionModeEnum) throws IOException
|
||||
{
|
||||
// write the outputs to a stream to prep for writing to the database
|
||||
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
|
||||
|
||||
// normally a DhStream should be the topmost stream to prevent closing the stream accidentally,
|
||||
// but since this stream will be closed immediately after writing anyway, it won't be an issue
|
||||
try (DhDataOutputStream compressedOut = new DhDataOutputStream(byteArrayOutputStream, compressionModeEnum))
|
||||
try (DhDataOutputStream compressedOut = DhDataOutputStream.create(compressionModeEnum, outputByteArray))
|
||||
{
|
||||
// write the data
|
||||
int dataArrayLength = FullDataSourceV2.WIDTH * FullDataSourceV2.WIDTH;
|
||||
@@ -342,20 +338,13 @@ public class FullDataSourceV2DTO
|
||||
compressedOut.writeLong(dataColumn.getLong(y));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// generate the checksum
|
||||
compressedOut.flush();
|
||||
byteArrayOutputStream.close();
|
||||
outputByteArray.addElements(0, byteArrayOutputStream.toByteArray());
|
||||
}
|
||||
}
|
||||
private static void readBlobToDataSourceDataArrayV1(
|
||||
ByteArrayList inputCompressedDataByteArray, LongArrayList[] outputDataLongArray,
|
||||
EDhApiDataCompressionMode compressionModeEnum) throws IOException, DataCorruptedException
|
||||
{
|
||||
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(inputCompressedDataByteArray.elements());
|
||||
try (DhDataInputStream compressedIn = new DhDataInputStream(byteArrayInputStream, compressionModeEnum))
|
||||
try (DhDataInputStream compressedIn = DhDataInputStream.create(inputCompressedDataByteArray, compressionModeEnum))
|
||||
{
|
||||
// read the data
|
||||
int dataArrayLength = FullDataSourceV2.WIDTH * FullDataSourceV2.WIDTH;
|
||||
@@ -407,8 +396,7 @@ public class FullDataSourceV2DTO
|
||||
maxZ = FullDataSourceV2.WIDTH-1;
|
||||
}
|
||||
|
||||
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
|
||||
try (DhDataOutputStream compressedOut = new DhDataOutputStream(byteArrayOutputStream, compressionModeEnum))
|
||||
try (DhDataOutputStream compressedOut = DhDataOutputStream.create(compressionModeEnum, outputByteArray))
|
||||
{
|
||||
// this method would be simpler if we allocated a bunch of temporary arrays,
|
||||
// but we're trying to avoid garbage.
|
||||
@@ -525,10 +513,6 @@ public class FullDataSourceV2DTO
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
compressedOut.flush();
|
||||
byteArrayOutputStream.close();
|
||||
outputByteArray.addElements(0, byteArrayOutputStream.toByteArray());
|
||||
}
|
||||
}
|
||||
private static void readBlobToDataSourceDataArrayV2(
|
||||
@@ -555,8 +539,8 @@ public class FullDataSourceV2DTO
|
||||
maxZ = FullDataSourceV2.WIDTH-1;
|
||||
}
|
||||
|
||||
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(inputCompressedDataByteArray.elements());
|
||||
try (DhDataInputStream compressedIn = new DhDataInputStream(byteArrayInputStream, compressionModeEnum))
|
||||
|
||||
try (DhDataInputStream compressedIn = DhDataInputStream.create(inputCompressedDataByteArray, compressionModeEnum))
|
||||
{
|
||||
// 1. column counts, preallocate
|
||||
for (int x = minX; x < maxX; x++)
|
||||
@@ -682,24 +666,17 @@ public class FullDataSourceV2DTO
|
||||
|
||||
private static void writeGenerationStepsToBlob(ByteArrayList inputColumnGenStepByteArray, ByteArrayList outputByteArray, EDhApiDataCompressionMode compressionModeEnum) throws IOException
|
||||
{
|
||||
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
|
||||
try (DhDataOutputStream compressedOut = new DhDataOutputStream(byteArrayOutputStream, compressionModeEnum))
|
||||
try (DhDataOutputStream compressedOut = DhDataOutputStream.create(compressionModeEnum, outputByteArray))
|
||||
{
|
||||
for (int i = 0; i < inputColumnGenStepByteArray.size(); i++)
|
||||
{
|
||||
compressedOut.writeByte(inputColumnGenStepByteArray.getByte(i));
|
||||
}
|
||||
|
||||
compressedOut.flush();
|
||||
byteArrayOutputStream.close();
|
||||
outputByteArray.addElements(0, byteArrayOutputStream.toByteArray());
|
||||
}
|
||||
}
|
||||
private static void readBlobToGenerationSteps(ByteArrayList inputCompressedDataByteArray, ByteArrayList outputByteArray, EDhApiDataCompressionMode compressionModeEnum) throws IOException, DataCorruptedException
|
||||
{
|
||||
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(inputCompressedDataByteArray.elements());
|
||||
|
||||
try(DhDataInputStream compressedIn = new DhDataInputStream(byteArrayInputStream, compressionModeEnum))
|
||||
try(DhDataInputStream compressedIn = DhDataInputStream.create(inputCompressedDataByteArray, compressionModeEnum))
|
||||
{
|
||||
compressedIn.readFully(outputByteArray.elements(), 0, FullDataSourceV2.WIDTH * FullDataSourceV2.WIDTH);
|
||||
}
|
||||
@@ -712,24 +689,17 @@ public class FullDataSourceV2DTO
|
||||
|
||||
private static void writeWorldCompressionModeToBlob(ByteArrayList inputWorldCompressionModeByteArray, ByteArrayList outputByteArray, EDhApiDataCompressionMode compressionModeEnum) throws IOException
|
||||
{
|
||||
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
|
||||
try (DhDataOutputStream compressedOut = new DhDataOutputStream(byteArrayOutputStream, compressionModeEnum))
|
||||
try (DhDataOutputStream compressedOut = DhDataOutputStream.create(compressionModeEnum, outputByteArray))
|
||||
{
|
||||
for (int i = 0; i < inputWorldCompressionModeByteArray.size(); i++)
|
||||
{
|
||||
compressedOut.write(inputWorldCompressionModeByteArray.getByte(i));
|
||||
}
|
||||
|
||||
compressedOut.flush();
|
||||
byteArrayOutputStream.close();
|
||||
outputByteArray.addElements(0, byteArrayOutputStream.toByteArray());
|
||||
}
|
||||
}
|
||||
private static void readBlobToWorldCompressionMode(ByteArrayList inputCompressedDataByteArray, ByteArrayList outputByteArray, EDhApiDataCompressionMode compressionModeEnum) throws IOException, DataCorruptedException
|
||||
{
|
||||
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(inputCompressedDataByteArray.elements());
|
||||
|
||||
try(DhDataInputStream compressedIn = new DhDataInputStream(byteArrayInputStream, compressionModeEnum))
|
||||
try(DhDataInputStream compressedIn = DhDataInputStream.create(inputCompressedDataByteArray, compressionModeEnum))
|
||||
{
|
||||
compressedIn.readFully(outputByteArray.elements(), 0, FullDataSourceV2.WIDTH * FullDataSourceV2.WIDTH);
|
||||
}
|
||||
@@ -742,20 +712,14 @@ public class FullDataSourceV2DTO
|
||||
|
||||
private static void writeDataMappingToBlob(FullDataPointIdMap mapping, ByteArrayList outputByteArray, EDhApiDataCompressionMode compressionModeEnum) throws IOException
|
||||
{
|
||||
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
|
||||
try(DhDataOutputStream compressedOut = new DhDataOutputStream(byteArrayOutputStream, compressionModeEnum))
|
||||
try(DhDataOutputStream compressedOut = DhDataOutputStream.create(compressionModeEnum, outputByteArray))
|
||||
{
|
||||
mapping.serialize(compressedOut);
|
||||
|
||||
compressedOut.flush();
|
||||
byteArrayOutputStream.close();
|
||||
outputByteArray.addElements(0, byteArrayOutputStream.toByteArray());
|
||||
}
|
||||
}
|
||||
private static FullDataPointIdMap readBlobToDataMapping(ByteArrayList compressedMappingByteArray, long pos, @NotNull ILevelWrapper levelWrapper, EDhApiDataCompressionMode compressionModeEnum) throws IOException, InterruptedException, DataCorruptedException
|
||||
private static FullDataPointIdMap readBlobToDataMapping(ByteArrayList inputCompressedDataByteArray, long pos, @NotNull ILevelWrapper levelWrapper, EDhApiDataCompressionMode compressionModeEnum) throws IOException, InterruptedException, DataCorruptedException
|
||||
{
|
||||
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(compressedMappingByteArray.elements());
|
||||
try (DhDataInputStream compressedIn = new DhDataInputStream(byteArrayInputStream, compressionModeEnum))
|
||||
try (DhDataInputStream compressedIn = DhDataInputStream.create(inputCompressedDataByteArray, compressionModeEnum))
|
||||
{
|
||||
FullDataPointIdMap mapping = FullDataPointIdMap.deserialize(compressedIn, pos, levelWrapper);
|
||||
return mapping;
|
||||
|
||||
+7
-2
@@ -572,9 +572,14 @@ public class FullDataSourceV2Repo extends AbstractDhRepo<Long, FullDataSourceV2D
|
||||
EDhApiDataCompressionMode compressionModeEnum = EDhApiDataCompressionMode.getFromValue(compressionModeEnumValue);
|
||||
|
||||
// decompress the data
|
||||
try(DhDataInputStream compressedIn = new DhDataInputStream(result.getBinaryStream("ColumnGenerationStep"), compressionModeEnum))
|
||||
try
|
||||
{
|
||||
putAllBytes(compressedIn, outputByteArray);
|
||||
ByteArrayList byteArrayList = new ByteArrayList();
|
||||
putAllBytes(result.getBinaryStream("ColumnGenerationStep"), byteArrayList);
|
||||
try(DhDataInputStream compressedIn = DhDataInputStream.create(byteArrayList, compressionModeEnum))
|
||||
{
|
||||
putAllBytes(compressedIn, outputByteArray);
|
||||
}
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.seibel.distanthorizons.core.util;
|
||||
import com.seibel.distanthorizons.core.util.objects.UncheckedInterruptedException;
|
||||
|
||||
import java.nio.channels.ClosedByInterruptException;
|
||||
import java.util.concurrent.CancellationException;
|
||||
import java.util.concurrent.CompletionException;
|
||||
import java.util.concurrent.RejectedExecutionException;
|
||||
|
||||
@@ -28,4 +29,16 @@ public class ExceptionUtil
|
||||
}
|
||||
|
||||
|
||||
public static boolean isInterruptOrReject(Throwable t)
|
||||
{
|
||||
Throwable unwrapped = ensureUnwrap(t);
|
||||
return UncheckedInterruptedException.isInterrupt(unwrapped) ||
|
||||
unwrapped instanceof RejectedExecutionException ||
|
||||
unwrapped instanceof CancellationException;
|
||||
}
|
||||
public static Throwable ensureUnwrap(Throwable t)
|
||||
{
|
||||
return t instanceof CompletionException ? ensureUnwrap(t.getCause()) : t;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -21,21 +21,11 @@ package com.seibel.distanthorizons.core.util;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CancellationException;
|
||||
import java.util.concurrent.CompletionException;
|
||||
import java.util.concurrent.RejectedExecutionException;
|
||||
|
||||
import com.seibel.distanthorizons.api.enums.config.EDhApiVanillaOverdraw;
|
||||
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.core.render.vertexFormat.DefaultLodVertexFormats;
|
||||
import com.seibel.distanthorizons.core.render.vertexFormat.LodVertexFormat;
|
||||
import com.seibel.distanthorizons.core.util.objects.UncheckedInterruptedException;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IDimensionTypeWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
|
||||
import com.seibel.distanthorizons.core.logging.DhLogger;
|
||||
|
||||
/**
|
||||
@@ -191,17 +181,6 @@ public class LodUtil
|
||||
throw new AssertFailureException("Assert Not Reach failed:\n " + message);
|
||||
}
|
||||
|
||||
public static Throwable ensureUnwrap(Throwable t)
|
||||
{
|
||||
return t instanceof CompletionException ? ensureUnwrap(t.getCause()) : t;
|
||||
}
|
||||
|
||||
public static boolean isInterruptOrReject(Throwable t)
|
||||
{
|
||||
Throwable unwrapped = LodUtil.ensureUnwrap(t);
|
||||
return UncheckedInterruptedException.isInterrupt(unwrapped) ||
|
||||
unwrapped instanceof RejectedExecutionException ||
|
||||
unwrapped instanceof CancellationException;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,111 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizons mod
|
||||
* licensed under the GNU LGPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.seibel.distanthorizons.core.util.objects;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class EventTimer
|
||||
{
|
||||
public static class Event
|
||||
{
|
||||
public long timeNs = -1;
|
||||
public String name;
|
||||
Event(String name)
|
||||
{
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
long lastEventNs = -1;
|
||||
|
||||
public ArrayList<Event> events = new ArrayList<>();
|
||||
|
||||
public EventTimer(String firstEventName)
|
||||
{
|
||||
lastEventNs = System.nanoTime();
|
||||
events.add(new Event(firstEventName));
|
||||
}
|
||||
|
||||
public void nextEvent(String name)
|
||||
{
|
||||
long timeNs = System.nanoTime();
|
||||
if (lastEventNs != -1 && !events.isEmpty() && events.get(events.size() - 1).timeNs == -1)
|
||||
{
|
||||
events.get(events.size() - 1).timeNs = timeNs - lastEventNs;
|
||||
}
|
||||
lastEventNs = timeNs;
|
||||
events.add(new Event(name));
|
||||
}
|
||||
|
||||
public void complete()
|
||||
{
|
||||
long timeNs = System.nanoTime();
|
||||
if (lastEventNs != -1 && !events.isEmpty() && events.get(events.size() - 1).timeNs == -1)
|
||||
{
|
||||
events.get(events.size() - 1).timeNs = timeNs - lastEventNs;
|
||||
}
|
||||
lastEventNs = -1;
|
||||
}
|
||||
|
||||
public long getEventTimeNs(String name)
|
||||
{
|
||||
for (Event e : events)
|
||||
{
|
||||
if (e.name.equals(name))
|
||||
{
|
||||
return e.timeNs;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public long getTotalTimeNs()
|
||||
{
|
||||
long total = 0;
|
||||
for (Event e : events)
|
||||
{
|
||||
if (e.timeNs != -1)
|
||||
{
|
||||
total += e.timeNs;
|
||||
}
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (Event e : events)
|
||||
{
|
||||
if (e.timeNs != -1)
|
||||
{
|
||||
sb.append(e.name).append(": ").append(Duration.ofNanos(e.timeNs)).append('\n');
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.append(e.name).append(": ").append("N/A").append('\n');
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -41,7 +41,7 @@ public class RollingAverage
|
||||
// input //
|
||||
//=======//
|
||||
|
||||
public void addValue(double value)
|
||||
public void add(double value)
|
||||
{
|
||||
this.arrayLock.lock();
|
||||
try
|
||||
|
||||
+2
-2
@@ -19,7 +19,7 @@
|
||||
|
||||
package com.seibel.distanthorizons.core.util.objects;
|
||||
|
||||
import com.seibel.distanthorizons.core.util.LodUtil;
|
||||
import com.seibel.distanthorizons.core.util.ExceptionUtil;
|
||||
|
||||
import java.util.concurrent.CompletionException;
|
||||
|
||||
@@ -76,7 +76,7 @@ public class UncheckedInterruptedException extends RuntimeException
|
||||
}
|
||||
public static boolean isInterrupt(Throwable t)
|
||||
{
|
||||
Throwable unwrapped = LodUtil.ensureUnwrap(t);
|
||||
Throwable unwrapped = ExceptionUtil.ensureUnwrap(t);
|
||||
return unwrapped instanceof InterruptedException || unwrapped instanceof UncheckedInterruptedException;
|
||||
}
|
||||
|
||||
|
||||
+41
-11
@@ -20,9 +20,11 @@
|
||||
package com.seibel.distanthorizons.core.util.objects.dataStreams;
|
||||
|
||||
import com.github.luben.zstd.RecyclingBufferPool;
|
||||
import com.github.luben.zstd.Zstd;
|
||||
import com.github.luben.zstd.ZstdInputStream;
|
||||
import com.seibel.distanthorizons.api.enums.config.EDhApiDataCompressionMode;
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import it.unimi.dsi.fastutil.bytes.ByteArrayList;
|
||||
import net.jpountz.lz4.LZ4FrameInputStream;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import com.seibel.distanthorizons.core.logging.DhLogger;
|
||||
@@ -48,11 +50,34 @@ public class DhDataInputStream extends DataInputStream
|
||||
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
|
||||
|
||||
|
||||
public DhDataInputStream(InputStream stream, EDhApiDataCompressionMode compressionMode) throws IOException
|
||||
{
|
||||
super(warpStream(new BufferedInputStream(stream), compressionMode));
|
||||
|
||||
//=============//
|
||||
// constructor //
|
||||
//=============//
|
||||
|
||||
public static DhDataInputStream create(ByteArrayList byteArrayList, EDhApiDataCompressionMode compressionMode) throws IOException
|
||||
{ return create(byteArrayList.toByteArray(), compressionMode); }
|
||||
public static DhDataInputStream create(byte[] byteArray, EDhApiDataCompressionMode compressionMode) throws IOException
|
||||
{
|
||||
// Z_Std handling compression outside the stream provides a significant performance boost
|
||||
ByteArrayInputStream byteArrayInputStream;
|
||||
if (compressionMode == EDhApiDataCompressionMode.Z_STD)
|
||||
{
|
||||
byteArrayInputStream = new ByteArrayInputStream(Zstd.decompress(byteArray));
|
||||
}
|
||||
else
|
||||
{
|
||||
byteArrayInputStream = new ByteArrayInputStream(byteArray);
|
||||
}
|
||||
|
||||
return new DhDataInputStream(byteArrayInputStream, compressionMode);
|
||||
}
|
||||
private static InputStream warpStream(InputStream stream, EDhApiDataCompressionMode compressionMode) throws IOException
|
||||
private DhDataInputStream(ByteArrayInputStream stream, EDhApiDataCompressionMode compressionMode) throws IOException
|
||||
{
|
||||
super(warpStream(stream, compressionMode));
|
||||
}
|
||||
@SuppressWarnings("deprecation")
|
||||
private static InputStream warpStream(ByteArrayInputStream stream, EDhApiDataCompressionMode compressionMode) throws IOException
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -63,15 +88,19 @@ public class DhDataInputStream extends DataInputStream
|
||||
case LZ4:
|
||||
return new LZ4FrameInputStream(stream);
|
||||
case Z_STD:
|
||||
return new ZstdInputStream(stream, RecyclingBufferPool.INSTANCE);
|
||||
// ZStd compression should be handled before this point
|
||||
// just return the stream
|
||||
return stream;
|
||||
case LZMA2:
|
||||
// using an array cache significantly reduces GC pressure
|
||||
ResettableArrayCache arrayCache = LZMA_RESETTABLE_ARRAY_CACHE_GETTER.get();
|
||||
arrayCache.reset();
|
||||
|
||||
// Note: all LZMA/XZ compressors can be decompressed using this same InputStream
|
||||
return new XZInputStream(stream, arrayCache);
|
||||
|
||||
case Z_STD_STREAM: // deprecated, only used for legacy support
|
||||
return new ZstdInputStream(stream, RecyclingBufferPool.INSTANCE);
|
||||
|
||||
default:
|
||||
throw new IllegalArgumentException("No compressor defined for [" + compressionMode + "]");
|
||||
}
|
||||
@@ -85,6 +114,11 @@ public class DhDataInputStream extends DataInputStream
|
||||
}
|
||||
|
||||
|
||||
|
||||
//================//
|
||||
// base overrides //
|
||||
//================//
|
||||
|
||||
@Override
|
||||
public int read() throws IOException
|
||||
{
|
||||
@@ -114,10 +148,6 @@ public class DhDataInputStream extends DataInputStream
|
||||
}
|
||||
}
|
||||
|
||||
// TODO at one point closing the streams caused errors, is that due to a bug with LZMA streams or some bug in DH's code that was since fixed?
|
||||
// if streams aren't closed that cause cause higher-than-expected native memory use if the GC decides
|
||||
// it doesn't want to clear the stream objects
|
||||
//@Override
|
||||
//public void close() throws IOException { /* Do nothing. */ }
|
||||
|
||||
|
||||
}
|
||||
|
||||
+56
-12
@@ -19,9 +19,11 @@
|
||||
|
||||
package com.seibel.distanthorizons.core.util.objects.dataStreams;
|
||||
|
||||
import com.github.luben.zstd.Zstd;
|
||||
import com.github.luben.zstd.ZstdOutputStream;
|
||||
import com.seibel.distanthorizons.api.enums.config.EDhApiDataCompressionMode;
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import it.unimi.dsi.fastutil.bytes.ByteArrayList;
|
||||
import net.jpountz.lz4.LZ4Factory;
|
||||
import net.jpountz.lz4.LZ4FrameOutputStream;
|
||||
import net.jpountz.xxhash.XXHashFactory;
|
||||
@@ -41,23 +43,38 @@ public class DhDataOutputStream extends DataOutputStream
|
||||
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
|
||||
private static final ThreadLocal<ResettableArrayCache> LZMA_RESETTABLE_ARRAY_CACHE_GETTER = ThreadLocal.withInitial(() -> new ResettableArrayCache(new LzmaArrayCache()));
|
||||
|
||||
private final ByteArrayList outputByteArray;
|
||||
private final ByteArrayOutputStream wrappedByteStream;
|
||||
private final EDhApiDataCompressionMode compressionMode;
|
||||
|
||||
|
||||
public DhDataOutputStream(OutputStream stream, EDhApiDataCompressionMode compressionMode) throws IOException
|
||||
|
||||
//=============//
|
||||
// constructor //
|
||||
//=============//
|
||||
|
||||
/**
|
||||
* @param outputByteArray where the contents of this stream will be written to when done
|
||||
*/
|
||||
public static DhDataOutputStream create(EDhApiDataCompressionMode compressionMode, ByteArrayList outputByteArray) throws IOException
|
||||
{ return new DhDataOutputStream(new ByteArrayOutputStream(), compressionMode, outputByteArray); }
|
||||
private DhDataOutputStream(ByteArrayOutputStream wrappedByteStream, EDhApiDataCompressionMode compressionMode, ByteArrayList outputByteArray) throws IOException
|
||||
{
|
||||
super(warpStream(new BufferedOutputStream(stream), compressionMode));
|
||||
super(warpStream(wrappedByteStream, compressionMode));
|
||||
|
||||
this.wrappedByteStream = wrappedByteStream;
|
||||
this.outputByteArray = outputByteArray;
|
||||
this.compressionMode = compressionMode;
|
||||
}
|
||||
private static OutputStream warpStream(OutputStream stream, EDhApiDataCompressionMode compressionMode) throws IOException
|
||||
@SuppressWarnings("deprecation")
|
||||
private static OutputStream warpStream(ByteArrayOutputStream stream, EDhApiDataCompressionMode compressionMode) throws IOException
|
||||
{
|
||||
try
|
||||
{
|
||||
switch (compressionMode)
|
||||
{
|
||||
case UNCOMPRESSED:
|
||||
return stream;
|
||||
|
||||
case Z_STD:
|
||||
return new ZstdOutputStream(stream, 3, true, true);
|
||||
return stream;
|
||||
case LZ4:
|
||||
return new LZ4FrameOutputStream(stream,
|
||||
LZ4FrameOutputStream.BLOCKSIZE.SIZE_64KB, -1L,
|
||||
@@ -68,6 +85,10 @@ public class DhDataOutputStream extends DataOutputStream
|
||||
//LZ4Factory.nativeInstance().fastCompressor(),
|
||||
//XXHashFactory.nativeInstance().hash32(),
|
||||
LZ4FrameOutputStream.FLG.Bits.BLOCK_INDEPENDENCE);
|
||||
case Z_STD:
|
||||
// ZStd compression should be handled after the stream is closed
|
||||
// just return the stream
|
||||
return stream;
|
||||
case LZMA2:
|
||||
// using an array cache significantly reduces GC pressure
|
||||
ResettableArrayCache arrayCache = LZMA_RESETTABLE_ARRAY_CACHE_GETTER.get();
|
||||
@@ -77,6 +98,10 @@ public class DhDataOutputStream extends DataOutputStream
|
||||
return new XZOutputStream(stream, new LZMA2Options(3),
|
||||
XZ.CHECK_CRC64, arrayCache);
|
||||
|
||||
case Z_STD_STREAM: // deprecated, only used for legacy support
|
||||
//return new ZstdOutputStream(stream, 3, true, true);
|
||||
throw new UnsupportedOperationException("Z_STD_STREAM is deprecated and shouldn't be used for encoding. The faster block encoding format should be used instead.");
|
||||
|
||||
default:
|
||||
throw new IllegalArgumentException("No compressor defined for ["+compressionMode+"]");
|
||||
}
|
||||
@@ -90,10 +115,29 @@ public class DhDataOutputStream extends DataOutputStream
|
||||
}
|
||||
|
||||
|
||||
// TODO at one point closing the streams caused errors, is that due to a bug with LZMA streams or some bug in DH's code that was since fixed?
|
||||
// if streams aren't closed that cause cause higher-than-expected native memory use if the GC decides
|
||||
// it doesn't want to clear the stream objects
|
||||
//@Override
|
||||
//public void close() throws IOException { /* Do nothing. */ }
|
||||
|
||||
//================//
|
||||
// base overrides //
|
||||
//================//
|
||||
|
||||
@Override
|
||||
public void close() throws IOException
|
||||
{
|
||||
super.close();
|
||||
|
||||
|
||||
this.outputByteArray.clear();
|
||||
if (this.compressionMode == EDhApiDataCompressionMode.Z_STD)
|
||||
{
|
||||
this.outputByteArray.addElements(0, Zstd.compress(this.wrappedByteStream.toByteArray(), 3));
|
||||
}
|
||||
else
|
||||
{
|
||||
this.outputByteArray.addElements(0, this.wrappedByteStream.toByteArray());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
+2
-2
@@ -190,7 +190,7 @@ public class PriorityTaskPicker
|
||||
{
|
||||
return new RateLimitedThreadPoolExecutor(
|
||||
Config.Common.MultiThreading.numberOfThreads.get(),
|
||||
new DhThreadFactory(this.name, Thread.MIN_PRIORITY, false),
|
||||
new DhThreadFactory(this.name, Config.Common.MultiThreading.threadPriority.get(), false),
|
||||
new ArrayBlockingQueue<>(Runtime.getRuntime().availableProcessors())
|
||||
);
|
||||
}
|
||||
@@ -320,7 +320,7 @@ public class PriorityTaskPicker
|
||||
finally
|
||||
{
|
||||
long timeElapsed = System.nanoTime() - startTime;
|
||||
this.executor.runTimeInMsRollingAverage.addValue(TimeUnit.NANOSECONDS.toMillis(timeElapsed));
|
||||
this.executor.runTimeInMsRollingAverage.add(TimeUnit.NANOSECONDS.toMillis(timeElapsed));
|
||||
|
||||
// Update variables related to task status
|
||||
this.parentTaskPicker.occupiedThreadsRef.getAndDecrement();
|
||||
|
||||
+2
-2
@@ -25,7 +25,7 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrappe
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.worldGeneration.AbstractBatchGenerationEnvironmentWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.worldGeneration.IBatchGeneratorEnvironmentWrapper;
|
||||
import com.seibel.distanthorizons.coreapi.interfaces.dependencyInjection.IBindable;
|
||||
|
||||
import java.io.IOException;
|
||||
@@ -39,7 +39,7 @@ import java.util.HashSet;
|
||||
*/
|
||||
public interface IWrapperFactory extends IDhApiWrapperFactory, IBindable
|
||||
{
|
||||
AbstractBatchGenerationEnvironmentWrapper createBatchGenerator(IDhLevel targetLevel);
|
||||
IBatchGeneratorEnvironmentWrapper createBatchGenerator(IDhLevel targetLevel);
|
||||
|
||||
IBiomeWrapper deserializeBiomeWrapper(String str, ILevelWrapper levelWrapper) throws IOException;
|
||||
IBiomeWrapper getPlainsBiomeWrapper(ILevelWrapper levelWrapper); // TODO it would be nice to remove the level wrapper if possible to put this in line with getAirBlockStateWrapper() but it isn't necessary
|
||||
|
||||
+6
@@ -158,6 +158,12 @@ public class ChunkLightStorage
|
||||
lightSection.set(x, y, z, lightLevel);
|
||||
}
|
||||
|
||||
public boolean isEmpty()
|
||||
{
|
||||
return this.lightSections == null
|
||||
|| this.lightSections.length == 0;
|
||||
}
|
||||
|
||||
public void clear()
|
||||
{
|
||||
if (this.lightSections != null)
|
||||
|
||||
+2
-27
@@ -17,34 +17,9 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.seibel.distanthorizons.core.enums.worldGeneration;
|
||||
package com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor;
|
||||
|
||||
/**
|
||||
* MULTI_THREADED, <br>
|
||||
* SINGLE_THREADED, <br>
|
||||
* SERVER_THREAD, <br>
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 7-25-2022
|
||||
*/
|
||||
public enum EWorldGenThreadMode
|
||||
public interface IC2meAccessor extends IModAccessor
|
||||
{
|
||||
/**
|
||||
* This world generator can be run on an unlimited number
|
||||
* of concurrent threads.
|
||||
*/
|
||||
MULTI_THREADED,
|
||||
|
||||
/**
|
||||
* This world generator can only be run on one thread at
|
||||
* a time, however that thread can run concurrently
|
||||
* to Minecraft's server thread.
|
||||
*/
|
||||
SINGLE_THREADED,
|
||||
|
||||
/**
|
||||
* This world generator can only be run on Minecraft's
|
||||
* server thread.
|
||||
*/
|
||||
SERVER_THREAD,
|
||||
}
|
||||
+7
-11
@@ -21,25 +21,21 @@ package com.seibel.distanthorizons.core.wrapperInterfaces.worldGeneration;
|
||||
|
||||
import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiDistantGeneratorMode;
|
||||
import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiWorldGenerationStep;
|
||||
import com.seibel.distanthorizons.core.level.IDhLevel;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public abstract class AbstractBatchGenerationEnvironmentWrapper
|
||||
public interface IBatchGeneratorEnvironmentWrapper extends AutoCloseable
|
||||
{
|
||||
public AbstractBatchGenerationEnvironmentWrapper(IDhLevel level) { }
|
||||
void updateAllFutures();
|
||||
|
||||
public abstract void updateAllFutures();
|
||||
|
||||
public abstract int getEventCount();
|
||||
|
||||
public abstract void stop();
|
||||
|
||||
public abstract CompletableFuture<Void> generateChunks(
|
||||
int minX, int minZ, int genSize, EDhApiDistantGeneratorMode generatorMode, EDhApiWorldGenerationStep targetStep,
|
||||
CompletableFuture<Void> queueGenEvent(
|
||||
int minX, int minZ, int genSize,
|
||||
EDhApiDistantGeneratorMode generatorMode, EDhApiWorldGenerationStep targetStep,
|
||||
ExecutorService worldGeneratorThreadPool, Consumer<IChunkWrapper> resultConsumer);
|
||||
|
||||
void close();
|
||||
|
||||
}
|
||||
@@ -678,6 +678,10 @@
|
||||
"How many threads DH will use.",
|
||||
"distanthorizons.config.common.multiThreading.threadRunTimeRatio":
|
||||
"Runtime % for threads",
|
||||
"distanthorizons.config.common.multiThreading.threadPriority":
|
||||
"Thread Priority",
|
||||
"distanthorizons.config.common.multiThreading.threadPriority.@tooltip":
|
||||
"What Java thread priority should DH's primary thread pools run with?\n\nYou probably don't need to change this unless you are also \nrunning C2ME and are seeing thread starvation in either C2ME or DH.",
|
||||
|
||||
|
||||
|
||||
@@ -720,6 +724,8 @@
|
||||
"Show Replay Warning",
|
||||
"distanthorizons.config.common.logging.warning.showUpdateQueueOverloadedChatWarning":
|
||||
"Show Update Queue Overloaded Warning",
|
||||
"distanthorizons.config.common.logging.warning.showSlowWorldGenSettingWarnings":
|
||||
"Show Slow World Gen Warnings",
|
||||
"distanthorizons.config.common.logging.warning.showModCompatibilityWarningsOnStartup":
|
||||
"Show Mod Compatibility Warnings",
|
||||
|
||||
@@ -975,7 +981,9 @@
|
||||
"distanthorizons.config.enum.EDhApiDataCompressionMode.LZ4":
|
||||
"Fastest/Big - LZ4",
|
||||
"distanthorizons.config.enum.EDhApiDataCompressionMode.Z_STD":
|
||||
"Fast/Small - Z_STD",
|
||||
"Fastest/Small - Z_STD - Block",
|
||||
"distanthorizons.config.enum.EDhApiDataCompressionMode.Z_STD_STREAM":
|
||||
"Fast/Small - Z_STD - Stream",
|
||||
"distanthorizons.config.enum.EDhApiDataCompressionMode.LZMA2":
|
||||
"Slow/Smallest - LZMA2",
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ import com.seibel.distanthorizons.core.sql.dto.FullDataSourceV2DTO;
|
||||
import com.seibel.distanthorizons.core.sql.dto.util.VarintUtil;
|
||||
import com.seibel.distanthorizons.core.util.objects.dataStreams.DhDataInputStream;
|
||||
import com.seibel.distanthorizons.core.util.objects.dataStreams.DhDataOutputStream;
|
||||
import it.unimi.dsi.fastutil.bytes.ByteArrayList;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
@@ -43,12 +44,12 @@ public class VarintTest
|
||||
// zig zag encoding is needed for varint handling, so test it first
|
||||
for (int i = -256; i < 256; i++)
|
||||
{
|
||||
//testZigZagEncoding(i);
|
||||
testZigZagEncoding(i);
|
||||
}
|
||||
|
||||
for (int i = -256; i < 256; i++)
|
||||
{
|
||||
//testSingleVarint(i);
|
||||
testSingleVarint(i);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,8 +63,8 @@ public class VarintTest
|
||||
private static void testSingleVarint(int value)
|
||||
{
|
||||
// write to stream
|
||||
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
|
||||
try (DhDataOutputStream outputStream = new DhDataOutputStream(byteArrayOutputStream, EDhApiDataCompressionMode.UNCOMPRESSED))
|
||||
ByteArrayList byteArrayList = new ByteArrayList();
|
||||
try (DhDataOutputStream outputStream = DhDataOutputStream.create(EDhApiDataCompressionMode.UNCOMPRESSED, byteArrayList))
|
||||
{
|
||||
int encodedValue = VarintUtil.zigzagEncode(value);
|
||||
VarintUtil.writeVarint(outputStream, encodedValue); // varint requires zig-zag encoding to function
|
||||
@@ -76,9 +77,7 @@ public class VarintTest
|
||||
|
||||
|
||||
// read stream
|
||||
byte[] byteArray = byteArrayOutputStream.toByteArray();
|
||||
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArray);
|
||||
try (DhDataInputStream inputStream = new DhDataInputStream(byteArrayInputStream, EDhApiDataCompressionMode.UNCOMPRESSED))
|
||||
try (DhDataInputStream inputStream = DhDataInputStream.create(byteArrayList, EDhApiDataCompressionMode.UNCOMPRESSED))
|
||||
{
|
||||
int encodedValue = VarintUtil.readVarint(inputStream);
|
||||
int decodedValue = VarintUtil.zigzagDecode(encodedValue);
|
||||
|
||||
Reference in New Issue
Block a user