Logo and LinkJagoClient Source Documentation

This file contains the documentation for the sources of the JagoClient program. I have made the sources available in the hope that some experienced programmers will help me develop this program. Clearly, you will need some help to fight your way through the over 150 classes that form JagoClient. This file should give you a good starting point.

Go and MainFrame classes

The main class is the go applet. It is contained in a MainFrame, which is derived from Frame (CloseFrame to be precise). The main() method in Go creates this frame and places a copy of the Go applet into this frame. If JagoClient is started as an applet, a MainFrame is created by the JagoClient applet class.

Due to this construction the menu (which is placed in the MainFrame) and the buttons (which are placed in the Go applet) have to be interpreted by different classes.

The go applet contains a CardPanel. This panel uses the CardLayout to present two different dialogs to the user, the server connections and the partner connections. It presents two buttons to let the user switch between the both.

The server connections, as well as the partner connections panel contain a list item and several buttons (add, new etc.). These buttons are interpreted by the go class.

If you are studying the menu, you will notice that many of the menu options simply set global parameters, using the Global class. The details are explained there. Some open dialogs to get values, like the server port, which is stored by this dialog into the Global class. The details are straightforward.

EditConnection and EditPartner classes

These classes are dialogs, which let the user edit server or partner connections. Like all other frames and dialogs, they note their sizes in Global variables, so that they are always opened in the same size.

The constructor is passed a class, which represents the connection. The dialog changes the values of the connection directly.

I use a GridLayout with two columns for these dialogs, presenting the variable names in the first column and the editable values in the second.

When the user leaves the dialog, they call update methods in the Go applet, so that the lists there are updated.

Connect class

This is a Thread, which tries to connect to the server. It keeps a record in the connection that it is trying to connect. This keeps the user from impatiently clicking the connect button several times.

The connection is established either via a relay demon or directly. The connection code is contained in the ConnectionFrame class and returns the connection state. If the server cannot connect, a message is displayed for 10 seconds, then the thread dies. If it can connect, the connection frame will be displayed.

ConnectionFrame class

This is the main class for server connections. It contains the output window and the input line. Furthermore it has a menu and several buttons. It handles all the related events, and it does handle function key presses, which are translated by the Global class and sent to the server, if a text is set for them.

The input from the server is actually handled by the IgsStream class (with instance In) together with the ReceiveThread class. The output is done directly when the user pressed return in the output line, or by several classes which have to speak to the server. For this purpose, these classes are passed the Out object, which is a PrintStream to the server.

IgsStream and ReceiveThread classes

IgsStream is a DataInputStream, which is connected to the server. The program must of course be able to wait for server input and to react to user input at the same time. Thus a separate thread is started to wait for user input. This is the ReceiveThread.

The IgsStream sends all input it cannot handle to the ReceiveThread. The ReceiveThread is also responsible for the automatic login. There is one difficulty here. The server prompts for login with an incomplete line. But lines may be incomplete also because of net lag. Thus the IgsStream reports a state flag to the ReceiveThread, wether the line was incomplete or not. The ReceiveThread interprets the line text for the login and the password prompt. This is a fragile construction, which may not work when the server changes its login prompt.

The ReceiveThread also toggles to client mode, if it receives a command prompt at line start.

To understand the following, you need to know that IGS sends command numbers in client mode. Each line will send start with an ascii-readable number, a blank, optional further numbers and finally the ascii-readable command itself.

Let us now explain, how the IgsStream interprets its input. First of all, it can only interpret input in full lines. It will discard non-ascii characters, unless it is in proxy mode, which means it has to take care of Telnet commands (it will deny all telnet commands to the server). It then extracts the command number and looks for a Distributor for this number. To this end, it has a list of Distributors. For some distributors, it checks for additional information (e.g. a game number).

The IgsStream interpreter does even create distributors. E.g., if a move for a game is received, it will create a PlayDistributor, unless there is already a game for this move (this happens after a load command by the user or his opponent).

A special feature is that some information commands (number 21) are interpreted by the distributor. E.g., it checks for information for games (starting with "{Game") and for match requests.

The IgsStream has methods for Distributors to remove themselves from the list.

It should be emphasized that the IGS protocol is not very rigid. Furthermore, there is no good documentation available. Many things had to be studied from trial and error, using the Dump output. And it is subject to changes. The problem command of NNGS made changes in the client code necessary. Nevertheless noone found it necessary to give detailed information about it to client writers.

Distributors

This is the parent class of several classes. The name "distributor" is not well chosen, for the distribution process is done by IgsStream itself. The distributor merely handles its input or passes it to its client. The distributor knows about its connection frame, so that clients can write to the output window directly. Furthermore, some distributors ask the connection frame about stats of certain flags ("show information" etc.).

The following distributors exist

There are also secondary distributors, which are created by other distributors. E.g., the PlayDistributor creates a PlayerSizeDistributor for the purpose to obtain the board size of a specific game.

GamesDistributor and WhoDistributor

These work in a much similar fashion. Once the user has pressed the "who" button, a WhoDistributor will be established. This distributors opens a WhoFrame, sends the "who" command to the server and waits for results. IgsStream will send the results in a consecutive stream, finished by a call to the allsend method. WhoFrame will then interpret the lines and display the information.

WhoFrame and GamesFrame handle their buttons, which may send commands to the server, or open dialogs or both.

It should be noted that the ConnectionFrame can look up if there is already a WhoFrame at work. If there is, it will call the refresh method of this frame rather than creating a new one.

PeekDistributor, StatusDistributor, PlayDistributor and ObserverDistributor

These actually display games on the server. They get the moves from the server via IgsStream. Each move comes in two lines, the first one containing the game number. IgsStream uses this information to choose the correct distributor. Of course, StatusDistributor will not receive any moves. It will remove itself after it has completely received the games status.

All these distributors open other help classes to do the actual interpretation of the input and these classes open the boards.

IGS does not send the sizes of boards for certain games. The only method I could find is to use the status command to get the game size. E.g., the PlayDistributor opens a PlayerSizeDistributor to get the game board size, before it actually starts to interpret or send moves. This is a nuisance, which is caused by the insufficient server protocol (or missing documentation). A consequence is, that some moves may be missed and the distributor must ask the server to resend the game.

Another problems is the undo of moves. In this case, the serve will simply resend a previous move. The above distributors take care of this.

MessageDistributor etc.

This distributor is automatically started by ConnectionFrame. It will send messages from users to a message window. This window contains a list of messages received along with the name of the sender. The user can reply to these messages by selecting a message, or reply to the last message without selecting a message.

The InformationDistributor class uses the global filter to see, if it is supposed to display the information in question.

GoFrame and ConnectedGoFrame classes

The GoFrame class contains the board, the comment and send areas, several buttons and a menu. It uses a GridBagLayout with the help of a constrain method, which creates the constrains.

One problem is to keep the area of the board approximately square. Of course, the board windows has to report a preferred size, which is a square. But this is not enough, since the board dimensions have not yet changed, when the layout manager calls the preferred size. Thus the paint method of the board checks for a changed size and calls the layout manager once more to improve the layout. I am still hoping that this will never lead to a cyclic resizing in any Java version.

Note, that the ConnectedGoFrame sets up the menu and layout itself. It uses a slightly different layout and different menu entries.

The buttons and the menu entries are interpreted by the GoFrame class. Mouse events, which occur inside the board are interpreted by the Board class, however.

Board class

This is a class, which is definitely to fat for object oriented design. However, I never came around to rearrange it.

The board class contains the game tree for this board. I will describe the organization of the game tree in the next paragraph.

It also knows about the comment window, the next move label and the coordinate label, which are part of the GoFrame. So it can update these items according to the board position.

The board class communicates with its GoFrame to send and receive moves from the server, or to get Kibitz comments. The board class also knows its distributor and will communicate with it. E.g. the refresh command is handled this way.

The handling of mouse events is dependent on the state (playing, setting or marking etc.). The Board class calls methods of the GoFrame to assure that the correct state is marked in the menu. It also disables navigation buttons in the GoFrame, using a method of GoFrame.

The Game Tree

The game tree is an instance of the Tree class. Its nodes contain instances of the Node class. The Node class contains a list of actions, which contain the changes at this node. The Node does also contain a flag, if it belongs to the main variation, the move number and list of changes in board position.

Each Action is a SGF action tag together with its parameters. However, the smart go format has shortcomings. E.g., it does not denote prisoners, nor the color of removed stones. Thus undoing the action becomes difficult, but is necessary when the game tree is traversed upward. Thus I note the board changes in each move in the list of Changes. The advantage of using SGF is that JagoClient will read and write unknown tags.

Position class

This class contains the board position. It is a square of instances of the Field class. Each Field contains its state (-1 is white, 0 is empty, 1 is black). It does also contain a marked flag, which help in marking connected groups or territory. Furthermore, it may contains labels and a variation tree, which is needed to mark the variations on the board.

The position class has methods to mark groups of the same color, or to search if a group has liberties. This is done recursively, which never provided a difficulty and makes the code elegant.

The True Color Board

The true color board is generated by a separate thread. Usually, this thread is started at program start and tries to paint a board based on the last known size.

PartnerFrame class

In many respects this class works like the ConnectionFrame class. It starts a thread, which waits for input from the partner asynchronously. It establishes a PrintStream to the partner.

However, I simplified the protocol. In general, a command will start with "@@", and anything else is interpreted as chat.

As an example the question for a game is sent to the partner with a "@@game ..." command, and the partner may decline it or accept it. Decline is sent by "@@-game" and acceptance as "@@!game". Likewise a move in a game is sent to the partner with a time stamp, and accepted by the partner. This way, partner connections can accurately time the move and will not count network traffic.

Server class

The Server class is a thread, which may be started by the user. It waits for incoming connections. If someone tries to connection to it and it is not busy, it will open a PartnerFrame class.

If the server starts, it will send this fact to all partner connections, which are defined as private, local or public. It gets back a list of open partners from these connections and stores them. The same applies, when the server closes. The server sends Datagrams, using the DatagramMessage class, for this purpose.

PartnerServerThread class

This thread watches out for datagrams, which are used to communicate in a local network of partners. It is started with the server, but never stops. Datagrams may inform the server of closing or opening of other servers. The class will send datagrams to inform other servers about open partners it knows of.

The Relay Demon

The relay demon is used to connect from WWW sites to go servers or go partners. This is necessary, if JagoClient is started as an applet, which has been download from a WWW site. Applet security inhibits direct connections. Of course, the relay class must be started on the server. You should use a command like

nohup Relay 32 6971 &

for this purpose, where 32 is the maximal number of connections the class can handle, and 6971 is the relay port.

The Relay class starts a RelayServer, which waits for connections. This thread reads the internet address of the server and its port as first two lines of each connection and opens a connection to the server. It then starts two threads for input and output.

Global class

The Global class contains static members to keep global information.

One of the most important data is the list of parameters. This list is stored in go.cfg at program end, and read from this file at program start. Parameters are always strings, but may be interpreted as boolean, integer or string. This is determined by the way the parameter is set and asked.

The Global class does also contain the filters for the server connection, a global invisible frame, which is the parent frame for many windows and dialogs, a list of function keys, the home directory, and many other things.

CardPanel class

This contains a Panel with a CardLayout manager. It may be used to hold several other panels with only one of them visible. In the south, it contains a panel of buttons to choose between the available panels.

Tree and List class

Lists are forward and backward chained lists. Each node has a field of type Object, which is used to store the content of the node.

Trees are nodes, which have a content and a list of children. The contents of the nodes of this list are again trees.