storage.html 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  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 :: Storage Sets
  8. </div>
  9. <!-- content starts here -->
  10. <div class="content-wrap"><div class="content-body-wrap"><div class="content">
  11. <div class="head">Storage Sets</div>
  12. <div class="info">
  13. Storage sets are a big part of NakedMud. They serve a few important purposes:
  14. They simplify the process of saving data from files by eliminating your need to
  15. come up with formatting schemes for your flatfiles. They also eliminate your
  16. need to write file parsers to extract data from files; the process of retrieving
  17. information from a file is reduced to querying for the value of some key. As we
  18. will learn later, they also play an integral role in the process of saving and
  19. loading auxiliary data.
  20. <p></p>
  21. In this section, we will learn the ropes of storage sets. We'll see how to
  22. store and read lists and strings. The other data types storage sets can deal
  23. with (ints, bools, doubles, longs) are handled in the exact same way as strings,
  24. except with different function names. After this tutorial, you should be able
  25. to extrapolate how these other data types interact with storage sets. In the
  26. next section on auxiliary data, we will examine how storage sets work in
  27. conjunction with auxiliary data.
  28. </div>
  29. <div class="head">The Mail Module</div>
  30. <div class="info">
  31. In the previous section, we designed a 'proof of concept' for a mail system.
  32. This section will build on top of it. If you did not complete the previous
  33. tutorial on the mail module, you can download the source code here.
  34. </div>
  35. <div class="head">Storage Set Basics</div>
  36. <div class="info">
  37. The first thing that is needed are the headers for interacting with storage
  38. sets. Along with all of the other includes, add:
  39. <pre class="code">
  40. #include "../handler.h" // for giving mail to characters
  41. #include "../storage.h" // for saving/loading mail
  42. </pre>
  43. Next, define a file where mail will be stored when the mud is down. The MUD's
  44. lib directory seems like an ideal candidate directory. Define the location for
  45. storing mail by where the table for storing mail is located:
  46. <pre class="code">
  47. // this is the file we will save all unreceived mail in, when the mud is down
  48. #define MAIL_FILE "../lib/misc/mail"
  49. // maps charName to a list of mail they have received
  50. HASHTABLE *mail_table = NULL;
  51. </pre>
  52. Two things needed are functions for converting both ways between MAIL_DATA
  53. and STORAGE_SETS. By convention, these functions are called xxxStore and
  54. xxxRead, where xxx is what is being converted to and from a STORAGE_SET. Get
  55. those functions set up, just below the <i>newMail</i> and <i>deleteMail</i>
  56. functions:
  57. <pre class="code">
  58. // parse a piece of mail from a storage set
  59. MAIL_DATA *mailRead(STORAGE_SET *set) {
  60. // allocate some memory for the mail
  61. MAIL_DATA *mail = malloc(sizeof(MAIL_DATA));
  62. mail->mssg = newBuffer(1);
  63. // read in all of our values
  64. mail->sender = strdup(read_string(set, "sender"));
  65. mail->time = strdup(read_string(set, "time"));
  66. bufferCat(mail->mssg, read_string(set, "mssg"));
  67. return mail;
  68. }
  69. // represent a piece of mail as a storage set
  70. STORAGE_SET *mailStore(MAIL_DATA *mail) {
  71. // create a new storage set
  72. STORAGE_SET *set = new_storage_set();
  73. store_string(set, "sender", mail->sender);
  74. store_string(set, "time", mail->time);
  75. store_string(set, "mssg", bufferString(mail->mssg));
  76. return set;
  77. }
  78. </pre>
  79. Now that there is actually the ability to store and read mail, create two
  80. functions for actually doing the storing and reading:
  81. <pre class="code">
  82. // saves all of our unreceived mail to disk
  83. void save_mail(void) {
  84. // make a storage set to hold all our mail
  85. STORAGE_SET *set = new_storage_set();
  86. // make a list of name:mail pairs, and store it in the set
  87. STORAGE_SET_LIST *list = new_storage_list();
  88. // iterate across all of the people who have not received mail, and
  89. // store their names in the storage list, along with their mail
  90. HASH_ITERATOR *mail_i = newHashIterator(mail_table);
  91. const char *name = NULL;
  92. LIST *mail = NULL;
  93. ITERATE_HASH(name, mail, mail_i) {
  94. // create a new storage set that holds each name:mail pair,
  95. // and add it to our list of all name:mail pairs
  96. STORAGE_SET *one_pair = new_storage_set();
  97. store_string (one_pair, "name", name);
  98. store_list (one_pair, "mail", gen_store_list(mail, mailStore));
  99. storage_list_put(list, one_pair);
  100. } deleteHashIterator(mail_i);
  101. // make sure we add the list of name:mail pairs we want to save
  102. store_list(set, "list", list);
  103. // now, store our set in the mail file, and clean up our mess
  104. storage_write(set, MAIL_FILE);
  105. storage_close(set);
  106. }
  107. // loads all of our unreceived mail from disk
  108. void load_mail(void) {
  109. // parse our storage set
  110. STORAGE_SET *set = storage_read(MAIL_FILE);
  111. // make sure the file existed and wasn't empty
  112. if(set == NULL) return;
  113. // get the list of all name:mail pairs, and parse each one
  114. STORAGE_SET_LIST *list = read_list(set, "list");
  115. STORAGE_SET *one_pair = NULL;
  116. while( (one_pair = storage_list_next(list)) != NULL) {
  117. const char *name = read_string(one_pair, "name");
  118. LIST *mail = gen_read_list(read_list(one_pair, "mail"), mailRead);
  119. hashPut(mail_table, name, mail);
  120. }
  121. // Everything is parsed! Now it's time to clean up our mess
  122. storage_close(set);
  123. }
  124. </pre>
  125. This code may be a bit ugly to the untrained eye. However, there are some very
  126. useful nuggets of knowledge buried within it. If you are having troubles
  127. understanding what is going on, it is highly suggested that you take a few
  128. minutes to trace through these two functions and figure out what is going on.
  129. Once we are completely done this section, it may also help to write a couple
  130. mails to yourself and examine what the mail file looks like. The file's
  131. structure might help elucidate many of the things that are going on in these
  132. two functions.
  133. Now that our save and load functions are written, we have to make sure they are
  134. called appropriately. We will want to load up unread mail when the mail module
  135. initialized, and we will want to make sure we update the contents of the mail
  136. file whenever mail is sent or received. At the end of cmd_mail, make sure mail
  137. is saved:
  138. <pre class="code">
  139. // let the character know we've sent the mail
  140. send_to_char(ch, "You send a message to %s.\r\n", arg);
  141. // save all unread mail
  142. save_mail();
  143. </pre>
  144. At the end of cmd_receive, make sure mail is saved:
  145. <pre class="code">
  146. // let the character know how much mail he received
  147. send_to_char(ch, "You receive %d letter%s.\r\n",
  148. listSize(mail_list), (listSize(mail_list) == 1 ? "" : "s"));
  149. // update the unread mail in our mail file
  150. save_mail();
  151. </pre>
  152. Finally, ensure we load up all unread mail when we initialize the module:
  153. <pre class="code">
  154. // boot up the mail module
  155. void init_mail(void) {
  156. // initialize our mail table
  157. mail_table = newHashtable();
  158. // parse any unread mail
  159. load_mail();
  160. </pre>
  161. Unreceived mail will now be persistent across reboots and crashes. You may
  162. have noticed that saving of mail is rather inefficient; every time someone
  163. receives a mail or sends a mail, we have to re-save all unreceived mails.
  164. Ideally, we would like to change it so we have to re-save as little information
  165. as possible. That is the problem we will tackle in the section on auxiliary
  166. data.
  167. </div>
  168. <!-- content ends here-->
  169. </div></div></div>
  170. <!-- navigation starts here -->
  171. <div class="nav-wrap"><div class="nav">
  172. <iframe src="nav.html" height="100%" width="100%" scrolling=no frameborder=0>
  173. </iframe>
  174. <!-- navigation ends here -->
  175. </div></div>
  176. <!--div class="footer">Edit Date: Nov 15, 2008. By Geoff Hollis</div-->
  177. </body>
  178. </html>