?? controller.java
字號:
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 + -