In my head, QubeKwest is always a client/server program. Meaning that even a single player local game is going to be the client program connecting to the server program over localhost. The fun part there is that it means that the client and the server can be located anywhere. On a powerful computer, they talk to each other over a localhost network with both parts running on the same computer. On lesser computers they talk to each other over an actual network with the client and server each being their own computer. And in the case of playing with your friends, they talk to each other over the internet. If I code it right, the game not only “won’t care” but it actually won’t even know the difference.
Networking is a tricky beast and starting with the server makes the most sense. A server can be coded in tons of different ways I’m sure, but the two that I have personally coded are the threaded approach and the asynchronous approach. The threaded approach just uses the normal java.net.* things while the asynchronous approach uses java.nio.*. If you’ve ever tried to code your own NIO version without help and pulled it off you are better at coding than me. Simply put, I get the java.net.* stuff and my brain skips a step and stumbles the rest of the way down the stairs to end in a heap at the bottom when I try to understand the java.nio.* stuff. Ok, that’s not fully true anymore, but it certainly was for previous attempts.
For a little background on the differences between the two approaches, I present a couple of paragraphs that get a little deep. In the threaded approach, your server has a full blown operating system thread to handle every single incoming connection from a client, and another one to act as the server thread itself. This is extremely heavy, not terribly scalable, and puts a pretty hard limit on the number of clients that can be connected at the same time. Most modern operating systems support thousands of concurrent threads, but long before you get anywhere near that number, you start slamming into the overhead of switching between the threads you are using. This approach is easy to code, easy to understand (if you understand how to play nicely with threads at least), and completely useless for large numbers of client connections.
In the asynchronous approach, you create a semi-magical server with just a thread or two to handle all the work of responding to clients. The magical part is that it doesn’t matter if there is one client connected or 1000, the number of threads the server uses to manage the whole thing doesn’t change. For a most excellent explanation of how this works, go check out the Rox Nio Server Tutorial. I used that tutorial to craft how my game server works and it is the best description of how to make Java NIO stuff work correctly that I’ve ever bumped into. This approach is a little trickier to code, vastly more complicated to get your head around, and scales well since you don’t need tons of threads to make it work.
This brings me to my diversion. In all of the refactoring and playing with and confusing things and in general just blowing up of code that I did while writing an NIO server for QubeKwest, I managed to entirely lose track of how to use it and then I managed to substantially break it. After seven months without looking at the code, I came back to discover that my network code was well and truly busted and that I had no clue how to fix it. I started trying to follow the Rox Tutorial again on the code I had, but quickly found that my code was so different from the original that even that wasn’t useful.
Then an idea popped into my head. Start over. I know, great idea… but sometimes it’s just what you have to do. In this case I decided to simply make a stand alone server that wasn’t connected to the QubeKwest code at all. This approach eliminated the QubeKwest package structure, the broken code, the refactors I’d incorporated, and it allowed me to learn how the whole thing was supposed to work again. All without further destroying the networking in QubeKwest.
It took me a couple of days to get it to work properly because I was taking my time and making sure I understood the code I was writing. When I thought I was done, I found that I was exactly one line of code short of having it work properly. The code I’d produced was pegging an entire CPU core to 100% (even without requests coming in) and didn’t respond to the client. This is obviously not an ideal server since the goal would be to use essentially 0% of the CPU unless you are responding to something and of course to actually respond to things.
Once I found the one line of code I’d missed, added it in, recompiled, and fired it up again, everything was suddenly behaving correctly. Now I just need to refactor the code a bit in the stand alone version so it is a little cleaner, and then start integrating it into the QubeKwest network package. It was an interesting couple of days, but at least I learned something and have functional code again.