001/*
002 * Copyright (c) 2016-2017 Daniel Ennis (Aikar) - MIT License
003 *
004 *  Permission is hereby granted, free of charge, to any person obtaining
005 *  a copy of this software and associated documentation files (the
006 *  "Software"), to deal in the Software without restriction, including
007 *  without limitation the rights to use, copy, modify, merge, publish,
008 *  distribute, sublicense, and/or sell copies of the Software, and to
009 *  permit persons to whom the Software is furnished to do so, subject to
010 *  the following conditions:
011 *
012 *  The above copyright notice and this permission notice shall be
013 *  included in all copies or substantial portions of the Software.
014 *
015 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
016 *  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
017 *  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
018 *  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
019 *  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
020 *  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
021 *  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
022 */
023
024package co.aikar.commands;
025
026import co.aikar.commands.annotation.Optional;
027import co.aikar.commands.contexts.CommandResultSupplier;
028import co.aikar.commands.contexts.OnlinePlayer;
029import org.jetbrains.annotations.Nullable;
030import org.spongepowered.api.Sponge;
031import org.spongepowered.api.command.CommandSource;
032import org.spongepowered.api.entity.living.player.Player;
033import org.spongepowered.api.text.format.TextColor;
034import org.spongepowered.api.text.format.TextColors;
035import org.spongepowered.api.text.format.TextStyle;
036import org.spongepowered.api.world.World;
037
038import javax.swing.plaf.ActionMapUIResource;
039import java.util.HashSet;
040import java.util.Set;
041import java.util.regex.Pattern;
042import java.util.stream.Collectors;
043import java.util.stream.Stream;
044
045@SuppressWarnings("WeakerAccess")
046public class SpongeCommandContexts extends CommandContexts<SpongeCommandExecutionContext> {
047
048    public SpongeCommandContexts(final SpongeCommandManager manager) {
049        super(manager);
050
051        registerIssuerOnlyContext(CommandResultSupplier.class, c -> new CommandResultSupplier());
052        registerContext(OnlinePlayer.class, c -> getOnlinePlayer(c.getIssuer(), c.popFirstArg(), c.hasAnnotation(Optional.class)));
053        registerContext(TextColor.class, c -> {
054            String first = c.popFirstArg();
055            Stream<TextColor> colours = Sponge.getRegistry().getAllOf(TextColor.class).stream();
056            String filter = c.getFlagValue("filter", (String)null);
057            if(filter != null) {
058                filter = ACFUtil.simplifyString(filter);
059                String finalFilter = filter;
060                colours = colours.filter(colour -> finalFilter.equals(ACFUtil.simplifyString(colour.getName())));
061            }
062            Stream<TextColor> finalColours = colours;
063            TextColor match = Sponge.getRegistry().getType(TextColor.class, ACFUtil.simplifyString(first)).orElseThrow(() -> {
064                String valid = finalColours
065                        .map(colour -> "<c2>" + ACFUtil.simplifyString(colour.getName()) + "</c2>")
066                        .collect(Collectors.joining("<c1>,</c1> "));
067                return new InvalidCommandArgument(MessageKeys.PLEASE_SPECIFY_ONE_OF, "{valid}", valid);
068            });
069            return match;
070        });
071        registerContext(TextStyle.Base.class, c -> {
072            String first = c.popFirstArg();
073            Stream<TextStyle.Base> styles = Sponge.getRegistry().getAllOf(TextStyle.Base.class).stream();
074            String filter = c.getFlagValue("filter", (String)null);
075            if(filter != null) {
076                filter = ACFUtil.simplifyString(filter);
077                String finalFilter = filter;
078                styles = styles.filter(style -> finalFilter.equals(ACFUtil.simplifyString(style.getName())));
079            }
080            Stream<TextStyle.Base> finalStyles = styles;
081            TextStyle.Base match = Sponge.getRegistry().getType(TextStyle.Base.class, ACFUtil.simplifyString(first)).orElseThrow(() -> {
082                String valid = finalStyles
083                        .map(style -> "<c2>" + ACFUtil.simplifyString(style.getName()) + "</c2>")
084                        .collect(Collectors.joining("<c1>,</c1> "));
085                return new InvalidCommandArgument(MessageKeys.PLEASE_SPECIFY_ONE_OF, "{valid}", valid);
086            });
087            return match;
088        });
089
090        registerIssuerAwareContext(CommandSource.class, SpongeCommandExecutionContext::getSource);
091        registerIssuerAwareContext(Player.class, (c) -> {
092            Player player = c.getSource() instanceof Player ? (Player) c.getSource() : null;
093            if (player == null && !c.hasAnnotation(Optional.class)) {
094                throw new InvalidCommandArgument(MessageKeys.NOT_ALLOWED_ON_CONSOLE, false);
095            }
096            /*PlayerInventory inventory = player != null ? player.getInventory() : null;
097            if (inventory != null && c.hasFlag("itemheld") && !ACFBukkitUtil.isValidItem(inventory.getItem(inventory.getHeldItemSlot()))) {
098                throw new InvalidCommandArgument(MinecraftMessageKeys.YOU_MUST_BE_HOLDING_ITEM, false);
099            }*/
100            return player;
101        });
102        registerContext(OnlinePlayer[].class, (c) ->  {
103            SpongeCommandIssuer issuer = c.getIssuer();
104            final String search = c.popFirstArg();
105            boolean allowMissing = c.hasFlag("allowmissing");
106            Set<OnlinePlayer> players = new HashSet<>();
107            Pattern split = ACFPatterns.COMMA;
108            String splitter = c.getFlagValue("splitter", (String) null);
109            if (splitter != null) {
110                split = Pattern.compile(Pattern.quote(splitter));
111            }
112            for (String lookup : split.split(search)) {
113                OnlinePlayer player = getOnlinePlayer(issuer, lookup, allowMissing);
114                if (player != null) {
115                    players.add(player);
116                }
117            }
118            if (players.isEmpty() && !c.hasFlag("allowempty")) {
119                issuer.sendError(MinecraftMessageKeys.NO_PLAYER_FOUND_SERVER,
120                        "{search}", search);
121
122                throw new InvalidCommandArgument(false);
123            }
124            return players.toArray(new OnlinePlayer[players.size()]);
125        });
126        registerIssuerAwareContext(World.class, (c) -> {
127            String firstArg = c.getFirstArg();
128            java.util.Optional<World> world = firstArg != null ? Sponge.getServer().getWorld(firstArg) : java.util.Optional.empty();
129            if (world.isPresent()) {
130                c.popFirstArg();
131            }
132            if (!world.isPresent() && c.getSource() instanceof Player) {
133                world = java.util.Optional.of(((Player) c.getSource()).getWorld());
134            }
135            if (!world.isPresent()) {
136                throw new InvalidCommandArgument(MinecraftMessageKeys.INVALID_WORLD);
137            }
138            return world.get();
139        });
140    }
141
142    @Nullable
143    OnlinePlayer getOnlinePlayer(SpongeCommandIssuer issuer, String lookup, boolean allowMissing) throws InvalidCommandArgument {
144        Player player = ACFSpongeUtil.findPlayerSmart(issuer, lookup);
145        //noinspection Duplicates
146        if (player == null) {
147            if (allowMissing) {
148                return null;
149            }
150            throw new InvalidCommandArgument(false);
151        }
152        return new OnlinePlayer(player);
153    }
154}