diff --git a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/block/BiomeWrapper.java b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/block/BiomeWrapper.java index 65910fb34..f43e3913a 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/block/BiomeWrapper.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/block/BiomeWrapper.java @@ -66,9 +66,9 @@ public class BiomeWrapper implements IBiomeWrapper private static final Logger LOGGER = LogManager.getLogger(); #if PRE_MC_1_18_2 - public static final ConcurrentMap biomeWrapperMap = new ConcurrentHashMap<>(); + public static final ConcurrentMap WRAPPER_BY_BIOME = new ConcurrentHashMap<>(); #else - public static final ConcurrentMap, BiomeWrapper> biomeWrapperMap = new ConcurrentHashMap<>(); + public static final ConcurrentMap, BiomeWrapper> WRAPPER_BY_BIOME = new ConcurrentHashMap<>(); #endif public static final String EMPTY_STRING = "EMPTY"; @@ -100,7 +100,17 @@ public class BiomeWrapper implements IBiomeWrapper return EMPTY_WRAPPER; } - return biomeWrapperMap.computeIfAbsent(biome, newBiome -> new BiomeWrapper(newBiome, levelWrapper)); + + if (WRAPPER_BY_BIOME.containsKey(biome)) + { + return WRAPPER_BY_BIOME.get(biome); + } + else + { + BiomeWrapper newWrapper = new BiomeWrapper(biome, levelWrapper); + WRAPPER_BY_BIOME.put(biome, newWrapper); + return newWrapper; + } } private BiomeWrapper(#if PRE_MC_1_18_2 Biome #else Holder #endif biome, ILevelWrapper levelWrapper) diff --git a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/block/BlockStateWrapper.java b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/block/BlockStateWrapper.java index b5c41d5fe..91e47ab14 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/block/BlockStateWrapper.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/block/BlockStateWrapper.java @@ -91,7 +91,17 @@ public class BlockStateWrapper implements IBlockStateWrapper return AIR; } - return WRAPPER_BY_BLOCK_STATE.computeIfAbsent(blockState, newBlockState -> new BlockStateWrapper(newBlockState, levelWrapper)); + + if (WRAPPER_BY_BLOCK_STATE.containsKey(blockState)) + { + return WRAPPER_BY_BLOCK_STATE.get(blockState); + } + else + { + BlockStateWrapper newWrapper = new BlockStateWrapper(blockState, levelWrapper); + WRAPPER_BY_BLOCK_STATE.put(blockState, newWrapper); + return newWrapper; + } } private BlockStateWrapper(BlockState blockState, ILevelWrapper levelWrapper) 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 new file mode 100644 index 000000000..8d0a465c5 --- /dev/null +++ b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/chunk/ChunkLightStorage.java @@ -0,0 +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 +*/ +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 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.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; + } + + //populate array if it doesn't exist. + if (this.lightSections == null) + { + this.lightSections = new LightSection[BitShiftUtil.divideByPowerOfTwo(this.maxY - this.minY, 4)]; + } + + int index = (y - this.minY) >> 4; + 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; + } + lightSection.set(x, y, z, lightLevel); + } + + + + //================// + // helper classes // + //================// + + public static class LightSection + { + public byte constantValue; + public long[] data; + public short[] counts; + + 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; + } + + x &= 15; + y &= 15; + z &= 15; + long bits = this.data[(z << 4) | x]; + return ((int) (bits >>> (y << 2))) & 15; + } + + public void set(int x, int y, int z, int lightLevel) + { + int oldLightLevel = -1; + 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; + } + //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); + //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); + DataRecycler.reclaim(this.data); + this.data = null; + } + } + + } + + 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); } + } + +} \ 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 21dd80c33..eadd1f31f 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 @@ -61,7 +61,10 @@ public class ChunkWrapper implements IChunkWrapper private static final Logger LOGGER = DhLoggerBuilder.getLogger(); /** useful for debugging, but can slow down chunk operations quite a bit due to being called every time. */ - private static final boolean RUN_RELATIVE_POS_INDEX_VALIDATION = false; + private static final boolean RUN_RELATIVE_POS_INDEX_VALIDATION = false; + + /** can be used for interactions with the underlying chunk where creating new BlockPos objects could cause issues for the garbage collector. */ + private static final ThreadLocal MUTABLE_BLOCK_POS_REF = ThreadLocal.withInitial(() -> new BlockPos.MutableBlockPos()); private final ChunkAccess chunk; @@ -73,8 +76,8 @@ public class ChunkWrapper implements IChunkWrapper /** only used when connected to a dedicated server */ private boolean isMcClientLightingCorrect = false; - private final byte[] blockLightArray; - private final byte[] skyLightArray; + private ChunkLightStorage blockLightStorage; + private ChunkLightStorage skyLightStorage; private ArrayList blockLightPosList = null; @@ -112,9 +115,7 @@ public class ChunkWrapper implements IChunkWrapper this.useDhLighting = isDhGeneratedChunk; // FIXME +1 is to handle the fact that LodDataBuilder adds +1 to all block lighting calculations, also done in the relative position validator - this.blockLightArray = new byte[LodUtil.CHUNK_WIDTH * LodUtil.CHUNK_WIDTH * (this.getHeight() + 1)]; - this.skyLightArray = new byte[LodUtil.CHUNK_WIDTH * LodUtil.CHUNK_WIDTH * (this.getHeight() + 1)]; - + chunksNeedingClientLightUpdating.add(this); } @@ -146,6 +147,21 @@ public class ChunkWrapper implements IChunkWrapper @Override public int getMaxBuildHeight() { return this.chunk.getMaxBuildHeight(); } + @Override + public int getMinFilledHeight() + { + LevelChunkSection[] sections = this.chunk.getSections(); + for (int index = 0; index < sections.length; index++) + { + if (!sections[index].hasOnlyAir()) + { + // convert from an index to a block coordinate + return this.chunk.getSectionYFromSectionIndex(index) * 16; + } + } + return Integer.MAX_VALUE; + } + @Override public int getSolidHeightMapValue(int xRel, int zRel) { return this.chunk.getOrCreateHeightmapUnprimed(Heightmap.Types.WORLD_SURFACE).getFirstAvailable(xRel, zRel); } @@ -242,34 +258,45 @@ public class ChunkWrapper implements IChunkWrapper public int getDhBlockLight(int relX, int y, int relZ) { this.throwIndexOutOfBoundsIfRelativePosOutsideChunkBounds(relX, y, relZ); - - int index = this.relativeBlockPosToIndex(relX, y, relZ); - return this.blockLightArray[index]; + return this.getBlockLightStorage().get(relX, y, relZ); } @Override public void setDhBlockLight(int relX, int y, int relZ, int lightValue) { this.throwIndexOutOfBoundsIfRelativePosOutsideChunkBounds(relX, y, relZ); - - int index = this.relativeBlockPosToIndex(relX, y, relZ); - this.blockLightArray[index] = (byte) 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); - - int index = this.relativeBlockPosToIndex(relX, y, relZ); - return this.skyLightArray[index]; + return this.getSkyLightStorage().get(relX, y, relZ); } @Override public void setDhSkyLight(int relX, int y, int relZ, int lightValue) { this.throwIndexOutOfBoundsIfRelativePosOutsideChunkBounds(relX, y, relZ); - - int index = this.relativeBlockPosToIndex(relX, y, relZ); - this.skyLightArray[index] = (byte) 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; } @@ -282,8 +309,7 @@ public class ChunkWrapper implements IChunkWrapper if (this.useDhLighting) { // DH lighting method - int index = this.relativeBlockPosToIndex(relX, y, relZ); - return this.blockLightArray[index]; + return this.getBlockLightStorage().get(relX, y, relZ); } else { @@ -303,8 +329,7 @@ public class ChunkWrapper implements IChunkWrapper if (this.useDhLighting) { // DH lighting method - int index = this.relativeBlockPosToIndex(relX, y, relZ); - return this.skyLightArray[index]; + return this.getSkyLightStorage().get(relX, y, relZ); } else { @@ -314,7 +339,7 @@ public class ChunkWrapper implements IChunkWrapper } @Override - public List getBlockLightPosList() + public ArrayList getBlockLightPosList() { // only populate the list once if (this.blockLightPosList == null) @@ -372,7 +397,13 @@ public class ChunkWrapper implements IChunkWrapper @Override public IBlockStateWrapper getBlockState(int relX, int relY, int relZ) { - return BlockStateWrapper.fromBlockState(this.chunk.getBlockState(new BlockPos(relX, relY, relZ)), this.wrappedLevel); + BlockPos.MutableBlockPos blockPos = MUTABLE_BLOCK_POS_REF.get(); + + blockPos.setX(relX); + blockPos.setY(relY); + blockPos.setZ(relZ); + + return BlockStateWrapper.fromBlockState(this.chunk.getBlockState(blockPos), this.wrappedLevel); } @Override @@ -442,7 +473,10 @@ public class ChunkWrapper implements IChunkWrapper private void throwIndexOutOfBoundsIfRelativePosOutsideChunkBounds(int x, int y, int z) throws IndexOutOfBoundsException { if (RUN_RELATIVE_POS_INDEX_VALIDATION) + { return; + } + // FIXME +1 is to handle the fact that LodDataBuilder adds +1 to all block lighting calculations, also done in the constructor int minHeight = this.getMinBuildHeight(); diff --git a/coreSubProjects b/coreSubProjects index 21f03526f..f0a62c813 160000 --- a/coreSubProjects +++ b/coreSubProjects @@ -1 +1 @@ -Subproject commit 21f03526f8ce58190b45bb206202d36b807812bd +Subproject commit f0a62c813a38ae533255051ba4802358203fa702 diff --git a/fabric/src/main/java/com/seibel/distanthorizons/fabric/FabricMain.java b/fabric/src/main/java/com/seibel/distanthorizons/fabric/FabricMain.java index 67d090a68..a441d70cb 100644 --- a/fabric/src/main/java/com/seibel/distanthorizons/fabric/FabricMain.java +++ b/fabric/src/main/java/com/seibel/distanthorizons/fabric/FabricMain.java @@ -103,6 +103,15 @@ public class FabricMain { ModAccessorInjector.INSTANCE.bind(IBCLibAccessor.class, new BCLibAccessor()); } + + #if MC_1_16_5 || MC_1_18_2 || MC_1_19_2 || MC_1_19_4 || MC_1_20_1 + // 1.17.1 won't support this since there isn't a matching Iris version + if (SingletonInjector.INSTANCE.get(IModChecker.class).isModLoaded("iris")) + { + ModAccessorInjector.INSTANCE.bind(IIrisAccessor.class, new IrisAccessor()); + } + #endif + LOGGER.info(ModInfo.READABLE_NAME + " Initialized"); ApiEventInjector.INSTANCE.fireAllEvents(DhApiAfterDhInitEvent.class, null); diff --git a/fabric/src/main/java/com/seibel/distanthorizons/fabric/wrappers/modAccessor/IrisAccessor.java b/fabric/src/main/java/com/seibel/distanthorizons/fabric/wrappers/modAccessor/IrisAccessor.java new file mode 100644 index 000000000..37fe1b2a0 --- /dev/null +++ b/fabric/src/main/java/com/seibel/distanthorizons/fabric/wrappers/modAccessor/IrisAccessor.java @@ -0,0 +1,50 @@ +/* + * 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.fabric.wrappers.modAccessor; + +#if MC_1_16_5 || MC_1_18_2 || MC_1_19_2 || MC_1_19_4 || MC_1_20_1 + +import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IIrisAccessor; +import net.coderbot.iris.Iris; +import net.irisshaders.iris.api.v0.IrisApi; + +public class IrisAccessor implements IIrisAccessor +{ + @Override + public String getModName() + { + //return "Iris-Fabric"; + return Iris.MODID; + } + + @Override + public boolean isShaderPackInUse() + { + return IrisApi.getInstance().isShaderPackInUse(); + } + + @Override + public boolean isRenderingShadowPass() + { + return IrisApi.getInstance().isRenderingShadowPass(); + } +} + +#endif diff --git a/versionProperties/1.16.5.properties b/versionProperties/1.16.5.properties index c3683be84..4d9b764c6 100644 --- a/versionProperties/1.16.5.properties +++ b/versionProperties/1.16.5.properties @@ -15,7 +15,7 @@ fabric_api_version=0.42.0+1.16 phosphor_version_fabric= lithium_version=mc1.16.5-0.6.6 sodium_version=mc1.16.5-0.2.0 - iris_version=1.16.x-v1.2.5 + iris_version=1.4.4+1.16.5 bclib_version= immersive_portals_version= canvas_version= @@ -30,7 +30,7 @@ fabric_api_version=0.42.0+1.16 enable_phosphor=0 enable_lithium=0 enable_sodium=1 - enable_iris=0 + enable_iris=1 # not available via github, use curse.maven if necessary enable_bclib=0 enable_immersive_portals=0 diff --git a/versionProperties/1.18.2.properties b/versionProperties/1.18.2.properties index 62c33f7aa..6c188d603 100644 --- a/versionProperties/1.18.2.properties +++ b/versionProperties/1.18.2.properties @@ -16,7 +16,7 @@ fabric_api_version=0.76.0+1.18.2 phosphor_version_fabric=3573395 lithium_version=mc1.18.2-0.10.3 sodium_version=mc1.18.2-0.4.1 - iris_version=1.18.x-v1.6.4 + iris_version=1.6.6+1.18.2 bclib_version=1.4.6 immersive_portals_version=v1.4.11-1.18 canvas_version=mc118:1.0.2616 @@ -31,7 +31,7 @@ fabric_api_version=0.76.0+1.18.2 enable_phosphor=0 enable_sodium=1 enable_lithium=0 - enable_iris=0 + enable_iris=1 enable_bclib=1 enable_immersive_portals=0 enable_canvas=0 diff --git a/versionProperties/1.19.2.properties b/versionProperties/1.19.2.properties index cdde6a134..5f2da8945 100644 --- a/versionProperties/1.19.2.properties +++ b/versionProperties/1.19.2.properties @@ -15,7 +15,7 @@ fabric_api_version=0.76.0+1.19.2 phosphor_version_fabric= lithium_version= sodium_version=mc1.19.2-0.4.4 - iris_version=1.6.4+1.19.2 + iris_version=1.6.6+1.19.2 bclib_version=2.1.6 immersive_portals_version= canvas_version=mc119-1.0.2480 @@ -30,7 +30,7 @@ fabric_api_version=0.76.0+1.19.2 enable_phosphor=0 enable_sodium=1 enable_lithium=0 - enable_iris=0 + enable_iris=1 enable_bclib=1 enable_immersive_portals=0 enable_canvas=0 diff --git a/versionProperties/1.19.4.properties b/versionProperties/1.19.4.properties index 2783daeab..5763b87c8 100644 --- a/versionProperties/1.19.4.properties +++ b/versionProperties/1.19.4.properties @@ -15,7 +15,7 @@ fabric_api_version=0.83.0+1.19.4 phosphor_version_fabric= lithium_version= sodium_version=mc1.19.4-0.4.10 - iris_version=1.6.4+1.19.4 + iris_version=1.6.6+1.19.4 bclib_version=2.3.3 immersive_portals_version= canvas_version= @@ -30,7 +30,7 @@ fabric_api_version=0.83.0+1.19.4 enable_phosphor=0 enable_sodium=1 enable_lithium=0 - enable_iris=0 + enable_iris=1 enable_bclib=1 enable_immersive_portals=0 enable_canvas=0 diff --git a/versionProperties/1.20.1.properties b/versionProperties/1.20.1.properties index 0e88c741d..da1395f4d 100644 --- a/versionProperties/1.20.1.properties +++ b/versionProperties/1.20.1.properties @@ -15,7 +15,7 @@ fabric_api_version=0.85.0+1.20.1 phosphor_version_fabric= lithium_version= sodium_version=mc1.20.1-0.5.1 - iris_version=1.6.4+1.20.1 + iris_version=1.6.8+1.20.1 bclib_version=3.0.12 immersive_portals_version= canvas_version= @@ -30,7 +30,7 @@ fabric_api_version=0.85.0+1.20.1 enable_phosphor=0 enable_sodium=1 enable_lithium=0 - enable_iris=0 + enable_iris=1 enable_bclib=1 enable_immersive_portals=0 enable_canvas=0