package net.minecraft.world.level.block.state; import com.google.common.base.MoreObjects; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSortedMap; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.mojang.datafixers.util.Pair; import com.mojang.serialization.Codec; import com.mojang.serialization.Decoder; import com.mojang.serialization.Encoder; import com.mojang.serialization.MapCodec; import it.unimi.dsi.fastutil.objects.Reference2ObjectArrayMap; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Supplier; import java.util.regex.Pattern; import java.util.stream.Collectors; import java.util.stream.Stream; import net.minecraft.world.level.block.state.properties.Property; import org.jetbrains.annotations.Nullable; public class StateDefinition> { static final Pattern NAME_PATTERN = Pattern.compile("^[a-z0-9_]+$"); private final O owner; private final ImmutableSortedMap> propertiesByName; private final ImmutableList states; protected StateDefinition(Function stateValueFunction, O owner, StateDefinition.Factory valueFunction, Map> propertiesByName) { this.owner = owner; this.propertiesByName = ImmutableSortedMap.copyOf(propertiesByName); Supplier supplier = () -> (StateHolder)stateValueFunction.apply(owner); MapCodec mapCodec = MapCodec.of(Encoder.empty(), Decoder.unit(supplier)); for (Entry> entry : this.propertiesByName.entrySet()) { mapCodec = appendPropertyCodec(mapCodec, supplier, (String)entry.getKey(), (Property)entry.getValue()); } MapCodec mapCodec2 = mapCodec; Map, Comparable>, S> map = Maps., Comparable>, S>newLinkedHashMap(); List list = Lists.newArrayList(); Stream, Comparable>>> stream = Stream.of(Collections.emptyList()); for (Property property : this.propertiesByName.values()) { stream = stream.flatMap(listx -> property.getPossibleValues().stream().map(comparable -> { List, Comparable>> list2 = Lists., Comparable>>newArrayList(listx); list2.add(Pair.of(property, comparable)); return list2; })); } stream.forEach(list2 -> { Reference2ObjectArrayMap, Comparable> reference2ObjectArrayMap = new Reference2ObjectArrayMap<>(list2.size()); for (Pair, Comparable> pair : list2) { reference2ObjectArrayMap.put(pair.getFirst(), pair.getSecond()); } S stateHolderx = valueFunction.create(owner, reference2ObjectArrayMap, mapCodec2); map.put(reference2ObjectArrayMap, stateHolderx); list.add(stateHolderx); }); for (S stateHolder : list) { stateHolder.populateNeighbours(map); } this.states = ImmutableList.copyOf(list); } private static , T extends Comparable> MapCodec appendPropertyCodec( MapCodec propertyCodec, Supplier holderSupplier, String value, Property property ) { return Codec.mapPair( propertyCodec, property.valueCodec().fieldOf(value).orElseGet((Consumer)(string -> {}), () -> property.value((StateHolder)holderSupplier.get())) ) .xmap( pair -> (StateHolder)((StateHolder)pair.getFirst()).setValue(property, ((Property.Value)pair.getSecond()).value()), stateHolder -> Pair.of(stateHolder, property.value(stateHolder)) ); } public ImmutableList getPossibleStates() { return this.states; } public S any() { return (S)this.states.get(0); } public O getOwner() { return this.owner; } public Collection> getProperties() { return this.propertiesByName.values(); } public String toString() { return MoreObjects.toStringHelper(this) .add("block", this.owner) .add("properties", this.propertiesByName.values().stream().map(Property::getName).collect(Collectors.toList())) .toString(); } @Nullable public Property getProperty(String propertyName) { return this.propertiesByName.get(propertyName); } public static class Builder> { private final O owner; private final Map> properties = Maps.>newHashMap(); public Builder(O owner) { this.owner = owner; } public StateDefinition.Builder add(Property... properties) { for (Property property : properties) { this.validateProperty(property); this.properties.put(property.getName(), property); } return this; } private > void validateProperty(Property property) { String string = property.getName(); if (!StateDefinition.NAME_PATTERN.matcher(string).matches()) { throw new IllegalArgumentException(this.owner + " has invalidly named property: " + string); } else { Collection collection = property.getPossibleValues(); if (collection.size() <= 1) { throw new IllegalArgumentException(this.owner + " attempted use property " + string + " with <= 1 possible values"); } else { for (T comparable : collection) { String string2 = property.getName(comparable); if (!StateDefinition.NAME_PATTERN.matcher(string2).matches()) { throw new IllegalArgumentException(this.owner + " has property: " + string + " with invalidly named value: " + string2); } } if (this.properties.containsKey(string)) { throw new IllegalArgumentException(this.owner + " has duplicate property: " + string); } } } } public StateDefinition create(Function stateValueFunction, StateDefinition.Factory stateFunction) { return new StateDefinition<>(stateValueFunction, this.owner, stateFunction, this.properties); } } public interface Factory { S create(O object, Reference2ObjectArrayMap, Comparable> reference2ObjectArrayMap, MapCodec mapCodec); } }