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.command;
020
021import com.google.common.collect.Lists;
022import com.google.inject.Inject;
023import com.plotsquared.core.configuration.Settings;
024import com.plotsquared.core.configuration.caption.TranslatableCaption;
025import com.plotsquared.core.location.Location;
026import com.plotsquared.core.permissions.Permission;
027import com.plotsquared.core.player.ConsolePlayer;
028import com.plotsquared.core.player.PlotPlayer;
029import com.plotsquared.core.plot.Plot;
030import com.plotsquared.core.plot.PlotArea;
031import com.plotsquared.core.plot.schematic.Schematic;
032import com.plotsquared.core.plot.world.PlotAreaManager;
033import com.plotsquared.core.util.SchematicHandler;
034import com.plotsquared.core.util.StringMan;
035import com.plotsquared.core.util.TabCompletions;
036import com.plotsquared.core.util.task.RunnableVal;
037import com.plotsquared.core.util.task.TaskManager;
038import net.kyori.adventure.text.minimessage.Template;
039import org.checkerframework.checker.nullness.qual.NonNull;
040
041import java.net.URL;
042import java.util.ArrayList;
043import java.util.Collection;
044import java.util.Collections;
045import java.util.LinkedList;
046import java.util.List;
047import java.util.UUID;
048import java.util.stream.Collectors;
049
050@CommandDeclaration(command = "schematic",
051        permission = "plots.schematic",
052        aliases = "schem",
053        category = CommandCategory.SCHEMATIC,
054        usage = "/plot schematic <save | saveall | paste | list>")
055public class SchematicCmd extends SubCommand {
056
057    private final PlotAreaManager plotAreaManager;
058    private final SchematicHandler schematicHandler;
059    private boolean running = false;
060
061    @Inject
062    public SchematicCmd(
063            final @NonNull PlotAreaManager plotAreaManager,
064            final @NonNull SchematicHandler schematicHandler
065    ) {
066        this.plotAreaManager = plotAreaManager;
067        this.schematicHandler = schematicHandler;
068    }
069
070    @Override
071    public boolean onCommand(final PlotPlayer<?> player, String[] args) {
072        if (args.length < 1) {
073            player.sendMessage(
074                    TranslatableCaption.of("commandconfig.command_syntax"),
075                    Template.of("value", "Possible values: save, paste, exportall, list")
076            );
077            return true;
078        }
079        String arg = args[0].toLowerCase();
080        switch (arg) {
081            case "paste" -> {
082                if (!player.hasPermission(Permission.PERMISSION_SCHEMATIC_PASTE)) {
083                    player.sendMessage(
084                            TranslatableCaption.of("permission.no_permission"),
085                            Template.of("node", String.valueOf(Permission.PERMISSION_SCHEMATIC_PASTE))
086                    );
087                    return false;
088                }
089                if (args.length < 2) {
090                    player.sendMessage(
091                            TranslatableCaption.of("commandconfig.command_syntax"),
092                            Template.of("value", "Possible values: save, paste, exportall, list")
093                    );
094                    break;
095                }
096                Location loc = player.getLocation();
097                final Plot plot = loc.getPlotAbs();
098                if (plot == null) {
099                    player.sendMessage(TranslatableCaption.of("errors.not_in_plot"));
100                    return false;
101                }
102                if (!plot.hasOwner()) {
103                    player.sendMessage(TranslatableCaption.of("info.plot_unowned"));
104                    return false;
105                }
106                if (!plot.isOwner(player.getUUID()) && !player.hasPermission("plots.admin.command.schematic.paste")) {
107                    player.sendMessage(TranslatableCaption.of("permission.no_plot_perms"));
108                    return false;
109                }
110                if (plot.getVolume() > Integer.MAX_VALUE) {
111                    player.sendMessage(TranslatableCaption.of("schematics.schematic_too_large"));
112                    return false;
113                }
114                if (this.running) {
115                    player.sendMessage(TranslatableCaption.of("error.task_in_process"));
116                    return false;
117                }
118                final String location = args[1];
119                this.running = true;
120                TaskManager.runTaskAsync(() -> {
121                    Schematic schematic = null;
122                    if (location.startsWith("url:")) {
123                        try {
124                            UUID uuid = UUID.fromString(location.substring(4));
125                            URL base = new URL(Settings.Web.URL);
126                            URL url = new URL(base, "uploads/" + uuid + ".schematic");
127                            schematic = this.schematicHandler.getSchematic(url);
128                        } catch (Exception e) {
129                            e.printStackTrace();
130                            player.sendMessage(
131                                    TranslatableCaption.of("schematics.schematic_invalid"),
132                                    Template.of("reason", "non-existent url: " + location)
133                            );
134                            SchematicCmd.this.running = false;
135                            return;
136                        }
137                    } else {
138                        try {
139                            schematic = this.schematicHandler.getSchematic(location);
140                        } catch (SchematicHandler.UnsupportedFormatException e) {
141                            e.printStackTrace();
142                        }
143                    }
144                    if (schematic == null) {
145                        SchematicCmd.this.running = false;
146                        player.sendMessage(
147                                TranslatableCaption.of("schematics.schematic_invalid"),
148                                Template.of("reason", "non-existent or not in gzip format")
149                        );
150                        return;
151                    }
152                    this.schematicHandler.paste(
153                            schematic,
154                            plot,
155                            0,
156                            plot.getArea().getMinBuildHeight(),
157                            0,
158                            false,
159                            player,
160                            new RunnableVal<>() {
161                                @Override
162                                public void run(Boolean value) {
163                                    SchematicCmd.this.running = false;
164                                    if (value) {
165                                        player.sendMessage(TranslatableCaption.of("schematics.schematic_paste_success"));
166                                    } else {
167                                        player.sendMessage(TranslatableCaption.of("schematics.schematic_paste_failed"));
168                                    }
169                                }
170                            }
171                    );
172                });
173            }
174            case "saveall", "exportall" -> {
175                if (!(player instanceof ConsolePlayer)) {
176                    player.sendMessage(TranslatableCaption.of("console.not_console"));
177                    return false;
178                }
179                if (args.length != 2) {
180                    player.sendMessage(TranslatableCaption.of("schematics.schematic_exportall_world_args"));
181                    player.sendMessage(
182                            TranslatableCaption.of("commandconfig.command_syntax"),
183                            Template.of("value", "Use /plot schematic exportall <area>")
184                    );
185                    return false;
186                }
187                PlotArea area = this.plotAreaManager.getPlotAreaByString(args[1]);
188                if (area == null) {
189                    player.sendMessage(
190                            TranslatableCaption.of("errors.not_valid_plot_world"),
191                            Template.of("value", args[1])
192                    );
193                    return false;
194                }
195                Collection<Plot> plots = area.getPlots();
196                if (plots.isEmpty()) {
197                    player.sendMessage(TranslatableCaption.of("schematic.schematic_exportall_world"));
198                    player.sendMessage(
199                            TranslatableCaption.of("commandconfig.command_syntax"),
200                            Template.of("value", "Use /plot sch exportall <area>")
201                    );
202                    return false;
203                }
204                boolean result = this.schematicHandler.exportAll(plots, null, null,
205                        () -> player.sendMessage(TranslatableCaption.of("schematics.schematic_exportall_finished"))
206                );
207                if (!result) {
208                    player.sendMessage(TranslatableCaption.of("error.task_in_process"));
209                    return false;
210                } else {
211                    player.sendMessage(TranslatableCaption.of("schematics.schematic_exportall_started"));
212                    player.sendMessage(
213                            TranslatableCaption.of("schematics.plot_to_schem"),
214                            Template.of("amount", String.valueOf(plots.size()))
215                    );
216                }
217            }
218            case "export", "save" -> {
219                if (!player.hasPermission(Permission.PERMISSION_SCHEMATIC_SAVE)) {
220                    player.sendMessage(
221                            TranslatableCaption.of("permission.no_permission"),
222                            Template.of("node", String.valueOf(Permission.PERMISSION_SCHEMATIC_SAVE))
223                    );
224                    return false;
225                }
226                if (this.running) {
227                    player.sendMessage(TranslatableCaption.of("error.task_in_process"));
228                    return false;
229                }
230                Location location = player.getLocation();
231                Plot plot = location.getPlotAbs();
232                if (plot == null) {
233                    player.sendMessage(TranslatableCaption.of("errors.not_in_plot"));
234                    return false;
235                }
236                if (!plot.hasOwner()) {
237                    player.sendMessage(TranslatableCaption.of("info.plot_unowned"));
238                    return false;
239                }
240                if (plot.getVolume() > Integer.MAX_VALUE) {
241                    player.sendMessage(TranslatableCaption.of("schematics.schematic_too_large"));
242                    return false;
243                }
244                if (!plot.isOwner(player.getUUID()) && !player.hasPermission("plots.admin.command.schematic.save")) {
245                    player.sendMessage(TranslatableCaption.of("permission.no_plot_perms"));
246                    return false;
247                }
248                ArrayList<Plot> plots = Lists.newArrayList(plot);
249                boolean result = this.schematicHandler.exportAll(plots, null, null, () -> {
250                    player.sendMessage(TranslatableCaption.of("schematics.schematic_exportall_single_finished"));
251                    SchematicCmd.this.running = false;
252                });
253                if (!result) {
254                    player.sendMessage(TranslatableCaption.of("error.task_in_process"));
255                    return false;
256                } else {
257                    player.sendMessage(TranslatableCaption.of("schematics.schematic_exportall_started"));
258                }
259            }
260            case "list" -> {
261                if (!player.hasPermission(Permission.PERMISSION_SCHEMATIC_LIST)) {
262                    player.sendMessage(
263                            TranslatableCaption.of("permission.no_permission"),
264                            Template.of("node", String.valueOf(Permission.PERMISSION_SCHEMATIC_LIST))
265                    );
266                    return false;
267                }
268                final String string = StringMan.join(this.schematicHandler.getSchematicNames(), "$2, $1");
269                player.sendMessage(
270                        TranslatableCaption.of("schematics.schematic_list"),
271                        Template.of("list", string)
272                );
273            }
274            default -> player.sendMessage(
275                    TranslatableCaption.of("commandconfig.command_syntax"),
276                    Template.of("value", "Possible values: save, paste, exportall, list")
277            );
278        }
279        return true;
280    }
281
282    @Override
283    public Collection<Command> tab(final PlotPlayer<?> player, final String[] args, final boolean space) {
284        if (args.length == 1) {
285            final List<String> completions = new LinkedList<>();
286            if (player.hasPermission(Permission.PERMISSION_SCHEMATIC_SAVE)) {
287                completions.add("save");
288            }
289            if (player.hasPermission(Permission.PERMISSION_SCHEMATIC_LIST)) {
290                completions.add("list");
291            }
292            if (player.hasPermission(Permission.PERMISSION_SCHEMATIC_PASTE)) {
293                completions.add("paste");
294            }
295            final List<Command> commands = completions.stream().filter(completion -> completion
296                            .toLowerCase()
297                            .startsWith(args[0].toLowerCase()))
298                    .map(completion -> new Command(
299                            null,
300                            true,
301                            completion,
302                            "",
303                            RequiredType.NONE,
304                            CommandCategory.ADMINISTRATION
305                    ) {
306                    }).collect(Collectors.toCollection(LinkedList::new));
307            if (player.hasPermission(Permission.PERMISSION_SCHEMATIC) && args[0].length() > 0) {
308                commands.addAll(TabCompletions.completePlayers(player, args[0], Collections.emptyList()));
309            }
310            return commands;
311        }
312        return TabCompletions.completePlayers(player, String.join(",", args).trim(), Collections.emptyList());
313    }
314
315}