/* * Copyright (C) 2012 maartenl * * 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 olr 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 mmud.commands; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.TreeMap; import java.util.concurrent.CopyOnWriteArrayList; import java.util.logging.Logger; import mmud.Utils; import mmud.commands.communication.AskCommand; import mmud.commands.communication.CryCommand; import mmud.commands.communication.OocCommand; import mmud.commands.communication.SayCommand; import mmud.commands.communication.ScreamCommand; import mmud.commands.communication.ShoutCommand; import mmud.commands.communication.SingCommand; import mmud.commands.communication.TellCommand; import mmud.commands.communication.WhisperCommand; import mmud.commands.guild.AcceptCommand; import mmud.commands.guild.ApplyCommand; import mmud.commands.guild.ChangeMasterCommand; import mmud.commands.guild.CreateGuildCommand; import mmud.commands.guild.DeleteGuildCommand; import mmud.commands.guild.DetailsCommand; import mmud.commands.guild.GuildMessageCommand; import mmud.commands.guild.LeaveCommand; import mmud.commands.guild.MessageCommand; import mmud.commands.guild.RankAssignCommand; import mmud.commands.guild.RankChangeCommand; import mmud.commands.guild.RankDeleteCommand; import mmud.commands.guild.RejectCommand; import mmud.commands.guild.RemoveCommand; import mmud.commands.items.BuyCommand; import mmud.commands.items.CloseCommand; import mmud.commands.items.DestroyCommand; import mmud.commands.items.DisarmCommand; import mmud.commands.items.DrinkCommand; import mmud.commands.items.DropCommand; import mmud.commands.items.EatCommand; import mmud.commands.items.GetCommand; import mmud.commands.items.GiveCommand; import mmud.commands.items.InventoryCommand; import mmud.commands.items.LockCommand; import mmud.commands.items.LookAtCommand; import mmud.commands.items.LookInCommand; import mmud.commands.items.OpenCommand; import mmud.commands.items.PutCommand; import mmud.commands.items.ReadCommand; import mmud.commands.items.RetrieveCommand; import mmud.commands.items.SellCommand; import mmud.commands.items.UndressCommand; import mmud.commands.items.UnlockCommand; import mmud.commands.items.UnwearCommand; import mmud.commands.items.UnwieldCommand; import mmud.commands.items.WearCommand; import mmud.commands.items.WieldCommand; import mmud.commands.movement.DownCommand; import mmud.commands.movement.EastCommand; import mmud.commands.movement.GoCommand; import mmud.commands.movement.NorthCommand; import mmud.commands.movement.SouthCommand; import mmud.commands.movement.UpCommand; import mmud.commands.movement.WestCommand; import mmud.database.entities.characters.User; import mmud.exceptions.MudException; /** * The factory that creates commands based on the string entered by the user. * * @author maartenl */ public class CommandFactory { private static final Logger itsLog = Logger.getLogger(CommandFactory.class.getName()); private static final CommandCreator BOGUS = () -> new BogusCommand(".+"); static final CommandCreator AWAKEN = () -> new AwakenCommand("awaken"); static final CommandCreator ALREADY_ASLEEP = () -> new AlreadyAsleepCommand(".+"); static final CommandCreator RIBBIT = () -> new RibbitCommand("ribbit"); static final CommandCreator HEEHAW = () -> new HeehawCommand("heehaw"); /** * Contains mappings from what command people have entered, to what command * should be executed. Supports two and one string, for example "look at" or * "look", in order to distinguish different commands with the same verb. */ private static final TreeMap<String, CommandCreator> theCommandStructure = new TreeMap<>(); private static List<UserCommandInfo> theUserCommandStructure = new CopyOnWriteArrayList<>(); public static interface CommandCreator { public NormalCommand createCommand(); } static { theCommandStructure.put("bow", (CommandCreator) () -> new BowCommand("bow( to (\\w)+)?( (\\w)+)?")); theCommandStructure.put("me", new CommandCreatorImpl()); // quit command has been replaced with a specific rest service. // theCommandStructure.put("quit", new QuitCommand("quit")); theCommandStructure.put("sleep", (CommandCreator) () -> new SleepCommand("sleep")); theCommandStructure.put("condition", (CommandCreator) () -> new ConditionCommand("(condition)|(condition .+)")); theCommandStructure.put("awaken", (CommandCreator) () -> new AwakenCommand("awaken")); theCommandStructure.put("ask", (CommandCreator) () -> new AskCommand("ask (to (\\w)+ )?.+")); theCommandStructure.put("ooc", (CommandCreator) () -> new OocCommand("ooc .+")); theCommandStructure.put("title", (CommandCreator) () -> new TitleCommand("(title|title ?.+)")); theCommandStructure.put("tell", (CommandCreator) () -> new TellCommand("tell to (\\w)+ .+")); theCommandStructure.put("say", (CommandCreator) () -> new SayCommand("say (to (\\w)+ )?.+")); theCommandStructure.put("macro", (CommandCreator) () -> new MacroCommand("macro( .+)?")); theCommandStructure.put("sing", (CommandCreator) () -> new SingCommand("sing (to (\\w)+ )?.+")); theCommandStructure.put("cry", (CommandCreator) () -> new CryCommand("cry (to (\\w)+ )?.+")); theCommandStructure.put("shout", (CommandCreator) () -> new ShoutCommand( "shout (to (\\w )+)?.+")); theCommandStructure.put("scream", (CommandCreator) () -> new ScreamCommand( "scream (to (\\w )+)?.+")); theCommandStructure.put("whisper", (CommandCreator) () -> new WhisperCommand( "whisper (to (\\w)+ )?.+")); theCommandStructure.put("clear", (CommandCreator) () -> new ClearCommand("clear")); theCommandStructure.put("time", (CommandCreator) () -> new TimeCommand("time")); theCommandStructure.put("date", (CommandCreator) () -> new DateCommand("date")); theCommandStructure.put("south", (CommandCreator) () -> new SouthCommand("south")); theCommandStructure.put("north", (CommandCreator) () -> new NorthCommand("north")); theCommandStructure.put("east", (CommandCreator) () -> new EastCommand("east")); theCommandStructure.put("west", (CommandCreator) () -> new WestCommand("west")); theCommandStructure.put("s", (CommandCreator) () -> new SouthCommand("s")); theCommandStructure.put("n", (CommandCreator) () -> new NorthCommand("n")); theCommandStructure.put("e", (CommandCreator) () -> new EastCommand("e")); theCommandStructure.put("w", (CommandCreator) () -> new WestCommand("w")); theCommandStructure.put("up", (CommandCreator) () -> new UpCommand("up")); theCommandStructure.put("down", (CommandCreator) () -> new DownCommand("down")); theCommandStructure.put("go", (CommandCreator) () -> new GoCommand( "go (up|down|north|south|east|west)?")); theCommandStructure.put("help", (CommandCreator) () -> new HelpCommand("help( (\\w)+)?")); theCommandStructure.put("show ignoring", (CommandCreator) () -> new IgnoringCommand( "show ignoring")); theCommandStructure.put("fully", (CommandCreator) () -> new IgnoreCommand( "fully ignore (\\w)+")); theCommandStructure.put("acknowledge", (CommandCreator) () -> new AcknowledgeCommand( "acknowledge (\\w)+")); theCommandStructure.put("curtsey", (CommandCreator) () -> new CurtseyCommand( "curtsey( to (\\w)+)?")); theCommandStructure.put("eyebrow", (CommandCreator) () -> new EyebrowCommand("eyebrow")); theCommandStructure.put("wimpy", (CommandCreator) () -> new WimpyCommand("wimpy( .+|help)?")); theCommandStructure.put("who", (CommandCreator) () -> new WhoCommand("who")); // theCommandStructure.put("pkill", new PkillCommand("pkill( (\\w)+)?")); // theCommandStructure.put("fight", new FightCommand("fight (\\w)+")); // theCommandStructure.put("stop", new FightCommand("stop fighting")); theCommandStructure.put("stats", (CommandCreator) () -> new StatsCommand("stats")); theCommandStructure.put("inventory", (CommandCreator) () -> new InventoryCommand("inventory")); theCommandStructure.put("i", (CommandCreator) () -> new InventoryCommand("i")); theCommandStructure.put("drink", (CommandCreator) () -> new DrinkCommand( "drink( (\\w|-)+){1,4}")); theCommandStructure.put("eat", (CommandCreator) () -> new EatCommand("eat( (\\w|-)+){1,4}")); theCommandStructure.put("destroy", (CommandCreator) () -> new DestroyCommand("destroy( (\\w|-)+){1,4}")); theCommandStructure.put("wear", (CommandCreator) () -> new WearCommand( "wear( (\\w|-)+){1,4} on (\\w)+")); theCommandStructure.put("remove", (CommandCreator) () -> new UnwearCommand( "remove from (\\w)+")); theCommandStructure.put("undress", (CommandCreator) () -> new UndressCommand( "undress")); theCommandStructure.put("disarm", (CommandCreator) () -> new DisarmCommand( "disarm")); theCommandStructure.put("wield", (CommandCreator) () -> new WieldCommand( "wield( (\\w|-)+){1,4} with (\\w)+")); theCommandStructure.put("unwield", (CommandCreator) () -> new UnwieldCommand( "unwield from (\\w)+")); theCommandStructure.put("drop", (CommandCreator) () -> new DropCommand("drop( (\\w|-)+){1,4}")); theCommandStructure.put("get", (CommandCreator) () -> new GetCommand("get( (\\w|-)+){1,4}")); theCommandStructure.put("put", (CommandCreator) () -> new PutCommand( "put( (\\w|-)+){1,4} in( (\\w|-)+){1,4}")); theCommandStructure.put("retrieve", (CommandCreator) () -> new RetrieveCommand( "retrieve( (\\w|-)+){1,4} from( (\\w|-)+){1,4}")); theCommandStructure.put("lock", (CommandCreator) () -> new LockCommand( "lock( (\\w|-)+){1,4} with( (\\w|-)+){1,4}")); theCommandStructure.put("unlock", (CommandCreator) () -> new UnlockCommand( "unlock( (\\w|-)+){1,4} with( (\\w|-)+){1,4}")); theCommandStructure.put("give", (CommandCreator) () -> new GiveCommand( "give( (\\w|-)+){1,4} to (\\w)+")); theCommandStructure.put("open", (CommandCreator) () -> new OpenCommand("open( (\\w|-)+){1,4}")); theCommandStructure.put("close", (CommandCreator) () -> new CloseCommand( "close( (\\w|-)+){1,4}")); theCommandStructure.put("read", (CommandCreator) () -> new ReadCommand("read( (\\w|-)+){1,4}")); theCommandStructure.put("readboard", (CommandCreator) () -> new ReadBoardCommand( "readboard (\\w)+")); theCommandStructure.put("post", (CommandCreator) () -> new PostBoardCommand("post (\\w)+ .+")); theCommandStructure.put("l", (CommandCreator) () -> new LookCommand("l")); theCommandStructure.put("look", (CommandCreator) () -> new LookCommand( "look")); theCommandStructure.put("look at", (CommandCreator) () -> new LookAtCommand( "look at( (\\w|-)+){1,4}")); theCommandStructure.put("look in", (CommandCreator) () -> new LookInCommand( "look in( (\\w|-)+){1,4}")); // theCommandStructure.put("search", new SearchCommand( // "search( (\\w|-)+){1,4}")); theCommandStructure.put("buy", (CommandCreator) () -> new BuyCommand( "buy( (\\w|-)+){1,4} from (\\w)+")); theCommandStructure.put("sell", (CommandCreator) () -> new SellCommand( "sell( (\\w|-)+){1,4} to (\\w)+")); // theCommandStructure.put("show", new ShowCommand( // "show( (\\w|-)+){1,4} to (\\w)+")); // theCommandStructure.put("title", new TitleCommand("title .+")); theCommandStructure.put("admin", (CommandCreator) () -> new AdminCommand("admin .+")); theCommandStructure.put("owner", (CommandCreator) () -> new OwnerCommand("owner( (\\w)+)?")); theCommandStructure.put("deputies", (CommandCreator) () -> new OwnerCommand("deputies")); // guild commands theCommandStructure.put("guildapply", (CommandCreator) () -> new ApplyCommand( "guildapply( (\\w)+)?")); theCommandStructure.put("guildmessage", (CommandCreator) () -> new GuildMessageCommand( "guildmessage .+")); theCommandStructure.put("guildleave", (CommandCreator) () -> new LeaveCommand("guildleave")); theCommandStructure.put("guilddetails", (CommandCreator) () -> new DetailsCommand( "guilddetails")); theCommandStructure.put("guildaccept", (CommandCreator) () -> new AcceptCommand( "guildaccept (\\w)+")); theCommandStructure.put("guildreject", (CommandCreator) () -> new RejectCommand( "guildreject (\\w)+")); theCommandStructure.put("guildrank", (CommandCreator) () -> new RankChangeCommand( "guildrank (\\d){1,3} (\\w)+")); theCommandStructure.put("guildaddrank", (CommandCreator) () -> new RankChangeCommand( "guildaddrank (\\d){1,3} (\\w)+")); theCommandStructure.put("guildassignrank", (CommandCreator) () -> new RankAssignCommand( "guildassignrank (\\w)+ (\\w)+")); theCommandStructure.put("guilddelrank", (CommandCreator) () -> new RankDeleteCommand( "guilddelrank (\\d){1,3}")); theCommandStructure.put("createguild", (CommandCreator) () -> new CreateGuildCommand( "createguild (\\w)+ .+")); theCommandStructure.put("deleteguild", (CommandCreator) () -> new DeleteGuildCommand( "deleteguild")); theCommandStructure.put("guildremove", (CommandCreator) () -> new RemoveCommand( "guildremove (\\w)+")); theCommandStructure.put("guildmasterchange", (CommandCreator) () -> new ChangeMasterCommand( "guildmasterchange (\\w)+")); theCommandStructure.put("guild", (CommandCreator) () -> new MessageCommand("guild .+")); for (Map.Entry<String, String> entry : Utils.getEmotions().entrySet()) { theCommandStructure.put(entry.getKey(), (CommandCreator) () -> new EmotionCommand(".+")); } for (Map.Entry<String, String> entry : Utils.getTargetEmotions().entrySet()) { theCommandStructure.put(entry.getKey(), (CommandCreator) () -> new EmotionToCommand(".+")); } } public static NormalCommand getBogusCommand() { return BOGUS.createCommand(); } /** * Returns the commands to be used, based on the first word in the command * entered by the user. * * @param aCommand String containing the command entered by the user. * @return List containing the commands that fit the description. The * commands that are contained are in the following order: * <ol> * <li>special commands retrieved from the database * <li>normal commands * <li>bogus command (the ultimate failover, "I don't understand that.".) * </ol> * It also means that this collection will always carry at least one * command, the bogus command. * <P> * All commands are newly created. */ static List<NormalCommand> getCommand(String aCommand) { List<NormalCommand> result = new ArrayList<>(5); String[] strings = aCommand.split(" "); NormalCommand myCommand = null; if (myCommand == null && strings.length >= 2) { final CommandCreator commandCreator = theCommandStructure.get(strings[0] + " " + strings[1]); myCommand = commandCreator == null ? null : commandCreator.createCommand(); } if (myCommand == null && strings.length >= 1) { final CommandCreator commandCreator = theCommandStructure.get(strings[0]); myCommand = commandCreator == null ? null : commandCreator.createCommand(); } if (myCommand == null) { myCommand = getBogusCommand(); } result.add(myCommand); return result; } /** * <p> * Returns all matching user commands.</p> * <p> * There are a few requirements<ul><li>commands must match</li> * <li>either the user defined command is valid for all rooms OR</li> * <li>room defined in the user defined commands euqals the room the user is * in</li></ul></p> * * @param user the user, used to get the room. * @param aCommand the string containing the command issued by the user * @return a list of possibly valid user defined commands. */ public static List<UserCommandInfo> getUserCommands(User user, String aCommand) { itsLog.entering("CommandFactory", "getUserCommands " + user + " " + aCommand); List<UserCommandInfo> result = new ArrayList<>(); for (UserCommandInfo myCom : theUserCommandStructure) { if (aCommand.matches(myCom.getCommand())) { if (myCom.getRoom() == null || myCom.getRoom().equals(user.getRoom().getId())) { result.add(myCom); } } } return Collections.unmodifiableList(result); } /** * The definition of a user command. Bear in mind that commands that are * executed often do not make very good global user commands, as the load on * the database might be a little too much. */ public static class UserCommandInfo { private final int theCommandId; private final String theCommand; private final String theMethodName; private final Integer theRoom; /** * constructor for creating a user command for a specific room. * * @param aCommand the regular expression for determining the command is * equal to the command entered. * @param aMethodName the name of the method to call. * @param aRoom the room in which this command is active. Can be a null * pointer if the command is active in all rooms. * @param aCommandId the identification number for the command */ public UserCommandInfo(int aCommandId, String aCommand, String aMethodName, Integer aRoom) { theCommandId = aCommandId; theCommand = aCommand; theMethodName = aMethodName; theRoom = aRoom; } /** * constructor for creating a user command for all rooms. * * @param aCommand the regular expression for determining the command is * equal to the command entered. * @param aMethodName the name of the method to call. * @param aCommandId the identification number for the command */ public UserCommandInfo(int aCommandId, String aCommand, String aMethodName) { theCommandId = aCommandId; theCommand = aCommand; theMethodName = aMethodName; theRoom = null; } public String getCommand() { return theCommand; } public int getCommandId() { return theCommandId; } public String getMethodName() { return theMethodName; } public Integer getRoom() { return theRoom; } @Override public boolean equals(Object o) { if (!(o instanceof UserCommandInfo)) { return false; } UserCommandInfo myUC = (UserCommandInfo) o; boolean p = myUC.getCommand().equals(getCommand()); return p; } public int getTheCommandId() { return theCommandId; } @Override public String toString() { return theCommandId + ":" + getCommand() + ":" + getMethodName() + ":" + getRoom(); } /** * Use this method when the command that had to be run, throws an * exception. */ public void deactivateCommand() throws MudException { theUserCommandStructure.remove(this); } } /** * Returns if the map of user commands is empty. * * @return */ public static boolean noUserCommands() { return theUserCommandStructure.isEmpty(); } /** * Clears the map containing all user-defined commands. It will be reloaded * automatically from the database, upon executing the first command. */ public static void clearUserCommandStructure() { CommandFactory.theUserCommandStructure.clear(); } /** * Add a user command to the structure. * * @param aCommandId the id of the command * @param aCommand the command * @param aMethodName the method name * @param aRoom the room, optional, may be null in which case the command is * valid for all rooms. */ public static void addUserCommand(int aCommandId, String aCommand, String aMethodName, Integer aRoom) { UserCommandInfo info = new UserCommandInfo(aCommandId, aCommand, aMethodName, aRoom); CommandFactory.theUserCommandStructure.add(info); } private static class CommandCreatorImpl implements CommandCreator { public CommandCreatorImpl() { } @Override public NormalCommand createCommand() { return new MeCommand("me .+"); } } }