<html> <head> <link href="../tutorial.css" rel="stylesheet" type="text/css"> </head> <body> <div class="header"> The NakedMud Tutorial :: Mail Module </div> <!-- content starts here --> <div class="content-wrap"><div class="content-body-wrap"><div class="content"> <div class="head">The Mail Module</div> <div class="info"> This basic incarnation of the mail module will allow players to send written messages to one another. Send mail will not be persistent (it will not save over reboots or crashes). That feature will be postponed until the section on Storage Sets. <p></p> To start, create a new structure to represent a piece of sent mail. Add this to <i>mail.c</i>: <pre class="code"> typedef struct { char *sender; // name of the char who sent this mail char *time; // the time it was sent at BUFFER *mssg; // the accompanying message } MAIL_DATA; </pre> Now, write some functions for a couple procedures that will be needed down the road -- the creation and deletion of mail: <pre class="code"> // create a new piece of mail. MAIL_DATA *newMail(CHAR_DATA *sender, const char *mssg) { MAIL_DATA *mail = malloc(sizeof(MAIL_DATA)); mail->sender = strdup(charGetName(sender)); mail->time = strdup(get_time()); mail->mssg = newBuffer(strlen(mssg)); bufferCat(mail->mssg, mssg); return mail; } // free all of the memory that was allocated to make this piece of mail void deleteMail(MAIL_DATA *mail) { if(mail->sender) free(mail->sender); if(mail->time) free(mail->time); if(mail->mssg) deleteBuffer(mail->mssg); free(mail); } </pre> Now that the basic steps for creating and deleting mail are complete, we can set up some commands to allow players to send mail: <pre class="code"> // mails a message to the specified person. This command must take the // following form: // mail <person> // // The contents of the character's notepad will be used as the body of the // message. The character's notepad must not be empty. COMMAND(cmd_mail) { // make sure the character exists if(!player_exists(arg)) send_to_char(ch, "Noone named %s is registered on %s.\r\n", arg, "<insert mud name here>"); // make sure we have a socket - we'll need access to its notepad else if(!charGetSocket(ch)) send_to_char(ch, "Only characters with sockets can send mail!\r\n"); // make sure our notepad is not empty else if(!*bufferString(socketGetNotepad(charGetSocket(ch)))) send_to_char(ch, "Your notepad is empty. " "First, try writing something with {cwrite{n.\r\n"); // the character exists. Let's parse the items and send the mail else { MAIL_DATA *mail = newMail(ch, bufferString(socketGetNotepad(charGetSocket(ch)))); // if we had some way of storing mail, we'd now do that. But since we // don't, let's just delete the mail. //*********** // FINISH ME //*********** deleteMail(mail); // let the character know we've sent the mail send_to_char(ch, "You send a message to %s.\r\n", arg); } } </pre> We will also want to add this new command to the main command table. To do that, make a call to add_cmd in init_mail: <pre class="code"> // boot up the mail module void init_mail(void) { // add all of the commands that come with this module add_cmd("mail", NULL, cmd_mail, "player", FALSE); } </pre> The first incarnation of the mail module is just about complete. The only thing needed now is a way to store mail that has been sent. To do this, there needs to be some way of mapping characters to their received mail, and a command for them to access that mail. Let us create a hashtable to map character names to their mail. Do this at the top of mail.c, just before the MAIL_DATA structure is defined: <pre class="code"> // maps charName to a list of mail they have received HASHTABLE *mail_table = NULL; </pre> The hashtable will also need to be initialized. This can be done in the init_mail function: <pre class="code"> // boot up the mail module void init_mail(void) { // initialize the mail table mail_table = newHashtable(); // add all of the commands that come with this module add_cmd("mail", NULL, cmd_mail, "player", FALSE); } </pre> Earlier, the mail command was left unfinished because there was no way to store mail. Now that there is a hashtable for serving this function, go back to cmd_mail and fill in the unfinishedp art: <pre class="code"> // the character exists. Let's parse the items and send the mail else { MAIL_DATA *mail = newMail(ch, bufferString(socketGetNotepad(charGetSocket(ch)))); // see if the receiver already has a mail list LIST *mssgs = hashGet(mail_table, arg); // if he doesn't, create one and add it to the hashtable if(mssgs == NULL) { mssgs = newList(); hashPut(mail_table, arg, mssgs); } // add the new mail to our mail list listPut(mssgs, mail); // let the character know we've sent the mail send_to_char(ch, "You send a message to %s.\r\n", arg); } </pre> It's all well and good being able to send mail, but what about receiving mail? Here is a way to write a command for performing that function: <pre class="code"> // checks to see if the character has any mail. If he does, convert each piece // of mail into an object, and transfer them all into the character's inventory. COMMAND(cmd_receive) { // Remove the character's mail list from our mail table LIST *mail_list = hashRemove(mail_table, charGetName(ch)); // make sure the list exists if(mail_list == NULL || listSize(mail_list) == 0) send_to_char(ch, "You have no new mail.\r\n"); // hand over all of the mail else { // go through each piece of mail, make an object for it, // and transfer the new object to us LIST_ITERATOR *mail_i = newListIterator(mail_list); MAIL_DATA *mail = NULL; ITERATE_LIST(mail, mail_i) { OBJ_DATA *obj = newObj(); objSetName (obj, "a letter"); objSetKeywords (obj, "letter, mail"); objSetRdesc (obj, "A letter is here."); objSetMultiName (obj, "A stack of %d letters"); objSetMultiRdesc(obj, "A stack of %d letters are here."); bprintf(objGetDescBuffer(obj), "Sender : %s\r\n" "Date sent: %s\r\n" "%s", mail->sender, mail->time, bufferString(mail->mssg)); // give the object to the character obj_to_game(obj); obj_to_char(obj, ch); } deleteListIterator(mail_i); // let the character know how much mail he received send_to_char(ch, "You receive %d letter%s.\r\n", listSize(mail_list), (listSize(mail_list) == 1 ? "" : "s")); } // delete the mail list, and all of its contents if(mail_list != NULL) deleteListWith(mail_list, deleteMail); } </pre> Like with cmd_mail, add the receive command to the global command table. You can copy and paste from what already exists. Once that is done, you have the first pass at a mail module. You could probably add lots to this module, like parcels of items, fees for sending mail, checks to make sure mail is only sent at mailboxes, etc... but for our purposes of demonstrating how modules work, this will suffice. The rest of the features will be left to you as an exercise. </div> <!-- content ends here--> </div></div></div> <!-- navigation starts here --> <div class="nav-wrap"><div class="nav"> <iframe src="nav.html" height="100%" width="100%" scrolling=no frameborder=0> </iframe> <!-- navigation ends here --> </div></div> <!--div class="footer">Edit Date: Nov 15, 2008. By Geoff Hollis</div--> </body> </html>