package ar.com.companeros; import net.minecraft.core.BlockPos; import net.minecraft.gametest.framework.GameTest; import net.minecraft.gametest.framework.GameTestHelper; import net.minecraft.nbt.CompoundTag; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; import net.minecraft.world.level.block.Blocks; import net.minecraftforge.gametest.GameTestHolder; import net.minecraftforge.gametest.PrefixGameTestTemplate; /** Pruebas de inventario y modificaciones reales del mundo en un servidor Forge. */ @GameTestHolder(Companeros.ID) @PrefixGameTestTemplate(false) public final class CompanionTests { @net.minecraft.gametest.framework.BeforeBatch(batch = "legacy") public static void resetProtection(net.minecraft.server.level.ServerLevel level) { // Cada ejecución del banco de pruebas debe comenzar con sus zonas vacías. level.getDataStorage().set("companeros_zonas", new WorkArea()); } private Companion bot(GameTestHelper helper) { Companion bot = Companeros.COMPANION.get().create(helper.getLevel()); bot.home = helper.absolutePos(new BlockPos(4, 2, 4)); bot.moveTo(bot.home.getX() + .5, bot.home.getY(), bot.home.getZ() + .5, 0, 0); bot.radius = 16; bot.mode = "quieto"; helper.getLevel().addFreshEntity(bot); return bot; } @GameTest(template = "empty", batch = "legacy") public void craftingConsumesIngredients(GameTestHelper helper) { Companion bot = bot(helper); helper.assertTrue(!Crafting.make(bot.serverLevel(), bot.bag, Items.OAK_PLANKS, false), "No puede fabricar sin madera"); bot.bag.addItem(new ItemStack(Items.OAK_LOG)); helper.assertTrue(Crafting.make(bot.serverLevel(), bot.bag, Items.OAK_PLANKS, false), "La receta debe funcionar"); helper.assertTrue(bot.count(Items.OAK_LOG) == 0 && bot.count(Items.OAK_PLANKS) == 4, "Consume un tronco y produce cuatro tablas"); helper.assertTrue(!Crafting.make(bot.serverLevel(), bot.bag, Items.WOODEN_PICKAXE, false), "No fabrica herramientas sin mesa"); helper.succeed(); } @GameTest(template = "empty", batch = "legacy") public void hungerAndSavePersist(GameTestHelper helper) { Companion bot = bot(helper); bot.hunger = 10; bot.bag.addItem(new ItemStack(Items.BREAD, 2)); helper.assertTrue(bot.eat(), "Puede comer"); helper.assertTrue(bot.hunger == 15 && bot.count(Items.BREAD) == 1, "Comer consume alimento"); bot.building = true; bot.shelterStep = 8; bot.owner = java.util.UUID.randomUUID(); CompoundTag tag = new CompoundTag(); bot.addAdditionalSaveData(tag); Companion loaded = Companeros.COMPANION.get().create(helper.getLevel()); loaded.readAdditionalSaveData(tag); helper.assertTrue(loaded.count(Items.BREAD) == 1 && loaded.hunger == 15 && loaded.shelterStep == 8 && loaded.home.equals(bot.home) && loaded.owner.equals(bot.owner), "Mantiene el progreso al cargar"); helper.succeed(); } @GameTest(template = "empty", batch = "legacy") public void protectedBlocksAndAreas(GameTestHelper helper) { Companion bot = bot(helper); BlockPos tree = bot.home.offset(7, 0, 0); helper.getLevel().setBlock(tree, Blocks.OAK_LOG.defaultBlockState(), 3); helper.getLevel().setBlock(tree.above(2), Blocks.OAK_LEAVES.defaultBlockState(), 3); helper.assertTrue(WorldActions.isResource(bot, tree, "wood"), "Detecta árbol con hojas"); WorkArea.get(helper.getLevel()).protect(tree); helper.assertTrue(!WorldActions.isResource(bot, tree, "wood"), "No corta un bloque protegido"); helper.assertTrue(!WorldActions.allowed(bot, bot.home.offset(40, 0, 0)), "No sale de su zona"); helper.succeed(); } @GameTest(template = "empty", batch = "legacy") public void placementConsumesMaterial(GameTestHelper helper) { Companion bot = bot(helper); BlockPos wall = bot.home.offset(2, 0, 0); helper.getLevel().setBlock(wall.below(), Blocks.DIRT.defaultBlockState(), 3); bot.bag.addItem(new ItemStack(Items.OAK_PLANKS, 2)); ItemStack stack = bot.bag.getItem(0); helper.assertTrue(WorldActions.place(bot, wall, stack), "Coloca pared sobre suelo"); helper.assertTrue(helper.getLevel().getBlockState(wall).is(Blocks.OAK_PLANKS) && stack.getCount() == 1, "La pared cuesta una tabla"); helper.assertTrue(!WorldActions.place(bot, wall, stack) && stack.getCount() == 1, "No reemplaza ni cobra dos veces"); helper.succeed(); } @GameTest(template = "empty", batch = "legacy") public void shelterBuiltFromInventory(GameTestHelper helper) { Companion bot = bot(helper); for (int x = -2; x <= 2; x++) for (int z = -2; z <= 2; z++) helper.getLevel().setBlock(bot.home.offset(x, -1, z), Blocks.DIRT.defaultBlockState(), 3); helper.assertTrue(Shelter.clearSite(bot, bot.home), "Campamento despejado"); bot.bag.addItem(new ItemStack(Items.OAK_PLANKS, 64)); bot.bag.addItem(new ItemStack(Items.OAK_PLANKS, 7)); bot.bag.addItem(new ItemStack(Items.OAK_DOOR)); bot.building = true; for (int i = 0; i < 80 && bot.building && !Shelter.complete(bot); i++) Shelter.step(bot); helper.assertTrue(Shelter.complete(bot), "Termina paredes, techo y puerta"); helper.assertTrue(bot.count(Items.OAK_PLANKS) == 0 && bot.count(Items.OAK_DOOR) == 0, "Utiliza todos los materiales exactos"); helper.assertTrue(helper.getLevel().getBlockState(bot.home.above(3)).is(Blocks.OAK_PLANKS), "Techo construido"); helper.assertTrue(helper.getLevel().getBlockState(bot.home.offset(0, 0, -2)).is(Blocks.OAK_DOOR), "Entrada con puerta"); helper.succeed(); } @GameTest(template = "empty", batch = "legacy") public void harvestingUsesRealLoot(GameTestHelper helper) { Companion bot = bot(helper); BlockPos tree = bot.home.offset(7, 0, 0); helper.getLevel().setBlock(tree, Blocks.OAK_LOG.defaultBlockState(), 3); helper.getLevel().setBlock(tree.above(2), Blocks.OAK_LEAVES.defaultBlockState(), 3); bot.moveTo(tree.getX() - 1, tree.getY(), tree.getZ() + .5, 0, 0); helper.getLevel().setBlock(bot.blockPosition().below(), Blocks.DIRT.defaultBlockState(), 3); bot.setOnGround(true); for (int i = 0; i < 100; i++) if (WorldActions.mine(bot, tree, "wood")) break; helper.assertTrue(helper.getLevel().getBlockState(tree).isAir(), "Corta árbol"); helper.assertTrue(bot.count(Items.OAK_LOG) == 1, "Recibe el botín real una sola vez"); WorldActions.mine(bot, tree, "wood"); helper.assertTrue(bot.count(Items.OAK_LOG) == 1, "No duplica botín"); helper.succeed(); } @GameTest(template = "empty", batch = "legacy", timeoutTicks = 500) public void autonomousForagingMovesAndEats(GameTestHelper helper) { helper.getLevel().setDayTime(1000); Companion bot = new Companion(Companeros.COMPANION.get(), helper.getLevel()) { @Override public boolean humanPresent() { return true; } }; bot.home = helper.absolutePos(new BlockPos(4, 2, 4)); bot.moveTo(bot.home.getX() + .5, bot.home.getY(), bot.home.getZ() + .5, 0, 0); bot.radius = 8; bot.hunger = 10; bot.mode = "autonomo"; for (int x = 0; x <= 13; x++) for (int z = 0; z <= 13; z++) helper.setBlock(new BlockPos(x, 1, z), Blocks.DIRT); BlockPos berries = bot.home.offset(6, 0, 0); helper.getLevel().setChunkForced(bot.home.getX() >> 4, bot.home.getZ() >> 4, true); helper.getLevel().setChunkForced(berries.getX() >> 4, berries.getZ() >> 4, true); helper.getLevel().setBlock(berries, Blocks.SWEET_BERRY_BUSH.defaultBlockState() .setValue(net.minecraft.world.level.block.SweetBerryBushBlock.AGE, 3), 3); helper.getLevel().addFreshEntity(bot); helper.succeedWhen(() -> { helper.assertTrue(bot.hunger > 10, "El cerebro debe buscar, caminar, recolectar y comer: " + bot.activity + ", ticks=" + bot.workTicks + ", recurso=" + bot.resource + ", posición=" + bot.blockPosition()); helper.assertTrue(bot.getX() > bot.home.getX() + 1, "Se desplazó hacia la comida"); }); } @GameTest(template = "empty", batch = "legacy") public void fullInventoryDoesNotLoseIngredients(GameTestHelper helper) { Companion bot = bot(helper); bot.bag.setItem(0, new ItemStack(Items.OAK_LOG, 2)); for (int i = 1; i < 36; i++) bot.bag.setItem(i, new ItemStack(Items.COBBLESTONE, 64)); helper.assertTrue(!Crafting.make(bot.serverLevel(), bot.bag, Items.OAK_PLANKS, false), "No cabe la fabricación"); helper.assertTrue(bot.count(Items.OAK_LOG) == 2 && bot.count(Items.OAK_PLANKS) == 0, "Conserva ingredientes cuando no hay espacio"); helper.succeed(); } public static final class PlacementVeto { private final BlockPos pos; public PlacementVeto(BlockPos pos) { this.pos = pos; } @net.minecraftforge.eventbus.api.SubscribeEvent public void cancel(net.minecraftforge.event.level.BlockEvent.EntityPlaceEvent event) { if (event.getPos().equals(pos)) event.setCanceled(true); } } @GameTest(template = "empty", batch = "legacy") public void placementRespectsProtectionEvents(GameTestHelper helper) { Companion bot = bot(helper); BlockPos wall = bot.home.offset(2, 0, 0); helper.getLevel().setBlock(wall.below(), Blocks.DIRT.defaultBlockState(), 3); bot.bag.addItem(new ItemStack(Items.OAK_PLANKS, 2)); var veto = new PlacementVeto(wall); net.minecraftforge.common.MinecraftForge.EVENT_BUS.register(veto); try { helper.assertTrue(!WorldActions.place(bot, wall, bot.bag.getItem(0)), "Una protección puede cancelar la acción"); helper.assertTrue(helper.getLevel().getBlockState(wall).isAir() && bot.count(Items.OAK_PLANKS) == 2, "La cancelación restaura el bloque y los materiales"); } finally { net.minecraftforge.common.MinecraftForge.EVENT_BUS.unregister(veto); } helper.succeed(); } }