Volver al índice

src/main/java/ar/com/companeros/CompanionTests.java

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();
    }
}