Enhanced CLI ~ How to Implement a New Action Operation - uchicago-cs/chiventure GitHub Wiki

This page concerns the process of adding a new action to Chiventure from beginning to end.

Note: there are already groups of actions that are grouped into 4 kinds (5/23/2022). If you have a command that matches these types of input, there is a distinct workflow. These are:

  1. ACTION <item>
  • Such as take chair
  1. ACTION <path>
  • Such as go north
  1. ACTION <item> <item>
  • Such as put orb on chair
  1. ACTION <self>
  • These are actions that involve some attribute about the player, like quests, stats, class, etc...
    • Such as view stats

For cases where a team wants an action that fits into current kinds, or AM wants to make a new kind of action, see below


Implementing a Completely New and Independent Action

This would be for implementing a unique action that does not follow the formatting of any of the 4 types of actions. Previous examples include view, quit, and credits.

First, define an operation

In the operations.h file, you will find many commands with the following type ascription:

char *operation(char *tokens[TOKEN_LIST_SIZE], chiventure_ctx_t *ctx);

This is the formatting all operation functions must fall under, where tokens is an array of strings outputted from parsing the command line input, and ctx is the chiventure context struct which holds basically all other relevant information about the game.

How to design an operation function

Once you've decided to implement a new action type that is NOT any of the existing types, you need to actually write your function! Usually as CLI we are validating the input to make sure it has the correct parameters, such that we can feed those parameters into someone else's function. Generally, it will have the following control flow: (in pseudocode)

char *operation(char *tokens[TOKEN_LIST_SIZE], chiventure_ctx_t *ctx)
{
  if (game is null) 
    return status message

  if (missing game element, such curr_room == NULL)
     return status message

  if (too few or too many arguments)
    return status message

  else
    //often there will be a string which we pass by reference into another groups function, 
    char *return_string;
    int return_code = someone_elses_function(their arguments, &return_string);
    //passing return string by reference to be modified in their function by something like sprintf

}

Note about tokens:

As of now (5/23/2022) the list of tokens will contain just the action word in the first slot, then all parsed arguments to that command ie: > view stats is parsed to:

tokens[0] = "view"
tokens[1] = "stats"
tokens[2] = NULL
tokens[3] = NULL

Update the Command Hash Table

At this point, chiventure probably will not recognize your input and run your new operation because it won't recognize your action token.

To fix this, you have to add the function and the associated action command verb (e.g. fight, talk, help, look) to a hash table of commands. This hash table is created upon the start of a game as a lookup_t struct (defined in cmd.h), and is initialized with all possible action commands and operations.

To do so, you must call the add_entry function directly in the lookup_t_init function. It should follow this format:

add_entry("LOOK",look_operation, NULL, t);
  1. The first argument is the action you want in all caps.
  2. The second argument is the name of the operation you want to call with this action. This operation should follow the general operation function type as defined above.
  3. The third argument is NULL, since this operation is not one of the four action kinds (re: above).
  4. The fourth argument is t, which is the name of the hash table that your operation and command will be stored in. It is the parameter of lookup_t_init.

Now that your action is recognized by chiventure, you should add it to be a possible suggestion to the user. In the operations.c file, there is an array of strings called actions_for_sug and a global variable called NUM_ACTIONS. Add your new action in all caps to the end of the array, and increment the NUM_ACTIONS variable by 1.

Congrats! At this point, after recompiling, your input should be recognized and run your operation.


IF A TEAM WANTS AN ACTION THAT FITS INTO AN EXISTING CATEGORY

All this pertains is updated the current kindX_action_operation function to do whatever kind of checks it needs before calling someone else's function. the add_action_entries function in cmd.c should handle adding the action to the command hash table.

You will likely have to update the global variables and constants in operations.c:

#define NUM_ACTIONS 31
...
#define min(x,y) (((x) <= (y)) ? (x) : (y))
 
char* actions_for_sug[NUM_ACTIONS] = {
              "OPEN",
              "CLOSE",
              "PUSH",
              ...

NUM_ACTIONS is the total number of unique actions actions_for_sug is a list of all actions for the suggestion operation to grab from, if your command isn't here it can't be recommended as an alternative when a command is misspelt.


IF ACTION_MANAGEMENT WANTS A NEW KIND

If AM wants a new type, they will have already done the work of updating their internal structs. What this entails for CLI is

  • add a new kindX_action_operation in operations.c
  • updating the hash table function add_action_entries
  • updating the same global variables in operations.c same as above

Adding new kindX operation

Same as adding a new action operation, adding a new operation for a kind of action follows the same format: (information about return string in section about implementation of new action)

char *kindX_operation(char *tokens[TOKEN_LIST_SIZE], chiventure_ctx_t *ctx)
{
  if (game is null) 
    return status message
  ... some other checks ...
  else
    char *return_string;
    int return_code = action_managements_do_X_action_function(their arguments, &return_string);

}

There isn't much else, it follows the same flow, and for these kinds of operations action management should have already made a function for it.

Updating add_action_entries

Currently, lookup_t_init handles all of our unique actions, and add_action_entries handles all of action managements actions. add_action_entries (as of 5/23/2022) follows this format:

void add_action_entries(lookup_t **table)
{
    //this is AM's in-house function which makes a linked list of all actions and their kinds. 
    //this should have all actions with the new kind associated with them
    list_action_type_t *all_actions = get_supported_actions();

    while(all_actions != NULL)
    {
        action_type_t *curr_action = all_actions->act;

        //actions are a type of enum, with particular values as defined in `include/action_management/action_structs.h`
        //for every kind, we add an entry to the table with it's name and the kinds operation
        if(curr_action->kind == 1)
        {
            add_entry(curr_action->c_name, kind1_action_operation, curr_action, table);
        }
        else if(curr_action->kind == 2)
        {
            add_entry(curr_action->c_name, kind2_action_operation, curr_action, table);
        }
        else if(curr_action->kind == 3)
        {
            add_entry(curr_action->c_name, kind3_action_operation, curr_action, table);
        }
        else if(curr_action->kind == 4)
        {
            add_entry(curr_action->c_name, kind4_action_operation, curr_action, table);
        }

        all_actions = all_actions->next;
    }
}

all that you would need to add to add_action_entries is a new else if like:

else if(curr_action->kind == X)
{
    add_entry(curr_action->c_name, kindX_action_operation, curr_action, table);
}

And that's all! Action management should have done their own work of adding the actions and kinds to their structs.

⚠️ **GitHub.com Fallback** ⚠️