diff --git a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/chunk/ChunkLightStorage.java b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/chunk/ChunkLightStorage.java
index ff5a0014e..8d0a465c5 100644
--- a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/chunk/ChunkLightStorage.java
+++ b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/chunk/ChunkLightStorage.java
@@ -1,146 +1,216 @@
+/*
+ * This file is part of the Distant Horizons mod
+ * licensed under the GNU LGPL v3 License.
+ *
+ * Copyright (C) 2020-2023 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 .
+ */
+
package com.seibel.distanthorizons.common.wrappers.chunk;
+import com.seibel.distanthorizons.coreapi.util.BitShiftUtil;
+
import java.util.ArrayList;
import java.util.Arrays;
/**
-compact, efficient storage for light levels.
-all blocks only take up 4 bits in total,
-and if a 16x16x16 area is detected to have the same light level in all positions,
-then we store a single byte for that light level, instead of 2 kilobytes.
-
-@author Builderb0y
+ * Compact, efficient storage for light levels.
+ * all blocks only take up 4 bits in total,
+ * and if a 16x16x16 area is detected to have the same light level in all positions,
+ * then we store a single byte for that light level, instead of 2 kilobytes.
+ *
+ * @author Builderb0y
*/
-public class ChunkLightStorage {
-
+public class ChunkLightStorage
+{
/** the minimum Y level in the chunk which this storage is storing light levels for (inclusive). */
public int minY;
/** the maximum Y level in the chunk which this storage is storing light levels for (exclusive). */
public int maxY;
+
/** the data stored in this storage, split up into 16x16x16 areas. */
- public Section[] sections;
-
- public ChunkLightStorage(int minY, int maxY) {
+ public LightSection[] lightSections;
+
+
+
+ public ChunkLightStorage(int minY, int maxY)
+ {
this.minY = minY;
this.maxY = maxY;
}
-
- public int get(int x, int y, int z) {
- if (y < this.minY) return 0;
- if (y >= this.maxY) return 15;
- if (this.sections != null) {
- Section section = this.sections[(y - this.minY) >> 4];
- if (section != null) {
- return section.get(x, y, z);
+
+
+
+ public int get(int x, int y, int z)
+ {
+ if (y < this.minY)
+ {
+ return 0;
+ }
+ if (y >= this.maxY)
+ {
+ return 15;
+ }
+
+ if (this.lightSections != null)
+ {
+ LightSection lightSection = this.lightSections[BitShiftUtil.divideByPowerOfTwo(y - this.minY, 4)];
+ if (lightSection != null)
+ {
+ return lightSection.get(x, y, z);
}
}
+
return 0;
}
-
- public void set(int x, int y, int z, int lightLevel) {
- if (y < this.minY || y >= this.maxY) return;
+
+ public void set(int x, int y, int z, int lightLevel)
+ {
+ if (y < this.minY || y >= this.maxY)
+ {
+ return;
+ }
+
//populate array if it doesn't exist.
- if (this.sections == null) {
- this.sections = new Section[(this.maxY - this.minY) >> 4];
+ if (this.lightSections == null)
+ {
+ this.lightSections = new LightSection[BitShiftUtil.divideByPowerOfTwo(this.maxY - this.minY, 4)];
}
+
int index = (y - this.minY) >> 4;
- Section section = this.sections[index];
- //populate section in array if it doesn't exist.
- if (section == null) {
- section = new Section(0);
- this.sections[index] = section;
+ LightSection lightSection = this.lightSections[index];
+
+ //populate lightSection in array if it doesn't exist.
+ if (lightSection == null)
+ {
+ lightSection = new LightSection(0);
+ this.lightSections[index] = lightSection;
}
- section.set(x, y, z, lightLevel);
+ lightSection.set(x, y, z, lightLevel);
}
-
- public static class Section {
-
+
+
+
+ //================//
+ // helper classes //
+ //================//
+
+ public static class LightSection
+ {
public byte constantValue;
public long[] data;
public short[] counts;
-
- public Section(int initialValue) {
- this.constantValue = (byte)(initialValue);
+
+ public LightSection(int initialValue)
+ {
+ this.constantValue = (byte) (initialValue);
this.counts = new short[16];
this.counts[initialValue] = 16 * 16 * 16;
}
-
- public int get(int x, int y, int z) {
- if (this.constantValue >= 0) return this.constantValue;
+
+ public int get(int x, int y, int z)
+ {
+ if (this.constantValue >= 0)
+ {
+ return this.constantValue;
+ }
+
x &= 15;
y &= 15;
z &= 15;
long bits = this.data[(z << 4) | x];
- return ((int)(bits >>> (y << 2))) & 15;
+ return ((int) (bits >>> (y << 2))) & 15;
}
-
- public void set(int x, int y, int z, int lightLevel) {
+
+ public void set(int x, int y, int z, int lightLevel)
+ {
int oldLightLevel = -1;
- if (this.constantValue >= 0) {
+ if (this.constantValue >= 0)
+ {
oldLightLevel = this.constantValue;
-
+
//if the light level didn't change, then there's nothing to do.
if (oldLightLevel == lightLevel) return;
-
+
//if we are a constant value and need to change something,
//then that means we need to convert to a non-constant value.
this.data = DataRecycler.get();
-
+
//repeat oldLightLevel 16 times as a bit pattern.
long payload = oldLightLevel;
payload |= payload << 4;
payload |= payload << 8;
payload |= payload << 16;
payload |= payload << 32;
-
+
//fill our data with our constant value.
Arrays.fill(this.data, payload);
-
+
//we are no longer a constant value.
this.constantValue = -1;
}
-
+
x &= 15;
y &= 15;
z &= 15;
int index = (z << 4) | x;
long bits = this.data[index];
//if we weren't a constant value before, now's the time to initialize oldLightLevel.
- if (oldLightLevel < 0) {
- oldLightLevel = ((int)(bits >>> (y << 2))) & 15;
+ if (oldLightLevel < 0)
+ {
+ oldLightLevel = ((int) (bits >>> (y << 2))) & 15;
}
//clear the 4 bits that correspond to the light level at x, y, z...
bits &= ~(15L << (y << 2));
//...and then re-populate those bits with the new light level.
- bits |= ((long)(lightLevel)) << (y << 2);
+ bits |= ((long) (lightLevel)) << (y << 2);
//store the updated bits in our data.
this.data[index] = bits;
-
+
//we have one less of the old light level...
this.counts[oldLightLevel]--;
//...and one more of the new level.
//if the number associated with the new level is now 4096 (AKA 16 ^ 3),
//then this implies every position in this section has the same light level,
//and therefore we can convert back to a constant value.
- if (++this.counts[lightLevel] == 4096) {
- this.constantValue = (byte)(lightLevel);
+ if (++this.counts[lightLevel] == 4096)
+ {
+ this.constantValue = (byte) (lightLevel);
DataRecycler.reclaim(this.data);
this.data = null;
}
}
+
}
-
- static class DataRecycler {
-
+
+ static class DataRecycler
+ {
private static final ArrayList recycled = new ArrayList<>(256);
-
- static synchronized long[] get() {
- if (recycled.isEmpty()) return new long[256];
- else return recycled.remove(recycled.size() - 1);
- }
-
- static synchronized void reclaim(long[] data) {
- if (recycled.size() < 256) recycled.add(data);
+
+ static synchronized long[] get()
+ {
+ if (recycled.isEmpty())
+ {
+ return new long[256];
+ }
+ else
+ {
+ return recycled.remove(recycled.size() - 1);
+ }
}
+
+ static synchronized void reclaim(long[] data) { if (recycled.size() < 256) recycled.add(data); }
}
+
}
\ No newline at end of file
diff --git a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/chunk/ChunkWrapper.java b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/chunk/ChunkWrapper.java
index 51103f0b2..755877581 100644
--- a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/chunk/ChunkWrapper.java
+++ b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/chunk/ChunkWrapper.java
@@ -31,7 +31,6 @@ 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.coreapi.util.BitShiftUtil;
import net.minecraft.client.multiplayer.ClientChunkCache;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.core.BlockPos;
@@ -77,8 +76,8 @@ public class ChunkWrapper implements IChunkWrapper
/** only used when connected to a dedicated server */
private boolean isMcClientLightingCorrect = false;
- private ChunkLightStorage blockLightArray;
- private ChunkLightStorage skyLightArray;
+ private ChunkLightStorage blockLightStorage;
+ private ChunkLightStorage skyLightStorage;
private ArrayList blockLightPosList = null;
@@ -254,48 +253,50 @@ public class ChunkWrapper implements IChunkWrapper
#endif
}
- private ChunkLightStorage getBlockLightArray()
- {
- if (this.blockLightArray == null)
- {
- this.blockLightArray = new ChunkLightStorage(this.getMinBuildHeight(), this.getMaxBuildHeight());
- }
- return this.blockLightArray;
- }
-
- private ChunkLightStorage getSkyLightArray()
- {
- if (this.skyLightArray == null)
- {
- this.skyLightArray = new ChunkLightStorage(this.getMinBuildHeight(), this.getMaxBuildHeight());
- }
- return this.skyLightArray;
- }
-
+
@Override
public int getDhBlockLight(int relX, int y, int relZ)
{
this.throwIndexOutOfBoundsIfRelativePosOutsideChunkBounds(relX, y, relZ);
- return this.getBlockLightArray().get(relX, y, relZ);
+ return this.getBlockLightStorage().get(relX, y, relZ);
}
@Override
public void setDhBlockLight(int relX, int y, int relZ, int lightValue)
{
this.throwIndexOutOfBoundsIfRelativePosOutsideChunkBounds(relX, y, relZ);
- this.getBlockLightArray().set(relX, y, relZ, lightValue);
+ this.getBlockLightStorage().set(relX, y, relZ, lightValue);
}
+ private ChunkLightStorage getBlockLightStorage()
+ {
+ if (this.blockLightStorage == null)
+ {
+ this.blockLightStorage = new ChunkLightStorage(this.getMinBuildHeight(), this.getMaxBuildHeight());
+ }
+ return this.blockLightStorage;
+ }
+
+
@Override
public int getDhSkyLight(int relX, int y, int relZ)
{
this.throwIndexOutOfBoundsIfRelativePosOutsideChunkBounds(relX, y, relZ);
- return this.getSkyLightArray().get(relX, y, relZ);
+ return this.getSkyLightStorage().get(relX, y, relZ);
}
@Override
public void setDhSkyLight(int relX, int y, int relZ, int lightValue)
{
this.throwIndexOutOfBoundsIfRelativePosOutsideChunkBounds(relX, y, relZ);
- this.getSkyLightArray().set(relX, y, relZ, lightValue);
+ this.getSkyLightStorage().set(relX, y, relZ, lightValue);
+ }
+
+ private ChunkLightStorage getSkyLightStorage()
+ {
+ if (this.skyLightStorage == null)
+ {
+ this.skyLightStorage = new ChunkLightStorage(this.getMinBuildHeight(), this.getMaxBuildHeight());
+ }
+ return this.skyLightStorage;
}
@@ -308,7 +309,7 @@ public class ChunkWrapper implements IChunkWrapper
if (this.useDhLighting)
{
// DH lighting method
- return this.getBlockLightArray().get(relX, y, relZ);
+ return this.getBlockLightStorage().get(relX, y, relZ);
}
else
{
@@ -328,7 +329,7 @@ public class ChunkWrapper implements IChunkWrapper
if (this.useDhLighting)
{
// DH lighting method
- return this.getSkyLightArray().get(relX, y, relZ);
+ return this.getSkyLightStorage().get(relX, y, relZ);
}
else
{