Package com.sk89q.commandbook

Source Code of com.sk89q.commandbook.CooldownsComponent$Commands

/*
* CommandBook
* Copyright (C) 2012 sk89q <http://www.sk89q.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

package com.sk89q.commandbook;

import com.sk89q.commandbook.commands.PaginatedResult;
import com.sk89q.commandbook.session.PersistentSession;
import com.sk89q.commandbook.session.SessionComponent;
import com.sk89q.commandbook.util.InputUtil;
import com.sk89q.minecraft.util.commands.Command;
import com.sk89q.minecraft.util.commands.CommandContext;
import com.sk89q.minecraft.util.commands.CommandException;
import com.sk89q.minecraft.util.commands.NestedCommand;
import com.zachsthings.libcomponents.ComponentInformation;
import com.zachsthings.libcomponents.Depend;
import com.zachsthings.libcomponents.InjectComponent;
import com.zachsthings.libcomponents.bukkit.BukkitComponent;
import com.zachsthings.libcomponents.config.ConfigurationBase;
import com.zachsthings.libcomponents.config.Setting;
import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerCommandPreprocessEvent;

import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.*;

import static com.sk89q.commandbook.util.NestUtil.getNestedMap;

/**
* This component provides command warmups and cooldowns, measured in seconds, by using
* a repeating scheduler task that increases the value for each entry in each CooldownState
* by one each second if the value is less than the number of seconds specified in the
* configuration, removing the entry if the warmup/cooldown has been removed from the configuration
*/
@Depend(components = SessionComponent.class)
@ComponentInformation(friendlyName = "Warmups and Cooldowns", desc = "Allows warmups and cooldowns for commands, specified in seconds.")
public class CooldownsComponent extends BukkitComponent implements Listener, Runnable {
    @InjectComponent private SessionComponent sessions;
    private LocalConfiguration config;
    private static ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(5);

    @Override
    public void enable() {
        config = configure(new LocalConfiguration());
        CommandBook.registerEvents(this);
        registerCommands(Commands.class);
        scheduler.scheduleAtFixedRate(this, 1, 1, TimeUnit.SECONDS);
    }

    @Override
    public void reload() {
        super.reload();
        configure(config);
    }

    @Override
    public void disable() {
        scheduler.shutdown();
    }

    private static String firstWord(final String str) {
        int spaceIndex = str.indexOf(" ");
        if (spaceIndex == -1) {
            return str;
        }
        return str.substring(0, spaceIndex);
    }

    private static String titleCase(String input) {
        StringBuilder ret = new StringBuilder();
        for (String word : input.split(" ")) {
            if (ret.length() > 0) {
                ret.append(" ");
            }
            if (ret.length() == 0 || word.length() > 2) {
                ret.append(Character.toUpperCase(word.charAt(0))).append(word.substring(1));
            } else {
                ret.append(word);
            }
        }
        return ret.toString();
    }

    private static final SimpleDateFormat timeFormat = new SimpleDateFormat("mm:ss");
    private static String formatTime(int timeInSeconds, TimeUnit unit) {
        synchronized (timeFormat) {
            return timeFormat.format(new Date(unit.toMillis(timeInSeconds)));
        }
    }

    public void run() {
        for (final CooldownState state : sessions.getSessions(CooldownState.class).values()) {
            final HashSet<String> visitedCooldowns = new HashSet<String>();
            for (Iterator<Map.Entry<String, Integer>> i = state.cooldownCommands.entrySet().iterator(); i.hasNext();) {
                final Map.Entry<String, Integer> entry = i.next();
                final Integer cooldownTime = getNestedMap(config.registeredActions, entry.getKey()).get("cooldown");
                if (cooldownTime == null) {
                    i.remove(); // The cooldown has been removed, so we can get rid of it.
                    continue;
                }
                if (entry.getValue() <= cooldownTime) { // Increment the time if it isn't already at the required time
                    entry.setValue(entry.getValue() + 1);
                }
                visitedCooldowns.add(entry.getKey());
            }

            for (Iterator<Map.Entry<String, WarmupInfo>> i = state.warmupCommands.entrySet().iterator(); i.hasNext();) {
                final Map.Entry<String, WarmupInfo> entry = i.next();
                final Integer warmupTime = getNestedMap(config.registeredActions, entry.getKey()).get("warmup");
                if (warmupTime == null) {
                    i.remove(); // The warmup has been removed, so we can get rid of it.
                    continue;
                } else if (visitedCooldowns.contains(entry.getKey())) {
                    continue;
                }

                if (entry.getValue().remainingTime < warmupTime) {
                    entry.getValue().remainingTime++;
                } else if (entry.getValue().remainingTime == warmupTime) {
                    // Reached the needed time, run a scheduler task to execute the command
                    // back on the main thread.
                    final CommandSender owner = state.getOwner();
                    if (owner != null) {
                        CommandBook.server().getScheduler().callSyncMethod(CommandBook.inst(), new Callable<Boolean>() {
                            @Override
                            public Boolean call() {
                                return CommandBook.server().dispatchCommand(owner, entry.getValue().fullCommand);
                            }
                        });
                    }
                    i.remove();
                }
            }
        }
    }

    private static class LocalConfiguration extends ConfigurationBase {
        @Setting("commands"/*, help = "A mapping of commands to their cooldown values." +
                "For each command there are both warmup and cooldown values, with numbers " +
                "measured in seconds"*/)
        public Map<String, Map<String, Integer>> registeredActions = createDefaultStructure();

        public Map<String, Map<String, Integer>> createDefaultStructure() {
            Map<String, Map<String, Integer>> result = new HashMap<String, Map<String, Integer>>();
            getNestedMap(result, "command-with-warmup").put("warmup", 50);
            getNestedMap(result, "command-with-cooldown").put("cooldown", 50);
            getNestedMap(result, "command-with-warmup-and-cooldown").put("cooldown", 50);
            getNestedMap(result, "command-with-warmup-and-cooldown").put("warmup", 50);
            return result;
        }
    }

    @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
    public void playerHandler(PlayerCommandPreprocessEvent event) {
        if (!checkCooldown(event.getPlayer(), event.getMessage().substring(1))
                || !checkWarmup(event.getPlayer(), event.getMessage().substring(1))) {
            event.setCancelled(true);
        }
    }

    /*@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true) // TODO: Make ServerCommandEvent Cancellable
    public void serverCommandHandler(ServerCommandEvent event) {
        if (!checkCooldown(event.getSender(), event.getCommand())
                || !checkWarmup(event.getSender(), event.getCommand())) {
            event.setCancelled(true);
        }
    }*/

    public boolean checkCooldown(CommandSender sender, String command) {
        CooldownState state = sessions.getSession(CooldownState.class, sender);
        command = firstWord(command);
        synchronized (state.cooldownCommands) {
            Map<String, Integer> storedTimes = config.registeredActions.get(command);
            if (storedTimes == null) { // Nothing in the config for this command
                return true;
            }

            Integer requiredCooldownTime = storedTimes.get("cooldown");
            if (requiredCooldownTime == null) { // No cooldown for this command
                return true;
            }

            Integer passedCooldownTime = state.cooldownCommands.get(command);
            if (passedCooldownTime == null) { // There isn't an in-progress cooldown for this command
                passedCooldownTime = 0;
                state.cooldownCommands.put(command, passedCooldownTime);
            }

            if (passedCooldownTime >= requiredCooldownTime
                    || CommandBook.inst().hasPermission(sender, "commandbook.cooldown.override." + command)) {
                state.cooldownCommands.remove(command);
                return true;
            } else {
                sender.sendMessage(ChatColor.YELLOW + "The command '" + command +
                        "' has a remaining cooldown of " +
                        formatTime(requiredCooldownTime - passedCooldownTime, TimeUnit.SECONDS)
                        + " seconds.");
                return false;
            }
        }
    }

    public boolean checkWarmup(CommandSender sender, String command) {
        CooldownState state = sessions.getSession(CooldownState.class, sender);
        synchronized (state.warmupCommands) {
            Map<String, Integer> storedTimes = config.registeredActions.get(firstWord(command));
            if (storedTimes == null) { // Nothing in the config for this command
                return true;
            }

            Integer requiredWarmupTime = storedTimes.get("warmup");
            if (requiredWarmupTime == null || CommandBook.inst().hasPermission(sender,
                    "commandbook.warmup.override." + firstWord(command))) { // No warmup for this command
                return true;
            }

            WarmupInfo warmupInfo = state.warmupCommands.get(firstWord(command));
            if (warmupInfo == null) { // There isn't an in-progress warmup for this command
                warmupInfo = new WarmupInfo(command);
                state.warmupCommands.put(command, warmupInfo);
                sender.sendMessage(ChatColor.YELLOW + "Warmup started for command '"
                        + firstWord(command) + "', time remaining: " +
                        formatTime(requiredWarmupTime, TimeUnit.SECONDS) + " seconds");
                return false;
            }

            if (!command.equals(warmupInfo.fullCommand)) {
                sender.sendMessage(ChatColor.RED + "You are trying to use the command '"
                        + command + "', which already has a warmup in progress. Type /warmup cancel "
                        + firstWord(command) + " to cancel the existing warmup" );
            }
            return false;
        }
    }

    private static class WarmupInfo {
        public final String fullCommand;
        public int remainingTime;

        public WarmupInfo(String fullCommand) {
            this.fullCommand = fullCommand;
        }
    }

    private static class CooldownState extends PersistentSession {
        public static final long MAX_AGE = TimeUnit.MINUTES.toMillis(30);

        public final Map<String, WarmupInfo> warmupCommands = new ConcurrentHashMap<String, WarmupInfo>();
        @Setting("cooldown-commands") public final Map<String, Integer> cooldownCommands = new ConcurrentHashMap<String, Integer>();

        protected CooldownState() {
            super(MAX_AGE);
        }
    }

    public class Commands {
        @Command(aliases = {"warmup", "warmups"}, desc = "Provides information about command warmups")
        @NestedCommand(WarmupCommands.class)
        public void warmup() {}

        @Command(aliases = {"cooldown", "cooldowns"}, desc = "Provides information about command cooldowns")
        @NestedCommand(CooldownCommands.class)
        public void cooldown() {}
    }

    public class CooldownCommands extends SubCommands<Map.Entry<String, Integer>> {

        @Command(aliases = {"list", "ls"}, desc = "List active command limitations", usage = "[-p page] [player]", flags = "p:", min = 0, max = 1)
        public void list(CommandContext args, CommandSender sender) throws CommandException {
            CommandSender target;
            if (args.argsLength() == 0) {
                target = sender;
            } else {
                target = InputUtil.PlayerParser.matchPlayerOrConsole(sender, args.getString(0));
            }
            getListOutput().display(sender, getActive(target), args.getFlagInteger('p', 1));
        }

        @Command(aliases = {"cancel", "c"}, desc = "Cancel a command limitation", usage = "<cmd>", min = 1)
        public void cancel(CommandContext args, CommandSender sender) throws CommandException {
            String item = args.getJoinedStrings(0);
            if (remove(sender, item)) {
                sender.sendMessage(ChatColor.YELLOW + titleCase(getTypeName()) +
                        " for command '" + item + "' removed.");
            } else {
                throw new CommandException("No " + getTypeName() + " for input " + item);
            }
        }

        @Override
        public String getTypeName() {
            return "cooldown";
        }

        @Override
        public PaginatedResult<Map.Entry<String, Integer>> getListOutput() {
            return new PaginatedResult<Map.Entry<String, Integer>>("Command - Time remaining") {

                @Override
                public String format(Map.Entry<String, Integer> entry) {
                    Map<String, Integer> storedTimes = config.registeredActions.get(entry.getKey());
                    if (storedTimes == null) { // Nothing in the config for this command
                        return "Invalid cooldown: " + entry.getKey();
                    }

                    Integer requiredCooldownTime = storedTimes.get("cooldown");
                    if (requiredCooldownTime == null) { // No cooldown for this command
                        return "Invalid cooldown: " + entry.getKey();
                    }
                    return entry.getKey() + " - " +
                            formatTime(requiredCooldownTime - entry.getValue(), TimeUnit.SECONDS);
                }
            };
        }

        @Override
        public Collection<Map.Entry<String, Integer>> getActive(CommandSender sender) {
            return sessions.getSession(CooldownState.class, sender).cooldownCommands.entrySet();
        }

        @Override
        public boolean remove(CommandSender sender, String name) {
            return sessions.getSession(CooldownState.class, sender).cooldownCommands.remove(name.toLowerCase()) != null;
        }
    }

    public class WarmupCommands extends SubCommands<WarmupInfo> {

        @Command(aliases = {"list", "ls"}, desc = "List active command limitations", usage = "[-p page] [player]", flags = "p:", min = 0, max = 1)
        public void list(CommandContext args, CommandSender sender) throws CommandException {
            CommandSender target;
            if (args.argsLength() == 0) {
                target = sender;
            } else {
                target = InputUtil.PlayerParser.matchPlayerOrConsole(sender, args.getString(0));
            }
            getListOutput().display(sender, getActive(target), args.getFlagInteger('p', 1));
        }

        @Command(aliases = {"cancel", "c"}, desc = "Cancel a command limitation", usage = "<cmd>", min = 1)
        public void cancel(CommandContext args, CommandSender sender) throws CommandException {
            String item = args.getJoinedStrings(0);
            if (remove(sender, item)) {
                sender.sendMessage(ChatColor.YELLOW + titleCase(getTypeName()) +
                        " for command '" + item + "' removed.");
            } else {
                throw new CommandException("No " + getTypeName() + " for input " + item);
            }
        }

        @Override
        public String getTypeName() {
            return "warmup";
        }

        @Override
        public PaginatedResult<WarmupInfo> getListOutput() {
            return new PaginatedResult<WarmupInfo>("Command - Remaining time") {
                @Override
                public String format(WarmupInfo entry) {
                    Map<String, Integer> storedTimes = config.registeredActions
                            .get(firstWord(entry.fullCommand));
                    if (storedTimes == null) { // Nothing in the config for this command
                        return "Invalid warmup: " + entry.fullCommand;
                    }

                    Integer requiredWarmupTime = storedTimes.get("warmup");
                    if (requiredWarmupTime == null) { // No cooldown for this command
                        return "Invalid warmup: " + entry.fullCommand;
                    }
                    return "/" + entry.fullCommand + " - " +
                            formatTime(requiredWarmupTime - entry.remainingTime, TimeUnit.SECONDS);
                }
            };
        }

        @Override
        public Collection<WarmupInfo> getActive(CommandSender sender) {
            return sessions.getSession(CooldownState.class, sender).warmupCommands.values();
        }

        @Override
        public boolean remove(CommandSender sender, String name) {
            return sessions.getSession(CooldownState.class, sender).warmupCommands.remove(name.toLowerCase()) != null;
        }
    }

    private abstract class SubCommands<CommandType> {
        public abstract String getTypeName();

        public abstract PaginatedResult<CommandType> getListOutput();

        public abstract Collection<CommandType> getActive(CommandSender sender);

        public abstract boolean remove(CommandSender sender, String name);

        /*@Command(aliases = {"list", "ls"}, desc = "List active command limitations", usage = "[-p page] [player]", flags = "p:", min = 0, max = 1)
        public void list(CommandContext args, CommandSender sender) throws CommandException {
            CommandSender target;
            if (args.argsLength() == 0) {
                target = sender;
            } else {
                target = InputUtil.PlayerParser.matchPlayerOrConsole(sender, args.getString(0));
            }
            getListOutput().display(sender, getActive(target), args.getFlagInteger('p', 1));
        }

        @Command(aliases = {"cancel", "c"}, desc = "Cancel a command limitation", usage = "<cmd>", min = 1)
        public void cancel(CommandContext args, CommandSender sender) throws CommandException {
            String item = args.getJoinedStrings(0);
            if (remove(sender, item)) {
                sender.sendMessage(ChatColor.YELLOW + titleCase(getTypeName()) +
                        " for command '" + item + "' removed.");
            } else {
                throw new CommandException("No " + getTypeName() + " for input " + item);
            }
        }*/
    }
}
TOP

Related Classes of com.sk89q.commandbook.CooldownsComponent$Commands

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.