Phase 4: regenerating ore veins, each tier in one zone
Regenerating Ore Veins 1.4.0 (CurseForge only, MIT). tools/zone_ores.py writes its config from world/zones.json: surface zones as boxes from the zone grid, cave tiers by cave biome, 21 tiers including Cobblemon's evolution stones (their natural ores are off). The world report now allows ore only in tracked veins inside their tier's zone. Self-tests: a mined vein regrows after its chunk unloads and after a restart (new after_restart pass in selftest.py and quick_check.py). make_world places showcase veins. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
82f540aa51
commit
5d275e4e14
19 files changed
+10843
-37
No files matched your search
@@ -0,0 +1,122 @@
|
||||
package dev.cobblesurvival.selftest
|
||||
|
||||
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.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("vein_regrows_after_unload", 1200, ::afterUnload),
|
||||
SelfTestCase("vein_mined_before_restart", 400, ::beforeRestart),
|
||||
SelfTestCase("vein_regrows_after_restart", 400, ::afterRestart, afterRestart = true),
|
||||
)
|
||||
|
||||
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) {
|
||||
val data = Class.forName("net.stargazer.regenerating_ore_veins.VeinSavedData")
|
||||
val saved = data.getMethod("get", ServerLevel::class.java).invoke(null, level)
|
||||
data.getMethod("markMined", BlockPos::class.java, Long::class.javaPrimitiveType).invoke(saved, pos, 1L)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user