[Home] [Downloads] [Search] [Help/forum]

How to add MXP support to your MUD server

Author: Nick Gammon


Introduction

This page describes how I added MXP support to SMAUG and ROM (both Diku derivatives). In both cases the technique was very similar, however you could use the general method on virtually any MUD server.

What is MXP?

MXP - Mud Extension Protocol - is a method of adding extra functionality to MUDs. MUD servers add extra "tags" (eg. <color red>) and supporting MUD client programs interpret those tags to provide the features for the player.

At present, MUSHclient and zMUD support MXP. Other clients are considering adding support.

You can read more about MXP at the MUSHclient MXP page.

Example

Example of MXP-enabled server - 26K

Example of MXP-enabled server (SMAUG).

Note in the example:


The basic technique involves substituting special non-printing characters for the MXP special symbols "<", ">" and "&" in the body of the server, and then changing them back to those characters at the last moment.

The special characters used are:

Instead of Use Defined as
< \x03 MXP_BEG
> \x04 MXP_END
& \x06 MXP_AMP

For example:


      MXP_BEG "send" MXP_END    becomes: <send>
      MXP_AMP "version;"        becomes: &version;

To further facilitate this process we have a define that does that for us:


#define MXPTAG(arg) MXP_BEG arg MXP_END

So now we just need to say:


MXPTAG ("send") "go west" MXPTAG ("/send")

This also has the advantage that by searching for "MXPTAG" we can quickly find the parts of the server that are using MXP.


Output routine

Our output routine (called "write_to_buffer" on both SMAUG and ROM) needs to handle a number of special cases.

By removing the MXP tags for players who are not using MXP, we allow the rest of the server to add the tags, where required, without having to worry about whether or not this particular player needs them, thus avoiding a considerable amount of code duplication.

To summarise, the output routine does this:

Convert To
< &lt;
> &gt;
& &amp;
" &quot;
0x03 <
0x04 >
0x05 &

The output conversion maintains a "state" so that it does not do the special character conversions inside MXP tags themselves.

Code fragment - add this to mud.h, merc.h, or whatever your standard include file is:



/* strings */

#define MXP_BEG "\x03"    /* becomes < */
#define MXP_END "\x04"    /* becomes > */
#define MXP_AMP "\x05"    /* becomes & */

/* characters */

#define MXP_BEGc '\x03'    /* becomes < */
#define MXP_ENDc '\x04'    /* becomes > */
#define MXP_AMPc '\x05'    /* becomes & */

/* constructs an MXP tag with < and > around it */

#define MXPTAG(arg) MXP_BEG arg MXP_END

#define ESC "\x1B"  /* esc character */

#define MXPMODE(arg) ESC "[" #arg "z"

/* flags for show_list_to_char */

enum {
  eItemNothing,   /* item is not readily accessible */
  eItemGet,     /* item on ground */
  eItemDrop,    /* item in inventory */
  eItemBid     /* auction item */
  };


Doing the output conversion

This is a two-step process. In ROM and SMAUG the output routines allocate memory to store the outgoing text, so we need to make a memory adjustment, either up or down depending on whether or not the player is using MXP, so we first call "count_mxp_tags" to see how much adjustment is needed.

We pass down a flag indicating whether or not this particular player is using MXP.

Code fragment - add this to comm.c or whereever your low-level output routine is:


/*
* Count number of mxp tags need converting
*    ie. < becomes &lt;
*        > becomes &gt;
*        & becomes &amp;
*/

int count_mxp_tags (const int bMXP, const char *txt, int length)
  {
  char c;
  const char * p;
  int count;
  int bInTag = FALSE;
  int bInEntity = FALSE;

  for (p = txt, count = 0; 
       length > 0; 
       p++, length--)
    {
    c = *p;

    if (bInTag)  /* in a tag, eg. <send> */
      {
      if (!bMXP)
        count--;     /* not output if not MXP */   
      if (c == MXP_ENDc)
        bInTag = FALSE;
      } /* end of being inside a tag */
    else if (bInEntity)  /* in a tag, eg. <send> */
      {
      if (!bMXP)
        count--;     /* not output if not MXP */   
      if (c == ';')
        bInEntity = FALSE;
      } /* end of being inside a tag */
    else switch (c)
      {

      case MXP_BEGc:
        bInTag = TRUE;
        if (!bMXP)
          count--;     /* not output if not MXP */   
        break;

      case MXP_ENDc:   /* shouldn't get this case */
        if (!bMXP)
          count--;     /* not output if not MXP */   
        break;

      case MXP_AMPc:
        bInEntity = TRUE;
        if (!bMXP)
          count--;     /* not output if not MXP */   
        break;

      default:
        if (bMXP)
          {
          switch (c)
            {
            case '<':       /* < becomes &lt; */
            case '>':       /* > becomes &gt; */
              count += 3;    
              break;

            case '&':
              count += 4;    /* & becomes &amp; */
              break;

            case '"':        /* " becomes &quot; */
              count += 5;    
              break;

            } /* end of inner switch */
          }   /* end of MXP enabled */
      } /* end of switch on character */

     }   /* end of counting special characters */

  return count;
  } /* end of count_mxp_tags */


Next (once the memory is allocated) we convert the text stream, as described earlier.

Code fragment - add this to comm.c or whereever your low-level output routine is:


void convert_mxp_tags (const int bMXP, char * dest, const char *src, int length)
  {
char c;
const char * ps;
char * pd;
int bInTag = FALSE;
int bInEntity = FALSE;

  for (ps = src, pd = dest; 
       length > 0; 
       ps++, length--)
    {
    c = *ps;
    if (bInTag)  /* in a tag, eg. <send> */
      {
      if (c == MXP_ENDc)
        {
        bInTag = FALSE;
        if (bMXP)
          *pd++ = '>';
        }
      else if (bMXP)
        *pd++ = c;  /* copy tag only in MXP mode */
      } /* end of being inside a tag */
    else if (bInEntity)  /* in a tag, eg. <send> */
      {
      if (bMXP)
        *pd++ = c;  /* copy tag only in MXP mode */
      if (c == ';')
        bInEntity = FALSE;
      } /* end of being inside a tag */
    else switch (c)
      {
      case MXP_BEGc:
        bInTag = TRUE;
        if (bMXP)
          *pd++ = '<';
        break;

      case MXP_ENDc:    /* shouldn't get this case */
        if (bMXP)
          *pd++ = '>';
        break;

      case MXP_AMPc:
        bInEntity = TRUE;
        if (bMXP)
          *pd++ = '&';
        break;

      default:
        if (bMXP)
          {
          switch (c)
            {
            case '<':
              memcpy (pd, "&lt;", 4);
              pd += 4;    
              break;

            case '>':
              memcpy (pd, "&gt;", 4);
              pd += 4;    
              break;

            case '&':
              memcpy (pd, "&amp;", 5);
              pd += 5;    
              break;

            case '"':
              memcpy (pd, "&quot;", 6);
              pd += 6;    
              break;

            default:
              *pd++ = c;
              break;  /* end of default */

            } /* end of inner switch */
          }
        else
          *pd++ = c;  /* not MXP - just copy character */
        break;  

      } /* end of switch on character */

    }   /* end of converting special characters */
  } /* end of convert_mxp_tags */

Finally we modify "write_to_buffer" to use these two new routines.

Code fragment - modify your low-level output routine. The modified code is in bold:


/*
 * Append onto an output buffer.
 */
void write_to_buffer( DESCRIPTOR_DATA *d, const char *txt, int length )
{
int origlength;

    /*
     * Find length in case caller didn't.
     */
    if ( length <= 0 )
	length = strlen(txt);

  origlength = length;
  /* work out how much we need to expand/contract it */
  length += count_mxp_tags (d->mxp, txt, length);


    /*
     * Initial \n\r if needed.
     */
    if ( d->outtop == 0 && !d->fcommand )
    {
	d->outbuf[0]	= '\n';
	d->outbuf[1]	= '\r';
	d->outtop	= 2;
    }

    /*
     * Expand the buffer as needed.
     */
    while ( d->outtop + length >= d->outsize )
    {
	char *outbuf;

        if (d->outsize >= 32000)
	{
	    bug("Buffer overflow. Closing.\n\r",0);
	    close_socket(d);
	    return;
 	}
	outbuf      = alloc_mem( 2 * d->outsize );
	strncpy( outbuf, d->outbuf, d->outtop );
	free_mem( d->outbuf, d->outsize );
	d->outbuf   = outbuf;
	d->outsize *= 2;
    }

    /*
     * Copy.
     */
    convert_mxp_tags (d->mxp, d->outbuf + d->outtop, txt, origlength );
    d->outtop += length;
    return;
}

Telnet negotiation - does the player want to use MXP?

Add the flag

First, we need a flag to indicate if this particular player is using MXP...

Code fragment - add this to mud.h, merc.h, or whatever your standard include file is. The new code is in bold:



/*
 * Descriptor (channel) structure.
 */
struct	descriptor_data
{
    DESCRIPTOR_DATA *	next;
    DESCRIPTOR_DATA *	snoop_by;
    CHAR_DATA *		character;
    CHAR_DATA *		original;
    char *		host;
    sh_int		descriptor;
    sh_int		connected;

/* ... other definitions here ... */

    bool    mxp;   /* player using MXP flag */ 

};

MXP off initially

Initially they won't use MXP, so add this line to "init_descriptor" or whereever a new descriptor is created. The new code is in bold ...


    dnew = new_descriptor();
    dnew->descriptor	= desc;
    dnew->mxp = FALSE;   /* Initially MXP is off */

Telnet sequences

Some definitions for telnet negotiation. To use telnet negotiation, add a few lines to near the start of comm.c:


#define  TELOPT_MXP        '\x5B'

const unsigned char will_mxp_str  [] = { IAC, WILL, TELOPT_MXP, '\0' };
const unsigned char start_mxp_str [] = { IAC, SB, TELOPT_MXP, IAC, SE, '\0' };
const unsigned char do_mxp_str    [] = { IAC, DO, TELOPT_MXP, '\0' };
const unsigned char dont_mxp_str  [] = { IAC, DONT, TELOPT_MXP, '\0' };

Send IAC WILL MXP to the player

Once the player has connected, in the "nanny" routine you can send the "do you want to use MXP?" sequence. The new code is in bold:


  if (check_ban(d-&gt;host,BAN_PERMIT) &amp;&amp; !IS_SET(ch-&gt;act,PLR_PERMIT))
    {
        write_to_buffer(d,&quot;Your site has been banned from this mud.\n\r&quot;,0);
        close_socket(d);
        return;
    }

  /* telnet negotiation to see if they support MXP */

    write_to_buffer( d, will_mxp_str, 0 );

    if ( check_reconnect( d, argument, FALSE ) )
    {
        fOld = TRUE;
    }

Handle the response from the player

Non-compliant clients will just ignore the above sequence, which is fine - the MXP flag will stay off. However compliant clients will send either IAC DO MXP or IAC DONT MXP. We need to detect both. I added some code to "read_from_buffer" which handles reading of one line of player input. The new code is in bold...


/*
 * Transfer one line from input buffer to input line.
 */
void read_from_buffer( DESCRIPTOR_DATA *d )
{
    int i, j, k;
    unsigned char * p;

    /*
     * Hold horses if pending command already.
     */
    if ( d->incomm[0] != '\0' )
	return;


/* 
  Look for incoming telnet negotiation
*/


  for (p = d->inbuf; *p; p++)
    if (*p == IAC)
      {
      if (memcmp (p, do_mxp_str, strlen (do_mxp_str)) == 0)
        {
        turn_on_mxp (d);
        /* remove string from input buffer */
        memmove (p, &p [strlen (do_mxp_str)], strlen (&p [strlen (do_mxp_str)]) + 1);
        p--; /* adjust to allow for discarded bytes */
        } /* end of turning on MXP */
      else  if (memcmp (p, dont_mxp_str, strlen (dont_mxp_str)) == 0)
        {
        d->mxp = FALSE;
        /* remove string from input buffer */
        memmove (p, &p [strlen (dont_mxp_str)], strlen (&p [strlen (dont_mxp_str)]) + 1);
        p--; /* adjust to allow for discarded bytes */
        } /* end of turning off MXP */
      } /* end of finding an IAC */

Turn MXP on

Once the player's client has sent back the IAC DO MXP sequence, we need to respond with IAC SB MXP IAC SE - this tells the client that from now on we are sending MXP sequences. Directly after that we switch to "permanent secure" mode (indicating that during the rest of the session all MXP tags should be enabled, and then send down some <!ELEMENT> definitions, which we will use later on, namely:

Defining these tags now greatly simplifies the way we do our hyperlinks later on.


/* set up MXP */
void turn_on_mxp (DESCRIPTOR_DATA *d)
  {
  d->mxp = TRUE;  /* turn it on now */
 	write_to_buffer( d, start_mxp_str, 0 );
	write_to_buffer( d, MXPMODE (6), 0 );   /* permanent secure mode */
  write_to_buffer( d, MXPTAG ("!-- Set up MXP elements --"), 0);
  /* Exit tag */
  write_to_buffer( d, MXPTAG ("!ELEMENT Ex '<send>' FLAG=RoomExit"), 0);
  /* Room description tag */
  write_to_buffer( d, MXPTAG ("!ELEMENT rdesc '<p>' FLAG=RoomDesc"), 0);
  /* Get an item tag (for things on the ground) */
  write_to_buffer( d, MXPTAG 
      ("!ELEMENT Get \"<send href='"
           "get &#39;&name;&#39;|"
           "examine &#39;&name;&#39;|"
           "drink &#39;&name;&#39;"
       "' "
       "hint='RH mouse click to use this object|"
           "Get &desc;|"
           "Examine &desc;|"
           "Drink from &desc;"
       "'>\" ATT='name desc'"), 
      0);
  /* Drop an item tag (for things in the inventory) */
  write_to_buffer( d, MXPTAG 
      ("!ELEMENT Drop \"<send href='"
           "drop &#39;&name;&#39;|"
           "examine &#39;&name;&#39;|"
           "look in &#39;&name;&#39;|"
           "wear &#39;&name;&#39;|"
           "eat &#39;&name;&#39;|"
           "drink &#39;&name;&#39;"
       "' "
       "hint='RH mouse click to use this object|"
           "Drop &desc;|"
           "Examine &desc;|"
           "Look inside &desc;|"
           "Wear &desc;|"
           "Eat &desc;|"
           "Drink &desc;"
       "'>\" ATT='name desc'"), 
      0);
  /* Bid an item tag (for things in the auction) */
  write_to_buffer( d, MXPTAG 
      ("!ELEMENT Bid \"<send href='bid &#39;&name;&#39;' "
       "hint='Bid for &desc;'>\" "
       "ATT='name desc'"), 
      0);
  /* List an item tag (for things in a shop) */
  write_to_buffer( d, MXPTAG 
      ("!ELEMENT List \"<send href='buy &#39;&name;&#39;' "
       "hint='Buy &desc;'>\" "
       "ATT='name desc'"), 
      0);
  /* Player tag (for who lists, tells etc.) */
  write_to_buffer( d, MXPTAG 
      ("!ELEMENT Player \"<send href='tell &#39;&name;&#39; ' "
       "hint='Send a message to &name;' prompt>\" "
       "ATT='name'"), 
      0);
  } /* end of turn_on_mxp */

Notice the use of &#39; in various places. This is the HTML code for "single quote". It is needed in various places where things might be multiple words, so we can send: get 'large sword'


Adding hyperlinks

With all this housekeeping out of the way, we can now easily add hyperlinks whereever we want to.

Example of adding hyperlinks to tells

For example, when doing a player tell, we might want to make the player's name a hyperlink so we can reply to them by clicking on the link. The new code is in bold ...

Code fragment - add this to comm.c, or whatever you do your tells.


    act( "You tell $N '$t'", ch, argument, victim, TO_CHAR );
    act_new(MXPTAG ("player $n") 
            "$n" 
            MXPTAG ("/player")            
            " tells you '$t'",ch,argument,victim,TO_VICT,POS_DEAD);
    victim->reply	= ch;

Adding hyperlinks to auto-exits

Code fragment - add this to act_info.c, or whatever you do your "do_exits".



if ( fAuto )
 {
 strcat( buf, " " );
 strcat (buf, MXPTAG ("Ex"));
 strcat( buf, dir_name[door] );
 strcat (buf, MXPTAG ("/Ex"));
 }

Adding hyperlinks to shop lists

Code fragment - add this to act_obj.c, or whatever you do your "do_list".


  for ( obj = keeper->carrying; obj; obj = obj->next_content )
  {

 
/* Find length of first word in the object name */
 int i = 0;
  char * p = obj->name;
  for ( ; *p && !isspace (*p); p++, i++)
    ;

  if ( obj->wear_loc == WEAR_NONE
      &&   can_see_obj( ch, obj )
      &&   ( cost = get_cost( keeper, obj, TRUE ) ) > 0 
      &&   ( arg[0] == '\0'  
         ||  is_name(arg,obj->name) ))
      {
    if ( !found )
    {
        found = TRUE;
        send_to_char( "[Lv Price Qty] Item\n\r", ch );
    }

    if (IS_OBJ_STAT(obj,ITEM_INVENTORY))
        sprintf(buf,"[%2d %5d -- ] "
                MXPTAG ("list '%.*s' '%s'")           
                "%s"
                MXPTAG ("/list") 
                "\n\r",
      obj->level,cost,
      i, obj->name, obj->short_descr,
      obj->short_descr);
    else
    {
        count = 1;

        while (obj->next_content != NULL 
        && obj->pIndexData == obj->next_content->pIndexData
        && !str_cmp(obj->short_descr,
              obj->next_content->short_descr))
        {
      obj = obj->next_content;
      count++;
        }
        sprintf(buf,"[%2d %5d %2d ] "
                MXPTAG ("list '%.*s' '%s'")           
                "%s"
                MXPTAG ("/list") 
                "\n\r",
      obj->level,cost,count,
      i, obj->name, obj->short_descr,
      obj->short_descr);
    }
    send_to_char( buf, ch );
      }
  }

Adding hyperlinks to objects in rooms and inventory

This is the most complicated one, because we really have three descriptions of each item:

We need to make fairly extensive changes to "show_list_to_char", to record the other two names that it doesn't usually bother with, and to distinguish between having the item in our inventory (in which case we might want to drop it), or on the ground (in which case we might want to get it).

I added another argument to show_list_to_char namely "const int iDefaultAction". This controls the sort of tag that is output.

Here is the entire changed routine, with my changes in bold. This is from act_info.c in ROM.


/*
 * Show a list to a character.
 * Can coalesce duplicated items.
 */
void show_list_to_char( OBJ_DATA *list, 
                        CHAR_DATA *ch, 
                        bool fShort, 
                        bool fShowNothing, 
                        const int iDefaultAction  )
{
    char buf[MAX_STRING_LENGTH];
    BUFFER *output;
    char **prgpstrShow;
    char **prgpstrName;        /* for MXP */
    char **prgpstrShortName;   /* for MXP */
    int *prgnShow;
    char *pstrShow;
    char *pstrName;            /* for MXP */
    char *pstrShortName;       /* for MXP */
    OBJ_DATA *obj;
    int nShow;
    int iShow;
    int count;
    bool fCombine;
    char * pAction = NULL;    /* for MXP */

    if ( ch->desc == NULL )
      return;

  /* work out which MXP tag to use */

  switch (iDefaultAction)
    {
    case eItemGet:  pAction = "Get"; break;   /* item on ground */
    case eItemDrop: pAction = "Drop"; break;   /* item in inventory */

    } /* end of switch on action */

    /*
     * Alloc space for output lines.
     */
    output = new_buf();

    count = 0;
    for ( obj = list; obj != NULL; obj = obj->next_content )
     count++;
    prgpstrShow = alloc_mem( count * sizeof(char *) );
    prgpstrName = alloc_mem( count * sizeof(char *) );
    prgpstrShortName = alloc_mem( count * sizeof(char *) );
    prgnShow    = alloc_mem( count * sizeof(int)    );
    nShow = 0;

    /*
     * Format the list of objects.
     */
    for ( obj = list; obj != NULL; obj = obj->next_content )
    { 
      if ( obj->wear_loc == WEAR_NONE && can_see_obj( ch, obj )) 
      {
      pstrShow = format_obj_to_char( obj, ch, fShort );
      pstrName = obj->name;
      pstrShortName = obj->short_descr;
      fCombine = FALSE;

      if ( IS_NPC(ch) || IS_SET(ch->comm, COMM_COMBINE) )
      {
    /*
     * Look for duplicates, case sensitive.
     * Matches tend to be near end so run loop backwords.
     */
    for ( iShow = nShow - 1; iShow >= 0; iShow-- )
    {
        if ( !strcmp( prgpstrShow[iShow], pstrShow ) )
        {
        prgnShow[iShow]++;
        fCombine = TRUE;
        break;
        }
    }
      }

      /*
       * Couldn't combine, or didn't want to.
       */
      if ( !fCombine )
      {
      prgpstrShow [nShow] = str_dup( pstrShow );
      prgpstrName [nShow] = str_dup( pstrName );
      prgpstrShortName [nShow] = str_dup( pstrShortName );
      prgnShow    [nShow] = 1;
      nShow++;
      }
  }
    }

    /*
     * Output the formatted list.
     */
    for ( iShow = 0; iShow < nShow; iShow++ )
    {
  if (prgpstrShow[iShow][0] == '\0')
  {
      free_string(prgpstrShow[iShow]);
      free_string(prgpstrName[iShow]);
      free_string(prgpstrShortName[iShow]);
      continue;
  }

  if ( IS_NPC(ch) || IS_SET(ch->comm, COMM_COMBINE) )
  {
      if ( prgnShow[iShow] != 1 )
      {
      sprintf( buf, "(%2d) ", prgnShow[iShow] );
      add_buf(output,buf);
      }
      else
      {
      add_buf(output,"     ");
      }
  }
  if (pAction)
    {
/* work out length of first word */   
    int i = 0;
    char * p = prgpstrName[iShow];
    for ( ; *p && !isspace (*p); p++, i++)
      ;
    sprintf (buf, MXPTAG ("%s '%.*s' '%s'"), 
             pAction, i, prgpstrName[iShow], prgpstrShortName[iShow]);
    add_buf(output,buf);
    } 
  add_buf(output,prgpstrShow[iShow]);
  if (pAction)
    {
    sprintf (buf, MXPTAG ("/%s"), pAction);
    add_buf(output,buf);
    }
  add_buf(output,"\n\r");
  free_string( prgpstrShow[iShow] );
  free_string(prgpstrName[iShow]);
  free_string(prgpstrShortName[iShow]);
    }

    if ( fShowNothing && nShow == 0 )
    {
  if ( IS_NPC(ch) || IS_SET(ch->comm, COMM_COMBINE) )
      send_to_char( "     ", ch );
  send_to_char( "Nothing.\n\r", ch );
    }
    page_to_char(buf_string(output),ch);

    /*
     * Clean up.
     */
    free_buf(output);
    free_mem( prgpstrShow, count * sizeof(char *) );
    free_mem( prgpstrName, count * sizeof(char *) );
    free_mem( prgpstrShortName, count * sizeof(char *) );
    free_mem( prgnShow,    count * sizeof(int)    );

    return;
}

Patches for SMAUG 1.4a

Patches to upgrade smaug1.4a to add MXP as described above (plus a couple of other items) are available from:

http://www.gammon.com.au/files/smaug/smaug1.4a_mxp_diffs.gz (8 Kb)

To apply these:

  1. Download the above file
  2. Unzip the SMAUG distribution (if required):
    
    tar xzvf smaug1.4a.tgz
    
  3. Put the patches into the "dist" directory (under "smaug")
  4. Unzip the patches:
    
    gunzip smaug1.4a_mxp_diffs.gz
    
  5. Apply the patches:
    
    patch -p3 < smaug1.4a_mxp_diffs
    
  6. Do the compile:
    
    cd src
    make 
    
  7. If all is OK, run the server:
    
    tcsh startup
    

SMAUG 1.4a distribution with MXP added

If you are using "stock" SMAUG 1.4a (ie. you haven't made any changes to the C code yourself) then you can just download the source with the patches already applied. This is at:

http://www.gammon.com.au/files/smaug/smaug1.4a_mxp.tgz (1.50 Mb)

Then simply unzip the source and compile.


Patches for ROM 2.4b6

Patches to upgrade ROM 2.4b6 to add MXP as described above are available from:

http://www.gammon.com.au/files/rom/Rom24_mxp_diffs.gz (6 Kb)

To apply these:

  1. Download the above file
  2. Unzip the ROM distribution (if required):
    
    tar xzvf Rom24b6.tar.gz
    
  3. Put the patches into the "Rom24" directory
  4. Unzip the patches:
    
    gunzip smaug1.4a_mxp_diffs.gz
    
  5. Apply the patches:
    
    patch -p2 < Rom24_mxp_diffs
    
  6. If you are compiling with Cygwin you will probably need to change lines 3 and 4 of the file Makefile as follows:
    
    NOCRYPT = -DNOCRYPT
    C_FLAGS =  -Wall $(PROF) $(NOCRYPT) -DOLD_RAND -Dunix
    
  7. Do the compile:
    
    cd src
    make 
    
  8. If all is OK, run the server:
    
    cd ../area
    ../src/rom.exe
    

ROM 2.4b6 distribution with MXP added

If you are using "stock" ROM 2.4b6 (ie. you haven't made any changes to the C code yourself) then you can just download the source with the patches already applied. This is at:

http://www.gammon.com.au/files/rom/Rom24b6_mxp.tar.gz (865 Kb)

Then simply unzip the source and compile.


Conclusion

This page shows how, after some initial setup, adding MXP tags to a server is quite straightforward. There are other places you could add tags, but the examples here should get you started. Any comments are welcome.


[Back] [Home]
Written by Nick Gammon - 5K

Comments to Gammon Software support

[Best viewed with any browser - 2K]    Internet Contents Rating Association (ICRA) - 2K    [Hosted at HostDash]

Page updated on Tuesday, 6 December 2005