package com.razz.dfashion.cosmetic; import com.mojang.serialization.Codec; import com.mojang.serialization.codecs.RecordCodecBuilder; import com.razz.dfashion.cosmetic.share.SharedCosmeticCache; import io.netty.buffer.ByteBuf; import io.netty.handler.codec.DecoderException; import net.minecraft.network.codec.ByteBufCodecs; import net.minecraft.network.codec.StreamCodec; import net.minecraft.resources.Identifier; /** * One of two ways an equipped cosmetic slot can reference its content: * * *

Wire codec tags the variant with a single byte (0 = local, 1 = shared) and caps each * inner string length. Invalid shared hashes are rejected via * {@link SharedCosmeticCache#isValidHash} on decode. */ public sealed interface CosmeticRef permits CosmeticRef.Local, CosmeticRef.Shared { record Local(Identifier id) implements CosmeticRef {} record Shared(String hash) implements CosmeticRef {} byte TAG_LOCAL = 0; byte TAG_SHARED = 1; Codec CODEC = RecordCodecBuilder.create(inst -> inst.group( Codec.BOOL.fieldOf("shared").forGetter(r -> r instanceof Shared), Codec.STRING.fieldOf("value").forGetter(r -> switch (r) { case Local l -> l.id().toString(); case Shared s -> s.hash(); }) ).apply(inst, (shared, value) -> shared ? new Shared(value) : new Local(Identifier.parse(value)))); StreamCodec STREAM_CODEC = new StreamCodec<>() { private final StreamCodec LOCAL_STR = ByteBufCodecs.stringUtf8(256); private final StreamCodec SHARED_STR = ByteBufCodecs.stringUtf8(64); @Override public void encode(ByteBuf buf, CosmeticRef ref) { switch (ref) { case Local l -> { buf.writeByte(TAG_LOCAL); LOCAL_STR.encode(buf, l.id().toString()); } case Shared s -> { buf.writeByte(TAG_SHARED); SHARED_STR.encode(buf, s.hash()); } } } @Override public CosmeticRef decode(ByteBuf buf) { byte tag = buf.readByte(); return switch (tag) { case TAG_LOCAL -> { String s = LOCAL_STR.decode(buf); Identifier id = Identifier.tryParse(s); if (id == null) throw new DecoderException("bad Identifier in CosmeticRef.Local: " + s); yield new Local(id); } case TAG_SHARED -> { String hash = SHARED_STR.decode(buf); if (!SharedCosmeticCache.isValidHash(hash)) { throw new DecoderException("invalid hash in CosmeticRef.Shared"); } yield new Shared(hash); } default -> throw new DecoderException("bad CosmeticRef tag " + tag); }; } }; }