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.Injector;
022import com.plotsquared.core.PlotSquared;
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.MetaDataAccess;
029import com.plotsquared.core.player.PlayerMetaDataKeys;
030import com.plotsquared.core.player.PlotPlayer;
031import com.plotsquared.core.plot.Plot;
032import com.plotsquared.core.plot.PlotArea;
033import com.plotsquared.core.plot.world.SinglePlotArea;
034import com.plotsquared.core.util.EconHandler;
035import com.plotsquared.core.util.PlotExpression;
036import com.plotsquared.core.util.task.RunnableVal2;
037import com.plotsquared.core.util.task.RunnableVal3;
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.apache.logging.log4j.LogManager;
042import org.apache.logging.log4j.Logger;
043
044import java.util.Arrays;
045import java.util.LinkedList;
046import java.util.List;
047import java.util.concurrent.CompletableFuture;
048
049/**
050 * PlotSquared command class.
051 */
052@CommandDeclaration(command = "plot",
053        aliases = {"plots", "p", "plotsquared", "plot2", "p2", "ps", "2", "plotme", "plotz", "ap"})
054public class MainCommand extends Command {
055
056    private static final Logger LOGGER = LogManager.getLogger("PlotSquared/" + MainCommand.class.getSimpleName());
057
058    private static MainCommand instance;
059    public Help help;
060    public Toggle toggle;
061
062    private MainCommand() {
063        super(null, true);
064        instance = this;
065    }
066
067    public static MainCommand getInstance() {
068        if (instance == null) {
069            instance = new MainCommand();
070
071            final Injector injector = PlotSquared.platform().injector();
072            final List<Class<? extends Command>> commands = new LinkedList<>();
073            commands.add(Caps.class);
074            commands.add(Buy.class);
075            if (Settings.Web.LEGACY_WEBINTERFACE) {
076                LOGGER.warn("Legacy webinterface is used. Please note that it will be removed in future.");
077            }
078            commands.add(Load.class);
079            commands.add(Confirm.class);
080            commands.add(Template.class);
081            commands.add(Download.class);
082            commands.add(Setup.class);
083            commands.add(Area.class);
084            commands.add(DebugSaveTest.class);
085            commands.add(DebugLoadTest.class);
086            commands.add(CreateRoadSchematic.class);
087            commands.add(DebugAllowUnsafe.class);
088            commands.add(RegenAllRoads.class);
089            commands.add(Claim.class);
090            commands.add(Auto.class);
091            commands.add(HomeCommand.class);
092            commands.add(Visit.class);
093            commands.add(Set.class);
094            commands.add(Clear.class);
095            commands.add(Delete.class);
096            commands.add(Trust.class);
097            commands.add(Add.class);
098            commands.add(Leave.class);
099            commands.add(Deny.class);
100            commands.add(Remove.class);
101            commands.add(Info.class);
102            commands.add(Near.class);
103            commands.add(ListCmd.class);
104            commands.add(Debug.class);
105            commands.add(SchematicCmd.class);
106            commands.add(PluginCmd.class);
107            commands.add(Purge.class);
108            commands.add(Reload.class);
109            commands.add(Merge.class);
110            commands.add(DebugPaste.class);
111            commands.add(Unlink.class);
112            commands.add(Kick.class);
113            commands.add(Inbox.class);
114            commands.add(Comment.class);
115            commands.add(DatabaseCommand.class);
116            commands.add(Swap.class);
117            commands.add(Music.class);
118            commands.add(DebugRoadRegen.class);
119            commands.add(DebugExec.class);
120            commands.add(FlagCommand.class);
121            commands.add(Target.class);
122            commands.add(Move.class);
123            commands.add(Condense.class);
124            commands.add(Copy.class);
125            commands.add(Trim.class);
126            commands.add(Done.class);
127            commands.add(Continue.class);
128            commands.add(Middle.class);
129            commands.add(Grant.class);
130            commands.add(Owner.class);
131            commands.add(Desc.class);
132            commands.add(Biome.class);
133            commands.add(Alias.class);
134            commands.add(SetHome.class);
135            commands.add(Cluster.class);
136            commands.add(DebugImportWorlds.class);
137            commands.add(Backup.class);
138
139            if (Settings.Ratings.USE_LIKES) {
140                commands.add(Like.class);
141                commands.add(Dislike.class);
142            } else {
143                commands.add(Rate.class);
144            }
145
146            for (final Class<? extends Command> command : commands) {
147                try {
148                    injector.getInstance(command);
149                } catch (final Exception e) {
150                    LOGGER.error("Failed to register command {}", command.getCanonicalName());
151                    e.printStackTrace();
152                }
153            }
154
155            // Referenced commands
156            instance.toggle = injector.getInstance(Toggle.class);
157            instance.help = new Help(instance);
158        }
159        return instance;
160    }
161
162    public static boolean onCommand(final PlotPlayer<?> player, String... args) {
163        final EconHandler econHandler = PlotSquared.platform().econHandler();
164        if (args.length >= 1 && args[0].contains(":")) {
165            String[] split2 = args[0].split(":");
166            if (split2.length == 2) {
167                // Ref: c:v, this will push value to the last spot in the array
168                // ex. /p h:2 SomeUsername
169                // > /p h SomeUsername 2
170                String[] tmp = new String[args.length + 1];
171                tmp[0] = split2[0];
172                tmp[args.length] = split2[1];
173                if (args.length >= 2) {
174                    System.arraycopy(args, 1, tmp, 1, args.length - 1);
175                }
176                args = tmp;
177            }
178        }
179        try {
180            getInstance().execute(player, args, new RunnableVal3<>() {
181                @Override
182                public void run(final Command cmd, final Runnable success, final Runnable failure) {
183                    if (cmd.hasConfirmation(player)) {
184                        CmdConfirm.addPending(player, cmd.getUsage(), () -> {
185                            PlotArea area = player.getApplicablePlotArea();
186                            if (area != null && econHandler.isEnabled(area) && !player.hasPermission(Permission.PERMISSION_ADMIN_BYPASS_ECON)) {
187                                PlotExpression priceEval =
188                                        area.getPrices().get(cmd.getFullId());
189                                double price = priceEval != null ? priceEval.evaluate(0d) : 0d;
190                                if (econHandler.getMoney(player) < price) {
191                                    if (failure != null) {
192                                        failure.run();
193                                    }
194                                    return;
195                                }
196                            }
197                            if (success != null) {
198                                success.run();
199                            }
200                        });
201                        return;
202                    }
203                    PlotArea area = player.getApplicablePlotArea();
204                    if (area != null && econHandler.isEnabled(area) && !player.hasPermission(Permission.PERMISSION_ADMIN_BYPASS_ECON)) {
205                        PlotExpression priceEval = area.getPrices().get(cmd.getFullId());
206                        double price = priceEval != null ? priceEval.evaluate(0d) : 0d;
207                        if (price != 0d && econHandler.getMoney(player) < price) {
208                            if (failure != null) {
209                                failure.run();
210                            }
211                            return;
212                        }
213                    }
214                    if (success != null) {
215                        success.run();
216                    }
217                }
218            }, new RunnableVal2<>() {
219                @Override
220                public void run(Command cmd, CommandResult result) {
221                    // Post command stuff!?
222                }
223            }).thenAccept(result -> {
224                // TODO: Something with the command result
225            });
226        } catch (CommandException e) {
227            e.perform(player);
228        }
229        // Always true
230        return true;
231    }
232
233    @Override
234    public CompletableFuture<Boolean> execute(
235            final PlotPlayer<?> player, String[] args,
236            RunnableVal3<Command, Runnable, Runnable> confirm,
237            RunnableVal2<Command, CommandResult> whenDone
238    ) {
239        // Optional command scope //
240        Location location = null;
241        Plot plot = null;
242        boolean tp = false;
243        if (args.length >= 2) {
244            PlotArea area = player.getApplicablePlotArea();
245            Plot newPlot = Plot.fromString(area, args[0]);
246            if (newPlot != null && (player instanceof ConsolePlayer || newPlot.getArea()
247                    .equals(area) || player.hasPermission(Permission.PERMISSION_ADMIN)
248                    || player.hasPermission(Permission.PERMISSION_ADMIN_AREA_SUDO))
249                    && !newPlot.isDenied(player.getUUID())) {
250                final Location newLoc;
251                if (newPlot.getArea() instanceof SinglePlotArea) {
252                    newLoc = newPlot.isLoaded() ? newPlot.getCenterSynchronous() : Location.at("", 0, 0, 0);
253                } else {
254                    newLoc = newPlot.getCenterSynchronous();
255                }
256                if (player.canTeleport(newLoc)) {
257                    // Save meta
258                    try (final MetaDataAccess<Location> locationMetaDataAccess
259                                 = player.accessTemporaryMetaData(PlayerMetaDataKeys.TEMPORARY_LOCATION)) {
260                        location = locationMetaDataAccess.get().orElse(null);
261                        locationMetaDataAccess.set(newLoc);
262                    }
263                    try (final MetaDataAccess<Plot> plotMetaDataAccess
264                                 = player.accessTemporaryMetaData(PlayerMetaDataKeys.TEMPORARY_LAST_PLOT)) {
265                        plot = plotMetaDataAccess.get().orElse(null);
266                        plotMetaDataAccess.set(newPlot);
267                    }
268                    tp = true;
269                } else {
270                    player.sendMessage(TranslatableCaption.of("border.denied"));
271                    return CompletableFuture.completedFuture(false);
272                }
273                // Trim command
274                args = Arrays.copyOfRange(args, 1, args.length);
275            }
276            if (args.length >= 2 && !args[0].isEmpty() && args[0].charAt(0) == '-') {
277                if ("f".equals(args[0].substring(1))) {
278                    confirm = new RunnableVal3<>() {
279                        @Override
280                        public void run(Command cmd, Runnable success, Runnable failure) {
281                            if (area != null && PlotSquared.platform().econHandler().isEnabled(area)) {
282                                PlotExpression priceEval =
283                                        area.getPrices().get(cmd.getFullId());
284                                double price = priceEval != null ? priceEval.evaluate(0d) : 0d;
285                                if (price != 0d
286                                        && PlotSquared.platform().econHandler().getMoney(player) < price) {
287                                    if (failure != null) {
288                                        failure.run();
289                                    }
290                                    return;
291                                }
292                            }
293                            if (success != null) {
294                                success.run();
295                            }
296                        }
297                    };
298                    args = Arrays.copyOfRange(args, 1, args.length);
299                } else {
300                    player.sendMessage(TranslatableCaption.of("errors.invalid_command_flag"));
301                    return CompletableFuture.completedFuture(false);
302                }
303            }
304        }
305        try {
306            super.execute(player, args, confirm, whenDone);
307        } catch (CommandException e) {
308            throw e;
309        } catch (Throwable e) {
310            e.printStackTrace();
311            String message = e.getMessage();
312            if (message != null) {
313                player.sendMessage(
314                        TranslatableCaption.of("errors.error"),
315                        TagResolver.resolver("value", Tag.inserting(Component.text(message)))
316                );
317            } else {
318                player.sendMessage(
319                        TranslatableCaption.of("errors.error_console"));
320            }
321        }
322        // Reset command scope //
323        if (tp && !(player instanceof ConsolePlayer)) {
324            try (final MetaDataAccess<Location> locationMetaDataAccess
325                         = player.accessTemporaryMetaData(PlayerMetaDataKeys.TEMPORARY_LOCATION)) {
326                if (location == null) {
327                    locationMetaDataAccess.remove();
328                } else {
329                    locationMetaDataAccess.set(location);
330                }
331            }
332            try (final MetaDataAccess<Plot> plotMetaDataAccess
333                         = player.accessTemporaryMetaData(PlayerMetaDataKeys.TEMPORARY_LAST_PLOT)) {
334                if (plot == null) {
335                    plotMetaDataAccess.remove();
336                } else {
337                    plotMetaDataAccess.set(plot);
338                }
339            }
340        }
341        return CompletableFuture.completedFuture(true);
342    }
343
344    @Override
345    public boolean canExecute(PlotPlayer<?> player, boolean message) {
346        return true;
347    }
348
349}