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.inject.Inject;
022import com.plotsquared.core.configuration.Settings;
023import com.plotsquared.core.configuration.caption.StaticCaption;
024import com.plotsquared.core.configuration.caption.TranslatableCaption;
025import com.plotsquared.core.permissions.Permission;
026import com.plotsquared.core.player.MetaDataAccess;
027import com.plotsquared.core.player.PlayerMetaDataKeys;
028import com.plotsquared.core.player.PlotPlayer;
029import com.plotsquared.core.plot.Plot;
030import com.plotsquared.core.plot.PlotArea;
031import com.plotsquared.core.plot.PlotId;
032import com.plotsquared.core.plot.schematic.Schematic;
033import com.plotsquared.core.plot.world.PlotAreaManager;
034import com.plotsquared.core.util.SchematicHandler;
035import com.plotsquared.core.util.TimeUtil;
036import com.plotsquared.core.util.task.RunnableVal;
037import com.plotsquared.core.util.task.TaskManager;
038import net.kyori.adventure.text.Component;
039import net.kyori.adventure.text.minimessage.tag.Tag;
040import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
041import org.checkerframework.checker.nullness.qual.NonNull;
042
043import java.net.MalformedURLException;
044import java.net.URI;
045import java.net.URL;
046import java.util.Collections;
047import java.util.List;
048
049@CommandDeclaration(command = "load",
050        aliases = "restore",
051        category = CommandCategory.SCHEMATIC,
052        requiredType = RequiredType.NONE,
053        permission = "plots.load",
054        usage = "/plot load")
055public class Load extends SubCommand {
056
057    private final PlotAreaManager plotAreaManager;
058    private final SchematicHandler schematicHandler;
059
060    @Inject
061    public Load(
062            final @NonNull PlotAreaManager plotAreaManager,
063            final @NonNull SchematicHandler schematicHandler
064    ) {
065        this.plotAreaManager = plotAreaManager;
066        this.schematicHandler = schematicHandler;
067    }
068
069    @Override
070    public boolean onCommand(final PlotPlayer<?> player, final String[] args) {
071        final Plot plot = player.getCurrentPlot();
072        if (plot == null) {
073            player.sendMessage(TranslatableCaption.of("errors.not_in_plot"));
074            return false;
075        }
076        if (!plot.hasOwner()) {
077            player.sendMessage(TranslatableCaption.of("info.plot_unowned"));
078            return false;
079        }
080        if (!plot.isOwner(player.getUUID()) && !player.hasPermission(Permission.PERMISSION_ADMIN_COMMAND_LOAD)) {
081            player.sendMessage(TranslatableCaption.of("permission.no_plot_perms"));
082            return false;
083        }
084        if (plot.getRunning() > 0) {
085            player.sendMessage(TranslatableCaption.of("errors.wait_for_timer"));
086            return false;
087        }
088
089        try (final MetaDataAccess<List<String>> metaDataAccess =
090                     player.accessTemporaryMetaData(PlayerMetaDataKeys.TEMPORARY_SCHEMATICS)) {
091            if (args.length != 0) {
092                if (args.length == 1) {
093                    List<String> schematics = metaDataAccess.get().orElse(null);
094                    if (schematics == null) {
095                        // No schematics found:
096                        player.sendMessage(
097                                TranslatableCaption.of("web.load_null"),
098                                TagResolver.resolver("command", Tag.inserting(Component.text("/plot load")))
099                        );
100                        return false;
101                    }
102                    String schematic;
103                    try {
104                        schematic = schematics.get(Integer.parseInt(args[0]) - 1);
105                    } catch (Exception ignored) {
106                        // use /plot load <index>
107                        player.sendMessage(
108                                TranslatableCaption.of("invalid.not_valid_number"),
109                                TagResolver.resolver("value", Tag.inserting(Component.text("(1, " + schematics.size() + ')')))
110                        );
111                        return false;
112                    }
113                    final URL url;
114                    try {
115                        url = URI.create(Settings.Web.URL + "saves/" + player.getUUID() + '/' + schematic).toURL();
116                    } catch (MalformedURLException e) {
117                        e.printStackTrace();
118                        player.sendMessage(TranslatableCaption.of("web.load_failed"));
119                        return false;
120                    }
121                    plot.addRunning();
122                    player.sendMessage(TranslatableCaption.of("working.generating_component"));
123                    TaskManager.runTaskAsync(() -> {
124                        Schematic taskSchematic = this.schematicHandler.getSchematic(url);
125                        if (taskSchematic == null) {
126                            plot.removeRunning();
127                            player.sendMessage(
128                                    TranslatableCaption.of("schematics.schematic_invalid"),
129                                    TagResolver.resolver(
130                                            "reason",
131                                            Tag.inserting(Component.text("non-existent or not in gzip format"))
132                                    )
133                            );
134                            return;
135                        }
136                        PlotArea area = plot.getArea();
137                        this.schematicHandler.paste(
138                                taskSchematic,
139                                plot,
140                                0,
141                                area.getMinBuildHeight(),
142                                0,
143                                false,
144                                player,
145                                new RunnableVal<>() {
146                                    @Override
147                                    public void run(Boolean value) {
148                                        plot.removeRunning();
149                                        if (value) {
150                                            player.sendMessage(TranslatableCaption.of("schematics.schematic_paste_success"));
151                                        } else {
152                                            player.sendMessage(TranslatableCaption.of("schematics.schematic_paste_failed"));
153                                        }
154                                    }
155                                }
156                        );
157                    });
158                    return true;
159                }
160                plot.removeRunning();
161                player.sendMessage(
162                        TranslatableCaption.of("commandconfig.command_syntax"),
163                        TagResolver.resolver("value", Tag.inserting(Component.text("/plot load <index>")))
164                );
165                return false;
166            }
167
168            // list schematics
169
170            List<String> schematics = metaDataAccess.get().orElse(null);
171            if (schematics == null) {
172                plot.addRunning();
173                TaskManager.runTaskAsync(() -> {
174                    List<String> schematics1 = this.schematicHandler.getSaves(player.getUUID());
175                    plot.removeRunning();
176                    if ((schematics1 == null) || schematics1.isEmpty()) {
177                        player.sendMessage(TranslatableCaption.of("web.load_failed"));
178                        return;
179                    }
180                    metaDataAccess.set(schematics1);
181                    displaySaves(player);
182                });
183            } else {
184                displaySaves(player);
185            }
186        }
187        return true;
188    }
189
190    public void displaySaves(PlotPlayer<?> player) {
191        try (final MetaDataAccess<List<String>> metaDataAccess =
192                     player.accessTemporaryMetaData(PlayerMetaDataKeys.TEMPORARY_SCHEMATICS)) {
193            List<String> schematics = metaDataAccess.get().orElse(Collections.emptyList());
194            for (int i = 0; i < Math.min(schematics.size(), 32); i++) {
195                try {
196                    String schematic = schematics.get(i).split("\\.")[0];
197                    String[] split = schematic.split("_");
198                    if (split.length < 5) {
199                        continue;
200                    }
201                    String time = TimeUtil.secToTime((System.currentTimeMillis() / 1000) - Long.parseLong(split[0]));
202                    String world = split[1];
203                    PlotId id = PlotId.fromString(split[2] + ';' + split[3]);
204                    String size = split[4];
205                    String color = "<dark_aqua>";
206                    player.sendMessage(StaticCaption.of("<dark_gray>[</dark_gray><gray>" + (i + 1) + "</gray><dark_aqua>] </dark_aqua>" + color + time + "<dark_gray> | </dark_gray>" + color + world + ';' + id
207                            + "<dark_gray> | </dark_gray>" + color + size + 'x' + size));
208                } catch (Exception e) {
209                    e.printStackTrace();
210                }
211            }
212            player.sendMessage(
213                    TranslatableCaption.of("web.load_list"),
214                    TagResolver.resolver("command", Tag.inserting(Component.text("/plot load #")))
215            );
216        }
217    }
218
219}