Merge pull request #3019 from Multiverse/ben/mv5/teleporter
Ben/mv5/teleporter
This commit is contained in:
commit
01cfcc5684
|
@ -4,6 +4,7 @@ import org.bukkit.Location;
|
||||||
import org.bukkit.World;
|
import org.bukkit.World;
|
||||||
import org.bukkit.entity.Minecart;
|
import org.bukkit.entity.Minecart;
|
||||||
import org.bukkit.entity.Vehicle;
|
import org.bukkit.entity.Vehicle;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.jvnet.hk2.annotations.Contract;
|
import org.jvnet.hk2.annotations.Contract;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -37,8 +38,35 @@ public interface BlockSafety {
|
||||||
*/
|
*/
|
||||||
boolean playerCanSpawnHereSafely(Location l);
|
boolean playerCanSpawnHereSafely(Location l);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds a portal-block next to the specified {@link Location}.
|
||||||
|
*
|
||||||
|
* @param location The {@link Location}
|
||||||
|
* @return The next portal-block's {@link Location}.
|
||||||
|
*/
|
||||||
|
Location findPortalBlockNextTo(Location location);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the next safe location around the given location.
|
||||||
|
*
|
||||||
|
* @param location A {@link Location}.
|
||||||
|
* @return A safe {@link Location}.
|
||||||
|
*/
|
||||||
|
@Nullable Location getSafeLocation(Location location);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the next safe location around the given location.
|
||||||
|
*
|
||||||
|
* @param location A {@link Location}.
|
||||||
|
* @param tolerance The tolerance of how far up and down to search.
|
||||||
|
* @param radius The radius around given location to search.
|
||||||
|
* @return A safe {@link Location}.
|
||||||
|
*/
|
||||||
|
@Nullable Location getSafeLocation(Location location, int tolerance, int radius);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets a safe bed spawn location OR null if the bed is invalid.
|
* Gets a safe bed spawn location OR null if the bed is invalid.
|
||||||
|
*
|
||||||
* @param l The location of the bead head (block with the pillow on it).
|
* @param l The location of the bead head (block with the pillow on it).
|
||||||
* @return Safe location around the bed or null if no location was found.
|
* @return Safe location around the bed or null if no location was found.
|
||||||
*/
|
*/
|
||||||
|
@ -46,6 +74,7 @@ public interface BlockSafety {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the location of the top block at the specified {@link Location}.
|
* Gets the location of the top block at the specified {@link Location}.
|
||||||
|
*
|
||||||
* @param l Any {@link Location}.
|
* @param l Any {@link Location}.
|
||||||
* @return The {@link Location} of the top-block.
|
* @return The {@link Location} of the top-block.
|
||||||
*/
|
*/
|
||||||
|
@ -53,6 +82,7 @@ public interface BlockSafety {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the location of the top block at the specified {@link Location}.
|
* Gets the location of the top block at the specified {@link Location}.
|
||||||
|
*
|
||||||
* @param l Any {@link Location}.
|
* @param l Any {@link Location}.
|
||||||
* @return The {@link Location} of the top-block.
|
* @return The {@link Location} of the top-block.
|
||||||
*/
|
*/
|
||||||
|
@ -60,6 +90,7 @@ public interface BlockSafety {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if an entity would be on track at the specified {@link Location}.
|
* Checks if an entity would be on track at the specified {@link Location}.
|
||||||
|
*
|
||||||
* @param l The {@link Location}.
|
* @param l The {@link Location}.
|
||||||
* @return True if an entity would be on tracks at the specified {@link Location}.
|
* @return True if an entity would be on tracks at the specified {@link Location}.
|
||||||
*/
|
*/
|
||||||
|
@ -67,6 +98,7 @@ public interface BlockSafety {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if the specified {@link Minecart} can spawn safely.
|
* Checks if the specified {@link Minecart} can spawn safely.
|
||||||
|
*
|
||||||
* @param cart The {@link Minecart}.
|
* @param cart The {@link Minecart}.
|
||||||
* @return True if the minecart can spawn safely.
|
* @return True if the minecart can spawn safely.
|
||||||
*/
|
*/
|
||||||
|
@ -74,6 +106,7 @@ public interface BlockSafety {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if the specified {@link Vehicle} can spawn safely.
|
* Checks if the specified {@link Vehicle} can spawn safely.
|
||||||
|
*
|
||||||
* @param vehicle The {@link Vehicle}.
|
* @param vehicle The {@link Vehicle}.
|
||||||
* @return True if the vehicle can spawn safely.
|
* @return True if the vehicle can spawn safely.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -45,14 +45,4 @@ public interface Destination<T extends DestinationInstance> {
|
||||||
* @return True if the SafeTeleporter will be used, false if not.
|
* @return True if the SafeTeleporter will be used, false if not.
|
||||||
*/
|
*/
|
||||||
boolean checkTeleportSafety();
|
boolean checkTeleportSafety();
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the teleporter to use for this destination.
|
|
||||||
*
|
|
||||||
* <p>By default, Multiverse will automatically use SafeTeleporter. If you want to use a different teleporter, you can
|
|
||||||
* override this method.</p>
|
|
||||||
*
|
|
||||||
* @return The custom teleporter to use for this destination. Return null to use the default teleporter.
|
|
||||||
*/
|
|
||||||
@Nullable Teleporter getTeleporter();
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,88 +0,0 @@
|
||||||
package org.mvplugins.multiverse.core.api;
|
|
||||||
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
|
||||||
|
|
||||||
import co.aikar.commands.BukkitCommandIssuer;
|
|
||||||
import org.bukkit.Location;
|
|
||||||
import org.bukkit.command.CommandSender;
|
|
||||||
import org.bukkit.entity.Entity;
|
|
||||||
import org.jvnet.hk2.annotations.Contract;
|
|
||||||
|
|
||||||
import org.mvplugins.multiverse.core.destination.ParsedDestination;
|
|
||||||
import org.mvplugins.multiverse.core.teleportation.TeleportResult;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Used to safely teleport people.
|
|
||||||
*/
|
|
||||||
@Contract
|
|
||||||
public interface SafeTTeleporter extends Teleporter {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the next safe location around the given location.
|
|
||||||
* @param l A {@link Location}.
|
|
||||||
* @return A safe {@link Location}.
|
|
||||||
*/
|
|
||||||
Location getSafeLocation(Location l);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the next safe location around the given location.
|
|
||||||
* @param l A {@link Location}.
|
|
||||||
* @param tolerance The tolerance.
|
|
||||||
* @param radius The radius.
|
|
||||||
* @return A safe {@link Location}.
|
|
||||||
*/
|
|
||||||
Location getSafeLocation(Location l, int tolerance, int radius);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Safely teleport the entity to the MVDestination. This will perform checks to see if the place is safe, and if
|
|
||||||
* it's not, will adjust the final destination accordingly.
|
|
||||||
*
|
|
||||||
* @param teleporter Person who performed the teleport command.
|
|
||||||
* @param teleportee Entity to teleport
|
|
||||||
* @param destination Destination to teleport them to
|
|
||||||
* @return true for success, false for failure
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
TeleportResult safelyTeleport(BukkitCommandIssuer teleporter, Entity teleportee, ParsedDestination<?> destination);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Safely teleport the entity to the MVDestination. This will perform checks to see if the place is safe, and if
|
|
||||||
* it's not, will adjust the final destination accordingly.
|
|
||||||
*
|
|
||||||
* @param teleporter Person who performed the teleport command.
|
|
||||||
* @param teleportee Entity to teleport
|
|
||||||
* @param destination Destination to teleport them to
|
|
||||||
* @return true for success, false for failure
|
|
||||||
*/
|
|
||||||
CompletableFuture<TeleportResult> safelyTeleportAsync(BukkitCommandIssuer teleporter, Entity teleportee, ParsedDestination<?> destination);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Safely teleport the entity to the Location. This may perform checks to
|
|
||||||
* see if the place is safe, and if
|
|
||||||
* it's not, will adjust the final destination accordingly.
|
|
||||||
*
|
|
||||||
* @param teleporter Person who issued the teleport command.
|
|
||||||
* @param teleportee Entity to teleport.
|
|
||||||
* @param location Location to teleport them to.
|
|
||||||
* @param safely Should the destination be checked for safety before teleport?
|
|
||||||
* @return true for success, false for failure.
|
|
||||||
*/
|
|
||||||
TeleportResult safelyTeleport(CommandSender teleporter, Entity teleportee, Location location,
|
|
||||||
boolean safely);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a safe location for the entity to spawn at.
|
|
||||||
*
|
|
||||||
* @param entity The entity to spawn
|
|
||||||
* @param destination The MVDestination to take the entity to.
|
|
||||||
* @return A new location to spawn the entity at.
|
|
||||||
*/
|
|
||||||
Location getSafeLocation(Entity entity, DestinationInstance destination);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Finds a portal-block next to the specified {@link Location}.
|
|
||||||
* @param l The {@link Location}
|
|
||||||
* @return The next portal-block's {@link Location}.
|
|
||||||
*/
|
|
||||||
Location findPortalBlockNextTo(Location l);
|
|
||||||
}
|
|
|
@ -1,34 +0,0 @@
|
||||||
package org.mvplugins.multiverse.core.api;
|
|
||||||
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
|
||||||
|
|
||||||
import co.aikar.commands.BukkitCommandIssuer;
|
|
||||||
import org.bukkit.entity.Entity;
|
|
||||||
import org.jvnet.hk2.annotations.Contract;
|
|
||||||
|
|
||||||
import org.mvplugins.multiverse.core.destination.ParsedDestination;
|
|
||||||
import org.mvplugins.multiverse.core.teleportation.TeleportResult;
|
|
||||||
|
|
||||||
@Contract
|
|
||||||
public interface Teleporter {
|
|
||||||
/**
|
|
||||||
* Teleport the entity to the Multiverse Destination.
|
|
||||||
*
|
|
||||||
* @param teleporter Person who performed the teleport command.
|
|
||||||
* @param teleportee Entity to teleport
|
|
||||||
* @param destination Destination to teleport them to
|
|
||||||
* @return true for success, false for failure
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
TeleportResult teleport(BukkitCommandIssuer teleporter, Entity teleportee, ParsedDestination<?> destination);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Teleport the entity to the Multiverse Destination.
|
|
||||||
*
|
|
||||||
* @param teleporter Person who performed the teleport command.
|
|
||||||
* @param teleportee Entity to teleport
|
|
||||||
* @param destination Destination to teleport them to
|
|
||||||
* @return true for success, false for failure
|
|
||||||
*/
|
|
||||||
CompletableFuture<TeleportResult> teleportAsync(BukkitCommandIssuer teleporter, Entity teleportee, ParsedDestination<?> destination);
|
|
||||||
}
|
|
|
@ -49,6 +49,6 @@ class CheckCommand extends MultiverseCommand {
|
||||||
"{player}", player.getName(),
|
"{player}", player.getName(),
|
||||||
"{destination}", destination.toString());
|
"{destination}", destination.toString());
|
||||||
// TODO: More detailed output on permissions required.
|
// TODO: More detailed output on permissions required.
|
||||||
this.destinationsProvider.checkTeleportPermissions(issuer, player, destination);
|
// this.destinationsProvider.checkTeleportPermissions(issuer, player, destination);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,14 @@
|
||||||
package org.mvplugins.multiverse.core.commands;
|
package org.mvplugins.multiverse.core.commands;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
|
||||||
import co.aikar.commands.MessageType;
|
import co.aikar.commands.MessageType;
|
||||||
import co.aikar.commands.annotation.CommandAlias;
|
import co.aikar.commands.annotation.CommandAlias;
|
||||||
import co.aikar.commands.annotation.CommandCompletion;
|
import co.aikar.commands.annotation.CommandCompletion;
|
||||||
import co.aikar.commands.annotation.CommandPermission;
|
import co.aikar.commands.annotation.CommandPermission;
|
||||||
import co.aikar.commands.annotation.Conditions;
|
import co.aikar.commands.annotation.Conditions;
|
||||||
import co.aikar.commands.annotation.Description;
|
import co.aikar.commands.annotation.Description;
|
||||||
|
import co.aikar.commands.annotation.Optional;
|
||||||
import co.aikar.commands.annotation.Single;
|
import co.aikar.commands.annotation.Single;
|
||||||
import co.aikar.commands.annotation.Subcommand;
|
import co.aikar.commands.annotation.Subcommand;
|
||||||
import co.aikar.commands.annotation.Syntax;
|
import co.aikar.commands.annotation.Syntax;
|
||||||
|
@ -17,48 +20,82 @@ 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.commandtools.MultiverseCommand;
|
||||||
|
import org.mvplugins.multiverse.core.commandtools.flags.CommandFlag;
|
||||||
|
import org.mvplugins.multiverse.core.commandtools.flags.ParsedCommandFlags;
|
||||||
import org.mvplugins.multiverse.core.commandtools.queue.QueuedCommand;
|
import org.mvplugins.multiverse.core.commandtools.queue.QueuedCommand;
|
||||||
import org.mvplugins.multiverse.core.utils.MVCorei18n;
|
import org.mvplugins.multiverse.core.utils.MVCorei18n;
|
||||||
|
import org.mvplugins.multiverse.core.utils.result.Async;
|
||||||
|
import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld;
|
||||||
import org.mvplugins.multiverse.core.world.WorldManager;
|
import org.mvplugins.multiverse.core.world.WorldManager;
|
||||||
|
import org.mvplugins.multiverse.core.world.helpers.PlayerWorldTeleporter;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
@CommandAlias("mv")
|
@CommandAlias("mv")
|
||||||
class DeleteCommand extends MultiverseCommand {
|
class DeleteCommand extends MultiverseCommand {
|
||||||
|
|
||||||
private final WorldManager worldManager;
|
private final WorldManager worldManager;
|
||||||
|
private final PlayerWorldTeleporter playerWorldTeleporter;
|
||||||
|
|
||||||
|
private final CommandFlag REMOVE_PLAYERS_FLAG = flag(CommandFlag.builder("--remove-players")
|
||||||
|
.addAlias("-r")
|
||||||
|
.build());
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
DeleteCommand(@NotNull MVCommandManager commandManager, @NotNull WorldManager worldManager) {
|
DeleteCommand(
|
||||||
|
@NotNull MVCommandManager commandManager,
|
||||||
|
@NotNull WorldManager worldManager,
|
||||||
|
@NotNull PlayerWorldTeleporter playerWorldTeleporter) {
|
||||||
super(commandManager);
|
super(commandManager);
|
||||||
this.worldManager = worldManager;
|
this.worldManager = worldManager;
|
||||||
|
this.playerWorldTeleporter = playerWorldTeleporter;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Subcommand("delete")
|
@Subcommand("delete")
|
||||||
@CommandPermission("multiverse.core.delete")
|
@CommandPermission("multiverse.core.delete")
|
||||||
@CommandCompletion("@mvworlds:scope=both")
|
@CommandCompletion("@mvworlds:scope=loaded @flags:groupName=mvdeletecommand")
|
||||||
@Syntax("<world>")
|
@Syntax("<world>")
|
||||||
@Description("{@@mv-core.delete.description}")
|
@Description("{@@mv-core.delete.description}")
|
||||||
void onDeleteCommand(
|
void onDeleteCommand(
|
||||||
MVCommandIssuer issuer,
|
MVCommandIssuer issuer,
|
||||||
|
|
||||||
@Single
|
@Single
|
||||||
@Conditions("worldname:scope=both")
|
@Conditions("worldname:scope=loaded")
|
||||||
@Syntax("<world>")
|
@Syntax("<world>")
|
||||||
@Description("The world you want to delete.")
|
@Description("The world you want to delete.")
|
||||||
String worldName) {
|
LoadedMultiverseWorld world,
|
||||||
|
|
||||||
|
@Optional
|
||||||
|
@Syntax("[--remove-players]")
|
||||||
|
@Description("")
|
||||||
|
String[] flags) {
|
||||||
|
ParsedCommandFlags parsedFlags = parseFlags(flags);
|
||||||
|
|
||||||
this.commandManager.getCommandQueueManager().addToQueue(new QueuedCommand(
|
this.commandManager.getCommandQueueManager().addToQueue(new QueuedCommand(
|
||||||
issuer.getIssuer(),
|
issuer.getIssuer(),
|
||||||
() -> {
|
() -> {
|
||||||
issuer.sendInfo(MVCorei18n.DELETE_DELETING, "{world}", worldName);
|
runDeleteCommand(issuer, world, parsedFlags);
|
||||||
worldManager.deleteWorld(worldName)
|
}, this.commandManager.formatMessage(issuer, MessageType.INFO, MVCorei18n.DELETE_PROMPT,
|
||||||
.onSuccess(deletedWorldName -> {
|
"{world}", world.getName())));
|
||||||
Logging.fine("World delete success: " + deletedWorldName);
|
}
|
||||||
issuer.sendInfo(MVCorei18n.DELETE_SUCCESS, "{world}", deletedWorldName);
|
|
||||||
}).onFailure(failure -> {
|
private void runDeleteCommand(MVCommandIssuer issuer, LoadedMultiverseWorld world, ParsedCommandFlags parsedFlags) {
|
||||||
Logging.fine("World delete failure: " + failure);
|
issuer.sendInfo(MVCorei18n.DELETE_DELETING, "{world}", world.getName());
|
||||||
issuer.sendError(failure.getFailureMessage());
|
|
||||||
});
|
var future = parsedFlags.hasFlag(REMOVE_PLAYERS_FLAG)
|
||||||
}, this.commandManager.formatMessage(
|
? playerWorldTeleporter.removeFromWorld(world)
|
||||||
issuer, MessageType.INFO, MVCorei18n.DELETE_PROMPT, "{world}", worldName)));
|
: Async.completedFuture(Collections.emptyList());
|
||||||
|
|
||||||
|
future.thenRun(() -> doWorldDeleting(issuer, world));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doWorldDeleting(MVCommandIssuer issuer, LoadedMultiverseWorld world) {
|
||||||
|
worldManager.deleteWorld(world)
|
||||||
|
.onSuccess(deletedWorldName -> {
|
||||||
|
Logging.fine("World delete success: " + deletedWorldName);
|
||||||
|
issuer.sendInfo(MVCorei18n.DELETE_SUCCESS, "{world}", deletedWorldName);
|
||||||
|
}).onFailure(failure -> {
|
||||||
|
Logging.fine("World delete failure: " + failure);
|
||||||
|
issuer.sendError(failure.getFailureMessage());
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package org.mvplugins.multiverse.core.commands;
|
package org.mvplugins.multiverse.core.commands;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
|
||||||
import co.aikar.commands.MessageType;
|
import co.aikar.commands.MessageType;
|
||||||
|
@ -13,6 +14,7 @@ import co.aikar.commands.annotation.Subcommand;
|
||||||
import co.aikar.commands.annotation.Syntax;
|
import co.aikar.commands.annotation.Syntax;
|
||||||
import com.dumptruckman.minecraft.util.Logging;
|
import com.dumptruckman.minecraft.util.Logging;
|
||||||
import jakarta.inject.Inject;
|
import jakarta.inject.Inject;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jvnet.hk2.annotations.Service;
|
import org.jvnet.hk2.annotations.Service;
|
||||||
|
|
||||||
|
@ -24,8 +26,10 @@ import org.mvplugins.multiverse.core.commandtools.flags.CommandValueFlag;
|
||||||
import org.mvplugins.multiverse.core.commandtools.flags.ParsedCommandFlags;
|
import org.mvplugins.multiverse.core.commandtools.flags.ParsedCommandFlags;
|
||||||
import org.mvplugins.multiverse.core.commandtools.queue.QueuedCommand;
|
import org.mvplugins.multiverse.core.commandtools.queue.QueuedCommand;
|
||||||
import org.mvplugins.multiverse.core.utils.MVCorei18n;
|
import org.mvplugins.multiverse.core.utils.MVCorei18n;
|
||||||
|
import org.mvplugins.multiverse.core.utils.result.Async;
|
||||||
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;
|
||||||
|
import org.mvplugins.multiverse.core.world.helpers.PlayerWorldTeleporter;
|
||||||
import org.mvplugins.multiverse.core.world.options.RegenWorldOptions;
|
import org.mvplugins.multiverse.core.world.options.RegenWorldOptions;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
|
@ -33,6 +37,7 @@ import org.mvplugins.multiverse.core.world.options.RegenWorldOptions;
|
||||||
class RegenCommand extends MultiverseCommand {
|
class RegenCommand extends MultiverseCommand {
|
||||||
|
|
||||||
private final WorldManager worldManager;
|
private final WorldManager worldManager;
|
||||||
|
private final PlayerWorldTeleporter playerWorldTeleporter;
|
||||||
|
|
||||||
private final CommandValueFlag<String> SEED_FLAG = flag(CommandValueFlag.builder("--seed", String.class)
|
private final CommandValueFlag<String> SEED_FLAG = flag(CommandValueFlag.builder("--seed", String.class)
|
||||||
.addAlias("-s")
|
.addAlias("-s")
|
||||||
|
@ -51,10 +56,18 @@ class RegenCommand extends MultiverseCommand {
|
||||||
.addAlias("-wb")
|
.addAlias("-wb")
|
||||||
.build());
|
.build());
|
||||||
|
|
||||||
|
private final CommandFlag REMOVE_PLAYERS_FLAG = flag(CommandFlag.builder("--remove-players")
|
||||||
|
.addAlias("-r")
|
||||||
|
.build());
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
RegenCommand(@NotNull MVCommandManager commandManager, @NotNull WorldManager worldManager) {
|
RegenCommand(
|
||||||
|
@NotNull MVCommandManager commandManager,
|
||||||
|
@NotNull WorldManager worldManager,
|
||||||
|
@NotNull PlayerWorldTeleporter playerWorldTeleporter) {
|
||||||
super(commandManager);
|
super(commandManager);
|
||||||
this.worldManager = worldManager;
|
this.worldManager = worldManager;
|
||||||
|
this.playerWorldTeleporter = playerWorldTeleporter;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Subcommand("regen")
|
@Subcommand("regen")
|
||||||
|
@ -77,23 +90,43 @@ class RegenCommand extends MultiverseCommand {
|
||||||
|
|
||||||
this.commandManager.getCommandQueueManager().addToQueue(new QueuedCommand(
|
this.commandManager.getCommandQueueManager().addToQueue(new QueuedCommand(
|
||||||
issuer.getIssuer(),
|
issuer.getIssuer(),
|
||||||
() -> {
|
() -> runRegenCommand(issuer, world, parsedFlags),
|
||||||
issuer.sendInfo(MVCorei18n.REGEN_REGENERATING, "{world}", world.getName());
|
|
||||||
worldManager.regenWorld(RegenWorldOptions.world(world)
|
|
||||||
.randomSeed(parsedFlags.hasFlag(SEED_FLAG))
|
|
||||||
.seed(parsedFlags.flagValue(SEED_FLAG))
|
|
||||||
.keepWorldConfig(!parsedFlags.hasFlag(RESET_WORLD_CONFIG_FLAG))
|
|
||||||
.keepGameRule(!parsedFlags.hasFlag(RESET_GAMERULES_FLAG))
|
|
||||||
.keepWorldBorder(!parsedFlags.hasFlag(RESET_WORLD_BORDER_FLAG))
|
|
||||||
).onSuccess(newWorld -> {
|
|
||||||
Logging.fine("World regen success: " + newWorld);
|
|
||||||
issuer.sendInfo(MVCorei18n.REGEN_SUCCESS, "{world}", newWorld.getName());
|
|
||||||
}).onFailure(failure -> {
|
|
||||||
Logging.fine("World regen failure: " + failure);
|
|
||||||
issuer.sendError(failure.getFailureMessage());
|
|
||||||
});
|
|
||||||
},
|
|
||||||
this.commandManager.formatMessage(
|
this.commandManager.formatMessage(
|
||||||
issuer, MessageType.INFO, MVCorei18n.REGEN_PROMPT, "{world}", world.getName())));
|
issuer, MessageType.INFO, MVCorei18n.REGEN_PROMPT, "{world}", world.getName())));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void runRegenCommand(MVCommandIssuer issuer, LoadedMultiverseWorld world, ParsedCommandFlags parsedFlags) {
|
||||||
|
issuer.sendInfo(MVCorei18n.REGEN_REGENERATING, "{world}", world.getName());
|
||||||
|
List<Player> worldPlayers = world.getPlayers().getOrElse(Collections.emptyList());
|
||||||
|
|
||||||
|
var future = parsedFlags.hasFlag(REMOVE_PLAYERS_FLAG)
|
||||||
|
? playerWorldTeleporter.removeFromWorld(world)
|
||||||
|
: Async.completedFuture(Collections.emptyList());
|
||||||
|
|
||||||
|
future.thenRun(() -> doWorldRegening(issuer, world, parsedFlags, worldPlayers));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doWorldRegening(
|
||||||
|
MVCommandIssuer issuer,
|
||||||
|
LoadedMultiverseWorld world,
|
||||||
|
ParsedCommandFlags parsedFlags,
|
||||||
|
List<Player> worldPlayers) {
|
||||||
|
RegenWorldOptions regenWorldOptions = RegenWorldOptions.world(world)
|
||||||
|
.randomSeed(parsedFlags.hasFlag(SEED_FLAG))
|
||||||
|
.seed(parsedFlags.flagValue(SEED_FLAG))
|
||||||
|
.keepWorldConfig(!parsedFlags.hasFlag(RESET_WORLD_CONFIG_FLAG))
|
||||||
|
.keepGameRule(!parsedFlags.hasFlag(RESET_GAMERULES_FLAG))
|
||||||
|
.keepWorldBorder(!parsedFlags.hasFlag(RESET_WORLD_BORDER_FLAG));
|
||||||
|
|
||||||
|
worldManager.regenWorld(regenWorldOptions).onSuccess(newWorld -> {
|
||||||
|
Logging.fine("World regen success: " + newWorld);
|
||||||
|
issuer.sendInfo(MVCorei18n.REGEN_SUCCESS, "{world}", newWorld.getName());
|
||||||
|
if (parsedFlags.hasFlag(REMOVE_PLAYERS_FLAG)) {
|
||||||
|
playerWorldTeleporter.teleportPlayersToWorld(worldPlayers, newWorld);
|
||||||
|
}
|
||||||
|
}).onFailure(failure -> {
|
||||||
|
Logging.fine("World regen failure: " + failure);
|
||||||
|
issuer.sendError(failure.getFailureMessage());
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
package org.mvplugins.multiverse.core.commands;
|
package org.mvplugins.multiverse.core.commands;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
|
||||||
import co.aikar.commands.annotation.CommandAlias;
|
import co.aikar.commands.annotation.CommandAlias;
|
||||||
import co.aikar.commands.annotation.CommandCompletion;
|
import co.aikar.commands.annotation.CommandCompletion;
|
||||||
import co.aikar.commands.annotation.CommandPermission;
|
import co.aikar.commands.annotation.CommandPermission;
|
||||||
import co.aikar.commands.annotation.Conditions;
|
import co.aikar.commands.annotation.Conditions;
|
||||||
import co.aikar.commands.annotation.Description;
|
import co.aikar.commands.annotation.Description;
|
||||||
|
import co.aikar.commands.annotation.Optional;
|
||||||
import co.aikar.commands.annotation.Single;
|
import co.aikar.commands.annotation.Single;
|
||||||
import co.aikar.commands.annotation.Subcommand;
|
import co.aikar.commands.annotation.Subcommand;
|
||||||
import co.aikar.commands.annotation.Syntax;
|
import co.aikar.commands.annotation.Syntax;
|
||||||
|
@ -16,20 +19,33 @@ 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.commandtools.MultiverseCommand;
|
||||||
|
import org.mvplugins.multiverse.core.commandtools.flags.CommandFlag;
|
||||||
|
import org.mvplugins.multiverse.core.commandtools.flags.ParsedCommandFlags;
|
||||||
import org.mvplugins.multiverse.core.utils.MVCorei18n;
|
import org.mvplugins.multiverse.core.utils.MVCorei18n;
|
||||||
|
import org.mvplugins.multiverse.core.utils.result.Async;
|
||||||
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;
|
||||||
|
import org.mvplugins.multiverse.core.world.helpers.PlayerWorldTeleporter;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
@CommandAlias("mv")
|
@CommandAlias("mv")
|
||||||
class RemoveCommand extends MultiverseCommand {
|
class RemoveCommand extends MultiverseCommand {
|
||||||
|
|
||||||
private final WorldManager worldManager;
|
private final WorldManager worldManager;
|
||||||
|
private final PlayerWorldTeleporter playerWorldTeleporter;
|
||||||
|
|
||||||
|
private final CommandFlag REMOVE_PLAYERS_FLAG = flag(CommandFlag.builder("--remove-players")
|
||||||
|
.addAlias("-r")
|
||||||
|
.build());
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
RemoveCommand(@NotNull MVCommandManager commandManager, @NotNull WorldManager worldManager) {
|
RemoveCommand(
|
||||||
|
@NotNull MVCommandManager commandManager,
|
||||||
|
@NotNull WorldManager worldManager,
|
||||||
|
@NotNull PlayerWorldTeleporter playerWorldTeleporter) {
|
||||||
super(commandManager);
|
super(commandManager);
|
||||||
this.worldManager = worldManager;
|
this.worldManager = worldManager;
|
||||||
|
this.playerWorldTeleporter = playerWorldTeleporter;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Subcommand("remove")
|
@Subcommand("remove")
|
||||||
|
@ -41,10 +57,27 @@ class RemoveCommand extends MultiverseCommand {
|
||||||
MVCommandIssuer issuer,
|
MVCommandIssuer issuer,
|
||||||
|
|
||||||
@Single
|
@Single
|
||||||
@Conditions("mvworlds:scope=both")
|
@Conditions("mvworlds:scope=both @flags:groupName=mvremovecommand")
|
||||||
@Syntax("<world>")
|
@Syntax("<world>")
|
||||||
@Description("{@@mv-core.remove.world.description}")
|
@Description("{@@mv-core.remove.world.description}")
|
||||||
MultiverseWorld world) {
|
MultiverseWorld world,
|
||||||
|
|
||||||
|
@Optional
|
||||||
|
@Syntax("[--remove-players]")
|
||||||
|
@Description(/* TODO */"")
|
||||||
|
String[] flags) {
|
||||||
|
ParsedCommandFlags parsedFlags = parseFlags(flags);
|
||||||
|
|
||||||
|
var future = parsedFlags.hasFlag(REMOVE_PLAYERS_FLAG)
|
||||||
|
? worldManager.getLoadedWorld(world)
|
||||||
|
.map(playerWorldTeleporter::removeFromWorld)
|
||||||
|
.getOrElse(Async.completedFuture(Collections.emptyList()))
|
||||||
|
: Async.completedFuture(Collections.emptyList());
|
||||||
|
|
||||||
|
future.thenRun(() -> doWorldRemoving(issuer, world));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doWorldRemoving(MVCommandIssuer issuer, MultiverseWorld world) {
|
||||||
worldManager.removeWorld(world)
|
worldManager.removeWorld(world)
|
||||||
.onSuccess(removedWorldName -> {
|
.onSuccess(removedWorldName -> {
|
||||||
Logging.fine("World remove success: " + removedWorldName);
|
Logging.fine("World remove success: " + removedWorldName);
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
package org.mvplugins.multiverse.core.commands;
|
package org.mvplugins.multiverse.core.commands;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.List;
|
||||||
import java.util.concurrent.CompletableFuture;
|
|
||||||
|
|
||||||
import co.aikar.commands.BukkitCommandIssuer;
|
|
||||||
import co.aikar.commands.CommandIssuer;
|
import co.aikar.commands.CommandIssuer;
|
||||||
import co.aikar.commands.annotation.CommandAlias;
|
import co.aikar.commands.annotation.CommandAlias;
|
||||||
import co.aikar.commands.annotation.CommandCompletion;
|
import co.aikar.commands.annotation.CommandCompletion;
|
||||||
|
@ -14,24 +12,32 @@ import co.aikar.commands.annotation.Syntax;
|
||||||
import com.dumptruckman.minecraft.util.Logging;
|
import com.dumptruckman.minecraft.util.Logging;
|
||||||
import jakarta.inject.Inject;
|
import jakarta.inject.Inject;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jvnet.hk2.annotations.Service;
|
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.MVCommandManager;
|
||||||
import org.mvplugins.multiverse.core.commandtools.MultiverseCommand;
|
import org.mvplugins.multiverse.core.commandtools.MultiverseCommand;
|
||||||
import org.mvplugins.multiverse.core.destination.DestinationsProvider;
|
|
||||||
import org.mvplugins.multiverse.core.destination.ParsedDestination;
|
import org.mvplugins.multiverse.core.destination.ParsedDestination;
|
||||||
|
import org.mvplugins.multiverse.core.permissions.CorePermissionsChecker;
|
||||||
|
import org.mvplugins.multiverse.core.teleportation.AsyncSafetyTeleporter;
|
||||||
import org.mvplugins.multiverse.core.utils.MVCorei18n;
|
import org.mvplugins.multiverse.core.utils.MVCorei18n;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
@CommandAlias("mv")
|
@CommandAlias("mv")
|
||||||
class TeleportCommand extends MultiverseCommand {
|
class TeleportCommand extends MultiverseCommand {
|
||||||
|
|
||||||
private final DestinationsProvider destinationsProvider;
|
private final CorePermissionsChecker permissionsChecker;
|
||||||
|
private final AsyncSafetyTeleporter safetyTeleporter;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
TeleportCommand(MVCommandManager commandManager, DestinationsProvider destinationsProvider) {
|
TeleportCommand(
|
||||||
|
@NotNull MVCommandManager commandManager,
|
||||||
|
@NotNull CorePermissionsChecker permissionsChecker,
|
||||||
|
@NotNull AsyncSafetyTeleporter safetyTeleporter) {
|
||||||
super(commandManager);
|
super(commandManager);
|
||||||
this.destinationsProvider = destinationsProvider;
|
this.permissionsChecker = permissionsChecker;
|
||||||
|
this.safetyTeleporter = safetyTeleporter;
|
||||||
}
|
}
|
||||||
|
|
||||||
@CommandAlias("mvtp")
|
@CommandAlias("mvtp")
|
||||||
|
@ -40,7 +46,7 @@ class TeleportCommand extends MultiverseCommand {
|
||||||
@Syntax("[player] <destination>")
|
@Syntax("[player] <destination>")
|
||||||
@Description("{@@mv-core.teleport.description}")
|
@Description("{@@mv-core.teleport.description}")
|
||||||
void onTeleportCommand(
|
void onTeleportCommand(
|
||||||
BukkitCommandIssuer issuer,
|
MVCommandIssuer issuer,
|
||||||
|
|
||||||
@Flags("resolve=issuerAware")
|
@Flags("resolve=issuerAware")
|
||||||
@Syntax("[player]")
|
@Syntax("[player]")
|
||||||
|
@ -56,17 +62,25 @@ class TeleportCommand extends MultiverseCommand {
|
||||||
? issuer.getPlayer() == players[0] ? "you" : players[0].getName()
|
? issuer.getPlayer() == players[0] ? "you" : players[0].getName()
|
||||||
: players.length + " players";
|
: players.length + " players";
|
||||||
|
|
||||||
|
// TODO: Multi player permission checking
|
||||||
|
if (!permissionsChecker.checkTeleportPermissions(issuer.getIssuer(), players[0], destination)) {
|
||||||
|
issuer.sendMessage("You do not have teleport permissions");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
issuer.sendInfo(MVCorei18n.TELEPORT_SUCCESS,
|
issuer.sendInfo(MVCorei18n.TELEPORT_SUCCESS,
|
||||||
"{player}", playerName, "{destination}", destination.toString());
|
"{player}", playerName, "{destination}", destination.toString());
|
||||||
|
|
||||||
CompletableFuture.allOf(Arrays.stream(players)
|
safetyTeleporter.teleportSafely(issuer.getIssuer(), List.of(players), destination)
|
||||||
.map(player -> this.destinationsProvider.playerTeleportAsync(issuer, player, destination))
|
.thenAccept(attempts -> Logging.fine("Async teleport completed: %s", attempts))
|
||||||
.toArray(CompletableFuture[]::new))
|
.exceptionally(throwable -> {
|
||||||
.thenRun(() -> Logging.finer("Async teleport completed."));
|
Logging.severe("Error while teleporting %s to %s: %s",
|
||||||
|
playerName, destination, throwable.getMessage());
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean hasPermission(CommandIssuer issuer) {
|
public boolean hasPermission(CommandIssuer issuer) {
|
||||||
return this.destinationsProvider.hasAnyTeleportPermission(issuer);
|
return permissionsChecker.hasAnyTeleportPermission(issuer.getIssuer());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
package org.mvplugins.multiverse.core.commands;
|
package org.mvplugins.multiverse.core.commands;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
|
||||||
import co.aikar.commands.annotation.CommandAlias;
|
import co.aikar.commands.annotation.CommandAlias;
|
||||||
import co.aikar.commands.annotation.CommandCompletion;
|
import co.aikar.commands.annotation.CommandCompletion;
|
||||||
import co.aikar.commands.annotation.CommandPermission;
|
import co.aikar.commands.annotation.CommandPermission;
|
||||||
|
@ -18,8 +20,10 @@ import org.mvplugins.multiverse.core.commandtools.MultiverseCommand;
|
||||||
import org.mvplugins.multiverse.core.commandtools.flags.CommandFlag;
|
import org.mvplugins.multiverse.core.commandtools.flags.CommandFlag;
|
||||||
import org.mvplugins.multiverse.core.commandtools.flags.ParsedCommandFlags;
|
import org.mvplugins.multiverse.core.commandtools.flags.ParsedCommandFlags;
|
||||||
import org.mvplugins.multiverse.core.utils.MVCorei18n;
|
import org.mvplugins.multiverse.core.utils.MVCorei18n;
|
||||||
|
import org.mvplugins.multiverse.core.utils.result.Async;
|
||||||
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;
|
||||||
|
import org.mvplugins.multiverse.core.world.helpers.PlayerWorldTeleporter;
|
||||||
import org.mvplugins.multiverse.core.world.options.UnloadWorldOptions;
|
import org.mvplugins.multiverse.core.world.options.UnloadWorldOptions;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
|
@ -27,6 +31,7 @@ import org.mvplugins.multiverse.core.world.options.UnloadWorldOptions;
|
||||||
class UnloadCommand extends MultiverseCommand {
|
class UnloadCommand extends MultiverseCommand {
|
||||||
|
|
||||||
private final WorldManager worldManager;
|
private final WorldManager worldManager;
|
||||||
|
private final PlayerWorldTeleporter playerWorldTeleporter;
|
||||||
|
|
||||||
private final CommandFlag REMOVE_PLAYERS_FLAG = flag(CommandFlag.builder("--remove-players")
|
private final CommandFlag REMOVE_PLAYERS_FLAG = flag(CommandFlag.builder("--remove-players")
|
||||||
.addAlias("-r")
|
.addAlias("-r")
|
||||||
|
@ -37,9 +42,13 @@ class UnloadCommand extends MultiverseCommand {
|
||||||
.build());
|
.build());
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
UnloadCommand(@NotNull MVCommandManager commandManager, @NotNull WorldManager worldManager) {
|
UnloadCommand(
|
||||||
|
@NotNull MVCommandManager commandManager,
|
||||||
|
@NotNull WorldManager worldManager,
|
||||||
|
@NotNull PlayerWorldTeleporter playerWorldTeleporter) {
|
||||||
super(commandManager);
|
super(commandManager);
|
||||||
this.worldManager = worldManager;
|
this.worldManager = worldManager;
|
||||||
|
this.playerWorldTeleporter = playerWorldTeleporter;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Subcommand("unload")
|
@Subcommand("unload")
|
||||||
|
@ -61,8 +70,16 @@ class UnloadCommand extends MultiverseCommand {
|
||||||
ParsedCommandFlags parsedFlags = parseFlags(flags);
|
ParsedCommandFlags parsedFlags = parseFlags(flags);
|
||||||
|
|
||||||
issuer.sendInfo(MVCorei18n.UNLOAD_UNLOADING, "{world}", world.getAlias());
|
issuer.sendInfo(MVCorei18n.UNLOAD_UNLOADING, "{world}", world.getAlias());
|
||||||
|
|
||||||
|
var future = parsedFlags.hasFlag(REMOVE_PLAYERS_FLAG)
|
||||||
|
? playerWorldTeleporter.removeFromWorld(world)
|
||||||
|
: Async.completedFuture(Collections.emptyList());
|
||||||
|
|
||||||
|
future.thenRun(() -> doWorldUnloading(issuer, world, parsedFlags));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doWorldUnloading(MVCommandIssuer issuer, LoadedMultiverseWorld world, ParsedCommandFlags parsedFlags) {
|
||||||
UnloadWorldOptions unloadWorldOptions = UnloadWorldOptions.world(world)
|
UnloadWorldOptions unloadWorldOptions = UnloadWorldOptions.world(world)
|
||||||
.removePlayers(parsedFlags.hasFlag(REMOVE_PLAYERS_FLAG))
|
|
||||||
.saveBukkitWorld(!parsedFlags.hasFlag(NO_SAVE_FLAG));
|
.saveBukkitWorld(!parsedFlags.hasFlag(NO_SAVE_FLAG));
|
||||||
worldManager.unloadWorld(unloadWorldOptions)
|
worldManager.unloadWorld(unloadWorldOptions)
|
||||||
.onSuccess(loadedWorld -> {
|
.onSuccess(loadedWorld -> {
|
||||||
|
|
|
@ -3,14 +3,10 @@ package org.mvplugins.multiverse.core.destination;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.CompletableFuture;
|
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import co.aikar.commands.BukkitCommandIssuer;
|
import co.aikar.commands.BukkitCommandIssuer;
|
||||||
import co.aikar.commands.CommandIssuer;
|
|
||||||
import jakarta.inject.Inject;
|
import jakarta.inject.Inject;
|
||||||
import org.bukkit.entity.Entity;
|
|
||||||
import org.bukkit.entity.Player;
|
|
||||||
import org.bukkit.permissions.Permission;
|
import org.bukkit.permissions.Permission;
|
||||||
import org.bukkit.plugin.PluginManager;
|
import org.bukkit.plugin.PluginManager;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
@ -19,9 +15,6 @@ import org.jvnet.hk2.annotations.Service;
|
||||||
|
|
||||||
import org.mvplugins.multiverse.core.api.Destination;
|
import org.mvplugins.multiverse.core.api.Destination;
|
||||||
import org.mvplugins.multiverse.core.api.DestinationInstance;
|
import org.mvplugins.multiverse.core.api.DestinationInstance;
|
||||||
import org.mvplugins.multiverse.core.api.SafeTTeleporter;
|
|
||||||
import org.mvplugins.multiverse.core.api.Teleporter;
|
|
||||||
import org.mvplugins.multiverse.core.teleportation.TeleportResult;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides destinations for teleportation.
|
* Provides destinations for teleportation.
|
||||||
|
@ -32,13 +25,11 @@ 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 SafeTTeleporter safetyTeleporter;
|
|
||||||
private final Map<String, Destination<?>> destinationMap;
|
private final Map<String, Destination<?>> destinationMap;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
DestinationsProvider(@NotNull PluginManager pluginManager, @NotNull SafeTTeleporter safetyTeleporter) {
|
DestinationsProvider(@NotNull PluginManager pluginManager) {
|
||||||
this.pluginManager = pluginManager;
|
this.pluginManager = pluginManager;
|
||||||
this.safetyTeleporter = safetyTeleporter;
|
|
||||||
this.destinationMap = new HashMap<>();
|
this.destinationMap = new HashMap<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -123,93 +114,11 @@ public class DestinationsProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Teleports the teleportee to the destination.
|
* Gets all registered destinations.
|
||||||
*
|
*
|
||||||
* @param teleporter The teleporter.
|
* @return A collection of destinations.
|
||||||
* @param teleportee The teleportee.
|
|
||||||
* @param destination The destination.
|
|
||||||
* @return The async teleport result.
|
|
||||||
*/
|
*/
|
||||||
public CompletableFuture<TeleportResult> playerTeleportAsync(
|
public @NotNull Collection<Destination<?>> getDestinations() {
|
||||||
@NotNull BukkitCommandIssuer teleporter,
|
return this.destinationMap.values();
|
||||||
@NotNull Player teleportee,
|
|
||||||
@NotNull ParsedDestination<?> destination) {
|
|
||||||
if (!checkTeleportPermissions(teleporter, teleportee, destination)) {
|
|
||||||
return CompletableFuture.completedFuture(TeleportResult.FAIL_PERMISSION);
|
|
||||||
}
|
|
||||||
return teleportAsync(teleporter, teleportee, destination);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Teleports the teleportee to the destination.
|
|
||||||
*
|
|
||||||
* @param teleporter The teleporter.
|
|
||||||
* @param teleportee The teleportee.
|
|
||||||
* @param destination The destination.
|
|
||||||
* @return The async teleport result.
|
|
||||||
*/
|
|
||||||
public CompletableFuture<TeleportResult> teleportAsync(
|
|
||||||
@NotNull BukkitCommandIssuer teleporter,
|
|
||||||
@NotNull Entity teleportee,
|
|
||||||
@NotNull ParsedDestination<?> destination) {
|
|
||||||
Teleporter teleportHandler = destination.getDestination().getTeleporter();
|
|
||||||
if (teleportHandler == null) {
|
|
||||||
teleportHandler = safetyTeleporter;
|
|
||||||
}
|
|
||||||
return teleportHandler.teleportAsync(teleporter, teleportee, destination);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if the teleporter has permission to teleport the teleportee to the destination.
|
|
||||||
*
|
|
||||||
* @param teleporter The teleporter.
|
|
||||||
* @param teleportee The teleportee.
|
|
||||||
* @param destination The destination.
|
|
||||||
* @return True if the teleporter has permission, false otherwise.
|
|
||||||
*/
|
|
||||||
public boolean checkTeleportPermissions(
|
|
||||||
CommandIssuer teleporter, Entity teleportee, ParsedDestination<?> destination) {
|
|
||||||
// TODO: Move permission checking to a separate class
|
|
||||||
String permission = PERMISSION_PREFIX
|
|
||||||
+ (teleportee.equals(teleporter.getIssuer()) ? "self" : "other") + "."
|
|
||||||
+ destination.getDestination().getIdentifier();
|
|
||||||
if (!teleporter.hasPermission(permission)) {
|
|
||||||
teleporter.sendMessage("You don't have permission to teleport to this destination.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Config whether to use finer permission
|
|
||||||
String finerPermissionSuffix = destination.getDestinationInstance().getFinerPermissionSuffix();
|
|
||||||
if (finerPermissionSuffix == null || finerPermissionSuffix.isEmpty()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
String finerPermission = permission + "." + finerPermissionSuffix;
|
|
||||||
if (!teleporter.hasPermission(finerPermission)) {
|
|
||||||
teleporter.sendMessage("You don't have permission to teleport to this destination.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if the issuer has permission to teleport to at least one destination.
|
|
||||||
*
|
|
||||||
* @param issuer The issuer.
|
|
||||||
* @return True if the issuer has permission, false otherwise.
|
|
||||||
*/
|
|
||||||
public boolean hasAnyTeleportPermission(CommandIssuer issuer) {
|
|
||||||
for (Destination<?> destination : this.destinationMap.values()) {
|
|
||||||
String permission = PERMISSION_PREFIX + "self." + destination.getIdentifier();
|
|
||||||
if (issuer.hasPermission(permission)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
permission = PERMISSION_PREFIX + "other." + destination.getIdentifier();
|
|
||||||
if (issuer.hasPermission(permission)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,6 @@ 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.api.Destination;
|
||||||
import org.mvplugins.multiverse.core.api.Teleporter;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link Destination} implementation for anchors.
|
* {@link Destination} implementation for anchors.
|
||||||
|
@ -61,12 +60,4 @@ public class AnchorDestination implements Destination<AnchorDestinationInstance>
|
||||||
public boolean checkTeleportSafety() {
|
public boolean checkTeleportSafety() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public @Nullable Teleporter getTeleporter() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,6 @@ 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.Destination;
|
||||||
import org.mvplugins.multiverse.core.api.Teleporter;
|
|
||||||
import org.mvplugins.multiverse.core.utils.PlayerFinder;
|
import org.mvplugins.multiverse.core.utils.PlayerFinder;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -62,12 +61,4 @@ public class BedDestination implements Destination<BedDestinationInstance> {
|
||||||
public boolean checkTeleportSafety() {
|
public boolean checkTeleportSafety() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public @Nullable Teleporter getTeleporter() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,6 @@ 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.Destination;
|
||||||
import org.mvplugins.multiverse.core.api.Teleporter;
|
|
||||||
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;
|
||||||
|
|
||||||
|
@ -97,12 +96,4 @@ public class CannonDestination implements Destination<CannonDestinationInstance>
|
||||||
public boolean checkTeleportSafety() {
|
public boolean checkTeleportSafety() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public @Nullable Teleporter getTeleporter() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,6 @@ 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.Destination;
|
||||||
import org.mvplugins.multiverse.core.api.Teleporter;
|
|
||||||
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;
|
||||||
|
|
||||||
|
@ -100,12 +99,4 @@ public class ExactDestination implements Destination<ExactDestinationInstance> {
|
||||||
public boolean checkTeleportSafety() {
|
public boolean checkTeleportSafety() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public @Nullable Teleporter getTeleporter() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,6 @@ 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.Destination;
|
||||||
import org.mvplugins.multiverse.core.api.Teleporter;
|
|
||||||
import org.mvplugins.multiverse.core.utils.PlayerFinder;
|
import org.mvplugins.multiverse.core.utils.PlayerFinder;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -60,12 +59,4 @@ public class PlayerDestination implements Destination<PlayerDestinationInstance>
|
||||||
public boolean checkTeleportSafety() {
|
public boolean checkTeleportSafety() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public @Nullable Teleporter getTeleporter() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,6 @@ import org.jvnet.hk2.annotations.Service;
|
||||||
|
|
||||||
import org.mvplugins.multiverse.core.api.Destination;
|
import org.mvplugins.multiverse.core.api.Destination;
|
||||||
import org.mvplugins.multiverse.core.api.LocationManipulation;
|
import org.mvplugins.multiverse.core.api.LocationManipulation;
|
||||||
import org.mvplugins.multiverse.core.api.Teleporter;
|
|
||||||
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;
|
||||||
|
|
||||||
|
@ -76,12 +75,4 @@ public class WorldDestination implements Destination<WorldDestinationInstance> {
|
||||||
public boolean checkTeleportSafety() {
|
public boolean checkTeleportSafety() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public @Nullable Teleporter getTeleporter() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,8 +14,8 @@ 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.api.SafeTTeleporter;
|
|
||||||
import org.mvplugins.multiverse.core.destination.ParsedDestination;
|
import org.mvplugins.multiverse.core.destination.ParsedDestination;
|
||||||
|
import org.mvplugins.multiverse.core.teleportation.AsyncSafetyTeleporter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Event that gets called when a player use the /mvtp command.
|
* Event that gets called when a player use the /mvtp command.
|
||||||
|
@ -89,8 +89,8 @@ public class MVTeleportEvent extends Event implements Cancellable {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Looks if this {@link MVTeleportEvent} is using the {@link SafeTTeleporter}.
|
* Looks if this {@link MVTeleportEvent} is using the {@link AsyncSafetyTeleporter}.
|
||||||
* @return True if this {@link MVTeleportEvent} is using the {@link SafeTTeleporter}.
|
* @return True if this {@link MVTeleportEvent} is using the {@link AsyncSafetyTeleporter}.
|
||||||
*/
|
*/
|
||||||
public boolean isUsingSafeTTeleporter() {
|
public boolean isUsingSafeTTeleporter() {
|
||||||
return useSafeTeleport;
|
return useSafeTeleport;
|
||||||
|
|
|
@ -31,8 +31,9 @@ import org.bukkit.event.player.PlayerTeleportEvent;
|
||||||
import org.bukkit.plugin.Plugin;
|
import org.bukkit.plugin.Plugin;
|
||||||
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.MultiverseCore;
|
import org.mvplugins.multiverse.core.MultiverseCore;
|
||||||
import org.mvplugins.multiverse.core.api.SafeTTeleporter;
|
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.DestinationsProvider;
|
import org.mvplugins.multiverse.core.destination.DestinationsProvider;
|
||||||
|
@ -40,6 +41,7 @@ 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.inject.InjectableListener;
|
import org.mvplugins.multiverse.core.inject.InjectableListener;
|
||||||
|
import org.mvplugins.multiverse.core.teleportation.AsyncSafetyTeleporter;
|
||||||
import org.mvplugins.multiverse.core.teleportation.TeleportQueue;
|
import org.mvplugins.multiverse.core.teleportation.TeleportQueue;
|
||||||
import org.mvplugins.multiverse.core.utils.result.ResultChain;
|
import org.mvplugins.multiverse.core.utils.result.ResultChain;
|
||||||
import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld;
|
import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld;
|
||||||
|
@ -56,7 +58,8 @@ public class MVPlayerListener implements InjectableListener {
|
||||||
private final Plugin plugin;
|
private final Plugin plugin;
|
||||||
private final MVCoreConfig config;
|
private final MVCoreConfig config;
|
||||||
private final Provider<WorldManager> worldManagerProvider;
|
private final Provider<WorldManager> worldManagerProvider;
|
||||||
private final SafeTTeleporter safetyTeleporter;
|
private final BlockSafety blockSafety;
|
||||||
|
private final AsyncSafetyTeleporter safetyTeleporter;
|
||||||
private final Server server;
|
private final Server server;
|
||||||
private final TeleportQueue teleportQueue;
|
private final TeleportQueue teleportQueue;
|
||||||
private final MVEconomist economist;
|
private final MVEconomist economist;
|
||||||
|
@ -72,7 +75,8 @@ public class MVPlayerListener implements InjectableListener {
|
||||||
MultiverseCore plugin,
|
MultiverseCore plugin,
|
||||||
MVCoreConfig config,
|
MVCoreConfig config,
|
||||||
Provider<WorldManager> worldManagerProvider,
|
Provider<WorldManager> worldManagerProvider,
|
||||||
SafeTTeleporter safetyTeleporter,
|
BlockSafety blockSafety,
|
||||||
|
AsyncSafetyTeleporter safetyTeleporter,
|
||||||
Server server,
|
Server server,
|
||||||
TeleportQueue teleportQueue,
|
TeleportQueue teleportQueue,
|
||||||
MVEconomist economist,
|
MVEconomist economist,
|
||||||
|
@ -83,6 +87,7 @@ public class MVPlayerListener implements InjectableListener {
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
this.config = config;
|
this.config = config;
|
||||||
this.worldManagerProvider = worldManagerProvider;
|
this.worldManagerProvider = worldManagerProvider;
|
||||||
|
this.blockSafety = blockSafety;
|
||||||
this.safetyTeleporter = safetyTeleporter;
|
this.safetyTeleporter = safetyTeleporter;
|
||||||
this.server = server;
|
this.server = server;
|
||||||
this.teleportQueue = teleportQueue;
|
this.teleportQueue = teleportQueue;
|
||||||
|
@ -211,7 +216,7 @@ public class MVPlayerListener implements InjectableListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finally, teleport the player
|
// Finally, teleport the player
|
||||||
safetyTeleporter.teleportAsync(getCommandManager().getCommandIssuer(player), player, joinDestination);
|
safetyTeleporter.teleportSafely(player, player, joinDestination);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -300,7 +305,7 @@ public class MVPlayerListener implements InjectableListener {
|
||||||
// REMEMBER! getTo MAY be NULL HERE!!!
|
// REMEMBER! getTo MAY be NULL HERE!!!
|
||||||
// If the player was actually outside of the portal, adjust the from location
|
// If the player was actually outside of the portal, adjust the from location
|
||||||
if (event.getFrom().getWorld().getBlockAt(event.getFrom()).getType() != Material.NETHER_PORTAL) {
|
if (event.getFrom().getWorld().getBlockAt(event.getFrom()).getType() != Material.NETHER_PORTAL) {
|
||||||
Location newloc = this.safetyTeleporter.findPortalBlockNextTo(event.getFrom());
|
Location newloc = blockSafety.findPortalBlockNextTo(event.getFrom());
|
||||||
// TODO: Fix this. Currently, we only check for PORTAL blocks. I'll have to figure out what
|
// TODO: Fix this. Currently, we only check for PORTAL blocks. I'll have to figure out what
|
||||||
// TODO: we want to do here.
|
// TODO: we want to do here.
|
||||||
if (newloc != null) {
|
if (newloc != null) {
|
||||||
|
@ -352,7 +357,7 @@ public class MVPlayerListener implements InjectableListener {
|
||||||
new Runnable() {
|
new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
safetyTeleporter.safelyTeleportAsync(getCommandManager().getCommandIssuer(player), player, parsedDestination);
|
safetyTeleporter.teleportSafely(player, player, parsedDestination);
|
||||||
}
|
}
|
||||||
}, 1L);
|
}, 1L);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,32 @@
|
||||||
package org.mvplugins.multiverse.core.permissions;
|
package org.mvplugins.multiverse.core.permissions;
|
||||||
|
|
||||||
public class CorePermissions {
|
final class CorePermissions {
|
||||||
public static String WORLD_ACCESS = "multiverse.access";
|
/**
|
||||||
public static String WORLD_EXEMPT = "multiverse.exempt";
|
* Permission to access a world.
|
||||||
public static String GAMEMODE_BYPASS = "mv.bypass.gamemode";
|
*/
|
||||||
public static String PLAYERLIMIT_BYPASS = "mv.bypass.playerlimit";
|
static final String WORLD_ACCESS = "multiverse.access";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Permission to bypass the entry fee of a world.
|
||||||
|
*/
|
||||||
|
static final String WORLD_EXEMPT = "multiverse.exempt";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Permission to bypass the gamemode of a world.
|
||||||
|
*/
|
||||||
|
static final String GAMEMODE_BYPASS = "mv.bypass.gamemode";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Permission to bypass the player limit of a world.
|
||||||
|
*/
|
||||||
|
static final String PLAYERLIMIT_BYPASS = "mv.bypass.playerlimit";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Permission to teleport to a destination.
|
||||||
|
*/
|
||||||
|
static final String TELEPORT = "multiverse.teleport";
|
||||||
|
|
||||||
|
private CorePermissions() {
|
||||||
|
// Prevent instantiation as this is a static utility class
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +1,28 @@
|
||||||
package org.mvplugins.multiverse.core.permissions;
|
package org.mvplugins.multiverse.core.permissions;
|
||||||
|
|
||||||
import com.dumptruckman.minecraft.util.Logging;
|
import com.dumptruckman.minecraft.util.Logging;
|
||||||
|
import jakarta.inject.Inject;
|
||||||
import org.bukkit.command.CommandSender;
|
import org.bukkit.command.CommandSender;
|
||||||
|
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.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;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
CorePermissionsChecker(@NotNull DestinationsProvider destinationsProvider) {
|
||||||
|
this.destinationsProvider = destinationsProvider;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean hasWorldAccessPermission(@NotNull CommandSender sender, @NotNull MultiverseWorld world) {
|
public boolean hasWorldAccessPermission(@NotNull CommandSender sender, @NotNull MultiverseWorld world) {
|
||||||
return hasPermission(sender, concatPermission(CorePermissions.WORLD_ACCESS, world.getName()));
|
return hasPermission(sender, concatPermission(CorePermissions.WORLD_ACCESS, world.getName()));
|
||||||
}
|
}
|
||||||
|
@ -26,7 +39,58 @@ public class CorePermissionsChecker {
|
||||||
return hasPermission(sender, concatPermission(CorePermissions.GAMEMODE_BYPASS, world.getName()));
|
return hasPermission(sender, concatPermission(CorePermissions.GAMEMODE_BYPASS, world.getName()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private String concatPermission(String permission, String...child) {
|
/**
|
||||||
|
* Checks if the teleporter has permission to teleport the teleportee to the destination.
|
||||||
|
*
|
||||||
|
* @param teleporter The teleporter.
|
||||||
|
* @param teleportee The teleportee.
|
||||||
|
* @param destination The destination.
|
||||||
|
* @return True if the teleporter has permission, false otherwise.
|
||||||
|
*/
|
||||||
|
public boolean checkTeleportPermissions(
|
||||||
|
@NotNull CommandSender teleporter,
|
||||||
|
@NotNull Entity teleportee,
|
||||||
|
@NotNull ParsedDestination<?> destination) {
|
||||||
|
|
||||||
|
String permission = concatPermission(
|
||||||
|
CorePermissions.TELEPORT,
|
||||||
|
teleportee.equals(teleporter) ? "self" : "other",
|
||||||
|
destination.getDestination().getIdentifier());
|
||||||
|
if (!hasPermission(teleporter, permission)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the issuer has permission to teleport to at least one destination.
|
||||||
|
*
|
||||||
|
* @param sender The sender to check.
|
||||||
|
* @return True if the issuer has permission, false otherwise.
|
||||||
|
*/
|
||||||
|
public boolean hasAnyTeleportPermission(CommandSender sender) {
|
||||||
|
for (Destination<?> destination : destinationsProvider.getDestinations()) {
|
||||||
|
String permission = concatPermission(CorePermissions.TELEPORT, "self", destination.getIdentifier());
|
||||||
|
if (hasPermission(sender, permission)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
permission = concatPermission(CorePermissions.TELEPORT, "other", destination.getIdentifier());
|
||||||
|
if (hasPermission(sender, permission)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String concatPermission(String permission, String... child) {
|
||||||
return permission + "." + String.join(".", child);
|
return permission + "." + String.join(".", child);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,158 @@
|
||||||
|
package org.mvplugins.multiverse.core.teleportation;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.dumptruckman.minecraft.util.Logging;
|
||||||
|
import io.papermc.lib.PaperLib;
|
||||||
|
import jakarta.inject.Inject;
|
||||||
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.entity.Entity;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
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.utils.result.Async;
|
||||||
|
import org.mvplugins.multiverse.core.utils.result.AsyncAttempt;
|
||||||
|
import org.mvplugins.multiverse.core.utils.result.Attempt;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Teleports entities safely and asynchronously.
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
public class AsyncSafetyTeleporter {
|
||||||
|
private final BlockSafety blockSafety;
|
||||||
|
private final TeleportQueue teleportQueue;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
AsyncSafetyTeleporter(
|
||||||
|
BlockSafety blockSafety,
|
||||||
|
TeleportQueue teleportQueue) {
|
||||||
|
this.blockSafety = blockSafety;
|
||||||
|
this.teleportQueue = teleportQueue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AsyncAttempt<Void, TeleportResult.Failure> teleportSafely(
|
||||||
|
@NotNull Entity teleportee,
|
||||||
|
@Nullable ParsedDestination<?> 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) {
|
||||||
|
return AsyncAttempt.allOf(teleportees.stream()
|
||||||
|
.map(teleportee -> teleportSafely(teleporter, teleportee, destination))
|
||||||
|
.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
public AsyncAttempt<Void, TeleportResult.Failure> teleportSafely(
|
||||||
|
@Nullable CommandSender teleporter,
|
||||||
|
@NotNull Entity teleportee,
|
||||||
|
@Nullable ParsedDestination<?> 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));
|
||||||
|
}
|
||||||
|
|
||||||
|
public AsyncAttempt<Void, TeleportResult.Failure> teleportSafely(
|
||||||
|
@NotNull Entity teleportee,
|
||||||
|
@Nullable Location location) {
|
||||||
|
return teleportSafely(null, teleportee, location);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AsyncAttempt<Void, TeleportResult.Failure> teleportSafely(
|
||||||
|
@Nullable CommandSender teleporter,
|
||||||
|
@NotNull Entity teleportee,
|
||||||
|
@Nullable Location location) {
|
||||||
|
if (location == null) {
|
||||||
|
return AsyncAttempt.failure(TeleportResult.Failure.NULL_LOCATION);
|
||||||
|
}
|
||||||
|
Location safeLocation = blockSafety.getSafeLocation(location);
|
||||||
|
if (safeLocation == null) {
|
||||||
|
return AsyncAttempt.failure(TeleportResult.Failure.UNSAFE_LOCATION);
|
||||||
|
}
|
||||||
|
return teleport(teleporter, teleportee, safeLocation);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T extends Entity> Async<List<Attempt<Void, TeleportResult.Failure>>> teleport(
|
||||||
|
@NotNull List<T> teleportees,
|
||||||
|
@Nullable ParsedDestination<?> destination) {
|
||||||
|
return AsyncAttempt.allOf(teleportees.stream()
|
||||||
|
.map(teleportee -> teleport(teleportee, destination))
|
||||||
|
.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
public AsyncAttempt<Void, TeleportResult.Failure> teleport(
|
||||||
|
@NotNull Entity teleportee,
|
||||||
|
@Nullable ParsedDestination<?> 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));
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T extends Entity> Async<List<Attempt<Void, TeleportResult.Failure>>> teleport(
|
||||||
|
@NotNull List<T> teleportees,
|
||||||
|
@Nullable Location location) {
|
||||||
|
return AsyncAttempt.allOf(teleportees.stream()
|
||||||
|
.map(teleportee -> teleport(teleportee, location))
|
||||||
|
.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
public AsyncAttempt<Void, TeleportResult.Failure> teleport(
|
||||||
|
@NotNull Entity teleportee,
|
||||||
|
@Nullable Location location) {
|
||||||
|
return teleport(null, teleportee, location);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AsyncAttempt<Void, TeleportResult.Failure> teleport(
|
||||||
|
@Nullable CommandSender teleporter,
|
||||||
|
@NotNull Entity teleportee,
|
||||||
|
@Nullable Location location) {
|
||||||
|
if (location == null) {
|
||||||
|
return AsyncAttempt.failure(TeleportResult.Failure.NULL_LOCATION);
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean shouldAddToQueue = teleporter != null && teleportee instanceof Player;
|
||||||
|
if (shouldAddToQueue) {
|
||||||
|
teleportQueue.addToQueue(teleporter.getName(), teleportee.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
return doAsyncTeleport(teleportee, location, shouldAddToQueue);
|
||||||
|
}
|
||||||
|
|
||||||
|
private AsyncAttempt<Void, TeleportResult.Failure> doAsyncTeleport(
|
||||||
|
@NotNull Entity teleportee,
|
||||||
|
@NotNull Location location,
|
||||||
|
boolean shouldAddToQueue) {
|
||||||
|
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 -> {
|
||||||
|
Logging.finer("Teleported async %s to %s", teleportee.getName(), location);
|
||||||
|
if (result) {
|
||||||
|
if (shouldAddToQueue) {
|
||||||
|
teleportQueue.popFromQueue(teleportee.getName());
|
||||||
|
}
|
||||||
|
return Attempt.success(null);
|
||||||
|
}
|
||||||
|
return Attempt.failure(TeleportResult.Failure.TELEPORT_FAILED);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,7 +8,6 @@
|
||||||
package org.mvplugins.multiverse.core.teleportation;
|
package org.mvplugins.multiverse.core.teleportation;
|
||||||
|
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import com.dumptruckman.minecraft.util.Logging;
|
import com.dumptruckman.minecraft.util.Logging;
|
||||||
|
@ -16,11 +15,14 @@ import jakarta.inject.Inject;
|
||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
import org.bukkit.Material;
|
import org.bukkit.Material;
|
||||||
import org.bukkit.World;
|
import org.bukkit.World;
|
||||||
|
import org.bukkit.block.Block;
|
||||||
import org.bukkit.block.BlockFace;
|
import org.bukkit.block.BlockFace;
|
||||||
import org.bukkit.block.data.BlockData;
|
import org.bukkit.block.data.BlockData;
|
||||||
import org.bukkit.block.data.type.Bed;
|
import org.bukkit.block.data.type.Bed;
|
||||||
import org.bukkit.entity.Minecart;
|
import org.bukkit.entity.Minecart;
|
||||||
import org.bukkit.entity.Vehicle;
|
import org.bukkit.entity.Vehicle;
|
||||||
|
import org.bukkit.util.Vector;
|
||||||
|
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;
|
||||||
|
@ -31,9 +33,13 @@ import org.mvplugins.multiverse.core.api.LocationManipulation;
|
||||||
*/
|
*/
|
||||||
@Service
|
@Service
|
||||||
public class SimpleBlockSafety implements BlockSafety {
|
public class SimpleBlockSafety implements BlockSafety {
|
||||||
private final LocationManipulation locationManipulation;
|
private static final Vector DEFAULT_VECTOR = new Vector();
|
||||||
|
private static final int DEFAULT_TOLERANCE = 6;
|
||||||
|
private static final int DEFAULT_RADIUS = 9;
|
||||||
private static final Set<BlockFace> AROUND_BLOCK = EnumSet.noneOf(BlockFace.class);
|
private static final Set<BlockFace> AROUND_BLOCK = EnumSet.noneOf(BlockFace.class);
|
||||||
|
|
||||||
|
private final LocationManipulation locationManipulation;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
AROUND_BLOCK.add(BlockFace.NORTH);
|
AROUND_BLOCK.add(BlockFace.NORTH);
|
||||||
AROUND_BLOCK.add(BlockFace.NORTH_EAST);
|
AROUND_BLOCK.add(BlockFace.NORTH_EAST);
|
||||||
|
@ -46,7 +52,7 @@ public class SimpleBlockSafety implements BlockSafety {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public SimpleBlockSafety(LocationManipulation locationManipulation) {
|
SimpleBlockSafety(LocationManipulation locationManipulation) {
|
||||||
this.locationManipulation = locationManipulation;
|
this.locationManipulation = locationManipulation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,6 +119,197 @@ public class SimpleBlockSafety implements BlockSafety {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Location findPortalBlockNextTo(Location location) {
|
||||||
|
Block b = location.getWorld().getBlockAt(location);
|
||||||
|
Location foundLocation = null;
|
||||||
|
if (b.getType() == Material.NETHER_PORTAL) {
|
||||||
|
return location;
|
||||||
|
}
|
||||||
|
if (b.getRelative(BlockFace.NORTH).getType() == Material.NETHER_PORTAL) {
|
||||||
|
foundLocation = getCloserBlock(location, b.getRelative(BlockFace.NORTH).getLocation(), foundLocation);
|
||||||
|
}
|
||||||
|
if (b.getRelative(BlockFace.SOUTH).getType() == Material.NETHER_PORTAL) {
|
||||||
|
foundLocation = getCloserBlock(location, b.getRelative(BlockFace.SOUTH).getLocation(), foundLocation);
|
||||||
|
}
|
||||||
|
if (b.getRelative(BlockFace.EAST).getType() == Material.NETHER_PORTAL) {
|
||||||
|
foundLocation = getCloserBlock(location, b.getRelative(BlockFace.EAST).getLocation(), foundLocation);
|
||||||
|
}
|
||||||
|
if (b.getRelative(BlockFace.WEST).getType() == Material.NETHER_PORTAL) {
|
||||||
|
foundLocation = getCloserBlock(location, b.getRelative(BlockFace.WEST).getLocation(), foundLocation);
|
||||||
|
}
|
||||||
|
return foundLocation;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Location getCloserBlock(Location source, Location blockA, Location blockB) {
|
||||||
|
// If B wasn't given, return a.
|
||||||
|
if (blockB == null) {
|
||||||
|
return blockA;
|
||||||
|
}
|
||||||
|
// Center our calculations
|
||||||
|
blockA.add(.5, 0, .5);
|
||||||
|
blockB.add(.5, 0, .5);
|
||||||
|
|
||||||
|
// Retrieve the distance to the normalized blocks
|
||||||
|
double testA = source.distance(blockA);
|
||||||
|
double testB = source.distance(blockB);
|
||||||
|
|
||||||
|
// Compare and return
|
||||||
|
if (testA <= testB) {
|
||||||
|
return blockA;
|
||||||
|
}
|
||||||
|
return blockB;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public @Nullable Location getSafeLocation(Location location) {
|
||||||
|
return this.getSafeLocation(location, DEFAULT_TOLERANCE, DEFAULT_RADIUS);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public @Nullable Location getSafeLocation(Location location, int tolerance, int radius) {
|
||||||
|
// Check around the player first in a configurable radius:
|
||||||
|
// TODO: Make this configurable
|
||||||
|
Location safe = checkAboveAndBelowLocation(location, tolerance, radius);
|
||||||
|
if (safe != null) {
|
||||||
|
safe.setX(safe.getBlockX() + .5); // SUPPRESS CHECKSTYLE: MagicNumberCheck
|
||||||
|
safe.setZ(safe.getBlockZ() + .5); // SUPPRESS CHECKSTYLE: MagicNumberCheck
|
||||||
|
Logging.fine("Hey! I found one: " + locationManipulation.strCoordsRaw(safe));
|
||||||
|
} else {
|
||||||
|
Logging.fine("Uh oh! No safe place found!");
|
||||||
|
}
|
||||||
|
return safe;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Location checkAboveAndBelowLocation(Location l, int tolerance, int radius) {
|
||||||
|
// Tolerance must be an even number:
|
||||||
|
if (tolerance % 2 != 0) {
|
||||||
|
tolerance += 1;
|
||||||
|
}
|
||||||
|
// We want half of it, so we can go up and down
|
||||||
|
tolerance /= 2;
|
||||||
|
Logging.finer("Given Location of: " + locationManipulation.strCoordsRaw(l));
|
||||||
|
Logging.finer("Checking +-" + tolerance + " with a radius of " + radius);
|
||||||
|
|
||||||
|
// For now this will just do a straight up block.
|
||||||
|
Location locToCheck = l.clone();
|
||||||
|
// Check the main level
|
||||||
|
Location safe = this.checkAroundLocation(locToCheck, radius);
|
||||||
|
if (safe != null) {
|
||||||
|
return safe;
|
||||||
|
}
|
||||||
|
// We've already checked zero right above this.
|
||||||
|
int currentLevel = 1;
|
||||||
|
while (currentLevel <= tolerance) {
|
||||||
|
// Check above
|
||||||
|
locToCheck = l.clone();
|
||||||
|
locToCheck.add(0, currentLevel, 0);
|
||||||
|
safe = this.checkAroundLocation(locToCheck, radius);
|
||||||
|
if (safe != null) {
|
||||||
|
return safe;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check below
|
||||||
|
locToCheck = l.clone();
|
||||||
|
locToCheck.subtract(0, currentLevel, 0);
|
||||||
|
safe = this.checkAroundLocation(locToCheck, radius);
|
||||||
|
if (safe != null) {
|
||||||
|
return safe;
|
||||||
|
}
|
||||||
|
currentLevel++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* For my crappy algorithm, radius MUST be odd.
|
||||||
|
*/
|
||||||
|
private Location checkAroundLocation(Location l, int diameter) {
|
||||||
|
if (diameter % 2 == 0) {
|
||||||
|
diameter += 1;
|
||||||
|
}
|
||||||
|
Location checkLoc = l.clone();
|
||||||
|
|
||||||
|
// Start at 3, the min diameter around a block
|
||||||
|
int loopcounter = 3;
|
||||||
|
while (loopcounter <= diameter) {
|
||||||
|
boolean foundSafeArea = checkAroundSpecificDiameter(checkLoc, loopcounter);
|
||||||
|
// If a safe area was found:
|
||||||
|
if (foundSafeArea) {
|
||||||
|
// Return the checkLoc, it is the safe location.
|
||||||
|
return checkLoc;
|
||||||
|
}
|
||||||
|
// Otherwise, let's reset our location
|
||||||
|
checkLoc = l.clone();
|
||||||
|
// And increment the radius
|
||||||
|
loopcounter += 2;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean checkAroundSpecificDiameter(Location checkLoc, int circle) {
|
||||||
|
// Adjust the circle to get how many blocks to step out.
|
||||||
|
// A radius of 3 makes the block step 1
|
||||||
|
// A radius of 5 makes the block step 2
|
||||||
|
// A radius of 7 makes the block step 3
|
||||||
|
// ...
|
||||||
|
int adjustedCircle = ((circle - 1) / 2);
|
||||||
|
checkLoc.add(adjustedCircle, 0, 0);
|
||||||
|
if (playerCanSpawnHereSafely(checkLoc)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// Now we go to the right that adjustedCircle many
|
||||||
|
for (int i = 0; i < adjustedCircle; i++) {
|
||||||
|
checkLoc.add(0, 0, 1);
|
||||||
|
if (playerCanSpawnHereSafely(checkLoc)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then down adjustedCircle *2
|
||||||
|
for (int i = 0; i < adjustedCircle * 2; i++) {
|
||||||
|
checkLoc.add(-1, 0, 0);
|
||||||
|
if (playerCanSpawnHereSafely(checkLoc)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then left adjustedCircle *2
|
||||||
|
for (int i = 0; i < adjustedCircle * 2; i++) {
|
||||||
|
checkLoc.add(0, 0, -1);
|
||||||
|
if (playerCanSpawnHereSafely(checkLoc)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then up Then left adjustedCircle *2
|
||||||
|
for (int i = 0; i < adjustedCircle * 2; i++) {
|
||||||
|
checkLoc.add(1, 0, 0);
|
||||||
|
if (playerCanSpawnHereSafely(checkLoc)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then finish up by doing adjustedCircle - 1
|
||||||
|
for (int i = 0; i < adjustedCircle - 1; i++) {
|
||||||
|
checkLoc.add(0, 0, 1);
|
||||||
|
if (playerCanSpawnHereSafely(checkLoc)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
|
@ -122,7 +319,7 @@ public class SimpleBlockSafety implements BlockSafety {
|
||||||
if (l == null) {
|
if (l == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
final Location trySpawn = this.getSafeSpawnAroundABlock(l);
|
final Location trySpawn = this.getSafeSpawnAroundBlock(l);
|
||||||
if (trySpawn != null) {
|
if (trySpawn != null) {
|
||||||
return trySpawn;
|
return trySpawn;
|
||||||
}
|
}
|
||||||
|
@ -131,18 +328,17 @@ public class SimpleBlockSafety implements BlockSafety {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
// Now we have 2 locations, check around each, if the type is bed, skip it.
|
// Now we have 2 locations, check around each, if the type is bed, skip it.
|
||||||
return this.getSafeSpawnAroundABlock(otherBlock);
|
return this.getSafeSpawnAroundBlock(otherBlock);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find a safe spawn around a location. (N,S,E,W,NE,NW,SE,SW)
|
* Find a safe spawn around a location. (N,S,E,W,NE,NW,SE,SW)
|
||||||
|
*
|
||||||
* @param l Location to check around
|
* @param l Location to check around
|
||||||
* @return A safe location, or none if it wasn't found.
|
* @return A safe location, or none if it wasn't found.
|
||||||
*/
|
*/
|
||||||
private Location getSafeSpawnAroundABlock(Location l) {
|
private Location getSafeSpawnAroundBlock(Location l) {
|
||||||
Iterator<BlockFace> checkblock = AROUND_BLOCK.iterator();
|
for (BlockFace face : AROUND_BLOCK) {
|
||||||
while (checkblock.hasNext()) {
|
|
||||||
final BlockFace face = checkblock.next();
|
|
||||||
if (this.playerCanSpawnHereSafely(l.getBlock().getRelative(face).getLocation())) {
|
if (this.playerCanSpawnHereSafely(l.getBlock().getRelative(face).getLocation())) {
|
||||||
// Don't forget to center the player.
|
// Don't forget to center the player.
|
||||||
return l.getBlock().getRelative(face).getLocation().add(.5, 0, .5);
|
return l.getBlock().getRelative(face).getLocation().add(.5, 0, .5);
|
||||||
|
@ -158,16 +354,15 @@ public class SimpleBlockSafety implements BlockSafety {
|
||||||
*/
|
*/
|
||||||
private Location findOtherBedPiece(Location checkLoc) {
|
private Location findOtherBedPiece(Location checkLoc) {
|
||||||
BlockData data = checkLoc.getBlock().getBlockData();
|
BlockData data = checkLoc.getBlock().getBlockData();
|
||||||
if (!(data instanceof Bed)) {
|
if (!(data instanceof Bed bed)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
Bed b = (Bed) data;
|
|
||||||
|
|
||||||
if (b.getPart() == Bed.Part.HEAD) {
|
if (bed.getPart() == Bed.Part.HEAD) {
|
||||||
return checkLoc.getBlock().getRelative(b.getFacing().getOppositeFace()).getLocation();
|
return checkLoc.getBlock().getRelative(bed.getFacing().getOppositeFace()).getLocation();
|
||||||
}
|
}
|
||||||
// We shouldn't ever be looking at the foot, but here's the code for it.
|
// We shouldn't ever be looking at the foot, but here's the code for it.
|
||||||
return checkLoc.getBlock().getRelative(b.getFacing()).getLocation();
|
return checkLoc.getBlock().getRelative(bed.getFacing()).getLocation();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -206,7 +401,7 @@ public class SimpleBlockSafety implements BlockSafety {
|
||||||
/*
|
/*
|
||||||
* If someone has a better way of this... Please either tell us, or submit a pull request!
|
* If someone has a better way of this... Please either tell us, or submit a pull request!
|
||||||
*/
|
*/
|
||||||
public static boolean isSolidBlock(Material type) {
|
private static boolean isSolidBlock(Material type) {
|
||||||
return type.isSolid();
|
return type.isSolid();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -237,7 +432,7 @@ public class SimpleBlockSafety implements BlockSafety {
|
||||||
if (oneBelow.getBlock().getType() == Material.WATER) {
|
if (oneBelow.getBlock().getType() == Material.WATER) {
|
||||||
Location twoBelow = oneBelow.clone();
|
Location twoBelow = oneBelow.clone();
|
||||||
twoBelow.subtract(0, 1, 0);
|
twoBelow.subtract(0, 1, 0);
|
||||||
return (oneBelow.getBlock().getType() == Material.WATER);
|
return oneBelow.getBlock().getType() == Material.WATER;
|
||||||
}
|
}
|
||||||
if (oneBelow.getBlock().getType() != Material.AIR) {
|
if (oneBelow.getBlock().getType() != Material.AIR) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -253,10 +448,7 @@ public class SimpleBlockSafety implements BlockSafety {
|
||||||
if (this.isBlockAboveAir(cart.getLocation())) {
|
if (this.isBlockAboveAir(cart.getLocation())) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (this.isEntitiyOnTrack(locationManipulation.getNextBlock(cart))) {
|
return this.isEntitiyOnTrack(locationManipulation.getNextBlock(cart));
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -264,10 +456,7 @@ public class SimpleBlockSafety implements BlockSafety {
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean canSpawnVehicleSafely(Vehicle vehicle) {
|
public boolean canSpawnVehicleSafely(Vehicle vehicle) {
|
||||||
if (this.isBlockAboveAir(vehicle.getLocation())) {
|
return this.isBlockAboveAir(vehicle.getLocation());
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,378 +0,0 @@
|
||||||
/******************************************************************************
|
|
||||||
* Multiverse 2 Copyright (c) the Multiverse Team 2011. *
|
|
||||||
* Multiverse 2 is licensed under the BSD License. *
|
|
||||||
* For more information please check the README.md file included *
|
|
||||||
* with this project. *
|
|
||||||
******************************************************************************/
|
|
||||||
|
|
||||||
package org.mvplugins.multiverse.core.teleportation;
|
|
||||||
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
|
||||||
|
|
||||||
import co.aikar.commands.BukkitCommandIssuer;
|
|
||||||
import com.dumptruckman.minecraft.util.Logging;
|
|
||||||
import io.papermc.lib.PaperLib;
|
|
||||||
import jakarta.inject.Inject;
|
|
||||||
import org.bukkit.Bukkit;
|
|
||||||
import org.bukkit.Location;
|
|
||||||
import org.bukkit.Material;
|
|
||||||
import org.bukkit.block.Block;
|
|
||||||
import org.bukkit.block.BlockFace;
|
|
||||||
import org.bukkit.command.CommandSender;
|
|
||||||
import org.bukkit.entity.Entity;
|
|
||||||
import org.bukkit.entity.Minecart;
|
|
||||||
import org.bukkit.entity.Player;
|
|
||||||
import org.bukkit.entity.Vehicle;
|
|
||||||
import org.bukkit.util.Vector;
|
|
||||||
import org.jvnet.hk2.annotations.Service;
|
|
||||||
|
|
||||||
import org.mvplugins.multiverse.core.MultiverseCore;
|
|
||||||
import org.mvplugins.multiverse.core.api.BlockSafety;
|
|
||||||
import org.mvplugins.multiverse.core.api.DestinationInstance;
|
|
||||||
import org.mvplugins.multiverse.core.api.LocationManipulation;
|
|
||||||
import org.mvplugins.multiverse.core.api.SafeTTeleporter;
|
|
||||||
import org.mvplugins.multiverse.core.destination.ParsedDestination;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The default-implementation of {@link SafeTTeleporter}.
|
|
||||||
*/
|
|
||||||
@Service
|
|
||||||
public class SimpleSafeTTeleporter implements SafeTTeleporter {
|
|
||||||
private final MultiverseCore plugin;
|
|
||||||
private final LocationManipulation locationManipulation;
|
|
||||||
private final BlockSafety blockSafety;
|
|
||||||
private final TeleportQueue teleportQueue;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
public SimpleSafeTTeleporter(
|
|
||||||
MultiverseCore plugin,
|
|
||||||
LocationManipulation locationManipulation,
|
|
||||||
BlockSafety blockSafety,
|
|
||||||
TeleportQueue teleportQueue
|
|
||||||
) {
|
|
||||||
this.plugin = plugin;
|
|
||||||
this.locationManipulation = locationManipulation;
|
|
||||||
this.blockSafety = blockSafety;
|
|
||||||
this.teleportQueue = teleportQueue;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final Vector DEFAULT_VECTOR = new Vector();
|
|
||||||
private static final int DEFAULT_TOLERANCE = 6;
|
|
||||||
private static final int DEFAULT_RADIUS = 9;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public Location getSafeLocation(Location l) {
|
|
||||||
return this.getSafeLocation(l, DEFAULT_TOLERANCE, DEFAULT_RADIUS);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public Location getSafeLocation(Location l, int tolerance, int radius) {
|
|
||||||
// Check around the player first in a configurable radius:
|
|
||||||
// TODO: Make this configurable
|
|
||||||
Location safe = checkAboveAndBelowLocation(l, tolerance, radius);
|
|
||||||
if (safe != null) {
|
|
||||||
safe.setX(safe.getBlockX() + .5); // SUPPRESS CHECKSTYLE: MagicNumberCheck
|
|
||||||
safe.setZ(safe.getBlockZ() + .5); // SUPPRESS CHECKSTYLE: MagicNumberCheck
|
|
||||||
Logging.fine("Hey! I found one: " + locationManipulation.strCoordsRaw(safe));
|
|
||||||
} else {
|
|
||||||
Logging.fine("Uh oh! No safe place found!");
|
|
||||||
}
|
|
||||||
return safe;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Location checkAboveAndBelowLocation(Location l, int tolerance, int radius) {
|
|
||||||
// Tolerance must be an even number:
|
|
||||||
if (tolerance % 2 != 0) {
|
|
||||||
tolerance += 1;
|
|
||||||
}
|
|
||||||
// We want half of it, so we can go up and down
|
|
||||||
tolerance /= 2;
|
|
||||||
Logging.finer("Given Location of: " + locationManipulation.strCoordsRaw(l));
|
|
||||||
Logging.finer("Checking +-" + tolerance + " with a radius of " + radius);
|
|
||||||
|
|
||||||
// For now this will just do a straight up block.
|
|
||||||
Location locToCheck = l.clone();
|
|
||||||
// Check the main level
|
|
||||||
Location safe = this.checkAroundLocation(locToCheck, radius);
|
|
||||||
if (safe != null) {
|
|
||||||
return safe;
|
|
||||||
}
|
|
||||||
// We've already checked zero right above this.
|
|
||||||
int currentLevel = 1;
|
|
||||||
while (currentLevel <= tolerance) {
|
|
||||||
// Check above
|
|
||||||
locToCheck = l.clone();
|
|
||||||
locToCheck.add(0, currentLevel, 0);
|
|
||||||
safe = this.checkAroundLocation(locToCheck, radius);
|
|
||||||
if (safe != null) {
|
|
||||||
return safe;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check below
|
|
||||||
locToCheck = l.clone();
|
|
||||||
locToCheck.subtract(0, currentLevel, 0);
|
|
||||||
safe = this.checkAroundLocation(locToCheck, radius);
|
|
||||||
if (safe != null) {
|
|
||||||
return safe;
|
|
||||||
}
|
|
||||||
currentLevel++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* For my crappy algorithm, radius MUST be odd.
|
|
||||||
*/
|
|
||||||
private Location checkAroundLocation(Location l, int diameter) {
|
|
||||||
if (diameter % 2 == 0) {
|
|
||||||
diameter += 1;
|
|
||||||
}
|
|
||||||
Location checkLoc = l.clone();
|
|
||||||
|
|
||||||
// Start at 3, the min diameter around a block
|
|
||||||
int loopcounter = 3;
|
|
||||||
while (loopcounter <= diameter) {
|
|
||||||
boolean foundSafeArea = checkAroundSpecificDiameter(checkLoc, loopcounter);
|
|
||||||
// If a safe area was found:
|
|
||||||
if (foundSafeArea) {
|
|
||||||
// Return the checkLoc, it is the safe location.
|
|
||||||
return checkLoc;
|
|
||||||
}
|
|
||||||
// Otherwise, let's reset our location
|
|
||||||
checkLoc = l.clone();
|
|
||||||
// And increment the radius
|
|
||||||
loopcounter += 2;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean checkAroundSpecificDiameter(Location checkLoc, int circle) {
|
|
||||||
// Adjust the circle to get how many blocks to step out.
|
|
||||||
// A radius of 3 makes the block step 1
|
|
||||||
// A radius of 5 makes the block step 2
|
|
||||||
// A radius of 7 makes the block step 3
|
|
||||||
// ...
|
|
||||||
int adjustedCircle = ((circle - 1) / 2);
|
|
||||||
checkLoc.add(adjustedCircle, 0, 0);
|
|
||||||
if (blockSafety.playerCanSpawnHereSafely(checkLoc)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// Now we go to the right that adjustedCircle many
|
|
||||||
for (int i = 0; i < adjustedCircle; i++) {
|
|
||||||
checkLoc.add(0, 0, 1);
|
|
||||||
if (blockSafety.playerCanSpawnHereSafely(checkLoc)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Then down adjustedCircle *2
|
|
||||||
for (int i = 0; i < adjustedCircle * 2; i++) {
|
|
||||||
checkLoc.add(-1, 0, 0);
|
|
||||||
if (blockSafety.playerCanSpawnHereSafely(checkLoc)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Then left adjustedCircle *2
|
|
||||||
for (int i = 0; i < adjustedCircle * 2; i++) {
|
|
||||||
checkLoc.add(0, 0, -1);
|
|
||||||
if (blockSafety.playerCanSpawnHereSafely(checkLoc)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Then up Then left adjustedCircle *2
|
|
||||||
for (int i = 0; i < adjustedCircle * 2; i++) {
|
|
||||||
checkLoc.add(1, 0, 0);
|
|
||||||
if (blockSafety.playerCanSpawnHereSafely(checkLoc)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Then finish up by doing adjustedCircle - 1
|
|
||||||
for (int i = 0; i < adjustedCircle - 1; i++) {
|
|
||||||
checkLoc.add(0, 0, 1);
|
|
||||||
if (blockSafety.playerCanSpawnHereSafely(checkLoc)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public TeleportResult safelyTeleport(BukkitCommandIssuer teleporter, Entity teleportee, ParsedDestination<?> destination) {
|
|
||||||
return safelyTeleportAsync(teleporter, teleportee, destination).join();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public CompletableFuture<TeleportResult> safelyTeleportAsync(BukkitCommandIssuer teleporter, Entity teleportee, ParsedDestination<?> destination) {
|
|
||||||
if (destination == null) {
|
|
||||||
Logging.finer("Entity tried to teleport to an invalid destination");
|
|
||||||
return CompletableFuture.completedFuture(TeleportResult.FAIL_INVALID);
|
|
||||||
}
|
|
||||||
|
|
||||||
Player teleporteePlayer = null;
|
|
||||||
if (teleportee instanceof Player) {
|
|
||||||
teleporteePlayer = ((Player) teleportee);
|
|
||||||
} else if (teleportee.getPassenger() instanceof Player) {
|
|
||||||
teleporteePlayer = ((Player) teleportee.getPassenger());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (teleporteePlayer == null) {
|
|
||||||
return CompletableFuture.completedFuture(TeleportResult.FAIL_INVALID);
|
|
||||||
}
|
|
||||||
|
|
||||||
teleportQueue.addToQueue(teleporter.getIssuer().getName(), teleporteePlayer.getName());
|
|
||||||
|
|
||||||
Location safeLoc = destination.getLocation(teleportee);
|
|
||||||
if (destination.getDestination().checkTeleportSafety()) {
|
|
||||||
safeLoc = this.getSafeLocation(teleportee, destination.getDestinationInstance());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (safeLoc == null) {
|
|
||||||
return CompletableFuture.completedFuture(TeleportResult.FAIL_UNSAFE);
|
|
||||||
}
|
|
||||||
|
|
||||||
CompletableFuture<TeleportResult> future = new CompletableFuture<>();
|
|
||||||
|
|
||||||
PaperLib.teleportAsync(teleportee, safeLoc).thenAccept(result -> {
|
|
||||||
if (!result) {
|
|
||||||
future.complete(TeleportResult.FAIL_OTHER);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Vector v = destination.getDestinationInstance().getVelocity(teleportee);
|
|
||||||
if (v != null && !DEFAULT_VECTOR.equals(v)) {
|
|
||||||
Bukkit.getScheduler().runTaskLater(this.plugin, () -> {
|
|
||||||
teleportee.setVelocity(v);
|
|
||||||
}, 1);
|
|
||||||
}
|
|
||||||
future.complete(TeleportResult.SUCCESS);
|
|
||||||
});
|
|
||||||
|
|
||||||
return future;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public TeleportResult safelyTeleport(CommandSender teleporter, Entity teleportee, Location location, boolean safely) {
|
|
||||||
if (safely) {
|
|
||||||
location = this.getSafeLocation(location);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (location != null) {
|
|
||||||
if (teleportee.teleport(location)) {
|
|
||||||
return TeleportResult.SUCCESS;
|
|
||||||
}
|
|
||||||
return TeleportResult.FAIL_OTHER;
|
|
||||||
}
|
|
||||||
return TeleportResult.FAIL_UNSAFE;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Location getSafeLocation(Entity entity, DestinationInstance destination) {
|
|
||||||
Location l = destination.getLocation(entity);
|
|
||||||
if (blockSafety.playerCanSpawnHereSafely(l)) {
|
|
||||||
Logging.fine("The first location you gave me was safe.");
|
|
||||||
return l;
|
|
||||||
}
|
|
||||||
if (entity instanceof Minecart) {
|
|
||||||
Minecart m = (Minecart) entity;
|
|
||||||
if (!blockSafety.canSpawnCartSafely(m)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
} else if (entity instanceof Vehicle) {
|
|
||||||
Vehicle v = (Vehicle) entity;
|
|
||||||
if (!blockSafety.canSpawnVehicleSafely(v)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Location safeLocation = this.getSafeLocation(l);
|
|
||||||
if (safeLocation != null) {
|
|
||||||
// Add offset to account for a vehicle on dry land!
|
|
||||||
if (entity instanceof Minecart && !blockSafety.isEntitiyOnTrack(safeLocation)) {
|
|
||||||
safeLocation.setY(safeLocation.getBlockY() + .5);
|
|
||||||
Logging.finer("Player was inside a minecart. Offsetting Y location.");
|
|
||||||
}
|
|
||||||
Logging.finer("Had to look for a bit, but I found a safe place for ya!");
|
|
||||||
return safeLocation;
|
|
||||||
}
|
|
||||||
if (entity instanceof Player) {
|
|
||||||
Player p = (Player) entity;
|
|
||||||
p.sendMessage("No safe locations found!");
|
|
||||||
Logging.finer("No safe location found for " + p.getName());
|
|
||||||
} else if (entity.getPassenger() instanceof Player) {
|
|
||||||
Player p = (Player) entity.getPassenger();
|
|
||||||
p.sendMessage("No safe locations found!");
|
|
||||||
Logging.finer("No safe location found for " + p.getName());
|
|
||||||
}
|
|
||||||
Logging.fine("Sorry champ, you're basically trying to teleport into a minefield. I should just kill you now.");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public Location findPortalBlockNextTo(Location l) {
|
|
||||||
Block b = l.getWorld().getBlockAt(l);
|
|
||||||
Location foundLocation = null;
|
|
||||||
if (b.getType() == Material.NETHER_PORTAL) {
|
|
||||||
return l;
|
|
||||||
}
|
|
||||||
if (b.getRelative(BlockFace.NORTH).getType() == Material.NETHER_PORTAL) {
|
|
||||||
foundLocation = getCloserBlock(l, b.getRelative(BlockFace.NORTH).getLocation(), foundLocation);
|
|
||||||
}
|
|
||||||
if (b.getRelative(BlockFace.SOUTH).getType() == Material.NETHER_PORTAL) {
|
|
||||||
foundLocation = getCloserBlock(l, b.getRelative(BlockFace.SOUTH).getLocation(), foundLocation);
|
|
||||||
}
|
|
||||||
if (b.getRelative(BlockFace.EAST).getType() == Material.NETHER_PORTAL) {
|
|
||||||
foundLocation = getCloserBlock(l, b.getRelative(BlockFace.EAST).getLocation(), foundLocation);
|
|
||||||
}
|
|
||||||
if (b.getRelative(BlockFace.WEST).getType() == Material.NETHER_PORTAL) {
|
|
||||||
foundLocation = getCloserBlock(l, b.getRelative(BlockFace.WEST).getLocation(), foundLocation);
|
|
||||||
}
|
|
||||||
return foundLocation;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Location getCloserBlock(Location source, Location blockA, Location blockB) {
|
|
||||||
// If B wasn't given, return a.
|
|
||||||
if (blockB == null) {
|
|
||||||
return blockA;
|
|
||||||
}
|
|
||||||
// Center our calculations
|
|
||||||
blockA.add(.5, 0, .5);
|
|
||||||
blockB.add(.5, 0, .5);
|
|
||||||
|
|
||||||
// Retrieve the distance to the normalized blocks
|
|
||||||
double testA = source.distance(blockA);
|
|
||||||
double testB = source.distance(blockB);
|
|
||||||
|
|
||||||
// Compare and return
|
|
||||||
if (testA <= testB) {
|
|
||||||
return blockA;
|
|
||||||
}
|
|
||||||
return blockB;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public TeleportResult teleport(BukkitCommandIssuer teleporter, Entity teleportee, ParsedDestination<?> destination) {
|
|
||||||
return safelyTeleport(teleporter, teleportee, destination);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public CompletableFuture<TeleportResult> teleportAsync(BukkitCommandIssuer teleporter, Entity teleportee, ParsedDestination<?> destination) {
|
|
||||||
return safelyTeleportAsync(teleporter, teleportee, destination);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -12,7 +12,7 @@ public class TeleportQueue {
|
||||||
|
|
||||||
private final Map<String, String> teleportQueue;
|
private final Map<String, String> teleportQueue;
|
||||||
|
|
||||||
public TeleportQueue() {
|
TeleportQueue() {
|
||||||
teleportQueue = new HashMap<>();
|
teleportQueue = new HashMap<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,38 +1,19 @@
|
||||||
/******************************************************************************
|
|
||||||
* Multiverse 2 Copyright (c) the Multiverse Team 2011. *
|
|
||||||
* Multiverse 2 is licensed under the BSD License. *
|
|
||||||
* For more information please check the README.md file included *
|
|
||||||
* with this project. *
|
|
||||||
******************************************************************************/
|
|
||||||
|
|
||||||
package org.mvplugins.multiverse.core.teleportation;
|
package org.mvplugins.multiverse.core.teleportation;
|
||||||
|
|
||||||
/**
|
import org.mvplugins.multiverse.core.utils.result.FailureReason;
|
||||||
* An enum containing possible teleport-results.
|
import org.mvplugins.multiverse.core.utils.result.SuccessReason;
|
||||||
*/
|
|
||||||
public enum TeleportResult {
|
public class TeleportResult {
|
||||||
/**
|
public enum Success implements SuccessReason {
|
||||||
* Insufficient permissions.
|
SUCCESS
|
||||||
*/
|
}
|
||||||
FAIL_PERMISSION,
|
|
||||||
/**
|
public enum Failure implements FailureReason {
|
||||||
* The teleport was unsafe.
|
NULL_DESTINATION,
|
||||||
*/
|
NULL_LOCATION,
|
||||||
FAIL_UNSAFE,
|
UNSAFE_LOCATION,
|
||||||
/**
|
TELEPORT_FAILED,
|
||||||
* The player was to poor.
|
TELEPORT_FAILED_EXCEPTION,
|
||||||
*/
|
PLAYER_OFFLINE,
|
||||||
FAIL_TOO_POOR,
|
}
|
||||||
/**
|
|
||||||
* The teleport was invalid.
|
|
||||||
*/
|
|
||||||
FAIL_INVALID,
|
|
||||||
/**
|
|
||||||
* Unknown reason.
|
|
||||||
*/
|
|
||||||
FAIL_OTHER,
|
|
||||||
/**
|
|
||||||
* The player was successfully teleported.
|
|
||||||
*/
|
|
||||||
SUCCESS
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,163 @@
|
||||||
|
package org.mvplugins.multiverse.core.utils.result;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents the result of an asynchronous operation that wraps a {@link CompletableFuture}.
|
||||||
|
*
|
||||||
|
* @param <T> The type of the value.
|
||||||
|
*/
|
||||||
|
public final class Async<T> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a new AsyncResult that is completed when all of the given AsyncResult complete.
|
||||||
|
*
|
||||||
|
* @param results The results to wait for.
|
||||||
|
* @return A new AsyncResult that is completed when all of the given AsyncResult complete.
|
||||||
|
*/
|
||||||
|
public static Async<Void> allOf(Async<?>... results) {
|
||||||
|
return new Async<>(CompletableFuture.allOf(Arrays.stream(results)
|
||||||
|
.map(result -> result.future)
|
||||||
|
.toArray(CompletableFuture[]::new)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a new AsyncResult that is completed when all of the given AsyncResult with the same type complete.
|
||||||
|
*
|
||||||
|
* @param results The results to wait for.
|
||||||
|
* @param <T> The type of the AsyncResult.
|
||||||
|
* @return A new AsyncResult that is completed when all of the given AsyncResult complete.
|
||||||
|
*/
|
||||||
|
public static <T> Async<List<T>> allOf(List<Async<T>> results) {
|
||||||
|
return new Async<>(CompletableFuture.allOf(results.stream()
|
||||||
|
.map(result -> result.future)
|
||||||
|
.toArray(CompletableFuture[]::new))
|
||||||
|
.thenApply(v -> results.stream()
|
||||||
|
.map(result -> result.future.join())
|
||||||
|
.toList()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wraps a CompletableFuture in an AsyncResult.
|
||||||
|
*
|
||||||
|
* @param future The future to wrap.
|
||||||
|
* @param <T> The type of the future.
|
||||||
|
* @return A new AsyncResult that is completed when the given future completes.
|
||||||
|
*/
|
||||||
|
public static <T> Async<T> of(CompletableFuture<T> future) {
|
||||||
|
return new Async<>(future);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a new AsyncResult that is already completed with the given value.
|
||||||
|
*
|
||||||
|
* @param value The value to complete the AsyncResult with.
|
||||||
|
* @param <T> The type of the value.
|
||||||
|
* @return The completed AsyncResult.
|
||||||
|
*/
|
||||||
|
public static <T> Async<T> completedFuture(T value) {
|
||||||
|
return new Async<>(CompletableFuture.completedFuture(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a new CompletableFuture that is already completed exceptionally with the given exception.
|
||||||
|
*
|
||||||
|
* @param throwable The exception to complete the AsyncResult with.
|
||||||
|
* @param <T> The type of the value.
|
||||||
|
* @return The completed AsyncResult.
|
||||||
|
*/
|
||||||
|
public static <T> Async<T> failedFuture(Throwable throwable) {
|
||||||
|
return new Async<>(CompletableFuture.failedFuture(throwable));
|
||||||
|
}
|
||||||
|
|
||||||
|
private final CompletableFuture<T> future;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new AsyncResult.
|
||||||
|
*/
|
||||||
|
public Async() {
|
||||||
|
this(new CompletableFuture<>());
|
||||||
|
}
|
||||||
|
|
||||||
|
private Async(CompletableFuture<T> future) {
|
||||||
|
this.future = future;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If not already completed, sets the value returned by related methods to the given value.
|
||||||
|
*
|
||||||
|
* @param value The value to complete the AsyncResult with.
|
||||||
|
* @return true if this invocation caused this AsyncResult to transition to a completed state, else false.
|
||||||
|
*/
|
||||||
|
public boolean complete(T value) {
|
||||||
|
return future.complete(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If not already completed, causes invocations of related methods to throw the given exception.
|
||||||
|
*
|
||||||
|
* @param throwable The exception to complete the AsyncResult with.
|
||||||
|
* @return true if this invocation caused this AsyncResult to transition to a completed state, else false.
|
||||||
|
*/
|
||||||
|
public boolean completeExceptionally(Throwable throwable) {
|
||||||
|
return future.completeExceptionally(throwable);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes the given action when this AsyncResult completes.
|
||||||
|
*
|
||||||
|
* @param consumer The action to perform.
|
||||||
|
* @return This AsyncResult.
|
||||||
|
*/
|
||||||
|
public Async<Void> thenAccept(Consumer<T> consumer) {
|
||||||
|
return new Async<>(future.thenAccept(consumer));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes the given action when this AsyncResult completes.
|
||||||
|
*
|
||||||
|
* @param runnable The action to perform.
|
||||||
|
* @return This AsyncResult.
|
||||||
|
*/
|
||||||
|
public Async<Void> thenRun(Runnable runnable) {
|
||||||
|
return new Async<>(future.thenRun(runnable));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes the given action when this AsyncResult completes and returns a new AsyncResult with the new value.
|
||||||
|
*
|
||||||
|
* @param function The action to perform.
|
||||||
|
* @param <U> The type of the new value.
|
||||||
|
* @return A new AsyncResult with the new value.
|
||||||
|
*/
|
||||||
|
public <U> Async<U> thenApply(Function<T, U> function) {
|
||||||
|
return new Async<>(future.thenApply(function));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes the given action when this AsyncResult completes with an exception.
|
||||||
|
*
|
||||||
|
* @param consumer The action to perform.
|
||||||
|
* @return This AsyncResult.
|
||||||
|
*/
|
||||||
|
public Async<T> exceptionally(Consumer<Throwable> consumer) {
|
||||||
|
return new Async<>(future.exceptionally(throwable -> {
|
||||||
|
consumer.accept(throwable);
|
||||||
|
return null;
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes the given action when this AsyncResult completes with an exception and returns a new AsyncResult with the new value.
|
||||||
|
*
|
||||||
|
* @param function The action to perform.
|
||||||
|
* @return A new AsyncResult with the new value.
|
||||||
|
*/
|
||||||
|
public Async<T> exceptionally(Function<Throwable, T> function) {
|
||||||
|
return new Async<>(future.exceptionally(function));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,73 @@
|
||||||
|
package org.mvplugins.multiverse.core.utils.result;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.function.BiFunction;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
import org.mvplugins.multiverse.core.utils.message.MessageReplacement;
|
||||||
|
|
||||||
|
public final class AsyncAttempt<T, F extends FailureReason> {
|
||||||
|
|
||||||
|
public static <T, F extends FailureReason> Async<List<Attempt<T, F>>> allOf(List<AsyncAttempt<T, F>> attempts) {
|
||||||
|
return Async.of(CompletableFuture.allOf(attempts.stream()
|
||||||
|
.map(attempt -> attempt.future)
|
||||||
|
.toArray(CompletableFuture[]::new))
|
||||||
|
.thenApply(v -> attempts.stream()
|
||||||
|
.map(attempt -> attempt.future.join())
|
||||||
|
.toList()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T, F extends FailureReason> AsyncAttempt<T, F> of(
|
||||||
|
CompletableFuture<T> future,
|
||||||
|
BiFunction<T, Throwable, Attempt<T, F>> completionHandler) {
|
||||||
|
return new AsyncAttempt<>(future.handle(completionHandler));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T, F extends FailureReason> AsyncAttempt<T, F> of(
|
||||||
|
CompletableFuture<T> future,
|
||||||
|
Function<Throwable, Attempt<T, F>> exceptionHandler) {
|
||||||
|
BiFunction<T, Throwable, Attempt<T, F>> completionHandler = (result, exception) -> exception != null
|
||||||
|
? exceptionHandler.apply(exception)
|
||||||
|
: Attempt.success(result);
|
||||||
|
return of(future, completionHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <F extends FailureReason> AsyncAttempt<Void, F> success() {
|
||||||
|
return new AsyncAttempt<>(CompletableFuture.completedFuture(null));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <F extends FailureReason> AsyncAttempt<Void, F> failure(
|
||||||
|
F failureReason,
|
||||||
|
MessageReplacement... messageReplacements) {
|
||||||
|
return new AsyncAttempt<>(CompletableFuture.completedFuture(
|
||||||
|
Attempt.failure(failureReason, messageReplacements)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private final CompletableFuture<Attempt<T, F>> future;
|
||||||
|
|
||||||
|
private AsyncAttempt(CompletableFuture<Attempt<T, F>> future) {
|
||||||
|
this.future = future;
|
||||||
|
}
|
||||||
|
|
||||||
|
public <U> AsyncAttempt<U, F> map(Function<? super T, ? extends U> mapper) {
|
||||||
|
return new AsyncAttempt<>(future.thenApply(attempt -> attempt.map(mapper)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public <U> AsyncAttempt<U, F> mapAttempt(Function<? super T, Attempt<U, F>> mapper) {
|
||||||
|
return new AsyncAttempt<>(future.thenApply(attempt -> attempt.mapAttempt(mapper)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public <U> AsyncAttempt<U, F> mapAsyncAttempt(Function<? super T, AsyncAttempt<U, F>> mapper) {
|
||||||
|
return new AsyncAttempt<>(future.thenApplyAsync(
|
||||||
|
attempt -> attempt.mapAttempt(rasult -> mapper.apply(rasult).toAttempt())));
|
||||||
|
}
|
||||||
|
|
||||||
|
public AsyncAttempt<T, F> onSuccess(Runnable runnable) {
|
||||||
|
return new AsyncAttempt<>(future.thenApply(attempt -> attempt.onSuccess(runnable)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Attempt<T, F> toAttempt() {
|
||||||
|
return future.join();
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,7 +14,6 @@ import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import org.mvplugins.multiverse.core.api.BlockSafety;
|
import org.mvplugins.multiverse.core.api.BlockSafety;
|
||||||
import org.mvplugins.multiverse.core.api.LocationManipulation;
|
import org.mvplugins.multiverse.core.api.LocationManipulation;
|
||||||
import org.mvplugins.multiverse.core.api.SafeTTeleporter;
|
|
||||||
import org.mvplugins.multiverse.core.world.config.NullLocation;
|
import org.mvplugins.multiverse.core.world.config.NullLocation;
|
||||||
import org.mvplugins.multiverse.core.world.config.SpawnLocation;
|
import org.mvplugins.multiverse.core.world.config.SpawnLocation;
|
||||||
import org.mvplugins.multiverse.core.world.config.WorldConfig;
|
import org.mvplugins.multiverse.core.world.config.WorldConfig;
|
||||||
|
@ -29,19 +28,16 @@ public class LoadedMultiverseWorld extends MultiverseWorld {
|
||||||
private final UUID worldUid;
|
private final UUID worldUid;
|
||||||
|
|
||||||
private final BlockSafety blockSafety;
|
private final BlockSafety blockSafety;
|
||||||
private final SafeTTeleporter safetyTeleporter;
|
|
||||||
private final LocationManipulation locationManipulation;
|
private final LocationManipulation locationManipulation;
|
||||||
|
|
||||||
LoadedMultiverseWorld(
|
LoadedMultiverseWorld(
|
||||||
@NotNull World world,
|
@NotNull World world,
|
||||||
@NotNull WorldConfig worldConfig,
|
@NotNull WorldConfig worldConfig,
|
||||||
@NotNull BlockSafety blockSafety,
|
@NotNull BlockSafety blockSafety,
|
||||||
@NotNull SafeTTeleporter safetyTeleporter,
|
|
||||||
@NotNull LocationManipulation locationManipulation) {
|
@NotNull LocationManipulation locationManipulation) {
|
||||||
super(world.getName(), worldConfig);
|
super(world.getName(), worldConfig);
|
||||||
this.worldUid = world.getUID();
|
this.worldUid = world.getUID();
|
||||||
this.blockSafety = blockSafety;
|
this.blockSafety = blockSafety;
|
||||||
this.safetyTeleporter = safetyTeleporter;
|
|
||||||
this.locationManipulation = locationManipulation;
|
this.locationManipulation = locationManipulation;
|
||||||
|
|
||||||
setupWorldConfig(world);
|
setupWorldConfig(world);
|
||||||
|
@ -82,7 +78,7 @@ public class LoadedMultiverseWorld extends MultiverseWorld {
|
||||||
// The location is not safe, so we need to find a better one.
|
// The location is not safe, so we need to find a better one.
|
||||||
Logging.warning("Spawn location from world.dat file was unsafe. Adjusting...");
|
Logging.warning("Spawn location from world.dat file was unsafe. Adjusting...");
|
||||||
Logging.warning("Original Location: " + locationManipulation.strCoordsRaw(location));
|
Logging.warning("Original Location: " + locationManipulation.strCoordsRaw(location));
|
||||||
Location newSpawn = safetyTeleporter.getSafeLocation(location,
|
Location newSpawn = blockSafety.getSafeLocation(location,
|
||||||
SPAWN_LOCATION_SEARCH_TOLERANCE, SPAWN_LOCATION_SEARCH_RADIUS);
|
SPAWN_LOCATION_SEARCH_TOLERANCE, SPAWN_LOCATION_SEARCH_RADIUS);
|
||||||
// I think we could also do this, as I think this is what Notch does.
|
// I think we could also do this, as I think this is what Notch does.
|
||||||
// Not sure how it will work in the nether...
|
// Not sure how it will work in the nether...
|
||||||
|
|
|
@ -27,7 +27,6 @@ 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.api.LocationManipulation;
|
import org.mvplugins.multiverse.core.api.LocationManipulation;
|
||||||
import org.mvplugins.multiverse.core.api.SafeTTeleporter;
|
|
||||||
import org.mvplugins.multiverse.core.utils.message.MessageReplacement;
|
import org.mvplugins.multiverse.core.utils.message.MessageReplacement;
|
||||||
import org.mvplugins.multiverse.core.utils.result.Attempt;
|
import org.mvplugins.multiverse.core.utils.result.Attempt;
|
||||||
import org.mvplugins.multiverse.core.utils.result.FailureReason;
|
import org.mvplugins.multiverse.core.utils.result.FailureReason;
|
||||||
|
@ -75,7 +74,6 @@ public class WorldManager {
|
||||||
private final PlayerWorldTeleporter playerWorldActions;
|
private final PlayerWorldTeleporter playerWorldActions;
|
||||||
private final FilesManipulator filesManipulator;
|
private final FilesManipulator filesManipulator;
|
||||||
private final BlockSafety blockSafety;
|
private final BlockSafety blockSafety;
|
||||||
private final SafeTTeleporter safetyTeleporter;
|
|
||||||
private final LocationManipulation locationManipulation;
|
private final LocationManipulation locationManipulation;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
|
@ -86,7 +84,6 @@ public class WorldManager {
|
||||||
@NotNull PlayerWorldTeleporter playerWorldActions,
|
@NotNull PlayerWorldTeleporter playerWorldActions,
|
||||||
@NotNull FilesManipulator filesManipulator,
|
@NotNull FilesManipulator filesManipulator,
|
||||||
@NotNull BlockSafety blockSafety,
|
@NotNull BlockSafety blockSafety,
|
||||||
@NotNull SafeTTeleporter safetyTeleporter,
|
|
||||||
@NotNull LocationManipulation locationManipulation) {
|
@NotNull LocationManipulation locationManipulation) {
|
||||||
this.worldsMap = new HashMap<>();
|
this.worldsMap = new HashMap<>();
|
||||||
this.loadedWorldsMap = new HashMap<>();
|
this.loadedWorldsMap = new HashMap<>();
|
||||||
|
@ -99,7 +96,6 @@ public class WorldManager {
|
||||||
this.playerWorldActions = playerWorldActions;
|
this.playerWorldActions = playerWorldActions;
|
||||||
this.filesManipulator = filesManipulator;
|
this.filesManipulator = filesManipulator;
|
||||||
this.blockSafety = blockSafety;
|
this.blockSafety = blockSafety;
|
||||||
this.safetyTeleporter = safetyTeleporter;
|
|
||||||
this.locationManipulation = locationManipulation;
|
this.locationManipulation = locationManipulation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -297,7 +293,6 @@ public class WorldManager {
|
||||||
world,
|
world,
|
||||||
worldConfig,
|
worldConfig,
|
||||||
blockSafety,
|
blockSafety,
|
||||||
safetyTeleporter,
|
|
||||||
locationManipulation);
|
locationManipulation);
|
||||||
loadedWorldsMap.put(loadedWorld.getName(), loadedWorld);
|
loadedWorldsMap.put(loadedWorld.getName(), loadedWorld);
|
||||||
saveWorldsConfig();
|
saveWorldsConfig();
|
||||||
|
@ -354,7 +349,6 @@ public class WorldManager {
|
||||||
world,
|
world,
|
||||||
worldConfig,
|
worldConfig,
|
||||||
blockSafety,
|
blockSafety,
|
||||||
safetyTeleporter,
|
|
||||||
locationManipulation);
|
locationManipulation);
|
||||||
loadedWorldsMap.put(loadedWorld.getName(), loadedWorld);
|
loadedWorldsMap.put(loadedWorld.getName(), loadedWorld);
|
||||||
saveWorldsConfig();
|
saveWorldsConfig();
|
||||||
|
@ -377,10 +371,6 @@ public class WorldManager {
|
||||||
return worldActionResult(UnloadFailureReason.WORLD_ALREADY_UNLOADING, world.getName());
|
return worldActionResult(UnloadFailureReason.WORLD_ALREADY_UNLOADING, world.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.removePlayers()) {
|
|
||||||
playerWorldActions.removeFromWorld(world);
|
|
||||||
}
|
|
||||||
|
|
||||||
return unloadBukkitWorld(world.getBukkitWorld().getOrNull(), options.saveBukkitWorld()).fold(
|
return unloadBukkitWorld(world.getBukkitWorld().getOrNull(), options.saveBukkitWorld()).fold(
|
||||||
exception -> worldActionResult(UnloadFailureReason.BUKKIT_UNLOAD_FAILED,
|
exception -> worldActionResult(UnloadFailureReason.BUKKIT_UNLOAD_FAILED,
|
||||||
world.getName(), exception),
|
world.getName(), exception),
|
||||||
|
@ -432,7 +422,7 @@ public class WorldManager {
|
||||||
*/
|
*/
|
||||||
public Attempt<String, RemoveFailureReason> removeWorld(@NotNull LoadedMultiverseWorld loadedWorld) {
|
public Attempt<String, RemoveFailureReason> removeWorld(@NotNull LoadedMultiverseWorld loadedWorld) {
|
||||||
// TODO: Config option on removePlayers
|
// TODO: Config option on removePlayers
|
||||||
return unloadWorld(UnloadWorldOptions.world(loadedWorld).removePlayers(true))
|
return unloadWorld(UnloadWorldOptions.world(loadedWorld))
|
||||||
.transform(RemoveFailureReason.UNLOAD_FAILED)
|
.transform(RemoveFailureReason.UNLOAD_FAILED)
|
||||||
.mapAttempt(this::removeWorldFromConfig);
|
.mapAttempt(this::removeWorldFromConfig);
|
||||||
}
|
}
|
||||||
|
@ -594,7 +584,6 @@ public class WorldManager {
|
||||||
*/
|
*/
|
||||||
public Attempt<LoadedMultiverseWorld, RegenFailureReason> regenWorld(@NotNull RegenWorldOptions options) {
|
public Attempt<LoadedMultiverseWorld, RegenFailureReason> regenWorld(@NotNull RegenWorldOptions options) {
|
||||||
LoadedMultiverseWorld world = options.world();
|
LoadedMultiverseWorld world = options.world();
|
||||||
List<Player> playersInWorld = world.getPlayers().getOrElse(Collections.emptyList());
|
|
||||||
DataTransfer<LoadedMultiverseWorld> dataTransfer = transferData(options, world);
|
DataTransfer<LoadedMultiverseWorld> dataTransfer = transferData(options, world);
|
||||||
boolean shouldKeepSpawnLocation = options.keepWorldConfig() && options.seed() == world.getSeed();
|
boolean shouldKeepSpawnLocation = options.keepWorldConfig() && options.seed() == world.getSeed();
|
||||||
Location spawnLocation = world.getSpawnLocation();
|
Location spawnLocation = world.getSpawnLocation();
|
||||||
|
@ -617,7 +606,6 @@ public class WorldManager {
|
||||||
// different seed.
|
// different seed.
|
||||||
newWorld.setSpawnLocation(spawnLocation);
|
newWorld.setSpawnLocation(spawnLocation);
|
||||||
}
|
}
|
||||||
playerWorldActions.teleportPlayersToWorld(playersInWorld, newWorld);
|
|
||||||
saveWorldsConfig();
|
saveWorldsConfig();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -681,12 +669,13 @@ public class WorldManager {
|
||||||
unloadTracker.add(world.getName());
|
unloadTracker.add(world.getName());
|
||||||
if (!Bukkit.unloadWorld(world, save)) {
|
if (!Bukkit.unloadWorld(world, save)) {
|
||||||
// TODO: Localize this, maybe with MultiverseException
|
// TODO: Localize this, maybe with MultiverseException
|
||||||
|
if (!world.getPlayers().isEmpty()) {
|
||||||
|
throw new Exception("There are still players in the world! Please use --remove-players flag to "
|
||||||
|
+ "your command if wish to teleport all players out of the world.");
|
||||||
|
}
|
||||||
throw new Exception("Is this the default world? You can't unload the default world!");
|
throw new Exception("Is this the default world? You can't unload the default world!");
|
||||||
}
|
}
|
||||||
Logging.fine("Bukkit unloaded world: " + world.getName());
|
Logging.fine("Bukkit unloaded world: " + world.getName());
|
||||||
}).onFailure(exception -> {
|
|
||||||
Logging.severe("Failed to unload bukkit world: " + world.getName());
|
|
||||||
exception.printStackTrace();
|
|
||||||
}).andFinally(() -> unloadTracker.remove(world.getName()));
|
}).andFinally(() -> unloadTracker.remove(world.getName()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,6 @@ package org.mvplugins.multiverse.core.world.helpers;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import com.dumptruckman.minecraft.util.Logging;
|
|
||||||
import jakarta.inject.Inject;
|
import jakarta.inject.Inject;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
|
@ -11,7 +10,10 @@ import org.bukkit.entity.Player;
|
||||||
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.SafeTTeleporter;
|
import org.mvplugins.multiverse.core.teleportation.AsyncSafetyTeleporter;
|
||||||
|
import org.mvplugins.multiverse.core.teleportation.TeleportResult;
|
||||||
|
import org.mvplugins.multiverse.core.utils.result.Async;
|
||||||
|
import org.mvplugins.multiverse.core.utils.result.Attempt;
|
||||||
import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld;
|
import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -19,10 +21,10 @@ import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld;
|
||||||
*/
|
*/
|
||||||
@Service
|
@Service
|
||||||
public class PlayerWorldTeleporter {
|
public class PlayerWorldTeleporter {
|
||||||
private final SafeTTeleporter safetyTeleporter;
|
private final AsyncSafetyTeleporter safetyTeleporter;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
PlayerWorldTeleporter(@NotNull SafeTTeleporter safetyTeleporter) {
|
PlayerWorldTeleporter(@NotNull AsyncSafetyTeleporter safetyTeleporter) {
|
||||||
this.safetyTeleporter = safetyTeleporter;
|
this.safetyTeleporter = safetyTeleporter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,11 +32,12 @@ public class PlayerWorldTeleporter {
|
||||||
* Removes all players from the given world.
|
* Removes all players from the given world.
|
||||||
*
|
*
|
||||||
* @param world The world to remove all players from.
|
* @param world The world to remove all players from.
|
||||||
|
* @return A list of async futures that represent the teleportation result of each player.
|
||||||
*/
|
*/
|
||||||
public void removeFromWorld(@NotNull LoadedMultiverseWorld world) {
|
public Async<List<Attempt<Void, TeleportResult.Failure>>> removeFromWorld(@NotNull LoadedMultiverseWorld world) {
|
||||||
// TODO: Better handling of fallback world
|
// TODO: Better handling of fallback world
|
||||||
World toWorld = Bukkit.getWorlds().get(0);
|
World toWorld = Bukkit.getWorlds().get(0);
|
||||||
transferFromWorldTo(world, toWorld);
|
return transferFromWorldTo(world, toWorld);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -42,9 +45,12 @@ public class PlayerWorldTeleporter {
|
||||||
*
|
*
|
||||||
* @param from The world to transfer players from.
|
* @param from The world to transfer players from.
|
||||||
* @param to The location to transfer players to.
|
* @param to The location to transfer players to.
|
||||||
|
* @return A list of async futures that represent the teleportation result of each player.
|
||||||
*/
|
*/
|
||||||
public void transferFromWorldTo(@NotNull LoadedMultiverseWorld from, @NotNull LoadedMultiverseWorld to) {
|
public Async<List<Attempt<Void, TeleportResult.Failure>>> transferFromWorldTo(
|
||||||
transferAllFromWorldToLocation(from, to.getSpawnLocation());
|
@NotNull LoadedMultiverseWorld from,
|
||||||
|
@NotNull LoadedMultiverseWorld to) {
|
||||||
|
return transferAllFromWorldToLocation(from, to.getSpawnLocation());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -52,24 +58,28 @@ public class PlayerWorldTeleporter {
|
||||||
*
|
*
|
||||||
* @param from The world to transfer players from.
|
* @param from The world to transfer players from.
|
||||||
* @param to The world to transfer players to.
|
* @param to The world to transfer players to.
|
||||||
|
* @return A list of async futures that represent the teleportation result of each player.
|
||||||
*/
|
*/
|
||||||
public void transferFromWorldTo(@NotNull LoadedMultiverseWorld from, @NotNull World to) {
|
public Async<List<Attempt<Void, TeleportResult.Failure>>> transferFromWorldTo(
|
||||||
transferAllFromWorldToLocation(from, to.getSpawnLocation());
|
@NotNull LoadedMultiverseWorld from,
|
||||||
|
@NotNull World to) {
|
||||||
|
return transferAllFromWorldToLocation(from, to.getSpawnLocation());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transfers all players from the given world to the given location.
|
* Transfers all players from the given world to the given location.
|
||||||
*
|
*
|
||||||
* @param world The world to transfer players from.
|
* @param world The world to transfer players from.
|
||||||
* @param location The location to transfer players to.
|
* @param location The location to transfer players to.
|
||||||
|
* @return A list of async futures that represent the teleportation result of each player.
|
||||||
*/
|
*/
|
||||||
public void transferAllFromWorldToLocation(@NotNull LoadedMultiverseWorld world, @NotNull Location location) {
|
public Async<List<Attempt<Void, TeleportResult.Failure>>> transferAllFromWorldToLocation(
|
||||||
world.getPlayers().peek(players -> players.forEach(player -> {
|
@NotNull LoadedMultiverseWorld world,
|
||||||
if (player.isOnline()) {
|
@NotNull Location location) {
|
||||||
Logging.fine("Teleporting player '%s' to world spawn: %s", player.getName(), location);
|
return world.getPlayers()
|
||||||
safetyTeleporter.safelyTeleport(null, player, location, true);
|
.map(players -> safetyTeleporter.teleport(players, location))
|
||||||
}
|
.getOrElse(() -> Async.failedFuture(
|
||||||
}));
|
new IllegalStateException("Unable to get players from world" + world.getName())));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -77,13 +87,12 @@ public class PlayerWorldTeleporter {
|
||||||
*
|
*
|
||||||
* @param players The players to teleport.
|
* @param players The players to teleport.
|
||||||
* @param world The world to teleport players to.
|
* @param world The world to teleport players to.
|
||||||
|
* @return A list of async futures that represent the teleportation result of each player.
|
||||||
*/
|
*/
|
||||||
public void teleportPlayersToWorld(@NotNull List<Player> players, @NotNull LoadedMultiverseWorld world) {
|
public Async<List<Attempt<Void, TeleportResult.Failure>>> teleportPlayersToWorld(
|
||||||
players.forEach(player -> {
|
@NotNull List<Player> players,
|
||||||
Location spawnLocation = world.getSpawnLocation();
|
@NotNull LoadedMultiverseWorld world) {
|
||||||
if (player.isOnline()) {
|
Location spawnLocation = world.getSpawnLocation();
|
||||||
safetyTeleporter.safelyTeleport(null, player, spawnLocation, true);
|
return safetyTeleporter.teleport(players, spawnLocation);
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,6 @@ public class UnloadWorldOptions {
|
||||||
}
|
}
|
||||||
|
|
||||||
private final LoadedMultiverseWorld world;
|
private final LoadedMultiverseWorld world;
|
||||||
private boolean removePlayers = false;
|
|
||||||
private boolean saveBukkitWorld = true;
|
private boolean saveBukkitWorld = true;
|
||||||
|
|
||||||
UnloadWorldOptions(LoadedMultiverseWorld world) {
|
UnloadWorldOptions(LoadedMultiverseWorld world) {
|
||||||
|
@ -34,26 +33,6 @@ public class UnloadWorldOptions {
|
||||||
return world;
|
return world;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets whether to teleport the players out from the world before unloading.
|
|
||||||
*
|
|
||||||
* @param removePlayersInput Whether to remove players from the world before unloading.
|
|
||||||
* @return This {@link UnloadWorldOptions} instance.
|
|
||||||
*/
|
|
||||||
public UnloadWorldOptions removePlayers(boolean removePlayersInput) {
|
|
||||||
this.removePlayers = removePlayersInput;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets whether to teleport the players out from the world before unloading.
|
|
||||||
*
|
|
||||||
* @return Whether to remove players from the world before unloading.
|
|
||||||
*/
|
|
||||||
public boolean removePlayers() {
|
|
||||||
return removePlayers;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets whether to save the bukkit world before unloading.
|
* Sets whether to save the bukkit world before unloading.
|
||||||
*
|
*
|
||||||
|
|
|
@ -5,16 +5,15 @@ 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.api.Destination
|
||||||
import org.mvplugins.multiverse.core.api.LocationManipulation
|
import org.mvplugins.multiverse.core.api.LocationManipulation
|
||||||
import org.mvplugins.multiverse.core.api.SafeTTeleporter
|
|
||||||
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
|
||||||
import org.mvplugins.multiverse.core.commandtools.PluginLocales
|
import org.mvplugins.multiverse.core.commandtools.PluginLocales
|
||||||
import org.mvplugins.multiverse.core.config.MVCoreConfig
|
import org.mvplugins.multiverse.core.config.MVCoreConfig
|
||||||
import org.mvplugins.multiverse.core.economy.MVEconomist
|
import org.mvplugins.multiverse.core.economy.MVEconomist
|
||||||
import org.mvplugins.multiverse.core.listeners.*
|
import org.mvplugins.multiverse.core.listeners.*
|
||||||
|
import org.mvplugins.multiverse.core.teleportation.AsyncSafetyTeleporter
|
||||||
import org.mvplugins.multiverse.core.teleportation.SimpleBlockSafety
|
import org.mvplugins.multiverse.core.teleportation.SimpleBlockSafety
|
||||||
import org.mvplugins.multiverse.core.teleportation.SimpleLocationManipulation
|
import org.mvplugins.multiverse.core.teleportation.SimpleLocationManipulation
|
||||||
import org.mvplugins.multiverse.core.teleportation.SimpleSafeTTeleporter
|
|
||||||
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
|
||||||
|
@ -27,6 +26,11 @@ class InjectionTest : TestWithMockBukkit() {
|
||||||
assertNotNull(multiverseCore.getService(AnchorManager::class.java))
|
assertNotNull(multiverseCore.getService(AnchorManager::class.java))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `AsyncSafetyTeleporter is available as a service`() {
|
||||||
|
assertNotNull(multiverseCore.getService(AsyncSafetyTeleporter::class.java))
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `BlockSafety is available as a service`() {
|
fun `BlockSafety is available as a service`() {
|
||||||
assertNotNull(multiverseCore.getService(BlockSafety::class.java))
|
assertNotNull(multiverseCore.getService(BlockSafety::class.java))
|
||||||
|
@ -49,12 +53,6 @@ class InjectionTest : TestWithMockBukkit() {
|
||||||
assertNotNull(multiverseCore.getService(SimpleLocationManipulation::class.java))
|
assertNotNull(multiverseCore.getService(SimpleLocationManipulation::class.java))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `SafeTTeleporter is available as a service`() {
|
|
||||||
assertNotNull(multiverseCore.getService(SafeTTeleporter::class.java))
|
|
||||||
assertNotNull(multiverseCore.getService(SimpleSafeTTeleporter::class.java))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `TeleportQueue is available as a service`() {
|
fun `TeleportQueue is available as a service`() {
|
||||||
assertNotNull(multiverseCore.getService(TeleportQueue::class.java))
|
assertNotNull(multiverseCore.getService(TeleportQueue::class.java))
|
||||||
|
|
Loading…
Reference in New Issue