Initial commit
Some checks failed
Build / build (push) Has been cancelled

This commit is contained in:
Shinuwa 2026-09-18 14:54:32 +02:00
commit 07eb6dc053
318 changed files with 339857 additions and 0 deletions

View file

@ -0,0 +1,656 @@
package fr.shinuwa.counterofshinuwa.test;
import fr.shinuwa.counterofshinuwa.*;
import fr.shinuwa.counterofshinuwa.block.ShopInventoryBlockEntity;
import fr.shinuwa.counterofshinuwa.core.*;
import fr.shinuwa.counterofshinuwa.gameplay.*;
import fr.shinuwa.counterofshinuwa.integration.easynpc.EasyNpcAdapter;
import fr.shinuwa.counterofshinuwa.menu.ShopMenu;
import fr.shinuwa.counterofshinuwa.production.*;
import java.util.*;
import net.minecraft.core.BlockPos;
import net.minecraft.gametest.framework.*;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.SimpleContainer;
import net.minecraft.world.inventory.*;
import net.minecraft.world.item.*;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.entity.AbstractFurnaceBlockEntity;
import net.neoforged.fml.common.Mod;
import net.neoforged.neoforge.gametest.*;
@Mod("counter_of_shinuwa_tests")
@GameTestHolder(CounterOfShinuwa.MODID)
@PrefixGameTestTemplate(false)
public final class ShopGameTests {
public ShopGameTests() {
TestProductionIntegration.register();
net.neoforged.neoforge.common.NeoForge.EVENT_BUS.addListener(
(net.neoforged.neoforge.event.server.ServerStartingEvent event) -> {
event.getServer().getPlayerList().setViewDistance(2);
event.getServer().getPlayerList().setSimulationDistance(2);
});
}
private record Fixture(
ServerPlayer player,
ShopManager manager,
Shop shop,
ShopSession session,
BlockPos crafting,
BlockPos furnace) {
void finish(GameTestHelper helper) {
var active = manager.session(shop.id);
if (active != null) manager.close(active, false);
manager.data.shops.remove(shop.id);
manager.data.setDirty();
player.server.getPlayerList().remove(player);
helper.succeed();
}
}
private static Fixture setup(GameTestHelper h) {
for (int x = 0; x < 16; x++) for (int z = 0; z < 16; z++) h.setBlock(x, 0, z, Blocks.STONE);
h.setBlock(1, 1, 1, CounterOfShinuwa.MARKER.get());
h.setBlock(14, 4, 14, CounterOfShinuwa.MARKER.get());
h.setBlock(8, 1, 5, CounterOfShinuwa.COUNTER.get());
h.setBlock(4, 1, 3, CounterOfShinuwa.STORAGE.get());
h.setBlock(2, 1, 5, CounterOfShinuwa.ACCESS.get());
h.setBlock(2, 1, 7, CounterOfShinuwa.ACCESS.get());
for (int x = 3; x <= 7; x++) h.setBlock(x, 1, 5, Blocks.BLUE_CARPET);
h.setBlock(8, 1, 6, Blocks.RED_CARPET);
for (int x = 3; x <= 8; x++) h.setBlock(x, 1, 7, Blocks.RED_CARPET);
h.setBlock(6, 1, 3, Blocks.CRAFTING_TABLE);
h.setBlock(8, 1, 3, Blocks.FURNACE);
ServerPlayer p = h.makeMockServerPlayerInLevel();
BlockPos stand = h.absolutePos(new BlockPos(5, 1, 4));
p.setPos(stand.getX() + .5, stand.getY(), stand.getZ() + .5);
var manager = ShopManager.get(h.getLevel().getServer());
Shop shop = manager.create(p, h.absolutePos(new BlockPos(8, 1, 5)));
shop.cornerA = h.absolutePos(new BlockPos(1, 1, 1));
shop.cornerB = h.absolutePos(new BlockPos(14, 4, 14));
shop.entry = h.absolutePos(new BlockPos(2, 1, 5));
shop.exit = h.absolutePos(new BlockPos(2, 1, 7));
shop.storages.add(h.absolutePos(new BlockPos(4, 1, 3)));
((ShopInventoryBlockEntity) h.getLevel().getBlockEntity(shop.counter)).owner = p.getUUID();
var storage =
(ShopInventoryBlockEntity) h.getLevel().getBlockEntity(shop.storages.iterator().next());
storage.owner = p.getUUID();
storage.setItem(0, new ItemStack(Items.OAK_LOG, 32));
storage.setItem(1, new ItemStack(Items.RAW_IRON, 16));
storage.setItem(2, new ItemStack(Items.COAL, 8));
var report = manager.verify(shop);
h.assertTrue(report.valid(), "Shop invalid: " + report.errors());
String opened = manager.open(p, shop, ShopGameplayMode.CHILL);
h.assertTrue(opened.equals("opened"), "Open failed: " + opened);
var s = manager.session(shop.id);
s.nextArrival = Long.MAX_VALUE;
return new Fixture(
p,
manager,
shop,
s,
h.absolutePos(new BlockPos(6, 1, 3)),
h.absolutePos(new BlockPos(8, 1, 3)));
}
private static ItemStack stamped(Fixture f, Item item, int n) {
var stack = new ItemStack(item, n);
stack.set(
CounterOfShinuwa.PRODUCTION.get(),
new ShopProductionData(
f.shop.id,
f.session.id,
f.player.getUUID(),
"counter_of_shinuwa:manual",
"counter_of_shinuwa:crafting"));
return stack;
}
@GameTest(template = "empty")
public static void persistenceAndModeIsolation(GameTestHelper h) {
Fixture f = setup(h);
var reloaded = Shop.load(f.shop.save());
h.assertTrue(
reloaded.owner.equals(f.shop.owner)
&& reloaded.counter.equals(f.shop.counter)
&& reloaded.storages.equals(f.shop.storages),
"Shop persistence");
ItemStack old = stamped(f, Items.OAK_PLANKS, 4);
f.manager.data.reputation(f.shop.owner, 500);
f.manager.close(f.session, false);
h.assertTrue(
f.manager.open(f.player, f.shop, ShopGameplayMode.STANDARD).equals("opened"),
"Switch mode");
var next = f.manager.session(f.shop.id);
h.assertTrue(!ShopProductionTracker.valid(next, old), "Old session accepted");
h.assertTrue(
f.manager.reputation(f.shop.owner) == 500, "Reputation changed during neutral close");
f.manager.close(next, false);
h.assertTrue(
f.manager.open(f.player, f.shop, ShopGameplayMode.CHILL).equals("opened"),
"Standard to Chill");
var chill = f.manager.session(f.shop.id);
for (var outcome : List.of(OrderOutcome.SUCCESS, OrderOutcome.REFUSED, OrderOutcome.TIMEOUT)) {
chill.order =
new ShopOrder(
UUID.randomUUID(),
UUID.randomUUID(),
Map.of("minecraft:oak_planks", 1),
0,
Long.MAX_VALUE);
f.manager.finish(chill, outcome);
h.assertTrue(
f.manager.reputation(f.shop.owner) == 500, "Chill reputation changed: " + outcome);
}
f.manager.close(chill, false);
h.assertTrue(
f.manager.open(f.player, f.shop, ShopGameplayMode.STANDARD).equals("opened"),
"Chill back to Standard");
h.assertTrue(
!f.manager.session(f.shop.id).quantities, "Advanced snapshot threshold not applied");
f.finish(h);
}
@GameTest(template = "empty")
public static void exactAtomicDelivery(GameTestHelper h) {
Fixture f = setup(h);
var stock =
new SimpleContainer(
stamped(f, Items.STONE, 64),
stamped(f, Items.TORCH, 5),
stamped(f, Items.TORCH, 3),
new ItemStack(Items.DIAMOND));
h.assertTrue(
!Delivery.consume(stock, f.session, Map.of("minecraft:stone", 24, "minecraft:torch", 9)),
"Partial delivery accepted");
h.assertTrue(stock.getItem(0).getCount() == 64, "Failed transaction consumed items");
h.assertTrue(
Delivery.consume(stock, f.session, Map.of("minecraft:stone", 24, "minecraft:torch", 8)),
"Valid split stacks rejected");
h.assertTrue(
stock.getItem(0).getCount() == 40
&& stock.getItem(1).isEmpty()
&& stock.getItem(2).isEmpty()
&& stock.getItem(3).is(Items.DIAMOND),
"Surplus mismatch");
f.finish(h);
}
@GameTest(template = "empty")
public static void shiftCraftingAndFullChain(GameTestHelper h) {
Fixture f = setup(h);
var menu =
new CraftingMenu(
1, f.player.getInventory(), ContainerLevelAccess.create(h.getLevel(), f.crafting));
f.player.containerMenu = menu;
menu.getSlot(1).setByPlayer(new ItemStack(Items.OAK_LOG, 2));
menu.clicked(0, 0, ClickType.QUICK_MOVE, f.player);
int valid = 0;
for (ItemStack stack : f.player.getInventory().items)
if (stack.is(Items.OAK_PLANKS) && ShopProductionTracker.valid(f.session, stack))
valid += stack.getCount();
h.assertTrue(valid == 8, "Shift crafting should mark exactly 8 planks, got " + valid);
for (int i = 1; i <= 9; i++) menu.getSlot(i).setByPlayer(ItemStack.EMPTY);
menu.getSlot(1).setByPlayer(new ItemStack(Items.OAK_PLANKS));
menu.getSlot(4).setByPlayer(new ItemStack(Items.OAK_PLANKS));
menu.clicked(0, 0, ClickType.PICKUP, f.player);
h.assertTrue(
menu.getCarried().is(Items.STICK)
&& !ShopProductionTracker.valid(f.session, menu.getCarried()),
"External intermediate laundered");
f.finish(h);
}
@GameTest(template = "empty")
public static void inventoryCrafting(GameTestHelper h) {
Fixture f = setup(h);
var menu = f.player.inventoryMenu;
f.player.containerMenu = menu;
menu.getSlot(1).setByPlayer(new ItemStack(Items.OAK_LOG));
menu.clicked(0, 0, ClickType.PICKUP, f.player);
h.assertTrue(
menu.getCarried().is(Items.OAK_PLANKS)
&& ShopProductionTracker.valid(f.session, menu.getCarried()),
"Inventory craft not tracked");
f.finish(h);
}
@GameTest(template = "empty", timeoutTicks = 500)
public static void manualFurnaceOutput(GameTestHelper h) {
Fixture f = setup(h);
var be = (AbstractFurnaceBlockEntity) h.getLevel().getBlockEntity(f.furnace);
var menu = new FurnaceMenu(2, f.player.getInventory(), be, new SimpleContainerData(4));
f.player.containerMenu = menu;
menu.setCarried(new ItemStack(Items.RAW_IRON, 2));
menu.clicked(0, 0, ClickType.PICKUP, f.player);
menu.setCarried(new ItemStack(Items.COAL));
menu.clicked(1, 0, ClickType.PICKUP, f.player);
h.runAfterDelay(
410,
() -> {
h.assertTrue(
be.getItem(2).getCount() == 2
&& ShopProductionTracker.valid(f.session, be.getItem(2)),
"Furnace did not track both operations");
f.finish(h);
});
}
@GameTest(template = "empty", timeoutTicks = 260)
public static void automaticFurnaceIsNotAccredited(GameTestHelper h) {
Fixture f = setup(h);
var be = (AbstractFurnaceBlockEntity) h.getLevel().getBlockEntity(f.furnace);
be.setItem(0, new ItemStack(Items.RAW_IRON));
be.setItem(1, new ItemStack(Items.COAL));
h.runAfterDelay(
210,
() -> {
h.assertTrue(
be.getItem(2).is(Items.IRON_INGOT)
&& !ShopProductionTracker.valid(f.session, be.getItem(2)),
"External furnace loading accredited");
f.finish(h);
});
}
@GameTest(template = "empty")
public static void storagePermissionsAndSingleSession(GameTestHelper h) {
Fixture f = setup(h);
ServerPlayer other = h.makeMockServerPlayerInLevel();
var storage =
(ShopInventoryBlockEntity) h.getLevel().getBlockEntity(f.shop.storages.iterator().next());
var menu = new ShopMenu(3, other.getInventory(), storage, f.player.getUUID());
h.assertTrue(menu.getSlot(0).mayPlace(new ItemStack(Items.DIAMOND)), "Outsider cannot supply");
h.assertTrue(
!menu.getSlot(0).mayPickup(other)
&& !menu.getSlot(0).mayPlace(new ItemStack(Items.DIAMOND_SWORD)),
"Storage permission bypass");
h.assertTrue(
f.manager.open(f.player, f.shop, ShopGameplayMode.STANDARD).equals("already_open"),
"Second session permitted");
var second =
new Shop(
UUID.randomUUID(),
f.player.getUUID(),
f.shop.dimension,
f.shop.counter.offset(40, 0, 0));
f.manager.data.shops.put(second.id, second);
h.assertTrue(
f.manager.open(f.player, second, ShopGameplayMode.CHILL).equals("already_open"),
"Another shop opened for the same owner");
f.manager.data.shops.remove(second.id);
other.server.getPlayerList().remove(other);
f.finish(h);
}
@GameTest(template = "empty", timeoutTicks = 180)
public static void easyNpcLifecycle(GameTestHelper h) {
npcLifecycle(h, "easy_npc:humanoid");
}
@GameTest(template = "empty", timeoutTicks = 180)
public static void villagerNpcLifecycle(GameTestHelper h) {
npcLifecycle(h, "easy_npc:villager");
}
private static void npcLifecycle(GameTestHelper h, String type) {
Fixture f = setup(h);
var adapter = new EasyNpcAdapter(List.of(type));
var npc =
adapter
.spawn(h.getLevel(), f.session, f.shop.entry)
.orElseThrow(() -> new AssertionError("Easy NPC spawn failed"));
adapter.say(npc, "cos.npc.order");
adapter.move(npc, h.absolutePos(new BlockPos(4, 1, 5)));
h.runAfterDelay(
120,
() -> {
h.assertTrue(
npc.distanceToSqr(
f.shop.entry.getX() + .5,
f.shop.entry.getY() + .125,
f.shop.entry.getZ() + .5)
> 1,
"Easy NPC did not navigate");
adapter.remove(npc);
h.assertTrue(npc.isRemoved(), "NPC was not removed");
f.finish(h);
});
}
@GameTest(template = "empty")
public static void stonecutterAndSmithing(GameTestHelper h) {
Fixture f = setup(h);
f.manager.close(f.session, false);
h.setBlock(8, 1, 3, Blocks.STONECUTTER);
h.setBlock(6, 1, 3, Blocks.SMITHING_TABLE);
h.assertTrue(
f.manager.open(f.player, f.shop, ShopGameplayMode.CHILL).equals("opened"),
"Reopen with new stations");
var session = f.manager.session(f.shop.id);
session.nextArrival = Long.MAX_VALUE;
var stone =
new StonecutterMenu(
4, f.player.getInventory(), ContainerLevelAccess.create(h.getLevel(), f.furnace));
f.player.containerMenu = stone;
stone.getSlot(0).setByPlayer(new ItemStack(Items.COBBLESTONE));
h.assertTrue(stone.clickMenuButton(f.player, 0), "Stone recipe missing");
stone.clicked(1, 0, ClickType.PICKUP, f.player);
h.assertTrue(
ShopProductionTracker.valid(session, stone.getCarried()), "Stonecutting not tracked");
var smith =
new SmithingMenu(
5, f.player.getInventory(), ContainerLevelAccess.create(h.getLevel(), f.crafting));
f.player.containerMenu = smith;
ItemStack sword = new ItemStack(Items.DIAMOND_SWORD);
sword.set(
CounterOfShinuwa.PRODUCTION.get(),
new ShopProductionData(
f.shop.id,
session.id,
f.player.getUUID(),
"counter_of_shinuwa:manual",
"counter_of_shinuwa:crafting"));
smith.getSlot(0).setByPlayer(new ItemStack(Items.NETHERITE_UPGRADE_SMITHING_TEMPLATE));
smith.getSlot(1).setByPlayer(sword);
smith.getSlot(2).setByPlayer(new ItemStack(Items.NETHERITE_INGOT));
smith.clicked(3, 0, ClickType.PICKUP, f.player);
h.assertTrue(
smith.getCarried().is(Items.NETHERITE_SWORD)
&& ShopProductionTracker.valid(session, smith.getCarried()),
"Smithing not tracked");
f.finish(h);
}
@GameTest(template = "empty", timeoutTicks = 800)
public static void completeSaleAndGracefulClose(GameTestHelper h) {
Fixture f = setup(h);
f.manager.data.reputation(f.shop.owner, 500);
f.session.nextArrival = h.getLevel().getGameTime();
h.runAfterDelay(
280,
() -> {
h.assertTrue(
f.session.order != null,
"Customer never reached counter; session="
+ f.manager.session(f.shop.id)
+ " state="
+ f.session.state
+ " customers="
+ f.session.customers.stream()
.map(
c ->
c.state
+ " wp="
+ c.waypoint
+ " target="
+ c.target
+ " npc="
+ h.getLevel().getEntity(c.npc))
.toList());
var original = f.session.order;
f.session.order =
new ShopOrder(
UUID.randomUUID(),
original.customer(),
Map.of("minecraft:oak_planks", 4),
6,
h.getLevel().getGameTime() + 1000);
var craft =
new CraftingMenu(
6,
f.player.getInventory(),
ContainerLevelAccess.create(h.getLevel(), f.crafting));
f.player.containerMenu = craft;
craft.getSlot(1).setByPlayer(new ItemStack(Items.OAK_LOG));
craft.clicked(0, 0, ClickType.PICKUP, f.player);
var be = (ShopInventoryBlockEntity) h.getLevel().getBlockEntity(f.shop.counter);
be.setItem(0, craft.getCarried());
craft.setCarried(ItemStack.EMPTY);
var counter = new ShopMenu(7, f.player.getInventory(), be, f.player.getUUID());
f.player.containerMenu = counter;
f.manager.action(f.player, counter, 8);
f.manager.action(f.player, counter, 8);
h.assertTrue(
f.player.getInventory().countItem(Items.EMERALD) == 6,
"Payment duplicated or missing");
h.assertTrue(f.manager.reputation(f.shop.owner) == 500, "Chill changed reputation");
f.manager.action(f.player, counter, 5);
});
h.runAfterDelay(
720,
() -> {
h.assertTrue(
f.manager.session(f.shop.id) == null, "Graceful close did not finish departure");
f.finish(h);
});
}
@GameTest(template = "empty", timeoutTicks = 260)
public static void oldFurnaceOutputCannotBeLaundered(GameTestHelper h) {
Fixture f = setup(h);
var be = (AbstractFurnaceBlockEntity) h.getLevel().getBlockEntity(f.furnace);
be.setItem(2, new ItemStack(Items.IRON_INGOT));
var menu = new FurnaceMenu(8, f.player.getInventory(), be, new SimpleContainerData(4));
f.player.containerMenu = menu;
menu.setCarried(new ItemStack(Items.RAW_IRON));
menu.clicked(0, 0, ClickType.PICKUP, f.player);
menu.setCarried(new ItemStack(Items.COAL));
menu.clicked(1, 0, ClickType.PICKUP, f.player);
h.runAfterDelay(
210,
() -> {
h.assertTrue(
!ShopProductionTracker.valid(f.session, be.getItem(2)), "Old output laundered");
f.finish(h);
});
}
@GameTest(template = "empty", timeoutTicks = 260)
public static void hopperInvalidatesManualBatch(GameTestHelper h) {
Fixture f = setup(h);
var be = (AbstractFurnaceBlockEntity) h.getLevel().getBlockEntity(f.furnace);
var menu = new FurnaceMenu(9, f.player.getInventory(), be, new SimpleContainerData(4));
f.player.containerMenu = menu;
menu.setCarried(new ItemStack(Items.RAW_IRON));
menu.clicked(0, 0, ClickType.PICKUP, f.player);
menu.setCarried(new ItemStack(Items.COAL));
menu.clicked(1, 0, ClickType.PICKUP, f.player);
net.minecraft.world.level.block.entity.HopperBlockEntity.addItem(
null, be, new ItemStack(Items.RAW_IRON), net.minecraft.core.Direction.UP);
h.runAfterDelay(
210,
() -> {
h.assertTrue(
!ShopProductionTracker.valid(f.session, be.getItem(2)),
"Automated mixed batch laundered");
f.finish(h);
});
}
@GameTest(template = "empty", timeoutTicks = 80)
public static void structuralBreakClosesWithoutPenalty(GameTestHelper h) {
Fixture f = setup(h);
f.manager.data.reputation(f.shop.owner, 500);
h.setBlock(4, 1, 5, Blocks.AIR);
f.manager.invalidate(h.getLevel(), h.absolutePos(new BlockPos(4, 1, 5)));
h.runAfterDelay(
25,
() -> {
h.assertTrue(
f.manager.session(f.shop.id) == null && f.manager.reputation(f.shop.owner) == 500,
"Broken path was not closed neutrally");
f.finish(h);
});
}
@GameTest(template = "empty")
public static void fullInventoryRetainsPayment(GameTestHelper h) {
Fixture f = setup(h);
for (int i = 0; i < 36; i++) f.player.getInventory().setItem(i, new ItemStack(Items.STONE, 64));
f.shop.credit = 100;
f.manager.withdraw(f.shop);
h.assertTrue(f.shop.credit == 100, "Full inventory lost credit");
f.player.getInventory().setItem(0, ItemStack.EMPTY);
f.manager.withdraw(f.shop);
h.assertTrue(
f.shop.credit == 36 && f.player.getInventory().getItem(0).getCount() == 64,
"Partial payout incorrect");
f.finish(h);
}
@GameTest(template = "empty")
public static void addonWithoutCoreChanges(GameTestHelper h) {
Fixture f = setup(h);
f.manager.close(f.session, false);
h.setBlock(10, 1, 3, Blocks.BELL);
h.assertTrue(
f.manager.open(f.player, f.shop, ShopGameplayMode.CHILL).equals("opened"),
"Addon station rejected");
var s = f.manager.session(f.shop.id);
var input = new SimpleContainer(new ItemStack(Items.DIAMOND, 2));
h.assertTrue(
s.structure.families().contains(TestProductionIntegration.ID), "Addon family missing");
h.assertTrue(
s.resolver
.plan(Map.of("minecraft:amethyst_block", 1), Map.of("minecraft:diamond", 1))
.isPresent(),
"Addon recipe missing");
var result =
TestProductionIntegration.produce(
s, f.player, h.absolutePos(new BlockPos(10, 1, 3)), input);
h.assertTrue(
ShopProductionTracker.valid(s, result) && input.getItem(0).getCount() == 1,
"Addon production not accredited exactly once");
f.finish(h);
}
@GameTest(template = "empty", timeoutTicks = 160)
public static void blastFurnaceAndSmoker(GameTestHelper h) {
Fixture f = setup(h);
f.manager.close(f.session, false);
h.setBlock(8, 1, 3, Blocks.BLAST_FURNACE);
h.setBlock(10, 1, 3, Blocks.SMOKER);
h.assertTrue(
f.manager.open(f.player, f.shop, ShopGameplayMode.CHILL).equals("opened"),
"Three families should be accepted");
var s = f.manager.session(f.shop.id);
s.nextArrival = Long.MAX_VALUE;
var blast = (AbstractFurnaceBlockEntity) h.getLevel().getBlockEntity(f.furnace);
var smoker = (AbstractFurnaceBlockEntity) h.getBlockEntity(new BlockPos(10, 1, 3));
for (var be : List.of(blast, smoker)) {
AbstractFurnaceMenu menu =
be == blast
? new BlastFurnaceMenu(10, f.player.getInventory(), be, new SimpleContainerData(4))
: new SmokerMenu(11, f.player.getInventory(), be, new SimpleContainerData(4));
f.player.containerMenu = menu;
menu.setCarried(new ItemStack(be == blast ? Items.RAW_IRON : Items.BEEF));
menu.clicked(0, 0, ClickType.PICKUP, f.player);
menu.setCarried(new ItemStack(Items.COAL));
menu.clicked(1, 0, ClickType.PICKUP, f.player);
}
h.runAfterDelay(
115,
() -> {
h.assertTrue(
blast.getItem(2).is(Items.IRON_INGOT)
&& ShopProductionTracker.valid(s, blast.getItem(2)),
"Blast furnace provenance");
h.assertTrue(
smoker.getItem(2).is(Items.COOKED_BEEF)
&& ShopProductionTracker.valid(s, smoker.getItem(2)),
"Smoker provenance");
f.finish(h);
});
}
@GameTest(template = "empty")
public static void stockTransfersDoNotDuplicate(GameTestHelper h) {
Fixture f = setup(h);
var initial = ShopStock.collect(h.getLevel(), f.shop, f.session, f.session.structure);
var storage =
(ShopInventoryBlockEntity) h.getLevel().getBlockEntity(f.shop.storages.iterator().next());
var craft =
new CraftingMenu(
12, f.player.getInventory(), ContainerLevelAccess.create(h.getLevel(), f.crafting));
f.player.containerMenu = craft;
craft.getSlot(1).setByPlayer(storage.removeItem(0, 2));
h.assertTrue(
ShopStock.collect(h.getLevel(), f.shop, f.session, f.session.structure).equals(initial),
"Menu input counted twice or lost");
craft.clicked(0, 0, ClickType.PICKUP, f.player);
h.assertTrue(
ShopStock.finished(h.getLevel(), f.shop, f.session, f.session.structure)
.getOrDefault("minecraft:oak_planks", 0)
== 4,
"Cursor output not counted exactly once");
f.finish(h);
}
@GameTest(template = "empty")
public static void rejectOtherProducerAndOutsideCraft(GameTestHelper h) {
Fixture f = setup(h);
ItemStack invalid = stamped(f, Items.OAK_PLANKS, 4);
invalid.set(
CounterOfShinuwa.PRODUCTION.get(),
new ShopProductionData(f.shop.id, f.session.id, UUID.randomUUID(), "manual", "crafting"));
h.assertTrue(
!Delivery.consume(
new SimpleContainer(invalid), f.session, Map.of("minecraft:oak_planks", 4)),
"Other producer accepted");
BlockPos outside = h.absolutePos(new BlockPos(0, 1, 0));
f.player.setPos(outside.getX(), outside.getY(), outside.getZ());
f.player.containerMenu = f.player.inventoryMenu;
var menu = f.player.inventoryMenu;
menu.getSlot(1).setByPlayer(new ItemStack(Items.OAK_LOG));
menu.clicked(0, 0, ClickType.PICKUP, f.player);
h.assertTrue(
!ShopProductionTracker.valid(f.session, menu.getCarried()), "Outside craft accredited");
f.finish(h);
}
@GameTest(template = "empty")
public static void craftingRemaindersKeepProvenance(GameTestHelper h) {
Fixture f = setup(h);
var menu =
new CraftingMenu(
13, f.player.getInventory(), ContainerLevelAccess.create(h.getLevel(), f.crafting));
f.player.containerMenu = menu;
for (int i = 1; i <= 3; i++) menu.getSlot(i).setByPlayer(stamped(f, Items.MILK_BUCKET, 1));
menu.getSlot(4).setByPlayer(stamped(f, Items.SUGAR, 1));
menu.getSlot(5).setByPlayer(new ItemStack(Items.EGG));
menu.getSlot(6).setByPlayer(stamped(f, Items.SUGAR, 1));
for (int i = 7; i <= 9; i++) menu.getSlot(i).setByPlayer(new ItemStack(Items.WHEAT));
menu.clicked(0, 0, ClickType.PICKUP, f.player);
h.assertTrue(
menu.getCarried().is(Items.CAKE)
&& ShopProductionTracker.valid(f.session, menu.getCarried()),
"Cake production failed");
for (int i = 1; i <= 3; i++)
h.assertTrue(
menu.getSlot(i).getItem().is(Items.BUCKET)
&& ShopProductionTracker.valid(f.session, menu.getSlot(i).getItem()),
"Remainder lost provenance");
f.finish(h);
}
@GameTest(template = "empty", timeoutTicks = 80)
public static void hoppersCannotWithdrawShopStock(GameTestHelper h) {
Fixture f = setup(h);
var pos = f.shop.storages.iterator().next();
h.getLevel().setBlockAndUpdate(pos.below(), Blocks.HOPPER.defaultBlockState());
h.runAfterDelay(
25,
() -> {
var storage = (ShopInventoryBlockEntity) h.getLevel().getBlockEntity(pos);
var hopper =
(net.minecraft.world.level.block.entity.HopperBlockEntity)
h.getLevel().getBlockEntity(pos.below());
h.assertTrue(
storage.getItem(0).getCount() == 32 && hopper.isEmpty(),
"Hopper bypassed owner withdrawal permission");
f.finish(h);
});
}
}

View file

@ -0,0 +1,64 @@
package fr.shinuwa.counterofshinuwa.test;
import fr.shinuwa.counterofshinuwa.core.ShopSession;
import fr.shinuwa.counterofshinuwa.production.*;
import fr.shinuwa.counterofshinuwa.recipe.ProductionRecipe;
import fr.shinuwa.counterofshinuwa.station.*;
import java.util.*;
import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.Container;
import net.minecraft.world.item.*;
import net.minecraft.world.level.block.Blocks;
/** Test-only addon: a bell transforms a diamond into an amethyst block. No core branches. */
public final class TestProductionIntegration implements ProductionHandler {
public static final String ID = "counter_of_shinuwa_tests:polishing";
public static void register() {
ProductionRegistry.INSTANCE.register(
state ->
state.is(Blocks.BELL)
? Optional.of(new StationDefinition(ID, ID, ID))
: Optional.empty(),
level ->
List.of(
new ProductionRecipe(
ID,
List.of(Set.of("minecraft:diamond")),
"minecraft:amethyst_block",
1,
Map.of(),
ID,
ID,
20,
false)),
() -> Set.of(ID),
new TestProductionIntegration());
}
@Override
public String id() {
return ID;
}
@Override
public boolean accepts(ShopSession session, List<ItemStack> inputs) {
return inputs.size() == 1
&& inputs.getFirst().is(Items.DIAMOND)
&& ShopProductionTracker.ingredient(session, inputs.getFirst());
}
public static ItemStack produce(
ShopSession session, ServerPlayer player, BlockPos pos, Container input) {
if (!player.level().getBlockState(pos).is(Blocks.BELL)
|| !session.shop.contains(player.blockPosition())) return ItemStack.EMPTY;
ItemStack output = new ItemStack(Items.AMETHYST_BLOCK);
if (!ShopProductionTracker.produced(
new ProductionContext(session, player.getUUID(), pos, ID, ID),
List.of(input.getItem(0).copy()),
output)) return ItemStack.EMPTY;
input.removeItem(0, 1);
return output;
}
}

View file

@ -0,0 +1,7 @@
modLoader="javafml"
loaderVersion="[4,)"
license="All Rights Reserved"
[[mods]]
modId="counter_of_shinuwa_tests"
version="1.0.0"
displayName="Counter of Shinuwa GameTests"