Chat command parser

A framework for Neverwinter Nights 1 persistent world servers.

@commands parsing

A common method of interfacing with certain things - emotes in particular - is to use chat commands. Waymark implements an abstraction interface to allow developers to easily add new chat commands in a standardized format.

2da Formatting

The interface is implemented using the 2da lookup table wm_commands.2da

This table has the following fields:

  • id - a simple numeric ID for the command
  • name - a plain text name for the command, this doesn’t have to be the actual command
  • validator - the name of a function that is called to validate if the command is invoked
  • executor - the name of a function that is called to actually do whatever the command does

Scripting a validator

A validator is a function that is used to test whether the player has one of the chat commands in the table. These are loaded when the module is first loaded so that they are not being reloaded with every single chat command, which would be onerous, for several commands.

Here is an example from the @help command’s validator:

// Function: isHelp
// Determine if a given chat string contains the help command qualifier
int isHelp(string sChatString) {
   // easiest to transpose the string to lowercase to test
   sChatString = GetStringLowerCase(sChatString);

   // Trim spoken word to the start to see if we have "@help" at the start
   while((sChatString != "") && (GetStringLeft(sChatString, 1) == " ")) {
      sChatString = GetStringRight(sChatString,
         GetStringLength(sChatString) -1);
   }

   if(GetStringLeft(sChatString, 5) != "@help") {
      return FALSE;
   } else {
      return TRUE;
   }
}

The choice of whether to validate whether the arguments are appropriate in the validator is left to the programmer. There’s costs and benefits: the code never steps into the executor so it is (very slightly) more efficient, but you may not be able to provide as clear error feedback, and it will continue looking at other commands.

In general, in our own code, we prefer to do it in the executor function, to prevent the fallthrough.

The validator must accept a string representing the chat line being validated as the argument, and no other arguments.

The validator must return FALSE if it does not meet the execution condition (so if it is not the command, or does not have valid arguments), and TRUE if it does. Any other return will result in undefined behaviour.