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.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.UUID; import net.minecraft.core.UUIDUtil; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.util.StringRepresentable; import net.minecraft.world.entity.EntityType; import net.minecraft.world.item.ItemStack; import org.jetbrains.annotations.Nullable; public interface HoverEvent { Codec CODEC = HoverEvent.Action.CODEC.dispatch("action", HoverEvent::action, action -> action.codec); HoverEvent.Action action(); public static enum Action implements StringRepresentable { SHOW_TEXT("show_text", true, HoverEvent.ShowText.CODEC), SHOW_ITEM("show_item", true, HoverEvent.ShowItem.CODEC), SHOW_ENTITY("show_entity", true, HoverEvent.ShowEntity.CODEC); public static final Codec UNSAFE_CODEC = StringRepresentable.fromValues(HoverEvent.Action::values); public static final Codec CODEC = UNSAFE_CODEC.validate(HoverEvent.Action::filterForSerialization); private final String name; private final boolean allowFromServer; 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 String toString() { return ""; } private static DataResult filterForSerialization(HoverEvent.Action action) { return !action.isAllowedFromServer() ? DataResult.error(() -> "Action not allowed: " + action) : DataResult.success(action, Lifecycle.stable()); } } public static class EntityTooltipInfo { public static final MapCodec CODEC = RecordCodecBuilder.mapCodec( instance -> instance.group( BuiltInRegistries.ENTITY_TYPE.byNameCodec().fieldOf("id").forGetter(entityTooltipInfo -> entityTooltipInfo.type), UUIDUtil.LENIENT_CODEC.fieldOf("uuid").forGetter(entityTooltipInfo -> entityTooltipInfo.uuid), ComponentSerialization.CODEC.optionalFieldOf("name").forGetter(entityTooltipInfo -> entityTooltipInfo.name) ) .apply(instance, HoverEvent.EntityTooltipInfo::new) ); public final EntityType type; public final UUID uuid; public final Optional name; @Nullable private List linesCache; public EntityTooltipInfo(EntityType type, UUID id, @Nullable Component name) { this(type, id, Optional.ofNullable(name)); } public EntityTooltipInfo(EntityType type, UUID id, Optional name) { this.type = type; this.uuid = id; this.name = name; } public List getTooltipLines() { if (this.linesCache == null) { this.linesCache = new ArrayList(); this.name.ifPresent(this.linesCache::add); this.linesCache.add(Component.translatable("gui.entity_tooltip.type", this.type.getDescription())); this.linesCache.add(Component.literal(this.uuid.toString())); } return this.linesCache; } public boolean equals(Object object) { if (this == object) { return true; } else if (object != null && this.getClass() == object.getClass()) { HoverEvent.EntityTooltipInfo entityTooltipInfo = (HoverEvent.EntityTooltipInfo)object; return this.type.equals(entityTooltipInfo.type) && this.uuid.equals(entityTooltipInfo.uuid) && this.name.equals(entityTooltipInfo.name); } else { return false; } } public int hashCode() { int i = this.type.hashCode(); i = 31 * i + this.uuid.hashCode(); return 31 * i + this.name.hashCode(); } } public record ShowEntity(HoverEvent.EntityTooltipInfo entity) implements HoverEvent { public static final MapCodec CODEC = RecordCodecBuilder.mapCodec( instance -> instance.group(HoverEvent.EntityTooltipInfo.CODEC.forGetter(HoverEvent.ShowEntity::entity)).apply(instance, HoverEvent.ShowEntity::new) ); @Override public HoverEvent.Action action() { return HoverEvent.Action.SHOW_ENTITY; } } public record ShowItem(ItemStack item) implements HoverEvent { public static final MapCodec CODEC = ItemStack.MAP_CODEC.xmap(HoverEvent.ShowItem::new, HoverEvent.ShowItem::item); public ShowItem(ItemStack item) { item = item.copy(); this.item = item; } @Override public HoverEvent.Action action() { return HoverEvent.Action.SHOW_ITEM; } public boolean equals(Object object) { return object instanceof HoverEvent.ShowItem showItem && ItemStack.matches(this.item, showItem.item); } public int hashCode() { return ItemStack.hashItemAndComponents(this.item); } } public record ShowText(Component value) implements HoverEvent { public static final MapCodec CODEC = RecordCodecBuilder.mapCodec( instance -> instance.group(ComponentSerialization.CODEC.fieldOf("value").forGetter(HoverEvent.ShowText::value)).apply(instance, HoverEvent.ShowText::new) ); @Override public HoverEvent.Action action() { return HoverEvent.Action.SHOW_TEXT; } } }