package net.minecraft.client.renderer.block.model; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList.Builder; import com.mojang.logging.LogUtils; import com.mojang.serialization.Codec; import com.mojang.serialization.DataResult; import com.mojang.serialization.codecs.RecordCodecBuilder; import java.util.IdentityHashMap; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.function.BiConsumer; import java.util.function.Predicate; import java.util.function.Supplier; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.minecraft.client.renderer.block.model.multipart.MultiPartModel; import net.minecraft.client.renderer.block.model.multipart.Selector; import net.minecraft.util.ExtraCodecs; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.StateDefinition; import net.minecraft.world.level.block.state.StateHolder; import org.slf4j.Logger; @Environment(EnvType.CLIENT) public record BlockModelDefinition( Optional simpleModels, Optional multiPart ) { static final Logger LOGGER = LogUtils.getLogger(); public static final Codec CODEC = RecordCodecBuilder.create( instance -> instance.group( BlockModelDefinition.SimpleModelSelectors.CODEC.optionalFieldOf("variants").forGetter(BlockModelDefinition::simpleModels), BlockModelDefinition.MultiPartDefinition.CODEC.optionalFieldOf("multipart").forGetter(BlockModelDefinition::multiPart) ) .apply(instance, BlockModelDefinition::new) ) .validate( blockModelDefinition -> blockModelDefinition.simpleModels().isEmpty() && blockModelDefinition.multiPart().isEmpty() ? DataResult.error(() -> "Neither 'variants' nor 'multipart' found") : DataResult.success(blockModelDefinition) ); public Map instantiate(StateDefinition stateDefinition, Supplier name) { Map map = new IdentityHashMap(); this.simpleModels.ifPresent(simpleModelSelectors -> simpleModelSelectors.instantiate(stateDefinition, name, (blockState, unbakedRoot) -> { BlockStateModel.UnbakedRoot unbakedRoot2 = (BlockStateModel.UnbakedRoot)map.put(blockState, unbakedRoot); if (unbakedRoot2 != null) { throw new IllegalArgumentException("Overlapping definition on state: " + blockState); } })); this.multiPart.ifPresent(multiPartDefinition -> { List list = stateDefinition.getPossibleStates(); BlockStateModel.UnbakedRoot unbakedRoot = multiPartDefinition.instantiate(stateDefinition); for (BlockState blockState : list) { map.putIfAbsent(blockState, unbakedRoot); } }); return map; } @Environment(EnvType.CLIENT) public record MultiPartDefinition(List selectors) { public static final Codec CODEC = ExtraCodecs.nonEmptyList(Selector.CODEC.listOf()) .xmap(BlockModelDefinition.MultiPartDefinition::new, BlockModelDefinition.MultiPartDefinition::selectors); public MultiPartModel.Unbaked instantiate(StateDefinition stateDefinition) { Builder> builder = ImmutableList.builderWithExpectedSize(this.selectors.size()); for (Selector selector : this.selectors) { builder.add(new MultiPartModel.Selector<>(selector.instantiate(stateDefinition), selector.variant())); } return new MultiPartModel.Unbaked(builder.build()); } } @Environment(EnvType.CLIENT) public record SimpleModelSelectors(Map models) { public static final Codec CODEC = ExtraCodecs.nonEmptyMap( Codec.unboundedMap(Codec.STRING, BlockStateModel.Unbaked.CODEC) ) .xmap(BlockModelDefinition.SimpleModelSelectors::new, BlockModelDefinition.SimpleModelSelectors::models); public void instantiate(StateDefinition stateDefinition, Supplier name, BiConsumer output) { this.models.forEach((string, unbaked) -> { try { Predicate> predicate = VariantSelector.predicate(stateDefinition, string); BlockStateModel.UnbakedRoot unbakedRoot = unbaked.asRoot(); for (BlockState blockState : stateDefinition.getPossibleStates()) { if (predicate.test(blockState)) { output.accept(blockState, unbakedRoot); } } } catch (Exception var9) { BlockModelDefinition.LOGGER.warn("Exception loading blockstate definition: '{}' for variant: '{}': {}", name.get(), string, var9.getMessage()); } }); } } }