feat(tree-feller): support Poplar progression
This commit is contained in:
1 parent
5ddc49f79f
commit
a9c5aeccdc
14 files changed
+374
-5
No files matched your search
@@ -15,6 +15,7 @@ public enum TreeSpecies {
|
||||
MANGROVE("mangrove", "Mangrove"),
|
||||
CHERRY("cherry", "Cherry"),
|
||||
PALE_OAK("pale-oak", "Pale Oak"),
|
||||
POPLAR("poplar", "Poplar"),
|
||||
CRIMSON("crimson", "Crimson Fungus"),
|
||||
WARPED("warped", "Warped Fungus"),
|
||||
RED_MUSHROOM("red-mushroom", "Giant Red Mushroom"),
|
||||
|
||||
@@ -19,6 +19,7 @@ public final class TreeTaxonomy {
|
||||
Map.entry(Material.MANGROVE_LOG, TreeSpecies.MANGROVE),
|
||||
Map.entry(Material.CHERRY_LOG, TreeSpecies.CHERRY),
|
||||
Map.entry(Material.PALE_OAK_LOG, TreeSpecies.PALE_OAK),
|
||||
Map.entry(Material.POPLAR_LOG, TreeSpecies.POPLAR),
|
||||
Map.entry(Material.CRIMSON_STEM, TreeSpecies.CRIMSON),
|
||||
Map.entry(Material.WARPED_STEM, TreeSpecies.WARPED));
|
||||
|
||||
@@ -64,6 +65,10 @@ public final class TreeTaxonomy {
|
||||
foliage.put(TreeSpecies.MANGROVE, Set.of(Material.MANGROVE_LEAVES));
|
||||
foliage.put(TreeSpecies.CHERRY, Set.of(Material.CHERRY_LEAVES));
|
||||
foliage.put(TreeSpecies.PALE_OAK, Set.of(Material.PALE_OAK_LEAVES));
|
||||
foliage.put(TreeSpecies.POPLAR, Set.of(
|
||||
Material.ORANGE_POPLAR_LEAVES,
|
||||
Material.RED_POPLAR_LEAVES,
|
||||
Material.YELLOW_POPLAR_LEAVES));
|
||||
foliage.put(TreeSpecies.CRIMSON, Set.of(Material.NETHER_WART_BLOCK));
|
||||
foliage.put(TreeSpecies.WARPED, Set.of(Material.WARPED_WART_BLOCK));
|
||||
foliage.put(TreeSpecies.RED_MUSHROOM, Set.of(Material.RED_MUSHROOM_BLOCK));
|
||||
|
||||
@@ -8,6 +8,7 @@ thresholds:
|
||||
mangrove: 100
|
||||
cherry: 100
|
||||
pale-oak: 100
|
||||
poplar: 100
|
||||
crimson: 100
|
||||
warped: 100
|
||||
red-mushroom: 100
|
||||
|
||||
@@ -65,6 +65,33 @@ class AnimatedTreeFellingEngineTest {
|
||||
assertEquals(3, completed.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
void progressivelyFellsPoplarUsingNativeBlockAndToolMaterials() {
|
||||
TestTree tree = new TestTree();
|
||||
BlockPoint origin = new BlockPoint(0, 0, 0);
|
||||
tree.add(origin, Material.POPLAR_LOG);
|
||||
tree.add(new BlockPoint(0, 1, 0), Material.POPLAR_LOG);
|
||||
QueueScheduler scheduler = new QueueScheduler();
|
||||
when(tree.player().breakBlock(any(Block.class))).thenReturn(true);
|
||||
List<FelledBlockSnapshot> completed = new ArrayList<>();
|
||||
AnimatedTreeFellingEngine engine = new AnimatedTreeFellingEngine(
|
||||
scheduler,
|
||||
new AutomaticBreakRegistry(),
|
||||
ignored -> true,
|
||||
(ignored, snapshots) -> completed.addAll(snapshots),
|
||||
ignored -> { });
|
||||
|
||||
assertTrue(engine.start(
|
||||
tree.player(),
|
||||
tree.block(origin),
|
||||
new TreeStructure(TreeSpecies.POPLAR, List.of(origin, new BlockPoint(0, 1, 0))),
|
||||
2));
|
||||
scheduler.runAll();
|
||||
|
||||
assertEquals(List.of(Material.POPLAR_LOG, Material.POPLAR_LOG),
|
||||
completed.stream().map(FelledBlockSnapshot::material).toList());
|
||||
}
|
||||
|
||||
@Test
|
||||
void stopsAfterAProtectionCancellationAndDoesNotRunAnOverlappingFelling() {
|
||||
TestTree tree = new TestTree();
|
||||
|
||||
@@ -57,6 +57,36 @@ class TreeFellingListenerTest {
|
||||
verify(starter, org.mockito.Mockito.times(1)).start(any(), any(), any(), eq(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
void startsUnlockedPoplarFellingThroughTheNativeBoundary() {
|
||||
UUID playerId = UUID.randomUUID();
|
||||
Player player = mock(Player.class);
|
||||
PlayerInventory inventory = mock(PlayerInventory.class);
|
||||
Block block = mock(Block.class);
|
||||
World world = mock(World.class);
|
||||
when(block.getWorld()).thenReturn(world);
|
||||
when(block.getType()).thenReturn(Material.POPLAR_LOG);
|
||||
when(world.getUID()).thenReturn(UUID.randomUUID());
|
||||
when(player.getUniqueId()).thenReturn(playerId);
|
||||
when(player.getName()).thenReturn("Player");
|
||||
when(player.getGameMode()).thenReturn(GameMode.SURVIVAL);
|
||||
when(player.getInventory()).thenReturn(inventory);
|
||||
when(inventory.getItemInMainHand()).thenReturn(new ItemStack(Material.IRON_AXE));
|
||||
TreeStructure tree = new TreeStructure(
|
||||
TreeSpecies.POPLAR, List.of(new BlockPoint(0, 0, 0), new BlockPoint(0, 1, 0)));
|
||||
PlayerStateStore states = mock(PlayerStateStore.class);
|
||||
when(states.load(playerId)).thenReturn(Optional.of(
|
||||
PlayerTreeFellerState.initial(playerId, "Player")
|
||||
.withUnlocked(TreeSpecies.POPLAR, true)));
|
||||
TreeFellingStarter starter = mock(TreeFellingStarter.class);
|
||||
TreeFellingListener listener = new TreeFellingListener(
|
||||
ignored -> Optional.of(tree), states, new AutomaticBreakRegistry(), starter, () -> 2);
|
||||
|
||||
listener.onBlockBreak(new BlockBreakEvent(block, player));
|
||||
|
||||
verify(starter).start(player, block, tree, 2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void aNonAxeCanEarnProgressButNeverStartsAutomaticFelling() {
|
||||
UUID playerId = UUID.randomUUID();
|
||||
|
||||
@@ -21,6 +21,41 @@ class TreeProgressListenerTest {
|
||||
@org.junit.jupiter.api.BeforeAll
|
||||
static void bootstrap() throws Exception { NativeRuntime.bootstrap(); }
|
||||
|
||||
@Test
|
||||
void recordsAndPersistsPoplarProgressThroughUnlock() throws Exception {
|
||||
UUID playerId = UUID.randomUUID();
|
||||
Player player = mock(Player.class);
|
||||
PlayerInventory inventory = mock(PlayerInventory.class);
|
||||
Block block = mock(Block.class);
|
||||
World world = mock(World.class);
|
||||
when(player.getUniqueId()).thenReturn(playerId);
|
||||
when(player.getName()).thenReturn("Player");
|
||||
when(player.getGameMode()).thenReturn(GameMode.SURVIVAL);
|
||||
when(player.getInventory()).thenReturn(inventory);
|
||||
when(inventory.getItemInMainHand()).thenReturn(new ItemStack(Material.IRON_AXE));
|
||||
when(block.getWorld()).thenReturn(world);
|
||||
when(block.getType()).thenReturn(Material.POPLAR_LOG);
|
||||
when(world.getUID()).thenReturn(UUID.randomUUID());
|
||||
when(block.getX()).thenReturn(1);
|
||||
when(block.getY()).thenReturn(64);
|
||||
when(block.getZ()).thenReturn(2);
|
||||
InMemoryStateStore states = new InMemoryStateStore();
|
||||
states.state = PlayerTreeFellerState.initial(playerId, "Player")
|
||||
.withProgress(TreeSpecies.POPLAR, 1);
|
||||
TreeProgressListener listener = new TreeProgressListener(
|
||||
ignored -> Optional.empty(),
|
||||
states,
|
||||
ignored -> 2,
|
||||
new AutomaticBreakRegistry(),
|
||||
(ignoredPlayer, ignoredUpdate) -> { },
|
||||
ignored -> { });
|
||||
|
||||
listener.onBlockBreak(new BlockBreakEvent(block, player));
|
||||
|
||||
assertEquals(2, states.state.progress(TreeSpecies.POPLAR));
|
||||
assertEquals(true, states.state.isUnlocked(TreeSpecies.POPLAR));
|
||||
}
|
||||
|
||||
@Test
|
||||
void recordsNaturalTrunkMaterialAfterTheTreeStructureIsNoLongerIntact() throws Exception {
|
||||
UUID playerId = UUID.randomUUID();
|
||||
|
||||
@@ -67,6 +67,24 @@ class TreeUndoServiceTest {
|
||||
assertTrue(fixture.store.get(fixture.playerId).isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
void consumesAndRestoresOnlyFelledPoplarTrunks() {
|
||||
Fixture fixture = new Fixture(FELLED_AT.plusSeconds(60));
|
||||
fixture.record(List.of(
|
||||
fixture.snapshot(0, Material.POPLAR_LOG),
|
||||
fixture.snapshot(1, Material.POPLAR_LOG)));
|
||||
when(fixture.inventory.getStorageContents()).thenReturn(new ItemStack[] {
|
||||
new ItemStack(Material.POPLAR_LOG, 2)
|
||||
});
|
||||
|
||||
UndoResult result = fixture.service().undo(fixture.player, 6);
|
||||
|
||||
assertEquals(UndoStatus.SUCCESS, result.status());
|
||||
verify(fixture.inventory).setItem(0, null);
|
||||
verify(fixture.block, org.mockito.Mockito.times(2))
|
||||
.setBlockData(fixture.restoredData, false);
|
||||
}
|
||||
|
||||
@Test
|
||||
void refusesTheWholeUndoWhenAnyRestorationPositionIsOccupied() {
|
||||
Fixture fixture = new Fixture(FELLED_AT.plusSeconds(60));
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
package games.dmg.treefeller;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.io.InputStreamReader;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.command.PluginCommand;
|
||||
import org.bukkit.command.SimpleCommandMap;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class RegisteredCommandAutocompleteTest {
|
||||
@Test
|
||||
void registeredPlayerCommandFiltersEveryApplicableArgumentPositionAndDeniesPermission()
|
||||
throws Exception {
|
||||
InMemoryCatalog states = new InMemoryCatalog();
|
||||
SimpleCommandMap commands = registered(
|
||||
"treefeller",
|
||||
"treefeller.command",
|
||||
new TreeFellerCommand(states, ignored -> { }));
|
||||
Player denied = mock(Player.class);
|
||||
Player player = mock(Player.class);
|
||||
when(player.hasPermission("treefeller.command")).thenReturn(true);
|
||||
|
||||
assertEquals(List.of(), complete(commands, denied, "treefeller unl"));
|
||||
assertEquals(List.of("unlocked"), complete(commands, player, "treefeller unl"));
|
||||
assertEquals(List.of("off", "on"), complete(commands, player, "treefeller enabled "));
|
||||
assertEquals(List.of(), complete(commands, player, "treefeller unlocked extra"));
|
||||
assertEquals(List.of(), complete(commands, player, "treefeller unsupported "));
|
||||
assertEquals(0, states.saveCount);
|
||||
}
|
||||
|
||||
@Test
|
||||
void registeredAdminCommandFiltersPoplarAtEverySpeciesPositionAndNeverMutates()
|
||||
throws Exception {
|
||||
Player target = mock(Player.class);
|
||||
when(target.getUniqueId()).thenReturn(UUID.randomUUID());
|
||||
when(target.getName()).thenReturn("Target");
|
||||
Server server = mock(Server.class);
|
||||
Collection<Player> players = List.of(target);
|
||||
when(server.getOnlinePlayers()).thenAnswer(ignored -> players);
|
||||
when(server.getPlayer(target.getUniqueId())).thenReturn(target);
|
||||
InMemoryCatalog states = new InMemoryCatalog();
|
||||
TreeFellerAdminCommand handler = new TreeFellerAdminCommand(
|
||||
server,
|
||||
states,
|
||||
new TreeFellerSettingsService(defaults(), (species, threshold) -> { }),
|
||||
ignored -> { });
|
||||
SimpleCommandMap commands = registered(
|
||||
"treefelleradmin", "treefeller.admin", handler);
|
||||
CommandSender denied = mock(CommandSender.class);
|
||||
CommandSender administrator = mock(CommandSender.class);
|
||||
when(administrator.hasPermission("treefeller.admin")).thenReturn(true);
|
||||
|
||||
assertEquals(List.of(), complete(commands, denied, "treefelleradmin "));
|
||||
assertEquals(List.of("poplar"), complete(
|
||||
commands, administrator, "treefelleradmin threshold pop"));
|
||||
assertEquals(List.of("poplar"), complete(
|
||||
commands, administrator, "treefelleradmin player Target tree pop"));
|
||||
assertEquals(List.of("grant"), complete(
|
||||
commands, administrator, "treefelleradmin player Target tree poplar g"));
|
||||
assertEquals(List.of(), complete(
|
||||
commands, administrator, "treefelleradmin player Target tree poplar grant extra"));
|
||||
assertEquals(List.of(), complete(
|
||||
commands, administrator, "treefelleradmin unsupported "));
|
||||
assertEquals(0, states.saveCount);
|
||||
}
|
||||
|
||||
private List<String> complete(
|
||||
SimpleCommandMap commands, CommandSender sender, String commandLine) {
|
||||
List<String> completions = commands.tabComplete(sender, commandLine);
|
||||
if (completions == null) {
|
||||
return List.of();
|
||||
}
|
||||
return completions.stream()
|
||||
.map(value -> value.substring(value.lastIndexOf(' ') + 1))
|
||||
.toList();
|
||||
}
|
||||
|
||||
private SimpleCommandMap registered(
|
||||
String name, String permission, CommandExecutor handler) throws Exception {
|
||||
Constructor<PluginCommand> constructor = PluginCommand.class
|
||||
.getDeclaredConstructor(String.class, Plugin.class);
|
||||
constructor.setAccessible(true);
|
||||
PluginCommand command = constructor.newInstance(name, mock(Plugin.class));
|
||||
command.setPermission(permission);
|
||||
command.setExecutor(handler);
|
||||
command.setTabCompleter((org.bukkit.command.TabCompleter) handler);
|
||||
SimpleCommandMap commandMap = new SimpleCommandMap(mock(Server.class), new HashMap<>());
|
||||
commandMap.register("treefeller", command);
|
||||
return commandMap;
|
||||
}
|
||||
|
||||
private TreeFellerSettings defaults() throws Exception {
|
||||
try (InputStreamReader reader = new InputStreamReader(
|
||||
getClass().getClassLoader().getResourceAsStream("config.yml"),
|
||||
StandardCharsets.UTF_8)) {
|
||||
return TreeFellerSettings.load(YamlConfiguration.loadConfiguration(reader));
|
||||
}
|
||||
}
|
||||
|
||||
private static final class InMemoryCatalog implements PlayerStateCatalog {
|
||||
private int saveCount;
|
||||
|
||||
@Override
|
||||
public Optional<PlayerTreeFellerState> load(UUID playerId) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PlayerTreeFellerState> loadAll() {
|
||||
return List.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(PlayerTreeFellerState changed) {
|
||||
saveCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -84,20 +84,47 @@ class TreeFellerAdminCommandTest {
|
||||
@Test
|
||||
void changesAndPersistsAValidatedSpeciesThreshold() throws Exception {
|
||||
InMemoryCatalog states = new InMemoryCatalog();
|
||||
List<String> saves = new ArrayList<>();
|
||||
TreeFellerSettingsService settings = new TreeFellerSettingsService(
|
||||
defaults(), (species, threshold) -> { });
|
||||
defaults(), (species, threshold) -> saves.add(species.id() + ":" + threshold));
|
||||
TreeFellerAdminCommand handler = new TreeFellerAdminCommand(
|
||||
mock(Server.class), states, settings, ignored -> { });
|
||||
CommandSender administrator = administrator();
|
||||
|
||||
handler.onCommand(administrator, mock(Command.class), "treefelleradmin",
|
||||
new String[] {"threshold", "dark-oak", "25"});
|
||||
new String[] {"threshold", "poplar", "25"});
|
||||
|
||||
assertEquals(25, settings.current().threshold(TreeSpecies.DARK_OAK));
|
||||
verify(administrator).sendMessage(contains("Dark Oak"));
|
||||
assertEquals(List.of("poplar:25"), saves);
|
||||
assertEquals(25, settings.current().threshold(TreeSpecies.POPLAR));
|
||||
verify(administrator).sendMessage(contains("Poplar"));
|
||||
verify(administrator).sendMessage(contains("25"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void administersPoplarAndRejectsUnsupportedArgumentsWithoutMutation() throws Exception {
|
||||
UUID playerId = UUID.randomUUID();
|
||||
Player target = mock(Player.class);
|
||||
when(target.getUniqueId()).thenReturn(playerId);
|
||||
when(target.getName()).thenReturn("Target");
|
||||
InMemoryCatalog states = new InMemoryCatalog();
|
||||
states.state = PlayerTreeFellerState.initial(playerId, "Target")
|
||||
.withProgress(TreeSpecies.POPLAR, 9);
|
||||
TreeFellerAdminCommand handler = handler(serverWith(target), states);
|
||||
CommandSender administrator = administrator();
|
||||
|
||||
handler.onCommand(administrator, mock(Command.class), "treefelleradmin",
|
||||
new String[] {"player", "Target", "status"});
|
||||
verify(administrator).sendMessage(contains("Poplar: locked (9/100)"));
|
||||
|
||||
handler.onCommand(administrator, mock(Command.class), "treefelleradmin",
|
||||
new String[] {"player", "Target", "tree", "poplar", "grant"});
|
||||
assertTrue(states.state.isUnlocked(TreeSpecies.POPLAR));
|
||||
|
||||
handler.onCommand(administrator, mock(Command.class), "treefelleradmin",
|
||||
new String[] {"player", "Target", "tree", "poplar", "grant", "extra"});
|
||||
assertEquals(1, states.saveCount);
|
||||
}
|
||||
|
||||
@Test
|
||||
void autocompleteIsPermissionAwareAndPositionSpecific() throws Exception {
|
||||
Player target = mock(Player.class);
|
||||
@@ -113,11 +140,26 @@ class TreeFellerAdminCommandTest {
|
||||
administrator, mock(Command.class), "treefelleradmin", new String[] {"p"}));
|
||||
assertEquals(List.of("Target"), handler.onTabComplete(
|
||||
administrator, mock(Command.class), "treefelleradmin", new String[] {"player", "T"}));
|
||||
assertEquals(List.of("poplar"), handler.onTabComplete(
|
||||
administrator,
|
||||
mock(Command.class),
|
||||
"treefelleradmin",
|
||||
new String[] {"threshold", "pop"}));
|
||||
assertEquals(List.of("poplar"), handler.onTabComplete(
|
||||
administrator,
|
||||
mock(Command.class),
|
||||
"treefelleradmin",
|
||||
new String[] {"player", "Target", "tree", "pop"}));
|
||||
assertEquals(List.of("grant"), handler.onTabComplete(
|
||||
administrator,
|
||||
mock(Command.class),
|
||||
"treefelleradmin",
|
||||
new String[] {"player", "Target", "tree", "oak", "g"}));
|
||||
new String[] {"player", "Target", "tree", "poplar", "g"}));
|
||||
assertEquals(List.of(), handler.onTabComplete(
|
||||
administrator,
|
||||
mock(Command.class),
|
||||
"treefelleradmin",
|
||||
new String[] {"player", "Target", "tree", "poplar", "grant", "extra"}));
|
||||
}
|
||||
|
||||
private TreeFellerAdminCommand handler(Server server, InMemoryCatalog states) throws Exception {
|
||||
@@ -152,6 +194,7 @@ class TreeFellerAdminCommandTest {
|
||||
|
||||
private static final class InMemoryCatalog implements PlayerStateCatalog {
|
||||
private PlayerTreeFellerState state;
|
||||
private int saveCount;
|
||||
|
||||
@Override
|
||||
public Optional<PlayerTreeFellerState> load(UUID playerId) {
|
||||
@@ -168,6 +211,7 @@ class TreeFellerAdminCommandTest {
|
||||
@Override
|
||||
public void save(PlayerTreeFellerState changed) {
|
||||
state = changed;
|
||||
saveCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -73,6 +73,7 @@ class TreeFellerCommandTest {
|
||||
|
||||
verify(player).sendMessage(contains("Oak: locked (12/100)"));
|
||||
verify(player).sendMessage(contains("Birch: unlocked"));
|
||||
verify(player).sendMessage(contains("Poplar: locked (0/100)"));
|
||||
verify(player, atLeastOnce()).sendMessage(contains("Mushroom"));
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,20 @@ class TreeFellerSettingsTest {
|
||||
assertEquals(6, settings.undoWindowMinutes());
|
||||
}
|
||||
|
||||
@Test
|
||||
void acceptsOnlyTheExistingThresholdRangeForPoplar() throws Exception {
|
||||
TreeFellerSettings settings = TreeFellerSettings.load(loadDefaults());
|
||||
|
||||
assertEquals(1, settings.withThreshold(TreeSpecies.POPLAR, 1)
|
||||
.threshold(TreeSpecies.POPLAR));
|
||||
assertEquals(1_000_000, settings.withThreshold(TreeSpecies.POPLAR, 1_000_000)
|
||||
.threshold(TreeSpecies.POPLAR));
|
||||
assertThrows(IllegalArgumentException.class,
|
||||
() -> settings.withThreshold(TreeSpecies.POPLAR, 0));
|
||||
assertThrows(IllegalArgumentException.class,
|
||||
() -> settings.withThreshold(TreeSpecies.POPLAR, 1_000_001));
|
||||
}
|
||||
|
||||
@Test
|
||||
void rejectsUnsafeRequiredNumbers() throws Exception {
|
||||
YamlConfiguration configuration = loadDefaults();
|
||||
|
||||
@@ -38,6 +38,27 @@ class TreeStructureScannerTest {
|
||||
.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
void recognizesEveryPoplarFoliageColorAsTheSameSpecies() {
|
||||
for (Material foliage : new Material[] {
|
||||
Material.ORANGE_POPLAR_LEAVES,
|
||||
Material.RED_POPLAR_LEAVES,
|
||||
Material.YELLOW_POPLAR_LEAVES
|
||||
}) {
|
||||
TestBlocks blocks = new TestBlocks();
|
||||
blocks.put(0, 0, 0, Material.POPLAR_LOG);
|
||||
blocks.put(0, 1, 0, Material.POPLAR_LOG);
|
||||
blocks.put(1, 1, 0, foliage);
|
||||
|
||||
TreeStructure tree = new TreeStructureScanner(100, 16)
|
||||
.scan(blocks, new BlockPoint(0, 0, 0))
|
||||
.orElseThrow();
|
||||
|
||||
assertEquals(TreeSpecies.POPLAR, tree.species());
|
||||
assertEquals(2, tree.trunkBlocks().size());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void distinguishesGiantMushroomsByTheirCaps() {
|
||||
TestBlocks blocks = new TestBlocks();
|
||||
|
||||
@@ -20,6 +20,7 @@ class TreeTaxonomyTest {
|
||||
Map.entry(Material.MANGROVE_LOG, TreeSpecies.MANGROVE),
|
||||
Map.entry(Material.CHERRY_LOG, TreeSpecies.CHERRY),
|
||||
Map.entry(Material.PALE_OAK_LOG, TreeSpecies.PALE_OAK),
|
||||
Map.entry(Material.POPLAR_LOG, TreeSpecies.POPLAR),
|
||||
Map.entry(Material.CRIMSON_STEM, TreeSpecies.CRIMSON),
|
||||
Map.entry(Material.WARPED_STEM, TreeSpecies.WARPED));
|
||||
|
||||
@@ -29,5 +30,12 @@ class TreeTaxonomyTest {
|
||||
assertTrue(TreeTaxonomy.directSpecies(Material.OAK_WOOD).isEmpty());
|
||||
assertTrue(TreeTaxonomy.directSpecies(Material.STRIPPED_OAK_LOG).isEmpty());
|
||||
assertTrue(TreeTaxonomy.directSpecies(Material.CRIMSON_HYPHAE).isEmpty());
|
||||
assertTrue(TreeTaxonomy.directSpecies(Material.POPLAR_WOOD).isEmpty());
|
||||
assertTrue(TreeTaxonomy.directSpecies(Material.STRIPPED_POPLAR_LOG).isEmpty());
|
||||
assertTrue(TreeTaxonomy.directSpecies(Material.STRIPPED_POPLAR_WOOD).isEmpty());
|
||||
assertTrue(TreeTaxonomy.directSpecies(Material.POPLAR_PLANKS).isEmpty());
|
||||
assertTrue(TreeTaxonomy.directSpecies(Material.POPLAR_SAPLING).isEmpty());
|
||||
assertTrue(TreeTaxonomy.directSpecies(Material.FIREFLY_BUSH).isEmpty());
|
||||
assertTrue(TreeTaxonomy.directSpecies(Material.RED_SHRUB).isEmpty());
|
||||
}
|
||||
}
|
||||
@@ -43,6 +43,37 @@ class YamlPlayerStateRepositoryTest {
|
||||
assertFalse(Files.exists(file.resolveSibling("players.yml.tmp")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void loadsLegacyRecordsWithZeroLockedPoplarProgressAndPreservesExistingData() throws Exception {
|
||||
Path file = temporaryDirectory.resolve("players.yml");
|
||||
UUID playerId = UUID.randomUUID();
|
||||
String root = "players." + playerId;
|
||||
YamlConfiguration legacy = new YamlConfiguration();
|
||||
legacy.set(root + ".latest-name", "CurrentName");
|
||||
legacy.set(root + ".known-names", java.util.List.of("OldName", "CurrentName"));
|
||||
legacy.set(root + ".enabled", false);
|
||||
legacy.set(root + ".locked", true);
|
||||
legacy.set(root + ".progress.oak", 73);
|
||||
legacy.set(root + ".unlocked", java.util.List.of("birch"));
|
||||
legacy.set(root + ".future-player", 42);
|
||||
legacy.save(file.toFile());
|
||||
|
||||
YamlPlayerStateRepository repository = new YamlPlayerStateRepository(file);
|
||||
PlayerTreeFellerState state = repository.load(playerId).orElseThrow();
|
||||
repository.save(state);
|
||||
|
||||
assertEquals(0, state.progress(TreeSpecies.POPLAR));
|
||||
assertFalse(state.isUnlocked(TreeSpecies.POPLAR));
|
||||
assertEquals(73, state.progress(TreeSpecies.OAK));
|
||||
assertTrue(state.isUnlocked(TreeSpecies.BIRCH));
|
||||
assertFalse(state.enabled());
|
||||
assertTrue(state.locked());
|
||||
assertEquals(java.util.Set.of("OldName", "CurrentName"), state.knownNames());
|
||||
YamlConfiguration saved = YamlConfiguration.loadConfiguration(file.toFile());
|
||||
assertEquals(42, saved.getInt(root + ".future-player"));
|
||||
assertEquals(0, saved.getLong(root + ".progress.poplar"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void rejectsARecordContainingUnknownUnlocksInsteadOfGrantingAccess() throws Exception {
|
||||
Path file = temporaryDirectory.resolve("players.yml");
|
||||
|
||||
Reference in new issue
Block a user