feat(base): implement progression system
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
package games.dmg.spigotbase;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
import java.util.UUID;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
final class AdminProgressionServiceTest {
|
||||
private final AdminProgressionService service = new AdminProgressionService();
|
||||
|
||||
@Test
|
||||
void settingTeleportUpgradeGrantsBasePrerequisites() {
|
||||
PlayerState player = PlayerState.newPlayer(UUID.randomUUID(), "Alex");
|
||||
|
||||
PlayerState updated = service.setLevel(player, ProgressionPath.WARMUP, 2);
|
||||
|
||||
assertEquals(3, updated.baseLevel());
|
||||
assertEquals(2, updated.warmupLevel());
|
||||
}
|
||||
|
||||
@Test
|
||||
void loweringBaseRemovesDependentBenefits() {
|
||||
PlayerState player = PlayerState.newPlayer(UUID.randomUUID(), "Alex")
|
||||
.withAdministrativeLevels(4, 3, 3, 3, 4, true, true, true);
|
||||
|
||||
PlayerState updated = service.setLevel(player, ProgressionPath.BASE, 0);
|
||||
|
||||
assertEquals(0, updated.sizeLevel());
|
||||
assertEquals(0, updated.flightLevel());
|
||||
assertEquals(0, updated.warmupLevel());
|
||||
assertEquals(0, updated.cooldownLevel());
|
||||
assertFalse(updated.navigationEnabled());
|
||||
assertFalse(updated.flightEnabled());
|
||||
assertFalse(updated.visitorsEnabled());
|
||||
}
|
||||
|
||||
@Test
|
||||
void rejectsUnknownLevel() {
|
||||
PlayerState player = PlayerState.newPlayer(UUID.randomUUID(), "Alex");
|
||||
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> service.setLevel(player, ProgressionPath.FLIGHT, 4)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package games.dmg.spigotbase;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.util.UUID;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
final class BaseAreaTest {
|
||||
@Test
|
||||
void usesCircularHorizontalAndSymmetricVerticalBounds() {
|
||||
UUID worldId = UUID.randomUUID();
|
||||
BaseArea area = new BaseArea(
|
||||
new BaseLocation(worldId, "world", 0, 64, 0, 0, 0),
|
||||
10,
|
||||
25
|
||||
);
|
||||
|
||||
assertTrue(area.contains(worldId, 6, 89, 8));
|
||||
assertTrue(area.contains(worldId, 6, 39, 8));
|
||||
assertFalse(area.contains(worldId, 10, 64, 10));
|
||||
assertFalse(area.contains(worldId, 0, 90, 0));
|
||||
assertFalse(area.contains(UUID.randomUUID(), 0, 64, 0));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package games.dmg.spigotbase;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
final class BaseBoundsServiceTest {
|
||||
private final BaseBoundsService service = new BaseBoundsService(PluginSettings.from(Map.of()));
|
||||
|
||||
@Test
|
||||
void sizeTiersSelectConfiguredRadius() {
|
||||
PlayerState player = PlayerState.newPlayer(UUID.randomUUID(), "Alex")
|
||||
.withGrassAndDirtProgress(250, 1);
|
||||
|
||||
assertEquals(10, service.radius(player));
|
||||
assertEquals(25, service.radius(player.withSizeProgress(500, 0, 0, 1)));
|
||||
assertEquals(75, service.radius(player.withSizeProgress(500, 1_000, 0, 2)));
|
||||
assertEquals(150, service.radius(player.withSizeProgress(500, 1_000, 1_000, 3)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void flightTiersSelectSymmetricVerticalRange() {
|
||||
PlayerState player = PlayerState.newPlayer(UUID.randomUUID(), "Alex")
|
||||
.withGrassAndDirtProgress(250, 1);
|
||||
|
||||
assertEquals(25, service.verticalRange(player));
|
||||
assertEquals(25, service.verticalRange(player.withFlightLevel(1, true)));
|
||||
assertEquals(100, service.verticalRange(player.withFlightLevel(2, true)));
|
||||
assertEquals(Integer.MAX_VALUE, service.verticalRange(player.withFlightLevel(3, true)));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package games.dmg.spigotbase;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
final class BaseNavigationProgressionTest {
|
||||
@Test
|
||||
void fiveHundredthGrassOrDirtBlockUnlocksBaseII() {
|
||||
BaseProgressionService service =
|
||||
new BaseProgressionService(PluginSettings.from(Map.of()));
|
||||
PlayerState player = PlayerState.newPlayer(UUID.randomUUID(), "Alex")
|
||||
.withGrassAndDirtProgress(499, 1);
|
||||
|
||||
ProgressionUpdate update = service.recordGrassOrDirtBreak(player);
|
||||
|
||||
assertEquals(2, update.player().baseLevel());
|
||||
assertTrue(update.player().navigationEnabled());
|
||||
assertTrue(update.unlockedBaseLevel());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package games.dmg.spigotbase;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.util.UUID;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
final class BaseProgressionServiceTest {
|
||||
private final PluginSettings settings = PluginSettings.from(java.util.Map.of());
|
||||
private final BaseProgressionService service = new BaseProgressionService(settings);
|
||||
|
||||
@Test
|
||||
void qualifyingBreakIncrementsProgress() {
|
||||
PlayerState initial = PlayerState.newPlayer(UUID.randomUUID(), "Alex");
|
||||
|
||||
ProgressionUpdate update = service.recordGrassOrDirtBreak(initial);
|
||||
|
||||
assertEquals(1, update.player().grassAndDirtBroken());
|
||||
assertEquals(0, update.player().baseLevel());
|
||||
assertFalse(update.unlockedBaseLevel());
|
||||
}
|
||||
|
||||
@Test
|
||||
void thresholdUnlocksBaseIExactlyOnce() {
|
||||
PlayerState initial = PlayerState.newPlayer(UUID.randomUUID(), "Alex")
|
||||
.withGrassAndDirtProgress(249, 0);
|
||||
|
||||
ProgressionUpdate unlocked = service.recordGrassOrDirtBreak(initial);
|
||||
ProgressionUpdate later = service.recordGrassOrDirtBreak(unlocked.player());
|
||||
|
||||
assertEquals(1, unlocked.player().baseLevel());
|
||||
assertTrue(unlocked.unlockedBaseLevel());
|
||||
assertEquals(1, later.player().baseLevel());
|
||||
assertFalse(later.unlockedBaseLevel());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package games.dmg.spigotbase;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
final class BaseServiceTest {
|
||||
private final PluginSettings settings = PluginSettings.from(Map.of());
|
||||
private final BaseService service = new BaseService(settings);
|
||||
|
||||
@Test
|
||||
void firstBaseCanBeSetImmediately() {
|
||||
PlayerState player = PlayerState.newPlayer(UUID.randomUUID(), "Alex")
|
||||
.withGrassAndDirtProgress(250, 1);
|
||||
|
||||
assertTrue(service.canSetBase(player, Instant.EPOCH));
|
||||
}
|
||||
|
||||
@Test
|
||||
void relocationUsesElapsedTwentyFourHours() {
|
||||
Instant firstSet = Instant.parse("2026-08-09T12:00:00Z");
|
||||
PlayerState player = PlayerState.newPlayer(UUID.randomUUID(), "Alex")
|
||||
.withGrassAndDirtProgress(250, 1)
|
||||
.withBase(
|
||||
new BaseLocation(UUID.randomUUID(), "world", 0, 64, 0, 0, 0),
|
||||
firstSet
|
||||
);
|
||||
|
||||
assertFalse(service.canSetBase(player, firstSet.plus(Duration.ofHours(23))));
|
||||
assertEquals(
|
||||
Duration.ofHours(1),
|
||||
service.relocationRemaining(player, firstSet.plus(Duration.ofHours(23))).orElseThrow()
|
||||
);
|
||||
assertTrue(service.canSetBase(player, firstSet.plus(Duration.ofHours(24))));
|
||||
}
|
||||
|
||||
@Test
|
||||
void lockedPlayerCannotSetBase() {
|
||||
PlayerState player = PlayerState.newPlayer(UUID.randomUUID(), "Alex");
|
||||
|
||||
assertFalse(service.canSetBase(player, Instant.EPOCH));
|
||||
assertEquals(Optional.empty(), service.relocationRemaining(player, Instant.EPOCH));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package games.dmg.spigotbase;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.UUID;
|
||||
import java.util.logging.Logger;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
|
||||
final class BaseStateManagerTest {
|
||||
@TempDir
|
||||
Path temporaryDirectory;
|
||||
|
||||
@Test
|
||||
void durableUpdateIsImmediatelyReadableFromRepository() throws Exception {
|
||||
Path stateFile = temporaryDirectory.resolve("state.yml");
|
||||
BaseStateManager manager = new BaseStateManager(
|
||||
new YamlBaseStateRepository(stateFile), Logger.getAnonymousLogger()
|
||||
);
|
||||
UUID playerId = UUID.randomUUID();
|
||||
|
||||
manager.updateAndSave(
|
||||
playerId,
|
||||
"Alex",
|
||||
player -> player.withGrassAndDirtProgress(250, 1)
|
||||
);
|
||||
|
||||
PlayerState loaded = new YamlBaseStateRepository(stateFile).load().players().get(playerId);
|
||||
assertEquals(1, loaded.baseLevel());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package games.dmg.spigotbase;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
final class PlayerStateTest {
|
||||
@Test
|
||||
void rejectsLevelsOutsideKnownRanges() {
|
||||
UUID playerId = UUID.randomUUID();
|
||||
|
||||
assertThrows(IllegalArgumentException.class, () -> new PlayerState(
|
||||
playerId, "Alex", Optional.empty(), 5, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0,
|
||||
false, false, true, false, Optional.empty(), Optional.empty(), Map.of()
|
||||
));
|
||||
}
|
||||
|
||||
@Test
|
||||
void rejectsBenefitsWhosePrerequisitesAreMissing() {
|
||||
UUID playerId = UUID.randomUUID();
|
||||
|
||||
assertThrows(IllegalArgumentException.class, () -> new PlayerState(
|
||||
playerId, "Alex", Optional.empty(), 0, 1, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0,
|
||||
false, false, true, false, Optional.empty(), Optional.empty(), Map.of()
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package games.dmg.spigotbase;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.Map;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.yaml.snakeyaml.Yaml;
|
||||
|
||||
final class PluginMetadataTest {
|
||||
@Test
|
||||
void declaresPluginEntrypointCommandsAndPermissions() {
|
||||
InputStream stream = getClass().getClassLoader().getResourceAsStream("plugin.yml");
|
||||
assertNotNull(stream);
|
||||
|
||||
Map<?, ?> plugin = new Yaml().load(stream);
|
||||
assertEquals("SpigotBase", plugin.get("name"));
|
||||
assertEquals("games.dmg.spigotbase.SpigotBasePlugin", plugin.get("main"));
|
||||
assertEquals("1.20", plugin.get("api-version"));
|
||||
|
||||
Map<?, ?> commands = (Map<?, ?>) plugin.get("commands");
|
||||
for (String command : new String[] {
|
||||
"setbase", "base", "basenavigation", "baseflight",
|
||||
"basevisitors", "gotobase", "baseprogress", "baseadmin"
|
||||
}) {
|
||||
assertNotNull(commands.get(command), command);
|
||||
}
|
||||
|
||||
Map<?, ?> permissions = (Map<?, ?>) plugin.get("permissions");
|
||||
assertNotNull(permissions.get("spigotbase.admin"));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package games.dmg.spigotbase;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
import java.util.Map;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
final class PluginSettingsTest {
|
||||
@Test
|
||||
void defaultsMatchApprovedProgression() {
|
||||
PluginSettings settings = PluginSettings.from(Map.of());
|
||||
|
||||
assertEquals(250, settings.baseUnlockBlocks());
|
||||
assertEquals(500, settings.navigationUnlockBlocks());
|
||||
assertEquals(10, settings.initialRadius());
|
||||
assertEquals(25, settings.initialVerticalRange());
|
||||
assertEquals(86_400L, settings.relocationCooldownSeconds());
|
||||
assertEquals(5, settings.flightWarningBuffer());
|
||||
assertEquals(200, settings.teleportUnlockPlacements());
|
||||
assertEquals(10_800L, settings.initialTeleportCooldownSeconds());
|
||||
assertEquals(128, settings.visitorUnlockDiamondCost());
|
||||
}
|
||||
|
||||
@Test
|
||||
void rejectsNavigationThresholdBeforeBaseThreshold() {
|
||||
Map<String, Object> values = Map.of(
|
||||
"base-unlock-blocks", 500,
|
||||
"navigation-unlock-blocks", 250
|
||||
);
|
||||
|
||||
assertThrows(IllegalArgumentException.class, () -> PluginSettings.from(values));
|
||||
}
|
||||
|
||||
@Test
|
||||
void rejectsNegativeDurationsAndDistances() {
|
||||
assertThrows(IllegalArgumentException.class,
|
||||
() -> PluginSettings.from(Map.of("relocation-cooldown-seconds", -1)));
|
||||
assertThrows(IllegalArgumentException.class,
|
||||
() -> PluginSettings.from(Map.of("initial-radius", 0)));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package games.dmg.spigotbase;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
final class SecondaryProgressionServiceTest {
|
||||
private final PluginSettings settings = PluginSettings.from(Map.of());
|
||||
private final SecondaryProgressionService service = new SecondaryProgressionService(settings);
|
||||
|
||||
@Test
|
||||
void stoneUnlocksFirstSizeTierAfterBaseI() {
|
||||
PlayerState player = PlayerState.newPlayer(UUID.randomUUID(), "Alex")
|
||||
.withGrassAndDirtProgress(250, 1)
|
||||
.withSizeProgress(499, 0, 0, 0);
|
||||
|
||||
ProgressionUpdate update = service.recordStoneBreak(player);
|
||||
|
||||
assertEquals(1, update.player().sizeLevel());
|
||||
assertTrue(update.unlockedSizeLevel());
|
||||
}
|
||||
|
||||
@Test
|
||||
void laterMaterialsDoNotCountBeforeTheirTierIsActive() {
|
||||
PlayerState player = PlayerState.newPlayer(UUID.randomUUID(), "Alex")
|
||||
.withGrassAndDirtProgress(250, 1);
|
||||
|
||||
ProgressionUpdate update = service.recordDeepslateBreak(player);
|
||||
|
||||
assertEquals(0, update.player().deepslateBroken());
|
||||
assertFalse(update.unlockedSizeLevel());
|
||||
}
|
||||
|
||||
@Test
|
||||
void threeElytraUnlockAllFlightTiersWithoutConsumption() {
|
||||
PlayerState player = PlayerState.newPlayer(UUID.randomUUID(), "Alex")
|
||||
.withGrassAndDirtProgress(250, 1)
|
||||
.withBase(
|
||||
new BaseLocation(UUID.randomUUID(), "world", 0, 64, 0, 0, 0),
|
||||
java.time.Instant.EPOCH
|
||||
);
|
||||
|
||||
ProgressionUpdate update = service.observeElytraCount(player, 3);
|
||||
|
||||
assertEquals(3, update.player().flightLevel());
|
||||
assertTrue(update.player().flightEnabled());
|
||||
assertTrue(update.unlockedFlightLevel());
|
||||
}
|
||||
|
||||
@Test
|
||||
void secondaryProgressDoesNothingBeforeBaseI() {
|
||||
PlayerState player = PlayerState.newPlayer(UUID.randomUUID(), "Alex");
|
||||
|
||||
assertEquals(player, service.recordStoneBreak(player).player());
|
||||
assertEquals(player, service.observeElytraCount(player, 3).player());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package games.dmg.spigotbase;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
final class TeleportPolicyTest {
|
||||
private final TeleportPolicy policy = new TeleportPolicy(PluginSettings.from(Map.of()));
|
||||
|
||||
@Test
|
||||
void warmupLevelsUseApprovedDurations() {
|
||||
PlayerState player = baseThreePlayer();
|
||||
|
||||
assertEquals(Duration.ofSeconds(30), policy.warmup(player));
|
||||
assertEquals(Duration.ofSeconds(15), policy.warmup(player.withTeleportLevels(1, 0)));
|
||||
assertEquals(Duration.ofSeconds(5), policy.warmup(player.withTeleportLevels(2, 0)));
|
||||
assertEquals(Duration.ZERO, policy.warmup(player.withTeleportLevels(3, 0)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void cooldownLevelsUseApprovedDurations() {
|
||||
PlayerState player = baseThreePlayer();
|
||||
|
||||
assertEquals(Duration.ofHours(3), policy.cooldown(player));
|
||||
assertEquals(Duration.ofHours(2), policy.cooldown(player.withTeleportLevels(0, 1)));
|
||||
assertEquals(Duration.ofHours(1), policy.cooldown(player.withTeleportLevels(0, 2)));
|
||||
assertEquals(Duration.ofMinutes(30), policy.cooldown(player.withTeleportLevels(0, 3)));
|
||||
assertEquals(Duration.ZERO, policy.cooldown(player.withTeleportLevels(0, 4)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void remainingCooldownUsesElapsedTime() {
|
||||
Instant usedAt = Instant.parse("2026-08-09T10:00:00Z");
|
||||
PlayerState player = baseThreePlayer().withLastBaseTeleport(usedAt);
|
||||
|
||||
assertEquals(
|
||||
Duration.ofHours(1),
|
||||
policy.remainingCooldown(player, usedAt.plus(Duration.ofHours(2))).orElseThrow()
|
||||
);
|
||||
assertTrue(policy.remainingCooldown(player, usedAt.plus(Duration.ofHours(3))).isEmpty());
|
||||
}
|
||||
|
||||
private static PlayerState baseThreePlayer() {
|
||||
return PlayerState.newPlayer(UUID.randomUUID(), "Alex")
|
||||
.withGrassAndDirtProgress(500, 3);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package games.dmg.spigotbase;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
final class TeleportProgressionServiceTest {
|
||||
private final PluginSettings settings = PluginSettings.from(Map.of());
|
||||
private final TeleportProgressionService service = new TeleportProgressionService(settings);
|
||||
|
||||
@Test
|
||||
void twoHundredthPlacementUnlocksBaseIII() {
|
||||
PlayerState player = baseTwoPlayer().withTeleportProgress(199, 0, 0, 0, 2);
|
||||
|
||||
ProgressionUpdate update = service.recordPlacement(player);
|
||||
|
||||
assertEquals(3, update.player().baseLevel());
|
||||
assertEquals(200, update.player().blocksPlacedInBase());
|
||||
assertTrue(update.unlockedBaseLevel());
|
||||
}
|
||||
|
||||
@Test
|
||||
void cumulativePlacementsReduceWarmup() {
|
||||
PlayerState player = baseTwoPlayer().withTeleportProgress(999, 0, 0, 0, 3);
|
||||
|
||||
ProgressionUpdate update = service.recordPlacement(player);
|
||||
|
||||
assertEquals(1, update.player().warmupLevel());
|
||||
assertTrue(update.unlockedWarmupLevel());
|
||||
}
|
||||
|
||||
@Test
|
||||
void cumulativeBreaksReduceCooldownSequentially() {
|
||||
PlayerState player = baseTwoPlayer().withTeleportProgress(200, 999, 0, 0, 3);
|
||||
|
||||
ProgressionUpdate update = service.recordBreak(player);
|
||||
|
||||
assertEquals(1, update.player().cooldownLevel());
|
||||
assertEquals(1_000, update.player().blocksBrokenInBase());
|
||||
assertTrue(update.unlockedCooldownLevel());
|
||||
}
|
||||
|
||||
private static PlayerState baseTwoPlayer() {
|
||||
return PlayerState.newPlayer(UUID.randomUUID(), "Alex")
|
||||
.withGrassAndDirtProgress(500, 2)
|
||||
.withNavigationEnabled(true)
|
||||
.withBase(
|
||||
new BaseLocation(UUID.randomUUID(), "world", 0, 64, 0, 0, 0),
|
||||
Instant.EPOCH
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package games.dmg.spigotbase;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.UUID;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
final class VisitorPolicyTest {
|
||||
private final VisitorPolicy policy = new VisitorPolicy();
|
||||
|
||||
@Test
|
||||
void onlyBaseIIIPlayerCanPurchaseBaseIV() {
|
||||
PlayerState baseThree = PlayerState.newPlayer(UUID.randomUUID(), "Owner")
|
||||
.withGrassAndDirtProgress(500, 3)
|
||||
.withBase(
|
||||
new BaseLocation(UUID.randomUUID(), "world", 0, 64, 0, 0, 0),
|
||||
Instant.EPOCH
|
||||
);
|
||||
|
||||
assertTrue(policy.canPurchase(baseThree));
|
||||
assertFalse(policy.canPurchase(baseThree.withBaseLevel(4)));
|
||||
assertFalse(policy.canPurchase(
|
||||
PlayerState.newPlayer(UUID.randomUUID(), "Locked").withGrassAndDirtProgress(500, 2)
|
||||
));
|
||||
assertFalse(policy.canPurchase(
|
||||
PlayerState.newPlayer(UUID.randomUUID(), "Unset").withGrassAndDirtProgress(500, 3)
|
||||
));
|
||||
}
|
||||
|
||||
@Test
|
||||
void visitorCooldownIsIndependentForEachOwner() {
|
||||
UUID firstOwner = UUID.randomUUID();
|
||||
UUID secondOwner = UUID.randomUUID();
|
||||
Instant now = Instant.parse("2026-08-09T12:00:00Z");
|
||||
PlayerState visitor = PlayerState.newPlayer(UUID.randomUUID(), "Visitor")
|
||||
.withVisitorCooldown(firstOwner, now.plus(Duration.ofHours(2)));
|
||||
|
||||
assertTrue(policy.remaining(visitor, firstOwner, now).isPresent());
|
||||
assertTrue(policy.remaining(visitor, secondOwner, now).isEmpty());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package games.dmg.spigotbase;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.time.Instant;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
|
||||
final class YamlBaseStateRepositoryTest {
|
||||
@TempDir
|
||||
Path temporaryDirectory;
|
||||
|
||||
@Test
|
||||
void roundTripsCompletePlayerState() throws Exception {
|
||||
UUID playerId = UUID.randomUUID();
|
||||
UUID worldId = UUID.randomUUID();
|
||||
UUID ownerId = UUID.randomUUID();
|
||||
BaseLocation base = new BaseLocation(worldId, "world", 12, 70, -8, 90.0F, 5.0F);
|
||||
PlayerState player = new PlayerState(
|
||||
playerId, "Alex", Optional.of(base), 4, 3, 2, 3, 4,
|
||||
500, 500, 1_000, 1_000, 2_000, 3_000,
|
||||
true, true, false, true,
|
||||
Optional.of(Instant.ofEpochMilli(1_750_000_000_000L)),
|
||||
Optional.of(Instant.ofEpochMilli(1_750_000_100_000L)),
|
||||
Map.of(ownerId, Instant.ofEpochMilli(1_750_001_000_000L))
|
||||
);
|
||||
PersistentState expected = new PersistentState(Map.of(playerId, player));
|
||||
YamlBaseStateRepository repository =
|
||||
new YamlBaseStateRepository(temporaryDirectory.resolve("state.yml"));
|
||||
|
||||
repository.save(expected);
|
||||
|
||||
assertEquals(expected, repository.load());
|
||||
}
|
||||
|
||||
@Test
|
||||
void invalidRecordsCannotGrantProgression() throws Exception {
|
||||
UUID playerId = UUID.randomUUID();
|
||||
Path stateFile = temporaryDirectory.resolve("state.yml");
|
||||
Files.writeString(stateFile, """
|
||||
players:
|
||||
%s:
|
||||
name: Alex
|
||||
base-level: 99
|
||||
visitors-enabled: true
|
||||
""".formatted(playerId));
|
||||
|
||||
PersistentState loaded = new YamlBaseStateRepository(stateFile).load();
|
||||
|
||||
assertFalse(loaded.players().containsKey(playerId));
|
||||
}
|
||||
|
||||
@Test
|
||||
void missingFileLoadsEmptyState() throws Exception {
|
||||
YamlBaseStateRepository repository =
|
||||
new YamlBaseStateRepository(temporaryDirectory.resolve("missing.yml"));
|
||||
|
||||
assertEquals(PersistentState.empty(), repository.load());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user