The Mail Module
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.
To start, create a new structure to represent a piece of sent mail. Add this to
mail.c:
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;Now, write some functions for a couple procedures that will be needed down the road -- the creation and deletion of mail:
// 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); }Now that the basic steps for creating and deleting mail are complete, we can set up some commands to allow players to send mail:
// mails a message to the specified person. This command must take the // following form: // mailWe 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:// // 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, " "); // 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); } }
// 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); }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:
// maps charName to a list of mail they have received HASHTABLE *mail_table = NULL;The hashtable will also need to be initialized. This can be done in the init_mail function:
// 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); }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:
// 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); }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:
// 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); }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.