📄 controller.java
字号:
catch (ParseException e) {
if (previousKibitz == null)
Debug.backtrace(e);
else {
try {
String kibitzer = previousKibitz.stringAt(1);
String rank = previousKibitz.stringAt(2);
String white = previousKibitz.stringAt(3);
String black = previousKibitz.stringAt(4);
int game = previousKibitz.intAt(5);
skipwhite(p);
client.displayKibitz(white, black, game, kibitzer, rank, p.rest());
}
catch (ParseException foo) {
Debug.backtrace(foo);
}
}
previousKibitz = null; // done with it.
}
}
// SAY = 19
// Say messages looks like tells:
// 19 *bug*: hi there
// Add them as kibitzes to the game being played.
private void handleSay (ParsedMessage p) {
client.displaySay(p.rest());
}
// INFO = 9
// Messages that should have been defined in the protocol but aren't
// are caught here. Bleuch.
//
private void handleInfo (ParsedMessage p) {
ParsedMessage pmess = null;
String rest = p.rest();
client.displayString(rest);
// Attempt to check for more common messages first, to avoid
// attempting to parse the message multiple times if not necessary.
// WING sends "9 \u0007" as beep whereas NNGS sends "2 \u0007"
if (rest.startsWith("\u0007")) {
client.beep();
return;
}
// Removing a group during scoring?
if (previousPrompt == 7) { // 7 = scoring mode
try {
ParsedMessage p1 = new ParsedMessage(rest, " Removing @ %a");
client.removeGroupAt(p1.stringAt(0));
}
catch (ParseException e1) {}
}
// Has the komi changed? Protocol for this is fukt:
// 9 Komi is now set to 1.5
// 9 Set the komi to 1.5
// 9 Komi set to 1.5 in match 3 // new on NNGS
try {
ParsedMessage mkomi = null;
try {
mkomi = new ParsedMessage(rest, " Set the komi to %f");
} catch (ParseException e1) {}
if (mkomi == null)
try {
mkomi = new ParsedMessage(rest, " Komi is now set to %f");
} catch (ParseException e2) {}
if (mkomi == null) {
// New: at my request, NNGS now sends the komi changes to observers.
mkomi = new ParsedMessage(rest, " Komi set to %f in match %i");
double komi = mkomi.doubleAt(0);
int match = mkomi.intAt(1);
client.setKomi(komi, match);
return;
}
else {
client.setKomi(mkomi.doubleAt(0), -1);
return; // the message has been handled.
}
} catch (ParseException e2) {}
// Match request?
// 9 Match[19x19] in 1 minute requested with jkong as White.
// 9 Use <match jkong B 19 1 10> or <decline jkong> to respond.
// *wms*: CLIENT: <cgoban 1.8.0> match wms wants handicap 5, komi 1.5
try {
// The last frob in the "regexp" is an int so it won't include
// the ">" character.
ParsedMessage mm
= new ParsedMessage(rest, " Use <match %s %s %s %s %i");
client.displayMatchRequest(mm.stringAt(0), // requester
mm.stringAt(1), // mycolor
mm.stringAt(2), // size
mm.stringAt(3), // initial time
((Integer) mm.matchAt(4)).toString()); // byotime
return;
}
catch (ParseException e3) {}
// 9 2 minutes were added to your opponents clock
// 9 Your opponent has added 2 minutes to your clock.
//
/*
try {
ParsedMessage mm = new ParsedMessage(rest, " Your opponent has added %i minutes");
client.addTime(GoClient.LocalPlayer, mm.intAt(0));
}
catch (ParseException e3) {
try {
ParsedMessage mm = new ParsedMessage(rest, " %i minutes were added");
client.addTime(GoClient.RemotePlayer, mm.intAt(0));
}
catch (ParseException e213321) {} // why force me to give it a goddamn name?
}
*/
// 9 Removing game 8 from observation list.
try {
ParsedMessage rem = new ParsedMessage(rest, " Removing game %i from ob");
client.unobserveGame(rem.intAt(0));
return;
}
catch (ParseException e4) {}
String fteachString = "This is now a FREE, UNRATED teaching game.";
if (rest.regionMatches(true, 0, fteachString, 0, fteachString.length())) {
client.makeGameFree(-1); // -1 means active game
}
if (lookForPlayerStats) {
// Look for the player's name and rank. The rank is needed for
// for when the player kibitzes, and the name is checked to
// correct possible character case problems on NNGS.
try {
ParsedMessage message = new ParsedMessage(rest, " Player: %s");
String acct = message.stringAt(0);
// Maybe correct the character case of the username on NNGS...
if (client.getAccountName().equalsIgnoreCase(acct))
client.setAccountName(acct);
return;
}
catch (ParseException e5) {
try {
ParsedMessage message = new ParsedMessage(rest, " Rating: %s ");
client.setAccountRank(message.stringAt(0));
lookForPlayerStats = false;
return;
}
catch (ParseException e6) {
try {
ParsedMessage message = new ParsedMessage(rest, " Rank: %s ");
client.setAccountRank(message.stringAt(0));
lookForPlayerStats = false;
return;
}
catch (ParseException e7) {}
}
}
}
// Ratings updates.
// 9 Ratings update: You are still 1d* (3003, 18 rated games)
// 9 Ratings update: You are now 1d* (3003 (were 3002), 18 rated games)
// We only pay attention to the latter message (i.e., changes in rating)
try {
ParsedMessage rat
= new ParsedMessage(rest, " Ratings update: You are now %s");
client.setAccountRank(rat.stringAt(0));
Debug.println("Rank set to " + rat.stringAt(0));
return;
}
catch (ParseException e8) {}
// UNDO during scoring mode.
// On NNGS this is handled like a normal UNDO of the last PASS move,
// and then the PASS move is resent. The following is for IGS only.
if (rest.startsWith
("Board is restored to what it was when you started scoring"))
client.getGameBeingPlayed().undoDuringServerScoring();
} // end method handleInfo
// Deal with * at end of name for "replayed games" on IGS
/*
private String trimmedName (String name) {
int len = name.length();
return ((len > 0 && name.charAt(len - 1) == '*')
? name.substring(0, len - 1) : name);
}
*/
/*
* Handle special tells.
*/
private void handleTell (ParsedMessage pm) {
String rest = pm.rest();
client.displayTell(rest);
/* removed during switch from TerminalWindow to GoClient.
was never fully implemented anyway.
if (currentMatchDialog != null) {
// Handle tells of the following form:
// *wms*: CLIENT: <cgoban 1.8.0> match wms wants handicap 5, komi 1.5
int space = rest.indexOf(' ', 2);
if (space != -1 && rest.startsWith("CLIENT: ", space + 1)) {
try {
pm.continueParse(" %s CLIENT: <%s %s match %s wants handicap %i, komi %s");
currentMatchDialog.setHandicap(pm.intAt(5));
currentMatchDialog.setKomi(pm.stringAt(6));
currentMatchDialog = null;
}
catch (ParseException e1) {
Debug.backtrace(e1);
}
}
}
*/
}
/*
* Handle a MOVE message. The fact that this message is broken
* into two parts makes it much harder to deal with. I could
* handle it by just calling conn.readLine again after the first
* part is received, but then none of the error checking that
* happens in the main loop would pertain. So instead I use a bunch
* of variable to hold some of the info I need from the first part.
* It's beyond me why they didn't make this a one line message in
* the so-called protocol.
*
* Format:
* 15 Game 1 I: whitename (2 591 24) vs blackname (4 222 2)
* ^^^^^^^^^^ ^^^^^^^^^
* (captured timeleft byo-stones)
* byo-stones = -1 means not in byo-yomi
* Game 79 I: xiaoren* (0 0 -1) vs NaiWei* (0 72 -1)
* 15 83(B): A1
* 15 83(B): handicap 4
* 15 83(B): pass
*
* Note that in the case of the "moves" command the header line
* is only sent once, at the beginning. - AMB.
*/
// Result of most recent "Game ..." move message.
private ParsedMessage handleMovesMessage;
// Window of game corresponding to handleMovesMessage (above).
// useful scope of this is between header and body of handleMove().
private GameWindow handleMovesWindow;
// Number of game that handleGames() should look for and add
// to the observation list. Normally -1, meaning none.
// scope of this is *between* handleMoves and the handleGames in question.
private int handleMovesGame = -1;
// this little block deals with waiting for the end of the "moves"
// command before we show the board.
// all these three are set simultaneously when handleGames receives
// the game with number equal to handleMovesGame.
private int waitingMoveNumber = -1;
private boolean seenmoveone = false;
private GameWindow waitingGameWindow = null;
// all three are *only* unset by this function, currently with 3 calls
// in this class.
private void visifyPendingGame() {
if (waitingGameWindow == null) return;
waitingMoveNumber = -1;
seenmoveone = false;
waitingGameWindow.game.ss.setQuiet(false);
}
// Note, importantly, that the reaction of the server on beginning
// to observe a game after sending the pointless observe message, is
// to send an empty moves header. This hits the first branch of the
// if here, which triggers a games command for just that game within
// handleGames.... this then rekicks back a moves command, and
// control returns here to deal with the rest of the moves.....
// What a protocol.....! AMB, light dawning at 0.6.
protected void handleMove (ParsedMessage p) throws ErgoException {
skipwhite(p);
String input = p.rest();
if(input.regionMatches(true, 0, "Game", 0, 4)) {
handleMovesMessage = p;
try {
p.continueParse("Game %i %c: %s (%i %i %i) vs %s (%i %i %i)");
int gameNumber = p.intAt(1);
String white = p.stringAt(3);
String black = p.stringAt(7);
handleMovesWindow = client.getGameWindowByName(white, black,
gameNumber);
if (handleMovesWindow == null) {
// There's no game window for this game yet, meaning it was
// just added to the observation list. Save the game number
// and issue a "games n" command so that handleMoves will
// create a window when it next parses a message for this game.
// The reason for this roundabout method is that there isn't
// enough info in any one message sent by the server that has
// all the info needed to create a game window (specifically,
// the game size can only be found in the "games" command's
// output).
// Bug Fix - if we are already pending the results of the "games"
// command, do not send "games" for this or any other game until
// they arrive. AMB at 0.8
if (handleMovesGame == -1) {
handleMovesGame = gameNumber;
// System.out.println("Received " + input + ";
// hMG was -1, set to "+handleMovesGame);
conn.send("games " + gameNumber, false);
}
return;
}
else { // there exists a window for this move.
// if (gameNumber == handleMovesGame)
// handleMovesGame = -1; // I *believe* this
// line can in fact go completely, since handleGames()
// will set hmG to -1 itself when it has successfully
// made the window. AMB of 0.8.
// if we receive *any* move header after move one seen of pending game,
// must show window.
if (waitingMoveNumber > -1 && seenmoveone) {
visifyPendingGame();
}
handleMovesWindow.
updateTimers(p.intAt(4), // wcapt
p.intAt(5), // wtime
p.intAt(6), // wstones
p.intAt(8), // bcapt
p.intAt(9), // btime
p.intAt(10)); // bstones
}
}
catch (ErgoException woof) {
Debug.backtrace(woof);
handleMovesMessage = null;
}
}
else { // this is the second branch of the Moves message....
if (handleMovesMessage == null || handleMovesWindow == null) {
return; }
// Format of input is "15 83(B): A1", "15 83(B): handicap 4",
// or "15 83(B): Pass"
try {
p.continueParse("%i(%c): %a");
int moveNumber = p.intAt(1);
char player = ((Character) p.matchAt(2)).charValue();
int color = (player == 'B') ? Move.BLACK : Move.WHITE;
int field = (player == 'B') ? 5 : 9;
int timeleft = handleMovesMessage.intAt(field);
Move parent = handleMovesWindow.game.finalServerMove();
if (moveNumber != parent.moveNumber() + 1) {
//Debug.println("handlemove(): Move received out of order. Parent = "
// + parentNum + ". New = " + moveNumber
// + ". Move message = " + p.message());
// This is almost certainly due to an UNDO (which resends the
// previous move) or an auto-re-observer of the game. Just
// ensure that the game knows it's now active, not adjourned.
// +++ Should really pass the correct times to ensureActive.
//
// NB - The other important case is where current moves from the game
// just becoming observed arrive before the output of the "moves"
// command from handleGames. This code solves that bug, and also
// the "slow history drawing" bug..... AMB at 0.7
handleMovesWindow.ensureActive();
return;
}
// Are we waiting for a moves command to finish for this game?
if (waitingMoveNumber > -1 && handleMovesWindow == waitingGameWindow) {
if (moveNumber == 1) {
seenmoveone = true;
}
if (moveNumber >= waitingMoveNumber - 1) {
// Have finished the accelerated portion of an observed game. AMB
// Show the position now...
visifyPendingGame();
}
}
Move move;
String string = p.stringAt(3);
if (string.regionMatches(true, 0, "ha", 0, 2)) { // handicap move?
p.continueParse(" %i");
int stones = p.intAt(4);
move = new HandicapMove(parent, timeleft, moveNumber, false,
stones, handleMovesWindow.game.size()); }
else if (string.regionMatches(true, 0, "pa", 0, 2)) { // pass move?
move = new PassMove(parent, timeleft, moveNumber, color, false);
}
else {
Position pos = new Position(string, handleMovesWindow.game.size());
move = new StoneMove(parent, timeleft, moveNumber, color,
false, pos);
}
handleMovesWindow.placeMove(move, true);
handleMovesWindow.game.ss.emit();
}
catch (ParseException e) {
Debug.backtrace(e);
}
}
} // end method handleMove
/*
* GAMES
*
* Message format:
* [##] white name [ rk ] black name [ rk ] (Move size H Komi BY FR) (###)
* [30] BUDDYM [ 1k*] vs. guen [ 2k*] ( 0 19 0 0.5 8 I) ( 0)
*
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -