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.primitives.Ints;
022import com.plotsquared.core.PlotSquared;
023import com.plotsquared.core.configuration.caption.TranslatableCaption;
024import com.plotsquared.core.database.DBFunc;
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.util.PlayerManager;
030import com.plotsquared.core.util.TabCompletions;
031import com.plotsquared.core.util.task.RunnableVal;
032import com.plotsquared.core.util.task.RunnableVal2;
033import com.plotsquared.core.util.task.RunnableVal3;
034import net.kyori.adventure.text.Component;
035import net.kyori.adventure.text.minimessage.tag.Tag;
036import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
037
038import java.util.Collection;
039import java.util.Collections;
040import java.util.LinkedList;
041import java.util.List;
042import java.util.Map;
043import java.util.UUID;
044import java.util.concurrent.CompletableFuture;
045import java.util.concurrent.TimeoutException;
046import java.util.stream.Collectors;
047
048@CommandDeclaration(command = "grant",
049        category = CommandCategory.CLAIMING,
050        usage = "/plot grant <check | add> [player]",
051        permission = "plots.grant",
052        requiredType = RequiredType.NONE)
053public class Grant extends Command {
054
055    public Grant() {
056        super(MainCommand.getInstance(), true);
057    }
058
059    @Override
060    public CompletableFuture<Boolean> execute(
061            final PlotPlayer<?> player, String[] args,
062            RunnableVal3<Command, Runnable, Runnable> confirm,
063            RunnableVal2<Command, CommandResult> whenDone
064    ) throws CommandException {
065        checkTrue(
066                args.length >= 1 && args.length <= 2,
067                TranslatableCaption.of("commandconfig.command_syntax"),
068                TagResolver.resolver("value", Tag.inserting(Component.text("/plot grant <check | add> [player]")))
069        );
070        final String arg0 = args[0].toLowerCase();
071        switch (arg0) {
072            case "add", "check" -> {
073                if (!player.hasPermission(Permission.PERMISSION_GRANT.format(arg0))) {
074                    player.sendMessage(
075                            TranslatableCaption.of("permission.no_permission"),
076                            TagResolver.resolver("node", Tag.inserting(Component.text(Permission.PERMISSION_GRANT.format(arg0))))
077                    );
078                    return CompletableFuture.completedFuture(false);
079                }
080                if (args.length != 2) {
081                    break;
082                }
083                PlayerManager.getUUIDsFromString(args[1], (uuids, throwable) -> {
084                    if (throwable instanceof TimeoutException) {
085                        player.sendMessage(TranslatableCaption.of("players.fetching_players_timeout"));
086                    } else if (throwable != null || uuids.size() != 1) {
087                        player.sendMessage(
088                                TranslatableCaption.of("errors.invalid_player"),
089                                TagResolver.resolver("value", Tag.inserting(Component.text(String.valueOf(uuids))))
090                        );
091                    } else {
092                        final UUID uuid = uuids.iterator().next();
093                        PlotPlayer<?> pp = PlotSquared.platform().playerManager().getPlayerIfExists(uuid);
094                        if (pp != null) {
095                            try (final MetaDataAccess<Integer> access = pp.accessPersistentMetaData(
096                                    PlayerMetaDataKeys.PERSISTENT_GRANTED_PLOTS)) {
097                                if (args[0].equalsIgnoreCase("check")) {
098                                    player.sendMessage(
099                                            TranslatableCaption.of("grants.granted_plots"),
100                                            TagResolver.resolver("amount", Tag.inserting(Component.text(access.get().orElse(0))))
101                                    );
102                                } else {
103                                    access.set(access.get().orElse(0) + 1);
104                                    player.sendMessage(
105                                            TranslatableCaption.of("grants.added"),
106                                            TagResolver.resolver("grants", Tag.inserting(Component.text(access.get().orElse(0))))
107                                    );
108                                }
109                            }
110                        } else {
111                            DBFunc.getPersistentMeta(uuid, new RunnableVal<>() {
112                                @Override
113                                public void run(Map<String, byte[]> value) {
114                                    final byte[] array = value.get("grantedPlots");
115                                    if (arg0.equals("check")) { // check
116                                        int granted;
117                                        if (array == null) {
118                                            granted = 0;
119                                        } else {
120                                            granted = Ints.fromByteArray(array);
121                                        }
122                                        player.sendMessage(
123                                                TranslatableCaption.of("grants.granted_plots"),
124                                                TagResolver.resolver("amount", Tag.inserting(Component.text(granted)))
125                                        );
126                                    } else { // add
127                                        int amount;
128                                        if (array == null) {
129                                            amount = 1;
130                                        } else {
131                                            amount = 1 + Ints.fromByteArray(array);
132                                        }
133                                        boolean replace = array != null;
134                                        String key = "grantedPlots";
135                                        byte[] rawData = Ints.toByteArray(amount);
136                                        DBFunc.addPersistentMeta(uuid, key, rawData, replace);
137                                        player.sendMessage(
138                                                TranslatableCaption.of("grants.added"),
139                                                TagResolver.resolver("grants", Tag.inserting(Component.text(amount)))
140                                        );
141                                    }
142                                }
143                            });
144                        }
145                    }
146                });
147                return CompletableFuture.completedFuture(true);
148            }
149        }
150        sendUsage(player);
151        return CompletableFuture.completedFuture(true);
152    }
153
154    @Override
155    public Collection<Command> tab(final PlotPlayer<?> player, final String[] args, final boolean space) {
156        if (args.length == 1) {
157            final List<String> completions = new LinkedList<>();
158            if (player.hasPermission(Permission.PERMISSION_GRANT_ADD)) {
159                completions.add("add");
160            }
161            if (player.hasPermission(Permission.PERMISSION_GRANT_CHECK)) {
162                completions.add("check");
163            }
164            final List<Command> commands = completions.stream().filter(completion -> completion
165                            .toLowerCase()
166                            .startsWith(args[0].toLowerCase()))
167                    .map(completion -> new Command(
168                            null,
169                            true,
170                            completion,
171                            "",
172                            RequiredType.NONE,
173                            CommandCategory.ADMINISTRATION
174                    ) {
175                    }).collect(Collectors.toCollection(LinkedList::new));
176            if (player.hasPermission(Permission.PERMISSION_GRANT_SINGLE) && args[0].length() > 0) {
177                commands.addAll(TabCompletions.completePlayers(player, args[0], Collections.emptyList()));
178            }
179            return commands;
180        } else if (args.length == 2) {
181            final String subcommand = args[0].toLowerCase();
182            if ((subcommand.equals("add") && player.hasPermission(Permission.PERMISSION_GRANT_ADD)) ||
183                (subcommand.equals("check") && player.hasPermission(Permission.PERMISSION_GRANT_CHECK))) {
184                return TabCompletions.completePlayers(player, args[1], Collections.emptyList());
185            }
186        }
187        return Collections.emptyList();
188    }
189
190}