mirror of
https://github.com/Multiverse/Multiverse-Core.git
synced 2024-11-22 10:36:06 +01:00
Refactor to remove need for ParsedDestination and add destination tests
This commit is contained in:
parent
1eeb265f4e
commit
d3f6b11fe5
@ -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;
|
||||
|
@ -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();
|
||||
}
|
@ -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());
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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) {
|
||||
|
@ -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();
|
||||
}
|
@ -0,0 +1,81 @@
|
||||
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 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();
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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;
|
||||
|
||||
@ -53,7 +53,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 +81,7 @@ public class ExactDestination implements Destination<ExactDestinationInstance> {
|
||||
}
|
||||
}
|
||||
|
||||
return new ExactDestinationInstance(location);
|
||||
return new ExactDestinationInstance(this, location);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -91,12 +91,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;
|
||||
}
|
||||
}
|
||||
|
@ -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,32 @@ public class ExactDestinationInstance 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) {
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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 world The world to teleport to.
|
||||
* @param direction The direction to face.
|
||||
* @param yaw The yaw to face.
|
||||
* @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());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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() {
|
||||
|
@ -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;
|
||||
|
@ -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) {
|
||||
if (destination == null) {
|
||||
return AsyncAttempt.failure(TeleportResult.Failure.NULL_DESTINATION);
|
||||
}
|
||||
return teleport(teleporter, teleportee, destination.getLocation(teleportee));
|
||||
@Nullable DestinationInstance<?, ?> destination) {
|
||||
if (destination == null) {
|
||||
return AsyncAttempt.failure(TeleportResult.Failure.NULL_DESTINATION);
|
||||
}
|
||||
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);
|
||||
|
@ -0,0 +1,111 @@
|
||||
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 `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)
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user