246 lines
17 KiB
Java
246 lines
17 KiB
Java
package games.dmg.fantasystick;
|
|
|
|
import static org.junit.jupiter.api.Assertions.*;
|
|
import static org.mockito.Mockito.*;
|
|
|
|
import games.dmg.fantasystick.adventure.AdventureQuestController;
|
|
import games.dmg.fantasystick.adventure.AdventureQuestDirectory;
|
|
import games.dmg.fantasystick.adventure.AdventureQuestState;
|
|
import games.dmg.fantasystick.dungeon.DungeonDirectory;
|
|
import java.nio.file.Path;
|
|
import java.util.List;
|
|
import java.util.Set;
|
|
import java.util.UUID;
|
|
import java.util.concurrent.TimeUnit;
|
|
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
|
|
import org.bukkit.event.inventory.InventoryClickEvent;
|
|
import org.junit.jupiter.api.BeforeAll;
|
|
import org.junit.jupiter.api.Test;
|
|
import org.junit.jupiter.api.io.TempDir;
|
|
|
|
final class AdventureParticipationModeNativeTest {
|
|
@TempDir Path data;
|
|
@BeforeAll static void bootstrap() throws Exception { NativeRuntime.bootstrap(); }
|
|
|
|
@Test void registeredSiteAndDungeonCommandsPersistReportUpdateAndCompleteModes() throws Exception {
|
|
java.nio.file.Files.writeString(data.resolve("config.yml"), "adventure-daily-site-count: 0\n");
|
|
try (var f = new FantasyStickLifecycleNativeTest.Fixture(data)) {
|
|
f.plugin.setEnabled(true);
|
|
var admin = new FantasyStickLifecycleNativeTest.Actor(data);
|
|
f.players.put(admin.core.id, admin.player);
|
|
doReturn(true).when(admin.player).hasPermission("fantasystick.admin");
|
|
when(admin.world.getUID()).thenReturn(UUID.randomUUID());
|
|
|
|
assertEquals(List.of("--mode"), f.command.tabComplete(admin.player, "fantasystick",
|
|
new String[]{"adventure-site", "place", "camp", "--"}));
|
|
assertEquals(List.of("individual-only"), f.command.tabComplete(admin.player, "fantasystick",
|
|
new String[]{"adventure-site", "place", "camp", "--mode", "i"}));
|
|
var adventures = (AdventureQuestDirectory) FantasyStickLifecycleNativeTest.Fixture.field(f.plugin,
|
|
"adventureQuests");
|
|
f.command.execute(admin.player, "fantasystick", new String[]{"adventure-site", "place", "camp",
|
|
"--mode", "individual-only", "10", "Solo Camp"});
|
|
await(f, () -> !adventures.snapshot().sites().isEmpty());
|
|
var site = adventures.snapshot().sites().getFirst();
|
|
assertEquals(AdventureQuestState.ParticipationMode.INDIVIDUAL_ONLY, site.participationMode());
|
|
assertTrue(admin.messages.getLast().contains("mode=individual-only"));
|
|
f.command.execute(admin.player, "fantasystick", new String[]{"adventure-site", "list"});
|
|
assertTrue(admin.messages.getLast().contains("mode=individual-only"));
|
|
assertEquals(List.of(site.id().toString()), f.command.tabComplete(admin.player, "fantasystick",
|
|
new String[]{"adventure-site", "mode", ""}));
|
|
assertEquals(List.of("party-available", "party-required"), f.command.tabComplete(admin.player,
|
|
"fantasystick", new String[]{"adventure-site", "mode", site.id().toString(), "party-"}));
|
|
f.command.execute(admin.player, "fantasystick", new String[]{"adventure-site", "mode",
|
|
site.id().toString()});
|
|
assertTrue(admin.messages.getLast().contains("individual-only"));
|
|
f.command.execute(admin.player, "fantasystick", new String[]{"adventure-site", "mode",
|
|
site.id().toString(), "party-required"});
|
|
drain(f);
|
|
assertEquals(AdventureQuestState.ParticipationMode.PARTY_REQUIRED,
|
|
adventures.findSite(site.id()).orElseThrow().participationMode());
|
|
int count = adventures.snapshot().sites().size();
|
|
f.command.execute(admin.player, "fantasystick", new String[]{"adventure-site", "place", "camp",
|
|
"--mode", "invalid", "Broken"});
|
|
assertEquals(count, adventures.snapshot().sites().size());
|
|
|
|
assertEquals(List.of("--mode"), f.command.tabComplete(admin.player, "fantasystick",
|
|
new String[]{"dungeon", "create", "--"}));
|
|
var dungeons = (DungeonDirectory) FantasyStickLifecycleNativeTest.Fixture.field(f.plugin, "dungeons");
|
|
f.command.execute(admin.player, "fantasystick", new String[]{"dungeon", "create", "--mode",
|
|
"party-required", "Mode Vault"});
|
|
await(f, () -> !dungeons.snapshot().dungeons().isEmpty());
|
|
var dungeon = dungeons.snapshot().dungeons().getFirst();
|
|
assertEquals(AdventureQuestState.ParticipationMode.PARTY_REQUIRED, dungeon.participationMode());
|
|
assertEquals(List.of(dungeon.id().toString()), f.command.tabComplete(admin.player, "fantasystick",
|
|
new String[]{"dungeon", "mode", ""}));
|
|
f.command.execute(admin.player, "fantasystick", new String[]{"dungeon", "mode",
|
|
dungeon.id().toString(), "party-available"});
|
|
drain(f);
|
|
assertEquals(AdventureQuestState.ParticipationMode.PARTY_AVAILABLE,
|
|
dungeons.find(dungeon.id()).orElseThrow().participationMode());
|
|
f.command.execute(admin.player, "fantasystick", new String[]{"dungeon", "mode",
|
|
dungeon.id().toString(), "party-required"}); drain(f);
|
|
var room = dungeons.addRadiusRoom(dungeon.id(), "Boss", 0, 64, 0, 2, 3)
|
|
.get(5, TimeUnit.SECONDS);
|
|
dungeons.setBoss(dungeon.id(), room.id(), org.bukkit.entity.EntityType.HUSK, "Keeper",
|
|
0, 65, 0, 0, 0).get(5, TimeUnit.SECONDS);
|
|
dungeons.setCompletionReward(dungeon.id(), java.math.BigInteger.ZERO, java.math.BigInteger.ZERO)
|
|
.get(5, TimeUnit.SECONDS);
|
|
f.command.execute(admin.player, "fantasystick", new String[]{"dungeon", "enable",
|
|
dungeon.id().toString()}); drain(f);
|
|
f.command.execute(admin.player, "fantasystick", new String[]{"dungeon", "open",
|
|
dungeon.id().toString()}); drain(f);
|
|
assertEquals(AdventureQuestState.ParticipationMode.PARTY_REQUIRED,
|
|
adventures.findSite(dungeon.id()).orElseThrow().participationMode());
|
|
}
|
|
}
|
|
|
|
@Test void questGiverLabelsAndExplicitlyAcceptsPersonalOrPartyForm() throws Exception {
|
|
java.nio.file.Files.writeString(data.resolve("config.yml"), "adventure-daily-site-count: 3\n");
|
|
try (var f = new FantasyStickLifecycleNativeTest.Fixture(data)) {
|
|
f.plugin.setEnabled(true);
|
|
var leader = new FantasyStickLifecycleNativeTest.Actor(data);
|
|
var member = new FantasyStickLifecycleNativeTest.Actor(data);
|
|
f.players.put(leader.core.id, leader.player); f.players.put(member.core.id, member.player);
|
|
UUID world = UUID.randomUUID(); when(leader.world.getUID()).thenReturn(world);
|
|
member.core.location.setWorld(leader.world);
|
|
f.activate(leader); f.activate(member);
|
|
var parties = PartyLifecycleNativeTest.parties(f);
|
|
var party = parties.create(leader.core.id).orElseThrow();
|
|
assertTrue(parties.accept(member.core.id,
|
|
parties.invite(leader.core.id, party.id(), member.core.id).orElseThrow().id()));
|
|
var adventures = (AdventureQuestDirectory) FantasyStickLifecycleNativeTest.Fixture.field(f.plugin,
|
|
"adventureQuests");
|
|
var personal = adventures.placeSite(AdventureQuestState.SiteType.CAMP, "Flexible Camp", world,
|
|
0, 64, 0, null, AdventureQuestState.ParticipationMode.PARTY_AVAILABLE).get(5, TimeUnit.SECONDS);
|
|
var required = adventures.placeSite(AdventureQuestState.SiteType.CHAMPION, "Party Summit", world,
|
|
20, 64, 0, null, AdventureQuestState.ParticipationMode.PARTY_REQUIRED).get(5, TimeUnit.SECONDS);
|
|
var individual = adventures.placeSite(AdventureQuestState.SiteType.CAMP, "Solo Camp", world,
|
|
40, 64, 0, null, AdventureQuestState.ParticipationMode.INDIVIDUAL_ONLY).get(5, TimeUnit.SECONDS);
|
|
adventures.ensureToday().get(5, TimeUnit.SECONDS);
|
|
var controller = (AdventureQuestController) FantasyStickLifecycleNativeTest.Fixture.field(f.plugin,
|
|
"adventureQuestController");
|
|
|
|
controller.open(member.player); drain(f);
|
|
var memberMenu = member.inventories.getLast();
|
|
var personalItem = java.util.stream.IntStream.range(0, memberMenu.getSize())
|
|
.mapToObj(memberMenu::getItem).filter(java.util.Objects::nonNull)
|
|
.filter(item -> PlainTextComponentSerializer.plainText().serialize(item.getItemMeta().displayName())
|
|
.equals("Flexible Camp")).findFirst().orElseThrow();
|
|
var lore = personalItem.getItemMeta().lore().stream()
|
|
.map(PlainTextComponentSerializer.plainText()::serialize).toList();
|
|
assertTrue(lore.contains("Mode: party-available"));
|
|
assertTrue(lore.stream().anyMatch(line -> line.contains("Left click: Accept individually")));
|
|
var personalClick = mock(InventoryClickEvent.class);
|
|
when(personalClick.getWhoClicked()).thenReturn(member.player);
|
|
when(personalClick.getInventory()).thenReturn(memberMenu);
|
|
when(personalClick.getCurrentItem()).thenReturn(personalItem);
|
|
controller.click(personalClick); drain(f);
|
|
var personalQuest = adventures.activeFor(member.core.record.id(), party.id()).orElseThrow();
|
|
assertEquals(AdventureQuestState.UnitType.SOLO, personalQuest.unitType());
|
|
assertEquals(Set.of(member.core.record.id()), personalQuest.snapshot());
|
|
assertTrue(adventures.abandon(personalQuest.id(), personalQuest.unit()).get(5, TimeUnit.SECONDS));
|
|
|
|
controller.open(member.player); drain(f);
|
|
var memberIndividualMenu = member.inventories.getLast();
|
|
var individualItem = offer(memberIndividualMenu, "Solo Camp");
|
|
click(controller, member, memberIndividualMenu, individualItem, false); drain(f);
|
|
var memberIndividual = adventures.activeFor(member.core.record.id(), party.id()).orElseThrow();
|
|
assertEquals(individual.id(), memberIndividual.site());
|
|
assertEquals(AdventureQuestState.UnitType.SOLO, memberIndividual.unitType());
|
|
assertTrue(adventures.abandon(memberIndividual.id(), memberIndividual.unit()).get(5, TimeUnit.SECONDS));
|
|
click(controller, member, memberIndividualMenu, individualItem, true);
|
|
assertTrue(member.messages.getLast().contains("individual-only"));
|
|
var memberRequiredItem = offer(memberIndividualMenu, "Party Summit");
|
|
click(controller, member, memberIndividualMenu, memberRequiredItem, false);
|
|
assertTrue(member.messages.getLast().contains("party-required"));
|
|
click(controller, member, memberIndividualMenu, memberRequiredItem, true);
|
|
assertTrue(member.messages.getLast().contains("Only the active party leader"));
|
|
|
|
var partyless = new FantasyStickLifecycleNativeTest.Actor(data);
|
|
partyless.core.location.setWorld(leader.world); f.players.put(partyless.core.id, partyless.player);
|
|
f.activate(partyless); controller.open(partyless.player); drain(f);
|
|
var partylessMenu = partyless.inventories.getLast();
|
|
click(controller, partyless, partylessMenu, offer(partylessMenu, "Solo Camp"), false); drain(f);
|
|
var partylessQuest = adventures.activeFor(partyless.core.record.id(), null).orElseThrow();
|
|
assertEquals(AdventureQuestState.UnitType.SOLO, partylessQuest.unitType());
|
|
assertTrue(adventures.abandon(partylessQuest.id(), partylessQuest.unit()).get(5, TimeUnit.SECONDS));
|
|
var partylessRequired = offer(partylessMenu, "Party Summit");
|
|
click(controller, partyless, partylessMenu, partylessRequired, false);
|
|
assertTrue(partyless.messages.getLast().contains("party-required"));
|
|
click(controller, partyless, partylessMenu, partylessRequired, true);
|
|
assertTrue(partyless.messages.getLast().contains("Only the active party leader"));
|
|
|
|
controller.open(leader.player); drain(f);
|
|
var leaderIndividualMenu = leader.inventories.getLast();
|
|
var leaderIndividualItem = offer(leaderIndividualMenu, "Solo Camp");
|
|
click(controller, leader, leaderIndividualMenu, leaderIndividualItem, false); drain(f);
|
|
var leaderIndividual = adventures.activeFor(leader.core.record.id(), party.id()).orElseThrow();
|
|
assertEquals(AdventureQuestState.UnitType.SOLO, leaderIndividual.unitType());
|
|
assertTrue(adventures.abandon(leaderIndividual.id(), leaderIndividual.unit()).get(5, TimeUnit.SECONDS));
|
|
click(controller, leader, leaderIndividualMenu, leaderIndividualItem, true);
|
|
assertTrue(leader.messages.getLast().contains("individual-only"));
|
|
|
|
controller.open(leader.player); drain(f);
|
|
var leaderMenu = leader.inventories.getLast();
|
|
var requiredItem = java.util.stream.IntStream.range(0, leaderMenu.getSize())
|
|
.mapToObj(leaderMenu::getItem).filter(java.util.Objects::nonNull)
|
|
.filter(item -> PlainTextComponentSerializer.plainText().serialize(item.getItemMeta().displayName())
|
|
.equals("Party Summit")).findFirst().orElseThrow();
|
|
var requiredLore = requiredItem.getItemMeta().lore().stream()
|
|
.map(PlainTextComponentSerializer.plainText()::serialize).toList();
|
|
assertTrue(requiredLore.contains("Mode: party-required"));
|
|
assertTrue(requiredLore.stream().anyMatch(line -> line.contains("Right click: Accept for party")));
|
|
var partyClick = mock(InventoryClickEvent.class);
|
|
when(partyClick.getWhoClicked()).thenReturn(leader.player);
|
|
when(partyClick.getInventory()).thenReturn(leaderMenu);
|
|
when(partyClick.getCurrentItem()).thenReturn(requiredItem);
|
|
when(partyClick.isRightClick()).thenReturn(true);
|
|
controller.click(partyClick);
|
|
assertTrue(parties.disband(leader.core.id, party.id()), "party changes before acceptance publication");
|
|
await(f, () -> leader.messages.stream().anyMatch(message -> message.contains("character or party changed")));
|
|
assertTrue(adventures.active().isEmpty());
|
|
|
|
party = parties.create(leader.core.id).orElseThrow();
|
|
assertTrue(parties.accept(member.core.id,
|
|
parties.invite(leader.core.id, party.id(), member.core.id).orElseThrow().id()));
|
|
controller.click(partyClick);
|
|
UUID acceptedParty = party.id();
|
|
await(f, () -> adventures.activeFor(leader.core.record.id(), acceptedParty).isPresent());
|
|
var partyQuest = adventures.activeFor(leader.core.record.id(), acceptedParty).orElseThrow();
|
|
assertEquals(AdventureQuestState.UnitType.PARTY, partyQuest.unitType());
|
|
assertEquals(Set.of(leader.core.record.id(), member.core.record.id()), partyQuest.snapshot());
|
|
assertEquals(AdventureQuestState.ParticipationMode.PARTY_REQUIRED,
|
|
partyQuest.participationMode());
|
|
}
|
|
}
|
|
|
|
private static org.bukkit.inventory.ItemStack offer(org.bukkit.inventory.Inventory menu, String name) {
|
|
return java.util.stream.IntStream.range(0, menu.getSize()).mapToObj(menu::getItem)
|
|
.filter(java.util.Objects::nonNull)
|
|
.filter(item -> PlainTextComponentSerializer.plainText().serialize(item.getItemMeta().displayName())
|
|
.equals(name)).findFirst().orElseThrow();
|
|
}
|
|
private static void click(AdventureQuestController controller, FantasyStickLifecycleNativeTest.Actor actor,
|
|
org.bukkit.inventory.Inventory menu, org.bukkit.inventory.ItemStack item,
|
|
boolean right) {
|
|
var event = mock(InventoryClickEvent.class);
|
|
when(event.getWhoClicked()).thenReturn(actor.player); when(event.getInventory()).thenReturn(menu);
|
|
when(event.getCurrentItem()).thenReturn(item); when(event.isRightClick()).thenReturn(right);
|
|
controller.click(event);
|
|
}
|
|
private static void drain(FantasyStickLifecycleNativeTest.Fixture f) throws Exception {
|
|
for (int attempt = 0; attempt < 20; attempt++) {
|
|
var task = f.tasks.poll(50, TimeUnit.MILLISECONDS);
|
|
if (task == null) break;
|
|
task.run();
|
|
}
|
|
}
|
|
private static void await(FantasyStickLifecycleNativeTest.Fixture f,
|
|
java.util.function.BooleanSupplier condition) throws Exception {
|
|
for (int attempt = 0; attempt < 50 && !condition.getAsBoolean(); attempt++) {
|
|
drain(f); Thread.sleep(5);
|
|
}
|
|
assertTrue(condition.getAsBoolean());
|
|
}
|
|
}
|