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.TranslatableCaption;
024import com.plotsquared.core.database.DBFunc;
025import com.plotsquared.core.events.PlayerClaimPlotEvent;
026import com.plotsquared.core.events.PlotMergeEvent;
027import com.plotsquared.core.events.Result;
028import com.plotsquared.core.location.Direction;
029import com.plotsquared.core.location.Location;
030import com.plotsquared.core.permissions.Permission;
031import com.plotsquared.core.player.MetaDataAccess;
032import com.plotsquared.core.player.PlayerMetaDataKeys;
033import com.plotsquared.core.player.PlotPlayer;
034import com.plotsquared.core.plot.Plot;
035import com.plotsquared.core.plot.PlotArea;
036import com.plotsquared.core.util.EconHandler;
037import com.plotsquared.core.util.EventDispatcher;
038import com.plotsquared.core.util.PlotExpression;
039import com.plotsquared.core.util.task.TaskManager;
040import net.kyori.adventure.text.minimessage.Template;
041import org.apache.logging.log4j.LogManager;
042import org.apache.logging.log4j.Logger;
043import org.checkerframework.checker.nullness.qual.NonNull;
044
045@CommandDeclaration(
046        command = "claim",
047        aliases = "c",
048        category = CommandCategory.CLAIMING,
049        requiredType = RequiredType.PLAYER, permission = "plots.claim",
050        usage = "/plot claim")
051public class Claim extends SubCommand {
052
053    private static final Logger LOGGER = LogManager.getLogger("PlotSquared/" + Claim.class.getSimpleName());
054
055    private final EventDispatcher eventDispatcher;
056    private final EconHandler econHandler;
057
058    @Inject
059    public Claim(
060            final @NonNull EventDispatcher eventDispatcher,
061            final @NonNull EconHandler econHandler
062    ) {
063        this.eventDispatcher = eventDispatcher;
064        this.econHandler = econHandler;
065    }
066
067    @Override
068    public boolean onCommand(final PlotPlayer<?> player, String[] args) {
069        String schematic = null;
070        if (args.length >= 1) {
071            schematic = args[0];
072        }
073        Location location = player.getLocation();
074        Plot plot = location.getPlotAbs();
075        if (plot == null) {
076            player.sendMessage(TranslatableCaption.of("errors.not_in_plot"));
077            return false;
078        }
079        final PlayerClaimPlotEvent event = this.eventDispatcher.callClaim(player, plot, schematic);
080        schematic = event.getSchematic();
081        if (event.getEventResult() == Result.DENY) {
082            player.sendMessage(
083                    TranslatableCaption.of("events.event_denied"),
084                    Template.of("value", "Claim")
085            );
086            return true;
087        }
088        boolean force = event.getEventResult() == Result.FORCE;
089        int currentPlots = Settings.Limit.GLOBAL ?
090                player.getPlotCount() :
091                player.getPlotCount(location.getWorldName());
092
093        final PlotArea area = plot.getArea();
094
095        try (final MetaDataAccess<Integer> metaDataAccess = player.accessPersistentMetaData(PlayerMetaDataKeys.PERSISTENT_GRANTED_PLOTS)) {
096            int grants = 0;
097            if (currentPlots >= player.getAllowedPlots() && !force) {
098                if (metaDataAccess.isPresent()) {
099                    grants = metaDataAccess.get().orElse(0);
100                    if (grants <= 0) {
101                        player.sendMessage(
102                                TranslatableCaption.of("permission.cant_claim_more_plots"),
103                                Template.of("amount", String.valueOf(grants))
104                        );
105                        metaDataAccess.remove();
106                    }
107                } else {
108                    player.sendMessage(
109                            TranslatableCaption.of("permission.cant_claim_more_plots"),
110                            Template.of("amount", String.valueOf(player.getAllowedPlots()))
111                    );
112                    return false;
113                }
114            }
115
116            if (!plot.canClaim(player)) {
117                player.sendMessage(TranslatableCaption.of("working.plot_is_claimed"));
118                return false;
119            }
120            if (schematic != null && !schematic.isEmpty()) {
121                if (area.isSchematicClaimSpecify()) {
122                    if (!area.hasSchematic(schematic)) {
123                        player.sendMessage(
124                                TranslatableCaption.of("schematics.schematic_invalid_named"),
125                                Template.of("schemname", schematic),
126                                Template.of("reason", "non-existent")
127                        );
128                    }
129                    if (!player.hasPermission(Permission.PERMISSION_CLAIM_SCHEMATIC
130                            .format(schematic)) && !player.hasPermission(
131                            "plots.admin.command.schematic"
132                    ) && !force) {
133                        player.sendMessage(
134                                TranslatableCaption.of("permission.no_schematic_permission"),
135                                Template.of("value", schematic)
136                        );
137                    }
138                }
139            }
140            if (this.econHandler.isEnabled(area) && !force) {
141                PlotExpression costExr = area.getPrices().get("claim");
142                double cost = costExr.evaluate(currentPlots);
143                if (cost > 0d) {
144                    if (!this.econHandler.isSupported()) {
145                        player.sendMessage(TranslatableCaption.of("economy.vault_or_consumer_null"));
146                        return false;
147                    }
148                    if (this.econHandler.getMoney(player) < cost) {
149                        player.sendMessage(
150                                TranslatableCaption.of("economy.cannot_afford_plot"),
151                                Template.of("money", this.econHandler.format(cost)),
152                                Template.of("balance", this.econHandler.format(this.econHandler.getMoney(player)))
153                        );
154                        return false;
155                    }
156                    this.econHandler.withdrawMoney(player, cost);
157                    player.sendMessage(
158                            TranslatableCaption.of("economy.removed_balance"),
159                            Template.of("money", this.econHandler.format(cost)),
160                            Template.of("balance", this.econHandler.format(this.econHandler.getMoney(player)))
161                    );
162                }
163            }
164            if (grants > 0) {
165                if (grants == 1) {
166                    metaDataAccess.remove();
167                } else {
168                    metaDataAccess.set(grants - 1);
169                }
170                player.sendMessage(
171                        TranslatableCaption.of("economy.removed_granted_plot"),
172                        Template.of("usedGrants", String.valueOf((grants - 1))),
173                        Template.of("remainingGrants", String.valueOf(grants))
174                );
175            }
176        }
177        if (!player.hasPermission(Permission.PERMISSION_ADMIN_BYPASS_BORDER)) {
178            int border = area.getBorder();
179            if (border != Integer.MAX_VALUE && plot.getDistanceFromOrigin() > border && !force) {
180                player.sendMessage(TranslatableCaption.of("border.denied"));
181                return false;
182            }
183        }
184        plot.setOwnerAbs(player.getUUID());
185        final String finalSchematic = schematic;
186        DBFunc.createPlotSafe(plot, () -> {
187            try {
188                TaskManager.getPlatformImplementation().sync(() -> {
189                    if (!plot.claim(player, true, finalSchematic, false, false)) {
190                        LOGGER.info("Failed to claim plot {}", plot.getId().toCommaSeparatedString());
191                        player.sendMessage(TranslatableCaption.of("working.plot_not_claimed"));
192                        plot.setOwnerAbs(null);
193                    } else if (area.isAutoMerge()) {
194                        PlotMergeEvent mergeEvent = Claim.this.eventDispatcher
195                                .callMerge(plot, Direction.ALL, Integer.MAX_VALUE, player);
196                        if (mergeEvent.getEventResult() == Result.DENY) {
197                            player.sendMessage(
198                                    TranslatableCaption.of("events.event_denied"),
199                                    Template.of("value", "Auto merge on claim")
200                            );
201                        } else {
202                            if (plot.getPlotModificationManager().autoMerge(
203                                    mergeEvent.getDir(),
204                                    mergeEvent.getMax(),
205                                    player.getUUID(),
206                                    player,
207                                    true
208                            )) {
209                                eventDispatcher.callPostMerge(player, plot);
210                            }
211                        }
212                    }
213                    return null;
214                });
215            } catch (final Exception e) {
216                e.printStackTrace();
217            }
218        }, () -> {
219            LOGGER.info("Failed to add plot to database: {}", plot.getId().toCommaSeparatedString());
220            player.sendMessage(TranslatableCaption.of("working.plot_not_claimed"));
221            plot.setOwnerAbs(null);
222        });
223        return true;
224    }
225
226}