package net.minecraft.commands; import com.google.common.collect.Lists; import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.Message; import com.mojang.brigadier.context.CommandContext; import com.mojang.brigadier.exceptions.CommandExceptionType; import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; import com.mojang.brigadier.suggestion.Suggestions; import com.mojang.brigadier.suggestion.SuggestionsBuilder; import java.util.Collection; import java.util.Objects; import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.function.BinaryOperator; import java.util.function.Supplier; import java.util.stream.Stream; import net.minecraft.ChatFormatting; import net.minecraft.advancements.AdvancementHolder; import net.minecraft.commands.arguments.EntityAnchorArgument.Anchor; import net.minecraft.commands.execution.TraceCallbacks; import net.minecraft.core.Registry; import net.minecraft.core.RegistryAccess; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.core.registries.Registries; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.ComponentUtils; import net.minecraft.network.chat.OutgoingChatMessage; import net.minecraft.network.chat.ChatType.Bound; import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.sounds.SoundEvent; import net.minecraft.util.Mth; import net.minecraft.util.TaskChainer; import net.minecraft.world.entity.Entity; import net.minecraft.world.flag.FeatureFlagSet; import net.minecraft.world.level.GameRules; import net.minecraft.world.level.Level; import net.minecraft.world.level.dimension.DimensionType; import net.minecraft.world.phys.Vec2; import net.minecraft.world.phys.Vec3; import org.jetbrains.annotations.Nullable; public class CommandSourceStack implements ExecutionCommandSource, SharedSuggestionProvider { public static final SimpleCommandExceptionType ERROR_NOT_PLAYER = new SimpleCommandExceptionType(Component.translatable("permissions.requires.player")); public static final SimpleCommandExceptionType ERROR_NOT_ENTITY = new SimpleCommandExceptionType(Component.translatable("permissions.requires.entity")); private final CommandSource source; private final Vec3 worldPosition; private final ServerLevel level; private final int permissionLevel; private final String textName; private final Component displayName; private final MinecraftServer server; private final boolean silent; @Nullable private final Entity entity; private final CommandResultCallback resultCallback; private final Anchor anchor; private final Vec2 rotation; private final CommandSigningContext signingContext; private final TaskChainer chatMessageChainer; public CommandSourceStack( CommandSource source, Vec3 worldPosition, Vec2 rotation, ServerLevel level, int permissionLevel, String textName, Component displayName, MinecraftServer server, @Nullable Entity entity ) { this( source, worldPosition, rotation, level, permissionLevel, textName, displayName, server, entity, false, CommandResultCallback.EMPTY, Anchor.FEET, CommandSigningContext.ANONYMOUS, TaskChainer.immediate(server) ); } protected CommandSourceStack( CommandSource source, Vec3 worldPosition, Vec2 rotation, ServerLevel level, int permissionLevel, String textName, Component displayName, MinecraftServer server, @Nullable Entity entity, boolean silent, CommandResultCallback resultCallback, Anchor anchor, CommandSigningContext signingContext, TaskChainer chatMessageChainer ) { this.source = source; this.worldPosition = worldPosition; this.level = level; this.silent = silent; this.entity = entity; this.permissionLevel = permissionLevel; this.textName = textName; this.displayName = displayName; this.server = server; this.resultCallback = resultCallback; this.anchor = anchor; this.rotation = rotation; this.signingContext = signingContext; this.chatMessageChainer = chatMessageChainer; } public CommandSourceStack withSource(CommandSource source) { return this.source == source ? this : new CommandSourceStack( source, this.worldPosition, this.rotation, this.level, this.permissionLevel, this.textName, this.displayName, this.server, this.entity, this.silent, this.resultCallback, this.anchor, this.signingContext, this.chatMessageChainer ); } public CommandSourceStack withEntity(Entity entity) { return this.entity == entity ? this : new CommandSourceStack( this.source, this.worldPosition, this.rotation, this.level, this.permissionLevel, entity.getName().getString(), entity.getDisplayName(), this.server, entity, this.silent, this.resultCallback, this.anchor, this.signingContext, this.chatMessageChainer ); } public CommandSourceStack withPosition(Vec3 pos) { return this.worldPosition.equals(pos) ? this : new CommandSourceStack( this.source, pos, this.rotation, this.level, this.permissionLevel, this.textName, this.displayName, this.server, this.entity, this.silent, this.resultCallback, this.anchor, this.signingContext, this.chatMessageChainer ); } public CommandSourceStack withRotation(Vec2 rotation) { return this.rotation.equals(rotation) ? this : new CommandSourceStack( this.source, this.worldPosition, rotation, this.level, this.permissionLevel, this.textName, this.displayName, this.server, this.entity, this.silent, this.resultCallback, this.anchor, this.signingContext, this.chatMessageChainer ); } public CommandSourceStack withCallback(CommandResultCallback commandResultCallback) { return Objects.equals(this.resultCallback, commandResultCallback) ? this : new CommandSourceStack( this.source, this.worldPosition, this.rotation, this.level, this.permissionLevel, this.textName, this.displayName, this.server, this.entity, this.silent, commandResultCallback, this.anchor, this.signingContext, this.chatMessageChainer ); } public CommandSourceStack withCallback(CommandResultCallback callback, BinaryOperator operator) { CommandResultCallback commandResultCallback = (CommandResultCallback)operator.apply(this.resultCallback, callback); return this.withCallback(commandResultCallback); } public CommandSourceStack withSuppressedOutput() { return !this.silent && !this.source.alwaysAccepts() ? new CommandSourceStack( this.source, this.worldPosition, this.rotation, this.level, this.permissionLevel, this.textName, this.displayName, this.server, this.entity, true, this.resultCallback, this.anchor, this.signingContext, this.chatMessageChainer ) : this; } public CommandSourceStack withPermission(int permissionLevel) { return permissionLevel == this.permissionLevel ? this : new CommandSourceStack( this.source, this.worldPosition, this.rotation, this.level, permissionLevel, this.textName, this.displayName, this.server, this.entity, this.silent, this.resultCallback, this.anchor, this.signingContext, this.chatMessageChainer ); } public CommandSourceStack withMaximumPermission(int permissionLevel) { return permissionLevel <= this.permissionLevel ? this : new CommandSourceStack( this.source, this.worldPosition, this.rotation, this.level, permissionLevel, this.textName, this.displayName, this.server, this.entity, this.silent, this.resultCallback, this.anchor, this.signingContext, this.chatMessageChainer ); } public CommandSourceStack withAnchor(Anchor anchor) { return anchor == this.anchor ? this : new CommandSourceStack( this.source, this.worldPosition, this.rotation, this.level, this.permissionLevel, this.textName, this.displayName, this.server, this.entity, this.silent, this.resultCallback, anchor, this.signingContext, this.chatMessageChainer ); } public CommandSourceStack withLevel(ServerLevel level) { if (level == this.level) { return this; } else { double d = DimensionType.getTeleportationScale(this.level.dimensionType(), level.dimensionType()); Vec3 vec3 = new Vec3(this.worldPosition.x * d, this.worldPosition.y, this.worldPosition.z * d); return new CommandSourceStack( this.source, vec3, this.rotation, level, this.permissionLevel, this.textName, this.displayName, this.server, this.entity, this.silent, this.resultCallback, this.anchor, this.signingContext, this.chatMessageChainer ); } } public CommandSourceStack facing(Entity entity, Anchor anchor) { return this.facing(anchor.apply(entity)); } public CommandSourceStack facing(Vec3 lookPos) { Vec3 vec3 = this.anchor.apply(this); double d = lookPos.x - vec3.x; double e = lookPos.y - vec3.y; double f = lookPos.z - vec3.z; double g = Math.sqrt(d * d + f * f); float h = Mth.wrapDegrees((float)(-(Mth.atan2(e, g) * 180.0F / (float)Math.PI))); float i = Mth.wrapDegrees((float)(Mth.atan2(f, d) * 180.0F / (float)Math.PI) - 90.0F); return this.withRotation(new Vec2(h, i)); } public CommandSourceStack withSigningContext(CommandSigningContext signingContext, TaskChainer chatMessageChainer) { return signingContext == this.signingContext && chatMessageChainer == this.chatMessageChainer ? this : new CommandSourceStack( this.source, this.worldPosition, this.rotation, this.level, this.permissionLevel, this.textName, this.displayName, this.server, this.entity, this.silent, this.resultCallback, this.anchor, signingContext, chatMessageChainer ); } public Component getDisplayName() { return this.displayName; } public String getTextName() { return this.textName; } @Override public boolean hasPermission(int permissionLevel) { return this.permissionLevel >= permissionLevel; } public Vec3 getPosition() { return this.worldPosition; } public ServerLevel getLevel() { return this.level; } @Nullable public Entity getEntity() { return this.entity; } public Entity getEntityOrException() throws CommandSyntaxException { if (this.entity == null) { throw ERROR_NOT_ENTITY.create(); } else { return this.entity; } } public ServerPlayer getPlayerOrException() throws CommandSyntaxException { if (this.entity instanceof ServerPlayer serverPlayer) { return serverPlayer; } else { throw ERROR_NOT_PLAYER.create(); } } @Nullable public ServerPlayer getPlayer() { return this.entity instanceof ServerPlayer serverPlayer ? serverPlayer : null; } public boolean isPlayer() { return this.entity instanceof ServerPlayer; } public Vec2 getRotation() { return this.rotation; } public MinecraftServer getServer() { return this.server; } public Anchor getAnchor() { return this.anchor; } public CommandSigningContext getSigningContext() { return this.signingContext; } public TaskChainer getChatMessageChainer() { return this.chatMessageChainer; } public boolean shouldFilterMessageTo(ServerPlayer receiver) { ServerPlayer serverPlayer = this.getPlayer(); return receiver == serverPlayer ? false : serverPlayer != null && serverPlayer.isTextFilteringEnabled() || receiver.isTextFilteringEnabled(); } public void sendChatMessage(OutgoingChatMessage message, boolean shouldFilter, Bound boundChatType) { if (!this.silent) { ServerPlayer serverPlayer = this.getPlayer(); if (serverPlayer != null) { serverPlayer.sendChatMessage(message, shouldFilter, boundChatType); } else { this.source.sendSystemMessage(boundChatType.decorate(message.content())); } } } public void sendSystemMessage(Component message) { if (!this.silent) { ServerPlayer serverPlayer = this.getPlayer(); if (serverPlayer != null) { serverPlayer.sendSystemMessage(message); } else { this.source.sendSystemMessage(message); } } } public void sendSuccess(Supplier messageSupplier, boolean allowLogging) { boolean bl = this.source.acceptsSuccess() && !this.silent; boolean bl2 = allowLogging && this.source.shouldInformAdmins() && !this.silent; if (bl || bl2) { Component component = (Component)messageSupplier.get(); if (bl) { this.source.sendSystemMessage(component); } if (bl2) { this.broadcastToAdmins(component); } } } private void broadcastToAdmins(Component message) { Component component = Component.translatable("chat.type.admin", this.getDisplayName(), message).withStyle(ChatFormatting.GRAY, ChatFormatting.ITALIC); if (this.server.getGameRules().getBoolean(GameRules.RULE_SENDCOMMANDFEEDBACK)) { for (ServerPlayer serverPlayer : this.server.getPlayerList().getPlayers()) { if (serverPlayer.commandSource() != this.source && this.server.getPlayerList().isOp(serverPlayer.getGameProfile())) { serverPlayer.sendSystemMessage(component); } } } if (this.source != this.server && this.server.getGameRules().getBoolean(GameRules.RULE_LOGADMINCOMMANDS)) { this.server.sendSystemMessage(component); } } public void sendFailure(Component message) { if (this.source.acceptsFailure() && !this.silent) { this.source.sendSystemMessage(Component.empty().append(message).withStyle(ChatFormatting.RED)); } } @Override public CommandResultCallback callback() { return this.resultCallback; } @Override public Collection getOnlinePlayerNames() { return Lists.newArrayList(this.server.getPlayerNames()); } @Override public Collection getAllTeams() { return this.server.getScoreboard().getTeamNames(); } @Override public Stream getAvailableSounds() { return BuiltInRegistries.SOUND_EVENT.stream().map(SoundEvent::location); } @Override public CompletableFuture customSuggestion(CommandContext context) { return Suggestions.empty(); } @Override public CompletableFuture suggestRegistryElements( ResourceKey> resourceKey, SharedSuggestionProvider.ElementSuggestionType registryKey, SuggestionsBuilder builder, CommandContext context ) { if (resourceKey == Registries.RECIPE) { return SharedSuggestionProvider.suggestResource( this.server.getRecipeManager().getRecipes().stream().map(recipeHolder -> recipeHolder.id().location()), builder ); } else if (resourceKey == Registries.ADVANCEMENT) { Collection collection = this.server.getAdvancements().getAllAdvancements(); return SharedSuggestionProvider.suggestResource(collection.stream().map(AdvancementHolder::id), builder); } else { return (CompletableFuture)this.registryAccess().lookup(resourceKey).map(registry -> { this.suggestRegistryElements(registry, registryKey, builder); return builder.buildFuture(); }).orElseGet(Suggestions::empty); } } @Override public Set> levels() { return this.server.levelKeys(); } @Override public RegistryAccess registryAccess() { return this.server.registryAccess(); } @Override public FeatureFlagSet enabledFeatures() { return this.level.enabledFeatures(); } @Override public CommandDispatcher dispatcher() { return this.getServer().getFunctions().getDispatcher(); } @Override public void handleError(CommandExceptionType exceptionType, Message message, boolean success, @Nullable TraceCallbacks traceCallbacks) { if (traceCallbacks != null) { traceCallbacks.onError(message.getString()); } if (!success) { this.sendFailure(ComponentUtils.fromMessage(message)); } } @Override public boolean isSilent() { return this.silent; } }