599 lines
16 KiB
Java
599 lines
16 KiB
Java
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<CommandSourceStack>, 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<CommandResultCallback> 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<Component> 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<String> getOnlinePlayerNames() {
|
|
return Lists.<String>newArrayList(this.server.getPlayerNames());
|
|
}
|
|
|
|
@Override
|
|
public Collection<String> getAllTeams() {
|
|
return this.server.getScoreboard().getTeamNames();
|
|
}
|
|
|
|
@Override
|
|
public Stream<ResourceLocation> getAvailableSounds() {
|
|
return BuiltInRegistries.SOUND_EVENT.stream().map(SoundEvent::location);
|
|
}
|
|
|
|
@Override
|
|
public CompletableFuture<Suggestions> customSuggestion(CommandContext<?> context) {
|
|
return Suggestions.empty();
|
|
}
|
|
|
|
@Override
|
|
public CompletableFuture<Suggestions> suggestRegistryElements(
|
|
ResourceKey<? extends Registry<?>> 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<AdvancementHolder> collection = this.server.getAdvancements().getAllAdvancements();
|
|
return SharedSuggestionProvider.suggestResource(collection.stream().map(AdvancementHolder::id), builder);
|
|
} else {
|
|
return (CompletableFuture<Suggestions>)this.registryAccess().lookup(resourceKey).map(registry -> {
|
|
this.suggestRegistryElements(registry, registryKey, builder);
|
|
return builder.buildFuture();
|
|
}).orElseGet(Suggestions::empty);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public Set<ResourceKey<Level>> levels() {
|
|
return this.server.levelKeys();
|
|
}
|
|
|
|
@Override
|
|
public RegistryAccess registryAccess() {
|
|
return this.server.registryAccess();
|
|
}
|
|
|
|
@Override
|
|
public FeatureFlagSet enabledFeatures() {
|
|
return this.level.enabledFeatures();
|
|
}
|
|
|
|
@Override
|
|
public CommandDispatcher<CommandSourceStack> 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;
|
|
}
|
|
}
|