aux_data.html 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. <html>
  2. <head>
  3. <link href="../tutorial.css" rel="stylesheet" type="text/css">
  4. </head>
  5. <body>
  6. <div class="header">
  7. The NakedMud Tutorial :: Title Here
  8. </div>
  9. <!-- content starts here -->
  10. <div class="content-wrap"><div class="content-body-wrap"><div class="content">
  11. <div class="head">Auxiliary Data</div>
  12. <div class="info">
  13. Auxiliary Data is perhaps the most important part of NakedMud's design.
  14. Auxiliary Data allows you to add new variables to the various datatypes
  15. NakedMud handles within the game (objects, rooms, mobiles, accounts, sockets)
  16. without touching the files that house those data structures. The biggest gain
  17. from this is the ability to modularize your code by what it is intended to do;
  18. all of the code related to combat - including new variables that must be
  19. created - can stay in one module. As was mentioned in modules section, this will
  20. help with the ability to maintain and distribute pieces of your code in the
  21. future. Designing new auxiliary data is very simple, but it does require a bit
  22. of effort if you have not done it in the past. This tutorial will walk you
  23. through the steps of writing and installing new auxiliary data.
  24. </div>
  25. <div class="head">Recap</div>
  26. <div class="info">
  27. In the section on modules, we designed a 'proof of concept' for a mail system.
  28. One feature we did not add was the ability for unreceived mail to be persistent.
  29. If the MUD ever crashed or rebooted, all mail not yet received would be lost.
  30. This inconvenience was addressed in the section on storage sets. However, we
  31. ran into another problem with the saving procedure being inefficient: to save
  32. any change to someone's unreceived mail status, we effectively had to re-save
  33. everyone's unreceived mail. This section will address the problem by attaching
  34. a character's unread mail to that character's actual data structure. Now,
  35. whenever we want to the unread mail for a character, we will only have to save
  36. that character, and not all unread mail in existence.
  37. </div>
  38. <div class="head">Creating Auxiliary Data</div>
  39. <div class="info">
  40. The first thing we will need to do is add the headers for interacting with
  41. auxiliary data and storage sets. Do this where we include all the other headers
  42. we need for interacting with core features of the mud. Add the new headers right
  43. after the <i>handler.h</i> header:
  44. <pre class="code">
  45. #include "../handler.h" // for giving mail to characters
  46. #include "../storage.h" // for saving/loading auxiliary data
  47. #include "../auxiliary.h" // for creating new auxiliary data
  48. #include "../world.h" // for loading offline chars receiving mail
  49. </pre>
  50. Because we will save unread mail as auxiliary data within character data, we
  51. will no longer need a hashtable for keeping everything stored. Therefore, search
  52. and destroy all references to the mail_table. Right after we finish including
  53. all of our headers, delete:
  54. <pre class="code">
  55. // maps charName to a list of mail they have received
  56. HASHTABLE *mail_table = NULL;
  57. </pre>
  58. In cmd_mail, delete the entire section within the last else statement:
  59. <pre class="code">
  60. MAIL_DATA *mail = newMail(ch, bufferString(socketGetNotepad(charGetSocket(ch))));
  61. // see if the receiver already has a mail list
  62. LIST *mssgs = hashGet(mail_table, arg);
  63. // if he doesn't, create one and add it to the hashtable
  64. if(mssgs == NULL) {
  65. mssgs = newList();
  66. hashPut(mail_table, arg, mssgs);
  67. }
  68. // add the new mail to our mail list
  69. listPut(mssgs, mail);
  70. // let the character know we've sent the mail
  71. send_to_char(ch, "You send a message to %s.\r\n", arg);
  72. </pre>
  73. At the very start of cmd_receive, remove the reference to hashRemove, and for
  74. the time being, set mail\_list to NULL:
  75. <pre class="code">
  76. // Remove the character's mail list from our mail table
  77. LIST *mail_list = hashRemove(mail_table, charGetName(ch));
  78. </pre>
  79. Finally, remove our creation of the mail_table in init_mail:
  80. <pre class="code">
  81. // initialize our mail table
  82. mail_table = newHashtable();
  83. </pre>
  84. Before we start writing our auxiliary data, we are going to have to provide a
  85. couple functions for handling the saving, reading, and copying of mail. Right
  86. after newMail and deleteMail, add these 3 new functions:
  87. <pre class="code">
  88. // parse a piece of mail from a storage set
  89. MAIL_DATA *mailRead(STORAGE_SET *set) {
  90. // allocate some memory for the mail
  91. MAIL_DATA *mail = malloc(sizeof(MAIL_DATA));
  92. mail->mssg = newBuffer(1);
  93. // read in all of our values
  94. mail->sender = strdup(read_string(set, "sender"));
  95. mail->time = strdup(read_string(set, "time"));
  96. bufferCat(mail->mssg, read_string(set, "mssg"));
  97. return mail;
  98. }
  99. // represent a piece of mail as a storage set
  100. STORAGE_SET *mailStore(MAIL_DATA *mail) {
  101. // create a new storage set
  102. STORAGE_SET *set = new_storage_set();
  103. store_string(set, "sender", mail->sender);
  104. store_string(set, "time", mail->time);
  105. store_string(set, "mssg", bufferString(mail->mssg));
  106. return set;
  107. }
  108. // copy a piece of mail. This will be needed by our auxiliary
  109. // data copy functions
  110. MAIL_DATA *mailCopy(MAIL_DATA *mail) {
  111. MAIL_DATA *newmail = malloc(sizeof(MAIL_DATA));
  112. newmail->sender = strdup(mail->sender);
  113. newmail->time = strdup(mail->time);
  114. newmail->mssg = bufferCopy(mail->mssg);
  115. return newmail;
  116. }
  117. </pre>
  118. Now we're ready to start writing our auxiliary data. Auxiliary Data require 7
  119. things: a new structure that is the auxiliary data, a function that constructs
  120. the auxiliary data, a function that deletes the auxiliary data, a function that
  121. copies the auxiliary data, a function that copies the auxiliary data to another
  122. instance of the same auxiliary data, a function that reads the auxiliary data
  123. from a storage set, and a function that writes the auxiliary data to a storage
  124. set. Below, we lay out all of those things. There is quite a bit of code but,
  125. as you will notice, it is all very simple (perhaps with the exception of the
  126. read and write functions). Right below the the mailCopy function, add the
  127. following bit of code:
  128. <pre class="code">
  129. // our mail auxiliary data.
  130. // Holds a list of all the unreceived mail a person has
  131. typedef struct {
  132. LIST *mail; // our list of unread mail
  133. } MAIL_AUX_DATA;
  134. // create a new instance of mail aux data, for us to put onto a character
  135. MAIL_AUX_DATA *newMailAuxData(void) {
  136. MAIL_AUX_DATA *data = malloc(sizeof(MAIL_AUX_DATA));
  137. data->mail = newList();
  138. return data;
  139. }
  140. // delete a character's mail aux data
  141. void deleteMailAuxData(MAIL_AUX_DATA *data) {
  142. if(data->mail) deleteListWith(data->mail, deleteMail);
  143. free(data);
  144. }
  145. // copy one mail aux data to another
  146. void mailAuxDataCopyTo(MAIL_AUX_DATA *from, MAIL_AUX_DATA *to) {
  147. if(to->mail) deleteListWith(to->mail, deleteMail);
  148. if(from->mail) to->mail = listCopyWith(from->mail, mailCopy);
  149. else to->mail = newList();
  150. }
  151. // return a copy of a mail aux data
  152. MAIL_AUX_DATA *mailAuxDataCopy(MAIL_AUX_DATA *data) {
  153. MAIL_AUX_DATA *newdata = newMailAuxData();
  154. mailAuxDataCopyTo(data, newdata);
  155. return newdata;
  156. }
  157. // parse a mail aux data from a storage set
  158. MAIL_AUX_DATA *mailAuxDataRead(STORAGE_SET *set) {
  159. MAIL_AUX_DATA *data = malloc(sizeof(MAIL_AUX_DATA));
  160. data->mail = gen_read_list(read_list(set, "mail"), mailRead);
  161. return data;
  162. }
  163. // represent a mail aux data as a storage set
  164. STORAGE_SET *mailAuxDataStore(MAIL_AUX_DATA *data) {
  165. STORAGE_SET *set = new_storage_set();
  166. store_list(set, "mail", gen_store_list(data->mail, mailStore));
  167. return set;
  168. }
  169. </pre>
  170. Finally, we will want to ensure this new auxiliary data exists on characters.
  171. When our mail module boots up, we will want to install this new bit of
  172. auxiliary data:
  173. <pre class="code">
  174. // boot up the mail module
  175. void init_mail(void) {
  176. // install our auxiliary data
  177. auxiliariesInstall("mail_aux_data",
  178. newAuxiliaryFuncs(AUXILIARY_TYPE_CHAR,
  179. newMailAuxData, deleteMailAuxData,
  180. mailAuxDataCopyTo, mailAuxDataCopy,
  181. mailAuxDataStore, mailAuxDataRead));
  182. // add all of the commands that come with this module
  183. add_cmd("mail", NULL, cmd_mail, "player", FALSE);
  184. add_cmd("receive", NULL, cmd_receive, "player", FALSE);
  185. }
  186. </pre>
  187. Our mail auxiliary data is installed and completely functional! Now, we will
  188. want to go back to cmd_mail and cmd_receive to ensure they use the auxiliary
  189. data in replacement of the hashtable we used before. Let's start with the new
  190. code for cmd_mail. In the last else block, add the following bit of code:
  191. <pre class="code">
  192. // create the new piece of mail
  193. MAIL_DATA *mail = newMail(ch, bufferString(socketGetNotepad(charGetSocket(ch))));
  194. // get a copy of the player, send mail, and save
  195. CHAR_DATA *recv = get_player(arg);
  196. send_to_char(recv, "You have new mail.\r\n");
  197. // let's pull out the character's mail aux data, and add the new piece
  198. MAIL_AUX_DATA *maux = charGetAuxiliaryData(recv, "mail_aux_data");
  199. listPut(maux->mail, mail);
  200. save_player(recv);
  201. // get rid of our reference, and extract from game if need be
  202. unreference_player(recv);
  203. // let the character know we've sent the mail
  204. send_to_char(ch, "You send a message to %s.\r\n", arg);
  205. </pre>
  206. Now make it so players can receive their unread mail. At the very start of
  207. cmd_receive, add the following bit of code:
  208. <pre class="code">
  209. COMMAND(cmd_receive) {
  210. // Remove the character's mail list from our mail table
  211. MAIL_AUX_DATA *maux = charGetAuxiliaryData(ch, "mail_aux_data");
  212. LIST *mail_list = maux->mail;
  213. // replace our old list with a new one. Our old one will be deleted soon
  214. maux->mail = newList();
  215. </pre>
  216. Players can now send and receive mail.
  217. </div>
  218. <!-- content ends here-->
  219. </div></div></div>
  220. <!-- navigation starts here -->
  221. <div class="nav-wrap"><div class="nav">
  222. <iframe src="nav.html" height="100%" width="100%" scrolling=no frameborder=0>
  223. </iframe>
  224. <!-- navigation ends here -->
  225. </div></div>
  226. <!--div class="footer">Edit Date: Nov 15, 2008. By Geoff Hollis</div-->
  227. </body>
  228. </html>