Merge pull request #3130 from Multiverse/ben/mv5/improve-destination

Refactor to remove need for ParsedDestination and add destination tests
This commit is contained in:
Ben Woo 2024-11-21 11:52:08 +08:00 committed by GitHub
commit e2050c825e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
28 changed files with 448 additions and 354 deletions

View File

@ -23,7 +23,7 @@ import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Service;
import org.mvplugins.multiverse.core.anchor.AnchorManager;
import org.mvplugins.multiverse.core.api.Destination;
import org.mvplugins.multiverse.core.destination.Destination;
import org.mvplugins.multiverse.core.api.MVCore;
import org.mvplugins.multiverse.core.commands.CoreCommand;
import org.mvplugins.multiverse.core.commandtools.MVCommandManager;

View File

@ -1,45 +0,0 @@
package org.mvplugins.multiverse.core.api;
import org.bukkit.Location;
import org.bukkit.entity.Entity;
import org.bukkit.util.Vector;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public interface DestinationInstance {
/**
* Gets the exact location to teleport an entity to.
*
* @param teleportee The entity to teleport.
* @return The location to teleport to.
*/
@Nullable Location getLocation(@NotNull Entity teleportee);
/**
* Gets the velocity to apply to an entity after teleporting.
*
* @param teleportee The entity to teleport.
* @return A vector representing the speed/direction the player should travel when arriving at the destination.
*/
@Nullable Vector getVelocity(@NotNull Entity teleportee);
/**
* Gets the permission suffix to check for when teleporting to this destination.
* This is used for finer per world/player permissions, such as "multiverse.teleport.self.worldname".
*
* <p>For example, if the destination is "w:world", the permission suffix is "world".</p>
*
* @return The permission suffix.
*/
@Nullable String getFinerPermissionSuffix();
/**
* Serialises the destination instance to a savable string.
*
* <p>This is used when plugins save destinations to configuration,
* and when the destination is displayed to the user.</p>
*
* @return The serialised destination instance.
*/
@NotNull String serialise();
}

View File

@ -13,9 +13,8 @@ import org.jvnet.hk2.annotations.Service;
import org.mvplugins.multiverse.core.commandtools.MVCommandIssuer;
import org.mvplugins.multiverse.core.commandtools.MVCommandManager;
import org.mvplugins.multiverse.core.commandtools.MultiverseCommand;
import org.mvplugins.multiverse.core.destination.DestinationInstance;
import org.mvplugins.multiverse.core.destination.DestinationsProvider;
import org.mvplugins.multiverse.core.destination.ParsedDestination;
import org.mvplugins.multiverse.core.utils.MVCorei18n;
@Service
@ -45,7 +44,7 @@ class CheckCommand extends CoreCommand {
@Syntax("<destination>")
@Description("{@@mv-core.check.destination.description}")
ParsedDestination<?> destination) {
DestinationInstance<?, ?> destination) {
issuer.sendInfo(MVCorei18n.CHECK_CHECKING,
"{player}", player.getName(),
"{destination}", destination.toString());

View File

@ -17,8 +17,7 @@ import org.jvnet.hk2.annotations.Service;
import org.mvplugins.multiverse.core.commandtools.MVCommandIssuer;
import org.mvplugins.multiverse.core.commandtools.MVCommandManager;
import org.mvplugins.multiverse.core.commandtools.MultiverseCommand;
import org.mvplugins.multiverse.core.destination.ParsedDestination;
import org.mvplugins.multiverse.core.destination.DestinationInstance;
import org.mvplugins.multiverse.core.permissions.CorePermissionsChecker;
import org.mvplugins.multiverse.core.teleportation.AsyncSafetyTeleporter;
import org.mvplugins.multiverse.core.utils.MVCorei18n;
@ -55,7 +54,7 @@ class TeleportCommand extends CoreCommand {
@Syntax("<destination>")
@Description("{@@mv-core.teleport.destination.description}")
ParsedDestination<?> destination) {
DestinationInstance<?, ?> destination) {
// TODO: Add warning if teleporting too many players at once.
String playerName = players.length == 1

View File

@ -28,8 +28,8 @@ import org.jvnet.hk2.annotations.Service;
import org.mvplugins.multiverse.core.config.MVCoreConfig;
import org.mvplugins.multiverse.core.configuration.handle.PropertyModifyAction;
import org.mvplugins.multiverse.core.destination.DestinationInstance;
import org.mvplugins.multiverse.core.destination.DestinationsProvider;
import org.mvplugins.multiverse.core.destination.ParsedDestination;
import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld;
import org.mvplugins.multiverse.core.world.MultiverseWorld;
import org.mvplugins.multiverse.core.world.WorldManager;
@ -68,7 +68,7 @@ class MVCommandCompletions extends PaperCommandCompletions {
registerAsyncCompletion("mvworldpropsvalue", this::suggestMVWorldPropsValue);
registerStaticCompletion("propsmodifyaction", suggestEnums(PropertyModifyAction.class));
setDefaultCompletion("destinations", ParsedDestination.class);
setDefaultCompletion("destinations", DestinationInstance.class);
setDefaultCompletion("difficulties", Difficulty.class);
setDefaultCompletion("environments", World.Environment.class);
setDefaultCompletion("flags", String[].class);

View File

@ -16,8 +16,8 @@ import org.jvnet.hk2.annotations.Service;
import org.mvplugins.multiverse.core.commandtools.context.GameRuleValue;
import org.mvplugins.multiverse.core.config.MVCoreConfig;
import org.mvplugins.multiverse.core.destination.DestinationInstance;
import org.mvplugins.multiverse.core.destination.DestinationsProvider;
import org.mvplugins.multiverse.core.destination.ParsedDestination;
import org.mvplugins.multiverse.core.display.filters.ContentFilter;
import org.mvplugins.multiverse.core.display.filters.DefaultContentFilter;
import org.mvplugins.multiverse.core.display.filters.RegexContentFilter;
@ -49,7 +49,7 @@ class MVCommandContexts extends PaperCommandContexts {
registerIssuerOnlyContext(BukkitCommandIssuer.class, BukkitCommandExecutionContext::getIssuer);
registerIssuerOnlyContext(MVCommandIssuer.class, this::parseMVCommandIssuer);
registerOptionalContext(ContentFilter.class, this::parseContentFilter);
registerContext(ParsedDestination.class, this::parseDestination);
registerContext(DestinationInstance.class, this::parseDestination);
registerContext(GameRule.class, this::parseGameRule);
registerContext(GameRuleValue.class, this::parseGameRuleValue);
registerIssuerAwareContext(LoadedMultiverseWorld.class, this::parseLoadedMultiverseWorld);
@ -74,18 +74,14 @@ class MVCommandContexts extends PaperCommandContexts {
return RegexContentFilter.fromString(filterString);
}
private ParsedDestination<?> parseDestination(BukkitCommandExecutionContext context) {
private DestinationInstance<?, ?> parseDestination(BukkitCommandExecutionContext context) {
String destination = context.popFirstArg();
if (Strings.isNullOrEmpty(destination)) {
throw new InvalidCommandArgument("No destination specified.");
}
ParsedDestination<?> parsedDestination = destinationsProvider.parseDestination(destination);
if (parsedDestination == null) {
throw new InvalidCommandArgument("The destination " + destination + " is not valid.");
}
return parsedDestination;
return destinationsProvider.parseDestination(destination)
.getOrElseThrow(() -> new InvalidCommandArgument("The destination " + destination + " is not valid."));
}
private GameRule<?> parseGameRule(BukkitCommandExecutionContext context) {

View File

@ -1,4 +1,4 @@
package org.mvplugins.multiverse.core.api;
package org.mvplugins.multiverse.core.destination;
import java.util.Collection;
@ -8,7 +8,7 @@ import org.jetbrains.annotations.Nullable;
import org.jvnet.hk2.annotations.Contract;
@Contract
public interface Destination<T extends DestinationInstance> {
public interface Destination<D extends Destination<D, T>, T extends DestinationInstance<T, D>> {
/**
* Returns the identifier or prefix that is required for this destination.
*
@ -36,13 +36,4 @@ public interface Destination<T extends DestinationInstance> {
* @return A list of possible destinations.
*/
@NotNull Collection<String> suggestDestinations(@NotNull BukkitCommandIssuer issuer, @Nullable String destinationParams);
/**
* Should the Multiverse SafeTeleporter be used?
*
* <p>If not, MV will blindly take people to the location specified.</p>
*
* @return True if the SafeTeleporter will be used, false if not.
*/
boolean checkTeleportSafety();
}

View File

@ -0,0 +1,90 @@
package org.mvplugins.multiverse.core.destination;
import io.vavr.control.Option;
import org.bukkit.Location;
import org.bukkit.entity.Entity;
import org.bukkit.util.Vector;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public abstract class DestinationInstance<I extends DestinationInstance<I, T>, T extends Destination<T, I>> {
protected final T destination;
protected DestinationInstance(@NotNull T destination) {
this.destination = destination;
}
/**
* Gets the destination that created this instance.
*
* @return The destination.
*/
public @NotNull T getDestination() {
return this.destination;
}
/**
* Gets the {@link Destination#getIdentifier()} for this instance.
*
* @return The identifier.
*/
public @NotNull String getIdentifier() {
return this.destination.getIdentifier();
}
/**
* Gets the exact location to teleport an entity to.
*
* @param teleportee The entity to teleport.
* @return The location to teleport to.
*/
public abstract @NotNull Option<Location> getLocation(@NotNull Entity teleportee);
/**
* Gets the velocity to apply to an entity after teleporting.
*
* @param teleportee The entity to teleport.
* @return A vector representing the speed/direction the player should travel when arriving at the destination.
*/
public abstract @NotNull Option<Vector> getVelocity(@NotNull Entity teleportee);
/**
* Should the Multiverse SafeTeleporter be used?
*
* <p>If not, MV will blindly take people to the location specified.</p>
*
* @return True if the SafeTeleporter will be used, false if not.
*/
public abstract boolean checkTeleportSafety();
/**
* Gets the permission suffix to check for when teleporting to this destination.
* This is used for finer per world/player permissions, such as "multiverse.teleport.self.worldname".
*
* <p>For example, if the destination is "w:world", the permission suffix is "world".</p>
*
* @return The permission suffix.
*/
public abstract @NotNull Option<String> getFinerPermissionSuffix();
/**
* Serialises the destination instance to a savable string.
*
* <p>This is used when plugins save destinations to configuration,
* and when the destination is displayed to the user.</p>
*
* @return The serialised destination instance.
*/
@NotNull
protected abstract String serialise();
/**
* String representation of the destination instance that can be deserialised back into the destination instance.
* @return The string representation of the destination instance.
*/
@Override
public String toString() {
return this.destination.getIdentifier() + ":" + this.serialise();
}
}

View File

@ -6,6 +6,7 @@ import java.util.Map;
import java.util.stream.Collectors;
import co.aikar.commands.BukkitCommandIssuer;
import io.vavr.control.Option;
import jakarta.inject.Inject;
import org.bukkit.permissions.Permission;
import org.bukkit.plugin.PluginManager;
@ -13,9 +14,6 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jvnet.hk2.annotations.Service;
import org.mvplugins.multiverse.core.api.Destination;
import org.mvplugins.multiverse.core.api.DestinationInstance;
/**
* Provides destinations for teleportation.
*/
@ -25,7 +23,7 @@ public class DestinationsProvider {
private static final String PERMISSION_PREFIX = "multiverse.teleport.";
private final PluginManager pluginManager;
private final Map<String, Destination<?>> destinationMap;
private final Map<String, Destination<?, ?>> destinationMap;
@Inject
DestinationsProvider(@NotNull PluginManager pluginManager) {
@ -38,12 +36,12 @@ public class DestinationsProvider {
*
* @param destination The destination.
*/
public void registerDestination(@NotNull Destination<?> destination) {
public void registerDestination(@NotNull Destination<?, ?> destination) {
this.destinationMap.put(destination.getIdentifier(), destination);
this.registerDestinationPerms(destination);
}
private void registerDestinationPerms(@NotNull Destination<?> destination) {
private void registerDestinationPerms(@NotNull Destination<?, ?> destination) {
pluginManager.addPermission(new Permission(PERMISSION_PREFIX + "self." + destination.getIdentifier()));
pluginManager.addPermission(new Permission(PERMISSION_PREFIX + "other." + destination.getIdentifier()));
}
@ -74,13 +72,13 @@ public class DestinationsProvider {
* @param destinationString The destination string.
* @return The destination object, or null if invalid format.
*/
@Nullable
public ParsedDestination<?> parseDestination(@NotNull String destinationString) {
@NotNull
public Option<DestinationInstance<?, ?>> parseDestination(@NotNull String destinationString) {
String[] items = destinationString.split(SEPARATOR, 2);
String idString = items[0];
String destinationParams;
Destination<?> destination;
Destination<?, ?> destination;
if (items.length < 2) {
// Assume world destination
@ -92,15 +90,10 @@ public class DestinationsProvider {
}
if (destination == null) {
return null;
return Option.none();
}
DestinationInstance destinationInstance = destination.getDestinationInstance(destinationParams);
if (destinationInstance == null) {
return null;
}
return new ParsedDestination<>(destination, destinationInstance);
return Option.of(destination.getDestinationInstance(destinationParams));
}
/**
@ -109,7 +102,7 @@ public class DestinationsProvider {
* @param identifier The identifier.
* @return The destination, or null if not found.
*/
public @Nullable Destination<?> getDestinationById(@Nullable String identifier) {
public @Nullable Destination<?, ?> getDestinationById(@Nullable String identifier) {
return this.destinationMap.get(identifier);
}
@ -118,7 +111,7 @@ public class DestinationsProvider {
*
* @return A collection of destinations.
*/
public @NotNull Collection<Destination<?>> getDestinations() {
public @NotNull Collection<Destination<?, ?>> getDestinations() {
return this.destinationMap.values();
}
}

View File

@ -1,86 +0,0 @@
package org.mvplugins.multiverse.core.destination;
import org.bukkit.Location;
import org.bukkit.entity.Entity;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.mvplugins.multiverse.core.api.Destination;
import org.mvplugins.multiverse.core.api.DestinationInstance;
/**
* A parsed destination.
*
* @param <S> The destination instance type.
*/
public class ParsedDestination<S extends DestinationInstance> {
private final Destination<S> destination;
private final DestinationInstance destinationInstance;
/**
* Creates a new parsed destination.
*
* @param destination The destination.
* @param destinationInstance The destination instance.
*/
public ParsedDestination(Destination<S> destination, DestinationInstance destinationInstance) {
this.destination = destination;
this.destinationInstance = destinationInstance;
}
/**
* Shortcut for {@link Destination#getIdentifier()}.
*
* @return The destination identifier.
*/
public @NotNull String getIdentifier() {
return destination.getIdentifier();
}
/**
* Shortcut for {@link DestinationInstance#getLocation(Entity)}.
*
* @param teleportee The entity to teleport.
* @return The location to teleport to.
*/
public @Nullable Location getLocation(@NotNull Entity teleportee) {
return destinationInstance.getLocation(teleportee);
}
/**
* Shortcut for {@link DestinationInstance#getFinerPermissionSuffix()}.
*
* @return The finer permission suffix.
*/
public @Nullable String getFinerPermissionSuffix() {
return destinationInstance.getFinerPermissionSuffix();
}
/**
* Gets the destination.
*
* @return The destination.
*/
public Destination<S> getDestination() {
return destination;
}
/**
* Gets the destination instance.
*
* @return The destination instance.
*/
public DestinationInstance getDestinationInstance() {
return destinationInstance;
}
/**
* Converts to saveable string representation of this destination.
*
* @return Serialized string.
*/
@Override
public String toString() {
return getIdentifier() + ":" + destinationInstance.serialise();
}
}

View File

@ -10,13 +10,13 @@ import org.jetbrains.annotations.Nullable;
import org.jvnet.hk2.annotations.Service;
import org.mvplugins.multiverse.core.anchor.AnchorManager;
import org.mvplugins.multiverse.core.api.Destination;
import org.mvplugins.multiverse.core.destination.Destination;
/**
* {@link Destination} implementation for anchors.
*/
@Service
public class AnchorDestination implements Destination<AnchorDestinationInstance> {
public class AnchorDestination implements Destination<AnchorDestination, AnchorDestinationInstance> {
private final AnchorManager anchorManager;
@ -38,11 +38,14 @@ public class AnchorDestination implements Destination<AnchorDestinationInstance>
*/
@Override
public @Nullable AnchorDestinationInstance getDestinationInstance(@Nullable String destinationParams) {
if (destinationParams == null) {
return null;
}
Location anchorLocation = this.anchorManager.getAnchorLocation(destinationParams);
if (anchorLocation == null) {
return null;
}
return new AnchorDestinationInstance(destinationParams, anchorLocation);
return new AnchorDestinationInstance(this, destinationParams, anchorLocation);
}
/**
@ -52,12 +55,4 @@ public class AnchorDestination implements Destination<AnchorDestinationInstance>
public @NotNull Collection<String> suggestDestinations(@NotNull BukkitCommandIssuer issuer, @Nullable String destinationParams) {
return this.anchorManager.getAnchors(issuer.getPlayer());
}
/**
* {@inheritDoc}
*/
@Override
public boolean checkTeleportSafety() {
return true;
}
}

View File

@ -1,17 +1,18 @@
package org.mvplugins.multiverse.core.destination.core;
import io.vavr.control.Option;
import org.bukkit.Location;
import org.bukkit.entity.Entity;
import org.bukkit.util.Vector;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.mvplugins.multiverse.core.api.DestinationInstance;
import org.mvplugins.multiverse.core.destination.DestinationInstance;
/**
* Destination instance implementation for the {@link AnchorDestination}.
*/
public class AnchorDestinationInstance implements DestinationInstance {
public class AnchorDestinationInstance extends DestinationInstance<AnchorDestinationInstance, AnchorDestination> {
private final String anchorName;
private final Location anchorLocation;
@ -21,7 +22,12 @@ public class AnchorDestinationInstance implements DestinationInstance {
* @param anchorName The name of the anchor.
* @param anchorLocation The location of the anchor.
*/
AnchorDestinationInstance(@NotNull String anchorName, @NotNull Location anchorLocation) {
AnchorDestinationInstance(
@NotNull AnchorDestination destination,
@NotNull String anchorName,
@NotNull Location anchorLocation
) {
super(destination);
this.anchorName = anchorName;
this.anchorLocation = anchorLocation;
}
@ -30,24 +36,32 @@ public class AnchorDestinationInstance implements DestinationInstance {
* {@inheritDoc}
*/
@Override
public @Nullable Location getLocation(@NotNull Entity teleportee) {
return anchorLocation;
public @NotNull Option<Location> getLocation(@NotNull Entity teleportee) {
return Option.of(anchorLocation);
}
/**
* {@inheritDoc}
*/
@Override
public @Nullable Vector getVelocity(@NotNull Entity teleportee) {
return null;
public @NotNull Option<Vector> getVelocity(@NotNull Entity teleportee) {
return Option.none();
}
/**
* {@inheritDoc}
*/
@Override
public @Nullable String getFinerPermissionSuffix() {
return anchorName;
public boolean checkTeleportSafety() {
return true;
}
/**
* {@inheritDoc}
*/
@Override
public @NotNull Option<String> getFinerPermissionSuffix() {
return Option.of(anchorName);
}
/**

View File

@ -11,14 +11,14 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jvnet.hk2.annotations.Service;
import org.mvplugins.multiverse.core.api.Destination;
import org.mvplugins.multiverse.core.destination.Destination;
import org.mvplugins.multiverse.core.utils.PlayerFinder;
/**
* {@link Destination} implementation for beds.
*/
@Service
public class BedDestination implements Destination<BedDestinationInstance> {
public class BedDestination implements Destination<BedDestination, BedDestinationInstance> {
static final String OWN_BED_STRING = "playerbed";
BedDestination() {
@ -41,7 +41,7 @@ public class BedDestination implements Destination<BedDestinationInstance> {
if (player == null && !destinationParams.equals(OWN_BED_STRING)) {
return null;
}
return new BedDestinationInstance(player);
return new BedDestinationInstance(this, player);
}
/**
@ -53,12 +53,4 @@ public class BedDestination implements Destination<BedDestinationInstance> {
collect.add(OWN_BED_STRING);
return collect;
}
/**
* {@inheritDoc}
*/
@Override
public boolean checkTeleportSafety() {
return false;
}
}

View File

@ -1,5 +1,6 @@
package org.mvplugins.multiverse.core.destination.core;
import io.vavr.control.Option;
import org.bukkit.Location;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
@ -7,12 +8,12 @@ import org.bukkit.util.Vector;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.mvplugins.multiverse.core.api.DestinationInstance;
import org.mvplugins.multiverse.core.destination.DestinationInstance;
/**
* Destination instance implementation for the {@link BedDestination}.
*/
public class BedDestinationInstance implements DestinationInstance {
public class BedDestinationInstance extends DestinationInstance<BedDestinationInstance, BedDestination> {
private final Player player;
/**
@ -20,7 +21,8 @@ public class BedDestinationInstance implements DestinationInstance {
*
* @param player The player whose bed to use.
*/
BedDestinationInstance(Player player) {
BedDestinationInstance(@NotNull BedDestination destination, @Nullable Player player) {
super(destination);
this.player = player;
}
@ -28,30 +30,38 @@ public class BedDestinationInstance implements DestinationInstance {
* {@inheritDoc}
*/
@Override
public @Nullable Location getLocation(@NotNull Entity teleportee) {
public @NotNull Option<Location> getLocation(@NotNull Entity teleportee) {
if (player != null) {
return player.getBedSpawnLocation();
return Option.of(player.getBedSpawnLocation());
}
if (teleportee instanceof Player) {
return ((Player) teleportee).getBedSpawnLocation();
return Option.of(((Player) teleportee).getBedSpawnLocation());
}
return null;
return Option.none();
}
/**
* {@inheritDoc}
*/
@Override
public @Nullable Vector getVelocity(@NotNull Entity teleportee) {
return null;
public @NotNull Option<Vector> getVelocity(@NotNull Entity teleportee) {
return Option.none();
}
/**
* {@inheritDoc}
*/
@Override
public @Nullable String getFinerPermissionSuffix() {
return player != null ? player.getName() : BedDestination.OWN_BED_STRING;
public boolean checkTeleportSafety() {
return false;
}
/**
* {@inheritDoc}
*/
@Override
public @NotNull Option<String> getFinerPermissionSuffix() {
return Option.of(player != null ? player.getName() : BedDestination.OWN_BED_STRING);
}
/**

View File

@ -11,7 +11,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jvnet.hk2.annotations.Service;
import org.mvplugins.multiverse.core.api.Destination;
import org.mvplugins.multiverse.core.destination.Destination;
import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld;
import org.mvplugins.multiverse.core.world.WorldManager;
@ -19,7 +19,7 @@ import org.mvplugins.multiverse.core.world.WorldManager;
* {@link Destination} implementation for cannons.
*/
@Service
public class CannonDestination implements Destination<CannonDestinationInstance> {
public class CannonDestination implements Destination<CannonDestination, CannonDestinationInstance> {
private final WorldManager worldManager;
@ -78,7 +78,7 @@ public class CannonDestination implements Destination<CannonDestinationInstance>
return null;
}
return new CannonDestinationInstance(location, dSpeed);
return new CannonDestinationInstance(this, location, dSpeed);
}
/**
@ -88,12 +88,4 @@ public class CannonDestination implements Destination<CannonDestinationInstance>
public @NotNull Collection<String> suggestDestinations(@NotNull BukkitCommandIssuer issuer, @Nullable String destinationParams) {
return Collections.singleton("");
}
/**
* {@inheritDoc}
*/
@Override
public boolean checkTeleportSafety() {
return false;
}
}

View File

@ -1,17 +1,19 @@
package org.mvplugins.multiverse.core.destination.core;
import io.vavr.control.Option;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.entity.Entity;
import org.bukkit.util.Vector;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.mvplugins.multiverse.core.api.DestinationInstance;
import org.mvplugins.multiverse.core.destination.DestinationInstance;
/**
* Destination instance implementation for the {@link CannonDestination}.
*/
public class CannonDestinationInstance implements DestinationInstance {
public class CannonDestinationInstance extends DestinationInstance<CannonDestinationInstance, CannonDestination> {
private final Location location;
private final double speed;
@ -21,7 +23,8 @@ public class CannonDestinationInstance implements DestinationInstance {
* @param location The location to teleport to.
* @param speed The speed to fire the player at.
*/
CannonDestinationInstance(@NotNull Location location, double speed) {
CannonDestinationInstance(@NotNull CannonDestination destination, @NotNull Location location, double speed) {
super(destination);
this.location = location;
this.speed = speed;
}
@ -30,15 +33,15 @@ public class CannonDestinationInstance implements DestinationInstance {
* {@inheritDoc}
*/
@Override
public @Nullable Location getLocation(@NotNull Entity teleportee) {
return location;
public @NotNull Option<Location> getLocation(@NotNull Entity teleportee) {
return Option.of(location);
}
/**
* {@inheritDoc}
*/
@Override
public @Nullable Vector getVelocity(@NotNull Entity teleportee) {
public @NotNull Option<Vector> getVelocity(@NotNull Entity teleportee) {
double pitchRadians = Math.toRadians(location.getPitch());
double yawRadians = Math.toRadians(location.getYaw());
double x = Math.sin(yawRadians) * speed * -1;
@ -47,15 +50,23 @@ public class CannonDestinationInstance implements DestinationInstance {
// Account for the angle they were pointed, and take away velocity
x = Math.cos(pitchRadians) * x;
z = Math.cos(pitchRadians) * z;
return new Vector(x, y, z);
return Option.of(new Vector(x, y, z));
}
/**
* {@inheritDoc}
*/
@Override
public @Nullable String getFinerPermissionSuffix() {
return location.getWorld().getName();
public boolean checkTeleportSafety() {
return false;
}
/**
* {@inheritDoc}
*/
@Override
public @NotNull Option<String> getFinerPermissionSuffix() {
return Option.of(location.getWorld()).map(World::getName);
}
/**

View File

@ -11,7 +11,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jvnet.hk2.annotations.Service;
import org.mvplugins.multiverse.core.api.Destination;
import org.mvplugins.multiverse.core.destination.Destination;
import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld;
import org.mvplugins.multiverse.core.world.WorldManager;
@ -19,7 +19,7 @@ import org.mvplugins.multiverse.core.world.WorldManager;
* {@link Destination} implementation for exact locations.
*/
@Service
public class ExactDestination implements Destination<ExactDestinationInstance> {
public class ExactDestination implements Destination<ExactDestination, ExactDestinationInstance> {
private final WorldManager worldManager;
@ -36,6 +36,16 @@ public class ExactDestination implements Destination<ExactDestinationInstance> {
return "e";
}
/**
* Make a new {@link ExactDestinationInstance} from a {@link Location}.
*
* @param location The target location
* @return A new {@link ExactDestinationInstance}
*/
public @NotNull ExactDestinationInstance fromLocation(@NotNull Location location) {
return new ExactDestinationInstance(this, location);
}
/**
* {@inheritDoc}
*/
@ -53,7 +63,7 @@ public class ExactDestination implements Destination<ExactDestinationInstance> {
return null;
}
World world = this.worldManager.getLoadedWorld(worldName).map(LoadedMultiverseWorld::getBukkitWorld).getOrNull().getOrNull();
World world = this.worldManager.getLoadedWorld(worldName).flatMap(LoadedMultiverseWorld::getBukkitWorld).getOrNull();
if (world == null) {
return null;
}
@ -81,7 +91,7 @@ public class ExactDestination implements Destination<ExactDestinationInstance> {
}
}
return new ExactDestinationInstance(location);
return new ExactDestinationInstance(this, location);
}
/**
@ -91,12 +101,4 @@ public class ExactDestination implements Destination<ExactDestinationInstance> {
public @NotNull Collection<String> suggestDestinations(@NotNull BukkitCommandIssuer issuer, @Nullable String destinationParams) {
return Collections.singleton("");
}
/**
* {@inheritDoc}
*/
@Override
public boolean checkTeleportSafety() {
return false;
}
}

View File

@ -1,17 +1,19 @@
package org.mvplugins.multiverse.core.destination.core;
import io.vavr.control.Option;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.entity.Entity;
import org.bukkit.util.Vector;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.mvplugins.multiverse.core.api.DestinationInstance;
import org.mvplugins.multiverse.core.destination.DestinationInstance;
/**
* Destination instance implementation for the {@link ExactDestination}.
*/
public class ExactDestinationInstance implements DestinationInstance {
public class ExactDestinationInstance extends DestinationInstance<ExactDestinationInstance, ExactDestination> {
private final Location location;
/**
@ -19,7 +21,8 @@ public class ExactDestinationInstance implements DestinationInstance {
*
* @param location The location to teleport to.
*/
ExactDestinationInstance(Location location) {
ExactDestinationInstance(@NotNull ExactDestination destination, @NotNull Location location) {
super(destination);
this.location = location;
}
@ -27,24 +30,33 @@ public class ExactDestinationInstance implements DestinationInstance {
* {@inheritDoc}
*/
@Override
public @Nullable Location getLocation(@NotNull Entity teleportee) {
return location;
public @NotNull Option<Location> getLocation(@NotNull Entity teleportee) {
// todo: maybe check if the world is null?
return Option.of(location);
}
/**
* {@inheritDoc}
*/
@Override
public @Nullable Vector getVelocity(@NotNull Entity teleportee) {
return null;
public @NotNull Option<Vector> getVelocity(@NotNull Entity teleportee) {
return Option.none();
}
/**
* {@inheritDoc}
*/
@Override
public @Nullable String getFinerPermissionSuffix() {
return location.getWorld().getName();
public boolean checkTeleportSafety() {
return false;
}
/**
* {@inheritDoc}
*/
@Override
public @NotNull Option<String> getFinerPermissionSuffix() {
return Option.of(location.getWorld()).map(World::getName);
}
/**

View File

@ -10,14 +10,14 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jvnet.hk2.annotations.Service;
import org.mvplugins.multiverse.core.api.Destination;
import org.mvplugins.multiverse.core.destination.Destination;
import org.mvplugins.multiverse.core.utils.PlayerFinder;
/**
* {@link Destination} implementation for players.s
*/
@Service
public class PlayerDestination implements Destination<PlayerDestinationInstance> {
public class PlayerDestination implements Destination<PlayerDestination, PlayerDestinationInstance> {
/**
* Creates a new instance of the PlayerDestination.
*/
@ -41,7 +41,7 @@ public class PlayerDestination implements Destination<PlayerDestinationInstance>
if (player == null) {
return null;
}
return new PlayerDestinationInstance(player);
return new PlayerDestinationInstance(this, player);
}
/**
@ -49,14 +49,6 @@ public class PlayerDestination implements Destination<PlayerDestinationInstance>
*/
@Override
public @NotNull Collection<String> suggestDestinations(@NotNull BukkitCommandIssuer issuer, @Nullable String destinationParams) {
return Bukkit.getOnlinePlayers().stream().map(Player::getName).collect(Collectors.toList());
}
/**
* {@inheritDoc}
*/
@Override
public boolean checkTeleportSafety() {
return true;
return Bukkit.getOnlinePlayers().stream().map(Player::getName).toList();
}
}

View File

@ -1,5 +1,6 @@
package org.mvplugins.multiverse.core.destination.core;
import io.vavr.control.Option;
import org.bukkit.Location;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
@ -7,12 +8,12 @@ import org.bukkit.util.Vector;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.mvplugins.multiverse.core.api.DestinationInstance;
import org.mvplugins.multiverse.core.destination.DestinationInstance;
/**
* Destination instance implementation for the {@link PlayerDestination}.
*/
public class PlayerDestinationInstance implements DestinationInstance {
public class PlayerDestinationInstance extends DestinationInstance<PlayerDestinationInstance, PlayerDestination> {
private final Player player;
/**
@ -20,7 +21,8 @@ public class PlayerDestinationInstance implements DestinationInstance {
*
* @param player The player whose location to go to.
*/
PlayerDestinationInstance(Player player) {
PlayerDestinationInstance(@NotNull PlayerDestination destination, @NotNull Player player) {
super(destination);
this.player = player;
}
@ -28,24 +30,32 @@ public class PlayerDestinationInstance implements DestinationInstance {
* {@inheritDoc}
*/
@Override
public @Nullable Location getLocation(@NotNull Entity teleportee) {
return player.getLocation();
public @NotNull Option<Location> getLocation(@NotNull Entity teleportee) {
return Option.of(player.getLocation());
}
/**
* {@inheritDoc}
*/
@Override
public @Nullable Vector getVelocity(@NotNull Entity teleportee) {
return null;
public @NotNull Option<Vector> getVelocity(@NotNull Entity teleportee) {
return Option.none();
}
/**
* {@inheritDoc}
*/
@Override
public @Nullable String getFinerPermissionSuffix() {
return player.getName();
public boolean checkTeleportSafety() {
return true;
}
/**
* {@inheritDoc}
*/
@Override
public @NotNull Option<String> getFinerPermissionSuffix() {
return Option.of(player.getName());
}
/**

View File

@ -9,7 +9,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jvnet.hk2.annotations.Service;
import org.mvplugins.multiverse.core.api.Destination;
import org.mvplugins.multiverse.core.destination.Destination;
import org.mvplugins.multiverse.core.api.LocationManipulation;
import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld;
import org.mvplugins.multiverse.core.world.WorldManager;
@ -18,7 +18,7 @@ import org.mvplugins.multiverse.core.world.WorldManager;
* {@link Destination} implementation for exact locations.
*/
@Service
public class WorldDestination implements Destination<WorldDestinationInstance> {
public class WorldDestination implements Destination<WorldDestination, WorldDestinationInstance> {
private final WorldManager worldManager;
private final LocationManipulation locationManipulation;
@ -56,7 +56,7 @@ public class WorldDestination implements Destination<WorldDestinationInstance> {
String direction = (items.length == 2) ? items[1] : null;
float yaw = direction != null ? this.locationManipulation.getYaw(direction) : -1;
return new WorldDestinationInstance(world, direction, yaw);
return new WorldDestinationInstance(this, world, direction, yaw);
}
/**
@ -67,12 +67,4 @@ public class WorldDestination implements Destination<WorldDestinationInstance> {
// Autocomplete of worlds is done by MVCommandCompletion without prefix
return Collections.emptyList();
}
/**
* {@inheritDoc}
*/
@Override
public boolean checkTeleportSafety() {
return true;
}
}

View File

@ -1,18 +1,19 @@
package org.mvplugins.multiverse.core.destination.core;
import io.vavr.control.Option;
import org.bukkit.Location;
import org.bukkit.entity.Entity;
import org.bukkit.util.Vector;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.mvplugins.multiverse.core.api.DestinationInstance;
import org.mvplugins.multiverse.core.destination.DestinationInstance;
import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld;
/**
* Destination instance implementation for the {@link WorldDestination}.
*/
public class WorldDestinationInstance implements DestinationInstance {
public class WorldDestinationInstance extends DestinationInstance<WorldDestinationInstance, WorldDestination> {
private final LoadedMultiverseWorld world;
private final String direction;
private final float yaw;
@ -20,11 +21,18 @@ public class WorldDestinationInstance implements DestinationInstance {
/**
* Constructor.
*
* @param destination The destination.
* @param world The world to teleport to.
* @param direction The direction to face.
* @param yaw The yaw to face.
*/
WorldDestinationInstance(@NotNull LoadedMultiverseWorld world, @Nullable String direction, float yaw) {
WorldDestinationInstance(
@NotNull WorldDestination destination,
@NotNull LoadedMultiverseWorld world,
@Nullable String direction,
float yaw
) {
super(destination);
this.world = world;
this.direction = direction;
this.yaw = yaw;
@ -34,29 +42,37 @@ public class WorldDestinationInstance implements DestinationInstance {
* {@inheritDoc}
*/
@Override
public @Nullable Location getLocation(@NotNull Entity teleportee) {
public @NotNull Option<Location> getLocation(@NotNull Entity teleportee) {
Location worldLoc = world.getSpawnLocation();
if (this.yaw >= 0) {
// Only modify the yaw if its set.
worldLoc.setYaw(this.yaw);
}
return worldLoc;
return Option.of(worldLoc);
}
/**
* {@inheritDoc}
*/
@Override
public @Nullable Vector getVelocity(@NotNull Entity teleportee) {
return null;
public @NotNull Option<Vector> getVelocity(@NotNull Entity teleportee) {
return Option.none();
}
/**
* {@inheritDoc}
*/
@Override
public @Nullable String getFinerPermissionSuffix() {
return world.getName();
public boolean checkTeleportSafety() {
return true;
}
/**
* {@inheritDoc}
*/
@Override
public @NotNull Option<String> getFinerPermissionSuffix() {
return Option.of(world.getName());
}
/**

View File

@ -14,7 +14,7 @@ import org.bukkit.event.Cancellable;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
import org.mvplugins.multiverse.core.destination.ParsedDestination;
import org.mvplugins.multiverse.core.destination.DestinationInstance;
import org.mvplugins.multiverse.core.teleportation.AsyncSafetyTeleporter;
/**
@ -23,11 +23,11 @@ import org.mvplugins.multiverse.core.teleportation.AsyncSafetyTeleporter;
public class MVTeleportEvent extends Event implements Cancellable {
private Player teleportee;
private CommandSender teleporter;
private ParsedDestination<?> dest;
private DestinationInstance<?, ?> dest;
private boolean useSafeTeleport;
private boolean isCancelled;
public MVTeleportEvent(ParsedDestination<?> dest, Player teleportee, CommandSender teleporter, boolean safeTeleport) {
public MVTeleportEvent(DestinationInstance<?, ?> dest, Player teleportee, CommandSender teleporter, boolean safeTeleport) {
this.teleportee = teleportee;
this.teleporter = teleporter;
this.dest = dest;
@ -84,7 +84,7 @@ public class MVTeleportEvent extends Event implements Cancellable {
*
* @return The destination the player will spawn at.
*/
public ParsedDestination<?> getDestination() {
public DestinationInstance<?, ?> getDestination() {
return this.dest;
}

View File

@ -36,8 +36,8 @@ import org.mvplugins.multiverse.core.MultiverseCore;
import org.mvplugins.multiverse.core.api.BlockSafety;
import org.mvplugins.multiverse.core.commandtools.MVCommandManager;
import org.mvplugins.multiverse.core.config.MVCoreConfig;
import org.mvplugins.multiverse.core.destination.DestinationInstance;
import org.mvplugins.multiverse.core.destination.DestinationsProvider;
import org.mvplugins.multiverse.core.destination.ParsedDestination;
import org.mvplugins.multiverse.core.economy.MVEconomist;
import org.mvplugins.multiverse.core.event.MVRespawnEvent;
import org.mvplugins.multiverse.core.teleportation.AsyncSafetyTeleporter;
@ -173,7 +173,8 @@ public class MVPlayerListener implements CoreListener {
return;
}
Option.of(destinationsProvider.parseDestination(config.getFirstSpawnLocation()))
// todo: Config should auto serialise to and from DestinationInstance
destinationsProvider.parseDestination(config.getFirstSpawnLocation())
.peek(parsedDestination -> {
if (!player.hasPlayedBefore()) {
Logging.finer("Player joined for the FIRST time!");
@ -207,15 +208,9 @@ public class MVPlayerListener implements CoreListener {
}
Logging.finer("JoinDestination is " + config.getJoinDestination());
ParsedDestination<?> joinDestination = destinationsProvider.parseDestination(config.getJoinDestination());
if (joinDestination == null) {
Logging.warning("The destination in JoinDestination in config is invalid");
return;
}
// Finally, teleport the player
safetyTeleporter.teleportSafely(player, player, joinDestination);
destinationsProvider.parseDestination(config.getJoinDestination())
.peek(destination -> safetyTeleporter.teleportSafely(player, player, destination))
.onEmpty(() -> Logging.warning("The destination in JoinDestination in config is invalid"));
}
/**
@ -350,7 +345,7 @@ public class MVPlayerListener implements CoreListener {
Logging.fine("Teleport result: %s", entryResult);
}
private void sendPlayerToDefaultWorld(final Player player, ParsedDestination parsedDestination) {
private void sendPlayerToDefaultWorld(final Player player, DestinationInstance<?, ?> parsedDestination) {
// Remove the player 1 tick after the login. I'm sure there's GOT to be a better way to do this...
this.server.getScheduler().scheduleSyncDelayedTask(this.plugin,
new Runnable() {

View File

@ -7,16 +7,16 @@ import org.bukkit.entity.Entity;
import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Service;
import org.mvplugins.multiverse.core.api.Destination;
import org.mvplugins.multiverse.core.destination.Destination;
import org.mvplugins.multiverse.core.destination.DestinationInstance;
import org.mvplugins.multiverse.core.destination.DestinationsProvider;
import org.mvplugins.multiverse.core.destination.ParsedDestination;
import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld;
import org.mvplugins.multiverse.core.world.MultiverseWorld;
@Service
public class CorePermissionsChecker {
private DestinationsProvider destinationsProvider;
private final DestinationsProvider destinationsProvider;
@Inject
CorePermissionsChecker(@NotNull DestinationsProvider destinationsProvider) {
@ -50,7 +50,7 @@ public class CorePermissionsChecker {
public boolean checkTeleportPermissions(
@NotNull CommandSender teleporter,
@NotNull Entity teleportee,
@NotNull ParsedDestination<?> destination) {
@NotNull DestinationInstance<?, ?> destination) {
String permission = concatPermission(
CorePermissions.TELEPORT,
@ -61,13 +61,13 @@ public class CorePermissionsChecker {
}
// TODO: Config whether to use finer permission
String finerPermissionSuffix = destination.getDestinationInstance().getFinerPermissionSuffix();
if (finerPermissionSuffix == null || finerPermissionSuffix.isEmpty()) {
return true;
}
String finerPermission = concatPermission(permission, finerPermissionSuffix);
return hasPermission(teleporter, finerPermission);
return destination.getFinerPermissionSuffix()
.filter(String::isBlank)
.map(finerPermissionSuffix -> hasPermission(
teleporter,
concatPermission(permission, finerPermissionSuffix)
))
.getOrElse(true);
}
/**
@ -77,7 +77,7 @@ public class CorePermissionsChecker {
* @return True if the issuer has permission, false otherwise.
*/
public boolean hasAnyTeleportPermission(CommandSender sender) {
for (Destination<?> destination : destinationsProvider.getDestinations()) {
for (Destination<?, ?> destination : destinationsProvider.getDestinations()) {
String permission = concatPermission(CorePermissions.TELEPORT, "self", destination.getIdentifier());
if (hasPermission(sender, permission)) {
return true;

View File

@ -14,7 +14,7 @@ import org.jetbrains.annotations.Nullable;
import org.jvnet.hk2.annotations.Service;
import org.mvplugins.multiverse.core.api.BlockSafety;
import org.mvplugins.multiverse.core.destination.ParsedDestination;
import org.mvplugins.multiverse.core.destination.DestinationInstance;
import org.mvplugins.multiverse.core.utils.result.Async;
import org.mvplugins.multiverse.core.utils.result.AsyncAttempt;
import org.mvplugins.multiverse.core.utils.result.Attempt;
@ -37,14 +37,14 @@ public class AsyncSafetyTeleporter {
public AsyncAttempt<Void, TeleportResult.Failure> teleportSafely(
@NotNull Entity teleportee,
@Nullable ParsedDestination<?> destination) {
@Nullable DestinationInstance<?, ?> destination) {
return teleportSafely(null, teleportee, destination);
}
public <T extends Entity> Async<List<Attempt<Void, TeleportResult.Failure>>> teleportSafely(
@Nullable CommandSender teleporter,
@NotNull List<T> teleportees,
@Nullable ParsedDestination<?> destination) {
@Nullable DestinationInstance<?, ?> destination) {
return AsyncAttempt.allOf(teleportees.stream()
.map(teleportee -> teleportSafely(teleporter, teleportee, destination))
.toList());
@ -53,13 +53,15 @@ public class AsyncSafetyTeleporter {
public AsyncAttempt<Void, TeleportResult.Failure> teleportSafely(
@Nullable CommandSender teleporter,
@NotNull Entity teleportee,
@Nullable ParsedDestination<?> destination) {
@Nullable DestinationInstance<?, ?> destination) {
if (destination == null) {
return AsyncAttempt.failure(TeleportResult.Failure.NULL_DESTINATION);
}
return destination.getDestination().checkTeleportSafety()
? teleportSafely(teleporter, teleportee, destination.getLocation(teleportee))
: teleport(teleporter, teleportee, destination.getLocation(teleportee));
return destination.getLocation(teleportee)
.map(location -> destination.checkTeleportSafety()
? teleportSafely(teleporter, teleportee, location)
: teleport(teleporter, teleportee, location))
.getOrElse(AsyncAttempt.failure(TeleportResult.Failure.NULL_LOCATION));
}
public AsyncAttempt<Void, TeleportResult.Failure> teleportSafely(
@ -84,7 +86,7 @@ public class AsyncSafetyTeleporter {
public <T extends Entity> Async<List<Attempt<Void, TeleportResult.Failure>>> teleport(
@NotNull List<T> teleportees,
@Nullable ParsedDestination<?> destination) {
@Nullable DestinationInstance<?, ?> destination) {
return AsyncAttempt.allOf(teleportees.stream()
.map(teleportee -> teleport(teleportee, destination))
.toList());
@ -92,18 +94,20 @@ public class AsyncSafetyTeleporter {
public AsyncAttempt<Void, TeleportResult.Failure> teleport(
@NotNull Entity teleportee,
@Nullable ParsedDestination<?> destination) {
@Nullable DestinationInstance<?, ?> destination) {
return teleport(null, teleportee, destination);
}
public AsyncAttempt<Void, TeleportResult.Failure> teleport(
@Nullable CommandSender teleporter,
@NotNull Entity teleportee,
@Nullable ParsedDestination<?> destination) {
@Nullable DestinationInstance<?, ?> destination) {
if (destination == null) {
return AsyncAttempt.failure(TeleportResult.Failure.NULL_DESTINATION);
}
return teleport(teleporter, teleportee, destination.getLocation(teleportee));
return destination.getLocation(teleportee)
.map(location -> teleport(teleporter, teleportee, location))
.getOrElse(AsyncAttempt.failure(TeleportResult.Failure.NULL_LOCATION));
}
public <T extends Entity> Async<List<Attempt<Void, TeleportResult.Failure>>> teleport(
@ -139,15 +143,15 @@ public class AsyncSafetyTeleporter {
private AsyncAttempt<Void, TeleportResult.Failure> doAsyncTeleport(
@NotNull Entity teleportee,
@NotNull Location location,
boolean shouldAddToQueue) {
boolean shouldRemoveFromQueue) {
return AsyncAttempt.of(PaperLib.teleportAsync(teleportee, location), exception -> {
Logging.warning("Failed to teleport %s to %s: %s",
teleportee.getName(), location, exception.getMessage());
return Attempt.failure(TeleportResult.Failure.TELEPORT_FAILED_EXCEPTION);
}).mapAttempt(result -> {
}).mapAttempt(success -> {
Logging.finer("Teleported async %s to %s", teleportee.getName(), location);
if (result) {
if (shouldAddToQueue) {
if (success) {
if (shouldRemoveFromQueue) {
teleportQueue.popFromQueue(teleportee.getName());
}
return Attempt.success(null);

View File

@ -0,0 +1,122 @@
package org.mvplugins.multiverse.core.destination
import org.bukkit.Location
import org.mockbukkit.mockbukkit.entity.PlayerMock
import org.mvplugins.multiverse.core.TestWithMockBukkit
import org.mvplugins.multiverse.core.destination.core.*
import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld
import org.mvplugins.multiverse.core.world.WorldManager
import org.mvplugins.multiverse.core.world.options.CreateWorldOptions
import kotlin.test.BeforeTest
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertTrue
class DestinationTest : TestWithMockBukkit() {
private lateinit var worldManager: WorldManager
private lateinit var destinationsProvider: DestinationsProvider
private lateinit var world: LoadedMultiverseWorld
private lateinit var player: PlayerMock
@BeforeTest
fun setUp() {
worldManager = serviceLocator.getActiveService(WorldManager::class.java).takeIf { it != null } ?: run {
throw IllegalStateException("WorldManager is not available as a service") }
destinationsProvider = serviceLocator.getActiveService(DestinationsProvider::class.java).takeIf { it != null } ?: run {
throw IllegalStateException("DestinationsProvider is not available as a service") }
world = worldManager.createWorld(CreateWorldOptions.worldName("world")).get()
player = server.addPlayer("benji_0224")
player.bedSpawnLocation = Location(world.bukkitWorld.orNull, 5.0, 10.0, 5.0)
}
@Test
fun `Bed destination instance`() {
assertTrue(destinationsProvider.getDestinationById("b") is BedDestination)
val destination = destinationsProvider.parseDestination("b:benji_0224").orNull
assertTrue(destination is BedDestinationInstance)
assertEquals(player.bedSpawnLocation, destination.getLocation(player).orNull)
assertEquals("b:benji_0224", destination.toString())
}
@Test
fun `Bed destination instance own player`() {
val destination = destinationsProvider.parseDestination("b:playerbed").orNull
assertTrue(destination is BedDestinationInstance)
assertEquals(player.bedSpawnLocation, destination.getLocation(player).orNull)
assertEquals("b:playerbed", destination.toString())
}
@Test
fun `Cannon destination instance`() {
assertTrue(destinationsProvider.getDestinationById("ca") is CannonDestination)
val destination = destinationsProvider.parseDestination("ca:world:1.2,2,3:10.5:9.5:5").orNull
assertTrue(destination is CannonDestinationInstance)
val expectedLocation = Location(world.bukkitWorld.orNull, 1.2, 2.0, 3.0, 9.5F, 10.5F)
assertEquals(expectedLocation, destination.getLocation(player).orNull)
// todo: assert the Vector
assertEquals("ca:world:1.2,2.0,3.0:10.5:9.5:5.0", destination.toString())
}
@Test
fun `Exact destination instance`() {
assertTrue(destinationsProvider.getDestinationById("e") is ExactDestination)
val destination = destinationsProvider.parseDestination("e:world:1.2,2,3:10.5:9.5").orNull
assertTrue(destination is ExactDestinationInstance)
val expectedLocation = Location(world.bukkitWorld.orNull, 1.2, 2.0, 3.0, 9.5F, 10.5F)
assertEquals(expectedLocation, destination.getLocation(player).orNull)
assertEquals("e:world:1.2,2.0,3.0:10.5:9.5", destination.toString())
}
@Test
fun `Exact destination instance from location`() {
val exactDestination = serviceLocator.getActiveService(ExactDestination::class.java).takeIf { it != null } ?: run {
throw IllegalStateException("ExactDestination is not available as a service") }
val location = Location(world.bukkitWorld.orNull, 1.2, 2.0, 3.0, 9.5F, 10.5F)
val destination = exactDestination.fromLocation(location)
assertEquals(location, destination.getLocation(player).orNull)
assertEquals("e:world:1.2,2.0,3.0:10.5:9.5", destination.toString())
}
@Test
fun `Player destination instance`() {
assertTrue(destinationsProvider.getDestinationById("pl") is PlayerDestination)
val destination = destinationsProvider.parseDestination("pl:benji_0224").orNull
assertTrue(destination is PlayerDestinationInstance)
assertEquals(player.location, destination.getLocation(player).orNull)
assertEquals("pl:benji_0224", destination.toString())
}
@Test
fun `World destination instance`() {
assertTrue(destinationsProvider.getDestinationById("w") is WorldDestination)
val destination = destinationsProvider.parseDestination("w:world").orNull
assertTrue(destination is WorldDestinationInstance)
assertEquals(world.spawnLocation, destination.getLocation(player).orNull)
assertEquals("w:world", destination.toString())
}
@Test
fun `World destination instance without identifier`() {
val destination = destinationsProvider.parseDestination("world").orNull
assertTrue(destination is WorldDestinationInstance)
assertEquals(world.spawnLocation, destination.getLocation(player).orNull)
assertEquals("w:world", destination.toString())
}
@Test
fun `Invalid destination instance`() {
assertTrue(destinationsProvider.parseDestination("").isEmpty)
assertTrue(destinationsProvider.parseDestination("idk:world").isEmpty)
assertTrue(destinationsProvider.parseDestination("a:invalid-anchor").isEmpty)
assertTrue(destinationsProvider.parseDestination("b:invalid-bed").isEmpty)
assertTrue(destinationsProvider.parseDestination("ca:invalid-cannon").isEmpty)
assertTrue(destinationsProvider.parseDestination("e:world:1,2,x").isEmpty)
assertTrue(destinationsProvider.parseDestination("pl:invalid-player").isEmpty)
assertTrue(destinationsProvider.parseDestination("w:invalid-world").isEmpty)
// todo: should we make invalid yaw for WorldDestination fail?
// assertTrue(destinationsProvider.parseDestination("w:world:f").isEmpty)
}
}

View File

@ -3,7 +3,7 @@ package org.mvplugins.multiverse.core.inject
import org.mvplugins.multiverse.core.TestWithMockBukkit
import org.mvplugins.multiverse.core.anchor.AnchorManager
import org.mvplugins.multiverse.core.api.BlockSafety
import org.mvplugins.multiverse.core.api.Destination
import org.mvplugins.multiverse.core.destination.Destination
import org.mvplugins.multiverse.core.api.LocationManipulation
import org.mvplugins.multiverse.core.commandtools.MVCommandManager
import org.mvplugins.multiverse.core.commandtools.MultiverseCommand
@ -17,7 +17,6 @@ import org.mvplugins.multiverse.core.teleportation.SimpleLocationManipulation
import org.mvplugins.multiverse.core.teleportation.TeleportQueue
import org.mvplugins.multiverse.core.utils.metrics.MetricsConfigurator
import org.mvplugins.multiverse.core.world.WorldManager
import org.mvplugins.multiverse.core.world.config.WorldsConfigManager
import kotlin.test.*
class InjectionTest : TestWithMockBukkit() {
@ -110,7 +109,6 @@ class InjectionTest : TestWithMockBukkit() {
@Test
fun `Destinations are available as services`() {
val destinations = serviceLocator.getAllActiveServices(Destination::class.java)
// TODO: come up with a better way to test this like via actually testing the effect of using each destination
assertEquals(6, destinations.size)
}