minecraft-src/net/minecraft/commands/arguments/MessageArgument.java
2025-07-04 01:41:11 +03:00

181 lines
7.6 KiB
Java

package net.minecraft.commands.arguments;
import com.google.common.collect.Lists;
import com.mojang.brigadier.StringReader;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.brigadier.exceptions.Dynamic2CommandExceptionType;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer;
import net.minecraft.commands.CommandSigningContext;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.arguments.selector.EntitySelector;
import net.minecraft.commands.arguments.selector.EntitySelectorParser;
import net.minecraft.network.chat.ChatDecorator;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.MutableComponent;
import net.minecraft.network.chat.PlayerChatMessage;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.network.FilteredText;
import org.jetbrains.annotations.Nullable;
public class MessageArgument implements SignedArgument<MessageArgument.Message> {
private static final Collection<String> EXAMPLES = Arrays.asList("Hello world!", "foo", "@e", "Hello @p :)");
static final Dynamic2CommandExceptionType TOO_LONG = new Dynamic2CommandExceptionType(
(object, object2) -> Component.translatableEscape("argument.message.too_long", object, object2)
);
public static MessageArgument message() {
return new MessageArgument();
}
public static Component getMessage(CommandContext<CommandSourceStack> context, String name) throws CommandSyntaxException {
MessageArgument.Message message = context.getArgument(name, MessageArgument.Message.class);
return message.resolveComponent(context.getSource());
}
public static void resolveChatMessage(CommandContext<CommandSourceStack> context, String key, Consumer<PlayerChatMessage> callback) throws CommandSyntaxException {
MessageArgument.Message message = context.getArgument(key, MessageArgument.Message.class);
CommandSourceStack commandSourceStack = context.getSource();
Component component = message.resolveComponent(commandSourceStack);
CommandSigningContext commandSigningContext = commandSourceStack.getSigningContext();
PlayerChatMessage playerChatMessage = commandSigningContext.getArgument(key);
if (playerChatMessage != null) {
resolveSignedMessage(callback, commandSourceStack, playerChatMessage.withUnsignedContent(component));
} else {
resolveDisguisedMessage(callback, commandSourceStack, PlayerChatMessage.system(message.text).withUnsignedContent(component));
}
}
private static void resolveSignedMessage(Consumer<PlayerChatMessage> callback, CommandSourceStack source, PlayerChatMessage message) {
MinecraftServer minecraftServer = source.getServer();
CompletableFuture<FilteredText> completableFuture = filterPlainText(source, message);
Component component = minecraftServer.getChatDecorator().decorate(source.getPlayer(), message.decoratedContent());
source.getChatMessageChainer().append(completableFuture, filteredText -> {
PlayerChatMessage playerChatMessage2 = message.withUnsignedContent(component).filter(filteredText.mask());
callback.accept(playerChatMessage2);
});
}
private static void resolveDisguisedMessage(Consumer<PlayerChatMessage> callback, CommandSourceStack source, PlayerChatMessage message) {
ChatDecorator chatDecorator = source.getServer().getChatDecorator();
Component component = chatDecorator.decorate(source.getPlayer(), message.decoratedContent());
callback.accept(message.withUnsignedContent(component));
}
private static CompletableFuture<FilteredText> filterPlainText(CommandSourceStack source, PlayerChatMessage message) {
ServerPlayer serverPlayer = source.getPlayer();
return serverPlayer != null && message.hasSignatureFrom(serverPlayer.getUUID())
? serverPlayer.getTextFilter().processStreamMessage(message.signedContent())
: CompletableFuture.completedFuture(FilteredText.passThrough(message.signedContent()));
}
public MessageArgument.Message parse(StringReader reader) throws CommandSyntaxException {
return MessageArgument.Message.parseText(reader, true);
}
public <S> MessageArgument.Message parse(StringReader stringReader, @Nullable S object) throws CommandSyntaxException {
return MessageArgument.Message.parseText(stringReader, EntitySelectorParser.allowSelectors(object));
}
@Override
public Collection<String> getExamples() {
return EXAMPLES;
}
public record Message(String text, MessageArgument.Part[] parts) {
Component resolveComponent(CommandSourceStack source) throws CommandSyntaxException {
return this.toComponent(source, EntitySelectorParser.allowSelectors(source));
}
/**
* Converts this message into a text component, replacing any selectors in the text with the actual evaluated selector.
*/
public Component toComponent(CommandSourceStack source, boolean allowSelectors) throws CommandSyntaxException {
if (this.parts.length != 0 && allowSelectors) {
MutableComponent mutableComponent = Component.literal(this.text.substring(0, this.parts[0].start()));
int i = this.parts[0].start();
for (MessageArgument.Part part : this.parts) {
Component component = part.toComponent(source);
if (i < part.start()) {
mutableComponent.append(this.text.substring(i, part.start()));
}
mutableComponent.append(component);
i = part.end();
}
if (i < this.text.length()) {
mutableComponent.append(this.text.substring(i));
}
return mutableComponent;
} else {
return Component.literal(this.text);
}
}
/**
* Parses a message. The algorithm for this is simply to run through and look for selectors, ignoring any invalid selectors in the text (since players may type e.g. "[@]").
*/
public static MessageArgument.Message parseText(StringReader reader, boolean allowSelectors) throws CommandSyntaxException {
if (reader.getRemainingLength() > 256) {
throw MessageArgument.TOO_LONG.create(reader.getRemainingLength(), 256);
} else {
String string = reader.getRemaining();
if (!allowSelectors) {
reader.setCursor(reader.getTotalLength());
return new MessageArgument.Message(string, new MessageArgument.Part[0]);
} else {
List<MessageArgument.Part> list = Lists.<MessageArgument.Part>newArrayList();
int i = reader.getCursor();
while (true) {
int j;
EntitySelector entitySelector;
while (true) {
if (!reader.canRead()) {
return new MessageArgument.Message(string, (MessageArgument.Part[])list.toArray(new MessageArgument.Part[0]));
}
if (reader.peek() == '@') {
j = reader.getCursor();
try {
EntitySelectorParser entitySelectorParser = new EntitySelectorParser(reader, true);
entitySelector = entitySelectorParser.parse();
break;
} catch (CommandSyntaxException var8) {
if (var8.getType() != EntitySelectorParser.ERROR_MISSING_SELECTOR_TYPE && var8.getType() != EntitySelectorParser.ERROR_UNKNOWN_SELECTOR_TYPE) {
throw var8;
}
reader.setCursor(j + 1);
}
} else {
reader.skip();
}
}
list.add(new MessageArgument.Part(j - i, reader.getCursor() - i, entitySelector));
}
}
}
}
}
public record Part(int start, int end, EntitySelector selector) {
/**
* Runs the selector and returns the component produced by it. This method does not actually appear to ever return null.
*/
public Component toComponent(CommandSourceStack source) throws CommandSyntaxException {
return EntitySelector.joinNames(this.selector.findEntities(source));
}
}
}