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.jvnet.hk2.annotations.Service;
import org.mvplugins.multiverse.core.anchor.AnchorManager; 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.api.MVCore;
import org.mvplugins.multiverse.core.commands.CoreCommand; import org.mvplugins.multiverse.core.commands.CoreCommand;
import org.mvplugins.multiverse.core.commandtools.MVCommandManager; 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.MVCommandIssuer;
import org.mvplugins.multiverse.core.commandtools.MVCommandManager; 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.DestinationsProvider;
import org.mvplugins.multiverse.core.destination.ParsedDestination;
import org.mvplugins.multiverse.core.utils.MVCorei18n; import org.mvplugins.multiverse.core.utils.MVCorei18n;
@Service @Service
@ -45,7 +44,7 @@ class CheckCommand extends CoreCommand {
@Syntax("<destination>") @Syntax("<destination>")
@Description("{@@mv-core.check.destination.description}") @Description("{@@mv-core.check.destination.description}")
ParsedDestination<?> destination) { DestinationInstance<?, ?> destination) {
issuer.sendInfo(MVCorei18n.CHECK_CHECKING, issuer.sendInfo(MVCorei18n.CHECK_CHECKING,
"{player}", player.getName(), "{player}", player.getName(),
"{destination}", destination.toString()); "{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.MVCommandIssuer;
import org.mvplugins.multiverse.core.commandtools.MVCommandManager; 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.ParsedDestination;
import org.mvplugins.multiverse.core.permissions.CorePermissionsChecker; import org.mvplugins.multiverse.core.permissions.CorePermissionsChecker;
import org.mvplugins.multiverse.core.teleportation.AsyncSafetyTeleporter; import org.mvplugins.multiverse.core.teleportation.AsyncSafetyTeleporter;
import org.mvplugins.multiverse.core.utils.MVCorei18n; import org.mvplugins.multiverse.core.utils.MVCorei18n;
@ -55,7 +54,7 @@ class TeleportCommand extends CoreCommand {
@Syntax("<destination>") @Syntax("<destination>")
@Description("{@@mv-core.teleport.destination.description}") @Description("{@@mv-core.teleport.destination.description}")
ParsedDestination<?> destination) { DestinationInstance<?, ?> destination) {
// TODO: Add warning if teleporting too many players at once. // TODO: Add warning if teleporting too many players at once.
String playerName = players.length == 1 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.config.MVCoreConfig;
import org.mvplugins.multiverse.core.configuration.handle.PropertyModifyAction; 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.DestinationsProvider;
import org.mvplugins.multiverse.core.destination.ParsedDestination;
import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld; import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld;
import org.mvplugins.multiverse.core.world.MultiverseWorld; import org.mvplugins.multiverse.core.world.MultiverseWorld;
import org.mvplugins.multiverse.core.world.WorldManager; import org.mvplugins.multiverse.core.world.WorldManager;
@ -68,7 +68,7 @@ class MVCommandCompletions extends PaperCommandCompletions {
registerAsyncCompletion("mvworldpropsvalue", this::suggestMVWorldPropsValue); registerAsyncCompletion("mvworldpropsvalue", this::suggestMVWorldPropsValue);
registerStaticCompletion("propsmodifyaction", suggestEnums(PropertyModifyAction.class)); registerStaticCompletion("propsmodifyaction", suggestEnums(PropertyModifyAction.class));
setDefaultCompletion("destinations", ParsedDestination.class); setDefaultCompletion("destinations", DestinationInstance.class);
setDefaultCompletion("difficulties", Difficulty.class); setDefaultCompletion("difficulties", Difficulty.class);
setDefaultCompletion("environments", World.Environment.class); setDefaultCompletion("environments", World.Environment.class);
setDefaultCompletion("flags", String[].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.commandtools.context.GameRuleValue;
import org.mvplugins.multiverse.core.config.MVCoreConfig; 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.DestinationsProvider;
import org.mvplugins.multiverse.core.destination.ParsedDestination;
import org.mvplugins.multiverse.core.display.filters.ContentFilter; import org.mvplugins.multiverse.core.display.filters.ContentFilter;
import org.mvplugins.multiverse.core.display.filters.DefaultContentFilter; import org.mvplugins.multiverse.core.display.filters.DefaultContentFilter;
import org.mvplugins.multiverse.core.display.filters.RegexContentFilter; import org.mvplugins.multiverse.core.display.filters.RegexContentFilter;
@ -49,7 +49,7 @@ class MVCommandContexts extends PaperCommandContexts {
registerIssuerOnlyContext(BukkitCommandIssuer.class, BukkitCommandExecutionContext::getIssuer); registerIssuerOnlyContext(BukkitCommandIssuer.class, BukkitCommandExecutionContext::getIssuer);
registerIssuerOnlyContext(MVCommandIssuer.class, this::parseMVCommandIssuer); registerIssuerOnlyContext(MVCommandIssuer.class, this::parseMVCommandIssuer);
registerOptionalContext(ContentFilter.class, this::parseContentFilter); registerOptionalContext(ContentFilter.class, this::parseContentFilter);
registerContext(ParsedDestination.class, this::parseDestination); registerContext(DestinationInstance.class, this::parseDestination);
registerContext(GameRule.class, this::parseGameRule); registerContext(GameRule.class, this::parseGameRule);
registerContext(GameRuleValue.class, this::parseGameRuleValue); registerContext(GameRuleValue.class, this::parseGameRuleValue);
registerIssuerAwareContext(LoadedMultiverseWorld.class, this::parseLoadedMultiverseWorld); registerIssuerAwareContext(LoadedMultiverseWorld.class, this::parseLoadedMultiverseWorld);
@ -74,18 +74,14 @@ class MVCommandContexts extends PaperCommandContexts {
return RegexContentFilter.fromString(filterString); return RegexContentFilter.fromString(filterString);
} }
private ParsedDestination<?> parseDestination(BukkitCommandExecutionContext context) { private DestinationInstance<?, ?> parseDestination(BukkitCommandExecutionContext context) {
String destination = context.popFirstArg(); String destination = context.popFirstArg();
if (Strings.isNullOrEmpty(destination)) { if (Strings.isNullOrEmpty(destination)) {
throw new InvalidCommandArgument("No destination specified."); throw new InvalidCommandArgument("No destination specified.");
} }
ParsedDestination<?> parsedDestination = destinationsProvider.parseDestination(destination); return destinationsProvider.parseDestination(destination)
if (parsedDestination == null) { .getOrElseThrow(() -> new InvalidCommandArgument("The destination " + destination + " is not valid."));
throw new InvalidCommandArgument("The destination " + destination + " is not valid.");
}
return parsedDestination;
} }
private GameRule<?> parseGameRule(BukkitCommandExecutionContext context) { 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; import java.util.Collection;
@ -8,7 +8,7 @@ import org.jetbrains.annotations.Nullable;
import org.jvnet.hk2.annotations.Contract; import org.jvnet.hk2.annotations.Contract;
@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. * 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. * @return A list of possible destinations.
*/ */
@NotNull Collection<String> suggestDestinations(@NotNull BukkitCommandIssuer issuer, @Nullable String destinationParams); @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 java.util.stream.Collectors;
import co.aikar.commands.BukkitCommandIssuer; import co.aikar.commands.BukkitCommandIssuer;
import io.vavr.control.Option;
import jakarta.inject.Inject; import jakarta.inject.Inject;
import org.bukkit.permissions.Permission; import org.bukkit.permissions.Permission;
import org.bukkit.plugin.PluginManager; import org.bukkit.plugin.PluginManager;
@ -13,9 +14,6 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.jvnet.hk2.annotations.Service; import org.jvnet.hk2.annotations.Service;
import org.mvplugins.multiverse.core.api.Destination;
import org.mvplugins.multiverse.core.api.DestinationInstance;
/** /**
* Provides destinations for teleportation. * Provides destinations for teleportation.
*/ */
@ -25,7 +23,7 @@ public class DestinationsProvider {
private static final String PERMISSION_PREFIX = "multiverse.teleport."; private static final String PERMISSION_PREFIX = "multiverse.teleport.";
private final PluginManager pluginManager; private final PluginManager pluginManager;
private final Map<String, Destination<?>> destinationMap; private final Map<String, Destination<?, ?>> destinationMap;
@Inject @Inject
DestinationsProvider(@NotNull PluginManager pluginManager) { DestinationsProvider(@NotNull PluginManager pluginManager) {
@ -38,12 +36,12 @@ public class DestinationsProvider {
* *
* @param destination The destination. * @param destination The destination.
*/ */
public void registerDestination(@NotNull Destination<?> destination) { public void registerDestination(@NotNull Destination<?, ?> destination) {
this.destinationMap.put(destination.getIdentifier(), destination); this.destinationMap.put(destination.getIdentifier(), destination);
this.registerDestinationPerms(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 + "self." + destination.getIdentifier()));
pluginManager.addPermission(new Permission(PERMISSION_PREFIX + "other." + destination.getIdentifier())); pluginManager.addPermission(new Permission(PERMISSION_PREFIX + "other." + destination.getIdentifier()));
} }
@ -74,13 +72,13 @@ public class DestinationsProvider {
* @param destinationString The destination string. * @param destinationString The destination string.
* @return The destination object, or null if invalid format. * @return The destination object, or null if invalid format.
*/ */
@Nullable @NotNull
public ParsedDestination<?> parseDestination(@NotNull String destinationString) { public Option<DestinationInstance<?, ?>> parseDestination(@NotNull String destinationString) {
String[] items = destinationString.split(SEPARATOR, 2); String[] items = destinationString.split(SEPARATOR, 2);
String idString = items[0]; String idString = items[0];
String destinationParams; String destinationParams;
Destination<?> destination; Destination<?, ?> destination;
if (items.length < 2) { if (items.length < 2) {
// Assume world destination // Assume world destination
@ -92,15 +90,10 @@ public class DestinationsProvider {
} }
if (destination == null) { if (destination == null) {
return null; return Option.none();
} }
DestinationInstance destinationInstance = destination.getDestinationInstance(destinationParams); return Option.of(destination.getDestinationInstance(destinationParams));
if (destinationInstance == null) {
return null;
}
return new ParsedDestination<>(destination, destinationInstance);
} }
/** /**
@ -109,7 +102,7 @@ public class DestinationsProvider {
* @param identifier The identifier. * @param identifier The identifier.
* @return The destination, or null if not found. * @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); return this.destinationMap.get(identifier);
} }
@ -118,7 +111,7 @@ public class DestinationsProvider {
* *
* @return A collection of destinations. * @return A collection of destinations.
*/ */
public @NotNull Collection<Destination<?>> getDestinations() { public @NotNull Collection<Destination<?, ?>> getDestinations() {
return this.destinationMap.values(); 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.jvnet.hk2.annotations.Service;
import org.mvplugins.multiverse.core.anchor.AnchorManager; 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. * {@link Destination} implementation for anchors.
*/ */
@Service @Service
public class AnchorDestination implements Destination<AnchorDestinationInstance> { public class AnchorDestination implements Destination<AnchorDestination, AnchorDestinationInstance> {
private final AnchorManager anchorManager; private final AnchorManager anchorManager;
@ -38,11 +38,14 @@ public class AnchorDestination implements Destination<AnchorDestinationInstance>
*/ */
@Override @Override
public @Nullable AnchorDestinationInstance getDestinationInstance(@Nullable String destinationParams) { public @Nullable AnchorDestinationInstance getDestinationInstance(@Nullable String destinationParams) {
if (destinationParams == null) {
return null;
}
Location anchorLocation = this.anchorManager.getAnchorLocation(destinationParams); Location anchorLocation = this.anchorManager.getAnchorLocation(destinationParams);
if (anchorLocation == null) { if (anchorLocation == null) {
return 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) { public @NotNull Collection<String> suggestDestinations(@NotNull BukkitCommandIssuer issuer, @Nullable String destinationParams) {
return this.anchorManager.getAnchors(issuer.getPlayer()); 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; package org.mvplugins.multiverse.core.destination.core;
import io.vavr.control.Option;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.entity.Entity; import org.bukkit.entity.Entity;
import org.bukkit.util.Vector; import org.bukkit.util.Vector;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; 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}. * 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 String anchorName;
private final Location anchorLocation; private final Location anchorLocation;
@ -21,7 +22,12 @@ public class AnchorDestinationInstance implements DestinationInstance {
* @param anchorName The name of the anchor. * @param anchorName The name of the anchor.
* @param anchorLocation The location 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.anchorName = anchorName;
this.anchorLocation = anchorLocation; this.anchorLocation = anchorLocation;
} }
@ -30,24 +36,32 @@ public class AnchorDestinationInstance implements DestinationInstance {
* {@inheritDoc} * {@inheritDoc}
*/ */
@Override @Override
public @Nullable Location getLocation(@NotNull Entity teleportee) { public @NotNull Option<Location> getLocation(@NotNull Entity teleportee) {
return anchorLocation; return Option.of(anchorLocation);
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@Override @Override
public @Nullable Vector getVelocity(@NotNull Entity teleportee) { public @NotNull Option<Vector> getVelocity(@NotNull Entity teleportee) {
return null; return Option.none();
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@Override @Override
public @Nullable String getFinerPermissionSuffix() { public boolean checkTeleportSafety() {
return anchorName; 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.jetbrains.annotations.Nullable;
import org.jvnet.hk2.annotations.Service; 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; import org.mvplugins.multiverse.core.utils.PlayerFinder;
/** /**
* {@link Destination} implementation for beds. * {@link Destination} implementation for beds.
*/ */
@Service @Service
public class BedDestination implements Destination<BedDestinationInstance> { public class BedDestination implements Destination<BedDestination, BedDestinationInstance> {
static final String OWN_BED_STRING = "playerbed"; static final String OWN_BED_STRING = "playerbed";
BedDestination() { BedDestination() {
@ -41,7 +41,7 @@ public class BedDestination implements Destination<BedDestinationInstance> {
if (player == null && !destinationParams.equals(OWN_BED_STRING)) { if (player == null && !destinationParams.equals(OWN_BED_STRING)) {
return null; 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); collect.add(OWN_BED_STRING);
return collect; return collect;
} }
/**
* {@inheritDoc}
*/
@Override
public boolean checkTeleportSafety() {
return false;
}
} }

View File

@ -1,5 +1,6 @@
package org.mvplugins.multiverse.core.destination.core; package org.mvplugins.multiverse.core.destination.core;
import io.vavr.control.Option;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.entity.Entity; import org.bukkit.entity.Entity;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
@ -7,12 +8,12 @@ import org.bukkit.util.Vector;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; 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}. * Destination instance implementation for the {@link BedDestination}.
*/ */
public class BedDestinationInstance implements DestinationInstance { public class BedDestinationInstance extends DestinationInstance<BedDestinationInstance, BedDestination> {
private final Player player; private final Player player;
/** /**
@ -20,7 +21,8 @@ public class BedDestinationInstance implements DestinationInstance {
* *
* @param player The player whose bed to use. * @param player The player whose bed to use.
*/ */
BedDestinationInstance(Player player) { BedDestinationInstance(@NotNull BedDestination destination, @Nullable Player player) {
super(destination);
this.player = player; this.player = player;
} }
@ -28,30 +30,38 @@ public class BedDestinationInstance implements DestinationInstance {
* {@inheritDoc} * {@inheritDoc}
*/ */
@Override @Override
public @Nullable Location getLocation(@NotNull Entity teleportee) { public @NotNull Option<Location> getLocation(@NotNull Entity teleportee) {
if (player != null) { if (player != null) {
return player.getBedSpawnLocation(); return Option.of(player.getBedSpawnLocation());
} }
if (teleportee instanceof Player) { if (teleportee instanceof Player) {
return ((Player) teleportee).getBedSpawnLocation(); return Option.of(((Player) teleportee).getBedSpawnLocation());
} }
return null; return Option.none();
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@Override @Override
public @Nullable Vector getVelocity(@NotNull Entity teleportee) { public @NotNull Option<Vector> getVelocity(@NotNull Entity teleportee) {
return null; return Option.none();
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@Override @Override
public @Nullable String getFinerPermissionSuffix() { public boolean checkTeleportSafety() {
return player != null ? player.getName() : BedDestination.OWN_BED_STRING; 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.jetbrains.annotations.Nullable;
import org.jvnet.hk2.annotations.Service; 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.LoadedMultiverseWorld;
import org.mvplugins.multiverse.core.world.WorldManager; import org.mvplugins.multiverse.core.world.WorldManager;
@ -19,7 +19,7 @@ import org.mvplugins.multiverse.core.world.WorldManager;
* {@link Destination} implementation for cannons. * {@link Destination} implementation for cannons.
*/ */
@Service @Service
public class CannonDestination implements Destination<CannonDestinationInstance> { public class CannonDestination implements Destination<CannonDestination, CannonDestinationInstance> {
private final WorldManager worldManager; private final WorldManager worldManager;
@ -78,7 +78,7 @@ public class CannonDestination implements Destination<CannonDestinationInstance>
return null; 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) { public @NotNull Collection<String> suggestDestinations(@NotNull BukkitCommandIssuer issuer, @Nullable String destinationParams) {
return Collections.singleton(""); return Collections.singleton("");
} }
/**
* {@inheritDoc}
*/
@Override
public boolean checkTeleportSafety() {
return false;
}
} }

View File

@ -1,17 +1,19 @@
package org.mvplugins.multiverse.core.destination.core; package org.mvplugins.multiverse.core.destination.core;
import io.vavr.control.Option;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.entity.Entity; import org.bukkit.entity.Entity;
import org.bukkit.util.Vector; import org.bukkit.util.Vector;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; 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}. * 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 Location location;
private final double speed; private final double speed;
@ -21,7 +23,8 @@ public class CannonDestinationInstance implements DestinationInstance {
* @param location The location to teleport to. * @param location The location to teleport to.
* @param speed The speed to fire the player at. * @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.location = location;
this.speed = speed; this.speed = speed;
} }
@ -30,15 +33,15 @@ public class CannonDestinationInstance implements DestinationInstance {
* {@inheritDoc} * {@inheritDoc}
*/ */
@Override @Override
public @Nullable Location getLocation(@NotNull Entity teleportee) { public @NotNull Option<Location> getLocation(@NotNull Entity teleportee) {
return location; return Option.of(location);
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@Override @Override
public @Nullable Vector getVelocity(@NotNull Entity teleportee) { public @NotNull Option<Vector> getVelocity(@NotNull Entity teleportee) {
double pitchRadians = Math.toRadians(location.getPitch()); double pitchRadians = Math.toRadians(location.getPitch());
double yawRadians = Math.toRadians(location.getYaw()); double yawRadians = Math.toRadians(location.getYaw());
double x = Math.sin(yawRadians) * speed * -1; 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 // Account for the angle they were pointed, and take away velocity
x = Math.cos(pitchRadians) * x; x = Math.cos(pitchRadians) * x;
z = Math.cos(pitchRadians) * z; z = Math.cos(pitchRadians) * z;
return new Vector(x, y, z); return Option.of(new Vector(x, y, z));
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@Override @Override
public @Nullable String getFinerPermissionSuffix() { public boolean checkTeleportSafety() {
return location.getWorld().getName(); 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.jetbrains.annotations.Nullable;
import org.jvnet.hk2.annotations.Service; 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.LoadedMultiverseWorld;
import org.mvplugins.multiverse.core.world.WorldManager; import org.mvplugins.multiverse.core.world.WorldManager;
@ -19,7 +19,7 @@ import org.mvplugins.multiverse.core.world.WorldManager;
* {@link Destination} implementation for exact locations. * {@link Destination} implementation for exact locations.
*/ */
@Service @Service
public class ExactDestination implements Destination<ExactDestinationInstance> { public class ExactDestination implements Destination<ExactDestination, ExactDestinationInstance> {
private final WorldManager worldManager; private final WorldManager worldManager;
@ -36,6 +36,16 @@ public class ExactDestination implements Destination<ExactDestinationInstance> {
return "e"; 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} * {@inheritDoc}
*/ */
@ -53,7 +63,7 @@ public class ExactDestination implements Destination<ExactDestinationInstance> {
return null; 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) { if (world == null) {
return 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) { public @NotNull Collection<String> suggestDestinations(@NotNull BukkitCommandIssuer issuer, @Nullable String destinationParams) {
return Collections.singleton(""); return Collections.singleton("");
} }
/**
* {@inheritDoc}
*/
@Override
public boolean checkTeleportSafety() {
return false;
}
} }

View File

@ -1,17 +1,19 @@
package org.mvplugins.multiverse.core.destination.core; package org.mvplugins.multiverse.core.destination.core;
import io.vavr.control.Option;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.entity.Entity; import org.bukkit.entity.Entity;
import org.bukkit.util.Vector; import org.bukkit.util.Vector;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; 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}. * Destination instance implementation for the {@link ExactDestination}.
*/ */
public class ExactDestinationInstance implements DestinationInstance { public class ExactDestinationInstance extends DestinationInstance<ExactDestinationInstance, ExactDestination> {
private final Location location; private final Location location;
/** /**
@ -19,7 +21,8 @@ public class ExactDestinationInstance implements DestinationInstance {
* *
* @param location The location to teleport to. * @param location The location to teleport to.
*/ */
ExactDestinationInstance(Location location) { ExactDestinationInstance(@NotNull ExactDestination destination, @NotNull Location location) {
super(destination);
this.location = location; this.location = location;
} }
@ -27,24 +30,33 @@ public class ExactDestinationInstance implements DestinationInstance {
* {@inheritDoc} * {@inheritDoc}
*/ */
@Override @Override
public @Nullable Location getLocation(@NotNull Entity teleportee) { public @NotNull Option<Location> getLocation(@NotNull Entity teleportee) {
return location; // todo: maybe check if the world is null?
return Option.of(location);
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@Override @Override
public @Nullable Vector getVelocity(@NotNull Entity teleportee) { public @NotNull Option<Vector> getVelocity(@NotNull Entity teleportee) {
return null; return Option.none();
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@Override @Override
public @Nullable String getFinerPermissionSuffix() { public boolean checkTeleportSafety() {
return location.getWorld().getName(); 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.jetbrains.annotations.Nullable;
import org.jvnet.hk2.annotations.Service; 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; import org.mvplugins.multiverse.core.utils.PlayerFinder;
/** /**
* {@link Destination} implementation for players.s * {@link Destination} implementation for players.s
*/ */
@Service @Service
public class PlayerDestination implements Destination<PlayerDestinationInstance> { public class PlayerDestination implements Destination<PlayerDestination, PlayerDestinationInstance> {
/** /**
* Creates a new instance of the PlayerDestination. * Creates a new instance of the PlayerDestination.
*/ */
@ -41,7 +41,7 @@ public class PlayerDestination implements Destination<PlayerDestinationInstance>
if (player == null) { if (player == null) {
return null; return null;
} }
return new PlayerDestinationInstance(player); return new PlayerDestinationInstance(this, player);
} }
/** /**
@ -49,14 +49,6 @@ public class PlayerDestination implements Destination<PlayerDestinationInstance>
*/ */
@Override @Override
public @NotNull Collection<String> suggestDestinations(@NotNull BukkitCommandIssuer issuer, @Nullable String destinationParams) { public @NotNull Collection<String> suggestDestinations(@NotNull BukkitCommandIssuer issuer, @Nullable String destinationParams) {
return Bukkit.getOnlinePlayers().stream().map(Player::getName).collect(Collectors.toList()); return Bukkit.getOnlinePlayers().stream().map(Player::getName).toList();
}
/**
* {@inheritDoc}
*/
@Override
public boolean checkTeleportSafety() {
return true;
} }
} }

View File

@ -1,5 +1,6 @@
package org.mvplugins.multiverse.core.destination.core; package org.mvplugins.multiverse.core.destination.core;
import io.vavr.control.Option;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.entity.Entity; import org.bukkit.entity.Entity;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
@ -7,12 +8,12 @@ import org.bukkit.util.Vector;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; 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}. * Destination instance implementation for the {@link PlayerDestination}.
*/ */
public class PlayerDestinationInstance implements DestinationInstance { public class PlayerDestinationInstance extends DestinationInstance<PlayerDestinationInstance, PlayerDestination> {
private final Player player; private final Player player;
/** /**
@ -20,7 +21,8 @@ public class PlayerDestinationInstance implements DestinationInstance {
* *
* @param player The player whose location to go to. * @param player The player whose location to go to.
*/ */
PlayerDestinationInstance(Player player) { PlayerDestinationInstance(@NotNull PlayerDestination destination, @NotNull Player player) {
super(destination);
this.player = player; this.player = player;
} }
@ -28,24 +30,32 @@ public class PlayerDestinationInstance implements DestinationInstance {
* {@inheritDoc} * {@inheritDoc}
*/ */
@Override @Override
public @Nullable Location getLocation(@NotNull Entity teleportee) { public @NotNull Option<Location> getLocation(@NotNull Entity teleportee) {
return player.getLocation(); return Option.of(player.getLocation());
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@Override @Override
public @Nullable Vector getVelocity(@NotNull Entity teleportee) { public @NotNull Option<Vector> getVelocity(@NotNull Entity teleportee) {
return null; return Option.none();
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@Override @Override
public @Nullable String getFinerPermissionSuffix() { public boolean checkTeleportSafety() {
return player.getName(); 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.jetbrains.annotations.Nullable;
import org.jvnet.hk2.annotations.Service; 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.api.LocationManipulation;
import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld; import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld;
import org.mvplugins.multiverse.core.world.WorldManager; import org.mvplugins.multiverse.core.world.WorldManager;
@ -18,7 +18,7 @@ import org.mvplugins.multiverse.core.world.WorldManager;
* {@link Destination} implementation for exact locations. * {@link Destination} implementation for exact locations.
*/ */
@Service @Service
public class WorldDestination implements Destination<WorldDestinationInstance> { public class WorldDestination implements Destination<WorldDestination, WorldDestinationInstance> {
private final WorldManager worldManager; private final WorldManager worldManager;
private final LocationManipulation locationManipulation; private final LocationManipulation locationManipulation;
@ -56,7 +56,7 @@ public class WorldDestination implements Destination<WorldDestinationInstance> {
String direction = (items.length == 2) ? items[1] : null; String direction = (items.length == 2) ? items[1] : null;
float yaw = direction != null ? this.locationManipulation.getYaw(direction) : -1; 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 // Autocomplete of worlds is done by MVCommandCompletion without prefix
return Collections.emptyList(); return Collections.emptyList();
} }
/**
* {@inheritDoc}
*/
@Override
public boolean checkTeleportSafety() {
return true;
}
} }

View File

@ -1,18 +1,19 @@
package org.mvplugins.multiverse.core.destination.core; package org.mvplugins.multiverse.core.destination.core;
import io.vavr.control.Option;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.entity.Entity; import org.bukkit.entity.Entity;
import org.bukkit.util.Vector; import org.bukkit.util.Vector;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; 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; import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld;
/** /**
* Destination instance implementation for the {@link WorldDestination}. * 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 LoadedMultiverseWorld world;
private final String direction; private final String direction;
private final float yaw; private final float yaw;
@ -20,11 +21,18 @@ public class WorldDestinationInstance implements DestinationInstance {
/** /**
* Constructor. * Constructor.
* *
* @param destination The destination.
* @param world The world to teleport to. * @param world The world to teleport to.
* @param direction The direction to face. * @param direction The direction to face.
* @param yaw The yaw 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.world = world;
this.direction = direction; this.direction = direction;
this.yaw = yaw; this.yaw = yaw;
@ -34,29 +42,37 @@ public class WorldDestinationInstance implements DestinationInstance {
* {@inheritDoc} * {@inheritDoc}
*/ */
@Override @Override
public @Nullable Location getLocation(@NotNull Entity teleportee) { public @NotNull Option<Location> getLocation(@NotNull Entity teleportee) {
Location worldLoc = world.getSpawnLocation(); Location worldLoc = world.getSpawnLocation();
if (this.yaw >= 0) { if (this.yaw >= 0) {
// Only modify the yaw if its set. // Only modify the yaw if its set.
worldLoc.setYaw(this.yaw); worldLoc.setYaw(this.yaw);
} }
return worldLoc; return Option.of(worldLoc);
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@Override @Override
public @Nullable Vector getVelocity(@NotNull Entity teleportee) { public @NotNull Option<Vector> getVelocity(@NotNull Entity teleportee) {
return null; return Option.none();
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@Override @Override
public @Nullable String getFinerPermissionSuffix() { public boolean checkTeleportSafety() {
return world.getName(); 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.Event;
import org.bukkit.event.HandlerList; 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; 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 { public class MVTeleportEvent extends Event implements Cancellable {
private Player teleportee; private Player teleportee;
private CommandSender teleporter; private CommandSender teleporter;
private ParsedDestination<?> dest; private DestinationInstance<?, ?> dest;
private boolean useSafeTeleport; private boolean useSafeTeleport;
private boolean isCancelled; 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.teleportee = teleportee;
this.teleporter = teleporter; this.teleporter = teleporter;
this.dest = dest; this.dest = dest;
@ -84,7 +84,7 @@ public class MVTeleportEvent extends Event implements Cancellable {
* *
* @return The destination the player will spawn at. * @return The destination the player will spawn at.
*/ */
public ParsedDestination<?> getDestination() { public DestinationInstance<?, ?> getDestination() {
return this.dest; 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.api.BlockSafety;
import org.mvplugins.multiverse.core.commandtools.MVCommandManager; import org.mvplugins.multiverse.core.commandtools.MVCommandManager;
import org.mvplugins.multiverse.core.config.MVCoreConfig; 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.DestinationsProvider;
import org.mvplugins.multiverse.core.destination.ParsedDestination;
import org.mvplugins.multiverse.core.economy.MVEconomist; import org.mvplugins.multiverse.core.economy.MVEconomist;
import org.mvplugins.multiverse.core.event.MVRespawnEvent; import org.mvplugins.multiverse.core.event.MVRespawnEvent;
import org.mvplugins.multiverse.core.teleportation.AsyncSafetyTeleporter; import org.mvplugins.multiverse.core.teleportation.AsyncSafetyTeleporter;
@ -173,7 +173,8 @@ public class MVPlayerListener implements CoreListener {
return; return;
} }
Option.of(destinationsProvider.parseDestination(config.getFirstSpawnLocation())) // todo: Config should auto serialise to and from DestinationInstance
destinationsProvider.parseDestination(config.getFirstSpawnLocation())
.peek(parsedDestination -> { .peek(parsedDestination -> {
if (!player.hasPlayedBefore()) { if (!player.hasPlayedBefore()) {
Logging.finer("Player joined for the FIRST time!"); Logging.finer("Player joined for the FIRST time!");
@ -207,15 +208,9 @@ public class MVPlayerListener implements CoreListener {
} }
Logging.finer("JoinDestination is " + config.getJoinDestination()); Logging.finer("JoinDestination is " + config.getJoinDestination());
ParsedDestination<?> joinDestination = destinationsProvider.parseDestination(config.getJoinDestination()); destinationsProvider.parseDestination(config.getJoinDestination())
.peek(destination -> safetyTeleporter.teleportSafely(player, player, destination))
if (joinDestination == null) { .onEmpty(() -> Logging.warning("The destination in JoinDestination in config is invalid"));
Logging.warning("The destination in JoinDestination in config is invalid");
return;
}
// Finally, teleport the player
safetyTeleporter.teleportSafely(player, player, joinDestination);
} }
/** /**
@ -350,7 +345,7 @@ public class MVPlayerListener implements CoreListener {
Logging.fine("Teleport result: %s", entryResult); 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... // 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, this.server.getScheduler().scheduleSyncDelayedTask(this.plugin,
new Runnable() { new Runnable() {

View File

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

View File

@ -14,7 +14,7 @@ import org.jetbrains.annotations.Nullable;
import org.jvnet.hk2.annotations.Service; import org.jvnet.hk2.annotations.Service;
import org.mvplugins.multiverse.core.api.BlockSafety; 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.Async;
import org.mvplugins.multiverse.core.utils.result.AsyncAttempt; import org.mvplugins.multiverse.core.utils.result.AsyncAttempt;
import org.mvplugins.multiverse.core.utils.result.Attempt; import org.mvplugins.multiverse.core.utils.result.Attempt;
@ -37,14 +37,14 @@ public class AsyncSafetyTeleporter {
public AsyncAttempt<Void, TeleportResult.Failure> teleportSafely( public AsyncAttempt<Void, TeleportResult.Failure> teleportSafely(
@NotNull Entity teleportee, @NotNull Entity teleportee,
@Nullable ParsedDestination<?> destination) { @Nullable DestinationInstance<?, ?> destination) {
return teleportSafely(null, teleportee, destination); return teleportSafely(null, teleportee, destination);
} }
public <T extends Entity> Async<List<Attempt<Void, TeleportResult.Failure>>> teleportSafely( public <T extends Entity> Async<List<Attempt<Void, TeleportResult.Failure>>> teleportSafely(
@Nullable CommandSender teleporter, @Nullable CommandSender teleporter,
@NotNull List<T> teleportees, @NotNull List<T> teleportees,
@Nullable ParsedDestination<?> destination) { @Nullable DestinationInstance<?, ?> destination) {
return AsyncAttempt.allOf(teleportees.stream() return AsyncAttempt.allOf(teleportees.stream()
.map(teleportee -> teleportSafely(teleporter, teleportee, destination)) .map(teleportee -> teleportSafely(teleporter, teleportee, destination))
.toList()); .toList());
@ -53,13 +53,15 @@ public class AsyncSafetyTeleporter {
public AsyncAttempt<Void, TeleportResult.Failure> teleportSafely( public AsyncAttempt<Void, TeleportResult.Failure> teleportSafely(
@Nullable CommandSender teleporter, @Nullable CommandSender teleporter,
@NotNull Entity teleportee, @NotNull Entity teleportee,
@Nullable ParsedDestination<?> destination) { @Nullable DestinationInstance<?, ?> destination) {
if (destination == null) { if (destination == null) {
return AsyncAttempt.failure(TeleportResult.Failure.NULL_DESTINATION); return AsyncAttempt.failure(TeleportResult.Failure.NULL_DESTINATION);
} }
return destination.getDestination().checkTeleportSafety() return destination.getLocation(teleportee)
? teleportSafely(teleporter, teleportee, destination.getLocation(teleportee)) .map(location -> destination.checkTeleportSafety()
: teleport(teleporter, teleportee, destination.getLocation(teleportee)); ? teleportSafely(teleporter, teleportee, location)
: teleport(teleporter, teleportee, location))
.getOrElse(AsyncAttempt.failure(TeleportResult.Failure.NULL_LOCATION));
} }
public AsyncAttempt<Void, TeleportResult.Failure> teleportSafely( public AsyncAttempt<Void, TeleportResult.Failure> teleportSafely(
@ -84,7 +86,7 @@ public class AsyncSafetyTeleporter {
public <T extends Entity> Async<List<Attempt<Void, TeleportResult.Failure>>> teleport( public <T extends Entity> Async<List<Attempt<Void, TeleportResult.Failure>>> teleport(
@NotNull List<T> teleportees, @NotNull List<T> teleportees,
@Nullable ParsedDestination<?> destination) { @Nullable DestinationInstance<?, ?> destination) {
return AsyncAttempt.allOf(teleportees.stream() return AsyncAttempt.allOf(teleportees.stream()
.map(teleportee -> teleport(teleportee, destination)) .map(teleportee -> teleport(teleportee, destination))
.toList()); .toList());
@ -92,18 +94,20 @@ public class AsyncSafetyTeleporter {
public AsyncAttempt<Void, TeleportResult.Failure> teleport( public AsyncAttempt<Void, TeleportResult.Failure> teleport(
@NotNull Entity teleportee, @NotNull Entity teleportee,
@Nullable ParsedDestination<?> destination) { @Nullable DestinationInstance<?, ?> destination) {
return teleport(null, teleportee, destination); return teleport(null, teleportee, destination);
} }
public AsyncAttempt<Void, TeleportResult.Failure> teleport( public AsyncAttempt<Void, TeleportResult.Failure> teleport(
@Nullable CommandSender teleporter, @Nullable CommandSender teleporter,
@NotNull Entity teleportee, @NotNull Entity teleportee,
@Nullable ParsedDestination<?> destination) { @Nullable DestinationInstance<?, ?> destination) {
if (destination == null) { if (destination == null) {
return AsyncAttempt.failure(TeleportResult.Failure.NULL_DESTINATION); 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( public <T extends Entity> Async<List<Attempt<Void, TeleportResult.Failure>>> teleport(
@ -139,15 +143,15 @@ public class AsyncSafetyTeleporter {
private AsyncAttempt<Void, TeleportResult.Failure> doAsyncTeleport( private AsyncAttempt<Void, TeleportResult.Failure> doAsyncTeleport(
@NotNull Entity teleportee, @NotNull Entity teleportee,
@NotNull Location location, @NotNull Location location,
boolean shouldAddToQueue) { boolean shouldRemoveFromQueue) {
return AsyncAttempt.of(PaperLib.teleportAsync(teleportee, location), exception -> { return AsyncAttempt.of(PaperLib.teleportAsync(teleportee, location), exception -> {
Logging.warning("Failed to teleport %s to %s: %s", Logging.warning("Failed to teleport %s to %s: %s",
teleportee.getName(), location, exception.getMessage()); teleportee.getName(), location, exception.getMessage());
return Attempt.failure(TeleportResult.Failure.TELEPORT_FAILED_EXCEPTION); return Attempt.failure(TeleportResult.Failure.TELEPORT_FAILED_EXCEPTION);
}).mapAttempt(result -> { }).mapAttempt(success -> {
Logging.finer("Teleported async %s to %s", teleportee.getName(), location); Logging.finer("Teleported async %s to %s", teleportee.getName(), location);
if (result) { if (success) {
if (shouldAddToQueue) { if (shouldRemoveFromQueue) {
teleportQueue.popFromQueue(teleportee.getName()); teleportQueue.popFromQueue(teleportee.getName());
} }
return Attempt.success(null); 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.TestWithMockBukkit
import org.mvplugins.multiverse.core.anchor.AnchorManager import org.mvplugins.multiverse.core.anchor.AnchorManager
import org.mvplugins.multiverse.core.api.BlockSafety 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.api.LocationManipulation
import org.mvplugins.multiverse.core.commandtools.MVCommandManager import org.mvplugins.multiverse.core.commandtools.MVCommandManager
import org.mvplugins.multiverse.core.commandtools.MultiverseCommand 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.teleportation.TeleportQueue
import org.mvplugins.multiverse.core.utils.metrics.MetricsConfigurator import org.mvplugins.multiverse.core.utils.metrics.MetricsConfigurator
import org.mvplugins.multiverse.core.world.WorldManager import org.mvplugins.multiverse.core.world.WorldManager
import org.mvplugins.multiverse.core.world.config.WorldsConfigManager
import kotlin.test.* import kotlin.test.*
class InjectionTest : TestWithMockBukkit() { class InjectionTest : TestWithMockBukkit() {
@ -110,7 +109,6 @@ class InjectionTest : TestWithMockBukkit() {
@Test @Test
fun `Destinations are available as services`() { fun `Destinations are available as services`() {
val destinations = serviceLocator.getAllActiveServices(Destination::class.java) 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) assertEquals(6, destinations.size)
} }