Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,8 @@ private ContentFilter parseContentFilter(BukkitCommandExecutionContext context)
}

return destinationsProvider.parseDestination(context.getSender(), destination)
.getOrThrow(failure -> MVInvalidCommandArgument.of(failure.getFailureMessage()));
.getOrThrow(failure ->
new InvalidCommandArgument(failure.getFailureMessage().formatted(context.getIssuer())));
}

private GameRule<?> parseGameRule(BukkitCommandExecutionContext context) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,12 @@
import org.mvplugins.multiverse.core.config.CoreConfig;
import org.mvplugins.multiverse.core.destination.Destination;
import org.mvplugins.multiverse.core.destination.DestinationSuggestionPacket;
import org.mvplugins.multiverse.core.exceptions.utils.position.PositionParseException;
import org.mvplugins.multiverse.core.locale.MVCorei18n;
import org.mvplugins.multiverse.core.utils.REPatterns;
import org.mvplugins.multiverse.core.utils.position.EntityPosition;
import org.mvplugins.multiverse.core.utils.position.PositionNumber;
import org.mvplugins.multiverse.core.utils.position.VectorPosition;
import org.mvplugins.multiverse.core.utils.result.Attempt;
import org.mvplugins.multiverse.core.utils.result.FailureReason;
import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld;
Expand Down Expand Up @@ -62,7 +66,11 @@ public ExactDestination(CoreConfig config, WorldManager worldManager, WorldEntry
* @return A new {@link ExactDestinationInstance}
*/
public @NotNull ExactDestinationInstance fromLocation(@NotNull Location location) {
return new ExactDestinationInstance(this, new UnloadedWorldLocation(location));
return new ExactDestinationInstance(
this,
location.getWorld().getName(),
EntityPosition.ofLocation(location)
);
}

/**
Expand All @@ -73,54 +81,38 @@ public ExactDestination(CoreConfig config, WorldManager worldManager, WorldEntry
@NotNull CommandSender sender,
@NotNull String destinationParams
) {
String[] items = REPatterns.COLON.split(destinationParams);
String[] items = REPatterns.COLON.split(destinationParams, 2);
if (items.length < 2) {
if (items[0].equals("@here")) {
return getLocationFromSender(sender)
.map(location -> Attempt.<ExactDestinationInstance, InstanceFailureReason>success(
new ExactDestinationInstance(this, new UnloadedWorldLocation(location))
new ExactDestinationInstance(
this,
location.getWorld().getName(),
EntityPosition.ofLocation(location)
)
))
.getOrElse(() -> Attempt.failure(InstanceFailureReason.INVALID_COORDINATES_FORMAT)); // todo: specific failure reason for this case
}
return Attempt.failure(InstanceFailureReason.INVALID_FORMAT);
}

String worldName = items[0];
String coordinates = items[1];
String[] coordinatesParams = REPatterns.COMMA.split(coordinates);
if (coordinatesParams.length != 3) {
return Attempt.failure(InstanceFailureReason.INVALID_COORDINATES_FORMAT);
}
String positionStr = items[1];

World world = getLoadedMultiverseWorld(worldName).flatMap(LoadedMultiverseWorld::getBukkitWorld).getOrNull();
if (world == null) {
return Attempt.failure(InstanceFailureReason.WORLD_NOT_FOUND, Replace.WORLD.with(worldName));
}

UnloadedWorldLocation location;
EntityPosition position;
try {
location = new UnloadedWorldLocation(
world,
Double.parseDouble(coordinatesParams[0]),
Double.parseDouble(coordinatesParams[1]),
Double.parseDouble(coordinatesParams[2])
);
} catch (NumberFormatException e) {
position = EntityPosition.fromString(positionStr);
} catch (PositionParseException e) {
return Attempt.failure(InstanceFailureReason.INVALID_NUMBER_FORMAT, Replace.ERROR.with(e));
}

if (items.length == 4) {
String pitch = items[2];
String yaw = items[3];
try {
location.setPitch(Float.parseFloat(pitch));
location.setYaw(Float.parseFloat(yaw));
} catch (NumberFormatException e) {
return Attempt.failure(InstanceFailureReason.INVALID_NUMBER_FORMAT, Replace.ERROR.with(e));
}
}

return Attempt.success(new ExactDestinationInstance(this, location));
return Attempt.success(new ExactDestinationInstance(this, worldName, position));
}

//TODO: Extract to a world finder class
Expand Down
Original file line number Diff line number Diff line change
@@ -1,40 +1,52 @@
package org.mvplugins.multiverse.core.destination.core;

import io.vavr.control.Option;
import org.bukkit.Bukkit;
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.mvplugins.multiverse.core.destination.DestinationInstance;
import org.mvplugins.multiverse.core.utils.position.EntityPosition;
import org.mvplugins.multiverse.core.utils.position.VectorPosition;
import org.mvplugins.multiverse.core.world.location.UnloadedWorldLocation;

/**
* Destination instance implementation for the {@link ExactDestination}.
*/
public final class ExactDestinationInstance extends DestinationInstance<ExactDestinationInstance, ExactDestination> {
private final UnloadedWorldLocation location;
private final String worldName;
private final EntityPosition position;

/**
* Constructor.
*
* @param location The location to teleport to.
* @param destination The parent destination.
* @param worldName The name of the world.
* @param position The position in the world.
*/
ExactDestinationInstance(@NotNull ExactDestination destination, @NotNull UnloadedWorldLocation location) {
ExactDestinationInstance(@NotNull ExactDestination destination,
@NotNull String worldName,
@NotNull EntityPosition position) {
super(destination);
this.location = location;
this.worldName = worldName;
this.position = position;
}

/**
* {@inheritDoc}
*/
@Override
public @NotNull Option<Location> getLocation(@NotNull Entity teleportee) {
if (location.getWorld() == null) {
World world = Bukkit.getWorld(worldName);
if (world == null) {
return Option.none();
}
return Option.of(location.toBukkitLocation());
Location destinationLocation = position.toBukkitLocation(teleportee.getLocation());
destinationLocation.setWorld(world);
return Option.of(destinationLocation);
}

/**
Expand All @@ -58,15 +70,14 @@ public boolean checkTeleportSafety() {
*/
@Override
public @NotNull Option<String> getFinerPermissionSuffix() {
return Option.of(location.getWorld()).map(World::getName);
return Option.of(worldName);
}

/**
* {@inheritDoc}
*/
@Override
public @NotNull String serialise() {
return location.getWorldName() + ":" + location.getX() + "," + location.getY()
+ "," + location.getZ() + ":" + location.getPitch() + ":" + location.getYaw();
return worldName + ":" + position.toString();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package org.mvplugins.multiverse.core.exceptions.utils.position;

import org.jetbrains.annotations.Nullable;
import org.mvplugins.multiverse.core.exceptions.MultiverseException;
import org.mvplugins.multiverse.core.locale.message.Message;

public class PositionParseException extends MultiverseException {
public PositionParseException(String message) {
super(message);
}

public PositionParseException(@Nullable Message message) {
super(message);
}

public PositionParseException(@Nullable String message, @Nullable Throwable cause) {
super(message, cause);
}

public PositionParseException(@Nullable Message message, @Nullable Throwable cause) {
super(message, cause);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,11 @@ public enum MVCorei18n implements MessageKeyProvider {
EXCEPTION_MULTIVERSEWORLD_UNLOADPLAYERSINWORLD,
EXCEPTION_MULTIVERSEWORLD_UNLOADERROR,

// multiverse position parse exception
EXCEPTION_POSITIONPARSE_INVALIDDIRECTION,
EXCEPTION_POSITIONPARSE_INVALIDCOORDINATES,
EXCEPTION_POSITIONPARSE_INVALIDNUMBER,

// generic
GENERIC_SUCCESS,
GENERIC_FAILURE,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
package org.mvplugins.multiverse.core.utils.position;

Check warning on line 1 in src/main/java/org/mvplugins/multiverse/core/utils/position/EntityPosition.java

View workflow job for this annotation

GitHub Actions / checkstyle / checkstyle

[checkstyle] reported by reviewdog 🐶 Missing package-info.java file. Raw Output: /github/workspace/./src/main/java/org/mvplugins/multiverse/core/utils/position/EntityPosition.java:1:0: warning: Missing package-info.java file. (com.puppycrawl.tools.checkstyle.checks.javadoc.JavadocPackageCheck)

import org.bukkit.Location;
import org.jetbrains.annotations.ApiStatus;
import org.mvplugins.multiverse.core.exceptions.utils.position.PositionParseException;

Check warning on line 5 in src/main/java/org/mvplugins/multiverse/core/utils/position/EntityPosition.java

View workflow job for this annotation

GitHub Actions / checkstyle / checkstyle

[checkstyle] reported by reviewdog 🐶 'org.mvplugins.multiverse.core.exceptions.utils.position.PositionParseException' should be separated from previous imports. Raw Output: /github/workspace/./src/main/java/org/mvplugins/multiverse/core/utils/position/EntityPosition.java:5:1: warning: 'org.mvplugins.multiverse.core.exceptions.utils.position.PositionParseException' should be separated from previous imports. (com.puppycrawl.tools.checkstyle.checks.imports.ImportOrderCheck)
import org.mvplugins.multiverse.core.utils.REPatterns;

/**
* Represents a position for an entity in 3D space, including both coordinates and facing direction.
*
* @since 5.3
*/
@ApiStatus.AvailableSince("5.3")
public class EntityPosition {

/**
* Creates an EntityPosition with absolute coordinates and direction.
*
* @param x The absolute X coordinate.
* @param y The absolute Y coordinate.
* @param z The absolute Z coordinate.
* @param pitch The absolute pitch (vertical angle).
* @param yaw The absolute yaw (horizontal angle).
* @return A new EntityPosition instance with the specified absolute coordinates and direction.
*
* @since 5.3
*/
@ApiStatus.AvailableSince("5.3")
public static EntityPosition ofAbsolute(double x, double y, double z, double pitch, double yaw) {

Check warning on line 29 in src/main/java/org/mvplugins/multiverse/core/utils/position/EntityPosition.java

View workflow job for this annotation

GitHub Actions / checkstyle / checkstyle

[checkstyle] reported by reviewdog 🐶 More than 4 parameters (found 5). Raw Output: /github/workspace/./src/main/java/org/mvplugins/multiverse/core/utils/position/EntityPosition.java:29:34: info: More than 4 parameters (found 5). (ConsiderLessMethodParameters)
return new EntityPosition(
VectorPosition.ofAbsolute(x, y, z),
FaceDirection.ofAbsolute(pitch, yaw)
);

Check warning on line 33 in src/main/java/org/mvplugins/multiverse/core/utils/position/EntityPosition.java

View workflow job for this annotation

GitHub Actions / checkstyle / checkstyle

[checkstyle] reported by reviewdog 🐶 ')' should be on the previous line. Raw Output: /github/workspace/./src/main/java/org/mvplugins/multiverse/core/utils/position/EntityPosition.java:33:9: warning: ')' should be on the previous line. (SeparatorWrapEol)
}

/**
* Creates an EntityPosition from a Bukkit Location with absolute coordinates and direction.
*
* @param location The Bukkit Location to convert.
* @return A new EntityPosition instance representing the given location.
*
* @since 5.3
*/
@ApiStatus.AvailableSince("5.3")
public static EntityPosition ofLocation(Location location) {
return new EntityPosition(VectorPosition.ofLocation(location), FaceDirection.ofLocation(location));
}

/**
* Parses an EntityPosition from a string representation.
* The expected format is "&lt;x&gt;,&lt;y&gt;,&lt;z&gt;:&lt;pitch&gt;:&lt;yaw&gt;" for absolute coordinates and direction,

Check warning on line 51 in src/main/java/org/mvplugins/multiverse/core/utils/position/EntityPosition.java

View workflow job for this annotation

GitHub Actions / checkstyle / checkstyle

[checkstyle] reported by reviewdog 🐶 Line is longer than 120 characters (found 127). Raw Output: /github/workspace/./src/main/java/org/mvplugins/multiverse/core/utils/position/EntityPosition.java:51:0: warning: Line is longer than 120 characters (found 127). (com.puppycrawl.tools.checkstyle.checks.sizes.LineLengthCheck)
* or "&lt;x&gt;,&lt;y&gt;,&lt;z&gt;" for absolute coordinates with default direction (0 pitch, 0 yaw).
* <br>
* Relative coordinates and direction can be specified using the '~' prefix, e.g., "~10,~,~-10:0:90".
*
* @param positionStr The string representation of the position.
* @return A new EntityPosition instance parsed from the string.
* @throws PositionParseException If the string format is invalid.
*
* @since 5.3
*/
@ApiStatus.AvailableSince("5.3")
public static EntityPosition fromString(String positionStr) throws PositionParseException {
String[] parts = REPatterns.COLON.split(positionStr, 2);
return parts.length == 2
? new EntityPosition(VectorPosition.fromString(parts[0]), FaceDirection.fromString(parts[1]))
: new EntityPosition(VectorPosition.fromString(parts[0]), FaceDirection.ofAbsolute(0, 0));
}

private final VectorPosition vector;
private final FaceDirection direction;

/**
* Creates a new EntityPosition with the specified vector and direction.
*
* @param vector The vector position (coordinates).
* @param direction The facing direction (pitch and yaw).
*
* @since 5.3
*/
@ApiStatus.AvailableSince("5.3")
public EntityPosition(VectorPosition vector, FaceDirection direction) {
this.vector = vector;
this.direction = direction;
}

/**
* Gets the vector position (coordinates).
*
* @return The vector position.
*
* @since 5.3
*/
@ApiStatus.AvailableSince("5.3")
public VectorPosition getVector() {
return vector;
}

/**
* Gets the facing direction (pitch and yaw).
*
* @return The facing direction.
*
* @since 5.3
*/
@ApiStatus.AvailableSince("5.3")
public FaceDirection getDirection() {
return direction;
}

/**
* Augments a given Bukkit Location by applying the relative components of this EntityPosition.
* This modifies the provided Location in place.
*
* @param base The base Bukkit Location to augment and offset for relative positioning as required.
*
* @since 5.3
*/
@ApiStatus.AvailableSince("5.3")
public void augmentBukkitLocation(Location base) {
vector.augmentBukkitLocation(base);
direction.augmentBukkitLocation(base);
}

/**
* Converts this EntityPosition to a new Bukkit Location based on a given base Location.
* This does not modify the base Location, but returns a new Location instance.
*
* @param base The base Bukkit Location to use as a reference for relative positioning as required.
* @return A new Bukkit Location representing this EntityPosition.
*
* @since 5.3
*/
@ApiStatus.AvailableSince("5.3")
public Location toBukkitLocation(Location base) {
return new Location(
base.getWorld(),
vector.getX().getValue(base.getX()),
vector.getY().getValue(base.getY()),
vector.getZ().getValue(base.getZ()),
(float) direction.getYaw().getValue(base.getYaw()),
(float) direction.getPitch().getValue(base.getPitch())
);

Check warning on line 143 in src/main/java/org/mvplugins/multiverse/core/utils/position/EntityPosition.java

View workflow job for this annotation

GitHub Actions / checkstyle / checkstyle

[checkstyle] reported by reviewdog 🐶 ')' should be on the previous line. Raw Output: /github/workspace/./src/main/java/org/mvplugins/multiverse/core/utils/position/EntityPosition.java:143:9: warning: ')' should be on the previous line. (SeparatorWrapEol)
}

@Override

Check warning on line 146 in src/main/java/org/mvplugins/multiverse/core/utils/position/EntityPosition.java

View workflow job for this annotation

GitHub Actions / checkstyle / checkstyle

[checkstyle] reported by reviewdog 🐶 Class 'EntityPosition' looks like designed for extension (can be subclassed), but the method 'toString' does not have javadoc that explains how to do that safely. If class is not designed for extension consider making the class 'EntityPosition' final or making the method 'toString' static/final/abstract/empty, or adding allowed annotation for the method. Raw Output: /github/workspace/./src/main/java/org/mvplugins/multiverse/core/utils/position/EntityPosition.java:146:5: info: Class 'EntityPosition' looks like designed for extension (can be subclassed), but the method 'toString' does not have javadoc that explains how to do that safely. If class is not designed for extension consider making the class 'EntityPosition' final or making the method 'toString' static/final/abstract/empty, or adding allowed annotation for the method. (com.puppycrawl.tools.checkstyle.checks.design.DesignForExtensionCheck)
public String toString() {
return vector + ":" + direction;
}
}
Loading
Loading