/* * 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.world.gen.flat; import com.flowpowered.math.GenericMath; import com.google.common.base.Joiner; import com.google.common.base.Splitter; import com.google.common.collect.Lists; import org.lanternpowered.server.game.registry.type.block.BlockRegistryModule; import org.lanternpowered.server.game.registry.type.world.biome.BiomeRegistryModule; import org.spongepowered.api.block.BlockState; import org.spongepowered.api.block.BlockType; import org.spongepowered.api.block.BlockTypes; import org.spongepowered.api.data.DataContainer; import org.spongepowered.api.data.DataQuery; import org.spongepowered.api.data.DataView; import org.spongepowered.api.data.MemoryDataContainer; import org.spongepowered.api.util.Coerce; import org.spongepowered.api.world.biome.BiomeType; import org.spongepowered.api.world.biome.BiomeTypes; import java.util.List; import java.util.Optional; import javax.annotation.Nullable; public final class FlatGeneratorSettingsParser { public static String toString(FlatGeneratorSettings settings) { // All the parts List<Object> parts = Lists.newArrayList(); // The current version parts.add(3); // All the layers List<String> layers = Lists.newArrayList(); settings.getLayers().forEach(layer -> { StringBuilder builder = new StringBuilder(); int depth = layer.getDepth(); // Only append the depth if needed if (depth > 1) { builder.append(depth).append('*'); } BlockState block = layer.getBlockState(); // Append the block id builder.append(block.getType().getId()); int data = BlockRegistryModule.get().getStateData(block); // Only append the data if needed if (data > 0) { builder.append(':').append(data); } layers.add(builder.toString()); }); // Add the layers part parts.add(Joiner.on(',').join(layers)); // Add the biome id part parts.add(BiomeRegistryModule.get().getInternalId(settings.getBiomeType())); List<String> extraDataValues = Lists.newArrayList(); settings.getExtraData().getValues(false).entrySet().stream().forEach(e -> { Object value = e.getValue(); if (value instanceof DataView) { List<String> values = Lists.newArrayList(); ((DataView) value).getValues(false).entrySet().stream().forEach(e1 -> { Object value1 = e1.getValue(); // Only integer numbers are currently supported if (value instanceof Number) { values.add(e1.getKey().getParts().get(0) + '=' + ((Number) value1).intValue()); } }); StringBuilder builder = new StringBuilder(); builder.append(e.getKey().getParts().get(0)); if (values.size() > 0) { builder.append('('); builder.append(Joiner.on(' ').join(values)); builder.append(')'); } extraDataValues.add(builder.toString()); } }); if (!extraDataValues.isEmpty()) { parts.add(Joiner.on(',').join(extraDataValues)); } return Joiner.on(';').join(parts); } @Nullable public static FlatGeneratorSettings fromString(@Nullable String value) { if (value == null) { return null; } // Split the value into parts List<String> parts = Lists.newArrayList(Splitter.on(';').split(value)); // Try to extract the version from the parts int version = 0; if (parts.size() > 1) { version = Coerce.toInteger(parts.remove(0)); } // Smaller then 0 is unknown? and 3 is the latest format version if (version < 0 || version > 3) { return null; } // The layers are stored in the first part String layersPart = parts.remove(0); // The parsed layers List<FlatLayer> layers = Lists.newArrayList(); // Can be empty if there are no layers if (!layersPart.isEmpty()) { // The seperator that can be used to create a layer // of x amount of blocks final char depthSeperator = version >= 3 ? '*' : 'x'; Splitter.on(',').split(layersPart).forEach(s -> { // The block type BlockType blockType; // The data value (optional) int blockData = 0; // The depth of the layer int depth = 1; // The depth seperated by the depth seperator followed by the block state List<String> parts1 = Lists.newArrayList(Splitter.on(depthSeperator).limit(2).split(s)); if (parts1.size() > 1) { Optional<Integer> optDepth = Coerce.asInteger(parts1.remove(0)); if (optDepth.isPresent()) { depth = GenericMath.clamp(optDepth.get(), 0, 255); if (depth <= 0) { // Skip to the next layer return; } } } String blockStatePart = parts1.get(0); int index = blockStatePart.lastIndexOf(':'); if (index > 0) { Optional<Integer> optData = Coerce.asInteger(blockStatePart.substring(index + 1)); if (optData.isPresent()) { blockData = GenericMath.clamp(optData.get(), 0, 15); blockStatePart = blockStatePart.substring(0, index); } } // Try to parse the block id as internal (int) id Optional<Integer> optId = Coerce.asInteger(blockStatePart); if (optId.isPresent()) { blockType = BlockRegistryModule.get().getStateByInternalId(optId.get()).orElse(BlockTypes.STONE.getDefaultState()).getType(); // Not an integer, try the catalog system } else { blockType = BlockRegistryModule.get().getById(blockStatePart).orElse(BlockTypes.STONE); } layers.add(new FlatLayer(BlockRegistryModule.get().getStateByTypeAndData(blockType, (byte) blockData).get(), depth)); }); } // Try to parse the biome type if present BiomeType biomeType = BiomeTypes.PLAINS; if (!parts.isEmpty()) { String biomePart = parts.remove(0); Optional<Integer> optBiomeId = Coerce.asInteger(biomePart); Optional<BiomeType> optBiome; if (optBiomeId.isPresent()) { optBiome = BiomeRegistryModule.get().getByInternalId(optBiomeId.get()); } else { optBiome = BiomeRegistryModule.get().getById(biomePart); } if (optBiome.isPresent()) { biomeType = optBiome.get(); } } // Extra data (like structures) DataContainer extraData = new MemoryDataContainer(); if (!parts.isEmpty()) { String extraPart = parts.remove(0); if (!extraPart.isEmpty()) { Splitter.on(',').split(extraPart).forEach(s -> { String key = s; // Check if there is extra data attached to the key int valuesIndex = s.indexOf('('); if (valuesIndex != -1) { // Separate the key from the values key = s.substring(0, valuesIndex); int endIndex = s.lastIndexOf(')'); if (endIndex == -1) { endIndex = s.length(); } // Get the values section from the string s = s.substring(valuesIndex + 1, endIndex); // Create the view to store the values DataView dataView = extraData.createView(DataQuery.of(key)); if (!s.isEmpty()) { Splitter.on(' ').split(s).forEach(v -> { List<String> parts1 = Splitter.on('=').limit(2).splitToList(v); // Must be greater then 1, otherwise it's invalid if (parts1.size() > 1) { // Currently, only integer values seem to be supported dataView.set(DataQuery.of(parts1.get(0)), Coerce.toInteger(parts1.get(1))); } }); } } else { extraData.createView(DataQuery.of(key)); } }); } } return new FlatGeneratorSettings(biomeType, layers, extraData); } private FlatGeneratorSettingsParser() { } }