Debugging an Action Map - possibly of very little interest
by Mark Dynna · 05/11/2008 (10:24 pm) · 8 comments
I have no idea if this is actually going to be of interest to many other people, but I thought I'd blog it anyway in the off-chance it is interesting to someone out there. This is the story of a strange bug I tracked down recently that got stranger and stranger as I attempted to debug it. Perhaps someone can learn something by following the process I went through to track down this problem, or perhaps someone can give me some pointers on how to find this stuff faster and easier. Anyways, enough forward...
Our team had been happily moving along, making good progress developing our Chariot racing game when I got a bug report. Most of the keyboard controls for our lead World Builder (and general boss-lady) had suddenly stopped working. No one else was currently experiencing this problem (including myself). As with most keyboard-related bugs, I first told her to delete her custom controls, reset back to defaults, and re-map any custom ones again. She reported that the custom controls would work, but the instant she tried to re-map any one of them, they would all stop working. So, I gave it a shot on my machine. I deleted my custom config file (which was pretty much all defaults anyway) and loaded up the game. Nothing. I could not move anywhere even though all of my movement keys were the defaults and were working just a second ago. (ASIDE: Even now, knowing the cause of the problem, these symptoms still baffle me...)
So, clearly something had changed that any new control schemes created just didn't work. Bug replicated, so I moved on. One of the curious other symptoms of the problem was that the controls would work fine when the Mission Editor was activated, but not in normal "play" mode. That initially led me to believe there was something strange going on with the ActionMaps since the Mission Editor GUI does some pushing and popping when it activates or deactivates.
Now, we do have some involved code involving Action Maps, in that we are allowing the user to have different control schemes for different movement modes (on foot, riding a horse, driving a chariot). So, it seemed natural to me that something could be broken there. I explored that option for awhile with changing things and scouring our multi-action map code, but everything looked like it was working as it should. In the process I wrote a nifty (I think) little function. I wanted to make sure all of our calls to push andpop were happening correctly, but I couldn't find a easy way to tell exactly which action maps were on the stack at any given time. So, I whipped up a fuunction called dumpActionMapStack. Nothing particularly fancy about it, but it got the job done:
Anyway, the whole ActionMap avenue didn't seem to be going anywhere. Everything always seemed to be exactly as it should be. So, I started to think of other reasons why the controls worked when the Mission Editor was active. That led me to investigate the GUI. A knew a quick test could quickly confirm that theory. : I commented out all of everything but the GameTSCtrl. Lo and behold I could move, so it had to be something in the GUI! Now, for those of you who wouldn't know where to go from here let me show you. Inputs (like keystrokes) in Torque "enter" the system through the GameInterface::processInputEvent function, in main.cpp:
I looked through all the possible places that the function could return TRUE (the event was processed/consumed) and put in a little code to see which control was responsible for consuming the event, like this:
As with most ridiculously difficult to track down problems, this one had a simple solution. In the initialization of the PlayGui and put two lines:
Those two lines have the power to freeze the player completely in their tracks or free him. Life can be a real mysterious sometimes. The only conclusion I can draw from all of this is that GuiTextListCtrl wasn't really designed to be used as part of the PlayGui. I'm sure the whole "first responder" code works great when in a "menu" sort of environment, but on a Play GUI its kind of critical not to have it eat up those keystrokes. Sometimes the things that happen in a computer program just make me shake my head.
PS - Our project is a game about racing Chariots in the ancient world (Roman era). If you are interested in helping us, especially if you are a 3D artist, please contact me via email.
Our team had been happily moving along, making good progress developing our Chariot racing game when I got a bug report. Most of the keyboard controls for our lead World Builder (and general boss-lady) had suddenly stopped working. No one else was currently experiencing this problem (including myself). As with most keyboard-related bugs, I first told her to delete her custom controls, reset back to defaults, and re-map any custom ones again. She reported that the custom controls would work, but the instant she tried to re-map any one of them, they would all stop working. So, I gave it a shot on my machine. I deleted my custom config file (which was pretty much all defaults anyway) and loaded up the game. Nothing. I could not move anywhere even though all of my movement keys were the defaults and were working just a second ago. (ASIDE: Even now, knowing the cause of the problem, these symptoms still baffle me...)
So, clearly something had changed that any new control schemes created just didn't work. Bug replicated, so I moved on. One of the curious other symptoms of the problem was that the controls would work fine when the Mission Editor was activated, but not in normal "play" mode. That initially led me to believe there was something strange going on with the ActionMaps since the Mission Editor GUI does some pushing and popping when it activates or deactivates.
Now, we do have some involved code involving Action Maps, in that we are allowing the user to have different control schemes for different movement modes (on foot, riding a horse, driving a chariot). So, it seemed natural to me that something could be broken there. I explored that option for awhile with changing things and scouring our multi-action map code, but everything looked like it was working as it should. In the process I wrote a nifty (I think) little function. I wanted to make sure all of our calls to push andpop were happening correctly, but I couldn't find a easy way to tell exactly which action maps were on the stack at any given time. So, I whipped up a fuunction called dumpActionMapStack. Nothing particularly fancy about it, but it got the job done:
ConsoleFunction( dumpActionMapStack, void, 1, 1, "dumpActionMapStack()" )
{
SimSet* pActionMapSet = Sim::getActiveActionMapSet();
Con::printf("Action Map stack:");
for(int i = 0; i < pActionMapSet->size(); i++) {
SimObject *actionMap = pActionMapSet->at(i);
Con::printf("%s", actionMap->getName());
}
}Anyway, the whole ActionMap avenue didn't seem to be going anywhere. Everything always seemed to be exactly as it should be. So, I started to think of other reasons why the controls worked when the Mission Editor was active. That led me to investigate the GUI. A knew a quick test could quickly confirm that theory. : I commented out all of everything but the GameTSCtrl. Lo and behold I could move, so it had to be something in the GUI! Now, for those of you who wouldn't know where to go from here let me show you. Inputs (like keystrokes) in Torque "enter" the system through the GameInterface::processInputEvent function, in main.cpp:
void DemoGame::processInputEvent(InputEvent *event)
{
if (!ActionMap::handleEventGlobal(event))
{
// Other input consumers here...
if (!(Canvas && Canvas->processInputEvent(event)))
ActionMap::handleEvent(event);
}
}As you can see, there's a definite order to things. First, the Global Action Map gets a crack to see if it should handle the event. If not, the Canvas (the current GUI) sees if it needs to handle the event (by returning TRUE), and only if it does not handle the event does the current ActionMap(s) actually get to see the event. So, I knew something was happening in that Canvas::processInputEvent function. Now, at this point, I could've thrown a breakpoint in that function and ran in Debug mode, but I have found that trying to Debug input events using a breakpoint is rough stuff: you usually end up catching 50 events you don't want, so you start nailing that F5 key repeatedly and inevitable end up missing the input even you do want. So, I decided to just throw in some console output and see what I got.I looked through all the possible places that the function could return TRUE (the event was processed/consumed) and put in a little code to see which control was responsible for consuming the event, like this:
if (mFirstResponder)
{
if(mFirstResponder->onKeyDown(mLastEvent)) {
[b]Con::printf("Control %s consumed event via First Responder", mFirstResponder->getName());[/b]
return true;
}
}So, with that done I fire up the game, hit my non-responsive movement key and check the console output:Control HistoryRacesWonList consumed event via First ResponderWhat? That's a GUI control we use to show the player which races they've participated in and how they finished. It was causing my movement bug? Turns out that little critter is a GuiTextListCtrl, and those insidious little guys have a tendency to always want to be the "first responer." What's that? Basically, if a "first responder" has been set, it has the potential to consume any key input event being sent. Thus the Text Lists that were on our GUI, and not even curently visible were taking all of those lovely keystrokes that should be doing things like moving the player and saying "nah, I'll just keep this one for myself."
As with most ridiculously difficult to track down problems, this one had a simple solution. In the initialization of the PlayGui and put two lines:
HistoryPlayersList.makeFirstResponder(false); HistoryRacesWonList.makeFirstResponder(false);
Those two lines have the power to freeze the player completely in their tracks or free him. Life can be a real mysterious sometimes. The only conclusion I can draw from all of this is that GuiTextListCtrl wasn't really designed to be used as part of the PlayGui. I'm sure the whole "first responder" code works great when in a "menu" sort of environment, but on a Play GUI its kind of critical not to have it eat up those keystrokes. Sometimes the things that happen in a computer program just make me shake my head.
PS - Our project is a game about racing Chariots in the ancient world (Roman era). If you are interested in helping us, especially if you are a 3D artist, please contact me via email.
About the author
#2
05/12/2008 (1:53 am)
Heh heh that bug was bugging her for the longest time. I am starting to understand programming a little better by reading your post. :P It's less like a bunch of words and is starting to become more of a language feel. Nice work Mark. :)
#3
05/12/2008 (4:27 am)
Nice write up, it was interesting to read your debugging process.
#4
Thanks for sharing your findings with the community. The more people who have this knowledge, the better indie games will be!
And THANK YOU for fixing this irritating bug! So frustrating! (Yes, it was MY system that was bugging out!) It was driving me insane.
God bless you,
-Sparkling
www.chariotsgame.com
05/12/2008 (9:01 am)
YAY Mark! Thanks for sharing your findings with the community. The more people who have this knowledge, the better indie games will be!
And THANK YOU for fixing this irritating bug! So frustrating! (Yes, it was MY system that was bugging out!) It was driving me insane.
God bless you,
-Sparkling
www.chariotsgame.com
#5
05/12/2008 (2:18 pm)
Haha I found it quite interesting, was kinda waiting for some twist at the end when you found the bug. Good to know about that firstResponse thing, else I'm sure I would have had the problem eventually and been clueless.
#6
05/13/2008 (6:12 am)
This is quite a helpful little tidbit! I've not run into it myself, but I've seen a ton of questions on it.
#7
Dang, what was GG thinking? It really is a bug.
04/07/2009 (6:21 pm)
Mark, flat out, you are my HERO!!! I spent hours trying to figure out what this was. I posted on post after post trying to figure it out.Dang, what was GG thinking? It really is a bug.
#8
Well, I'm glad this blog helped you almost a full year after its initial post.
04/07/2009 (9:04 pm)
Wow Matt, you dug up an old blog. As with most things of that nature, the whole First Responder logic probably had a purpose that made sense at the time, but had this very unintended side-effect.Well, I'm glad this blog helped you almost a full year after its initial post.
#9
Thanks a bunch!
02/09/2011 (9:07 am)
...and it continues to help some folks. I spent an hour trying to figure out where my keystrokes were being consumed after making some gui/keybind changes, only to stumble upon this while trying to solve it. And sure enough, I had added a GuiTextListCtrl to my play.gui (although it was invisible at the time) and it was consuming them.Thanks a bunch!

Associate Phillip O'Shea
Violent Tulip