fix(teleport): enforce visitor warm-up
Release / release (push) Successful in 2m28s
CI / build (push) Successful in 1m3s

This commit is contained in:
dmg
2026-08-21 22:28:34 -04:00
parent 9692df9101
commit 3be3e43072
4 changed files with 20 additions and 2 deletions
@@ -21,7 +21,7 @@ As a **player with Base III**, I want to open my base to visitors so that other
- [x] `/gotobase <owner>` autocompletes bases that the requesting player is currently eligible to visit. - [x] `/gotobase <owner>` autocompletes bases that the requesting player is currently eligible to visit.
- [x] `/visit <owner>` aliases `/gotobase <owner>` with identical autocomplete, including eligible bases whose owners are offline. - [x] `/visit <owner>` aliases `/gotobase <owner>` with identical autocomplete, including eligible bases whose owners are offline.
- [x] Enabled bases remain visitable while their owners are offline. - [x] Enabled bases remain visitable while their owners are offline.
- [x] A visitor teleport uses the destination owner's current warm-up tier. - [x] A visitor teleport uses the destination owner's current warm-up tier, but always requires at least a one-second stationary warm-up; `/visit` and `/gotobase` never teleport instantly.
- [x] Looking around is permitted, while movement between block coordinates, damage, teleportation, world change, death, logout, or a conflicting teleport cancels the visitor warm-up. - [x] Looking around is permitted, while movement between block coordinates, damage, teleportation, world change, death, logout, or a conflicting teleport cancels the visitor warm-up.
- [x] Cancellation or destination failure does not consume a visitor cooldown. - [x] Cancellation or destination failure does not consume a visitor cooldown.
- [x] A safe destination is resolved at or near the owner's recorded base center. - [x] A safe destination is resolved at or near the owner's recorded base center.
@@ -95,7 +95,7 @@ final class BaseTeleportManager implements Listener {
begin( begin(
visitor, visitor,
owner.base().orElseThrow(), owner.base().orElseThrow(),
policy.warmup(owner), policy.visitorWarmup(owner),
owner.playerId(), owner.playerId(),
policy.cooldown(owner), policy.cooldown(owner),
"visit " + owner.latestName() + "'s base" "visit " + owner.latestName() + "'s base"
@@ -25,6 +25,13 @@ public final class TeleportPolicy {
}; };
} }
public Duration visitorWarmup(PlayerState owner) {
Duration warmup = warmup(owner);
return warmup.compareTo(Duration.ofSeconds(1)) < 0
? Duration.ofSeconds(1)
: warmup;
}
public Duration cooldown(PlayerState player) { public Duration cooldown(PlayerState player) {
return switch (player.cooldownLevel()) { return switch (player.cooldownLevel()) {
case 0 -> Duration.ofSeconds(settings.current().initialTeleportCooldownSeconds()); case 0 -> Duration.ofSeconds(settings.current().initialTeleportCooldownSeconds());
@@ -22,6 +22,17 @@ final class TeleportPolicyTest {
assertEquals(Duration.ZERO, policy.warmup(player.withTeleportLevels(3, 0))); assertEquals(Duration.ZERO, policy.warmup(player.withTeleportLevels(3, 0)));
} }
@Test
void visitorWarmupHasOneSecondMinimum() {
PlayerState player = baseThreePlayer();
assertEquals(Duration.ofSeconds(30), policy.visitorWarmup(player));
assertEquals(
Duration.ofSeconds(1),
policy.visitorWarmup(player.withTeleportLevels(3, 0))
);
}
@Test @Test
void cooldownLevelsUseApprovedDurations() { void cooldownLevelsUseApprovedDurations() {
PlayerState player = baseThreePlayer(); PlayerState player = baseThreePlayer();