fix(teleport): require same dimension for visits
This commit is contained in:
@@ -21,6 +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] `/visit` and `/gotobase` require the visitor to be in the destination base's recorded world; cross-dimension requests are rejected before warm-up with a clear message and without consuming cooldown.
|
||||||
- [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] 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.
|
||||||
|
|||||||
@@ -83,6 +83,13 @@ final class BaseTeleportManager implements Listener {
|
|||||||
visitor.sendMessage(ChatColor.RED + "That base is not accepting visitors.");
|
visitor.sendMessage(ChatColor.RED + "That base is not accepting visitors.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
BaseLocation destination = owner.base().orElseThrow();
|
||||||
|
if (!visitor.getWorld().getUID().equals(destination.worldId())) {
|
||||||
|
visitor.sendMessage(
|
||||||
|
ChatColor.RED + "You must be in the same dimension as that base to visit it."
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
PlayerState visitorState = stateManager.player(visitor.getUniqueId(), visitor.getName());
|
PlayerState visitorState = stateManager.player(visitor.getUniqueId(), visitor.getName());
|
||||||
Optional<Duration> remaining = visitorPolicy.remaining(
|
Optional<Duration> remaining = visitorPolicy.remaining(
|
||||||
visitorState, owner.playerId(), clock.instant()
|
visitorState, owner.playerId(), clock.instant()
|
||||||
@@ -94,7 +101,7 @@ final class BaseTeleportManager implements Listener {
|
|||||||
}
|
}
|
||||||
begin(
|
begin(
|
||||||
visitor,
|
visitor,
|
||||||
owner.base().orElseThrow(),
|
destination,
|
||||||
policy.visitorWarmup(owner),
|
policy.visitorWarmup(owner),
|
||||||
owner.playerId(),
|
owner.playerId(),
|
||||||
policy.cooldown(owner),
|
policy.cooldown(owner),
|
||||||
|
|||||||
@@ -0,0 +1,64 @@
|
|||||||
|
package games.dmg.spigotbase;
|
||||||
|
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.verifyNoInteractions;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import java.time.Clock;
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.UUID;
|
||||||
|
import org.bukkit.ChatColor;
|
||||||
|
import org.bukkit.World;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.plugin.Plugin;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
final class BaseTeleportManagerTest {
|
||||||
|
@Test
|
||||||
|
void visitFromAnotherWorldIsRejectedBeforeWarmupAndCooldownChecks() {
|
||||||
|
UUID visitorId = UUID.randomUUID();
|
||||||
|
UUID ownerId = UUID.randomUUID();
|
||||||
|
UUID baseWorldId = UUID.randomUUID();
|
||||||
|
BaseStateManager stateManager = mock(BaseStateManager.class);
|
||||||
|
TeleportPolicy teleportPolicy = mock(TeleportPolicy.class);
|
||||||
|
VisitorPolicy visitorPolicy = mock(VisitorPolicy.class);
|
||||||
|
Player visitor = mock(Player.class);
|
||||||
|
World visitorWorld = mock(World.class);
|
||||||
|
PlayerState owner = PlayerState.newPlayer(ownerId, "Owner")
|
||||||
|
.withGrassAndDirtProgress(500, 4)
|
||||||
|
.withBase(
|
||||||
|
new BaseLocation(baseWorldId, "world", 0, 64, 0, 0, 0),
|
||||||
|
Instant.EPOCH
|
||||||
|
)
|
||||||
|
.withVisitorsEnabled(true);
|
||||||
|
PlayerState visitorState = PlayerState.newPlayer(visitorId, "Visitor");
|
||||||
|
BaseTeleportManager manager = new BaseTeleportManager(
|
||||||
|
mock(Plugin.class),
|
||||||
|
stateManager,
|
||||||
|
teleportPolicy,
|
||||||
|
visitorPolicy,
|
||||||
|
mock(SafeBaseDestination.class),
|
||||||
|
Clock.systemUTC()
|
||||||
|
);
|
||||||
|
|
||||||
|
when(visitor.getUniqueId()).thenReturn(visitorId);
|
||||||
|
when(visitor.getName()).thenReturn("Visitor");
|
||||||
|
when(visitor.getWorld()).thenReturn(visitorWorld);
|
||||||
|
when(visitorWorld.getUID()).thenReturn(UUID.randomUUID());
|
||||||
|
when(stateManager.player(visitorId, "Visitor")).thenReturn(visitorState);
|
||||||
|
when(visitorPolicy.remaining(eq(visitorState), eq(ownerId), any()))
|
||||||
|
.thenReturn(Optional.of(Duration.ofSeconds(1)));
|
||||||
|
|
||||||
|
manager.startVisit(visitor, owner);
|
||||||
|
|
||||||
|
verify(visitor).sendMessage(
|
||||||
|
ChatColor.RED + "You must be in the same dimension as that base to visit it."
|
||||||
|
);
|
||||||
|
verifyNoInteractions(stateManager, teleportPolicy, visitorPolicy);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user