From 9515dcb95c18eae756c45d454d9eba86fbbc2fe9 Mon Sep 17 00:00:00 2001 From: Thiakil Date: Thu, 5 Sep 2024 21:20:42 +0800 Subject: [PATCH] fix anchor-upgraded Digital Miners failing when running during initial chunkload --- .../content/miner/MinerRegionCache.java | 159 ++++++++++++++++++ .../content/miner/ThreadMinerSearch.java | 5 +- .../tile/machine/TileEntityDigitalMiner.java | 4 +- 3 files changed, 163 insertions(+), 5 deletions(-) create mode 100644 src/main/java/mekanism/common/content/miner/MinerRegionCache.java diff --git a/src/main/java/mekanism/common/content/miner/MinerRegionCache.java b/src/main/java/mekanism/common/content/miner/MinerRegionCache.java new file mode 100644 index 00000000000..c6dfa639e3d --- /dev/null +++ b/src/main/java/mekanism/common/content/miner/MinerRegionCache.java @@ -0,0 +1,159 @@ +package mekanism.common.content.miner; + +import com.google.common.base.Suppliers; +import com.mojang.logging.LogUtils; +import java.util.List; +import java.util.concurrent.ExecutionException; +import java.util.function.Supplier; +import javax.annotation.Nullable; +import mekanism.api.annotations.NothingNullByDefault; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Holder; +import net.minecraft.core.SectionPos; +import net.minecraft.core.registries.Registries; +import net.minecraft.server.level.ServerChunkCache; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.util.profiling.ProfilerFiller; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.ChunkPos; +import net.minecraft.world.level.CollisionGetter; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.biome.Biomes; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.border.WorldBorder; +import net.minecraft.world.level.chunk.ChunkAccess; +import net.minecraft.world.level.chunk.EmptyLevelChunk; +import net.minecraft.world.level.chunk.status.ChunkStatus; +import net.minecraft.world.level.material.FluidState; +import net.minecraft.world.level.material.Fluids; +import net.minecraft.world.phys.AABB; +import net.minecraft.world.phys.shapes.VoxelShape; +import org.slf4j.Logger; + +/** + * Copy of PathNavigationRegion, but will force chunks to load as PathNavigationRegion won't do it (if anchor upgrade installed + */ +@NothingNullByDefault +public class MinerRegionCache implements BlockGetter, CollisionGetter { + private static final Logger LOGGER = LogUtils.getLogger(); + protected final int centerX; + protected final int centerZ; + protected final ChunkAccess[][] chunks; + protected boolean allEmpty; + protected final Level level; + private final Supplier> plains; + + public MinerRegionCache(ServerLevel level, BlockPos centerPos, BlockPos offsetPos, boolean hasAnchor) { + this.level = level; + this.plains = Suppliers.memoize(() -> level.registryAccess().registryOrThrow(Registries.BIOME).getHolderOrThrow(Biomes.PLAINS)); + this.centerX = SectionPos.blockToSectionCoord(centerPos.getX()); + this.centerZ = SectionPos.blockToSectionCoord(centerPos.getZ()); + int i = SectionPos.blockToSectionCoord(offsetPos.getX()); + int j = SectionPos.blockToSectionCoord(offsetPos.getZ()); + this.chunks = new ChunkAccess[i - this.centerX + 1][j - this.centerZ + 1]; + ServerChunkCache chunksource = level.getChunkSource(); + this.allEmpty = true; + + for (int x = this.centerX; x <= i; x++) { + for (int z = this.centerZ; z <= j; z++) { + ChunkAccess chunkAccess; + if (hasAnchor) { + try { + chunkAccess = chunksource.getChunkFuture(x, z, ChunkStatus.FULL, true).get().orElse(null); + }catch (InterruptedException | ExecutionException ignored){ + chunkAccess = null; + } + } else { + chunkAccess = chunksource.getChunkNow(x, z);// returns null if not loaded + } + this.chunks[x - this.centerX][z - this.centerZ] = chunkAccess; + LOGGER.error("Failed to load chunk for searcher cache: {}, {}", x, z); + } + } + + for (int x = SectionPos.blockToSectionCoord(centerPos.getX()); x <= SectionPos.blockToSectionCoord(offsetPos.getX()); x++) { + for (int z = SectionPos.blockToSectionCoord(centerPos.getZ()); z <= SectionPos.blockToSectionCoord(offsetPos.getZ()); z++) { + ChunkAccess chunkaccess = this.chunks[x - this.centerX][z - this.centerZ]; + if (chunkaccess != null && !chunkaccess.isYSpaceEmpty(centerPos.getY(), offsetPos.getY())) { + this.allEmpty = false; + return; + } + } + } + } + + private ChunkAccess getChunk(BlockPos pos) { + return this.getChunk(SectionPos.blockToSectionCoord(pos.getX()), SectionPos.blockToSectionCoord(pos.getZ())); + } + + private ChunkAccess getChunk(int x, int z) { + int i = x - this.centerX; + int j = z - this.centerZ; + if (i >= 0 && i < this.chunks.length && j >= 0 && j < this.chunks[i].length) { + ChunkAccess chunkaccess = this.chunks[i][j]; + return chunkaccess != null ? chunkaccess : new EmptyLevelChunk(this.level, new ChunkPos(x, z), this.plains.get()); + } else { + return new EmptyLevelChunk(this.level, new ChunkPos(x, z), this.plains.get()); + } + } + + @Override + public WorldBorder getWorldBorder() { + return this.level.getWorldBorder(); + } + + @Override + public BlockGetter getChunkForCollisions(int chunkX, int chunkZ) { + return this.getChunk(chunkX, chunkZ); + } + + @Override + public List getEntityCollisions(@Nullable Entity entity, AABB collisionBox) { + return List.of(); + } + + @Nullable + @Override + public BlockEntity getBlockEntity(BlockPos pos) { + ChunkAccess chunkaccess = this.getChunk(pos); + return chunkaccess.getBlockEntity(pos); + } + + @Override + public BlockState getBlockState(BlockPos pos) { + if (this.isOutsideBuildHeight(pos)) { + return Blocks.AIR.defaultBlockState(); + } else { + ChunkAccess chunkaccess = this.getChunk(pos); + return chunkaccess.getBlockState(pos); + } + } + + @Override + public FluidState getFluidState(BlockPos pos) { + if (this.isOutsideBuildHeight(pos)) { + return Fluids.EMPTY.defaultFluidState(); + } else { + ChunkAccess chunkaccess = this.getChunk(pos); + return chunkaccess.getFluidState(pos); + } + } + + @Override + public int getMinBuildHeight() { + return this.level.getMinBuildHeight(); + } + + @Override + public int getHeight() { + return this.level.getHeight(); + } + + public ProfilerFiller getProfiler() { + return this.level.getProfiler(); + } +} diff --git a/src/main/java/mekanism/common/content/miner/ThreadMinerSearch.java b/src/main/java/mekanism/common/content/miner/ThreadMinerSearch.java index bff1e38a96e..442cb599b2d 100644 --- a/src/main/java/mekanism/common/content/miner/ThreadMinerSearch.java +++ b/src/main/java/mekanism/common/content/miner/ThreadMinerSearch.java @@ -22,7 +22,6 @@ import net.minecraft.network.codec.StreamCodec; import net.minecraft.util.ByIdMap; import net.minecraft.world.level.ChunkPos; -import net.minecraft.world.level.PathNavigationRegion; import net.minecraft.world.level.block.BedBlock; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.DoorBlock; @@ -36,7 +35,7 @@ public class ThreadMinerSearch extends Thread { private final TileEntityDigitalMiner tile; private final Long2ObjectMap oresToMine = new Long2ObjectOpenHashMap<>(); - private PathNavigationRegion chunkCache; + private MinerRegionCache chunkCache; public State state = State.IDLE; public int found = 0; @@ -46,7 +45,7 @@ public ThreadMinerSearch(TileEntityDigitalMiner tile) { setDaemon(true); } - public void setChunkCache(PathNavigationRegion cache) { + public void setChunkCache(MinerRegionCache cache) { this.chunkCache = cache; } diff --git a/src/main/java/mekanism/common/tile/machine/TileEntityDigitalMiner.java b/src/main/java/mekanism/common/tile/machine/TileEntityDigitalMiner.java index b5de08789ef..4334f398c3d 100644 --- a/src/main/java/mekanism/common/tile/machine/TileEntityDigitalMiner.java +++ b/src/main/java/mekanism/common/tile/machine/TileEntityDigitalMiner.java @@ -40,6 +40,7 @@ import mekanism.common.config.MekanismConfig; import mekanism.common.content.filter.SortableFilterManager; import mekanism.common.content.miner.MinerFilter; +import mekanism.common.content.miner.MinerRegionCache; import mekanism.common.content.miner.ThreadMinerSearch; import mekanism.common.content.miner.ThreadMinerSearch.State; import mekanism.common.content.network.transmitter.LogisticalTransporterBase; @@ -99,7 +100,6 @@ import net.minecraft.world.item.enchantment.Enchantments; import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.Level; -import net.minecraft.world.level.PathNavigationRegion; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.LevelEvent; import net.minecraft.world.level.block.entity.BlockEntity; @@ -794,7 +794,7 @@ public void start() { if (searcher.state == State.IDLE) { BlockPos startingPos = getStartingPos(); int diameter = getDiameter(); - searcher.setChunkCache(new PathNavigationRegion(getLevel(), startingPos, startingPos.offset(diameter, getMaxY() - getMinY() + 1, diameter))); + searcher.setChunkCache(new MinerRegionCache((ServerLevel) getLevel(), startingPos, startingPos.offset(diameter, getMaxY() - getMinY() + 1, diameter), this.upgradeComponent.isUpgradeInstalled(Upgrade.ANCHOR))); searcher.start(); } running = true;