LoCo Day 30

Today I added support for command-line options when starting the web server. Specifically, it’s now possible to specify which port the server will listen on, and the hostname that will be used when showing type-safe URLs. Previously, both of these were hard-coded in such a way that made it difficult to use the server on anything but localhost. I also increased the server-side timeout, so that I can go increase the long-poll duration.

Now that November’s over, I haven’t decided whether I’ll keep posting daily updates about progress on the game. I would like to keep working on it and get it to the point where I can actually put it on the Internet — though I did succeed in my goal of having a playable game by the end of the month, there’s a lot of polishing that needs to be done, to say nothing of the important-but-nonessential functionality I skipped over to focus on the core gameplay. I have no idea how long it’ll take me to get all that done, though.

LoCo Day 29

Two small but welcome changes today:

First, I reduced a lot of the lag during game play by excising a bit of debugging code on the server. The server had been printing out to the console the session context for each request before processing it further. Since I had gotten sessions working quite some time ago, all this did in practice was add a slight delay in processing each request, which wasn’t noticeable until you start trying to play a real-time game where each move sends a new request to the server. There’s still a bit of noticeable lag during the game, but it feels much smaller now.

Second, colors are now used to distinguish you from other players: you are blue, and everyone else is red. This way, you can tell right from the beginning who you are in the game. This is currently implemented by having the server reply to the “I’m ready to play” request by sending the client the ID used to identify that player during the game. There’s still no indication of who is who in the chat window, or is information about who dies when presented to the player (other than saying “someone died”), but the information is there for whenever I get around to implementing it.

Also! I discovered that part of the Conf structure passed into Happstack to start the web server is indeed a timeout for how long to let a request sit idle before killing it. The timeout defaults to 30 seconds, which explains why my initial attempt to use a 60-second interval for long polling kept resulting in server-generated errors. One of the next things on my to-do list is to make the host and port the web server listens on configurable instead of hard-coded, so while I’m at it I might as well lengthen the timeout.

LoCo Day 28

Today I tried to solve the problem of the “ready to play” button swallowing keyboard input when the game starts, even though the button was disabled. Apparently jQuery doesn’t let you put the input focus on the document itself, which is where the keyboard input handler is attached. I ultimately settled on hiding the button entirely instead of disabling it after it’s been pressed, which has the result I wanted: putting the input focus on nothing in particular, so that the document sees the key presses.

This probably won’t work well if you start using the chat window again before the game starts, since the focus would still be in the text entry box, and arrow keys there will move the text cursor instead of moving your player avatar in the game, leading to much unhappiness as you get killed off before realizing what’s going on. So, this is more of a quick hack instead of an actual fix.

LoCo Discontinuity

Apparently my posts for Friday and Saturday never made it out of draft form, and so didn’t appear on the blog until now. Just in case you were wondering what I was up to after Thanksgiving.

Comments Off

LoCo Day 27

Success? The basic gameplay in the browser is working now, so for the first time ever it’s actually possible to play the game. My loving girlfriend Renee helped me test that it does indeed work with multiple players, and she even beat me in the first real match we played against each other. For posterity’s sake, I will note that the name of the room she created for that first fateful match was named “Stupid time for uncool animals”. (Although I did win against her in some of the subsequent matches, she is quick to point out that there is some lag between changing your player’s direction and when it takes effect on screen. I’m not sure what the precise cause of that is yet, since lag over a LAN should be minimal.)

Although the game is playable in the most basic sense, there is a huge amount of work to be done to make this something that could be exposed to the public. Heck, right now the game doesn’t even provide a way to distinguish your player from the others on screen, so the beginning of each match usually involves each player frantically trying to figure out which one they control before they run into something and die. That needs to be fixed, obviously. There’s still no concept of user accounts, or even user identities, so all status messages refer to everyone as “someone”.

Also, Renee wants more explosions when collisions occur. The baseline number of explosions is zero, currently.

Sometime soon I’ll try to post a screenshot or two of what this game actually looks like, now that it’s in a state where stuff actually shows up on screen instead of living hidden in the server’s memory.

LoCo Day 26

Continued work on client-side support for the game. I realize the JSON format I had defined for sending game state to the browser wasn’t very useful for what the client-side JavaScript needs to do to display the state of the game on the page. Instead of doing additional processing on the browser, I decided to do the conversion on the server side, instead of generating JSON that just wraps how game data is modeled on the server. Unfortunately, it’s taking me longer than I anticipated to get my test cases working again following the changes, which suggests there’s some corner cases my existing code isn’t handling properly.

Comments Off

LoCo Day 25

Now the server actually runs the game when everyone in a room is ready to play, instead of just waiting for a few seconds and then claiming the game is over.

At the moment, however, there’s nothing on the client side that understands the game, so the next chunk of code to write is the JavaScript (ugh) that implements the game logic on the browser side. When the game is actually running, the client and server are running the game logic relatively independently of one another, with messages being send back and forth only to update each other when a player changes direction. This way, there won’t be a bunch of unnecessary traffic crossing the network.

I also think at some point I’m going to need to change how the server maintains the state of all the rooms. Right now, it’s a Map stored inside an MVar, but that means the MVar effectively acts as a global exclusive lock for anything that wants to do anything with a room. Not a problem for testing, but I bet that’ll be a big problem with even just a moderate load. I think I’ll replace that with a TVar for the Map itself, and another TVar for each room. Using software transactional memory instead of exclusive locks will allow multiple threads (i.e., multiple requests) to operate on pieces of the state at the same time. This matches the actual use much more nicely:

  • Most operations are just sending and receiving messages via a room’s channel, so the room and Map themselves aren’t being modified. Read-only operations are nice for STM.
  • Less commonly, an existing room is being modified (e.g. someone entering or leaving, or games being started up). This modifies an individual room but leaves everything else alone, and with each room having its own TVar, these within-room writes can be isolated from other write operations.
  • More rarely, rooms are created or destroyed, which is the only time when the structure of the overall Map itself needs to change.

Using TVars instead of an MVar also gives a bit more consistency with the message-passing bits of code, which are already using STM objects (namely TChans and TVars). Perhaps each request could be treated as a single STM transaction, instead of separate bits and pieces being run separately and intermixed with other IO operations. But since the next chunk of functionality will almost be all client-side, I probably won’t get around to migrating more stuff to STM for a little while.

Comments Off

LoCo Day 24

Today I made a few small steps towards integrating the game logic with the web interface.

Comments Off

LoCo Day 23

Today I finished the core server-side game logic, at least to the point of it passing the set of test cases I prepared earlier. Now the next big task is to connect the game to the web interface, so that it’s possible to actually play and see what’s going on.

Comments Off

LoCo Day 22

Today I implemented generation of the starting configurations of games. For now, all initial player positions are aligned along an invisible grid across the play area. It’d be nice to have more randomness in there, but that complicates things a bit more, since I still want to ensure players don’t start off too close to each other.

QuickCheck is great, but sometimes you have to be a bit careful with the set of function inputs you let it generate. In this case, I had to make the dimensions and grid spacing sized to prevent them from getting obnoxiously, impractically huge. I’m never going to try to create play areas measured in millions or billions of pixels on a side, or that have a grid spacing of similar magnitude. My starting configuration generator doesn’t work well in those extremes (by which I mean, it runs in to integer overflow problems), so it’s not worth testing them, especially if I’d have to contort my code to deal with those kinds of inputs.

Comments Off

LoCo Day 21

Almost no progress today; I neglected to set aside enough time from doing other things. I wrote a little more code to satisfy the tests I was working on earlier, but that’s about it.

If I were doing NaNo this year, and allotting the same amount of time I have been to this, I bet I’d be several thousand words behind at this point.

Comments Off

LoCo Day 20

Today I cleaned up the test cases I created yesterday. I simplified some of the things I was checking for, and as a result most of the tests had common functionality that I was able to pull out into separate top-level functions, which makes the testing code a lot more readable and helps prevent the type checker from going too far down the garden path if there’s a type error somewhere.

That done, I then started implementing some of the actual game-related code. Well, OK, mostly the JSON conversion code, but tomorrow I’ll tackle the real game stuff. The first thing I’ll need to figure out is how to properly generate a starting configuration for a game, making sure players are reasonably spaced out. I don’t know offhand what the generally accepted algorithms are for that, but I bet I’ll be able to find out after a brief bit of searching.

Comments Off

LoCo Day 19

More work on the actual game part of the game. Actually, most of that work was on coming up with good QuickCheck tests to check the yet-to-be-written implementations of the functions that process player input and step the state of the game through time. The fact that most of the game operations live in the STM monad complicates things a little, but I’ve (mostly) gotten the hang of writing monadic QuickCheck properties.

Comments Off

LoCo Day 18

Today I implemented the fundamental movement and collision detection logic for the game. It’s fairly simple, but I shouldn’t need to elaborate on it too much more when I implement the actual game on top of it. I also implemented a transactional queue to hold a player’s moves before the game processes them; if a player sends multiple moves in rapid succession, it’s possible they’ll arrive at the server faster than the game can consume them, one move per time step. Using a TChan doesn’t work, since it would retry the transaction if the queue were empty, whereas a player not having a move queued up is perfectly valid, and in fact would be the normal situation. My transactional queue (implemented as a queue wrapped in a TVar) simply returns Nothing if the queue is empty, which the game will then interpret as being no change in the player’s movement direction.

Comments Off

LoCo Day 17

Today I started implementing the core types for the game itself. They’re not yet connected to the rest of the program yet, so they’re only being exercised by the QuickCheck test cases for them. It is nice to be working in the realm of pure code again, at least until I get to the point where I need to start worrying about user interaction again.

Comments Off