This commit is contained in:
commit
07eb6dc053
318 changed files with 339857 additions and 0 deletions
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
7
src/gameTest/resources/META-INF/neoforge.mods.toml
Normal file
7
src/gameTest/resources/META-INF/neoforge.mods.toml
Normal 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"
|
||||
Binary file not shown.
Loading…
Add table
Add a link
Reference in a new issue