mirror of
				https://github.com/SquidDev-CC/CC-Tweaked
				synced 2025-10-31 13:42:59 +00:00 
			
		
		
		
	Add a test for exploding turtles
There's been a couple of bug reports in the past where the game would crash if a turtle is destroyed while breaking a block (typically due to the block exploding). This commit adds a test, to ensure that this is handled gracefully. I'm not entirely sure this is testing the right thing. Looking at the issues in question, it doesn't look like I ever managed to reproduce the bug. However, it's hopefully at least a quick sanity test to check we never break this case.
This commit is contained in:
		| @@ -693,6 +693,20 @@ class Turtle_Test { | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Tests a turtle can break a block that explodes, causing the turtle itself to explode. | ||||
|      * | ||||
|      * This attempts to test [#585](https://github.com/cc-tweaked/CC-Tweaked/issues/585) and other similar issues. It's | ||||
|      * not clear if this is a good test case, as that bug does not seem reliably reproducible, but it's at least a good | ||||
|      * sanity check. | ||||
|      */ | ||||
|     @GameTest | ||||
|     fun Breaks_exploding_block(context: GameTestHelper) = context.sequence { | ||||
|         thenOnComputer { turtle.dig(Optional.empty()) } | ||||
|         thenIdle(2) | ||||
|         thenExecute { context.assertItemEntityPresent(ModRegistry.Items.TURTLE_NORMAL.get(), BlockPos(2, 2, 2), 1.0) } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Render turtles as an item. | ||||
|      */ | ||||
|   | ||||
| @@ -13,7 +13,13 @@ import dan200.computercraft.shared.computer.core.ServerContext | ||||
| import net.minecraft.core.BlockPos | ||||
| import net.minecraft.gametest.framework.* | ||||
| import net.minecraft.server.MinecraftServer | ||||
| import net.minecraft.server.level.ServerLevel | ||||
| import net.minecraft.world.level.GameRules | ||||
| import net.minecraft.world.level.Level | ||||
| import net.minecraft.world.level.LevelAccessor | ||||
| import net.minecraft.world.level.block.Blocks | ||||
| import net.minecraft.world.level.block.state.BlockState | ||||
| import net.minecraft.world.phys.Vec3 | ||||
| import org.slf4j.Logger | ||||
| import org.slf4j.LoggerFactory | ||||
| import java.io.File | ||||
| @@ -173,4 +179,23 @@ object TestHooks { | ||||
|             throw RuntimeException(e) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Adds a hook that makes breaking a bone block spawn an explosion. | ||||
|      * | ||||
|      * It would be more Correct to register a custom block, but that's quite a lot of work, and doesn't seem worth it | ||||
|      * for test code. | ||||
|      * | ||||
|      * See also [Turtle_Test.Breaks_exploding_block]. | ||||
|      */ | ||||
|     @JvmStatic | ||||
|     fun onBeforeDestroyBlock(level: LevelAccessor, pos: BlockPos, state: BlockState): Boolean { | ||||
|         if (state.block === Blocks.BONE_BLOCK && level is ServerLevel) { | ||||
|             val explosionPos = Vec3.atCenterOf(pos) | ||||
|             level.explode(null, explosionPos.x, explosionPos.y, explosionPos.z, 4.0f, Level.ExplosionInteraction.TNT) | ||||
|             return true | ||||
|         } | ||||
| 
 | ||||
|         return false | ||||
|     } | ||||
| } | ||||
|   | ||||
							
								
								
									
										139
									
								
								projects/common/src/testMod/resources/data/cctest/structures/turtle_test.breaks_exploding_block.snbt
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										139
									
								
								projects/common/src/testMod/resources/data/cctest/structures/turtle_test.breaks_exploding_block.snbt
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,139 @@ | ||||
| { | ||||
|     DataVersion: 3465, | ||||
|     size: [5, 5, 5], | ||||
|     data: [ | ||||
|         {pos: [0, 0, 0], state: "minecraft:obsidian"}, | ||||
|         {pos: [0, 0, 1], state: "minecraft:obsidian"}, | ||||
|         {pos: [0, 0, 2], state: "minecraft:obsidian"}, | ||||
|         {pos: [0, 0, 3], state: "minecraft:obsidian"}, | ||||
|         {pos: [0, 0, 4], state: "minecraft:obsidian"}, | ||||
|         {pos: [1, 0, 0], state: "minecraft:obsidian"}, | ||||
|         {pos: [1, 0, 1], state: "minecraft:obsidian"}, | ||||
|         {pos: [1, 0, 2], state: "minecraft:obsidian"}, | ||||
|         {pos: [1, 0, 3], state: "minecraft:obsidian"}, | ||||
|         {pos: [1, 0, 4], state: "minecraft:obsidian"}, | ||||
|         {pos: [2, 0, 0], state: "minecraft:obsidian"}, | ||||
|         {pos: [2, 0, 1], state: "minecraft:obsidian"}, | ||||
|         {pos: [2, 0, 2], state: "minecraft:obsidian"}, | ||||
|         {pos: [2, 0, 3], state: "minecraft:obsidian"}, | ||||
|         {pos: [2, 0, 4], state: "minecraft:obsidian"}, | ||||
|         {pos: [3, 0, 0], state: "minecraft:obsidian"}, | ||||
|         {pos: [3, 0, 1], state: "minecraft:obsidian"}, | ||||
|         {pos: [3, 0, 2], state: "minecraft:obsidian"}, | ||||
|         {pos: [3, 0, 3], state: "minecraft:obsidian"}, | ||||
|         {pos: [3, 0, 4], state: "minecraft:obsidian"}, | ||||
|         {pos: [4, 0, 0], state: "minecraft:obsidian"}, | ||||
|         {pos: [4, 0, 1], state: "minecraft:obsidian"}, | ||||
|         {pos: [4, 0, 2], state: "minecraft:obsidian"}, | ||||
|         {pos: [4, 0, 3], state: "minecraft:obsidian"}, | ||||
|         {pos: [4, 0, 4], state: "minecraft:obsidian"}, | ||||
|         {pos: [0, 1, 0], state: "minecraft:barrier"}, | ||||
|         {pos: [0, 1, 1], state: "minecraft:barrier"}, | ||||
|         {pos: [0, 1, 2], state: "minecraft:barrier"}, | ||||
|         {pos: [0, 1, 3], state: "minecraft:barrier"}, | ||||
|         {pos: [0, 1, 4], state: "minecraft:barrier"}, | ||||
|         {pos: [1, 1, 0], state: "minecraft:barrier"}, | ||||
|         {pos: [1, 1, 1], state: "minecraft:air"}, | ||||
|         {pos: [1, 1, 2], state: "minecraft:air"}, | ||||
|         {pos: [1, 1, 3], state: "minecraft:air"}, | ||||
|         {pos: [1, 1, 4], state: "minecraft:barrier"}, | ||||
|         {pos: [2, 1, 0], state: "minecraft:barrier"}, | ||||
|         {pos: [2, 1, 1], state: "minecraft:air"}, | ||||
|         {pos: [2, 1, 2], state: "computercraft:turtle_normal{facing:south,waterlogged:false}", nbt: {ComputerId: 1, Fuel: 0, Items: [], Label: "turtle_test.breaks_exploding_block", LeftUpgrade: "minecraft:diamond_pickaxe", LeftUpgradeNbt: {Tag: {Damage: 0}}, On: 1b, Owner: {LowerId: -5670393268852517359L, Name: "Player172", UpperId: 3578583684139923613L}, Slot: 0, id: "computercraft:turtle_normal"}}, | ||||
|         {pos: [2, 1, 3], state: "minecraft:bone_block{axis:y}"}, | ||||
|         {pos: [2, 1, 4], state: "minecraft:barrier"}, | ||||
|         {pos: [3, 1, 0], state: "minecraft:barrier"}, | ||||
|         {pos: [3, 1, 1], state: "minecraft:air"}, | ||||
|         {pos: [3, 1, 2], state: "minecraft:air"}, | ||||
|         {pos: [3, 1, 3], state: "minecraft:air"}, | ||||
|         {pos: [3, 1, 4], state: "minecraft:barrier"}, | ||||
|         {pos: [4, 1, 0], state: "minecraft:barrier"}, | ||||
|         {pos: [4, 1, 1], state: "minecraft:barrier"}, | ||||
|         {pos: [4, 1, 2], state: "minecraft:barrier"}, | ||||
|         {pos: [4, 1, 3], state: "minecraft:barrier"}, | ||||
|         {pos: [4, 1, 4], state: "minecraft:barrier"}, | ||||
|         {pos: [0, 2, 0], state: "minecraft:barrier"}, | ||||
|         {pos: [0, 2, 1], state: "minecraft:barrier"}, | ||||
|         {pos: [0, 2, 2], state: "minecraft:air"}, | ||||
|         {pos: [0, 2, 3], state: "minecraft:barrier"}, | ||||
|         {pos: [0, 2, 4], state: "minecraft:barrier"}, | ||||
|         {pos: [1, 2, 0], state: "minecraft:barrier"}, | ||||
|         {pos: [1, 2, 1], state: "minecraft:air"}, | ||||
|         {pos: [1, 2, 2], state: "minecraft:air"}, | ||||
|         {pos: [1, 2, 3], state: "minecraft:air"}, | ||||
|         {pos: [1, 2, 4], state: "minecraft:barrier"}, | ||||
|         {pos: [2, 2, 0], state: "minecraft:barrier"}, | ||||
|         {pos: [2, 2, 1], state: "minecraft:air"}, | ||||
|         {pos: [2, 2, 2], state: "minecraft:air"}, | ||||
|         {pos: [2, 2, 3], state: "minecraft:air"}, | ||||
|         {pos: [2, 2, 4], state: "minecraft:barrier"}, | ||||
|         {pos: [3, 2, 0], state: "minecraft:barrier"}, | ||||
|         {pos: [3, 2, 1], state: "minecraft:air"}, | ||||
|         {pos: [3, 2, 2], state: "minecraft:air"}, | ||||
|         {pos: [3, 2, 3], state: "minecraft:air"}, | ||||
|         {pos: [3, 2, 4], state: "minecraft:barrier"}, | ||||
|         {pos: [4, 2, 0], state: "minecraft:barrier"}, | ||||
|         {pos: [4, 2, 1], state: "minecraft:barrier"}, | ||||
|         {pos: [4, 2, 2], state: "minecraft:barrier"}, | ||||
|         {pos: [4, 2, 3], state: "minecraft:barrier"}, | ||||
|         {pos: [4, 2, 4], state: "minecraft:barrier"}, | ||||
|         {pos: [0, 3, 0], state: "minecraft:air"}, | ||||
|         {pos: [0, 3, 1], state: "minecraft:air"}, | ||||
|         {pos: [0, 3, 2], state: "minecraft:air"}, | ||||
|         {pos: [0, 3, 3], state: "minecraft:air"}, | ||||
|         {pos: [0, 3, 4], state: "minecraft:air"}, | ||||
|         {pos: [1, 3, 0], state: "minecraft:air"}, | ||||
|         {pos: [1, 3, 1], state: "minecraft:air"}, | ||||
|         {pos: [1, 3, 2], state: "minecraft:air"}, | ||||
|         {pos: [1, 3, 3], state: "minecraft:air"}, | ||||
|         {pos: [1, 3, 4], state: "minecraft:air"}, | ||||
|         {pos: [2, 3, 0], state: "minecraft:air"}, | ||||
|         {pos: [2, 3, 1], state: "minecraft:air"}, | ||||
|         {pos: [2, 3, 2], state: "minecraft:air"}, | ||||
|         {pos: [2, 3, 3], state: "minecraft:air"}, | ||||
|         {pos: [2, 3, 4], state: "minecraft:air"}, | ||||
|         {pos: [3, 3, 0], state: "minecraft:air"}, | ||||
|         {pos: [3, 3, 1], state: "minecraft:air"}, | ||||
|         {pos: [3, 3, 2], state: "minecraft:air"}, | ||||
|         {pos: [3, 3, 3], state: "minecraft:air"}, | ||||
|         {pos: [3, 3, 4], state: "minecraft:air"}, | ||||
|         {pos: [4, 3, 0], state: "minecraft:air"}, | ||||
|         {pos: [4, 3, 1], state: "minecraft:air"}, | ||||
|         {pos: [4, 3, 2], state: "minecraft:air"}, | ||||
|         {pos: [4, 3, 3], state: "minecraft:air"}, | ||||
|         {pos: [4, 3, 4], state: "minecraft:air"}, | ||||
|         {pos: [0, 4, 0], state: "minecraft:air"}, | ||||
|         {pos: [0, 4, 1], state: "minecraft:air"}, | ||||
|         {pos: [0, 4, 2], state: "minecraft:air"}, | ||||
|         {pos: [0, 4, 3], state: "minecraft:air"}, | ||||
|         {pos: [0, 4, 4], state: "minecraft:air"}, | ||||
|         {pos: [1, 4, 0], state: "minecraft:air"}, | ||||
|         {pos: [1, 4, 1], state: "minecraft:air"}, | ||||
|         {pos: [1, 4, 2], state: "minecraft:air"}, | ||||
|         {pos: [1, 4, 3], state: "minecraft:air"}, | ||||
|         {pos: [1, 4, 4], state: "minecraft:air"}, | ||||
|         {pos: [2, 4, 0], state: "minecraft:air"}, | ||||
|         {pos: [2, 4, 1], state: "minecraft:air"}, | ||||
|         {pos: [2, 4, 2], state: "minecraft:air"}, | ||||
|         {pos: [2, 4, 3], state: "minecraft:air"}, | ||||
|         {pos: [2, 4, 4], state: "minecraft:air"}, | ||||
|         {pos: [3, 4, 0], state: "minecraft:air"}, | ||||
|         {pos: [3, 4, 1], state: "minecraft:air"}, | ||||
|         {pos: [3, 4, 2], state: "minecraft:air"}, | ||||
|         {pos: [3, 4, 3], state: "minecraft:air"}, | ||||
|         {pos: [3, 4, 4], state: "minecraft:air"}, | ||||
|         {pos: [4, 4, 0], state: "minecraft:air"}, | ||||
|         {pos: [4, 4, 1], state: "minecraft:air"}, | ||||
|         {pos: [4, 4, 2], state: "minecraft:air"}, | ||||
|         {pos: [4, 4, 3], state: "minecraft:air"}, | ||||
|         {pos: [4, 4, 4], state: "minecraft:air"} | ||||
|     ], | ||||
|     entities: [], | ||||
|     palette: [ | ||||
|         "minecraft:obsidian", | ||||
|         "minecraft:barrier", | ||||
|         "minecraft:bone_block{axis:y}", | ||||
|         "minecraft:air", | ||||
|         "computercraft:turtle_normal{facing:south,waterlogged:false}" | ||||
|     ] | ||||
| } | ||||
| @@ -14,6 +14,7 @@ import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback; | ||||
| import net.fabricmc.fabric.api.event.Event; | ||||
| import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents; | ||||
| import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents; | ||||
| import net.fabricmc.fabric.api.event.player.PlayerBlockBreakEvents; | ||||
| import net.minecraft.gametest.framework.GameTestRegistry; | ||||
| import net.minecraft.resources.ResourceLocation; | ||||
| 
 | ||||
| @@ -26,6 +27,7 @@ public class TestMod implements ModInitializer, ClientModInitializer { | ||||
|         ServerLifecycleEvents.SERVER_STARTED.addPhaseOrdering(Event.DEFAULT_PHASE, phase); | ||||
|         ServerLifecycleEvents.SERVER_STARTED.register(phase, TestHooks::onServerStarted); | ||||
|         CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> CCTestCommand.register(dispatcher)); | ||||
|         PlayerBlockBreakEvents.BEFORE.register((level, player, pos, state, blockEntity) -> !TestHooks.onBeforeDestroyBlock(level, pos, state)); | ||||
| 
 | ||||
|         TestHooks.loadTests(GameTestRegistry::register); | ||||
|     } | ||||
|   | ||||
| @@ -12,6 +12,7 @@ import net.minecraftforge.common.MinecraftForge; | ||||
| import net.minecraftforge.event.RegisterCommandsEvent; | ||||
| import net.minecraftforge.event.RegisterGameTestsEvent; | ||||
| import net.minecraftforge.event.TickEvent; | ||||
| import net.minecraftforge.event.level.BlockEvent; | ||||
| import net.minecraftforge.event.server.ServerStartedEvent; | ||||
| import net.minecraftforge.eventbus.api.EventPriority; | ||||
| import net.minecraftforge.fml.DistExecutor; | ||||
| @@ -26,6 +27,10 @@ public class TestMod { | ||||
|         var bus = MinecraftForge.EVENT_BUS; | ||||
|         bus.addListener(EventPriority.LOW, (ServerStartedEvent e) -> TestHooks.onServerStarted(e.getServer())); | ||||
|         bus.addListener((RegisterCommandsEvent e) -> CCTestCommand.register(e.getDispatcher())); | ||||
|         bus.addListener((BlockEvent.BreakEvent e) -> { | ||||
|             if (TestHooks.onBeforeDestroyBlock(e.getLevel(), e.getPos(), e.getState())) e.setCanceled(true); | ||||
|         }); | ||||
| 
 | ||||
|         DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> TestMod::onInitializeClient); | ||||
| 
 | ||||
|         var modBus = FMLJavaModLoadingContext.get().getModEventBus(); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Jonathan Coates
					Jonathan Coates