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