From fbb44c2570be8dd758162384a3d394c4f76d5289 Mon Sep 17 00:00:00 2001 From: John Paul R Date: Mon, 19 Oct 2026 17:46:17 -0400 Subject: [PATCH] feat: tp toggle --- changeset.md | 8 ++ docs/Feature-Guide.md | 1 + docs/List-of-Commands-&-Permissions.md | 1 + docs/Permissions-Quickstart.md | 2 + docs/Player-Profiles.md | 19 +++++ .../fibermc/essentialcommands/ECPerms.java | 3 +- .../EssentialCommandRegistry.java | 12 +++ .../commands/ProfileCommand.java | 6 +- .../commands/TeleportAskCommand.java | 4 + .../commands/TeleportAskHereCommand.java | 4 + .../commands/TeleportToggleCommand.java | 73 +++++++++++++++++++ .../playerdata/PlayerProfile.java | 40 ++++++++++ .../types/AllowTeleportRequests.java | 29 ++++++++ .../types/ProfileOption.java | 18 ++++- .../types/ProfileOptionFromContextSetter.java | 4 +- .../data/essential_commands/lang/en_us.json | 7 ++ 16 files changed, 227 insertions(+), 4 deletions(-) create mode 100644 src/main/java/com/fibermc/essentialcommands/commands/TeleportToggleCommand.java create mode 100644 src/main/java/com/fibermc/essentialcommands/types/AllowTeleportRequests.java diff --git a/changeset.md b/changeset.md index 0ff24f83..5665bbfd 100644 --- a/changeset.md +++ b/changeset.md @@ -1,3 +1,11 @@ +Features: + +- Add `/tptoggle [on/off/off-silent]` (permission `essentialcommands.tptoggle`) + to control whether other players can send you teleport requests. Backed by + the new `allowTeleportRequests` player profile option. + - `off` auto-denies requests and tells you someone tried. + - `off-silent` auto-denies requests without notifying you. + Fixes: - Fix OPs being unable to set styled nicknames for other players when diff --git a/docs/Feature-Guide.md b/docs/Feature-Guide.md index a3135f35..18267710 100644 --- a/docs/Feature-Guide.md +++ b/docs/Feature-Guide.md @@ -37,6 +37,7 @@ Essential Commands provides a request-based teleportation system that allows any | Accept teleport request | `/tpaccept ` | `essentialcommands.tpaccept` | | Deny teleport request | `/tpdeny ` | `essentialcommands.tpdeny` | | Cancel teleport request | `/tpcancel ` | Same as original teleport | +| Toggle receiving teleport requests | `/tptoggle [on/off/off-silent]` | `essentialcommands.tptoggle` | **Related Config Options:** - `teleport_request_duration` - How long teleport requests remain valid (seconds) - Default: `60` diff --git a/docs/List-of-Commands-&-Permissions.md b/docs/List-of-Commands-&-Permissions.md index 058e0c55..01eb1d88 100644 --- a/docs/List-of-Commands-&-Permissions.md +++ b/docs/List-of-Commands-&-Permissions.md @@ -21,6 +21,7 @@ Grant access to all subcommands using wildcards, like so: | /tpahere \ | `essentialcommands.tpahere` | Request that a player teleports to you. | | /tpaccept \ | `essentialcommands.tpaccept` | Accept player's teleport request. | | /tpdeny \ | `essentialcommands.tpdeny` | Deny player's teleport request. | +| /tptoggle [on/off/off-silent] | `essentialcommands.tptoggle` | Toggle whether you accept teleport requests. See [Player Profiles](Player-Profiles.md). | | /home set \ | `essentialcommands.home.set` | Set a personal home location. | | /home tp \ | `essentialcommands.home.tp` | Teleport to your home. | | /home delete \ | `essentialcommands.home.delete` | Delete your home. | diff --git a/docs/Permissions-Quickstart.md b/docs/Permissions-Quickstart.md index 62b2ae0d..eb49ab14 100644 --- a/docs/Permissions-Quickstart.md +++ b/docs/Permissions-Quickstart.md @@ -36,6 +36,7 @@ teleport to `warp`s, and to use `/rtp`: /lp group default permission set essentialcommands.tpahere true /lp group default permission set essentialcommands.tpaccept true /lp group default permission set essentialcommands.tpdeny true +/lp group default permission set essentialcommands.tptoggle true /lp group default permission set essentialcommands.home.{set,tp,delete} true /lp group default permission set essentialcommands.warp.tp true @@ -76,6 +77,7 @@ this: "essentialcommands.tpahere": "allow", "essentialcommands.tpaccept": "allow", "essentialcommands.tpdeny": "allow", + "essentialcommands.tptoggle": "allow", "essentialcommands.home.set": "allow", "essentialcommands.home.tp": "allow", "essentialcommands.home.delete": "allow", diff --git a/docs/Player-Profiles.md b/docs/Player-Profiles.md index e9e1ef76..96c6c2e6 100644 --- a/docs/Player-Profiles.md +++ b/docs/Player-Profiles.md @@ -10,3 +10,22 @@ Essential Commands are presented: - `/essentialcommands profile set formattingDefault ` - `/essentialcommands profile set formattingAccent ` - `/essentialcommands profile set printTeleportCoordinates ` + +## Teleport Requests + +`allowTeleportRequests` controls whether other players can send you teleport +requests (`/tpa` and `/tpahere`). It defaults to `on`. + +- `on`: requests are delivered as normal. +- `off`: requests are auto-denied. You get a short notice in chat that someone + tried, and the sender is told you saw it. +- `off-silent`: requests are auto-denied, and nothing appears in your chat. The + sender is told you did not see it. Use this to stop request spam. + +Set it with `/essentialcommands profile set allowTeleportRequests `, +or with the `/tptoggle` shorthand: + +- `/tptoggle`: switch between `on` and `off` (switches to `on` from `off-silent`) +- `/tptoggle on`, `/tptoggle off`, `/tptoggle off-silent`: set it directly + +Requests that are already pending when you turn this off are not affected. diff --git a/src/main/java/com/fibermc/essentialcommands/ECPerms.java b/src/main/java/com/fibermc/essentialcommands/ECPerms.java index 57d204f9..f3dd71b5 100644 --- a/src/main/java/com/fibermc/essentialcommands/ECPerms.java +++ b/src/main/java/com/fibermc/essentialcommands/ECPerms.java @@ -27,6 +27,7 @@ public static final class Registry { public static final Identifier tpahere = of("tpahere"); public static final Identifier tpaccept = of("tpaccept"); public static final Identifier tpdeny = of("tpdeny"); + public static final Identifier tptoggle = of("tptoggle"); public static final Identifier home_set = of("home.set"); public static final Identifier home_tp = of("home.tp"); public static final Identifier home_tp_others = of("home_tp_others"); @@ -89,7 +90,7 @@ public static final class Registry { public static final Identifier admin_lastpos = of("admin.lastpos"); public static final class Group { - public static final Identifier[] tpa_group = {tpa, tpahere, tpaccept, tpdeny}; + public static final Identifier[] tpa_group = {tpa, tpahere, tpaccept, tpdeny, tptoggle}; public static final Identifier[] home_group = {home_set, home_tp, home_delete}; public static final Identifier[] warp_group = {warp_set, warp_tp, warp_delete}; public static final Identifier[] spawn_group = {spawn_tp, spawn_set}; diff --git a/src/main/java/com/fibermc/essentialcommands/EssentialCommandRegistry.java b/src/main/java/com/fibermc/essentialcommands/EssentialCommandRegistry.java index 7b7cdae7..9d38e177 100644 --- a/src/main/java/com/fibermc/essentialcommands/EssentialCommandRegistry.java +++ b/src/main/java/com/fibermc/essentialcommands/EssentialCommandRegistry.java @@ -14,6 +14,7 @@ import com.fibermc.essentialcommands.commands.suggestions.WarpSuggestion; import com.fibermc.essentialcommands.commands.utility.*; import com.fibermc.essentialcommands.text.ECText; +import com.fibermc.essentialcommands.types.AllowTeleportRequests; import com.fibermc.essentialcommands.types.NamedMinecraftLocation; import com.fibermc.essentialcommands.util.EssentialsConvertor; import com.fibermc.essentialcommands.util.EssentialsXParser; @@ -113,6 +114,17 @@ public static void register( .then(NicknameTargetResolver.targetPlayerArgument() .executes(new TeleportAskHereCommand())) .build()); + + registerNode.accept(Commands.literal("tptoggle") + .requires(ECPerms.require(ECPerms.Registry.tptoggle, 0)) + .executes(TeleportToggleCommand::toggle) + .then(Commands.literal("on") + .executes(TeleportToggleCommand.setTo(AllowTeleportRequests.ON))) + .then(Commands.literal("off") + .executes(TeleportToggleCommand.setTo(AllowTeleportRequests.OFF))) + .then(Commands.literal("off-silent") + .executes(TeleportToggleCommand.setTo(AllowTeleportRequests.OFF_SILENT))) + .build()); } if (CONFIG.ENABLE_HOME) { diff --git a/src/main/java/com/fibermc/essentialcommands/commands/ProfileCommand.java b/src/main/java/com/fibermc/essentialcommands/commands/ProfileCommand.java index f9071d76..17127782 100644 --- a/src/main/java/com/fibermc/essentialcommands/commands/ProfileCommand.java +++ b/src/main/java/com/fibermc/essentialcommands/commands/ProfileCommand.java @@ -27,8 +27,12 @@ public static LiteralCommandNode buildNode() { var name = entry.getKey(); var option = entry.getValue(); + var valueArgument = argument("value", option.argumentType()); + if (option.suggestionProvider() != null) { + valueArgument.suggests(option.suggestionProvider()); + } set.then(Commands.literal(name) - .then(argument("value", option.argumentType()).executes((context) -> { + .then(valueArgument.executes((context) -> { var player = context.getSource().getPlayerOrException(); var profile = ((ServerPlayerEntityAccess) player).ec$getProfile(); option.profileSetter().setValue(context, "value", profile); diff --git a/src/main/java/com/fibermc/essentialcommands/commands/TeleportAskCommand.java b/src/main/java/com/fibermc/essentialcommands/commands/TeleportAskCommand.java index 4c4f63e3..b19eacef 100644 --- a/src/main/java/com/fibermc/essentialcommands/commands/TeleportAskCommand.java +++ b/src/main/java/com/fibermc/essentialcommands/commands/TeleportAskCommand.java @@ -40,6 +40,10 @@ public int run(CommandContext context) throws CommandSyntaxE } } + if (TeleportToggleCommand.denyIfDisallowed(senderPlayer, targetPlayer)) { + return 0; + } + //inform target player of tp request via chat var targetPlayerEcText = ECText.access(targetPlayer); var targetPlayerProfile = PlayerProfile.access(targetPlayer); diff --git a/src/main/java/com/fibermc/essentialcommands/commands/TeleportAskHereCommand.java b/src/main/java/com/fibermc/essentialcommands/commands/TeleportAskHereCommand.java index 4d40882d..179b211a 100644 --- a/src/main/java/com/fibermc/essentialcommands/commands/TeleportAskHereCommand.java +++ b/src/main/java/com/fibermc/essentialcommands/commands/TeleportAskHereCommand.java @@ -38,6 +38,10 @@ public int run(CommandContext context) throws CommandSyntaxE } } + if (TeleportToggleCommand.denyIfDisallowed(senderPlayer, targetPlayer)) { + return 0; + } + //inform target player of tp request via chat var targetPlayerEcText = ECText.access(targetPlayer); targetPlayerData.sendMessage( diff --git a/src/main/java/com/fibermc/essentialcommands/commands/TeleportToggleCommand.java b/src/main/java/com/fibermc/essentialcommands/commands/TeleportToggleCommand.java new file mode 100644 index 00000000..06d48d75 --- /dev/null +++ b/src/main/java/com/fibermc/essentialcommands/commands/TeleportToggleCommand.java @@ -0,0 +1,73 @@ +package com.fibermc.essentialcommands.commands; + +import com.fibermc.essentialcommands.playerdata.PlayerData; +import com.fibermc.essentialcommands.playerdata.PlayerProfile; +import com.fibermc.essentialcommands.text.TextFormatType; +import com.fibermc.essentialcommands.types.AllowTeleportRequests; + +import com.mojang.brigadier.Command; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; + +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.server.level.ServerPlayer; + +public final class TeleportToggleCommand { + private TeleportToggleCommand() {} + + public static int toggle(CommandContext context) throws CommandSyntaxException { + var profile = PlayerProfile.accessFromContextOrThrow(context); + var newValue = profile.getAllowTeleportRequests() == AllowTeleportRequests.ON + ? AllowTeleportRequests.OFF + : AllowTeleportRequests.ON; + return set(context, newValue); + } + + public static Command setTo(AllowTeleportRequests value) { + return (context) -> set(context, value); + } + + private static int set(CommandContext context, AllowTeleportRequests value) + throws CommandSyntaxException + { + var player = context.getSource().getPlayerOrException(); + var profile = PlayerProfile.access(player); + profile.setAllowTeleportRequests(value); + profile.setDirty(); + profile.save(context.getSource().getServer().registryAccess()); + + PlayerData.access(player).sendCommandFeedback("cmd.tptoggle.feedback." + value.getId()); + return Command.SINGLE_SUCCESS; + } + + /** + * Auto-denies a tpa/tpahere request if the target does not allow teleport requests, notifying the sender (and, + * unless silent, the target). + * + * @return true if the request was denied and should not be sent. + */ + static boolean denyIfDisallowed(ServerPlayer senderPlayer, ServerPlayer targetPlayer) { + var targetPlayerProfile = PlayerProfile.access(targetPlayer); + var senderPlayerProfile = PlayerProfile.access(senderPlayer); + var targetPlayerText = targetPlayer.getDisplayName().copy() + .withStyle(senderPlayerProfile.getStyle(TextFormatType.Accent)); + + switch (targetPlayerProfile.getAllowTeleportRequests()) { + case ON -> { + return false; + } + case OFF -> { + PlayerData.access(targetPlayer).sendMessage( + "cmd.tpask.receive.disallowed", + senderPlayer.getDisplayName().copy().withStyle(targetPlayerProfile.getStyle(TextFormatType.Accent))); + PlayerData.access(senderPlayer).sendCommandError("cmd.tpask.error.disallowed", targetPlayerText); + return true; + } + case OFF_SILENT -> { + PlayerData.access(senderPlayer).sendCommandError("cmd.tpask.error.disallowed_silent", targetPlayerText); + return true; + } + } + throw new IllegalStateException(); + } +} diff --git a/src/main/java/com/fibermc/essentialcommands/playerdata/PlayerProfile.java b/src/main/java/com/fibermc/essentialcommands/playerdata/PlayerProfile.java index 52c58f7c..120fa267 100644 --- a/src/main/java/com/fibermc/essentialcommands/playerdata/PlayerProfile.java +++ b/src/main/java/com/fibermc/essentialcommands/playerdata/PlayerProfile.java @@ -3,11 +3,15 @@ import java.io.File; import java.io.IOException; import java.util.AbstractMap.SimpleEntry; +import java.util.Arrays; import java.util.Map; import java.util.Optional; import com.fibermc.essentialcommands.EssentialCommands; import com.fibermc.essentialcommands.access.ServerPlayerEntityAccess; +import com.fibermc.essentialcommands.commands.CommandUtil; +import com.fibermc.essentialcommands.text.ECText; +import com.fibermc.essentialcommands.types.AllowTeleportRequests; import com.fibermc.essentialcommands.types.IStyleProvider; import com.fibermc.essentialcommands.types.ProfileOption; import org.jetbrains.annotations.NotNull; @@ -19,9 +23,11 @@ import com.mojang.brigadier.exceptions.CommandSyntaxException; import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.SharedSuggestionProvider; import net.minecraft.core.HolderLookup; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.NbtIo; +import net.minecraft.network.chat.Component; import net.minecraft.network.chat.Style; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.level.saveddata.SavedData; @@ -46,6 +52,7 @@ private static final class ProfileOptions { private Optional