From a6143780fa10a242b56231d653b77d1eba89a64e Mon Sep 17 00:00:00 2001 From: James Seibel Date: Thu, 15 Feb 2024 21:45:11 -0600 Subject: [PATCH] Fix C2ME file loading --- .../BatchGenerationEnvironment.java | 25 +++++ .../RegionFileStorageExternalCache.java | 6 ++ .../fabric/mixins/server/MixinChunkMap.java | 102 ++++++++++-------- 3 files changed, 87 insertions(+), 46 deletions(-) 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..26f49ee4c 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; @@ -388,6 +389,30 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv // it can throw EOFExceptions that are caught and logged by Minecraft //chunkData = level.getChunkSource().chunkMap.readChunk(chunkPos); + IOWorker ioWorker = this.params.level.getChunkSource().chunkMap.worker; + + #if MC_VER <= MC_1_18_2 + chunkData = ioWorker.load(chunkPos); + #else + + // 10 second timeout should prevent locking up the thread if the ioWorker dies or has issues + int maxGetTimeInMs = 10_000; + CompletableFuture> future = ioWorker.loadAsync(chunkPos); + try + { + Optional data = future.get(maxGetTimeInMs, TimeUnit.MILLISECONDS); + if (data.isPresent()) + { + chunkData = data.get(); + } + } + catch (Exception e) + { + LOAD_LOGGER.warn("Unable to get chunk at pos ["+chunkPos+"] after ["+maxGetTimeInMs+"] milliseconds.", e); + future.cancel(true); + } + #endif + RegionFileStorage storage = this.params.level.getChunkSource().chunkMap.worker.storage; RegionFileStorageExternalCache cache = this.getOrCreateRegionFileCache(storage); chunkData = cache.read(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 bcdd6f5ab..a181e2253 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/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) - ); } }