package net.minecraft.world.entity.player; import com.mojang.datafixers.DataFixUtils; import com.mojang.serialization.MapCodec; import com.mojang.serialization.codecs.RecordCodecBuilder; import io.netty.buffer.ByteBuf; import java.util.Optional; import net.minecraft.core.ClientAsset; import net.minecraft.network.codec.ByteBufCodecs; import net.minecraft.network.codec.StreamCodec; import org.jspecify.annotations.Nullable; public record PlayerSkin( ClientAsset.Texture body, @Nullable ClientAsset.Texture cape, @Nullable ClientAsset.Texture elytra, PlayerModelType model, boolean secure ) { public static PlayerSkin insecure( final ClientAsset.Texture body, @Nullable final ClientAsset.Texture cape, @Nullable final ClientAsset.Texture elytra, final PlayerModelType model ) { return new PlayerSkin(body, cape, elytra, model, false); } public PlayerSkin with(final PlayerSkin.Patch patch) { return patch.equals(PlayerSkin.Patch.EMPTY) ? this : insecure( DataFixUtils.orElse(patch.body, this.body), DataFixUtils.orElse(patch.cape, this.cape), DataFixUtils.orElse(patch.elytra, this.elytra), (PlayerModelType)patch.model.orElse(this.model) ); } public record Patch( Optional body, Optional cape, Optional elytra, Optional model ) { public static final PlayerSkin.Patch EMPTY = new PlayerSkin.Patch(Optional.empty(), Optional.empty(), Optional.empty(), Optional.empty()); public static final MapCodec MAP_CODEC = RecordCodecBuilder.mapCodec( i -> i.group( ClientAsset.ResourceTexture.CODEC.optionalFieldOf("texture").forGetter(PlayerSkin.Patch::body), ClientAsset.ResourceTexture.CODEC.optionalFieldOf("cape").forGetter(PlayerSkin.Patch::cape), ClientAsset.ResourceTexture.CODEC.optionalFieldOf("elytra").forGetter(PlayerSkin.Patch::elytra), PlayerModelType.CODEC.optionalFieldOf("model").forGetter(PlayerSkin.Patch::model) ) .apply(i, PlayerSkin.Patch::create) ); public static final StreamCodec STREAM_CODEC = StreamCodec.composite( ClientAsset.ResourceTexture.STREAM_CODEC.apply(ByteBufCodecs::optional), PlayerSkin.Patch::body, ClientAsset.ResourceTexture.STREAM_CODEC.apply(ByteBufCodecs::optional), PlayerSkin.Patch::cape, ClientAsset.ResourceTexture.STREAM_CODEC.apply(ByteBufCodecs::optional), PlayerSkin.Patch::elytra, PlayerModelType.STREAM_CODEC.apply(ByteBufCodecs::optional), PlayerSkin.Patch::model, PlayerSkin.Patch::create ); public static PlayerSkin.Patch create( final Optional texture, final Optional capeTexture, final Optional elytraTexture, final Optional model ) { return texture.isEmpty() && capeTexture.isEmpty() && elytraTexture.isEmpty() && model.isEmpty() ? EMPTY : new PlayerSkin.Patch(texture, capeTexture, elytraTexture, model); } } }