/* * This file is part of LanternServer, licensed under the MIT License (MIT). * * Copyright (c) LanternPowered <https://www.lanternpowered.org> * Copyright (c) SpongePowered <https://www.spongepowered.org> * Copyright (c) contributors * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the Software), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package org.lanternpowered.server.block.state; import static com.google.common.base.Preconditions.checkNotNull; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableTable; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import org.lanternpowered.server.block.LanternBlockType; import org.lanternpowered.server.block.trait.LanternBlockTrait; import org.spongepowered.api.block.BlockState; import org.spongepowered.api.block.trait.BlockTrait; import org.spongepowered.api.data.key.Key; import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; public final class LanternBlockStateMap { private final ImmutableMap<String, BlockTrait<?>> blockTraits; private final ImmutableList<BlockState> blockStates; final ImmutableSet<Key<?>> keys; private final LanternBlockType blockType; @SuppressWarnings("rawtypes") public LanternBlockStateMap(LanternBlockType blockType, Iterable<BlockTrait<?>> blockTraits) { this.blockType = blockType; // There are no block traits if (!blockTraits.iterator().hasNext()) { LanternBlockState blockState = new LanternBlockState(this, ImmutableMap.of()); blockState.propertyValueTable = ImmutableTable.of(); this.blockStates = ImmutableList.of(blockState); this.blockTraits = ImmutableMap.of(); this.keys = ImmutableSet.of(); return; } // Convert to a list so it can be sorted List<BlockTrait<?>> list = Lists.newArrayList(blockTraits); // Sort the traits by the name Collections.sort(list, (o1, o2) -> o1.getName().compareTo(o2.getName())); // The builder for the name to trait lookup ImmutableMap.Builder<String, BlockTrait<?>> builder = ImmutableMap.builder(); ImmutableSet.Builder<Key<?>> keys = ImmutableSet.builder(); // All the sets with all the allowed values List<Set<Comparable<?>>> allowedValues = Lists.newArrayList(); for (BlockTrait<?> trait : list) { allowedValues.add(Sets.newHashSet(trait.getPossibleValues())); keys.add(((LanternBlockTrait) trait).getKey()); builder.put(trait.getName(), trait); } // Build the lookups this.blockTraits = builder.build(); this.keys = keys.build(); // A map with as the key the trait values map and as value the state LinkedHashMap<Map<?, ?>, BlockState> stateByValuesMap = Maps.newLinkedHashMap(); // The block states ImmutableList.Builder<BlockState> blockStates = ImmutableList.builder(); // Do the cartesian product to get all the possible combinations for (List<Comparable<?>> comparables : Sets.cartesianProduct(allowedValues)) { Iterator<Comparable<?>> objectsIt = comparables.iterator(); ImmutableMap.Builder<BlockTrait<?>, Comparable<?>> traitValuesBuilder = ImmutableMap.builder(); for (BlockTrait<?> trait : list) { traitValuesBuilder.put(trait, objectsIt.next()); } ImmutableMap<BlockTrait<?>, Comparable<?>> traitValues = traitValuesBuilder.build(); LanternBlockState blockState = new LanternBlockState(this, traitValues); stateByValuesMap.put(traitValues, blockState); blockStates.add(blockState); } this.blockStates = blockStates.build(); this.blockStates.stream().map(state -> (LanternBlockState) state).forEach(state -> { ImmutableTable.Builder<BlockTrait<?>, Comparable<?>, BlockState> tableBuilder = ImmutableTable.builder(); list.forEach(trait -> trait.getPossibleValues().stream().filter(value -> value != state.getTraitValue(trait).get()).forEach(value -> { Map<BlockTrait<?>, Comparable<?>> valueByTrait = Maps.newHashMap(); valueByTrait.putAll(state.traitValues); valueByTrait.put(trait, value); tableBuilder.put(trait, value, stateByValuesMap.get(valueByTrait)); })); state.propertyValueTable = tableBuilder.build(); }); int internalId = 0; for (BlockState blockState : this.blockStates) { final LanternBlockState blockState1 = (LanternBlockState) blockState; blockState1.extended = blockType.getExtendedBlockStateProvider().remove(blockState) != blockState; blockState1.internalId = internalId++; } } public LanternBlockType getBlockType() { return this.blockType; } public BlockState getBaseState() { return this.blockStates.get(0); } public Collection<BlockState> getBlockStates() { return this.blockStates; } public Optional<BlockTrait<?>> getTrait(String name) { if (this.blockTraits.containsKey(checkNotNull(name, "name"))) { return Optional.of(this.blockTraits.get(name)); } return Optional.empty(); } }