Skip to content

Commit

Permalink
Start work on the new command system
Browse files Browse the repository at this point in the history
  • Loading branch information
graywolf336 committed May 17, 2018
1 parent 2dd999b commit b5775fc
Show file tree
Hide file tree
Showing 6 changed files with 472 additions and 0 deletions.
13 changes: 13 additions & 0 deletions src/main/java/com/craftyn/casinoslots/CasinoManager.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.craftyn.casinoslots;

public class CasinoManager {
private CasinoSlots plugin;

public CasinoManager(CasinoSlots plugin) {
this.plugin = plugin;
}

public CasinoSlots getPlugin() {
return this.plugin;
}
}
35 changes: 35 additions & 0 deletions src/main/java/com/craftyn/casinoslots/command/Command.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.craftyn.casinoslots.command;

import java.util.List;

import org.bukkit.command.CommandSender;

import com.craftyn.casinoslots.CasinoManager;

/**
* The base of all the commands.
*
* @author graywolf336
* @since 3.0.0
* @version 1.0.0
*/
public interface Command {
/**
* Execute the command given the arguments, returning whether the command handled it or not.
*
* <p>
*
* When the method returns false, the usage message is printed to the sender. If the method
* handles the command in any way, such as sending a message to the sender or actually doing
* something, then it should return true so that the sender of the command doesn't get the
* usage message.
*
* @param cm An instance of the {@link CasinoManager}
* @param sender The {@link CommandSender sender} of the command
* @param args The args, in an array
* @return True if the method handled it in any way, false if we should send the usage message.
*/
public boolean execute(CasinoManager cm, CommandSender sender, String... args) throws Exception;

public List<String> provideTabCompletions(CasinoManager cm, CommandSender sender, String... args) throws Exception;
}
224 changes: 224 additions & 0 deletions src/main/java/com/craftyn/casinoslots/command/CommandHandler.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,224 @@
package com.craftyn.casinoslots.command;

import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map.Entry;

import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;

import com.craftyn.casinoslots.CasinoManager;
import com.craftyn.casinoslots.CasinoSlots;
import com.craftyn.casinoslots.enums.Lang;
import com.craftyn.casinoslots.enums.Settings;

/**
* Where all the commands are registered, handled, and processed.
*
* @author graywolf336
* @since 3.0.0
* @version 1.0.0
*
*/
public class CommandHandler {
private LinkedHashMap<String, Command> commands;

public CommandHandler(CasinoSlots plugin) {
commands = new LinkedHashMap<String, Command>();
loadCommands();

plugin.debug("Loaded " + commands.size() + " commands.");
}

public List<String> parseTabComplete(CasinoManager cm, CommandSender sender, String[] args) throws Exception {
if(args[0].isEmpty() || args.length == 1) {
List<String> results = new ArrayList<String>();
String arg0 = args[0].toLowerCase();

for(Command c : commands.values()) {
CommandInfo i = c.getClass().getAnnotation(CommandInfo.class);

// Skip if the command requires a player and the sender isn't a player
if(i.needsPlayer() && !(sender instanceof Player)) continue;

// If the sender has permission to the command
// and the first argument (sub command) is empty OR
// the first argument matches the command or starts with the string
if(sender.hasPermission(i.permission()) && (arg0.isEmpty() || (arg0.toLowerCase().matches(i.pattern()) || i.pattern().startsWith(arg0)))) {
results.add(i.pattern().split("\\|")[0]);
}
}

//Sort the results before adding the player names
Collections.sort(results);

return results;
}else {
String arg0 = args[0].toLowerCase();

for(Command c : commands.values()) {
CommandInfo i = c.getClass().getAnnotation(CommandInfo.class);

if(!arg0.toLowerCase().matches(i.pattern())) continue;

// Sender provided too many arguments which means there
// is nothing to tab complete
if(i.maxArgs() != -1 && i.maxArgs() < args.length - 1) continue;

// Skip if the command requires a player and the sender isn't a player
if(i.needsPlayer() && !(sender instanceof Player)) continue;

// If the sender doesn't have permission, we won't send them further
if(!sender.hasPermission(i.permission())) continue;

return c.provideTabCompletions(cm, sender, args);
}
}

return Collections.emptyList();
}

/**
* Handles the given command and checks that the command is in valid form.
*
* <p>
*
* It checks in the following order:
* <ol>
* <li>If they have permission for it, if they don't then we send them a message stating so.</li>
* <li>If the command needs a player instance, if so we send a message stating that.</li>
* <li>If the required minimum arguments have been passed, if not sends the usage.</li>
* <li>If the required maximum arguments have been passed (if there is a max, -1 if no max), if not sends the usage.</li>
* <li>Then executes, upon failed execution it sends the usage command.</li>
* </ol>
*
* @param cm The instance of {@link CasinoManager}.
* @param sender The sender of the command.
* @param args The arguments passed to the command.
*/
public boolean parseCommand(CasinoManager cm, CommandSender sender, String[] args) {
Command c = null;

// If they didn't provide any arguments (aka just: /casinoslots) then we will need to send them some help
if(args.length == 0) {
// TODO: Create the help page(s)
c = getMatches("casino").get(0);
}else {
// /casino add

// Get the matches from the first argument passed
List<Command> matches = getMatches(args[0].toLowerCase());

if(matches.isEmpty()) {
// No matches found, thus it is likely they don't know what they're trying to do
c = getMatches("casino").get(0);
} else if(matches.size() > 1) {
// If we found more than one match
// then let's send the usage of each match to the sender
for(Command cmd : matches)
showUsage(sender, cmd);

return true;
}else {
// Only one match was found, so let's continue
c = matches.get(0);
}
}

CommandInfo i = c.getClass().getAnnotation(CommandInfo.class);

// First, let's check if the sender has permission for the command.
if(!i.permission().isEmpty() && !sender.hasPermission(i.permission())) {
cm.getPlugin().debug("Sender has no permission: " + i.permission());
sender.sendMessage(Lang.NOPERMISSION.get() + (Settings.DEBUG.asBoolean() ? " (" + i.permission() + ")" : ""));
return true;
}

// Next, let's check if we need a player and then if the sender is actually a player
if(i.needsPlayer() && !(sender instanceof Player)) {
cm.getPlugin().debug("Sender is not a player.");
sender.sendMessage(Lang.PLAYERCONTEXTREQUIRED.get());
return true;
}

// Now, let's check the size of the arguments passed.
// If it is shorter than the minimum required args, let's show the usage.
// The reason we are subtracting one is because the command is
// `/casino <subcommand>` and the subcommand is viewed as an argument
if(args.length - 1 < i.minimumArgs()) {
cm.getPlugin().debug("Sender didn't provide enough arguments.");
showUsage(sender, c);
return true;
}

// Then, if the maximumArgs doesn't equal -1, we need to check
// if the size of the arguments passed is greater than the maximum args.
// The reason we are subtracting one is because the command is
// `/casino <subcommand>` and the subcommand is viewed as an argument
if(i.maxArgs() != -1 && i.maxArgs() < args.length - 1) {
cm.getPlugin().debug("Sender provided too many arguments.");
showUsage(sender, c);
return true;
}

// Since everything has been checked and we're all clear, let's execute it.
// But if get back false, let's show the usage message.
try {
if(!c.execute(cm, sender, args)) {
showUsage(sender, c);
return true;
}else {
return true;
}
} catch (Exception e) {
if(Settings.DEBUG.asBoolean()) {
e.printStackTrace();
}

cm.getPlugin().getLogger().severe("An error occured while handling the command: " + i.usage());
showUsage(sender, c);
return true;
}
}

private List<Command> getMatches(String command) {
List<Command> result = new ArrayList<Command>();

for(Entry<String, Command> entry : commands.entrySet()) {
if(command.matches(entry.getKey())) {
result.add(entry.getValue());
}
}

return result;
}

/**
* Shows the usage information to the sender, if they have permission.
*
* @param sender The sender of the command
* @param command The command to send usage of.
*/
private void showUsage(CommandSender sender, Command command) {
CommandInfo info = command.getClass().getAnnotation(CommandInfo.class);
if(!sender.hasPermission(info.permission())) return;

sender.sendMessage(info.usage());
}

private void loadCommands() {
}

private void load(Class<? extends Command> c) {
CommandInfo info = c.getAnnotation(CommandInfo.class);
if(info == null) return;

try {
commands.put(info.pattern(), c.newInstance());
}catch(Exception e) {
e.printStackTrace();
}
}
}
77 changes: 77 additions & 0 deletions src/main/java/com/craftyn/casinoslots/command/CommandInfo.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package com.craftyn.casinoslots.command;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

/**
* Contains a definition of all the annotations {@link Command commands} should have, if a {@link Command command} doesn't have any then it isn't registered.
*
* <p>
*
* This helps make loading and registering and verifying commands a lot
* easier, makes sure that before we really process a command that it
* is structured correctly and all the right information is passed. If
* the command takes a variety of options, then that command obviously
* has to handle it. We just make sure that the minimum amount of
* arguments is met and same with the maximum amount, if there is a max.
* We also check if the commands needs a player or not, if so and the
* sender is something other than a player we send a message stating that
* a player context is required. The pattern is just used to match up
* the command used to it's registered value, in regex form. We check
* the permission stated and determine if the sender has it or not, if
* they don't then we send a message stating they don't have permission
* for that command. Finally we have the usage string, which is sent
* when the sender of the command sends an incorrectly formatted
* command. The order of checking is as defined in {@link CommandHandler#handleCommand(org.bukkit.command.CommandSender, String, String[]) CommandHandler.handleCommand}.
*
* @author graywolf336
* @since 3.0.0
* @version 1.0.0
*
*/
@Retention (RetentionPolicy.RUNTIME)
public @interface CommandInfo {
/**
* Gets the maximum amount of arguments required, -1 if no maximum
* (ex: Jailing someone with a reason or editing a reason).
*
* @return The maximum number of arguments required, -1 if no maximum.
*/
public int maxArgs();

/**
* Gets the minimum amount of arguments required.
*
* @return The minimum number of arguments required.
*/
public int minimumArgs();

/**
* Whether the command needs a player context or not.
*
* @return True if requires a player, false if not.
*/
public boolean needsPlayer();

/**
* A regex pattern that allows for alternatives to
* the command (ex: /casino or /cs).
*
* @return The regex pattern to match.
*/
public String pattern();

/**
* Gets the permission required to execute this command.
*
* @return The permission required.
*/
public String permission();

/**
* Gets the usage message for this command.
*
* @return The usage message.
*/
public String usage();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.craftyn.casinoslots.command.commands;

import java.util.Collections;
import java.util.List;

import org.bukkit.command.CommandSender;

import com.craftyn.casinoslots.CasinoManager;
import com.craftyn.casinoslots.command.Command;
import com.craftyn.casinoslots.command.CommandInfo;

@CommandInfo(
maxArgs = 3,
minimumArgs = 2,
needsPlayer = true,
pattern = "add",
permission = "casinoslots.command.add",
usage = "/casino add [name] [type]"
)
public class CasinoAddCommand implements Command {
public boolean execute(CasinoManager cm, CommandSender sender, String... args) throws Exception {
// TODO Auto-generated method stub
return false;
}

public List<String> provideTabCompletions(CasinoManager cm, CommandSender sender, String... args) throws Exception {
// TODO Auto-generated method stub
return Collections.emptyList();
}
}
Loading

1 comment on commit b5775fc

@mibby
Copy link

@mibby mibby commented on b5775fc Sep 26, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @graywolf336,

Any possibility on continuing with development to get CasinoSlots 3.0 fully functional and ready for 1.13.1?

Please sign in to comment.