001/*
002 *     Copyright 2021 Siroshun09
003 *
004 *     Licensed under the Apache License, Version 2.0 (the "License");
005 *     you may not use this file except in compliance with the License.
006 *     You may obtain a copy of the License at
007 *
008 *         http://www.apache.org/licenses/LICENSE-2.0
009 *
010 *     Unless required by applicable law or agreed to in writing, software
011 *     distributed under the License is distributed on an "AS IS" BASIS,
012 *     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 *     See the License for the specific language governing permissions and
014 *     limitations under the License.
015 */
016
017package com.github.siroshun09.mccommand.bukkit;
018
019import com.github.siroshun09.mccommand.bukkit.sender.BukkitSender;
020import com.github.siroshun09.mccommand.common.Command;
021import com.github.siroshun09.mccommand.common.context.CommandContext;
022import com.github.siroshun09.mccommand.common.context.SimpleCommandContext;
023import net.kyori.adventure.platform.bukkit.BukkitAudiences;
024import org.bukkit.command.CommandExecutor;
025import org.bukkit.command.CommandSender;
026import org.bukkit.command.PluginCommand;
027import org.bukkit.command.TabCompleter;
028import org.bukkit.plugin.Plugin;
029import org.bukkit.plugin.java.JavaPlugin;
030import org.jetbrains.annotations.NotNull;
031import org.jetbrains.annotations.Nullable;
032
033import java.util.List;
034import java.util.concurrent.Executor;
035import java.util.concurrent.Executors;
036
037/**
038 * The class for registering commands to Bukkit.
039 */
040public final class BukkitCommandFactory {
041
042    private BukkitCommandFactory() {
043        throw new UnsupportedOperationException();
044    }
045
046    /**
047     * Registers the command.
048     *
049     * @param target  the {@link PluginCommand}
050     * @param command the command to register
051     */
052    public static void register(@NotNull PluginCommand target, @NotNull Command command) {
053        BukkitCommandImpl bukkitCommand = new BukkitCommandImpl(target.getPlugin(), command);
054
055        target.setExecutor(bukkitCommand);
056        target.setTabCompleter(bukkitCommand);
057    }
058
059    /**
060     * Searches for {@link PluginCommand} that is named as {@link Command#getName()}
061     * and register the command if it exists.
062     *
063     * @param sourcePlugin the plugin to search for the command
064     * @param command      the command to register
065     */
066    public static void registerIfExists(@NotNull JavaPlugin sourcePlugin, @NotNull Command command) {
067        PluginCommand pluginCommand = sourcePlugin.getCommand(command.getName());
068
069        if (pluginCommand != null) {
070            register(pluginCommand, command);
071        }
072    }
073
074    /**
075     * Registers the command.
076     * <p>
077     * If you register a command with this method, it will be executed asynchronously.
078     * <p>
079     * Note:
080     * <p>
081     * The tab completion will execute on main thread.
082     *
083     * @param target  the {@link PluginCommand}
084     * @param command the command to register
085     */
086    public static void registerAsync(@NotNull PluginCommand target, @NotNull Command command) {
087        registerAsync(target, command, Executors.newSingleThreadExecutor());
088    }
089
090    /**
091     * Searches for {@link PluginCommand} that is named as {@link Command#getName()}
092     * and register the command if it exists.
093     *
094     * @param sourcePlugin the plugin to search for the command
095     * @param command      the command to register
096     * @see BukkitCommandFactory#registerAsync(PluginCommand, Command)
097     */
098    public static void registerAsyncIfExists(@NotNull JavaPlugin sourcePlugin, @NotNull Command command) {
099        PluginCommand pluginCommand = sourcePlugin.getCommand(command.getName());
100
101        if (pluginCommand != null) {
102            registerAsync(pluginCommand, command);
103        }
104    }
105
106    /**
107     * Registers the command.
108     * <p>
109     * If you register a command with this method, it will be executed asynchronously.
110     * <p>
111     * Note:
112     * <p>
113     * The tab completion will execute on main thread.
114     *
115     * @param target   the {@link PluginCommand}
116     * @param command  the command to register
117     * @param executor the executor to run command
118     */
119    public static void registerAsync(@NotNull PluginCommand target, @NotNull Command command, @NotNull Executor executor) {
120        AsyncBukkitCommandImpl bukkitCommand = new AsyncBukkitCommandImpl(target.getPlugin(), command, executor);
121
122        target.setExecutor(bukkitCommand);
123        target.setTabCompleter(bukkitCommand);
124    }
125
126    /**
127     * Searches for {@link PluginCommand} that is named as {@link Command#getName()}
128     * and register the command if it exists.
129     *
130     * @param sourcePlugin the plugin to search for the command
131     * @param command      the command to register
132     * @param executor     the executor to run command
133     * @see BukkitCommandFactory#registerAsync(PluginCommand, Command)
134     */
135    public static void registerAsyncIfExists(@NotNull JavaPlugin sourcePlugin, @NotNull Command command,
136                                             @NotNull Executor executor) {
137        PluginCommand pluginCommand = sourcePlugin.getCommand(command.getName());
138
139        if (pluginCommand != null) {
140            registerAsync(pluginCommand, command, executor);
141        }
142    }
143
144    private static class BukkitCommandImpl implements CommandExecutor, TabCompleter {
145
146        private final BukkitAudiences audiences;
147        private final Command command;
148
149        private BukkitCommandImpl(@NotNull Plugin plugin, @NotNull Command command) {
150            this.audiences = BukkitAudiences.create(plugin);
151            this.command = command;
152        }
153
154        @Override
155        public boolean onCommand(@NotNull CommandSender sender, org.bukkit.command.@NotNull Command cmd,
156                                 @NotNull String label, @NotNull String[] args) {
157            command.onExecution(
158                    createContext(sender, label, args)
159            );
160
161            return true;
162        }
163
164        @Override
165        public @Nullable List<String> onTabComplete(@NotNull CommandSender sender, org.bukkit.command.@NotNull Command cmd,
166                                                    @NotNull String label, @NotNull String[] args) {
167            return command.onTabCompletion(
168                    createContext(sender, label, args)
169            );
170        }
171
172        private CommandContext createContext(@NotNull CommandSender sender, @NotNull String label, @NotNull String[] args) {
173            return SimpleCommandContext.newBuilder()
174                    .setCommand(command)
175                    .setSender(new BukkitSender(audiences, sender))
176                    .setArguments(args)
177                    .setLabel(label)
178                    .build();
179        }
180    }
181
182    private static class AsyncBukkitCommandImpl implements CommandExecutor, TabCompleter {
183
184        private final BukkitAudiences audiences;
185        private final Command command;
186        private final Executor executor;
187
188        private AsyncBukkitCommandImpl(@NotNull Plugin plugin, @NotNull Command command, @NotNull Executor executor) {
189            this.audiences = BukkitAudiences.create(plugin);
190            this.command = command;
191            this.executor = executor;
192        }
193
194        @Override
195        public boolean onCommand(@NotNull CommandSender sender, org.bukkit.command.@NotNull Command cmd,
196                                 @NotNull String label, @NotNull String[] args) {
197            executor.execute(() ->
198                    command.onExecution(
199                            createContext(sender, label, args)
200                    ));
201
202            return true;
203        }
204
205        @Override
206        public @Nullable List<String> onTabComplete(@NotNull CommandSender sender, org.bukkit.command.@NotNull Command cmd,
207                                                    @NotNull String label, @NotNull String[] args) {
208            return command.onTabCompletion(
209                    createContext(sender, label, args)
210            );
211        }
212
213        private CommandContext createContext(@NotNull CommandSender sender, @NotNull String label, @NotNull String[] args) {
214            return SimpleCommandContext.newBuilder()
215                    .setCommand(command)
216                    .setSender(new BukkitSender(audiences, sender))
217                    .setArguments(args)
218                    .setLabel(label)
219                    .build();
220        }
221    }
222}