minecraft-src/net/minecraft/commands/Commands.java
2025-07-04 03:45:38 +03:00

510 lines
23 KiB
Java

package net.minecraft.commands;
import com.google.common.collect.Maps;
import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.ParseResults;
import com.mojang.brigadier.StringReader;
import com.mojang.brigadier.arguments.ArgumentType;
import com.mojang.brigadier.builder.ArgumentBuilder;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.builder.RequiredArgumentBuilder;
import com.mojang.brigadier.context.CommandContextBuilder;
import com.mojang.brigadier.context.ContextChain;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.brigadier.tree.CommandNode;
import com.mojang.brigadier.tree.RootCommandNode;
import com.mojang.logging.LogUtils;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.function.UnaryOperator;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import net.minecraft.ChatFormatting;
import net.minecraft.SharedConstants;
import net.minecraft.Util;
import net.minecraft.commands.Commands.1.1;
import net.minecraft.commands.execution.ExecutionContext;
import net.minecraft.commands.synchronization.ArgumentTypeInfos;
import net.minecraft.commands.synchronization.ArgumentUtils;
import net.minecraft.commands.synchronization.SuggestionProviders;
import net.minecraft.core.HolderLookup;
import net.minecraft.core.Registry;
import net.minecraft.core.HolderLookup.RegistryLookup.Delegate;
import net.minecraft.data.registries.VanillaRegistries;
import net.minecraft.gametest.framework.TestCommand;
import net.minecraft.network.chat.ClickEvent;
import net.minecraft.network.chat.CommonComponents;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.ComponentUtils;
import net.minecraft.network.chat.HoverEvent;
import net.minecraft.network.chat.MutableComponent;
import net.minecraft.network.protocol.game.ClientboundCommandsPacket;
import net.minecraft.resources.ResourceKey;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.commands.AdvancementCommands;
import net.minecraft.server.commands.AttributeCommand;
import net.minecraft.server.commands.BanIpCommands;
import net.minecraft.server.commands.BanListCommands;
import net.minecraft.server.commands.BanPlayerCommands;
import net.minecraft.server.commands.BossBarCommands;
import net.minecraft.server.commands.ClearInventoryCommands;
import net.minecraft.server.commands.CloneCommands;
import net.minecraft.server.commands.DamageCommand;
import net.minecraft.server.commands.DataPackCommand;
import net.minecraft.server.commands.DeOpCommands;
import net.minecraft.server.commands.DebugCommand;
import net.minecraft.server.commands.DebugConfigCommand;
import net.minecraft.server.commands.DebugMobSpawningCommand;
import net.minecraft.server.commands.DebugPathCommand;
import net.minecraft.server.commands.DefaultGameModeCommands;
import net.minecraft.server.commands.DifficultyCommand;
import net.minecraft.server.commands.EffectCommands;
import net.minecraft.server.commands.EmoteCommands;
import net.minecraft.server.commands.EnchantCommand;
import net.minecraft.server.commands.ExecuteCommand;
import net.minecraft.server.commands.ExperienceCommand;
import net.minecraft.server.commands.FillBiomeCommand;
import net.minecraft.server.commands.FillCommand;
import net.minecraft.server.commands.ForceLoadCommand;
import net.minecraft.server.commands.FunctionCommand;
import net.minecraft.server.commands.GameModeCommand;
import net.minecraft.server.commands.GameRuleCommand;
import net.minecraft.server.commands.GiveCommand;
import net.minecraft.server.commands.HelpCommand;
import net.minecraft.server.commands.ItemCommands;
import net.minecraft.server.commands.JfrCommand;
import net.minecraft.server.commands.KickCommand;
import net.minecraft.server.commands.KillCommand;
import net.minecraft.server.commands.ListPlayersCommand;
import net.minecraft.server.commands.LocateCommand;
import net.minecraft.server.commands.LootCommand;
import net.minecraft.server.commands.MsgCommand;
import net.minecraft.server.commands.OpCommand;
import net.minecraft.server.commands.PardonCommand;
import net.minecraft.server.commands.PardonIpCommand;
import net.minecraft.server.commands.ParticleCommand;
import net.minecraft.server.commands.PerfCommand;
import net.minecraft.server.commands.PlaceCommand;
import net.minecraft.server.commands.PlaySoundCommand;
import net.minecraft.server.commands.PublishCommand;
import net.minecraft.server.commands.RaidCommand;
import net.minecraft.server.commands.RandomCommand;
import net.minecraft.server.commands.RecipeCommand;
import net.minecraft.server.commands.ReloadCommand;
import net.minecraft.server.commands.ReturnCommand;
import net.minecraft.server.commands.RideCommand;
import net.minecraft.server.commands.RotateCommand;
import net.minecraft.server.commands.SaveAllCommand;
import net.minecraft.server.commands.SaveOffCommand;
import net.minecraft.server.commands.SaveOnCommand;
import net.minecraft.server.commands.SayCommand;
import net.minecraft.server.commands.ScheduleCommand;
import net.minecraft.server.commands.ScoreboardCommand;
import net.minecraft.server.commands.SeedCommand;
import net.minecraft.server.commands.ServerPackCommand;
import net.minecraft.server.commands.SetBlockCommand;
import net.minecraft.server.commands.SetPlayerIdleTimeoutCommand;
import net.minecraft.server.commands.SetSpawnCommand;
import net.minecraft.server.commands.SetWorldSpawnCommand;
import net.minecraft.server.commands.SpawnArmorTrimsCommand;
import net.minecraft.server.commands.SpectateCommand;
import net.minecraft.server.commands.SpreadPlayersCommand;
import net.minecraft.server.commands.StopCommand;
import net.minecraft.server.commands.StopSoundCommand;
import net.minecraft.server.commands.SummonCommand;
import net.minecraft.server.commands.TagCommand;
import net.minecraft.server.commands.TeamCommand;
import net.minecraft.server.commands.TeamMsgCommand;
import net.minecraft.server.commands.TeleportCommand;
import net.minecraft.server.commands.TellRawCommand;
import net.minecraft.server.commands.TickCommand;
import net.minecraft.server.commands.TimeCommand;
import net.minecraft.server.commands.TitleCommand;
import net.minecraft.server.commands.TransferCommand;
import net.minecraft.server.commands.TriggerCommand;
import net.minecraft.server.commands.WardenSpawnTrackerCommand;
import net.minecraft.server.commands.WeatherCommand;
import net.minecraft.server.commands.WhitelistCommand;
import net.minecraft.server.commands.WorldBorderCommand;
import net.minecraft.server.commands.data.DataCommands;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.util.profiling.Profiler;
import net.minecraft.util.profiling.jfr.JvmProfiler;
import net.minecraft.world.flag.FeatureFlagSet;
import net.minecraft.world.flag.FeatureFlags;
import net.minecraft.world.level.GameRules;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
public class Commands {
private static final ThreadLocal<ExecutionContext<CommandSourceStack>> CURRENT_EXECUTION_CONTEXT = new ThreadLocal();
private static final Logger LOGGER = LogUtils.getLogger();
public static final int LEVEL_ALL = 0;
public static final int LEVEL_MODERATORS = 1;
public static final int LEVEL_GAMEMASTERS = 2;
public static final int LEVEL_ADMINS = 3;
public static final int LEVEL_OWNERS = 4;
private final CommandDispatcher<CommandSourceStack> dispatcher = new CommandDispatcher<>();
public Commands(Commands.CommandSelection selection, CommandBuildContext context) {
AdvancementCommands.register(this.dispatcher);
AttributeCommand.register(this.dispatcher, context);
ExecuteCommand.register(this.dispatcher, context);
BossBarCommands.register(this.dispatcher, context);
ClearInventoryCommands.register(this.dispatcher, context);
CloneCommands.register(this.dispatcher, context);
DamageCommand.register(this.dispatcher, context);
DataCommands.register(this.dispatcher);
DataPackCommand.register(this.dispatcher);
DebugCommand.register(this.dispatcher);
DefaultGameModeCommands.register(this.dispatcher);
DifficultyCommand.register(this.dispatcher);
EffectCommands.register(this.dispatcher, context);
EmoteCommands.register(this.dispatcher);
EnchantCommand.register(this.dispatcher, context);
ExperienceCommand.register(this.dispatcher);
FillCommand.register(this.dispatcher, context);
FillBiomeCommand.register(this.dispatcher, context);
ForceLoadCommand.register(this.dispatcher);
FunctionCommand.register(this.dispatcher);
GameModeCommand.register(this.dispatcher);
GameRuleCommand.register(this.dispatcher, context);
GiveCommand.register(this.dispatcher, context);
HelpCommand.register(this.dispatcher);
ItemCommands.register(this.dispatcher, context);
KickCommand.register(this.dispatcher);
KillCommand.register(this.dispatcher);
ListPlayersCommand.register(this.dispatcher);
LocateCommand.register(this.dispatcher, context);
LootCommand.register(this.dispatcher, context);
MsgCommand.register(this.dispatcher);
ParticleCommand.register(this.dispatcher, context);
PlaceCommand.register(this.dispatcher);
PlaySoundCommand.register(this.dispatcher);
RandomCommand.register(this.dispatcher);
ReloadCommand.register(this.dispatcher);
RecipeCommand.register(this.dispatcher);
ReturnCommand.register(this.dispatcher);
RideCommand.register(this.dispatcher);
RotateCommand.register(this.dispatcher);
SayCommand.register(this.dispatcher);
ScheduleCommand.register(this.dispatcher);
ScoreboardCommand.register(this.dispatcher, context);
SeedCommand.register(this.dispatcher, selection != Commands.CommandSelection.INTEGRATED);
SetBlockCommand.register(this.dispatcher, context);
SetSpawnCommand.register(this.dispatcher);
SetWorldSpawnCommand.register(this.dispatcher);
SpectateCommand.register(this.dispatcher);
SpreadPlayersCommand.register(this.dispatcher);
StopSoundCommand.register(this.dispatcher);
SummonCommand.register(this.dispatcher, context);
TagCommand.register(this.dispatcher);
TeamCommand.register(this.dispatcher, context);
TeamMsgCommand.register(this.dispatcher);
TeleportCommand.register(this.dispatcher);
TellRawCommand.register(this.dispatcher, context);
TestCommand.register(this.dispatcher, context);
TickCommand.register(this.dispatcher);
TimeCommand.register(this.dispatcher);
TitleCommand.register(this.dispatcher, context);
TriggerCommand.register(this.dispatcher);
WeatherCommand.register(this.dispatcher);
WorldBorderCommand.register(this.dispatcher);
if (JvmProfiler.INSTANCE.isAvailable()) {
JfrCommand.register(this.dispatcher);
}
if (SharedConstants.IS_RUNNING_IN_IDE) {
RaidCommand.register(this.dispatcher, context);
DebugPathCommand.register(this.dispatcher);
DebugMobSpawningCommand.register(this.dispatcher);
WardenSpawnTrackerCommand.register(this.dispatcher);
SpawnArmorTrimsCommand.register(this.dispatcher);
ServerPackCommand.register(this.dispatcher);
if (selection.includeDedicated) {
DebugConfigCommand.register(this.dispatcher);
}
}
if (selection.includeDedicated) {
BanIpCommands.register(this.dispatcher);
BanListCommands.register(this.dispatcher);
BanPlayerCommands.register(this.dispatcher);
DeOpCommands.register(this.dispatcher);
OpCommand.register(this.dispatcher);
PardonCommand.register(this.dispatcher);
PardonIpCommand.register(this.dispatcher);
PerfCommand.register(this.dispatcher);
SaveAllCommand.register(this.dispatcher);
SaveOffCommand.register(this.dispatcher);
SaveOnCommand.register(this.dispatcher);
SetPlayerIdleTimeoutCommand.register(this.dispatcher);
StopCommand.register(this.dispatcher);
TransferCommand.register(this.dispatcher);
WhitelistCommand.register(this.dispatcher);
}
if (selection.includeIntegrated) {
PublishCommand.register(this.dispatcher);
}
this.dispatcher.setConsumer(ExecutionCommandSource.resultConsumer());
}
public static <S> ParseResults<S> mapSource(ParseResults<S> parseResults, UnaryOperator<S> mapper) {
CommandContextBuilder<S> commandContextBuilder = parseResults.getContext();
CommandContextBuilder<S> commandContextBuilder2 = commandContextBuilder.withSource((S)mapper.apply(commandContextBuilder.getSource()));
return new ParseResults<>(commandContextBuilder2, parseResults.getReader(), parseResults.getExceptions());
}
public void performPrefixedCommand(CommandSourceStack source, String command) {
command = command.startsWith("/") ? command.substring(1) : command;
this.performCommand(this.dispatcher.parse(command, source), command);
}
public void performCommand(ParseResults<CommandSourceStack> parseResults, String command) {
CommandSourceStack commandSourceStack = parseResults.getContext().getSource();
Profiler.get().push((Supplier<String>)(() -> "/" + command));
ContextChain<CommandSourceStack> contextChain = finishParsing(parseResults, command, commandSourceStack);
try {
if (contextChain != null) {
executeCommandInContext(
commandSourceStack,
executionContext -> ExecutionContext.queueInitialCommandExecution(executionContext, command, contextChain, commandSourceStack, CommandResultCallback.EMPTY)
);
}
} catch (Exception var12) {
MutableComponent mutableComponent = Component.literal(var12.getMessage() == null ? var12.getClass().getName() : var12.getMessage());
if (LOGGER.isDebugEnabled()) {
LOGGER.error("Command exception: /{}", command, var12);
StackTraceElement[] stackTraceElements = var12.getStackTrace();
for (int i = 0; i < Math.min(stackTraceElements.length, 3); i++) {
mutableComponent.append("\n\n")
.append(stackTraceElements[i].getMethodName())
.append("\n ")
.append(stackTraceElements[i].getFileName())
.append(":")
.append(String.valueOf(stackTraceElements[i].getLineNumber()));
}
}
commandSourceStack.sendFailure(Component.translatable("command.failed").withStyle(style -> style.withHoverEvent(new HoverEvent.ShowText(mutableComponent))));
if (SharedConstants.IS_RUNNING_IN_IDE) {
commandSourceStack.sendFailure(Component.literal(Util.describeError(var12)));
LOGGER.error("'/{}' threw an exception", command, var12);
}
} finally {
Profiler.get().pop();
}
}
@Nullable
private static ContextChain<CommandSourceStack> finishParsing(ParseResults<CommandSourceStack> parseResults, String command, CommandSourceStack source) {
try {
validateParseResults(parseResults);
return (ContextChain<CommandSourceStack>)ContextChain.tryFlatten(parseResults.getContext().build(command))
.orElseThrow(() -> CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownCommand().createWithContext(parseResults.getReader()));
} catch (CommandSyntaxException var7) {
source.sendFailure(ComponentUtils.fromMessage(var7.getRawMessage()));
if (var7.getInput() != null && var7.getCursor() >= 0) {
int i = Math.min(var7.getInput().length(), var7.getCursor());
MutableComponent mutableComponent = Component.empty()
.withStyle(ChatFormatting.GRAY)
.withStyle(style -> style.withClickEvent(new ClickEvent.SuggestCommand("/" + command)));
if (i > 10) {
mutableComponent.append(CommonComponents.ELLIPSIS);
}
mutableComponent.append(var7.getInput().substring(Math.max(0, i - 10), i));
if (i < var7.getInput().length()) {
Component component = Component.literal(var7.getInput().substring(i)).withStyle(ChatFormatting.RED, ChatFormatting.UNDERLINE);
mutableComponent.append(component);
}
mutableComponent.append(Component.translatable("command.context.here").withStyle(ChatFormatting.RED, ChatFormatting.ITALIC));
source.sendFailure(mutableComponent);
}
return null;
}
}
public static void executeCommandInContext(CommandSourceStack source, Consumer<ExecutionContext<CommandSourceStack>> contextConsumer) {
MinecraftServer minecraftServer = source.getServer();
ExecutionContext<CommandSourceStack> executionContext = (ExecutionContext<CommandSourceStack>)CURRENT_EXECUTION_CONTEXT.get();
boolean bl = executionContext == null;
if (bl) {
int i = Math.max(1, minecraftServer.getGameRules().getInt(GameRules.RULE_MAX_COMMAND_CHAIN_LENGTH));
int j = minecraftServer.getGameRules().getInt(GameRules.RULE_MAX_COMMAND_FORK_COUNT);
try (ExecutionContext<CommandSourceStack> executionContext2 = new ExecutionContext<>(i, j, Profiler.get())) {
CURRENT_EXECUTION_CONTEXT.set(executionContext2);
contextConsumer.accept(executionContext2);
executionContext2.runCommandQueue();
} finally {
CURRENT_EXECUTION_CONTEXT.set(null);
}
} else {
contextConsumer.accept(executionContext);
}
}
public void sendCommands(ServerPlayer player) {
Map<CommandNode<CommandSourceStack>, CommandNode<SharedSuggestionProvider>> map = Maps.<CommandNode<CommandSourceStack>, CommandNode<SharedSuggestionProvider>>newHashMap();
RootCommandNode<SharedSuggestionProvider> rootCommandNode = new RootCommandNode<>();
map.put(this.dispatcher.getRoot(), rootCommandNode);
this.fillUsableCommands(this.dispatcher.getRoot(), rootCommandNode, player.createCommandSourceStack(), map);
player.connection.send(new ClientboundCommandsPacket(rootCommandNode));
}
private void fillUsableCommands(
CommandNode<CommandSourceStack> rootCommandSource,
CommandNode<SharedSuggestionProvider> rootSuggestion,
CommandSourceStack source,
Map<CommandNode<CommandSourceStack>, CommandNode<SharedSuggestionProvider>> commandNodeToSuggestionNode
) {
for (CommandNode<CommandSourceStack> commandNode : rootCommandSource.getChildren()) {
if (commandNode.canUse(source)) {
ArgumentBuilder<SharedSuggestionProvider, ?> argumentBuilder = commandNode.createBuilder();
argumentBuilder.requires(sharedSuggestionProvider -> true);
if (argumentBuilder.getCommand() != null) {
argumentBuilder.executes(commandContext -> 0);
}
if (argumentBuilder instanceof RequiredArgumentBuilder<SharedSuggestionProvider, ?> requiredArgumentBuilder
&& requiredArgumentBuilder.getSuggestionsProvider() != null) {
requiredArgumentBuilder.suggests(SuggestionProviders.safelySwap(requiredArgumentBuilder.getSuggestionsProvider()));
}
if (argumentBuilder.getRedirect() != null) {
argumentBuilder.redirect((CommandNode<SharedSuggestionProvider>)commandNodeToSuggestionNode.get(argumentBuilder.getRedirect()));
}
CommandNode<SharedSuggestionProvider> commandNode2 = argumentBuilder.build();
commandNodeToSuggestionNode.put(commandNode, commandNode2);
rootSuggestion.addChild(commandNode2);
if (!commandNode.getChildren().isEmpty()) {
this.fillUsableCommands(commandNode, commandNode2, source, commandNodeToSuggestionNode);
}
}
}
}
/**
* Creates a new argument. Intended to be imported statically. The benefit of this over the brigadier {@link LiteralArgumentBuilder#literal} method is that it is typed to {@link CommandSource}.
*/
public static LiteralArgumentBuilder<CommandSourceStack> literal(String name) {
return LiteralArgumentBuilder.literal(name);
}
/**
* Creates a new argument. Intended to be imported statically. The benefit of this over the brigadier {@link RequiredArgumentBuilder#argument} method is that it is typed to {@link CommandSource}.
*/
public static <T> RequiredArgumentBuilder<CommandSourceStack, T> argument(String name, ArgumentType<T> type) {
return RequiredArgumentBuilder.argument(name, type);
}
public static Predicate<String> createValidator(Commands.ParseFunction parser) {
return string -> {
try {
parser.parse(new StringReader(string));
return true;
} catch (CommandSyntaxException var3) {
return false;
}
};
}
public CommandDispatcher<CommandSourceStack> getDispatcher() {
return this.dispatcher;
}
public static <S> void validateParseResults(ParseResults<S> parseResults) throws CommandSyntaxException {
CommandSyntaxException commandSyntaxException = getParseException(parseResults);
if (commandSyntaxException != null) {
throw commandSyntaxException;
}
}
@Nullable
public static <S> CommandSyntaxException getParseException(ParseResults<S> result) {
if (!result.getReader().canRead()) {
return null;
} else if (result.getExceptions().size() == 1) {
return (CommandSyntaxException)result.getExceptions().values().iterator().next();
} else {
return result.getContext().getRange().isEmpty()
? CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownCommand().createWithContext(result.getReader())
: CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownArgument().createWithContext(result.getReader());
}
}
public static CommandBuildContext createValidationContext(HolderLookup.Provider provider) {
return new CommandBuildContext() {
@Override
public FeatureFlagSet enabledFeatures() {
return FeatureFlags.REGISTRY.allFlags();
}
@Override
public Stream<ResourceKey<? extends Registry<?>>> listRegistryKeys() {
return provider.listRegistryKeys();
}
@Override
public <T> Optional<HolderLookup.RegistryLookup<T>> lookup(ResourceKey<? extends Registry<? extends T>> registryKey) {
return provider.lookup(registryKey).map(this::createLookup);
}
private <T> Delegate<T> createLookup(HolderLookup.RegistryLookup<T> registryLookup) {
return new 1(this, registryLookup);
}
};
}
public static void validate() {
CommandBuildContext commandBuildContext = createValidationContext(VanillaRegistries.createLookup());
CommandDispatcher<CommandSourceStack> commandDispatcher = new Commands(Commands.CommandSelection.ALL, commandBuildContext).getDispatcher();
RootCommandNode<CommandSourceStack> rootCommandNode = commandDispatcher.getRoot();
commandDispatcher.findAmbiguities(
(commandNode, commandNode2, commandNode3, collection) -> LOGGER.warn(
"Ambiguity between arguments {} and {} with inputs: {}", commandDispatcher.getPath(commandNode2), commandDispatcher.getPath(commandNode3), collection
)
);
Set<ArgumentType<?>> set = ArgumentUtils.findUsedArgumentTypes(rootCommandNode);
Set<ArgumentType<?>> set2 = (Set<ArgumentType<?>>)set.stream()
.filter(argumentType -> !ArgumentTypeInfos.isClassRecognized(argumentType.getClass()))
.collect(Collectors.toSet());
if (!set2.isEmpty()) {
LOGGER.warn(
"Missing type registration for following arguments:\n {}", set2.stream().map(argumentType -> "\t" + argumentType).collect(Collectors.joining(",\n"))
);
throw new IllegalStateException("Unregistered argument types");
}
}
public static enum CommandSelection {
ALL(true, true),
DEDICATED(false, true),
INTEGRATED(true, false);
final boolean includeIntegrated;
final boolean includeDedicated;
private CommandSelection(final boolean includeIntegrated, final boolean includeDedicated) {
this.includeIntegrated = includeIntegrated;
this.includeDedicated = includeDedicated;
}
}
@FunctionalInterface
public interface ParseFunction {
void parse(StringReader stringReader) throws CommandSyntaxException;
}
}