001/*
002 * PlotSquared, a land and world management plugin for Minecraft.
003 * Copyright (C) IntellectualSites <https://intellectualsites.com>
004 * Copyright (C) IntellectualSites team and contributors
005 *
006 * This program is free software: you can redistribute it and/or modify
007 * it under the terms of the GNU General Public License as published by
008 * the Free Software Foundation, either version 3 of the License, or
009 * (at your option) any later version.
010 *
011 * This program is distributed in the hope that it will be useful,
012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
014 * GNU General Public License for more details.
015 *
016 * You should have received a copy of the GNU General Public License
017 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
018 */
019package com.plotsquared.core.util;
020
021import com.plotsquared.core.PlotSquared;
022import com.plotsquared.core.configuration.ConfigurationSection;
023import com.plotsquared.core.configuration.caption.TranslatableCaption;
024import com.plotsquared.core.player.ConsolePlayer;
025import com.plotsquared.core.plot.BlockBucket;
026import com.sk89q.worldedit.world.block.BlockState;
027import net.kyori.adventure.text.minimessage.Template;
028import org.apache.logging.log4j.LogManager;
029import org.apache.logging.log4j.Logger;
030import org.checkerframework.checker.nullness.qual.NonNull;
031
032import java.util.Collection;
033import java.util.HashMap;
034import java.util.List;
035import java.util.Map;
036
037/**
038 * Converts legacy configurations into the new (BlockBucket) format
039 */
040@SuppressWarnings("unused")
041public final class LegacyConverter {
042
043    public static final String CONFIGURATION_VERSION = "post_flattening";
044    private static final Logger LOGGER = LogManager.getLogger("PlotSquared/" + LegacyConverter.class.getSimpleName());
045    private static final HashMap<String, ConfigurationType> TYPE_MAP = new HashMap<>();
046
047    static {
048        TYPE_MAP.put("plot.filling", ConfigurationType.BLOCK_LIST);
049        TYPE_MAP.put("plot.floor", ConfigurationType.BLOCK_LIST);
050        TYPE_MAP.put("wall.filling", ConfigurationType.BLOCK);
051        TYPE_MAP.put("wall.block_claimed", ConfigurationType.BLOCK);
052        TYPE_MAP.put("wall.block", ConfigurationType.BLOCK);
053        TYPE_MAP.put("road.block", ConfigurationType.BLOCK);
054    }
055
056    private final ConfigurationSection configuration;
057
058    public LegacyConverter(final @NonNull ConfigurationSection configuration) {
059        this.configuration = configuration;
060    }
061
062    private BlockBucket blockToBucket(final @NonNull String block) {
063        final BlockState plotBlock = PlotSquared.platform().worldUtil().getClosestBlock(block).best;
064        return BlockBucket.withSingle(plotBlock);
065    }
066
067    private void setString(
068            final @NonNull ConfigurationSection section,
069            final @NonNull String string, final @NonNull BlockBucket blocks
070    ) {
071        if (!section.contains(string)) {
072            throw new IllegalArgumentException(String.format("No such key: %s", string));
073        }
074        section.set(string, blocks.toString());
075    }
076
077    private BlockBucket blockListToBucket(final @NonNull BlockState[] blocks) {
078        final Map<BlockState, Integer> counts = new HashMap<>();
079        for (final BlockState block : blocks) {
080            counts.putIfAbsent(block, 0);
081            counts.put(block, counts.get(block) + 1);
082        }
083        boolean includeRatios = false;
084        for (final Integer integer : counts.values()) {
085            if (integer > 1) {
086                includeRatios = true;
087                break;
088            }
089        }
090        final BlockBucket bucket = new BlockBucket();
091        if (includeRatios) {
092            for (final Map.Entry<BlockState, Integer> count : counts.entrySet()) {
093                bucket.addBlock(count.getKey(), count.getValue());
094            }
095        } else {
096            counts.keySet().forEach(bucket::addBlock);
097        }
098        return bucket;
099    }
100
101    private BlockState[] splitBlockList(final @NonNull List<String> list) {
102        return list.stream().map(s -> PlotSquared.platform().worldUtil().getClosestBlock(s).best)
103                .toArray(BlockState[]::new);
104    }
105
106    private void convertBlock(
107            final @NonNull ConfigurationSection section,
108            final @NonNull String key,
109            final @NonNull String block
110    ) {
111        final BlockBucket bucket = this.blockToBucket(block);
112        this.setString(section, key, bucket);
113        ConsolePlayer.getConsole().sendMessage(
114                TranslatableCaption.of("legacyconfig.legacy_config_replaced"),
115                Template.of("value1", block),
116                Template.of("value2", bucket.toString())
117        );
118    }
119
120    private void convertBlockList(
121            final @NonNull ConfigurationSection section,
122            final @NonNull String key,
123            final @NonNull List<String> blockList
124    ) {
125        final BlockState[] blocks = this.splitBlockList(blockList);
126        final BlockBucket bucket = this.blockListToBucket(blocks);
127        this.setString(section, key, bucket);
128        ConsolePlayer.getConsole()
129                .sendMessage(
130                        TranslatableCaption.of("legacyconfig.legacy_config_replaced"),
131                        Template.of("value1", plotBlockArrayString(blocks)),
132                        Template.of("value2", bucket.toString())
133                );
134    }
135
136    private String plotBlockArrayString(final @NonNull BlockState[] blocks) {
137        final StringBuilder builder = new StringBuilder();
138        for (int i = 0; i < blocks.length; i++) {
139            builder.append(blocks[i].toString());
140            if ((i + 1) < blocks.length) {
141                builder.append(",");
142            }
143        }
144        return builder.toString();
145    }
146
147    public void convert() {
148        // Section is the "worlds" section
149        final Collection<String> worlds = this.configuration.getKeys(false);
150        for (final String world : worlds) {
151            final ConfigurationSection worldSection = configuration.getConfigurationSection(world);
152            for (final Map.Entry<String, ConfigurationType> entry : TYPE_MAP.entrySet()) {
153                if (worldSection.contains(entry.getKey())) {
154                    if (entry.getValue() == ConfigurationType.BLOCK) {
155                        this.convertBlock(worldSection, entry.getKey(),
156                                worldSection.getString(entry.getKey())
157                        );
158                    } else {
159                        this.convertBlockList(worldSection, entry.getKey(),
160                                worldSection.getStringList(entry.getKey())
161                        );
162                    }
163                }
164            }
165        }
166    }
167
168    private enum ConfigurationType {
169        BLOCK,
170        BLOCK_LIST
171    }
172
173}