Saturday, May 10, 2008

Day 100: Wild and Crazy Ride

100 days of programming! Barring a week where my sinuses were killing me and a month where I bought and moved into a new house, I've been programming pretty much nonstop.

Today's story is, I got quite a bit through having missions able to pop up information to the player, which is the primary way of getting the plot of the game across. I shall now explain how this came about.

First, a note: #127 was a minor bug that annoyed me. You'd click on a button in one state, and any buttons in that same location in the parent state would end up clicked too. I'll come back to that.

My main work was on #137. Presenting the player with messages when they're given a mission turned out to be very easy, so long as I was doing only that. It actually displayed the correct message the first time I ran the program, which almost never happens for anything. I was very happy!

I'd made the dialog box which displays this general enough that it could handle yes/no questions as well, so I figured I'd go on to the next part, which is having conditions ask questions. So you could land on a planet, get a question asking "Do you want to be recruited into this planet's defense force?", and if you answer yes you get the mission.

This worked pretty much first thing as well. I was quite happy, but noticed something awry. Specifically, I clicked 'yes' to the mission, but didn't get the follow-up "You accepted the mission" message I'd put in the test mission.

An aside: I'd unit tested everything I could about these new conditions and actions, but as they displayed items on screen and rely on the user for input, this turned out to not be much.

I ended up having to make the evaluator a full-fledged state rather than the 'plugin' sort of existence it had before, because it made handling the continuations that much easier. This ended up not helping and I'm considering reverting that particular change (because a plugin is /far/ more useful in more situations than a full-fledged state). I figured something was going wrong with my continuations. As mentioned before, I love continuations but I'll freely admit they're somewhat of a black art to me. I know enough to be dangerous and get some things done, but when it starts falling apart I'm not sure where to look.

Eventually, print statements are everywhere. What they tell me, in every single case, is that the message dialog is, in fact, being displayed, despite the fact that I can't see it. I spend a good full hour doing nothing but putting tracer code in my entire setup to find out when this is happening.

Suddenly, it worked! I immediately realized that I hadn't done anything. I frowned, horrible suspicion dawning in my mind. I ran the program again, clicked the button in the exact middle, and didn't see the message. One more time through, clicking the button off to the side where there was no corresponding button in the message displayer, however? That worked fine.

I'd spent an hour tracking down a bug I already knew existed. Thanks, #127.

The rest of my time was spent fixing #127, which proved surprisingly difficult. I figured that there was a situation where the mouse-up would trigger a state change before the mouse-up was done fully processing, and so the new state would get it. But that turned out to not be the case. My army of print statements helped here, because what I saw happen was something like this: (output made up because it's fixed now and I don't have anything to paste here)

Beginning event handling loop
Got mouse down event#<MouseDown #7544>
Got mouse up event #<MouseUp #4556>
State transitioned
Ended event handling loop
...
Beginning event handling loop
Got mouse down event#<MouseDown #7544>
Got mouse up event #<MouseUp #4556>

I wasn't getting duplicate mouse-ups that weren't handled, this was two separate times through the event loop, yet somehow I was getting the exact same events as before.

It was this that led me to the 'time machine' metaphor for continuations. They don't just return you to where they left off, they change the variables and such to be exactly like they were then. This is handy because otherwise I wouldn't be able to refer to them, but putting continuations in the middle of my event-handling loop ended up rewinding and then replaying it!

I fixed this by having the event loop just build an array of MouseUp events, then later - once outside that loop - going through them and calling the actual functions. It seems to work; that loop itself never gets rewound or if it does it's not causing the same problems.

But man! What is it about the stuff that's supposed to be easy that makes it so hard?

No comments: