diff --git a/build.gradle b/build.gradle index ac073fc65..41b3c94a9 100644 --- a/build.gradle +++ b/build.gradle @@ -199,6 +199,12 @@ subprojects { p -> implementation("org.apache.logging.log4j:log4j-api:${rootProject.log4j_version}") implementation("org.apache.logging.log4j:log4j-core:${rootProject.log4j_version}") + // JOML + if (project.hasProperty("embed_joml") && embed_joml == "true") + forgeShadowMe("org.joml:joml:${rootProject.joml_version}") + else + implementation("org.joml:joml:${rootProject.joml_version}") + // JUnit tests implementation("org.junit.jupiter:junit-jupiter:5.8.2") implementation("org.junit.jupiter:junit-jupiter-engine:5.8.2") @@ -305,7 +311,8 @@ subprojects { p -> // relocate "org.sqlite", "${librariesLocation}.sqlite" // JOML - relocate "org.joml", "${librariesLocation}.joml" + if (project.hasProperty("embed_joml") && embed_joml == "true") + relocate "org.joml", "${librariesLocation}.joml" // NightConfig (includes Toml & Json) relocate "com.electronwill.nightconfig", "${librariesLocation}.electronwill.nightconfig" 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 843817892..9cbdd9816 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 @@ -65,7 +65,7 @@ public class BiomeWrapper implements IBiomeWrapper #endif public static final String EMPTY_STRING = "EMPTY"; - public static final BiomeWrapper EMPTY_WRAPPER = new BiomeWrapper(null, null); + public static final BiomeWrapper EMPTY_WRAPPER = new BiomeWrapper(); /** keep track of broken biomes so we don't log every time */ private static final HashSet brokenResourceLocationStrings = new HashSet<>(); @@ -115,7 +115,6 @@ public class BiomeWrapper implements IBiomeWrapper return newWrapper; } } - private BiomeWrapper(#if MC_VER < MC_1_18_2 Biome #else Holder #endif biome, ILevelWrapper levelWrapper) { this.biome = biome; @@ -123,6 +122,13 @@ public class BiomeWrapper implements IBiomeWrapper LOGGER.trace("Created BiomeWrapper ["+this.serialString+"] for ["+biome+"]"); } + /** should only be used to create {@link BiomeWrapper#EMPTY_WRAPPER} */ + private BiomeWrapper() + { + this.biome = null; + this.serialString = EMPTY_STRING; + } + //=========// 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 3e3fbd24e..e44a30e0a 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 @@ -489,6 +489,10 @@ public class BlockStateWrapper implements IBlockStateWrapper { return IrisBlockMaterial.LEAVES; } + else if (this.blockState.is(Blocks.LAVA)) + { + return IrisBlockMaterial.LAVA; + } else if (this.isLiquid() || this.blockState.is(Blocks.WATER)) { return IrisBlockMaterial.WATER; @@ -524,10 +528,6 @@ public class BlockStateWrapper implements IBlockStateWrapper ) { return IrisBlockMaterial.DIRT; - } - else if (this.blockState.is(Blocks.LAVA)) - { - return IrisBlockMaterial.LAVA; } #if MC_VER >= MC_1_17_1 else if (this.blockState.getSoundType() == SoundType.DEEPSLATE diff --git a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/worldGeneration/BatchGenerationEnvironment.java b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/worldGeneration/BatchGenerationEnvironment.java index bed2582a3..de268a590 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/worldGeneration/BatchGenerationEnvironment.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/worldGeneration/BatchGenerationEnvironment.java @@ -70,6 +70,7 @@ import net.minecraft.world.level.chunk.ChunkStatus; import net.minecraft.world.level.chunk.LevelChunk; import net.minecraft.world.level.chunk.ProtoChunk; import net.minecraft.world.level.chunk.UpgradeData; +import net.minecraft.world.level.chunk.storage.IOWorker; import net.minecraft.world.level.chunk.storage.RegionFileStorage; import net.minecraft.world.level.levelgen.DebugLevelSource; import net.minecraft.world.level.levelgen.FlatLevelSource; @@ -381,22 +382,50 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv { ServerLevel level = this.params.level; + + + //====================// + // get the chunk data // + //====================// + CompoundTag chunkData = null; try { - // Warning: if multiple threads attempt to access this method at the same time, - // it can throw EOFExceptions that are caught and logged by Minecraft - //chunkData = level.getChunkSource().chunkMap.readChunk(chunkPos); + IOWorker ioWorker = level.getChunkSource().chunkMap.worker; - RegionFileStorage storage = this.params.level.getChunkSource().chunkMap.worker.storage; - RegionFileStorageExternalCache cache = this.getOrCreateRegionFileCache(storage); - chunkData = cache.read(chunkPos); + #if MC_VER <= MC_1_18_2 + chunkData = ioWorker.load(chunkPos); + #else + + // timeout should prevent locking up the thread if the ioWorker dies or has issues + int maxGetTimeInSec = Config.Client.Advanced.WorldGenerator.worldGenerationTimeoutLengthInSeconds.get(); + CompletableFuture> future = ioWorker.loadAsync(chunkPos); + try + { + Optional data = future.get(maxGetTimeInSec, TimeUnit.SECONDS); + if (data.isPresent()) + { + chunkData = data.get(); + } + } + catch (Exception e) + { + LOAD_LOGGER.warn("Unable to get chunk at pos ["+chunkPos+"] after ["+maxGetTimeInSec+"] milliseconds.", e); + future.cancel(true); + } + #endif } catch (Exception e) { LOAD_LOGGER.error("DistantHorizons: Couldn't load or make chunk " + chunkPos + ". Error: " + e.getMessage(), e); } + + + //========================// + // convert the chunk data // + //========================// + if (chunkData == null) { return EmptyChunk(level, chunkPos); diff --git a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/worldGeneration/mimicObject/RegionFileStorageExternalCache.java b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/worldGeneration/mimicObject/RegionFileStorageExternalCache.java index 5fe18b12f..628f52cad 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/worldGeneration/mimicObject/RegionFileStorageExternalCache.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/worldGeneration/mimicObject/RegionFileStorageExternalCache.java @@ -17,6 +17,12 @@ import java.nio.file.Path; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.locks.ReentrantLock; +/** + * @deprecated should be replaced with net.minecraft.world.level.chunk.storage.IOWorker to + * prevent potential file corruption and issues with the C2ME mod. + * Generally this would be done via (MC ServerLevel) level.getChunkSource().chunkMap.worker#loadAsync() + */ +@Deprecated public class RegionFileStorageExternalCache implements AutoCloseable { private static final Logger LOGGER = DhLoggerBuilder.getLogger(); diff --git a/coreSubProjects b/coreSubProjects index 556550fea..7826d756d 160000 --- a/coreSubProjects +++ b/coreSubProjects @@ -1 +1 @@ -Subproject commit 556550fea802c5e7ad526204a40916ba9b698ad0 +Subproject commit 7826d756d0a882eedfb033882ade06aaf4cbf7ab diff --git a/fabric/src/main/java/com/seibel/distanthorizons/fabric/FabricClientProxy.java b/fabric/src/main/java/com/seibel/distanthorizons/fabric/FabricClientProxy.java index 931113d95..d9216458d 100644 --- a/fabric/src/main/java/com/seibel/distanthorizons/fabric/FabricClientProxy.java +++ b/fabric/src/main/java/com/seibel/distanthorizons/fabric/FabricClientProxy.java @@ -32,6 +32,8 @@ import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.dependencyInjection.ModAccessorInjector; import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; +import com.seibel.distanthorizons.core.pos.DhBlockPos; +import com.seibel.distanthorizons.core.pos.DhChunkPos; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.ISodiumAccessor; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper; @@ -123,17 +125,20 @@ public class FabricClientProxy implements AbstractModInitializer.IEventProxy // if we have access to the server, use the chunk save event instead if (MC.clientConnectedToDedicatedServer()) { - // Since fabric doesn't have a client-side break-block API event, this is the next best thing - ChunkAccess chunk = level.getChunk(blockPos); - if (chunk != null) + if (SharedApi.isChunkAtBlockPosAlreadyUpdating(blockPos.getX(), blockPos.getZ())) { - LOGGER.trace("attack block at blockPos: " + blockPos); - - IClientLevelWrapper wrappedLevel = ClientLevelWrapper.getWrapper((ClientLevel) level); - SharedApi.INSTANCE.chunkBlockChangedEvent( - new ChunkWrapper(chunk, level, wrappedLevel), - wrappedLevel - ); + // Since fabric doesn't have a client-side break-block API event, this is the next best thing + ChunkAccess chunk = level.getChunk(blockPos); + if (chunk != null) + { + LOGGER.trace("attack block at blockPos: " + blockPos); + + IClientLevelWrapper wrappedLevel = ClientLevelWrapper.getWrapper((ClientLevel) level); + SharedApi.INSTANCE.chunkBlockChangedEvent( + new ChunkWrapper(chunk, level, wrappedLevel), + wrappedLevel + ); + } } } @@ -147,20 +152,23 @@ public class FabricClientProxy implements AbstractModInitializer.IEventProxy // if we have access to the server, use the chunk save event instead if (MC.clientConnectedToDedicatedServer()) { - // Since fabric doesn't have a client-side place-block API event, this is the next best thing - if (hitResult.getType() == HitResult.Type.BLOCK - && !hitResult.isInside()) + if (SharedApi.isChunkAtBlockPosAlreadyUpdating(hitResult.getBlockPos().getX(), hitResult.getBlockPos().getZ())) { - ChunkAccess chunk = level.getChunk(hitResult.getBlockPos()); - if (chunk != null) + // Since fabric doesn't have a client-side place-block API event, this is the next best thing + if (hitResult.getType() == HitResult.Type.BLOCK + && !hitResult.isInside()) { - LOGGER.trace("use block at blockPos: " + hitResult.getBlockPos()); - - IClientLevelWrapper wrappedLevel = ClientLevelWrapper.getWrapper((ClientLevel) level); - SharedApi.INSTANCE.chunkBlockChangedEvent( - new ChunkWrapper(chunk, level, wrappedLevel), - wrappedLevel - ); + ChunkAccess chunk = level.getChunk(hitResult.getBlockPos()); + if (chunk != null) + { + LOGGER.trace("use block at blockPos: " + hitResult.getBlockPos()); + + IClientLevelWrapper wrappedLevel = ClientLevelWrapper.getWrapper((ClientLevel) level); + SharedApi.INSTANCE.chunkBlockChangedEvent( + new ChunkWrapper(chunk, level, wrappedLevel), + wrappedLevel + ); + } } } } diff --git a/fabric/src/main/java/com/seibel/distanthorizons/fabric/mixins/server/MixinChunkMap.java b/fabric/src/main/java/com/seibel/distanthorizons/fabric/mixins/server/MixinChunkMap.java index 617a8aaf0..ffef0238c 100644 --- a/fabric/src/main/java/com/seibel/distanthorizons/fabric/mixins/server/MixinChunkMap.java +++ b/fabric/src/main/java/com/seibel/distanthorizons/fabric/mixins/server/MixinChunkMap.java @@ -28,56 +28,66 @@ public class MixinChunkMap @Final ServerLevel level; - @Inject(method = "save", at = @At(value = "INVOKE", target = CHUNK_SERIALIZER_WRITE)) + // firing at INVOKE causes issues with C2ME and is probably unnecessary since we + // don't need the chunk(s) before MC has finished saving them + @Inject(method = "save", at = @At(value = "RETURN", target = CHUNK_SERIALIZER_WRITE)) private void onChunkSave(ChunkAccess chunk, CallbackInfoReturnable ci) { - //=====================================// - // corrupt/incomplete chunk validation // - //=====================================// - - // MC has a tendency to try saving incomplete or corrupted chunks (which show up as empty or black chunks) - // this logic should prevent that from happening - #if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1 - if (chunk.isUnsaved() || chunk.getUpgradeData() != null || !chunk.isLightCorrect()) + // true means a chunk was saved to disk + if (ci.getReturnValue()) { - return; + // TODO is this validation necessary since we are checking above if + // the callback return value should state if the chunk was actually saved or not? + // Do we trust it to always be correct? + + //=====================================// + // corrupt/incomplete chunk validation // + //=====================================// + + // MC has a tendency to try saving incomplete or corrupted chunks (which show up as empty or black chunks) + // this logic should prevent that from happening + #if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1 + if (chunk.isUnsaved() || chunk.getUpgradeData() != null || !chunk.isLightCorrect()) + { + return; + } + #else + if (chunk.isUnsaved() || chunk.isUpgrading() || !chunk.isLightCorrect()) + { + return; + } + #endif + + + //==================// + // biome validation // + //==================// + + // some chunks may be missing their biomes, which cause issues when attempting to save them + #if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1 + if (chunk.getBiomes() == null) + { + return; + } + #else + try + { + // this will throw an exception if the biomes aren't set up + chunk.getNoiseBiome(0,0,0); + } + catch (Exception e) + { + return; + } + #endif + + + + ServerApi.INSTANCE.serverChunkSaveEvent( + new ChunkWrapper(chunk, this.level, ServerLevelWrapper.getWrapper(this.level)), + ServerLevelWrapper.getWrapper(this.level) + ); } - #else - if (chunk.isUnsaved() || chunk.isUpgrading() || !chunk.isLightCorrect()) - { - return; - } - #endif - - - //==================// - // biome validation // - //==================// - - // some chunks may be missing their biomes, which cause issues when attempting to save them - #if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1 - if (chunk.getBiomes() == null) - { - return; - } - #else - try - { - // this will throw an exception if the biomes aren't set up - chunk.getNoiseBiome(0,0,0); - } - catch (Exception e) - { - return; - } - #endif - - - - ServerApi.INSTANCE.serverChunkSaveEvent( - new ChunkWrapper(chunk, this.level, ServerLevelWrapper.getWrapper(this.level)), - ServerLevelWrapper.getWrapper(this.level) - ); } } diff --git a/forge/src/main/java/com/seibel/distanthorizons/forge/ForgeClientProxy.java b/forge/src/main/java/com/seibel/distanthorizons/forge/ForgeClientProxy.java index 469f7b134..9b1b03c49 100644 --- a/forge/src/main/java/com/seibel/distanthorizons/forge/ForgeClientProxy.java +++ b/forge/src/main/java/com/seibel/distanthorizons/forge/ForgeClientProxy.java @@ -173,6 +173,11 @@ public class ForgeClientProxy implements AbstractModInitializer.IEventProxy @SubscribeEvent public void rightClickBlockEvent(PlayerInteractEvent.RightClickBlock event) { + if (SharedApi.isChunkAtBlockPosAlreadyUpdating(event.getPos().getX(), event.getPos().getZ())) + { + return; + } + LOGGER.trace("interact or block place event at blockPos: " + event.getPos()); #if MC_VER < MC_1_19_2 @@ -187,6 +192,11 @@ public class ForgeClientProxy implements AbstractModInitializer.IEventProxy @SubscribeEvent public void leftClickBlockEvent(PlayerInteractEvent.LeftClickBlock event) { + if (SharedApi.isChunkAtBlockPosAlreadyUpdating(event.getPos().getX(), event.getPos().getZ())) + { + return; + } + LOGGER.trace("break or block attack at blockPos: " + event.getPos()); #if MC_VER < MC_1_19_2 diff --git a/neoforge/src/main/java/com/seibel/distanthorizons/neoforge/NeoforgeClientProxy.java b/neoforge/src/main/java/com/seibel/distanthorizons/neoforge/NeoforgeClientProxy.java index d46be455c..69da1d5e1 100644 --- a/neoforge/src/main/java/com/seibel/distanthorizons/neoforge/NeoforgeClientProxy.java +++ b/neoforge/src/main/java/com/seibel/distanthorizons/neoforge/NeoforgeClientProxy.java @@ -146,6 +146,11 @@ public class NeoforgeClientProxy implements AbstractModInitializer.IEventProxy @SubscribeEvent public void rightClickBlockEvent(PlayerInteractEvent.RightClickBlock event) { + if (SharedApi.isChunkAtBlockPosAlreadyUpdating(event.getPos().getX(), event.getPos().getZ())) + { + return; + } + LOGGER.trace("interact or block place event at blockPos: " + event.getPos()); LevelAccessor level = event.getLevel(); @@ -156,6 +161,11 @@ public class NeoforgeClientProxy implements AbstractModInitializer.IEventProxy @SubscribeEvent public void leftClickBlockEvent(PlayerInteractEvent.LeftClickBlock event) { + if (SharedApi.isChunkAtBlockPosAlreadyUpdating(event.getPos().getX(), event.getPos().getZ())) + { + return; + } + LOGGER.trace("break or block attack at blockPos: " + event.getPos()); LevelAccessor level = event.getLevel(); diff --git a/neoforge/src/main/java/com/seibel/distanthorizons/neoforge/wrappers/modAccessor/ModChecker.java b/neoforge/src/main/java/com/seibel/distanthorizons/neoforge/wrappers/modAccessor/ModChecker.java index 775869e49..6494d6edf 100644 --- a/neoforge/src/main/java/com/seibel/distanthorizons/neoforge/wrappers/modAccessor/ModChecker.java +++ b/neoforge/src/main/java/com/seibel/distanthorizons/neoforge/wrappers/modAccessor/ModChecker.java @@ -22,14 +22,22 @@ package com.seibel.distanthorizons.neoforge.wrappers.modAccessor; import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IModChecker; import net.neoforged.fml.ModList; +import java.io.File; + public class ModChecker implements IModChecker { public static final ModChecker INSTANCE = new ModChecker(); @Override - public boolean isModLoaded(String modid) + public boolean isModLoaded(String modid) { - return ModList.get().isLoaded(modid); + return ModList.get().isLoaded(modid); + } + + @Override + public File modLocation(String modid) + { + return ModList.get().getModFileById(modid).getFile().getFilePath().toFile(); } } diff --git a/versionProperties/1.16.5.properties b/versionProperties/1.16.5.properties index dc8d54645..dae7f1dc0 100644 --- a/versionProperties/1.16.5.properties +++ b/versionProperties/1.16.5.properties @@ -5,6 +5,7 @@ parchment_version=1.16.5:2022.03.06 compatible_minecraft_versions=["1.16.4", "1.16.5"] accessWidenerVersion=1_16 builds_for=fabric,forge +embed_joml=true # Fabric loader fabric_loader_version=0.14.24 diff --git a/versionProperties/1.17.1.properties b/versionProperties/1.17.1.properties index 44be229a8..4b5b8dfc9 100644 --- a/versionProperties/1.17.1.properties +++ b/versionProperties/1.17.1.properties @@ -5,6 +5,7 @@ parchment_version=1.17.1:2021.12.12 compatible_minecraft_versions=["1.17", "1.17.1"] accessWidenerVersion=1_17 builds_for=fabric,forge +embed_joml=true # Fabric loader fabric_loader_version=0.14.24 diff --git a/versionProperties/1.18.2.properties b/versionProperties/1.18.2.properties index e987c439f..e168e4f1b 100644 --- a/versionProperties/1.18.2.properties +++ b/versionProperties/1.18.2.properties @@ -5,6 +5,7 @@ parchment_version=1.18.2:2022.11.06 compatible_minecraft_versions=["1.18.2"] accessWidenerVersion=1_18 builds_for=fabric,forge +embed_joml=true # Fabric loader fabric_loader_version=0.14.24 diff --git a/versionProperties/1.19.2.properties b/versionProperties/1.19.2.properties index c4fe3195f..34e38b150 100644 --- a/versionProperties/1.19.2.properties +++ b/versionProperties/1.19.2.properties @@ -5,6 +5,7 @@ parchment_version=1.19.2:2022.11.27 compatible_minecraft_versions=["1.19.2"] accessWidenerVersion=1_19_2 builds_for=fabric,forge +embed_joml=true # Fabric loader fabric_loader_version=0.14.24