package net.minecraft.network.chat; import com.mojang.serialization.Codec; import com.mojang.serialization.DataResult; import com.mojang.serialization.Lifecycle; import com.mojang.serialization.MapCodec; import com.mojang.serialization.codecs.RecordCodecBuilder; import java.io.File; import java.net.URI; import java.nio.file.Path; import net.minecraft.util.ExtraCodecs; import net.minecraft.util.StringRepresentable; public interface ClickEvent { Codec CODEC = ClickEvent.Action.CODEC.dispatch("action", ClickEvent::action, action -> action.codec); ClickEvent.Action action(); public static enum Action implements StringRepresentable { OPEN_URL("open_url", true, ClickEvent.OpenUrl.CODEC), OPEN_FILE("open_file", false, ClickEvent.OpenFile.CODEC), RUN_COMMAND("run_command", true, ClickEvent.RunCommand.CODEC), SUGGEST_COMMAND("suggest_command", true, ClickEvent.SuggestCommand.CODEC), CHANGE_PAGE("change_page", true, ClickEvent.ChangePage.CODEC), COPY_TO_CLIPBOARD("copy_to_clipboard", true, ClickEvent.CopyToClipboard.CODEC); public static final Codec UNSAFE_CODEC = StringRepresentable.fromEnum(ClickEvent.Action::values); public static final Codec CODEC = UNSAFE_CODEC.validate(ClickEvent.Action::filterForSerialization); private final boolean allowFromServer; /** * The canonical name used to refer to this action. */ private final String name; final MapCodec codec; private Action(final String name, final boolean allowFromServer, final MapCodec codec) { this.name = name; this.allowFromServer = allowFromServer; this.codec = codec; } /** * Indicates whether this event can be run from chat text. */ public boolean isAllowedFromServer() { return this.allowFromServer; } @Override public String getSerializedName() { return this.name; } public static DataResult filterForSerialization(ClickEvent.Action action) { return !action.isAllowedFromServer() ? DataResult.error(() -> "Click event type not allowed: " + action) : DataResult.success(action, Lifecycle.stable()); } } public record ChangePage(int page) implements ClickEvent { public static final MapCodec CODEC = RecordCodecBuilder.mapCodec( instance -> instance.group(ExtraCodecs.POSITIVE_INT.fieldOf("page").forGetter(ClickEvent.ChangePage::page)).apply(instance, ClickEvent.ChangePage::new) ); @Override public ClickEvent.Action action() { return ClickEvent.Action.CHANGE_PAGE; } } public record CopyToClipboard(String value) implements ClickEvent { public static final MapCodec CODEC = RecordCodecBuilder.mapCodec( instance -> instance.group(Codec.STRING.fieldOf("value").forGetter(ClickEvent.CopyToClipboard::value)).apply(instance, ClickEvent.CopyToClipboard::new) ); @Override public ClickEvent.Action action() { return ClickEvent.Action.COPY_TO_CLIPBOARD; } } public record OpenFile(String path) implements ClickEvent { public static final MapCodec CODEC = RecordCodecBuilder.mapCodec( instance -> instance.group(Codec.STRING.fieldOf("path").forGetter(ClickEvent.OpenFile::path)).apply(instance, ClickEvent.OpenFile::new) ); public OpenFile(File file) { this(file.toString()); } public OpenFile(Path path) { this(path.toFile()); } public File file() { return new File(this.path); } @Override public ClickEvent.Action action() { return ClickEvent.Action.OPEN_FILE; } } public record OpenUrl(URI uri) implements ClickEvent { public static final MapCodec CODEC = RecordCodecBuilder.mapCodec( instance -> instance.group(ExtraCodecs.UNTRUSTED_URI.fieldOf("url").forGetter(ClickEvent.OpenUrl::uri)).apply(instance, ClickEvent.OpenUrl::new) ); @Override public ClickEvent.Action action() { return ClickEvent.Action.OPEN_URL; } } public record RunCommand(String command) implements ClickEvent { public static final MapCodec CODEC = RecordCodecBuilder.mapCodec( instance -> instance.group(ExtraCodecs.CHAT_STRING.fieldOf("command").forGetter(ClickEvent.RunCommand::command)).apply(instance, ClickEvent.RunCommand::new) ); @Override public ClickEvent.Action action() { return ClickEvent.Action.RUN_COMMAND; } } public record SuggestCommand(String command) implements ClickEvent { public static final MapCodec CODEC = RecordCodecBuilder.mapCodec( instance -> instance.group(ExtraCodecs.CHAT_STRING.fieldOf("command").forGetter(ClickEvent.SuggestCommand::command)) .apply(instance, ClickEvent.SuggestCommand::new) ); @Override public ClickEvent.Action action() { return ClickEvent.Action.SUGGEST_COMMAND; } } }