Breakout Journal

2:09pm EST, 1 Mar 2005
Tuesday

It may be a while until the next update. I haven't felt like working on Breakout lately.

10:34pm EST, 25 Feb 2005
Friday

Optimized the painting code. Instead of painting the entire screen with every update from the server, it only paints the smallest rectangle that encompasses all the areas of the screen that need to be painted.

Also wrestled with a glitch caused by some code that called repaint() from a thread other than the event-dispatching thread. Apparently that's a no-no. It was causing parts of the paddles not to be erased as they moved. That's fixed now.

There's another minor bug that's been bothering me, but I haven't been able to reliably reproduce it. Every once in a while, when the players go back to the lobby from the game, one of the player names is duplicated in the name list. I'll have to review that code step-by-step to see what's going on. It seems like some kind of race condition going on since it happens so infrequently.

9:59pm EST, 24 Feb 2005
Thursday

Check it out :)

(Compare to the concept image.) The players can move the paddles left and right with the mouse.

I ran into a really weird problem while working on the paddle movement, and I'm still not quite sure what the cause was. I had an object listening for mouse events on the game screen, and every time the mouse pointer moved, it sent a PaddlePositionMessage to the server. It basically worked, but if you kept moving the mouse around for several seconds without stopping, then the paddle would start to lag behind the mouse, and the longer you kept it up the greater the lag became.

I put console print statements all over the client and server trying to find where the messages were getting backed up, but nothing showed any delay between receiving a message and processing it. I finally ran two tcpdumps side-by-side, one on the client and one on the server, while moving a paddle around, and found that the problem was actually happening somewhere in the network. On the local tcpdump, I'd see UDP datagrams go out in real time as I moved the mouse around, but on the server tcpdump I'd see that strange increasing lag.

My guess is that the network doesn't handle tiny datagrams coming across at a high rate very well. Working from that assumption, I changed the mouse listener so that, instead of firing off a message every time the mouse moves, it just updates a local variable. Then I created a timer task that runs roughly every thirtieth of a second and polls that variable. If it's different than it was the last time it checked, then it sends a PaddlePositionMessage to the server. And once I implemented that, the lag disappeared.

(Incidentally, the problem never showed up when the client and server were running on the same machine.)

The next step will be to add a ball to the game, so it'll be like Pong. I suppose I should put walls on the sides of the board while I'm at it.

10:35pm EST, 23 Feb 2005
Wednesday

Simplified some code on the client side.

The game screen is starting to develop. It's got buttons for quitting the game and for disconnecting from the server. The actual game display is still just a blank area, and there isn't any game functionality on the server side yet. I suppose that will be next.

11:17pm EST, 17 Feb 2005
Thursday

The client now displays games that are in progress in the bottom-right area of the display.

The next step is to set up the game screen. I'll probably have a "leave game" button and an "exit server" button, and, of course, the actual game display.

3:34pm EST, 17 Feb 2005
Thursday

More code for moving players from the lobby to a new game is in place. On the client side, it switches from the lobby screen to the game screen (which right now is just blank). The Game object on the server handles player exits and timeouts correctly.

Next I'd like to split the area on the right side of the client screen in two, so that the bottom half shows games in progress. Once I do that, the lobby will be pretty much done.

11:01pm EST, 16 Feb 2005
Wednesday

The event notification code is in place.

The GameManager creates a Game object with the two players. It doesn't actually do anything yet, and I'd like to work on it some more, but the cats insist that it's bed time and won't let me concentrate. So, off to bed.

10:26pm EST, 16 Feb 2005
Wednesday

Been working on the server side. When it gets a JoinGame message, it removes the two players involved from the lobby, converts them from LobbyMember objects to GameMember objects, and sends them off to the GameManager object's createGame() method. That's as far as it's gotten so far. createGame() just prints a message to the console screen.

The lobby needs to know about game creation and destruction events, but those are handled by an outside object, so I'm going to set up an event notification mechanism similar to the Java AWT event model. There will be a GameListener interface that Lobby will implement, with methods such as gameCreated() and gameDestroyed(). The GameManager will have a method like addGameListener() so the lobby can register itself to receive such notifications and pass them on to the users who are hanging out in the lobby.

6:06pm EST, 16 Feb 2005
Wednesday

Polished up the user interface regarding the list of games that are waiting for players to join. Here are a couple of screen shots, the first of which shows the lobby when nobody has created a game, and the second of which shows it after one of the players has created a game:

On the server side, it detects when a user double-clicks on somebody else's game. Right now it just prints a message to the console when this happens.

Getting very close now to the actual game play....

6:30pm EST, 15 Feb 2005
Tuesday

The client now keeps track of and displays on the right side of the screen games that have been created by players in the lobby.

The next step will be actually launching a game when a user clicks one of the games in the list.

11:39pm EST, 14 Feb 2005
Monday

When the server gets a CreateGame message, it adds an entry to a list that keeps track of games that are waiting for a second player to join, and then it broadcasts a GameCreated message to all clients in the lobby. It follows a similar process when it receives a CancelGame message.

On the client side, right now when it receives a GameCreated or a GameCancelled message, it just prints a message to the console screen.

The next step will be to update the EnterLobby message to deliver a list of created games along with the list of players in the lobby. Will do that next time I sit down to work on this.

4:11pm EST, 10 Feb 2005
Thursday

The client now sends a message to the server when the user clicks the "Create Game" button. It's a toggle button, so there are actually two messages involved: CreateGame and CancelGame. On the server side, it understands the messages but currently only prints a message to the console when it receives them.

Also worked a little more on the login code:

10:48pm EST, 9 Feb 2005
Wednesday

Worked on the user interface today. The login screen now has a status bar. If you get disconnected from the server, it will report the reason. When the program first starts up, the status bar shows a welcome message:

The lobby is more polished. There are a couple of buttons on it now and the text in the chat area uses various styles and colors:

The next step will be to add an area on the right side of the lobby where it shows games that are waiting for another player to join, as well as games in progress.

2:49pm EST, 8 Feb 2005
Tuesday

Added a PlayerExited message so that clients can exit gracefully from the server, rather than having to time out. This chat program Breakout game is really coming along....

8:29pm EST, 3 Feb 2005
Thursday

I started to read about Java's java.util.logging API, but I got bored. Today I implemented chat functionality in the lobby, so I now have a working chat program at least.

12:04pm EST, 1 Feb 2005
Tuesday

The server now detects when a client crashes or becomes unreachable.

I'm going to take some time out from coding and research any Java APIs for message logging. I figure the more work I put in to message logging on the server now, the more grief it will save me later on when those inevitable bizarre bugs start to show up.

9:00pm EST, 26 Jan 2005
Wednesday

The client-side GUI is coming along. It has a form where the user enters his name, the server name or IP address, and the server port.

The user then clicks the "Connect" button and sees a list of all the users in the lobby. That list also updates when another user connects.

I started working on some heartbeat code, so that if the server crashes then the client will know it, and vice versa. So far it's only half in place: the client detects when the server has crashed, but the server doesn't check for the client's heartbeat yet, though much of that code is already in place.

9:48pm EST, 25 Jan 2005
Tuesday

Been working on Breakout off and on all day. On the server side, the lobby is in place. It accepts join messages from clients and puts them in the lobby, and any client that joins gets back a list of names of everybody there. Also, everybody gets a notification when a new client enters. Working great so far.

Especially exciting is the fact that the server-side code is turning out to be game-independent. It looks like I'll be able to re-use this code with future games, and in fact integrate future games into a single multigame server. So when a user connects to the server and wants to start a game, he has several different games to choose from.

I've started re-writing the client side from scratch. I'm going to start with a GUI where the user enters his name and the server address and port to connect to, and go from there. It's been forever since I've done any GUI programming. I think I may call it a night.

2:40pm EST, 25 Jan 2005
Tuesday

Thought about the communication scheme between the client and server some more. Realized that when the server receives a datagram, it's got to know which client it's associated with, and the way to do that is to examine the source IP address and port. So basically the first thing the server needs to do when it receives a message is to look up the appropriate recipient in a table, where the key is the message source. If the source isn't in the table, then send the message to a default recipient that understands how to add a new client to the server (i.e., the default recipient understands the JoinMessage).

So now the server just sits in a loop receiving messages and passing them on to the proper recipients. I'm setting a timeout on the datagram socket so that the receive method comes up for air every 100 milliseconds to see if it needs to stop (it checks a variable that gets set when the stop() method is called on the BreakoutServer object).

To get an idea of how it works, here's a list of the steps that occur when a client joins the server:

  1. User launches client program, specifies server host and port.
  2. The client program ("client" from here on out) creates an instance of JoiningRecipient, which is a ServerMessageRecipient that understands the EnterLobby message from the server.
  3. Client sends a JoinMessage to the server, containing the name of the user.
  4. Client displays a note to the user like "Connecting to server..."
  5. The server gets the message and looks up the recipient in the table for the message's source IP and port.
  6. Since this is the first message that this client has sent to the server, the server finds no entry for this source and so delivers it to the default recipient.
  7. On delivery, the JoinMessage calls the default recipient's playerJoined() method, passing in as parameters the player's name and the message source.
  8. The default recipient creates an instance of LobbyMember for this client. LobbyMember itself implements ClientMessageRecipient, and it gets put in the table as the recipient of future messages from the same client that this message came from.
  9. The default recipient puts the newly created LobbyMember into the Lobby, by calling the Lobby's addMember() method.
  10. Lobby's addMember() sends an EnterLobby message to the client, which contains information about the current state of the lobby such as a list of other client names, games in progress, games waiting for more players, etc.
  11. The client receives the EnterLobby message, and delivers it to its instance of JoiningRecipient.
  12. The EnterLobby message calls the JoiningRecipient's enterLobby() method, passing in the lobby state information.
  13. The JoiningRecipient's enterLobby() method replaces itself with an instance of LobbyRecipient.
  14. The JoiningRecipient's enterLobby() method then clears off the "Connecting to server..." note and renders a GUI view of the lobby.

9:24pm EST, 21 Jan 2005
Friday

I've decided to take yet another step back from the inner workings of the program and start at the beginning.

Yesterday I was thinking about how I churned out Gravity Wars in less than a week, and I remembered the order in which I coded things. First, I started with the lobby, where the player goes when he first connects to the server. The first thing I implemented with the game was nothing more than a chat room. There was no code in place for the actual game, but people could connect and see each other on and chat with each other.

Next week on my days off, I'm going to write the lobby for Breakout.

11:45am EST, 19 Jan 2005
Wednesday

I've got a message-passing mechanism in place whereby each datagram sent from the client to the server contains a serialized object that implements the ClientMessage interface. That interface contains one method:

public void deliver( ClientMessageRecipient recipient );

There will be various classes that implement this interface, such as JoinMessage for when a player connects to the server, something like a PaddleMoveMessage for when (you guessed it) the player moves his paddle, and the like.

The ClientMessageRecipient interface, in turn, will contain a method for each type of message that the client can send to the server. For example, it has a playerJoined() method that is called by JoinMessage objects.

When the client wishes to send a message to the server, it instantiates the appropriate class that implements ClientMessage and serializes it to a byte array. It sends that byte array to the server in a UDP datagram. The server deserializes it and calls its deliver() method, passing in a local instance of ClientMessageRecipient. The deliver() method calls the appropriate method on the recipient object, and the message is delivered.

The same situation exists for messages going the other way, from the server to the client. There's a ServerMessage interface and the corresponding ServerMessageRecipient.

I know that I could accomplish the same thing more simply using RMI, but I want to do this with UDP to get the practice.

I've tested this with the JoinMessage. For now, all it takes in its constructor is the name of the player, and its deliver() calls playerJoined( String name ) on the recipient. On the server side, I have an implementation of ClientMessageRecipient whose playerJoined( String name ) simply prints the name to standard out. So far, it's working beautifully.

10:09am EST, 19 Jan 2005
Wednesday

That was it. I removed that rule from that iptables chain on Stone and the datagram got through with no problem. Now that I've established that I can send UDP packets over the network, I'm going to try to implement a very simple client / server session.

11:20pm EST, 18 Jan 2005
Tuesday

I was just re-reading the last entry when I noticed that that "admin prohibited" message is coming from Stone. 'iptables --list' on Stone shows the following (only showing relevant chain):

Chain RH-Firewall-1-INPUT (2 references)
target prot opt source destination
ACCEPT all -- anywhere anywhere
ACCEPT icmp -- anywhere anywhere icmp any
ACCEPT ipv6-crypt-- anywhere anywhere
ACCEPT ipv6-auth-- anywhere anywhere
ACCEPT udp -- anywhere 224.0.0.251 udp dpt:5353
ACCEPT udp -- anywhere anywhere udp dpt:ipp
ACCEPT all -- anywhere anywhere state RELATED,ESTABLISHED
ACCEPT tcp -- anywhere anywhere state NEW tcp dpt:ssh
ACCEPT tcp -- anywhere anywhere state NEW tcp dpt:http
ACCEPT tcp -- anywhere anywhere state NEW tcp dpt:ftp
ACCEPT tcp -- anywhere anywhere state NEW tcp dpt:smtp
REJECT all -- anywhere anywhere reject-with icmp-host-prohibited

Notice that last line. Anyway, I'm really going to bed now. I'll mess with this some more tomorrow.

11:10pm EST, 18 Jan 2005
Tuesday

I started the server as a tiny little program that waits for a datagram packet on a specified port and then prints out the byte values it received. It works fine if I send the datagram from Stone, my home computer (the server also runs on Stone), but when I try to send it from wbrameld4.name, it never gets here. tcpdump on wbrameld4.name shows the following:

22:59:24.603663 209.51.137.74.35141 > 198.78.107.94.35229: udp 5 (DF)
22:59:24.620998 198.78.107.94 > 209.51.137.74: icmp: host 198.78.107.94 unreachable - admin prohibited [tos 0xc0]

tcpdump on Stone shows nothing at all. One link from a Google search suggested it might be something to do with iptables, but 'iptables --list' on wbrameld4.name shows no rules defined. Oh well...it's late and I'm too sleepy to troubleshoot this right now.

9:40pm EST, 18 Jan 2005
Tuesday

I'm going to scratch all the code I wrote last week. The bottom-up approach just doesn't work. I find, again and again, that the methods and class definitions don't exactly match the needs of the program. I'm going to set that code aside for now and pretend it isn't there. I'm going to start with the main() method and go from there, just like in the good old days.

6:36pm EST, 18 Jan 2005
Tuesday

I've decided to go with UDP as the transport layer protocol, mainly because I've never written a UDP application before. The main() method is very minimal at the moment:

   static public void main( String[] args ) {
      try {
         Player playerOne = new Player();
         playerOne.setWaitingForOpponent();

         Player playerTwo = new Player();

         playerOne.setReady();
         playerTwo.setReady();

         Breakout breakout = new Breakout( playerOne, playerTwo );

         breakout.addTickListener( playerOne );
         breakout.addTickListener( playerTwo );

         for (;;) {
            breakout.tick();

            try { Thread.sleep( 50 ); } catch ( InterruptedException exc ) {}
         }
      } catch ( IOException exc ) {
         System.err.println( "I/O error: " + exc.getMessage() );
         System.exit( 1 );
      }

      System.exit( 0 );
   }

I haven't actually run it yet, since first I have to go back and write the constructor for Player, which waits for a UDP packet from a client, and write the tick() method in the Breakout class. I also have to make the Player class implement the TickListener interface.

5:45pm EST, 18 Jan 2005
Tuesday

I thought it would be interesting to keep a journal of my progress in one of my programming projects.

I started working on Breakout about a week ago. Sam and I came up with the idea for the game. Two paddles, one on the bottom and one on the top, with several rows of bricks in the middle. Each player has a ball, so there are two balls in play at any time. Here's a concept image:

If the red ball hits a brick, the red player gets points, regardless of which paddle last hit the red ball. Same for the green ball. If the red player misses either ball, then the green player gets points, and vice versa.

Bricks score higher the farther they are from your paddle. The trick then is to get your ball on your opponent's side. He doesn't dare miss it, but he doesn't want you to hit the bricks on that side either since they get you lots of points.

So far...

Last week I wrote a lot of code dealing with the server-side objects in the game. Progress was slow, but the basic framework of collisions and scoring is in place.

This week I'm taking a different approach. For all the coding I did last week, I never had something concrete to run. No main() method, and it's hard to sustain the effort when you never see any payoff. Today I'm going to start with the main() method in the BreakoutServer class and work my way down, incorporating the classes I built last week.

« x Blog x Philes x »

FairTax     Stop Sylvia Browne