package net.minecraft.world.item.component; import com.mojang.logging.LogUtils; import com.mojang.serialization.Codec; import com.mojang.serialization.DataResult; import com.mojang.serialization.DynamicOps; import com.mojang.serialization.MapDecoder; import com.mojang.serialization.MapEncoder; import com.mojang.serialization.MapLike; import io.netty.buffer.ByteBuf; import java.util.UUID; import java.util.function.Consumer; import net.minecraft.core.Holder; import net.minecraft.core.HolderLookup; import net.minecraft.core.Registry; import net.minecraft.core.component.DataComponentType; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.NbtOps; import net.minecraft.nbt.NbtUtils; import net.minecraft.nbt.Tag; import net.minecraft.nbt.TagParser; import net.minecraft.network.codec.ByteBufCodecs; import net.minecraft.network.codec.StreamCodec; import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.entity.Entity; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.block.entity.BlockEntity; import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; public final class CustomData { private static final Logger LOGGER = LogUtils.getLogger(); public static final CustomData EMPTY = new CustomData(new CompoundTag()); private static final String TYPE_TAG = "id"; public static final Codec CODEC = Codec.withAlternative(CompoundTag.CODEC, TagParser.FLATTENED_CODEC) .xmap(CustomData::new, customData -> customData.tag); public static final Codec CODEC_WITH_ID = CODEC.validate( customData -> customData.getUnsafe().getString("id").isPresent() ? DataResult.success(customData) : DataResult.error(() -> "Missing id for entity in: " + customData) ); @Deprecated public static final StreamCodec STREAM_CODEC = ByteBufCodecs.COMPOUND_TAG.map(CustomData::new, customData -> customData.tag); private final CompoundTag tag; private CustomData(CompoundTag tag) { this.tag = tag; } public static CustomData of(CompoundTag tag) { return new CustomData(tag.copy()); } public boolean matchedBy(CompoundTag tag) { return NbtUtils.compareNbt(tag, this.tag, true); } public static void update(DataComponentType componentType, ItemStack stack, Consumer updater) { CustomData customData = stack.getOrDefault(componentType, EMPTY).update(updater); if (customData.tag.isEmpty()) { stack.remove(componentType); } else { stack.set(componentType, customData); } } public static void set(DataComponentType componentType, ItemStack stack, CompoundTag tag) { if (!tag.isEmpty()) { stack.set(componentType, of(tag)); } else { stack.remove(componentType); } } public CustomData update(Consumer updater) { CompoundTag compoundTag = this.tag.copy(); updater.accept(compoundTag); return new CustomData(compoundTag); } @Nullable public ResourceLocation parseEntityId() { return (ResourceLocation)this.tag.read("id", ResourceLocation.CODEC).orElse(null); } @Nullable public T parseEntityType(HolderLookup.Provider registries, ResourceKey> registryKey) { ResourceLocation resourceLocation = this.parseEntityId(); return (T)(resourceLocation == null ? null : registries.lookup(registryKey) .flatMap(registryLookup -> registryLookup.get(ResourceKey.create(registryKey, resourceLocation))) .map(Holder::value) .orElse(null)); } public void loadInto(Entity entity) { CompoundTag compoundTag = entity.saveWithoutId(new CompoundTag()); UUID uUID = entity.getUUID(); compoundTag.merge(this.tag); entity.load(compoundTag); entity.setUUID(uUID); } public boolean loadInto(BlockEntity blockEntity, HolderLookup.Provider levelRegistry) { CompoundTag compoundTag = blockEntity.saveCustomOnly(levelRegistry); CompoundTag compoundTag2 = compoundTag.copy(); compoundTag.merge(this.tag); if (!compoundTag.equals(compoundTag2)) { try { blockEntity.loadCustomOnly(compoundTag, levelRegistry); blockEntity.setChanged(); return true; } catch (Exception var8) { LOGGER.warn("Failed to apply custom data to block entity at {}", blockEntity.getBlockPos(), var8); try { blockEntity.loadCustomOnly(compoundTag2, levelRegistry); } catch (Exception var7) { LOGGER.warn("Failed to rollback block entity at {} after failure", blockEntity.getBlockPos(), var7); } } } return false; } public DataResult update(DynamicOps ops, MapEncoder encoder, T value) { return encoder.encode(value, ops, ops.mapBuilder()).build(this.tag).map(tag -> new CustomData((CompoundTag)tag)); } public DataResult read(MapDecoder decoder) { return this.read(NbtOps.INSTANCE, decoder); } public DataResult read(DynamicOps ops, MapDecoder decoder) { MapLike mapLike = ops.getMap(this.tag).getOrThrow(); return decoder.decode(ops, mapLike); } public int size() { return this.tag.size(); } public boolean isEmpty() { return this.tag.isEmpty(); } public CompoundTag copyTag() { return this.tag.copy(); } public boolean contains(String key) { return this.tag.contains(key); } public boolean equals(Object object) { if (object == this) { return true; } else { return object instanceof CustomData customData ? this.tag.equals(customData.tag) : false; } } public int hashCode() { return this.tag.hashCode(); } public String toString() { return this.tag.toString(); } @Deprecated public CompoundTag getUnsafe() { return this.tag; } }