Skip to content

Instantly share code, notes, and snippets.

@Romain-P
Created March 15, 2015 00:08
Show Gist options
  • Select an option

  • Save Romain-P/8fcaeda4fa075ff8ab21 to your computer and use it in GitHub Desktop.

Select an option

Save Romain-P/8fcaeda4fa075ff8ab21 to your computer and use it in GitHub Desktop.
example
package org.heat.world.commands.impl;
import lombok.Getter;
import org.heat.world.commands.Command;
import org.heat.world.commands.CommandCond;
/**
* Managed by romain on 08/03/2015.
*/
public abstract class AbstractCommand implements Command {
@Getter private final String name, syntax;
@Getter private final Class<CommandCond>[] conditions;
@Getter private final String[] arguments, subCommands;
public AbstractCommand(String name, String syntax, Class<CommandCond>[] conditions, String[] arguments, String[] subCommands) {
this.name = name;
this.syntax = syntax;
this.conditions = conditions;
this.arguments = arguments;
this.subCommands = subCommands;
}
}
package org.heat.world.commands;
/**
* Managed by romain on 08/03/2015.
*/
public interface Command {
String getName();
String getSyntax();
Class<CommandCond>[] getConditions();
String[] getArguments();
String[] getSubCommands();
CommandAction getAction();
}
package org.heat.world.commands;
/**
* Managed by romain on 08/03/2015.
*/
public interface CommandAction {
void execute(CommandSender sender, String... args);
}
package org.heat.world.commands;
/**
* Managed by romain on 11/03/2015.
*/
public interface CommandTree {
String getName();
Command getCommand();
CommandArg getArgument(String label);
CommandTree getSubCommandTree(String name);
CommandArg[] getArguments();
CommandTree[] getSubCommandTrees();
CommandArg[] getRequiredArguments();
}
package org.heat.world.commands.impl;
import lombok.Getter;
import org.heat.world.commands.Command;
import org.heat.world.commands.CommandArg;
import org.heat.world.commands.CommandTree;
import java.util.Map;
/**
* Managed by romain on 11/03/2015.
*/
public class DefaultCommandTree implements CommandTree{
@Getter private final String name;
@Getter private final Command command;
private final Map<String, CommandArg> arguments;
private final Map<String, CommandTree> subCommandTrees;
private final CommandArg[] argCache;
private final CommandTree[] subCache;
@Getter private final CommandArg[] requiredArguments;
public DefaultCommandTree(String name,
Command command,
Map<String, CommandArg> arguments,
Map<String, CommandTree> subCommandTrees) {
this.name = name;
this.command = command;
this.arguments = arguments;
this.subCommandTrees = subCommandTrees;
this.argCache = (CommandArg[]) arguments.values().toArray();
this.subCache = (CommandTree[]) arguments.values().toArray();
this.requiredArguments = arguments.values()
.stream()
.filter(CommandArg::isRequired)
.toArray(CommandArg[]::new);
}
public CommandArg getArgument(String label) {
return arguments.get(label);
}
public CommandTree getSubCommandTree(String name) {
return subCommandTrees.get(name);
}
public CommandArg[] getArguments() {
return this.argCache;
}
public CommandTree[] getSubCommandTrees() {
return this.subCache;
}
}
package org.heat.world.commands.impl;
import com.google.inject.Inject;
import org.heat.world.commands.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* Managed by romain on 08/03/2015.
*/
public class SimpleCommandManager implements CommandManager {
private final Map<String, CommandTree> commands;
private final Map<Class, CommandCond> conditions;
private final static char prefix = '!';
@Inject
public SimpleCommandManager(Map<String, CommandTree> commands, Map<Class, CommandCond> conditions) {
this.commands = commands;
this.conditions = conditions;
}
public boolean execute(CommandSender sender, String msg, boolean withoutPrefix) {
if(!withoutPrefix && (msg.length() < 2 || msg.charAt(0) != prefix))
return false;
String[] split = msg.split(" ");
String name = split[0];
CommandTree command = findCommand(sender, name);
if(command == null)
return true;
else if(!playerFulfillsConditions(command, sender))
return true;
else if(!commandHasValidParameters(command, split)) {
sender.reply("Commande incomplète, veillez à respecter la syntaxe: \n" +
command.getCommand().getSyntax());
return true;
}
try {
parseAndExecuteCommand(sender, command, msg);
} catch(IllegalArgumentException e) {
sender.reply("Commande erronée, veillez à respecter la syntaxe: \n" +
command.getCommand().getSyntax());
}
return true;
}
private void parseAndExecuteCommand(CommandSender sender, CommandTree command, String msg) {
String[] params = (msg = msg.substring(command.getName().length() + 1)).split(" ");
if(params.length > 0 && command.getSubCommandTrees().length > 0) {
String next = params[0];
CommandTree subTree = command.getSubCommandTree(next);
if (subTree == null && (subTree = getSubCommandByShortcut(command, next)) == null) {
sender.reply("La sous-commande n'existe pas: \n" +
command.getCommand().getSyntax());
return;
}
parseAndExecuteCommand(sender, subTree, msg);
return;
}
CommandArg[] commandArgs = command.getArguments();
int length = commandArgs.length, index = length - 1;
if(length > 0 && commandArgs[index].isWithSpaces()) {
int middle = msg.indexOf(params[index]);
String[] args = msg.substring(0, middle-1).split(" ");
String arg = msg.substring(middle);
char capital = Character.toUpperCase(arg.charAt(0));
String[] lastArg = new String[] { (capital + arg.substring(1)) };
params = Stream.concat(Arrays.stream(args), Arrays.stream(lastArg)).toArray(String[]::new);
}
command.getCommand().getAction().execute(sender, params);
}
private CommandTree findCommand(CommandSender sender, String name) {
CommandTree command;
if((command = commands.get(name)) == null
&& (command = getCommandByShortcut(sender, name)) == null)
return null;
return command; //TODO: inject
}
private CommandTree getSubCommandByShortcut(CommandTree command, String shortcut) {
CommandTree subCommand = null;
for(String name: command.getCommand().getSubCommands()) {
if(subCommand != null)
return null;
else if (name.startsWith(shortcut))
subCommand = command.getSubCommandTree(name);
}
return subCommand;
}
private boolean playerFulfillsConditions(CommandTree command, CommandSender sender) {
List<String> errors = new ArrayList<>();
for(Class<CommandCond> condClass: command.getCommand().getConditions()) {
CommandCond condition = conditions.get(condClass);
condition.isEligible(sender, errors);
}
if(!errors.isEmpty()) {
String text = "Vous ne remplissez pas les conditions nécessaire:"
+ errors.stream().collect(Collectors.joining("\n- ", "\n- ", ""));
sender.reply(text);
return false;
}
return true;
}
/**
* @param command is the command the to check.
* @param split represents parameters.
* @return false if there isn't the same number of arguments between
* ones needed & ones written by the sender. This method return
* false again if the current command hasn't any action and has
* some sub commands and if the sender haven't written any parameter.
* Return false if static args are wrong.
*/
private boolean commandHasValidParameters(CommandTree command, String[] split) {
int count = command.getRequiredArguments().length;
int size = split.length - 1; //exclude the name at index of 0
if(size < count || (size < 1 && command.getSubCommandTrees().length > 0 &&
command.getCommand().getAction() == null))
return false;
if(count > 0) {
int i = 1;
for(CommandArg arg: command.getArguments()) {
if(arg.isRequired()) {
boolean checked = false;
for(String sarg: arg.getStaticArgs())
if(sarg.equalsIgnoreCase(split[i]))
checked = true;
if(!checked) return false;
}
i++;
}
}
return true;
}
private CommandTree getCommandByShortcut(CommandSender sender, String shortcut) {
List<String> found = commands.keySet().stream().filter(
(name) -> name.startsWith(shortcut)
).collect(Collectors.toList());
int cSize = found.size();
if(cSize == 1) return commands.get(found.get(0));
if(cSize <= 0) {
sender.reply("Commande non reconnue: !help pour la liste.");
} else {
sender.reply("Vous cherchez peut-être: " +
found.stream()
.map(Function.identity())
.collect(Collectors.joining(", ")));
}
return null;
}
}
@dpryden
Copy link

dpryden commented Mar 15, 2015

In your comment at StackOverflow, you said you need to inject Command.getAction(). Does that mean you need to inject a CommandAction into a Command implementation, or inject the result of calling getAction() on an injected Command, or something else?

(Sorry, this is a lot of code to try to absorb, and it's not obvious how you want it to wire together.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment