[RM2K3] [DYNRPG] EDGE CASE BUG: EMPTY COMMENT LINES

Posts

Pages: 1
I ran into an interesting and sneaky bug in a DynRPG project, and I thought it worth sharing here. When I dropped my DynParams plugin into my game project and tried to run it, I got an error message about a request to terminate in an unusual way, and then the game crashed. I used some Message commands to trace where the crash was happening, and found that it occurred in the middle of a bunch of Comment lines, which of course should do nothing. o.O I narrowed it down further and found that the culprit was a completely empty Comment line, which I'd used for visually separating one set of Comments from another.

In the C++ code for DynParams, I had an onComment implementation like this:

bool onComment( const char* text,

const RPG::ParsedCommentData* parsedData,
RPG::EventScriptLine* nextScriptLine,
RPG::EventScriptData* scriptData,
int eventId,
int pageId,
int lineId,
int* nextLineId )
{

std::string commentText = text;

/* more stuff */

}


It turns out that if the Comment line is empty, the function argument "text" will be NULL, which causes trouble if you try to initialize a string with the =operator using it. My solution looked like this:

bool onComment( const char* text,

const RPG::ParsedCommentData* parsedData,
RPG::EventScriptLine* nextScriptLine,
RPG::EventScriptData* scriptData,
int eventId,
int pageId,
int lineId,
int* nextLineId )
{

std::string commentText;

if( NULL == text )
{ // Comment text is empty

commentText = "";

}
else
{ // Comment text is non-empty

commentText = text;

}
/* more stuff */

}


This is, perhaps, more a general C++ thing than a DynRPG thing--make sure your char array isn't NULL when trying to feed it into a std::string--but it came up because I wasn't keenly aware of how onComment handles the case of an empty Comment line, and it's the sort of thing that can easily slip past testing and give your plugin a bad rap because it caused somebody's game to crash. Watch out!

And of course, if you downloaded DynParams before now, you may want to get a fresh copy. I also fixed a couple things in the readme file.
Hmmm. Very interesting. Makes sense I guess though. It's funny, because I've always ignored the "text" parameter of that function entirely. I forget who wrote this technique originally (probably Kazesui), but this is probably a safer way to get a comment command anyway:

bool onComment( ... )
{
// parsedData already stores a parsed verison of the command, so you can use it here
std::string cmd = parsedData->command;

// cmd.compare() will evaluate to 0 (false) if cmd matches the given text ("my_command" in this case).
// It sounds a little counter-intuitive, but that's how .compare() works.
// The "!" is a cleaner way of writing "if ____ is false" or "if ____ is 0"
// parametersCount ensures there is a certain number of parameters before it runs any more code... thus preventing any sort of crashing.
// If parameters are optional, use "parameterCount <= #"
if(!cmd.compare("my_command") && parsedData->parametersCount == 2)
{
// stuff here...
return false; // prevents any additional code from running during this cycle.
// Removing this line is not a good idea because your plugin could take a performance hit depending on how many comment commands
// you're checking for in your plugin
}
return true;
}

I wouldn't check for the entire comment text otherwise though, because it'll just check every time there's a comment. You could easily do "@command_to_check "here's some text to check for."
Yeah, I actually do use parsedData for most stuff, but DynParams has a string-building capability, for which it was handy to have the raw, untokenized text. I probably could've worked with the provided char array for that, but it was convenient to have it as a std::string.
Pages: 1