package dev.cobblesurvival.selftest import dev.cobblesurvival.dev.OreVeins import net.minecraft.core.BlockPos import net.minecraft.core.registries.BuiltInRegistries import net.minecraft.resources.ResourceLocation import net.minecraft.server.level.ServerLevel import net.minecraft.server.level.ServerPlayer import net.minecraft.world.level.ChunkPos import net.minecraft.world.level.ClipContext import net.minecraft.world.level.GameType import net.minecraft.world.level.block.Blocks import net.minecraft.world.phys.Vec3 /** * Roadmap Phase 4: ore veins regrow after their chunk was unloaded and after a server restart (Regenerating Ore * Veins). The tests place the pack's "selftest" vein (one coal block that takes an hour to regrow and never generates * by itself) in the air away from the test platform, mine it, and wind its clock back in the mod's saved data * instead of waiting the hour. The restart case has two halves: the first runs with the other tests, then * tools/quick_check.py restarts the server and runs the second with `/cobblesurvival selftest run after_restart`. */ object VeinTests { private const val VEIN = "selftest" /** Blocks east (unload) and west (restart) of spawn: far beyond view distance from the platform. */ private const val UNLOAD_OFFSET = 320 private const val RESTART_OFFSET = -320 private val REGENERATOR = ResourceLocation.fromNamespaceAndPath("regenerating_ore_veins", "regenerator_block") fun all() = listOf( SelfTestCase("regenerator_lets_the_aim_through", 20, ::aimThrough), SelfTestCase("vein_regrows_after_unload", 1200, ::afterUnload), SelfTestCase("vein_mined_before_restart", 400, ::beforeRestart), SelfTestCase("vein_regrows_after_restart", 400, ::afterRestart, afterRestart = true), ) /** * A mined vein block waiting to grow back doesn't stop the player's aim, so the ore behind it can be mined (Adrian, * 2026-11-09; mixin/RegeneratorBlockMixin.java); while sneaking the player can target it, which opens its info. */ private fun aimThrough(s: Scenario) { val regenerator = BuiltInRegistries.BLOCK.get(ResourceLocation.parse("regenerating_ore_veins:regenerator_block")) s.check(regenerator != Blocks.AIR) { "Regenerating Ore Veins' regenerator isn't installed" } val player = s.player() val pos = s.helper.absolutePos(BlockPos(7, 2, 7)) val behind = pos.east() s.level.setBlockAndUpdate(pos, regenerator.defaultBlockState()) s.level.setBlockAndUpdate(behind, Blocks.STONE.defaultBlockState()) fun aimedAt(): BlockPos { val from = Vec3(pos.x - 2.5, pos.y + 0.5, pos.z + 0.5) return s.level.clip(ClipContext(from, from.add(6.0, 0.0, 0.0), ClipContext.Block.OUTLINE, ClipContext.Fluid.NONE, player)).blockPos } s.check(aimedAt() == behind) { "the aim stops at ${aimedAt().toShortString()}, not at the block behind the regenerator" } player.isShiftKeyDown = true s.check(aimedAt() == pos) { "sneaking, the aim doesn't find the regenerator (it stops at ${aimedAt().toShortString()})" } player.isShiftKeyDown = false s.helper.succeed() } private fun afterUnload(s: Scenario) { val level = s.level val pos = spot(level, UNLOAD_OFFSET) val chunk = ChunkPos(pos) val player = s.player() s.afterTest { level.setChunkForced(chunk.x, chunk.z, false) } s.helper.startSequence() .thenExecute { level.setChunkForced(chunk.x, chunk.z, true) placeAndMine(s, player, pos) } .thenWaitUntil { s.check(isRegenerator(level, pos)) { "the mined vein block is ${level.getBlockState(pos)}, not a regenerator" } } .thenExecute { level.setChunkForced(chunk.x, chunk.z, false) } .thenWaitUntil { s.check(!level.hasChunk(chunk.x, chunk.z)) { "the vein's chunk is still loaded" } } .thenExecute { rewind(level, pos) level.setChunkForced(chunk.x, chunk.z, true) } .thenWaitUntil { s.check(level.getBlockState(pos).`is`(Blocks.COAL_ORE)) { "the vein didn't regrow once its chunk was back: ${level.getBlockState(pos)}" } } .thenExecute { remove(player, pos) } .thenWaitUntil { s.check(level.getBlockState(pos).isAir) { "the vein block wasn't removed: ${level.getBlockState(pos)}" } } .thenSucceed() } private fun beforeRestart(s: Scenario) { val level = s.level val pos = spot(level, RESTART_OFFSET) val chunk = ChunkPos(pos) val player = s.player() s.afterTest { level.setChunkForced(chunk.x, chunk.z, false) } s.helper.startSequence() .thenExecute { level.setChunkForced(chunk.x, chunk.z, true) placeAndMine(s, player, pos) } .thenWaitUntil { s.check(isRegenerator(level, pos)) { "the mined vein block is ${level.getBlockState(pos)}, not a regenerator" } } .thenSucceed() } private fun afterRestart(s: Scenario) { val level = s.level val pos = spot(level, RESTART_OFFSET) val chunk = ChunkPos(pos) val player = s.player() s.afterTest { level.setChunkForced(chunk.x, chunk.z, false) } s.helper.startSequence() .thenExecute { level.setChunkForced(chunk.x, chunk.z, true) } .thenWaitUntil { s.check(isRegenerator(level, pos)) { "no regenerator where the vein was mined before the restart: ${level.getBlockState(pos)}" } } .thenExecute { rewind(level, pos) } .thenWaitUntil { s.check(level.getBlockState(pos).`is`(Blocks.COAL_ORE)) { "the vein didn't regrow after the restart: ${level.getBlockState(pos)}" } } .thenExecute { remove(player, pos) } .thenWaitUntil { s.check(level.getBlockState(pos).isAir) { "the vein block wasn't removed: ${level.getBlockState(pos)}" } } .thenSucceed() } private fun spot(level: ServerLevel, offset: Int): BlockPos = level.sharedSpawnPos.let { BlockPos(it.x + offset, SelfTest.PLATFORM_Y, it.z) } /** Places the vein with the mod's own command and mines its block as a survival player. */ private fun placeAndMine(s: Scenario, player: ServerPlayer, pos: BlockPos) { val level = s.level val server = level.server val source = server.createCommandSourceStack().withLevel(level).withPosition(Vec3.atCenterOf(pos)).withSuppressedOutput() server.commands.performPrefixedCommand(source, "regenerating_ore_veins place $VEIN") s.check(level.getBlockState(pos).`is`(Blocks.COAL_ORE)) { "the vein was not placed: ${level.getBlockState(pos)}" } s.check(player.gameMode.destroyBlock(pos)) { "the vein block could not be mined" } } /** Breaking a regenerator in creative mode removes the vein for good, so the test world keeps no stray ore. */ private fun remove(player: ServerPlayer, pos: BlockPos) { player.setGameMode(GameType.CREATIVE) player.gameMode.destroyBlock(pos) } private fun isRegenerator(level: ServerLevel, pos: BlockPos) = BuiltInRegistries.BLOCK.getKey(level.getBlockState(pos).block) == REGENERATOR /** Moves the vein block's "last mined" time far into the past; the mod keeps it in its saved data. */ private fun rewind(level: ServerLevel, pos: BlockPos) = OreVeins.markMined(level, pos, 1L) }