/*------------------------------------------------------------------------- svninfo: $Id$ Maarten's Mud, WWW-based MUD using MYSQL Copyright (C) 1998 Maarten van Leunen 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 2 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 or 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, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. Maarten van Leunen Appelhof 27 5345 KA Oss Nederland Europe maarten_l@yahoo.com -------------------------------------------------------------------------*/ package mmud; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.Socket; import java.net.UnknownHostException; import java.util.logging.Level; import java.util.logging.Logger; import mmud.characters.Macro; import mmud.characters.InvalidFrameException; import mmud.characters.Person; import mmud.characters.PersonException; import mmud.characters.Persons; import mmud.characters.User; import mmud.characters.UserAlreadyActiveException; import mmud.characters.UserNotFoundException; import mmud.database.Database; import mmud.database.MailDb; /** * the class that takes care of all the socket communication. Is basically a * thread that terminates when the socket communication is terminated.<p/> * Now there are a couple of use cases that are worth mentioning: * <ol><li>A person logs onto the mud * <li>A person plays the mud * <li>A person logs off of the mud * <li>A person logs onto the mud, but is already playing * </ol> * 1. A Person logs onto the mud<p/> * 2. A person plays the mud<p/> * 3. A person logs off of the mud<p/> * 4. A person logs onto the mud, but is already playing<p/> * */ public class MudSocket implements Runnable { private Socket theSocket = null; private boolean theSuccess = true; private boolean theThreadCountDone = false; /** * Checks to see if the mud is temporarily offline or not. * * @return String containing a description to print if the mud is offline. * Returns a null pointer if the mud is not offline at all. */ public String isOffline() throws IOException, MudException { return Constants.getOfflineDescription(); } /** * Indicates if the mud call was successfull. * * @return boolean, true if successfull, false otherwise */ public boolean isSuccessfull() { Logger.getLogger("mmud").finer(""); return theSuccess; } /** * Constructor * * @param aSocket * the (already open) socket through which communication has to * take place. */ public MudSocket(Socket aSocket) { Logger.getLogger("mmud").finer(""); theSocket = aSocket; } /** * A little wrapper to properly deal with end-of-stream and io exceptions. * * @param aReader * the reader stream, should be opened already. * @return String read. * @throws MudException * incase of problems of end-of-stream reached. */ private String readLine(BufferedReader aReader) throws MudException { try { String read = aReader.readLine(); if (read == null) { throw new MudException("unexpected end of connection detected."); } return read; } catch (IOException e) { throw new MudException("io error during reading from socket.", e); } // this point is never reached. There is a return in the try statement. } /** * Used primarily for receiving information for logging a user onto the mud * and executing the enterMud method with the retrieved information. * * @param aWriter * the writer to write output to. * @param aReader * the reader to read input from. * @see #enterMud * @throws MudException * when either reading of the input goes wrong, or executing mud * functionality goes wrong. */ private void readLogonInfoFromSocket(PrintWriter aWriter, BufferedReader aReader) throws MudException { aWriter.println("Name:"); String name = readLine(aReader); Logger.getLogger("mmud").finest( "received from socket: name=[" + name + "]"); aWriter.println("Password:"); String password = readLine(aReader); aWriter.println("Address:"); String address = readLine(aReader); Logger.getLogger("mmud").finest( "received from socket: password=[" + password + "]"); aWriter.println("Cookie:"); String cookie = readLine(aReader); Logger.getLogger("mmud").finest( "received from socket: cookie=[" + cookie + "]"); aWriter.println("Frames:"); String frames = readLine(aReader); Logger.getLogger("mmud").finest( "received from socket: frames=[" + frames + "]"); int frame = 1; try { frame = Integer.parseInt(frames); } catch (NumberFormatException e) { Logger.getLogger("mmud").warning( "unable to interpret frame information, defaulting to 0."); } aWriter.println(enterMud(name, password, address, cookie, frame - 1) + "\n.\n"); } /** * Used primarily for receiving information and executing a specific command * for a playing user in the mud. * * @param aWriter * the writer to write output to. * @param aReader * the reader to read input from. * @see #executeMud * @throws MudException * when either reading of the input goes wrong, or executing mud * functionality goes wrong. */ private void readCommandInfoFromSocket(PrintWriter aWriter, BufferedReader aReader) throws MudException { aWriter.println("Name:"); String name = readLine(aReader); Logger.getLogger("mmud").finest( "received from socket: name=[" + name + "]"); aWriter.println("Cookie:"); String cookie = readLine(aReader); Logger.getLogger("mmud").finest( "received from socket: cookie=[" + cookie + "]"); aWriter.println("Frames:"); String frames = readLine(aReader); Logger.getLogger("mmud").finest( "received from socket: frames=[" + frames + "]"); int frame = 1; try { frame = Integer.parseInt(frames); } catch (NumberFormatException e) { Logger.getLogger("mmud").throwing("mmud.MudSocket", "readCommandInfoFromSocket", e); } aWriter.println("Command:"); StringBuffer command = new StringBuffer(); String readem = readLine(aReader); if (!readem.equals(".")) { command.append(readem); readem = readLine(aReader); } while (!readem.equals(".")) { command.append("\n" + readem); readem = readLine(aReader); } Logger.getLogger("mmud").finest( "received from socket: command=[" + command + "]"); if (command.toString().trim().equals("")) { command = new StringBuffer("l"); } String result = executeMud(name, theSocket.getInetAddress() .getCanonicalHostName(), cookie, frame - 1, command.toString()) + "\n.\n"; aWriter.println(result); // } } /** * Run method of the thread. This is started when the start method is * called. Reads from and writes to the socket provided upon creation of * this thread. * <P> * The protocol description is as follows. (red is output sent to the user, * green is input received from the user) The following are examples: * <P> * <FONT COLOR="red">Action (logon, mud, newchar):</FONT><BR> * <FONT COLOR="green">logon</FONT><BR> * <FONT COLOR="red">Name:</FONT><BR> * <FONT COLOR="green">Karn</FONT><BR> * <FONT COLOR="red">Password:</FONT><BR> * <FONT COLOR="green">some damnable password thingy</FONT><BR> * <FONT COLOR="red">Address:</FONT><BR> * <FONT COLOR="green">someaddress.athome.nl</FONT><BR> * <FONT COLOR="red">Cookie:</FONT><BR> * <FONT COLOR="green">bdf87d7dfbb8fdbf87dfb7dfb</FONT><BR> * <FONT COLOR="red">Frames:</FONT><BR> * <FONT COLOR="green">1</FONT><BR> * * <P> * <FONT COLOR="red">Action (logon, mud, newchar):</FONT><BR> * <FONT COLOR="green">mud</FONT><BR> * <FONT COLOR="red">Name:</FONT><BR> * <FONT COLOR="green">Karn</FONT><BR> * <FONT COLOR="red">Cookie:</FONT><BR> * <FONT COLOR="green">bdf87d7dfbb8fdbf87dfb7dfb</FONT><BR> * <FONT COLOR="red">Frames:</FONT><BR> * <FONT COLOR="green">1</FONT><BR> * <FONT COLOR="red">Command:</FONT><BR> * <FONT COLOR="green">look around<BR> * .</FONT> * * <P> * Bear in mind that the <I>Command</I> field must end in a "." on a * separate line. * <P> * * @see mmud.character.User#getFrames */ public void run() { Constants.incrementThreadsRunning(); Logger.getLogger("mmud").finer(""); PrintWriter myOutputStream = null; BufferedReader myInputStream = null; try { myOutputStream = new PrintWriter(theSocket.getOutputStream(), true); myInputStream = new BufferedReader(new InputStreamReader(theSocket .getInputStream())); } catch (UnknownHostException e) { theSuccess = false; Logger.getLogger("mmud").throwing("mmud.MudSocket", "run", e); Database.writeLog("root", e); setThreadCounters(); return; } catch (IOException e) { Logger.getLogger("mmud").throwing("mmud.MudSocket", "run", e); Database.writeLog("root", e); theSuccess = false; setThreadCounters(); return; } try { myOutputStream.println(Constants.IDENTITY); myOutputStream.println("Action (logon, mud, newchar):"); String myAction = readLine(myInputStream); Logger.getLogger("mmud").finest( "received from socket: [" + myAction + "]"); if (myAction.equals("logon")) { readLogonInfoFromSocket(myOutputStream, myInputStream); } else if (myAction.equals("mud")) { readCommandInfoFromSocket(myOutputStream, myInputStream); } else { throw new MudException( "unable to determine the action to take."); } String expectingOk; expectingOk = readLine(myInputStream); while (expectingOk != null && !expectingOk.equals("Ok")) { expectingOk = readLine(myInputStream); } myOutputStream.close(); try { myInputStream.close(); } catch (IOException e) { throw new MudException("unable to close input stream.", e); } try { theSocket.close(); } catch (IOException e) { throw new MudException("unable to close socket.", e); } Logger.getLogger("mmud").finer("closing connection..."); } catch (MudException e) { theSuccess = false; Logger.getLogger("mmud").throwing("mmud.MudSocket", "run", e); Database.writeLog("root", e); setThreadCounters(); return; } catch (OutOfMemoryError mem_error) { mem_error.printStackTrace(); Constants.logger.log(Level.WARNING, "exception {0}", mem_error); System.out.println("Exiting abnormally."); System.exit(1); return; } catch (RuntimeException e2) { Logger.getLogger("mmud").throwing("mmud.MudSocket", "run", e2); Database.writeLog("root", e2); theSuccess = false; setThreadCounters(); return; } setThreadCounters(); theSuccess = true; return; } private void setThreadCounters() { if (theThreadCountDone) { throw new RuntimeException( "Error! Already stepped thread counters."); } theThreadCountDone = true; Constants.decrementThreadsRunning(); Constants.incrementThreadsProcessed(); } /** * main function for logging into the game. * <P> * The following checks and tasks are performed: * <OL> * <LI>check if mud is enabled/disabled * <LI>validate input using perl reg exp. * <LI>check if user is banned * <LI>create new sessionpassword * </OL> * * @param aName * the name of the character to logon. * @param aPassword * password of the character to logon. * @param aAddress * the address of the person connecting. * @param aCookie * the sessionpassword to verify the current session. * @param aFrames * the user interface to use * <ul> * <li>0 = single window </li><li>1 = multiple windows (frames) </li><li>2 * = drupal</li> * </ul> * @return String containing the text to be sent to the user * @throws MudException * in case something goes wrong. */ private String enterMud(String aName, String aPassword, String aAddress, String aCookie, int aFrames) throws MudException { Logger.getLogger("mmud").finer( "aName=" + aName + ",aPassword=" + aPassword + ",aAddress=" + aAddress + ",aCookie=" + aCookie + ",aFrames=" + aFrames); if (Constants.debugOn(aName)) { Logger.getLogger("mmud_debug").finest( "enterMud aName=" + aName + ",aPassword=" + aPassword + ",aAddress=" + aAddress + ",aCookie=" + aCookie + ",aFrames=" + aFrames); } if ((aAddress == null) || ("".equals(aAddress.trim()))) { throw new MudException("address unknown"); } try { try { String myOfflineString = isOffline(); if (myOfflineString != null) { if (aFrames == 2) {return "Karchan is offline.";} return myOfflineString; } } catch (IOException e) { throw new MudException(e.getMessage(), e); } if (!aName.matches("[A-Z|_|a-z]{3,}")) { Logger.getLogger("mmud").info("invalid name " + aName); if (aFrames == 2) {return "Name contains illegal characters.";} return Constants.logoninputerrormessage; } if (aPassword.length() < 5) { Logger.getLogger("mmud") .info("password too short " + aPassword); if (aFrames == 2) {return "Password needs at least 5 characters.";} return Constants.logoninputerrormessage; } if (Database.isUserBanned(aName, aAddress)) { Logger.getLogger("mmud").info( "thrown " + Constants.USERBANNEDERROR); if (aFrames == 2) {return "Player is banned from playing.";} throw new MudException(Constants.USERBANNEDERROR); } if (!Database.existsUser(aName)) { if (aFrames == 2) {return "Player not found.";} throw new UserNotFoundException(); } User myUser = Persons.activateUser(aName, aPassword, aAddress, aCookie); myUser.generateSessionPassword(); myUser.setFrames(aFrames); myUser.writeMessage(Database.getLogonMessage()); if (MailDb.hasUserNewMail(myUser)) { myUser.writeMessage("You have new Mudmail!<P>\r\n"); } else { myUser.writeMessage("You have no new Mudmail...<P>\r\n"); } if (myUser.getGuild() != null) { if (myUser.getGuild().getLogonMessage() != null) { myUser.writeMessage(myUser.getGuild().getLogonMessage() + "<HR>"); } myUser.writeMessage(myUser.getGuild().getAlarmDescription() + "<HR>"); } StringBuffer returnStuff = new StringBuffer(); Database.writeLog(myUser.getName(), "entered game."); switch (myUser.getFrames()) { case 0: { returnStuff.append("sessionpassword=" + myUser.getSessionPassword() + "\n"); returnStuff.append(gameMain(myUser, "me has entered the game...")); break; } case 1: { returnStuff.append("sessionpassword=" + myUser.getSessionPassword() + "\n"); returnStuff .append("<FRAMESET ROWS=\"*,80\">\r\n" + " <FRAMESET COLS=\"*,180\">\r\n" + " <FRAME SRC="); returnStuff.append(myUser.getUrl("me+has+entered+the+game...")); returnStuff.append(" NAME=\"main\" border=0>\r\n" + " <FRAME SRC=" + Constants.leftframecgi + "?name=" + myUser.getName() + "&password=" + myUser.getPassword() + " NAME=\"leftframe\" scrolling=\"no\" border=0>\r\n"); returnStuff.append(" </FRAMESET>\r\n"); returnStuff.append(" <FRAME SRC=" + Constants.logonframecgi + "?name=" + myUser.getName() + "&password=" + myUser.getPassword() + " NAME=\"logon\" scrolling=\"no\" border=0>\r\n" + "</FRAMESET>\r\n"); break; } case 2: { returnStuff.append("sessionpassword=" + myUser.getSessionPassword() + "\n"); returnStuff.append("Ok"); break; } default: { Logger.getLogger("mmud").info( "thrown " + Constants.INVALIDFRAMEERROR); throw new InvalidFrameException(); } } if (Constants.debugOn(aName)) { Logger.getLogger("mmud_debug").finest("returns: [" + returnStuff + "]"); } Logger.getLogger("mmud").finest("returns: [" + returnStuff + "]"); return returnStuff.toString(); } catch (UserAlreadyActiveException e) { if (Constants.debugOn(aName)) { Logger.getLogger("mmud_debug").throwing(this.getClass().getName(), "enterMud", e); } // already active user wishes to relogin User user = (User) Persons.retrievePerson(aName); String returnStuff = reloginMud(user, aName, aPassword, aFrames); Database.writeLog(aName, "logonattempt, already playing."); Logger.getLogger("mmud").finest("returns: [" + returnStuff + "]"); return returnStuff; } catch (UserNotFoundException e) { if (Constants.debugOn(aName)) { Logger.getLogger("mmud_debug").throwing(this.getClass().getName(), "enterMud", e); } // new user String myString = ""; try { myString = Constants.readFile("newchar.html"); } catch (IOException f) { throw new MudException(f.getMessage()); } Database.writeLog(aName, "unknown character."); if (aFrames == 2) {return "Player not found.";} return myString; } catch (PersonException e) { if (Constants.debugOn(aName)) { Logger.getLogger("mmud_debug").throwing(this.getClass().getName(), "enterMud", e); } Logger.getLogger("mmud").log(Level.WARNING, "Exception detected!", e); return Database.getErrorMessage(e); } catch (MudException e) { if (Constants.debugOn(aName)) { Logger.getLogger("mmud_debug").throwing(this.getClass().getName(), "enterMud", e); } Logger.getLogger("mmud").log(Level.WARNING, "Exception detected!", e); return Database.getErrorMessage(e); } } /** * main function for executing a command in the game. * <P> * The following checks and tasks are performed: * <OL> * <LI>check if mud is enabled/disabled * <LI>validate input using perl reg exp. * <LI>check if user is banned * </OL> * * @param aName * the name of the character * @param aAddress * the address of the computer connecting * @param aCookie * the session password * @param aFrames * the user interface to use * <ul> * <li>0 = single window <li>1 = multiple windows (frames) <li>2 * = multiple windows with serverpush and javascript and all * sorts of other stuff. * </ul> * @param aCommand * the command to be executed * @return the text to be sent back to the user connecting */ private String executeMud(String aName, String aAddress, String aCookie, int aFrames, String aCommand) { if (Constants.debugOn(aName)) { Logger.getLogger("mmud_debug").finest("executeMud [" + aCommand + "]"); } Logger.getLogger("mmud").finer( "aName=" + aName + ",aAddress=" + aAddress + ",aCookie=" + aCookie + ",aFrames=" + aFrames + ",aCommand=" + aCommand); try { try { String myOfflineString = isOffline(); if (myOfflineString != null) { return myOfflineString; } } catch (IOException e) { throw new MudException(e.getMessage()); } if (!aName.matches("[A-Z|_|a-z]{3,}")) { Logger.getLogger("mmud").info("invalid name " + aName); return Constants.logoninputerrormessage; } if ((aCommand.toUpperCase().matches("(.)*<APPLET(.)*")) || (aCommand.toUpperCase().matches("(.)*ONLOAD(.)*")) || (aCommand.toUpperCase().matches("(.)*<SCRIPT(.)*")) || (aCommand.toUpperCase().matches("(.)*JAVA-SCRIPT(.)*")) || (aCommand.toUpperCase().matches("(.)*JAVASCRIPT(.)*")) || (aCommand.toUpperCase().matches("(.)*COMMANDFORM(.)*"))) { Logger.getLogger("mmud").info( "thrown: " + Constants.INVALIDCOMMANDERROR); throw new MudException(Constants.INVALIDCOMMANDERROR); } if (Database.isUserBanned(aName, aAddress)) { Logger.getLogger("mmud").info( "thrown: " + Constants.USERBANNEDERROR); throw new MudException(Constants.USERBANNEDERROR); } Person myChar = Persons.retrievePerson(aName); if (myChar == null) { Logger.getLogger("mmud").info( "thrown: " + Constants.USERNOTFOUNDERROR); throw new MudException(Constants.USERNOTFOUNDERROR); } if (!(myChar instanceof User)) { Logger.getLogger("mmud").info( "thrown: " + Constants.NOTAUSERERROR); throw new MudException(Constants.NOTAUSERERROR); } User myUser = (User) myChar; if (!myUser.verifySessionPassword(aCookie)) { Logger.getLogger("mmud").info( "thrown: " + Constants.PWDINCORRECTERROR); throw new MudException(Constants.PWDINCORRECTERROR); } myUser.setFrames(aFrames); StringBuffer returnStuff; if (aCommand.contains(" ; ") && (!aCommand.toLowerCase().startsWith("macro "))) { Logger.getLogger("mmud").finest("multiple commands"); if (Constants.debugOn(aName)) { Logger.getLogger("mmud_debug").logp(Level.FINEST, this.getClass().getName(), "executeMud", "multiple commands"); } returnStuff = new StringBuffer(runMultipleCommands(myUser, aCommand)); } else { String[] parsedCommand = aCommand.split(" "); Macro macro = null; if (parsedCommand.length <= 2 && (!aCommand.toLowerCase().startsWith("macro "))) { macro = Database.getMacro(myUser, parsedCommand[0]); } if (macro == null || macro.getContents() == null || macro.getContents().trim().equals("")) { // single command Logger.getLogger("mmud").finest("single command"); if (Constants.debugOn(aName)) { Logger.getLogger("mmud_debug").logp(Level.FINEST, this.getClass().getName(), "executeMud", "single command"); } returnStuff = new StringBuffer(gameMain(myUser, aCommand)); } else { Logger.getLogger("mmud").finest("macro"); if (Constants.debugOn(aName)) { Logger.getLogger("mmud_debug").logp(Level.FINEST, this.getClass().getName(), "executeMud", "macro"); } aCommand = macro.getContents(); // macro if (parsedCommand.length == 2) { aCommand = aCommand.replaceAll("%t", parsedCommand[1]); } returnStuff = new StringBuffer(runMultipleCommands(myUser, aCommand)); } } if (myUser.getFrames() != 2) { returnStuff.append("<HR><FONT Size=1><DIV ALIGN=right>"); returnStuff.append(Constants.mudcopyright); returnStuff.append("<DIV ALIGN=left></FONT>"); } Logger.getLogger("mmud").finest("returns: " + returnStuff); return returnStuff.toString(); } catch (PersonException e) { e.printStackTrace(); return Database.getErrorMessage(e); } catch (MudException e) { e.printStackTrace(); return Database.getErrorMessage(e); } } private String runMultipleCommands(User aUser, String aCommand) throws MudException { // multiple commands String[] splitted = aCommand.split(" ; "); String stuff = null; for (String str : splitted) { stuff = gameMain(aUser, str.trim()); } return stuff; } /** * dump a page to the user displaying a <I>relogging in</I> page. */ private String reloginMud(User aUser, String aName, String aPassword, int aFrames) throws MudException { Logger.getLogger("mmud").finer(""); StringBuffer returnStuff; aUser.generateSessionPassword(); aUser.setFrames(aFrames); returnStuff = new StringBuffer("sessionpassword=" + aUser.getSessionPassword() + "\n"); switch (aFrames) { case 0: { returnStuff .append("<H1>Already Active</H1><HR>\n"); returnStuff .append("You tried to start a session which is already in progress. You can't play \n"); returnStuff .append("two sessions at the same time! Please check below to try again. In case you \n"); returnStuff .append("accidently turned of your computerterminal or Netscape or Lynx without first \n"); returnStuff .append("having typed <B>QUIT</B> while you were in the MUD, you can reenter the game \n"); returnStuff .append("by using the second link. You have to sit at the same computer as you did \n"); returnStuff.append("when you logged in.<P>\n"); returnStuff .append("<A HREF=\"/karchan/enter.html\">Click here to\n"); returnStuff.append("retry</A><P>\n"); returnStuff .append("Do you wish to enter into the active character?<BR><UL><LI>"); returnStuff.append("<A HREF=\"" + Constants.mudcgi + "?command=me+entered+the+game+again...&name=" + aName + "&frames=" + (aFrames + 1) + "\">Yes</A>"); returnStuff .append("<LI><A HREF=\"/karchan/index.html\">No</A></UL>"); returnStuff.append("<HR><FONT Size=1><DIV ALIGN=right>" + Constants.mudcopyright); returnStuff.append("<DIV ALIGN=left><P>"); break; } case 1: { returnStuff.append("<FRAMESET ROWS=\"*,50\">\r\n"); returnStuff.append(" <FRAMESET COLS=\"*,180\">\r\n"); returnStuff .append(" <FRAME SRC=\"/karchan/already_active.html\" NAME=\"main\" border=0>\r\n"); returnStuff.append(" <FRAME SRC=\"" + Constants.leftframecgi + "?name=" + aName + "&password=" + aPassword + "\" NAME=\"leftframe\" scrolling=\"no\" border=0>\r\n"); returnStuff.append(" </FRAMESET>\r\n"); returnStuff.append(" <FRAME SRC=\"" + Constants.logonframecgi + "?name=" + aName + "&password=" + aPassword + "\" NAME=\"logon\" scrolling=\"no\" border=0>\r\n"); returnStuff.append("</FRAMESET>\r\n"); break; } case 2: { returnStuff.append("Player is already active."); break; } default: { if (Constants.debugOn(aName)) { Logger.getLogger("mmud_debug").logp(Level.FINEST, this.getClass().getName(), "reloginMud", "Invalid frame " + aFrames); } throw new MudException(Constants.INVALIDFRAMEERROR); } } Logger.getLogger("mmud").finer("returns: [" + returnStuff + "]"); if (Constants.debugOn(aName)) { Logger.getLogger("mmud_debug").logp(Level.FINEST, this.getClass().getName(), "reloginMud", "returns: [" + returnStuff + "]"); } return returnStuff.toString(); } /** * the main batch of the server. It parses and executes the command of the * user. * * @param aUser * User who wishes to execute a command. * @param aCommand * String containing the command entered * @return returns a string containing the answer. * @throws MudException * when something goes wrong. */ public String gameMain(User aUser, String aCommand) throws MudException { Logger.getLogger("mmud").finer( "aUser=" + aUser.getName() + ",aCommand=" + aCommand); StringBuffer returnStuff = new StringBuffer(""); String result = null; Database.writeCommandLog(aUser.getName(), aCommand); try { result = aUser.runCommand(aCommand); } catch (MudException e) { Database.writeLog(aUser.getName(), e); throw e; } if (result == null) { returnStuff.append(aUser.getRoom().getDescription(aUser)); if (aUser.getFrames() != 2) {returnStuff.append(aUser.printForm());} returnStuff.append(aUser.readLog()); } else { returnStuff.append(result); } return returnStuff.toString(); } }