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.Component;
028import net.kyori.adventure.text.minimessage.tag.Tag;
029import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
030import org.apache.logging.log4j.LogManager;
031import org.apache.logging.log4j.Logger;
032import org.checkerframework.checker.nullness.qual.NonNull;
033
034import java.util.Collection;
035import java.util.HashMap;
036import java.util.List;
037import java.util.Map;
038
039/**
040 * Converts legacy configurations into the new (BlockBucket) format
041 */
042@SuppressWarnings("unused")
043public final class LegacyConverter {
044
045    public static final String CONFIGURATION_VERSION = "post_flattening";
046    private static final Logger LOGGER = LogManager.getLogger("PlotSquared/" + LegacyConverter.class.getSimpleName());
047    private static final HashMap<String, ConfigurationType> TYPE_MAP = new HashMap<>();
048
049    static {
050        TYPE_MAP.put("plot.filling", ConfigurationType.BLOCK_LIST);
051        TYPE_MAP.put("plot.floor", ConfigurationType.BLOCK_LIST);
052        TYPE_MAP.put("wall.filling", ConfigurationType.BLOCK);
053        TYPE_MAP.put("wall.block_claimed", ConfigurationType.BLOCK);
054        TYPE_MAP.put("wall.block", ConfigurationType.BLOCK);
055        TYPE_MAP.put("road.block", ConfigurationType.BLOCK);
056    }
057
058    private final ConfigurationSection configuration;
059
060    public LegacyConverter(final @NonNull ConfigurationSection configuration) {
061        this.configuration = configuration;
062    }
063
064    private BlockBucket blockToBucket(final @NonNull String block) {
065        final BlockState plotBlock = PlotSquared.platform().worldUtil().getClosestBlock(block).best;
066        return BlockBucket.withSingle(plotBlock);
067    }
068
069    private void setString(
070            final @NonNull ConfigurationSection section,
071            final @NonNull String string, final @NonNull BlockBucket blocks
072    ) {
073        if (!section.contains(string)) {
074            throw new IllegalArgumentException(String.format("No such key: %s", string));
075        }
076        section.set(string, blocks.toString());
077    }
078
079    private BlockBucket blockListToBucket(final @NonNull BlockState[] blocks) {
080        final Map<BlockState, Integer> counts = new HashMap<>();
081        for (final BlockState block : blocks) {
082            counts.putIfAbsent(block, 0);
083            counts.put(block, counts.get(block) + 1);
084        }
085        boolean includeRatios = false;
086        for (final Integer integer : counts.values()) {
087            if (integer > 1) {
088                includeRatios = true;
089                break;
090            }
091        }
092        final BlockBucket bucket = new BlockBucket();
093        if (includeRatios) {
094            for (final Map.Entry<BlockState, Integer> count : counts.entrySet()) {
095                bucket.addBlock(count.getKey(), count.getValue());
096            }
097        } else {
098            counts.keySet().forEach(bucket::addBlock);
099        }
100        return bucket;
101    }
102
103    private BlockState[] splitBlockList(final @NonNull List<String> list) {
104        return list.stream().map(s -> PlotSquared.platform().worldUtil().getClosestBlock(s).best)
105                .toArray(BlockState[]::new);
106    }
107
108    private void convertBlock(
109            final @NonNull ConfigurationSection section,
110            final @NonNull String key,
111            final @NonNull String block
112    ) {
113        final BlockBucket bucket = this.blockToBucket(block);
114        this.setString(section, key, bucket);
115        ConsolePlayer.getConsole().sendMessage(
116                TranslatableCaption.of("legacyconfig.legacy_config_replaced"),
117                TagResolver.builder()
118                        .tag("value1", Tag.inserting(Component.text(block)))
119                        .tag("value2", Tag.inserting(Component.text(bucket.toString())))
120                        .build()
121        );
122    }
123
124    private void convertBlockList(
125            final @NonNull ConfigurationSection section,
126            final @NonNull String key,
127            final @NonNull List<String> blockList
128    ) {
129        final BlockState[] blocks = this.splitBlockList(blockList);
130        final BlockBucket bucket = this.blockListToBucket(blocks);
131        this.setString(section, key, bucket);
132        ConsolePlayer.getConsole()
133                .sendMessage(
134                        TranslatableCaption.of("legacyconfig.legacy_config_replaced"),
135                        TagResolver.builder()
136                                .tag("value1", Tag.inserting(Component.text(plotBlockArrayString(blocks))))
137                                .tag("value2", Tag.inserting(Component.text(bucket.toString())))
138                                .build()
139                );
140    }
141
142    private String plotBlockArrayString(final @NonNull BlockState[] blocks) {
143        final StringBuilder builder = new StringBuilder();
144        for (int i = 0; i < blocks.length; i++) {
145            builder.append(blocks[i].toString());
146            if ((i + 1) < blocks.length) {
147                builder.append(",");
148            }
149        }
150        return builder.toString();
151    }
152
153    public void convert() {
154        // Section is the "worlds" section
155        final Collection<String> worlds = this.configuration.getKeys(false);
156        for (final String world : worlds) {
157            final ConfigurationSection worldSection = configuration.getConfigurationSection(world);
158            for (final Map.Entry<String, ConfigurationType> entry : TYPE_MAP.entrySet()) {
159                if (worldSection.contains(entry.getKey())) {
160                    if (entry.getValue() == ConfigurationType.BLOCK) {
161                        this.convertBlock(worldSection, entry.getKey(),
162                                worldSection.getString(entry.getKey())
163                        );
164                    } else {
165                        this.convertBlockList(worldSection, entry.getKey(),
166                                worldSection.getStringList(entry.getKey())
167                        );
168                    }
169                }
170            }
171        }
172    }
173
174    private enum ConfigurationType {
175        BLOCK,
176        BLOCK_LIST
177    }
178
179}