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.bukkit.util;
020
021import com.google.inject.Inject;
022import com.google.inject.Singleton;
023import com.plotsquared.bukkit.generator.BukkitPlotGenerator;
024import com.plotsquared.core.PlotSquared;
025import com.plotsquared.core.configuration.ConfigurationNode;
026import com.plotsquared.core.configuration.ConfigurationSection;
027import com.plotsquared.core.configuration.file.YamlConfiguration;
028import com.plotsquared.core.generator.GeneratorWrapper;
029import com.plotsquared.core.inject.annotations.WorldConfig;
030import com.plotsquared.core.inject.annotations.WorldFile;
031import com.plotsquared.core.plot.PlotArea;
032import com.plotsquared.core.plot.PlotAreaType;
033import com.plotsquared.core.plot.world.PlotAreaManager;
034import com.plotsquared.core.setup.PlotAreaBuilder;
035import com.plotsquared.core.util.SetupUtils;
036import com.plotsquared.core.util.task.TaskManager;
037import org.bukkit.Bukkit;
038import org.bukkit.Chunk;
039import org.bukkit.Location;
040import org.bukkit.World;
041import org.bukkit.entity.Player;
042import org.bukkit.generator.ChunkGenerator;
043import org.bukkit.plugin.Plugin;
044import org.checkerframework.checker.nullness.qual.NonNull;
045
046import java.io.File;
047import java.io.IOException;
048import java.util.HashMap;
049import java.util.Map.Entry;
050import java.util.Objects;
051
052@Singleton
053public class BukkitSetupUtils extends SetupUtils {
054
055    private final PlotAreaManager plotAreaManager;
056    private final YamlConfiguration worldConfiguration;
057    private final File worldFile;
058
059    @Inject
060    public BukkitSetupUtils(
061            final @NonNull PlotAreaManager plotAreaManager,
062            @WorldConfig final @NonNull YamlConfiguration worldConfiguration,
063            @WorldFile final @NonNull File worldFile
064    ) {
065        this.plotAreaManager = plotAreaManager;
066        this.worldConfiguration = worldConfiguration;
067        this.worldFile = worldFile;
068    }
069
070    @Override
071    public void updateGenerators(final boolean force) {
072        if (loaded && !SetupUtils.generators.isEmpty() && !force) {
073            return;
074        }
075        String testWorld = "CheckingPlotSquaredGenerator";
076        for (Plugin plugin : Bukkit.getPluginManager().getPlugins()) {
077            try {
078                if (plugin.isEnabled()) {
079                    ChunkGenerator generator = plugin.getDefaultWorldGenerator(testWorld, "");
080                    if (generator != null) {
081                        PlotSquared.get().removePlotAreas(testWorld);
082                        String name = plugin.getDescription().getName();
083                        GeneratorWrapper<?> wrapped;
084                        if (generator instanceof GeneratorWrapper<?>) {
085                            wrapped = (GeneratorWrapper<?>) generator;
086                        } else {
087                            wrapped = new BukkitPlotGenerator(testWorld, generator, this.plotAreaManager);
088                        }
089                        SetupUtils.generators.put(name, wrapped);
090                    }
091                }
092            } catch (Throwable e) { // Recover from third party generator error
093                e.printStackTrace();
094            }
095        }
096        loaded = true;
097    }
098
099    @Override
100    public void unload(String worldName, boolean save) {
101        TaskManager.runTask(() -> {
102            World world = Bukkit.getWorld(worldName);
103            if (world == null) {
104                return;
105            }
106            Location location = Bukkit.getWorlds().get(0).getSpawnLocation();
107            for (Player player : world.getPlayers()) {
108                player.teleport(location);
109            }
110            if (save) {
111                for (Chunk chunk : world.getLoadedChunks()) {
112                    chunk.unload(true);
113                }
114            } else {
115                for (Chunk chunk : world.getLoadedChunks()) {
116                    chunk.unload(false);
117                }
118            }
119            Bukkit.unloadWorld(world, false);
120        });
121    }
122
123    @Override
124    public String setupWorld(PlotAreaBuilder builder) {
125        this.updateGenerators(false);
126        ConfigurationNode[] steps = builder.settingsNodesWrapper() == null ?
127                new ConfigurationNode[0] : builder.settingsNodesWrapper().settingsNodes();
128        String world = builder.worldName();
129        PlotAreaType type = builder.plotAreaType();
130        String worldPath = "worlds." + builder.worldName();
131        switch (type) {
132            case PARTIAL -> {
133                if (builder.areaName() != null) {
134                    if (!this.worldConfiguration.contains(worldPath)) {
135                        this.worldConfiguration.createSection(worldPath);
136                    }
137                    ConfigurationSection worldSection =
138                            this.worldConfiguration.getConfigurationSection(worldPath);
139                    String areaName = builder.areaName() + "-" + builder.minimumId() + "-" + builder.maximumId();
140                    String areaPath = "areas." + areaName;
141                    if (!worldSection.contains(areaPath)) {
142                        worldSection.createSection(areaPath);
143                    }
144                    ConfigurationSection areaSection =
145                            worldSection.getConfigurationSection(areaPath);
146                    HashMap<String, Object> options = new HashMap<>();
147                    for (ConfigurationNode step : steps) {
148                        options.put(step.getConstant(), step.getValue());
149                    }
150                    options.put("generator.type", builder.plotAreaType().toString());
151                    options.put("generator.terrain", builder.terrainType().toString());
152                    options.put("generator.plugin", builder.plotManager());
153                    if (builder.generatorName() != null && !builder.generatorName()
154                            .equals(builder.plotManager())) {
155                        options.put("generator.init", builder.generatorName());
156                    }
157                    for (Entry<String, Object> entry : options.entrySet()) {
158                        String key = entry.getKey();
159                        Object value = entry.getValue();
160                        if (worldSection.contains(key)) {
161                            Object current = worldSection.get(key);
162                            if (!Objects.equals(value, current)) {
163                                areaSection.set(key, value);
164                            }
165                        } else {
166                            worldSection.set(key, value);
167                        }
168                    }
169                }
170                GeneratorWrapper<?> gen = SetupUtils.generators.get(builder.generatorName());
171                if (gen != null && gen.isFull()) {
172                    builder.generatorName(null);
173                }
174            }
175            case AUGMENTED -> {
176                if (!builder.plotManager().endsWith(":single")) {
177                    if (!this.worldConfiguration.contains(worldPath)) {
178                        this.worldConfiguration.createSection(worldPath);
179                    }
180                    if (steps.length != 0) {
181                        ConfigurationSection worldSection =
182                                this.worldConfiguration.getConfigurationSection(worldPath);
183                        for (ConfigurationNode step : steps) {
184                            worldSection.set(step.getConstant(), step.getValue());
185                        }
186                    }
187                    this.worldConfiguration
188                            .set("worlds." + world + ".generator.type", builder.plotAreaType().toString());
189                    this.worldConfiguration
190                            .set("worlds." + world + ".generator.terrain", builder.terrainType().toString());
191                    this.worldConfiguration
192                            .set("worlds." + world + ".generator.plugin", builder.plotManager());
193                    if (builder.generatorName() != null && !builder.generatorName()
194                            .equals(builder.plotManager())) {
195                        this.worldConfiguration
196                                .set("worlds." + world + ".generator.init", builder.generatorName());
197                    }
198                }
199                GeneratorWrapper<?> gen = SetupUtils.generators.get(builder.generatorName());
200                if (gen != null && gen.isFull()) {
201                    builder.generatorName(null);
202                }
203            }
204            case NORMAL -> {
205                if (steps.length != 0) {
206                    if (!this.worldConfiguration.contains(worldPath)) {
207                        this.worldConfiguration.createSection(worldPath);
208                    }
209                    ConfigurationSection worldSection =
210                            this.worldConfiguration.getConfigurationSection(worldPath);
211                    for (ConfigurationNode step : steps) {
212                        worldSection.set(step.getConstant(), step.getValue());
213                    }
214                }
215            }
216        }
217
218        try {
219            this.worldConfiguration.save(this.worldFile);
220        } catch (IOException e) {
221            e.printStackTrace();
222        }
223
224        Objects.requireNonNull(PlotSquared.platform()).worldManager()
225                .handleWorldCreation(builder.worldName(), builder.generatorName());
226
227        if (Bukkit.getWorld(world) != null) {
228            return world;
229        }
230
231        return builder.worldName();
232    }
233
234    @Override
235    public String getGenerator(PlotArea plotArea) {
236        if (SetupUtils.generators.isEmpty()) {
237            updateGenerators(false);
238        }
239        World world = Bukkit.getWorld(plotArea.getWorldName());
240        if (world == null) {
241            return null;
242        }
243        ChunkGenerator generator = world.getGenerator();
244        if (!(generator instanceof BukkitPlotGenerator)) {
245            return null;
246        }
247        for (Entry<String, GeneratorWrapper<?>> entry : SetupUtils.generators.entrySet()) {
248            GeneratorWrapper<?> current = entry.getValue();
249            if (current.equals(generator)) {
250                return entry.getKey();
251            }
252        }
253        return null;
254    }
255
256}