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.PlotSquared;
023import com.plotsquared.core.configuration.Settings;
024import com.plotsquared.core.configuration.caption.TranslatableCaption;
025import com.plotsquared.core.events.PlotChangeOwnerEvent;
026import com.plotsquared.core.events.PlotUnlinkEvent;
027import com.plotsquared.core.events.Result;
028import com.plotsquared.core.permissions.Permission;
029import com.plotsquared.core.player.MetaDataAccess;
030import com.plotsquared.core.player.PlayerMetaDataKeys;
031import com.plotsquared.core.player.PlotPlayer;
032import com.plotsquared.core.plot.Plot;
033import com.plotsquared.core.util.EventDispatcher;
034import com.plotsquared.core.util.TabCompletions;
035import com.plotsquared.core.util.task.TaskManager;
036import net.kyori.adventure.text.Component;
037import net.kyori.adventure.text.minimessage.tag.Tag;
038import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
039import org.checkerframework.checker.nullness.qual.NonNull;
040import org.checkerframework.checker.nullness.qual.Nullable;
041
042import java.util.Collection;
043import java.util.Collections;
044import java.util.Set;
045import java.util.UUID;
046import java.util.function.Consumer;
047
048@CommandDeclaration(command = "setowner",
049        permission = "plots.admin.command.setowner",
050        usage = "/plot setowner <player>",
051        aliases = {"owner", "so", "seto"},
052        category = CommandCategory.CLAIMING,
053        requiredType = RequiredType.NONE,
054        confirmation = true)
055public class Owner extends SetCommand {
056
057    private final EventDispatcher eventDispatcher;
058
059    @Inject
060    public Owner(final @NonNull EventDispatcher eventDispatcher) {
061        this.eventDispatcher = eventDispatcher;
062    }
063
064    @Override
065    public boolean set(final PlotPlayer<?> player, final Plot plot, String value) {
066        if (value == null || value.isEmpty()) {
067            player.sendMessage(
068                    TranslatableCaption.of("commandconfig.command_syntax"),
069                    TagResolver.resolver("value", Tag.inserting(Component.text("/plot setowner <owner>")))
070            );
071            return false;
072        }
073        @Nullable final UUID oldOwner = plot.getOwnerAbs();
074        Set<Plot> plots = plot.getConnectedPlots();
075
076        final Consumer<UUID> uuidConsumer = uuid -> {
077            if (uuid == null && !value.equalsIgnoreCase("none") && !value.equalsIgnoreCase("null")
078                    && !value.equalsIgnoreCase("-")) {
079                player.sendMessage(
080                        TranslatableCaption.of("errors.invalid_player"),
081                        TagResolver.resolver("value", Tag.inserting(Component.text(value)))
082                );
083                return;
084            }
085            PlotChangeOwnerEvent event = this.eventDispatcher.callOwnerChange(
086                    player,
087                    plot,
088                    plot.hasOwner() ? plot.getOwnerAbs() : null,
089                    uuid,
090                    plot.hasOwner()
091            );
092            if (event.getEventResult() == Result.DENY) {
093                player.sendMessage(
094                        TranslatableCaption.of("events.event_denied"),
095                        TagResolver.resolver("value", Tag.inserting(Component.text("Owner change")))
096                );
097                return;
098            }
099            uuid = event.getNewOwner();
100            boolean force = event.getEventResult() == Result.FORCE;
101            if (uuid == null) {
102                if (!force && !player.hasPermission(
103                        Permission.PERMISSION_ADMIN_COMMAND_SET_OWNER,
104                        true
105                )) {
106                    return;
107                }
108                PlotUnlinkEvent unlinkEvent = this.eventDispatcher.callUnlink(
109                        plot.getArea(),
110                        plot,
111                        false,
112                        false,
113                        PlotUnlinkEvent.REASON.NEW_OWNER
114                );
115                if (unlinkEvent.getEventResult() == Result.DENY) {
116                    player.sendMessage(
117                            TranslatableCaption.of("events.event_denied"),
118                            TagResolver.resolver("value", Tag.inserting(Component.text("Unlink on owner change")))
119                    );
120                    return;
121                }
122                if (plot.getPlotModificationManager().unlinkPlot(unlinkEvent.isCreateRoad(), unlinkEvent.isCreateRoad())) {
123                    eventDispatcher.callPostUnlink(plot, PlotUnlinkEvent.REASON.NEW_OWNER);
124                }
125                Set<Plot> connected = plot.getConnectedPlots();
126                for (Plot current : connected) {
127                    current.unclaim();
128                    current.getPlotModificationManager().removeSign();
129                }
130                eventDispatcher.callPostOwnerChange(player, plot, oldOwner);
131                player.sendMessage(TranslatableCaption.of("owner.set_owner"));
132                return;
133            }
134            final PlotPlayer<?> other = PlotSquared.platform().playerManager().getPlayerIfExists(uuid);
135            if (plot.isOwner(uuid)) {
136                player.sendMessage(
137                        TranslatableCaption.of("member.already_owner"),
138                        PlotSquared.platform().playerManager().getUsernameCaption(uuid)
139                                .thenApply(caption -> TagResolver.resolver(
140                                "player",
141                                Tag.inserting(caption.toComponent(player))
142                        ))
143                );
144                return;
145            }
146            if (!force && !player.hasPermission(Permission.PERMISSION_ADMIN_COMMAND_SET_OWNER)) {
147                if (other == null) {
148                    player.sendMessage(
149                            TranslatableCaption.of("errors.invalid_player_offline"),
150                            PlotSquared.platform().playerManager().getUsernameCaption(uuid)
151                                    .thenApply(caption -> TagResolver.resolver(
152                                    "player",
153                                    Tag.inserting(caption.toComponent(player))
154                            ))
155                    );
156                    return;
157                }
158                int size = plots.size();
159                int currentPlots = (Settings.Limit.GLOBAL ?
160                        other.getPlotCount() :
161                        other.getPlotCount(plot.getWorldName())) + size;
162                try (final MetaDataAccess<Integer> metaDataAccess = player.accessPersistentMetaData(PlayerMetaDataKeys.PERSISTENT_GRANTED_PLOTS)) {
163                    int grants;
164                    if (currentPlots >= other.getAllowedPlots()) {
165                        if (metaDataAccess.isPresent()) {
166                            grants = metaDataAccess.get().orElse(0);
167                            if (grants <= 0) {
168                                metaDataAccess.remove();
169                                player.sendMessage(TranslatableCaption.of("permission.cant_transfer_more_plots"));
170                                return;
171                            }
172                        }
173                    }
174                }
175            }
176            final UUID finalUUID = uuid;
177            PlotSquared.get().getImpromptuUUIDPipeline().getSingle(uuid, (finalName, throwable) -> {
178                final boolean removeDenied = plot.isDenied(finalUUID);
179                Runnable run = () -> {
180                    if (plot.setOwner(finalUUID, player)) {
181                        if (removeDenied) {
182                            plot.removeDenied(finalUUID);
183                        }
184                        plot.getPlotModificationManager().setSign(finalName);
185                        player.sendMessage(TranslatableCaption.of("owner.set_owner"));
186                        eventDispatcher.callPostOwnerChange(player, plot, oldOwner);
187                        if (other != null) {
188                            other.sendMessage(
189                                    TranslatableCaption.of("owner.now_owner"),
190                                    TagResolver.resolver(
191                                            "plot",
192                                            Tag.inserting(Component.text(plot.getArea() + ";" + plot.getId()))
193                                    )
194                            );
195                        }
196                    } else {
197                        player.sendMessage(TranslatableCaption.of("owner.set_owner_cancelled"));
198                    }
199                };
200                if (hasConfirmation(player)) {
201                    CmdConfirm.addPending(player, "/plot setowner " + value, run);
202                } else {
203                    TaskManager.runTask(run);
204                }
205            });
206        };
207
208        if (value.length() == 36) {
209            try {
210                uuidConsumer.accept(UUID.fromString(value));
211            } catch (Exception ignored) {
212            }
213        } else {
214            PlotSquared.get().getImpromptuUUIDPipeline().getSingle(value, (uuid, throwable) -> uuidConsumer.accept(uuid));
215        }
216        return true;
217    }
218
219    @Override
220    public Collection<Command> tab(final PlotPlayer<?> player, final String[] args, final boolean space) {
221        return TabCompletions.completePlayers(player, String.join(",", args).trim(), Collections.emptyList());
222    }
223
224}