diff --git a/src/main/java/org/mvplugins/multiverse/core/command/MVCommandCompletions.java b/src/main/java/org/mvplugins/multiverse/core/command/MVCommandCompletions.java index 6a0493785..71acc4e50 100644 --- a/src/main/java/org/mvplugins/multiverse/core/command/MVCommandCompletions.java +++ b/src/main/java/org/mvplugins/multiverse/core/command/MVCommandCompletions.java @@ -27,6 +27,7 @@ import org.bukkit.Material; import org.bukkit.World; import org.bukkit.command.CommandSender; +import org.bukkit.entity.Entity; import org.bukkit.entity.Player; import org.bukkit.entity.SpawnCategory; import org.jetbrains.annotations.NotNull; @@ -43,9 +44,9 @@ import org.mvplugins.multiverse.core.destination.DestinationInstance; import org.mvplugins.multiverse.core.destination.DestinationSuggestionPacket; import org.mvplugins.multiverse.core.destination.DestinationsProvider; +import org.mvplugins.multiverse.core.destination.core.WorldDestination; import org.mvplugins.multiverse.core.permissions.CorePermissionsChecker; import org.mvplugins.multiverse.core.utils.REPatterns; -import org.mvplugins.multiverse.core.utils.StringFormatter; import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld; import org.mvplugins.multiverse.core.world.MultiverseWorld; import org.mvplugins.multiverse.core.world.WorldManager; @@ -100,7 +101,7 @@ public class MVCommandCompletions extends PaperCommandCompletions { registerAsyncCompletion("mvworlds", this::suggestMVWorlds); registerAsyncCompletion("mvworldpropsname", this::suggestMVWorldPropsName); registerAsyncCompletion("mvworldpropsvalue", this::suggestMVWorldPropsValue); - registerAsyncCompletion("playersarray", this::suggestPlayersArray); + registerCompletion("playersarray", this::suggestPlayersArray); // getting online players cannot be async registerStaticCompletion("propsmodifyaction", suggestEnums(PropertyModifyAction.class)); registerStaticCompletion("spawncategories", suggestEnums(SpawnCategory.class)); registerAsyncCompletion("spawncategorypropsname", this::suggestSpawnCategoryPropsName); @@ -224,18 +225,22 @@ private Collection suggestDestinations(BukkitCommandCompletionContext co // Most likely console did not specify a player return Collections.emptyList(); } - if (context.hasConfig("othersOnly") && (players.length == 1 && players[0].equals(context.getIssuer().getIssuer()))) { + CommandSender sender = context.getIssuer().getIssuer(); + if (context.hasConfig("othersOnly") && (players.length == 1 && players[0].equals(sender))) { return Collections.emptyList(); } - return suggestDestinationsWithPerms(context.getIssuer().getIssuer(), players, context.getInput()); + return suggestDestinationsWithPerms(sender, Arrays.asList(players), context.getInput()); }) .getOrElse(Collections.emptyList()); } - private Collection suggestDestinationsWithPerms(CommandSender teleporter, Player[] players, String deststring) { + private Collection suggestDestinationsWithPerms(CommandSender teleporter, List teleportees, String deststring) { return destinationsProvider.suggestDestinations(teleporter, deststring).stream() + .filter(packet -> !config.getSimplifiedDestinationTabCompletion() + || packet.destination() instanceof WorldDestination + || deststring.startsWith(packet.destination().getIdentifier() + ":")) .filter(packet -> corePermissionsChecker - .checkDestinationPacketPermission(teleporter, Arrays.asList(players), packet)) + .checkDestinationPacketPermission(teleporter, teleportees, packet)) .map(DestinationSuggestionPacket::parsableString) .toList(); } diff --git a/src/main/java/org/mvplugins/multiverse/core/commands/TeleportCommand.java b/src/main/java/org/mvplugins/multiverse/core/commands/TeleportCommand.java index 8030c1792..c6d7a6783 100644 --- a/src/main/java/org/mvplugins/multiverse/core/commands/TeleportCommand.java +++ b/src/main/java/org/mvplugins/multiverse/core/commands/TeleportCommand.java @@ -19,7 +19,10 @@ import org.mvplugins.multiverse.core.command.MVCommandIssuer; import org.mvplugins.multiverse.core.command.context.issueraware.PlayerArrayValue; +import org.mvplugins.multiverse.core.command.flag.CommandFlag; +import org.mvplugins.multiverse.core.command.flag.CommandFlagsManager; import org.mvplugins.multiverse.core.command.flag.ParsedCommandFlags; +import org.mvplugins.multiverse.core.command.flags.PageFilterFlags; import org.mvplugins.multiverse.core.command.flags.UnsafeFlags; import org.mvplugins.multiverse.core.config.CoreConfig; import org.mvplugins.multiverse.core.destination.DestinationInstance; @@ -35,14 +38,14 @@ final class TeleportCommand extends CoreCommand { private final CoreConfig config; private final CorePermissionsChecker permissionsChecker; private final AsyncSafetyTeleporter safetyTeleporter; - private final UnsafeFlags flags; + private final Flags flags; @Inject TeleportCommand( @NotNull CoreConfig config, @NotNull CorePermissionsChecker permissionsChecker, @NotNull AsyncSafetyTeleporter safetyTeleporter, - @NotNull UnsafeFlags flags + @NotNull Flags flags ) { this.config = config; this.permissionsChecker = permissionsChecker; @@ -54,14 +57,14 @@ final class TeleportCommand extends CoreCommand { @Subcommand("teleport|tp") @CommandPermission("@mvteleport") @CommandCompletion("@playersarray:checkPermissions=@mvteleportother|@destinations:byIssuerForArg=arg1 " - + "@destinations:notByIssuerForArg=arg1|@flags:byIssuerForArg=arg1,groupName=" + UnsafeFlags.NAME + " " - + "@flags:notByIssuerForArg=arg1,groupName=" + UnsafeFlags.NAME) + + "@destinations:notByIssuerForArg=arg1|@flags:byIssuerForArg=arg1,groupName=" + Flags.NAME + " " + + "@flags:notByIssuerForArg=arg1,groupName=" + Flags.NAME) @Syntax("[player] [--unsafe]") @Description("{@@mv-core.teleport.description}") void onTeleportCommand( MVCommandIssuer issuer, - @Flags("resolve=issuerAware") + @co.aikar.commands.annotation.Flags("resolve=issuerAware") @Syntax("[player]") @Description("{@@mv-core.teleport.player.description}") PlayerArrayValue playersValue, @@ -103,16 +106,21 @@ private void teleportSinglePlayer(MVCommandIssuer issuer, Player player, .checkSafety(!parsedFlags.hasFlag(flags.unsafe) && destination.checkTeleportSafety()) .passengerMode(config.getPassengerMode()) .teleportSingle(player) - .onSuccess(() -> issuer.sendInfo(MVCorei18n.TELEPORT_SUCCESS, - Replace.PLAYER.with(getYouOrName(issuer, player)), - Replace.DESTINATION.with(destination.toString()))) + .onSuccess(() -> { + if (parsedFlags.hasFlag(flags.silent)) { + return; + } + issuer.sendInfo(MVCorei18n.TELEPORT_SUCCESS, + Replace.PLAYER.with(getYouOrName(issuer, player)), + Replace.DESTINATION.with(destination.getDisplayMessage())); + }) .onFailureCount(reasonsCountMap -> { for (var entry : reasonsCountMap.entrySet()) { Logging.finer("Failed to teleport %s players to %s: %s", entry.getValue(), destination, entry.getKey()); issuer.sendError(MVCorei18n.TELEPORT_FAILED, Replace.PLAYER.with(player.getName()), - Replace.DESTINATION.with(destination.toString()), + Replace.DESTINATION.with(destination.getDisplayMessage()), Replace.REASON.with(Message.of(entry.getKey()))); } }); @@ -136,18 +144,38 @@ private void teleportMultiplePlayers(MVCommandIssuer issuer, Player[] players, .checkSafety(!parsedFlags.hasFlag(flags.unsafe) && destination.checkTeleportSafety()) .passengerMode(config.getPassengerMode()) .teleport(List.of(players)) - .onSuccessCount(successCount -> issuer.sendInfo(MVCorei18n.TELEPORT_SUCCESS, - Replace.PLAYER.with(successCount + " players"), - Replace.DESTINATION.with(destination.toString()))) + .onSuccessCount(successCount -> { + if (parsedFlags.hasFlag(flags.silent)) { + return; + } + issuer.sendInfo(MVCorei18n.TELEPORT_SUCCESS, + Replace.PLAYER.with(successCount + " players"), + Replace.DESTINATION.with(destination.getDisplayMessage())); + }) .onFailureCount(reasonsCountMap -> { for (var entry : reasonsCountMap.entrySet()) { Logging.finer("Failed to teleport %s players to %s: %s", entry.getValue(), destination, entry.getKey()); issuer.sendError(MVCorei18n.TELEPORT_FAILED, Replace.PLAYER.with(entry.getValue() + " players"), - Replace.DESTINATION.with(destination.toString()), + Replace.DESTINATION.with(destination.getDisplayMessage()), Replace.REASON.with(Message.of(entry.getKey()))); } }); } + + @Service + private static final class Flags extends UnsafeFlags { + + private static final String NAME = "mvteleport"; + + @Inject + private Flags(@NotNull CommandFlagsManager flagsManager) { + super(NAME, flagsManager); + } + + private final CommandFlag silent = flag(CommandFlag.builder("--silent") + .addAlias("-s") + .build()); + } } diff --git a/src/main/java/org/mvplugins/multiverse/core/config/CoreConfig.java b/src/main/java/org/mvplugins/multiverse/core/config/CoreConfig.java index 70bfb706e..c9f2fbb9a 100644 --- a/src/main/java/org/mvplugins/multiverse/core/config/CoreConfig.java +++ b/src/main/java/org/mvplugins/multiverse/core/config/CoreConfig.java @@ -8,16 +8,13 @@ import com.dumptruckman.minecraft.util.Logging; import io.vavr.control.Try; import jakarta.inject.Inject; -import jakarta.inject.Provider; import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.event.EventPriority; -import org.bukkit.plugin.PluginManager; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jvnet.hk2.annotations.Service; import org.mvplugins.multiverse.core.MultiverseCore; -import org.mvplugins.multiverse.core.command.MVCommandManager; import org.mvplugins.multiverse.core.command.queue.ConfirmMode; import org.mvplugins.multiverse.core.config.handle.CommentedConfigurationHandle; import org.mvplugins.multiverse.core.config.handle.StringPropertyHandle; @@ -28,7 +25,6 @@ import org.mvplugins.multiverse.core.config.migration.action.MoveMigratorAction; import org.mvplugins.multiverse.core.config.migration.VersionMigrator; import org.mvplugins.multiverse.core.config.migration.action.SetMigratorAction; -import org.mvplugins.multiverse.core.destination.DestinationsProvider; import org.mvplugins.multiverse.core.teleportation.PassengerMode; import org.mvplugins.multiverse.core.teleportation.PassengerModes; import org.mvplugins.multiverse.core.world.helpers.DimensionFinder.DimensionFormat; @@ -539,6 +535,16 @@ public boolean getResolveAliasName() { return configHandle.get(configNodes.resolveAliasName); } + @ApiStatus.AvailableSince("5.4") + public Try setSimplifiedDestinationTabCompletion(boolean simplifiedDestinationTabCompletion) { + return configHandle.set(configNodes.simplifiedDestinationTabCompletion, simplifiedDestinationTabCompletion); + } + + @ApiStatus.AvailableSince("5.4") + public boolean getSimplifiedDestinationTabCompletion() { + return configHandle.get(configNodes.simplifiedDestinationTabCompletion); + } + /** * {@inheritDoc} */ diff --git a/src/main/java/org/mvplugins/multiverse/core/config/CoreConfigNodes.java b/src/main/java/org/mvplugins/multiverse/core/config/CoreConfigNodes.java index 9a57f983c..3bf86be34 100644 --- a/src/main/java/org/mvplugins/multiverse/core/config/CoreConfigNodes.java +++ b/src/main/java/org/mvplugins/multiverse/core/config/CoreConfigNodes.java @@ -413,6 +413,15 @@ private N node(N node) { .name("resolve-alias-name") .build()); + final ConfigNode simplifiedDestinationTabCompletion = node(ConfigNode.builder("command.simplified-destination-tab-completion", Boolean.class) + .comment("") + .comment("If this is set to true, Multiverse will only suggest simple destination formats in tab completion.") + .comment("This means only world names will be suggested without other destination type such as `e:worldname:x,y,z` or `p:playername`.") + .comment("Note: This DOES NOT prevent players from using the destinations, as that is controlled by permissions.") + .defaultValue(false) + .name("simplified-destination-tab-completion") + .build()); + final ConfigNode confirmMode = node(ConfigNode.builder("command.confirm-mode", ConfirmMode.class) .comment("") .comment("This config option defines whether `/mv confirm` is needed before running a DANGEROUS action.") diff --git a/src/main/java/org/mvplugins/multiverse/core/destination/DestinationInstance.java b/src/main/java/org/mvplugins/multiverse/core/destination/DestinationInstance.java index e2c04ab7d..63d3d2d26 100644 --- a/src/main/java/org/mvplugins/multiverse/core/destination/DestinationInstance.java +++ b/src/main/java/org/mvplugins/multiverse/core/destination/DestinationInstance.java @@ -4,8 +4,9 @@ import org.bukkit.Location; import org.bukkit.entity.Entity; import org.bukkit.util.Vector; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; -import org.mvplugins.multiverse.core.utils.result.FailureReason; +import org.mvplugins.multiverse.core.locale.message.Message; import org.mvplugins.multiverse.core.world.location.UnloadedWorldLocation; /** @@ -79,6 +80,20 @@ protected DestinationInstance(@NotNull T destination) { */ public abstract @NotNull Option getFinerPermissionSuffix(); + /** + * Gets a user-friendly display text for this destination instance. This is used when displaying the destination + * to the player. By default, this returns the same as {@link #toString()}. Override this method to provide a more + * user-friendly display text with colors, formatting and localization support. + * + * @return The display message. + * @since 5.4 + */ + @ApiStatus.AvailableSince("5.4") + @NotNull + public Message getDisplayMessage() { + return Message.of(this.toString()); + } + /** * Serialises the destination instance to a savable string. * diff --git a/src/main/java/org/mvplugins/multiverse/core/destination/core/AnchorDestinationInstance.java b/src/main/java/org/mvplugins/multiverse/core/destination/core/AnchorDestinationInstance.java index 92b291426..806f2020c 100644 --- a/src/main/java/org/mvplugins/multiverse/core/destination/core/AnchorDestinationInstance.java +++ b/src/main/java/org/mvplugins/multiverse/core/destination/core/AnchorDestinationInstance.java @@ -7,6 +7,7 @@ import org.jetbrains.annotations.NotNull; import org.mvplugins.multiverse.core.destination.DestinationInstance; +import org.mvplugins.multiverse.core.locale.message.Message; /** * Destination instance implementation for the {@link AnchorDestination}. @@ -63,6 +64,15 @@ public boolean checkTeleportSafety() { return Option.of(anchorName); } + /** + * {@inheritDoc} + */ + @Override + public @NotNull Message getDisplayMessage() { + //TODO Localize + return Message.of("anchor '" + anchorName + "'"); + } + /** * {@inheritDoc} */ diff --git a/src/main/java/org/mvplugins/multiverse/core/destination/core/BedDestination.java b/src/main/java/org/mvplugins/multiverse/core/destination/core/BedDestination.java index 7527152cc..06683edc1 100644 --- a/src/main/java/org/mvplugins/multiverse/core/destination/core/BedDestination.java +++ b/src/main/java/org/mvplugins/multiverse/core/destination/core/BedDestination.java @@ -6,7 +6,7 @@ import co.aikar.locales.MessageKey; import co.aikar.locales.MessageKeyProvider; -import org.bukkit.Bukkit; +import jakarta.inject.Inject; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; @@ -20,8 +20,7 @@ import org.mvplugins.multiverse.core.utils.PlayerFinder; import org.mvplugins.multiverse.core.utils.result.Attempt; import org.mvplugins.multiverse.core.utils.result.FailureReason; - -import static org.mvplugins.multiverse.core.locale.message.MessageReplacement.replace; +import org.mvplugins.multiverse.core.world.helpers.ConcurrentPlayerWorldTracker; /** * {@link Destination} implementation for beds. @@ -30,7 +29,11 @@ public final class BedDestination implements Destination { static final String OWN_BED_STRING = "playerbed"; - BedDestination() { + private final ConcurrentPlayerWorldTracker worldTracker; + + @Inject + BedDestination(@NotNull ConcurrentPlayerWorldTracker worldTracker) { + this.worldTracker = worldTracker; } /** @@ -62,8 +65,8 @@ public final class BedDestination implements Destination suggestDestinations( @NotNull CommandSender sender, @Nullable String destinationParams) { - List collect = Bukkit.getOnlinePlayers().stream() - .map(player -> new DestinationSuggestionPacket(this, player.getName(), player.getName())) + List collect = worldTracker.getOnlinePlayers().stream() + .map(player -> new DestinationSuggestionPacket(this, player, player)) .collect(Collectors.toList()); if (sender instanceof Player) { collect.add(new DestinationSuggestionPacket(this, OWN_BED_STRING, OWN_BED_STRING)); diff --git a/src/main/java/org/mvplugins/multiverse/core/destination/core/BedDestinationInstance.java b/src/main/java/org/mvplugins/multiverse/core/destination/core/BedDestinationInstance.java index 5516ac1ce..5144c9245 100644 --- a/src/main/java/org/mvplugins/multiverse/core/destination/core/BedDestinationInstance.java +++ b/src/main/java/org/mvplugins/multiverse/core/destination/core/BedDestinationInstance.java @@ -9,12 +9,13 @@ import org.jetbrains.annotations.Nullable; import org.mvplugins.multiverse.core.destination.DestinationInstance; +import org.mvplugins.multiverse.core.locale.message.Message; /** * Destination instance implementation for the {@link BedDestination}. */ public final class BedDestinationInstance extends DestinationInstance { - private final Player player; + private final @Nullable Player player; /** * Constructor. @@ -64,6 +65,15 @@ public boolean checkTeleportSafety() { return Option.of(player != null ? player.getName() : BedDestination.OWN_BED_STRING); } + /** + * {@inheritDoc} + */ + @Override + public @NotNull Message getDisplayMessage() { + //TODO Localize + return Message.of(player == null ? "your bed/respawn point" : player.getName() + "'s bed/respawn point"); + } + /** * {@inheritDoc} */ diff --git a/src/main/java/org/mvplugins/multiverse/core/destination/core/ExactDestination.java b/src/main/java/org/mvplugins/multiverse/core/destination/core/ExactDestination.java index c37969d05..ddc040f20 100644 --- a/src/main/java/org/mvplugins/multiverse/core/destination/core/ExactDestination.java +++ b/src/main/java/org/mvplugins/multiverse/core/destination/core/ExactDestination.java @@ -23,14 +23,11 @@ import org.mvplugins.multiverse.core.locale.MVCorei18n; import org.mvplugins.multiverse.core.utils.REPatterns; import org.mvplugins.multiverse.core.utils.position.EntityPosition; -import org.mvplugins.multiverse.core.utils.position.PositionNumber; -import org.mvplugins.multiverse.core.utils.position.VectorPosition; import org.mvplugins.multiverse.core.utils.result.Attempt; import org.mvplugins.multiverse.core.utils.result.FailureReason; import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld; import org.mvplugins.multiverse.core.world.WorldManager; import org.mvplugins.multiverse.core.world.entrycheck.WorldEntryCheckerProvider; -import org.mvplugins.multiverse.core.world.location.UnloadedWorldLocation; import static org.mvplugins.multiverse.core.locale.message.MessageReplacement.*; @@ -68,6 +65,7 @@ public ExactDestination(CoreConfig config, WorldManager worldManager, WorldEntry public @NotNull ExactDestinationInstance fromLocation(@NotNull Location location) { return new ExactDestinationInstance( this, + worldManager, location.getWorld().getName(), EntityPosition.ofLocation(location) ); @@ -88,6 +86,7 @@ public ExactDestination(CoreConfig config, WorldManager worldManager, WorldEntry .map(location -> Attempt.success( new ExactDestinationInstance( this, + worldManager, location.getWorld().getName(), EntityPosition.ofLocation(location) ) @@ -112,7 +111,7 @@ public ExactDestination(CoreConfig config, WorldManager worldManager, WorldEntry return Attempt.failure(InstanceFailureReason.INVALID_NUMBER_FORMAT, Replace.ERROR.with(e)); } - return Attempt.success(new ExactDestinationInstance(this, worldName, position)); + return Attempt.success(new ExactDestinationInstance(this, worldManager, worldName, position)); } //TODO: Extract to a world finder class diff --git a/src/main/java/org/mvplugins/multiverse/core/destination/core/ExactDestinationInstance.java b/src/main/java/org/mvplugins/multiverse/core/destination/core/ExactDestinationInstance.java index 3eebadfd8..a5c08c81c 100644 --- a/src/main/java/org/mvplugins/multiverse/core/destination/core/ExactDestinationInstance.java +++ b/src/main/java/org/mvplugins/multiverse/core/destination/core/ExactDestinationInstance.java @@ -9,14 +9,16 @@ import org.jetbrains.annotations.NotNull; import org.mvplugins.multiverse.core.destination.DestinationInstance; +import org.mvplugins.multiverse.core.locale.message.Message; import org.mvplugins.multiverse.core.utils.position.EntityPosition; -import org.mvplugins.multiverse.core.utils.position.VectorPosition; -import org.mvplugins.multiverse.core.world.location.UnloadedWorldLocation; +import org.mvplugins.multiverse.core.world.MultiverseWorld; +import org.mvplugins.multiverse.core.world.WorldManager; /** * Destination instance implementation for the {@link ExactDestination}. */ public final class ExactDestinationInstance extends DestinationInstance { + private final WorldManager worldManager; private final String worldName; private final EntityPosition position; @@ -28,9 +30,11 @@ public final class ExactDestinationInstance extends DestinationInstance { + + private final ConcurrentPlayerWorldTracker playerWorldTracker; + /** * Creates a new instance of the PlayerDestination. */ - PlayerDestination() { + @Inject + PlayerDestination(@NotNull ConcurrentPlayerWorldTracker playerWorldTracker) { + this.playerWorldTracker = playerWorldTracker; } /** @@ -60,8 +65,8 @@ public final class PlayerDestination implements Destination suggestDestinations( @NotNull CommandSender sender, @Nullable String destinationParams) { - return Bukkit.getOnlinePlayers().stream() - .map(p -> new DestinationSuggestionPacket(this, p.getName(), p.getName())) + return playerWorldTracker.getOnlinePlayers().stream() + .map(player -> new DestinationSuggestionPacket(this, player, player)) .toList(); } diff --git a/src/main/java/org/mvplugins/multiverse/core/destination/core/PlayerDestinationInstance.java b/src/main/java/org/mvplugins/multiverse/core/destination/core/PlayerDestinationInstance.java index 83bbbd5b5..67abd08af 100644 --- a/src/main/java/org/mvplugins/multiverse/core/destination/core/PlayerDestinationInstance.java +++ b/src/main/java/org/mvplugins/multiverse/core/destination/core/PlayerDestinationInstance.java @@ -9,6 +9,7 @@ import org.jetbrains.annotations.NotNull; import org.mvplugins.multiverse.core.destination.DestinationInstance; +import org.mvplugins.multiverse.core.locale.message.Message; import java.util.UUID; @@ -66,6 +67,14 @@ public boolean checkTeleportSafety() { return Option.of(playerName); } + /** + * {@inheritDoc} + */ + @Override + public @NotNull Message getDisplayMessage() { + return Message.of(playerName); + } + /** * {@inheritDoc} */ diff --git a/src/main/java/org/mvplugins/multiverse/core/destination/core/WorldDestinationInstance.java b/src/main/java/org/mvplugins/multiverse/core/destination/core/WorldDestinationInstance.java index ad5ba3ce3..1ab20524e 100644 --- a/src/main/java/org/mvplugins/multiverse/core/destination/core/WorldDestinationInstance.java +++ b/src/main/java/org/mvplugins/multiverse/core/destination/core/WorldDestinationInstance.java @@ -8,12 +8,9 @@ import org.jetbrains.annotations.Nullable; import org.mvplugins.multiverse.core.destination.DestinationInstance; -import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld; +import org.mvplugins.multiverse.core.locale.message.Message; import org.mvplugins.multiverse.core.world.MultiverseWorld; -import java.lang.ref.Reference; -import java.lang.ref.WeakReference; - /** * Destination instance implementation for the {@link WorldDestination}. */ @@ -87,6 +84,14 @@ public boolean checkTeleportSafety() { return Option.of(world.getName()); } + /** + * {@inheritDoc} + */ + @Override + public @NotNull Message getDisplayMessage() { + return Message.of(world.getAliasOrName()); + } + /** * {@inheritDoc} */ diff --git a/src/main/java/org/mvplugins/multiverse/core/listeners/MVChatListener.java b/src/main/java/org/mvplugins/multiverse/core/listeners/MVChatListener.java index 948c990af..2c3c5ad0c 100644 --- a/src/main/java/org/mvplugins/multiverse/core/listeners/MVChatListener.java +++ b/src/main/java/org/mvplugins/multiverse/core/listeners/MVChatListener.java @@ -16,6 +16,7 @@ import org.mvplugins.multiverse.core.dynamiclistener.annotations.SkipIfEventExist; import org.mvplugins.multiverse.core.utils.text.ChatTextFormatter; import org.mvplugins.multiverse.core.world.WorldManager; +import org.mvplugins.multiverse.core.world.helpers.ConcurrentPlayerWorldTracker; /** * Multiverse's Listener for players. @@ -24,16 +25,17 @@ final class MVChatListener implements CoreListener { private final CoreConfig config; private final WorldManager worldManager; - private final MVPlayerListener playerListener; + private final ConcurrentPlayerWorldTracker playerWorldTracker; @Inject MVChatListener( CoreConfig config, WorldManager worldManager, - MVPlayerListener playerListener) { + ConcurrentPlayerWorldTracker playerWorldTracker + ) { this.config = config; this.worldManager = worldManager; - this.playerListener = playerListener; + this.playerWorldTracker = playerWorldTracker; } @EventClass("io.papermc.paper.event.player.AsyncChatEvent") @@ -94,12 +96,9 @@ void asyncPlayerChat(AsyncPlayerChatEvent event) { } private String getWorldName(Player player) { - String world = playerListener.getPlayerWorld().get(player.getName()); - if (world == null) { - world = player.getWorld().getName(); - playerListener.getPlayerWorld().put(player.getName(), world); - } - return this.worldManager.getLoadedWorld(world) + String worldName = playerWorldTracker.getPlayerWorld(player.getName()) + .getOrElse(() -> player.getWorld().getName()); + return this.worldManager.getLoadedWorld(worldName) .map(mvworld -> mvworld.isHidden() ? "" : mvworld.getAliasOrName()) .getOrElse(""); } diff --git a/src/main/java/org/mvplugins/multiverse/core/listeners/MVPlayerListener.java b/src/main/java/org/mvplugins/multiverse/core/listeners/MVPlayerListener.java index e570a380c..1e9203c47 100644 --- a/src/main/java/org/mvplugins/multiverse/core/listeners/MVPlayerListener.java +++ b/src/main/java/org/mvplugins/multiverse/core/listeners/MVPlayerListener.java @@ -74,8 +74,6 @@ final class MVPlayerListener implements CoreListener { private final CorePermissionsChecker corePermissionsChecker; private final AsyncSafetyTeleporter asyncSafetyTeleporter; - private final Map playerWorld = new ConcurrentHashMap<>(); - @Inject MVPlayerListener( MultiverseCore plugin, @@ -120,15 +118,6 @@ private PluginLocales getLocales() { return getCommandManager().getLocales(); } - /** - * Gets the map of player and the world name they are in. - * - * @return the playerWorld-map - */ - Map getPlayerWorld() { - return playerWorld; - } - /** * This method is called when a player respawns. * @@ -272,7 +261,6 @@ private void teleportToDestinationOnJoin(Player player, DestinationInstance playerWorldMap; + + @Inject + ConcurrentPlayerWorldTracker(@NotNull MultiverseCore plugin) { + this.playerWorldMap = new ConcurrentHashMap<>(); + plugin.getServer().getPluginManager().registerEvents(this, plugin); + } + + /** + * Get an unmodifiable collection of all online player names on the server. + * + * @return Unmodifiable collection of online player names. + * + * @since 5.4 + */ + @ApiStatus.AvailableSince("5.4") + @NotNull + @UnmodifiableView + public Collection getOnlinePlayers() { + return Collections.unmodifiableCollection(playerWorldMap.keySet()); + } + + /** + * Get the world name a player is currently in. + * + * @param playerName Name of the player. + * @return World name the player is in, or null if the player is not online. + * + * @since 5.4 + */ + @ApiStatus.AvailableSince("5.4") + @NotNull + public Option getPlayerWorld(String playerName) { + return Option.of(playerWorldMap.get(playerName)); + } + + @EventHandler(priority = EventPriority.LOWEST) + private void onPlayerJoin(PlayerJoinEvent event) { + setPlayerWorld(event.getPlayer()); + } + + @EventHandler(priority = EventPriority.LOWEST) + private void onPlayerChangedWorld(@NotNull PlayerChangedWorldEvent event) { + setPlayerWorld(event.getPlayer()); + } + + private void setPlayerWorld(Player player) { + String playerName = player.getName(); + String worldName = player.getWorld().getName(); + playerWorldMap.put(playerName, worldName); + } + + @EventHandler + private void onPlayerQuit(@NotNull PlayerQuitEvent event) { + String playerName = event.getPlayer().getName(); + playerWorldMap.remove(playerName); + } +} diff --git a/src/test/resources/configs/fresh_config.yml b/src/test/resources/configs/fresh_config.yml index 0df83a617..b258b7de4 100644 --- a/src/test/resources/configs/fresh_config.yml +++ b/src/test/resources/configs/fresh_config.yml @@ -41,6 +41,7 @@ messaging: command: resolve-alias-name: true + simplified-destination-tab-completion: false confirm-mode: enable use-confirm-otp: true confirm-timeout: 30