⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 controller.java

📁 ErGo是一个很早的Java通用围棋服务器(IGS/NNGS)客户端程序。有全部源码和文档
💻 JAVA
📖 第 1 页 / 共 3 页
字号:
package ergo.server;

// $Id: Controller.java,v 1.6 1999/08/29 02:40:39 sigue Exp $

/*
 *  Copyright (C) 1999  Carl L. Gay and Antranig M. Basman.
 *  See the file copyright.txt, distributed with this software,
 *  for further information.
 */

import ergo.logic.*;
import ergo.util.*;
import ergo.ui.GoClient;    // interface to the UI
import ergo.ui.ServerPlayerContainer;
import ergo.ui.ServerGameContainer;
import ergo.ui.GameWindow;  // should get rid of this dependency.
import ergo.Ergo;

import java.awt.Color;

/**
 * The Controller class is responsible for all communication to and from
 * the server.  It will (optionally) scan the input from the server
 * for particular messages (e.g., moves) and handle them specially.
 * Any messages it doesn't handle specially will be printed on the
 * current output window (usually the main window).  Some messages
 * may be handled specially and ALSO be displayed to the user.
 * <p>
 * Any state that must be kept around due to the current brain-damaged
 * protocal should be kept in this class so that when the protocol is
 * improved I can mostly just replace this class.
 */
public class Controller extends Thread {

  // currently these access each other from here.
  private GoClient client;
  private ServerConnection conn;

  // It's questionable whether these two should be part of Controller.
  public ServerPlayerContainer spc;
  public ServerGameContainer sgc;

  private boolean readingHelpFile = false;
  private boolean loggedIn = false;
  private boolean lookForPlayerStats = false;

  public Controller(ServerConnection sc, GoClient client) {
    conn = sc;
    this.client = client;
  }


  /*
   * The run() method is called when a thread starts.
   * This method is the top-level loop of the Controller process.
   * It just sets up some general exception handlers and then
   * goes into a loop reading lines from the stream and dispatching
   * them to the appropriate routine.
   */
  public void run() {
    try {
      try {  // Don't let info window creation prevent me from connecting!
	spc = new ServerPlayerContainer(this, conn, client);
	sgc = new ServerGameContainer(this, conn, client);
      }
      catch (RuntimeException re) {
	Debug.backtrace(re);
      }
      while(true) {
	try {
	  if (!conn.isConnected()) {
	    Debug.println("Controller killing itself.");
	    stop();		// Nothing to do.  Kill myself.
	  }
	  else { // we are connected AMB.
	    String inputString = conn.readLine();
	    if (client.getRawMode())
	      client.displayString(inputString, null, true);

	    // Possibly enter login name and password...
	    if (conn.scanningForLogin()
		&& "Login:".regionMatches(true, 0, inputString, 0, 6)) {
	      client.displayString("Login: "); // space added
	      client.displayLoginDialog();
	    }
	    else if (conn.scanningForLogin()
		     && inputString.equalsIgnoreCase("password:")
		     && client.loginDialogExists()
		     && client.loginDialogDone()) {
	      // If we get here, client was not toggled on initially.
	      // If it was on, password entry is done in handlePrompt.
	      client.displayString(inputString);
	      if (!client.passwordSent()) {
		client.sendPassword();
	      }
	    }
	    else {  // inputString is not "login:" or "password:"
	      if (readingHelpFile) {
		if (inputString.startsWith("8 File")
		    || inputString.startsWith("25 File"))
		  readingHelpFile = false;
		else
		  client.displayString(inputString, null, false);
	      }
	      else {
		ParsedMessage pm = null;
		try {
		  // Protocol messages start with an int and a space.
		  pm = new ParsedMessage(inputString, "%i% ");
		  dispatchMessage(pm);
		}
		catch (ErgoException ge) {
		  // The input doesn't have a message type prepended, so
		  // client mode must be off.  Use some heuristics to see
		  // if we should toggle client on yet.  (Must be done
		  // after password is sent.)
		  //		  Debug.println("Unrec input "+inputString);
		  if (!loggedIn
		      && client.loginDialogExists()
		      // Removal of this seems to fix guest logon problem; check - AMB 0.8
		      //	      && client.getLoginDialog().passwordSent
		      && (inputString.startsWith(conn.shortName())
			  // #> is bogus.  user could set prompt to other.
			  || inputString.startsWith("#>")))
		    noteLoggedIn();
		  if (!isEmptyString(inputString))
		    client.displayString(inputString, null, false);
		} // end exception: no message type
	      } // end if expecting normal message
	    } // end if did not find login/pass
	  } // end if we are connected.
	} // end IO exception block
	catch (java.io.IOException e) {
	  System.err.println("I/O failed on the connection to server");
	  conn.close();
	}
	catch (Exception e) {
	  Debug.backtrace(e); }
      } // end loop forever
    } // end global exception block
    // Cleanup when the Controller process exits...
    finally {
      conn.close();
      spc.destroy();
      sgc.destroy();
    }
  }  // end method run

  public boolean isEmptyString(String s) {
    for (int i = 0; i < s.length(); i++)
      if (!Character.isWhitespace(s.charAt(i)))
	return false;
    return true;
  }

  // a common cliche
  private void skipwhite(ParsedMessage p) {
    try {
      p.continueParse(" "); }
    catch (ErgoException e) { }
  }    

  // Do stuff that should be done once, only at login time.
  // This is called only after login and password have been successfully
  // entered.
  private void noteLoggedIn () {
    if (!loggedIn) {
      client.displayWarning("My dog has fleas.");  // humor
      loggedIn = true;
      conn.send("toggle client on", false);	// turns off verbose too
      // IGS doesn't seem to handle two commands in rapid succession well.
      // The second one gets dropped sometimes, so wait 1/2 second.
      if (conn.server.isIGStype())
	try { Thread.sleep(500); } catch (InterruptedException e) {}
      conn.send("toggle quiet off", false);
      if (conn.server.isIGStype())
	try { Thread.sleep(500); } catch (InterruptedException e) {}
      conn.send("toggle verbose off", false);
      conn.stopScanningForLogin();
      if (conn.server.isNNGStype())
	conn.send("client 6", false); // +++ What? No Ergo client type?

      // Issue a stats command and begin waiting for the account name
      // (with correct case for NNGS) and rank.
      if (conn.server.isIGStype())
	try { Thread.sleep(500); } catch (InterruptedException e) {}
      lookForPlayerStats = true;
      conn.send("stats", false);
    }
  }

  /*
   * Just do the appropriate thing with this message.
   * This is embarassing.  Wish I had first class functions
   * and macros to make a more elegant solution...
   */

  // If you thought *that* was embarrassing.....
  // prevmess is currently used to detect when the server has come to
  // the end of the list of games, so we may refresh the window.
  int prevmess = -1;

  // countergames is used to counter redisplay of the games window in the case
  // the games command was issued by handleMoves() and not the user.
  // Perhaps you can think of a better mechanism for these -- AMB at 0.7
  private boolean countergames = false;

  private void dispatchMessage(ParsedMessage pm) throws ErgoException {
    int messageType = pm.intAt(0);
    switch (messageType) {
    case POSP.MOVE:
      handleMove(pm);
      break;
    case POSP.PROMPT:
      handlePrompt(pm);
      break;
    case POSP.INFO:
      handleInfo(pm);
      break;
    case POSP.GAMES:
      handleGames(pm);
      break;
    case POSP.SHOUT:
      handleShout(pm);
      break;
    case POSP.THIST:
    case POSP.HELP:
      readingHelpFile = !readingHelpFile;
      break;
    case POSP.UNDO:
      handleUndo(pm);
      break;
    case POSP.VERSION:
      // We get here only if client is already toggled on upon login.
      noteLoggedIn();
      break;
    case POSP.STATUS:
    case POSP.SCORE_M:
      handleScore(pm);
      break;
    case POSP.YELL:
      handleYell(pm);
      break;
    case POSP.KIBITZ:
      handleKibitz(pm);
      break;
    case POSP.TELL:
      handleTell(pm);
      break;
    case POSP.ERROR:
      client.displayWarning(pm.rest());
      // IGS reports bad passwords this way.
      // Others do it by prompting for Login: again...
      if ("Invalid password".regionMatches(true, 0, pm.rest(), 0, 15)) {
	client.displayString("Login: "); // space added
	client.displayLoginDialog();
      }
      break;
    case POSP.WHO:
      handleWho(pm);
      break;
    case POSP.DOWN:
      client.displayWarning(pm.rest());
      break;
    case POSP.SAY:
      handleSay(pm);
      break;
    case POSP.BEEP:
      // +++ Should have a separate option flag for whether to beep for
      //     tells or not, and for moves or not.
      client.beep();
      break;
    case POSP.UNKNOWN:
    case POSP.AUTOMAT:
    case POSP.AUTOASK:
    case POSP.CHOICES:
    case POSP.CLIVRFY:
    case POSP.BOARD:
    case POSP.FIL:
    case POSP.LAST:
    case POSP.LOAD:
    case POSP.LOOK_M:
    case POSP.MESSAGE:
    case POSP.OBSERVE:
    case POSP.REFRESH:
    case POSP.SAVED:
    case POSP.SGF_M:
    case POSP.SHOW:
    case POSP.STORED:
    case POSP.TEACH:
    case POSP.DOT:
    case POSP.TIM:
    case POSP.TRANS:
    case POSP.TTT_BOARD:
    case POSP.USER:
    default:
      // Don't recognize input, so just display it to the user.
      client.displayString(pm.rest());
      break;
    }

    // Deal with exceptional situations due to broken protocol.
    // i) Detect if we have seen the last game from the "games" command.
    if (prevmess == POSP.GAMES && messageType != POSP.GAMES) {
      if (countergames) {
	countergames = false;
      }
      else {
        spc.update(); // applyreflect meddles with this quietly also.
        sgc.update();
        sgc.show(); 
	//        client.registrar.registerWindowsMenuCommand(sgc.sgcw);
      }
    }

    // ii) Detect if we have seen anything other than a move after the
    // end of a "moves" moves command.
    if (waitingMoveNumber > -1 && seenmoveone && messageType != POSP.MOVE) {
      visifyPendingGame();
    }
    prevmess = messageType;
  }		

 /* Who looks like this: 
  * 27  Info       Name       Idle   Rank |  Info       Name       Idle   Rank
  * 27   X --   -- anomaly    13m     NR  |   X --   -- betsy      43s     NR
  * 27     --   -- biogeek    11s    19k* |     --    6 ManyFaces  19s    11k*
  * 27  S  --   -- Bosmon     47s    10k* |     --    6 chudnall   52s    10k*
  * 27     --    2 cutter      1m     8k* |  Q  --    4 GriGri     11s     5k*
  * 27   X --   -- HM          2m     4k* |   X  7   -- wms        26s     3k*
  * 27  Q!  7   -- arbor       6s     1k* |   X  7   -- F22        10m     1k*
  * 27  S  --    7 flyaway     1m     1k* |     --    7 OLive       2s     1k*
  * 27   X --   -- daveg       2m     2d* |
  * 27                 ********  27 Players 5 Total Games ********
  */
  int whostate = 0; // 0 = no state, 1 = middle of who
  private void handleWho(ParsedMessage p) {
    if (p.continueParseQuietly(" Info Name Idle Rank | Info Name Idle Rank ")
	!= null) {
      //Debug.println("Got header");
      whostate = 1;
      spc.clear();
      return;
    }
    else if (p.continueParseQuietly(" ******** %i Players %i Total Games %s ")
	     != null) { 
      spc.update();
      spc.show();
      whostate = 0;
    }
    else {
      // it's some actual honest-to-goodness data...
      // we will parse it non-freeform...
      try {
        int base = 0;
        while (true) {
	  //          System.out.println(p.message());
	  //	            System.out.println(p.rest());
          p.continueParse("% %c%c %s %s %s %s %s ");
	  //          System.out.println("1: "+p.matchAt(base+1).toString());
	  //          System.out.println("2: "+p.matchAt(base+2).toString());
          String amalgflags
	    = new String(((Character)p.matchAt(base + 1)).toString() +  
			 ((Character)p.matchAt(base + 2)).toString());
	  // This block will protect the rest of the players from
          // being trashed by one duff one, and also give enough
	  // information on what sorts of duffness we meet.
          try {
	    spc.apply(amalgflags, p.stringAt(base + 3), p.stringAt(base + 4),
		      p.stringAt(base + 5), p.stringAt(base + 6),
		      p.stringAt(base + 7), true);
	  }
          catch (Exception e) {
            if (! (e instanceof ParseException)) {
              Debug.println("Failed to parse " + p.message());
              Debug.backtrace(e);
	    }
	  }
          p.continueParse("|% ");
          base += 7;
	} // end while for one line
      }
      catch (ParseException e) {
	//	        System.out.println(e);
      }
    } // end if it is data
  } // end handlewho

  /*
   * Kibitz messages look like this:
   *   11 Kibitz MZ [ 8k ]: Game tonny vs olo [5]
   *   11    Hi, test
   */

  /*
Ergo.ParseException: No match.  Expected ']'.  Got 'G'.
        at Ergo.ParsedMessage.parse(util.java:393)
        at Ergo.ParsedMessage.continueParse(util.java:417)
        at Ergo.Controller.handleKibitz(Controller.java:290)
        at Ergo.Controller.dispatchMessage(Controller.java:241)
        at Ergo.Controller.run(Controller.java:145)
        at java.lang.Thread.run(Thread.java)
Ergo.ParseException: No match.  Expected 'K'.  Got 't'.
        at Ergo.ParsedMessage.parse(util.java:393)
        at Ergo.ParsedMessage.continueParse(util.java:417)
        at Ergo.Controller.handleKibitz(Controller.java:290)
        at Ergo.Controller.dispatchMessage(Controller.java:241)
        at Ergo.Controller.run(Controller.java:145)
        at java.lang.Thread.run(Thread.java)
*/

  private ParsedMessage previousKibitz;
  private void handleKibitz (ParsedMessage p) {
    //displayString(p.rest());
    try {
      p.continueParse(" Kibitz %a [ %w ]: Game %a vs %a [ %i ]");
      previousKibitz = p;
    }

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -