You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

81 lines
3.3 KiB
Java

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:
* <ul>
* <li>{@link Local} — a built-in cosmetic addressed by {@link Identifier}.
* Resolved against {@code CosmeticCache.cosmetics}.</li>
* <li>{@link Shared} — a server-side blob addressed by its 64-char content hash.
* Resolved against {@code ClientSharedCosmeticCache}.</li>
* </ul>
*
* <p>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<CosmeticRef> 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<ByteBuf, CosmeticRef> STREAM_CODEC = new StreamCodec<>() {
private final StreamCodec<ByteBuf, String> LOCAL_STR = ByteBufCodecs.stringUtf8(256);
private final StreamCodec<ByteBuf, String> 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);
};
}
};
}