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                                }
105                            }
106                        } else {
107                            DBFunc.getPersistentMeta(uuid, new RunnableVal<>() {
108                                @Override
109                                public void run(Map<String, byte[]> value) {
110                                    final byte[] array = value.get("grantedPlots");
111                                    if (arg0.equals("check")) { // check
112                                        int granted;
113                                        if (array == null) {
114                                            granted = 0;
115                                        } else {
116                                            granted = Ints.fromByteArray(array);
117                                        }
118                                        player.sendMessage(
119                                                TranslatableCaption.of("grants.granted_plots"),
120                                                TagResolver.resolver("amount", Tag.inserting(Component.text(granted)))
121                                        );
122                                    } else { // add
123                                        int amount;
124                                        if (array == null) {
125                                            amount = 1;
126                                        } else {
127                                            amount = 1 + Ints.fromByteArray(array);
128                                        }
129                                        boolean replace = array != null;
130                                        String key = "grantedPlots";
131                                        byte[] rawData = Ints.toByteArray(amount);
132                                        DBFunc.addPersistentMeta(uuid, key, rawData, replace);
133                                        player.sendMessage(
134                                                TranslatableCaption.of("grants.added"),
135                                                TagResolver.resolver("grants", Tag.inserting(Component.text(amount)))
136                                        );
137                                    }
138                                }
139                            });
140                        }
141                    }
142                });
143                return CompletableFuture.completedFuture(true);
144            }
145        }
146        sendUsage(player);
147        return CompletableFuture.completedFuture(true);
148    }
149
150    @Override
151    public Collection<Command> tab(final PlotPlayer<?> player, final String[] args, final boolean space) {
152        if (args.length == 1) {
153            final List<String> completions = new LinkedList<>();
154            if (player.hasPermission(Permission.PERMISSION_GRANT_ADD)) {
155                completions.add("add");
156            }
157            if (player.hasPermission(Permission.PERMISSION_GRANT_CHECK)) {
158                completions.add("check");
159            }
160            final List<Command> commands = completions.stream().filter(completion -> completion
161                            .toLowerCase()
162                            .startsWith(args[0].toLowerCase()))
163                    .map(completion -> new Command(
164                            null,
165                            true,
166                            completion,
167                            "",
168                            RequiredType.NONE,
169                            CommandCategory.ADMINISTRATION
170                    ) {
171                    }).collect(Collectors.toCollection(LinkedList::new));
172            if (player.hasPermission(Permission.PERMISSION_GRANT_SINGLE) && args[0].length() > 0) {
173                commands.addAll(TabCompletions.completePlayers(player, args[0], Collections.emptyList()));
174            }
175            return commands;
176        }
177        return TabCompletions.completePlayers(player, String.join(",", args).trim(), Collections.emptyList());
178    }
179
180}