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> 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 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 ParseResults mapSource(ParseResults parseResults, UnaryOperator mapper) { CommandContextBuilder commandContextBuilder = parseResults.getContext(); CommandContextBuilder 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 parseResults, String command) { CommandSourceStack commandSourceStack = parseResults.getContext().getSource(); Profiler.get().push((Supplier)(() -> "/" + command)); ContextChain 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 finishParsing(ParseResults parseResults, String command, CommandSourceStack source) { try { validateParseResults(parseResults); return (ContextChain)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> contextConsumer) { MinecraftServer minecraftServer = source.getServer(); ExecutionContext executionContext = (ExecutionContext)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 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> map = Maps., CommandNode>newHashMap(); RootCommandNode 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 rootCommandSource, CommandNode rootSuggestion, CommandSourceStack source, Map, CommandNode> commandNodeToSuggestionNode ) { for (CommandNode commandNode : rootCommandSource.getChildren()) { if (commandNode.canUse(source)) { ArgumentBuilder argumentBuilder = commandNode.createBuilder(); argumentBuilder.requires(sharedSuggestionProvider -> true); if (argumentBuilder.getCommand() != null) { argumentBuilder.executes(commandContext -> 0); } if (argumentBuilder instanceof RequiredArgumentBuilder requiredArgumentBuilder && requiredArgumentBuilder.getSuggestionsProvider() != null) { requiredArgumentBuilder.suggests(SuggestionProviders.safelySwap(requiredArgumentBuilder.getSuggestionsProvider())); } if (argumentBuilder.getRedirect() != null) { argumentBuilder.redirect((CommandNode)commandNodeToSuggestionNode.get(argumentBuilder.getRedirect())); } CommandNode 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 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 RequiredArgumentBuilder argument(String name, ArgumentType type) { return RequiredArgumentBuilder.argument(name, type); } public static Predicate createValidator(Commands.ParseFunction parser) { return string -> { try { parser.parse(new StringReader(string)); return true; } catch (CommandSyntaxException var3) { return false; } }; } public CommandDispatcher getDispatcher() { return this.dispatcher; } public static void validateParseResults(ParseResults parseResults) throws CommandSyntaxException { CommandSyntaxException commandSyntaxException = getParseException(parseResults); if (commandSyntaxException != null) { throw commandSyntaxException; } } @Nullable public static CommandSyntaxException getParseException(ParseResults 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>> listRegistryKeys() { return provider.listRegistryKeys(); } @Override public Optional> lookup(ResourceKey> registryKey) { return provider.lookup(registryKey).map(this::createLookup); } private Delegate createLookup(HolderLookup.RegistryLookup registryLookup) { return new 1(this, registryLookup); } }; } public static void validate() { CommandBuildContext commandBuildContext = createValidationContext(VanillaRegistries.createLookup()); CommandDispatcher commandDispatcher = new Commands(Commands.CommandSelection.ALL, commandBuildContext).getDispatcher(); RootCommandNode 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> set = ArgumentUtils.findUsedArgumentTypes(rootCommandNode); Set> set2 = (Set>)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; } }