Attempt to improve LOD building speed and reduce broken lighting on servers
This commit is contained in:
@@ -24,6 +24,7 @@ import com.seibel.distanthorizons.core.config.Config;
|
||||
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
|
||||
import com.seibel.distanthorizons.core.generation.DhLightingEngine;
|
||||
import com.seibel.distanthorizons.core.level.IDhLevel;
|
||||
import com.seibel.distanthorizons.core.level.IDhServerLevel;
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.core.logging.f3.F3Screen;
|
||||
import com.seibel.distanthorizons.core.pos.DhBlockPos2D;
|
||||
@@ -31,6 +32,7 @@ import com.seibel.distanthorizons.core.pos.DhChunkPos;
|
||||
import com.seibel.distanthorizons.core.render.renderer.DebugRenderer;
|
||||
import com.seibel.distanthorizons.core.sql.dto.BeaconBeamDTO;
|
||||
import com.seibel.distanthorizons.core.sql.repo.AbstractDhRepo;
|
||||
import com.seibel.distanthorizons.core.util.LodUtil;
|
||||
import com.seibel.distanthorizons.core.util.TimerUtil;
|
||||
import com.seibel.distanthorizons.core.util.objects.Pair;
|
||||
import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil;
|
||||
@@ -46,6 +48,7 @@ import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.RejectedExecutionException;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/** Contains code and variables used by both {@link ClientApi} and {@link ServerApi} */
|
||||
public class SharedApi
|
||||
@@ -328,29 +331,44 @@ public class SharedApi
|
||||
}
|
||||
|
||||
|
||||
// Save or populate the chunk wrapper's lighting
|
||||
// this is done so we don't have to worry about MC unloading the lighting data for this chunk
|
||||
boolean onlyUseDhLighting = Config.Client.Advanced.LodBuilding.onlyUseDhLightingEngine.get();
|
||||
if (!onlyUseDhLighting && chunkWrapper.isLightCorrect())
|
||||
// chunk light baking is disabled since profiling revealed it used
|
||||
// roughly the same amount of time as generating the lighting ourselves and
|
||||
// was much more likely to have issues with corrupt (all black or all bright) chunks
|
||||
boolean tryUsingMcLightingEngine = false;
|
||||
if (tryUsingMcLightingEngine)
|
||||
{
|
||||
try
|
||||
// Save or populate the chunk wrapper's lighting
|
||||
// this is done so we don't have to worry about MC unloading the lighting data for this chunk
|
||||
boolean chunkLightPopulated = false;
|
||||
boolean onlyUseDhLighting = Config.Client.Advanced.LodBuilding.onlyUseDhLightingEngine.get();
|
||||
if (!onlyUseDhLighting && chunkWrapper.isLightCorrect())
|
||||
{
|
||||
// If MC's lighting engine isn't thread safe this may cause the server thread to lag
|
||||
chunkWrapper.bakeDhLightingUsingMcLightingEngine();
|
||||
chunkLightPopulated = chunkWrapper.bakeDhLightingUsingMcLightingEngine(dhLevel.getLevelWrapper());
|
||||
if (!chunkLightPopulated)
|
||||
{
|
||||
// clear any existing data to prevent partial or corrupt lighting
|
||||
// when re-generating it
|
||||
chunkWrapper.clearDhBlockLighting();
|
||||
chunkWrapper.clearDhSkyLighting();
|
||||
}
|
||||
}
|
||||
catch (IllegalStateException e)
|
||||
|
||||
// something went wrong during the baking process so we have to generate the lighting ourselves
|
||||
if (!chunkLightPopulated)
|
||||
{
|
||||
LOGGER.warn("Chunk light baking error: " + e.getMessage(), e);
|
||||
DhLightingEngine.INSTANCE.lightChunk(chunkWrapper, nearbyChunkList, dhLevel.hasSkyLight() ? LodUtil.MAX_MC_LIGHT : LodUtil.MIN_MC_LIGHT);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// generate the chunk's lighting, using neighboring chunks if present
|
||||
DhLightingEngine.INSTANCE.lightChunk(chunkWrapper, nearbyChunkList, dhLevel.hasSkyLight() ? 15 : 0);
|
||||
DhLightingEngine.INSTANCE.lightChunk(chunkWrapper, nearbyChunkList, dhLevel.hasSkyLight() ? LodUtil.MAX_MC_LIGHT : LodUtil.MIN_MC_LIGHT);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// get this chunk's active beacons
|
||||
List<BeaconBeamDTO> beaconBeamList = chunkWrapper.getAllActiveBeacons(nearbyChunkList);
|
||||
dhLevel.setBeaconBeamsForChunk(chunkWrapper.getChunkPos(), beaconBeamList);
|
||||
|
||||
@@ -781,8 +781,10 @@ public class Config
|
||||
+ "")
|
||||
.build();
|
||||
|
||||
@Deprecated
|
||||
public static ConfigEntry<Boolean> onlyUseDhLightingEngine = new ConfigEntry.Builder<Boolean>()
|
||||
.set(false)
|
||||
.setAppearance(EConfigEntryAppearance.ONLY_IN_API)
|
||||
.comment(""
|
||||
+ "If false LODs will be lit by Minecraft's lighting engine when possible \n"
|
||||
+ "and fall back to the DH lighting engine only when necessary. \n"
|
||||
|
||||
+19
@@ -141,6 +141,25 @@ public class ChunkLightStorage
|
||||
lightSection.set(x, y, z, lightLevel);
|
||||
}
|
||||
|
||||
public void clear()
|
||||
{
|
||||
if (this.lightSections != null)
|
||||
{
|
||||
for (int i = 0; i < this.lightSections.length; i++)
|
||||
{
|
||||
LightSection section = this.lightSections[i];
|
||||
if (section != null)
|
||||
{
|
||||
section.constantValue = LodUtil.MIN_MC_LIGHT;
|
||||
if (section.data != null)
|
||||
{
|
||||
Arrays.fill(section.data, 0L);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//================//
|
||||
|
||||
+81
-10
@@ -25,6 +25,7 @@ import com.seibel.distanthorizons.core.pos.DhBlockPos2D;
|
||||
import com.seibel.distanthorizons.core.pos.DhChunkPos;
|
||||
import com.seibel.distanthorizons.core.sql.dto.BeaconBeamDTO;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
|
||||
import com.seibel.distanthorizons.coreapi.ModInfo;
|
||||
import com.seibel.distanthorizons.coreapi.interfaces.dependencyInjection.IBindable;
|
||||
import com.seibel.distanthorizons.core.util.LodUtil;
|
||||
@@ -79,9 +80,11 @@ public interface IChunkWrapper extends IBindable
|
||||
|
||||
int getDhSkyLight(int relX, int relY, int relZ);
|
||||
void setDhSkyLight(int relX, int relY, int relZ, int lightValue);
|
||||
void clearDhSkyLighting();
|
||||
|
||||
int getDhBlockLight(int relX, int relY, int relZ);
|
||||
void setDhBlockLight(int relX, int relY, int relZ, int lightValue);
|
||||
void clearDhBlockLighting();
|
||||
|
||||
int getBlockLight(int relX, int relY, int relZ);
|
||||
int getSkyLight(int relX, int relY, int relZ);
|
||||
@@ -150,29 +153,77 @@ public interface IChunkWrapper extends IBindable
|
||||
* This is generally done in cases where MC's lighting is correct now, but may not be later (like when a chunk is unloading).
|
||||
*
|
||||
* @throws IllegalStateException if the chunk's lighting isn't valid. This is done to prevent accidentally baking broken lighting.
|
||||
* @return true if the chunk's lighting was successfully populated, false otherwise
|
||||
*/
|
||||
default void bakeDhLightingUsingMcLightingEngine() throws IllegalStateException
|
||||
@Deprecated
|
||||
default boolean bakeDhLightingUsingMcLightingEngine(ILevelWrapper levelWrapper) throws IllegalStateException
|
||||
{
|
||||
if (!this.isLightCorrect())
|
||||
{
|
||||
throw new IllegalStateException("Unable to bake lighting for for chunk [" + this.getChunkPos() + "], Minecraft lighting not valid.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// get the lighting for every relative block pos
|
||||
//=======================//
|
||||
// get lighting for each //
|
||||
// relative block pos //
|
||||
//=======================//
|
||||
|
||||
boolean lightingFound = false;
|
||||
// if the level doesn't have sky lights, then this check can be ignored
|
||||
// since all sky light values will be 0 anyway
|
||||
boolean skyLightingFound = !levelWrapper.hasSkyLight();
|
||||
|
||||
for (int relX = 0; relX < LodUtil.CHUNK_WIDTH; relX++)
|
||||
{
|
||||
for (int relZ = 0; relZ < LodUtil.CHUNK_WIDTH; relZ++)
|
||||
{
|
||||
for (int y = this.getMinBuildHeight(); y < this.getMaxBuildHeight(); y++)
|
||||
{
|
||||
this.setDhSkyLight(relX, y, relZ, this.getSkyLight(relX, y, relZ));
|
||||
this.setDhBlockLight(relX, y, relZ, this.getBlockLight(relX, y, relZ));
|
||||
int skyLight = this.getSkyLight(relX, y, relZ);
|
||||
this.setDhSkyLight(relX, y, relZ, skyLight);
|
||||
int blockLight = this.getBlockLight(relX, y, relZ);
|
||||
this.setDhBlockLight(relX, y, relZ, blockLight);
|
||||
|
||||
// MC defaults to max sky light and no block light, including underground blocks.
|
||||
// If any position has something different then those default values, it's likely that the
|
||||
// lighting was properly populated for at least part of the chunk
|
||||
if (!lightingFound &&
|
||||
(skyLight != LodUtil.MAX_MC_LIGHT || blockLight != LodUtil.MIN_MC_LIGHT))
|
||||
{
|
||||
lightingFound = true;
|
||||
}
|
||||
|
||||
if (!skyLightingFound
|
||||
&& skyLight != LodUtil.MIN_MC_LIGHT)
|
||||
{
|
||||
skyLightingFound = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//=================//
|
||||
// validate result //
|
||||
//=================//
|
||||
|
||||
// if no lighting was found or the sky is always black, the lighting is likely broken
|
||||
if (!lightingFound || !skyLightingFound
|
||||
// if lighting is no longer correct or doesn't match the saved values
|
||||
// its very likely it broke halfway through and will need regenerating
|
||||
|| !this.isLightCorrect()
|
||||
|| this.getSkyLight(0, 0, 0) != this.getDhSkyLight(0,0,0)
|
||||
|| this.getBlockLight(0, 0, 0) != this.getDhBlockLight(0,0,0))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// lighting is valid
|
||||
this.setIsDhLightCorrect(true);
|
||||
this.setUseDhLighting(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -230,17 +281,37 @@ public interface IChunkWrapper extends IBindable
|
||||
int primeBlockMultiplier = 227;
|
||||
int primeBiomeMultiplier = 701;
|
||||
|
||||
int minBuildHeight = this.getMinBuildHeight();
|
||||
int maxBuildHeight = this.getMaxBuildHeight();
|
||||
int minBuildHeight = this.getMaxNonEmptyHeight();
|
||||
int maxBuildHeight = this.getMinNonEmptyHeight();
|
||||
|
||||
|
||||
// most blocks (only some blocks are sampled since checking every block is a very slow operation)
|
||||
for (int x = 0; x < LodUtil.CHUNK_WIDTH; x+=2)
|
||||
{
|
||||
for (int z = 0; z < LodUtil.CHUNK_WIDTH; z+=2)
|
||||
{
|
||||
for (int y = minBuildHeight; y < maxBuildHeight; y+=8)
|
||||
{
|
||||
hash = (hash * primeBlockMultiplier) + this.getBlockState(x, y, z).hashCode();
|
||||
hash = (hash * primeBiomeMultiplier) + this.getBiome(x, y, z).hashCode();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// surface (this should cover most cases for when users modify chunks)
|
||||
for (int x = 0; x < LodUtil.CHUNK_WIDTH; x++)
|
||||
{
|
||||
for (int z = 0; z < LodUtil.CHUNK_WIDTH; z++)
|
||||
{
|
||||
for (int y = minBuildHeight; y < maxBuildHeight; y++)
|
||||
int lightBlockingY = this.getLightBlockingHeightMapValue(x, z);
|
||||
hash = (hash * primeBlockMultiplier) + this.getBlockState(x, lightBlockingY, z).hashCode();
|
||||
hash = (hash * primeBiomeMultiplier) + this.getBiome(x, lightBlockingY, z).hashCode();
|
||||
|
||||
int solidY = this.getSolidHeightMapValue(x, z);
|
||||
if (solidY != lightBlockingY)
|
||||
{
|
||||
hash = (hash * primeBlockMultiplier) + this.getBlockState(x, y, z).hashCode();
|
||||
hash = (hash * primeBiomeMultiplier) + this.getBiome(x, y, z).hashCode();
|
||||
hash = (hash * primeBlockMultiplier) + this.getBlockState(x, solidY, z).hashCode();
|
||||
hash = (hash * primeBiomeMultiplier) + this.getBiome(x, solidY, z).hashCode();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -350,6 +350,8 @@ public class LightingTestChunkWrapper implements IChunkWrapper
|
||||
}
|
||||
return this.blockLightStorage;
|
||||
}
|
||||
@Override
|
||||
public void clearDhBlockLighting() { throw new UnsupportedOperationException("Not implemented"); }
|
||||
|
||||
|
||||
@Override
|
||||
@@ -364,6 +366,8 @@ public class LightingTestChunkWrapper implements IChunkWrapper
|
||||
this.throwIndexOutOfBoundsIfRelativePosOutsideChunkBounds(relX, y, relZ);
|
||||
this.getSkyLightStorage().set(relX, y, relZ, lightValue);
|
||||
}
|
||||
@Override
|
||||
public void clearDhSkyLighting() { throw new UnsupportedOperationException("Not implemented"); }
|
||||
|
||||
private ChunkLightStorage getSkyLightStorage()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user