Advertisement
Guest User

Untitled

a guest
Jun 26th, 2017
57
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 17.69 KB | None | 0 0
  1. //*****************************************************************************
  2. //
  3. // persistent.c
  4. //
  5. // handles all the goings-on for persistent rooms. If a room is to be loaded,
  6. // first check if it has a persistent copy on disk. Read that in. Otherwise,
  7. // run the rproto as usual. When a persistent room's state changes, make sure
  8. // it is saved to disk. When persistent rooms need to be loaded back up after
  9. // a copyover or reboot, make sure that happens.
  10. //
  11. //*****************************************************************************
  12.  
  13. #include "../mud.h"
  14. #include "../utils.h"
  15. #include "../world.h"
  16. #include "../auxiliary.h"
  17. #include "../storage.h"
  18. #include "../room.h"
  19. #include "../handler.h"
  20. #include "../hooks.h"
  21. #include "../event.h"
  22. #include "../character.h"
  23. #include "../object.h"
  24. #include "../bitvector.h"
  25.  
  26.  
  27.  
  28. //*****************************************************************************
  29. // mandatory modules
  30. //*****************************************************************************
  31. #include "../scripts/scripts.h"
  32. #include "../scripts/pyroom.h"
  33.  
  34.  
  35.  
  36. //*****************************************************************************
  37. // auxiliary data
  38. //*****************************************************************************
  39. typedef struct {
  40.   bool      dirty; // do we need to be saved?
  41.   bool persistent; // are we persistent or not?
  42.   int    activity; // how many 'things' are going on in us? If activity is
  43.                    // > 0, we have to make sure we force-load at startup
  44.   time_t last_use; // the last time someone entered our room
  45. } PERSISTENT_DATA;
  46.  
  47. PERSISTENT_DATA *newPersistentData(void) {
  48.   PERSISTENT_DATA *data = malloc(sizeof(PERSISTENT_DATA));
  49.   data->persistent      = FALSE;
  50.   data->activity        = 0;
  51.   data->last_use        = current_time;
  52.   data->dirty           = FALSE;
  53.   return data;
  54. }
  55.  
  56. void deletePersistentData(PERSISTENT_DATA *data) {
  57.   free(data);
  58. }
  59.  
  60. void persistentDataCopyTo(PERSISTENT_DATA *from, PERSISTENT_DATA *to) {
  61.   *to = *from;
  62. }
  63.  
  64. PERSISTENT_DATA *persistentDataCopy(PERSISTENT_DATA *data) {
  65.   PERSISTENT_DATA *newdata = newPersistentData();
  66.   persistentDataCopyTo(data, newdata);
  67.   return newdata;
  68. }
  69.  
  70. STORAGE_SET *persistentDataStore(PERSISTENT_DATA *data) {
  71.   STORAGE_SET *set = new_storage_set();
  72.   store_bool(set, "persistent", data->persistent);
  73.   store_int(set,  "activity",   data->activity);
  74.   return set;
  75. }
  76.  
  77. PERSISTENT_DATA *persistentDataRead(STORAGE_SET *set) {
  78.   PERSISTENT_DATA *data = newPersistentData();
  79.   data->persistent = read_bool(set, "persistent");
  80.   data->activity   = read_int(set,  "activity");
  81.   return data;
  82. }
  83.  
  84.  
  85.  
  86. //*****************************************************************************
  87. // local functions
  88. //*****************************************************************************
  89. LIST *p_to_save = NULL; // our list of persistent rooms to save to disk
  90.  
  91. // at 1 million rooms, this should mean 1000000 / (64 * 64) = 244 files/folder
  92. #define WORLD_BINS 64
  93.  
  94.  
  95.  
  96. //*****************************************************************************
  97. // interaction with the database of persistent rooms
  98. //*****************************************************************************
  99. bool persistentRoomExists(WORLD_DATA *world, const char *key) {
  100.   static char fname[MAX_BUFFER];
  101.   if(!*key)
  102.     return FALSE;
  103.  
  104.   *fname = '\0';
  105.   sprintf(fname, "%s/persistent/%lu/%lu/%s",
  106.       worldGetPath(world),
  107.       pearson_hash8_1(key) % WORLD_BINS,
  108.       pearson_hash8_2(key) % WORLD_BINS,
  109.       key);
  110.  
  111.   return file_exists(fname);
  112. }
  113.  
  114. //
  115. // store a room in the persistent database
  116. void worldClearPersistentRoom(WORLD_DATA *world, const char *key) {
  117.   static char fname[MAX_BUFFER];
  118.   if(!*key)
  119.     return;
  120.  
  121.   *fname = '\0';
  122.   sprintf(fname, "%s/persistent/%lu/%lu/%s",
  123.       worldGetPath(world),
  124.       pearson_hash8_1(key) % WORLD_BINS,
  125.       pearson_hash8_2(key) % WORLD_BINS,
  126.       key);
  127.   log_string("Clearing persistent room, %s at %s", key, fname);
  128.  
  129.   if(file_exists(fname))
  130.     unlink(fname);
  131. }
  132.  
  133. //
  134. // store a room in the persistent database
  135. void worldStorePersistentRoom(WORLD_DATA *world, const char *key,
  136.                   ROOM_DATA *room) {
  137.   static char fname[MAX_BUFFER];
  138.   static char  dir1[SMALL_BUFFER];
  139.   static char  dir2[SMALL_BUFFER];
  140.   if(!*key)
  141.     return;
  142.  
  143.   unsigned long hash1 = pearson_hash8_1(key) % WORLD_BINS;
  144.   unsigned long hash2 = pearson_hash8_2(key) % WORLD_BINS;
  145.   *fname = '\0';
  146.   *dir1  = '\0';
  147.   *dir2  = '\0';
  148.  
  149.   // make sure our hash bins exist
  150.   sprintf(dir1, "%s/persistent/%lu",
  151.       worldGetPath(world), hash1);
  152.   if(!dir_exists(dir1))
  153.     mkdir(dir1, S_IRWXU | S_IRWXG);
  154.  
  155.   // and the second one as well
  156.   sprintf(dir2, "%s/persistent/%lu/%lu",
  157.       worldGetPath(world), hash1, hash2);
  158.   if(!dir_exists(dir2))
  159.     mkdir(dir2, S_IRWXU | S_IRWXG);
  160.  
  161.   // now, store the room
  162.   sprintf(fname, "%s/persistent/%lu/%lu/%s",
  163.       worldGetPath(world), hash1, hash2, key);
  164.   STORAGE_SET *set = roomStore(room);
  165.   storage_write(set, fname);
  166.   storage_close(set);
  167.  
  168.   // log_string("stored persistent room :: %s", fname);
  169. }
  170.  
  171. //
  172. // pre-emptively, we're preparing for very large persistent world (1mil+rooms).
  173. // Something like this, it would be real nice to have database storage for.
  174. // Alas, we're using flat files... so we're going to have to do some pretty
  175. // creative hashing, so the folders don't overflow and become impossible to
  176. // access. make two layers of directories, each with 500 folders. That will
  177. // give us 4 room files to a folder, for a 1mil room persistent world.
  178. ROOM_DATA *worldGetPersistentRoom(WORLD_DATA *world, const char *key) {
  179.   static char fname[MAX_BUFFER];
  180.   if(!*key)
  181.     return NULL;
  182.  
  183.   *fname = '\0';
  184.   sprintf(fname, "%s/persistent/%lu/%lu/%s",
  185.       worldGetPath(world),
  186.       pearson_hash8_1(key) % WORLD_BINS,
  187.       pearson_hash8_2(key) % WORLD_BINS,
  188.       key);
  189.  
  190.   if(!file_exists(fname))
  191.     return NULL;
  192.   else {
  193.     // log_string("%-30s get persistent: %s", key, fname);
  194.     STORAGE_SET *set = storage_read(fname);
  195.     ROOM_DATA  *room = roomRead(set);
  196.     storage_close(set);
  197.     worldPutRoom(world, key, room);
  198.     room_to_game(room);
  199.     return room;
  200.   }
  201. }
  202.  
  203.  
  204.  
  205. //*****************************************************************************
  206. // interaction with the persistent aux data
  207. //*****************************************************************************
  208. void roomUpdateLastUse(ROOM_DATA *room) {
  209.   PERSISTENT_DATA *data = roomGetAuxiliaryData(room, "persistent_data");
  210.   data->last_use = current_time;
  211. }
  212.  
  213. time_t roomGetLastUse(ROOM_DATA *room) {
  214.   PERSISTENT_DATA *data = roomGetAuxiliaryData(room, "persistent_data");
  215.   return data->last_use;
  216. }
  217.  
  218. void roomSetPersistent(ROOM_DATA *room, bool val) {
  219.   PERSISTENT_DATA *data = roomGetAuxiliaryData(room, "persistent_data");
  220.  
  221.   // if it was persistent before and not now, clear our database entry
  222.   if(data->persistent == TRUE && val == FALSE) {
  223.     worldClearPersistentRoom(gameworld, roomGetClass(room));
  224.     if(listIn(p_to_save, room)) {
  225.       listRemove(p_to_save, room);
  226.     }    
  227.   }
  228.  
  229.   data->persistent = val;
  230. }
  231.  
  232. bool roomIsPersistent(ROOM_DATA *room) {
  233.   PERSISTENT_DATA *data = roomGetAuxiliaryData(room, "persistent_data");
  234.   return data->persistent;
  235. }
  236.  
  237. bool roomIsPersistentDirty(ROOM_DATA *room) {
  238.   PERSISTENT_DATA *data = roomGetAuxiliaryData(room, "persistent_data");
  239.   return data->dirty;
  240. }
  241.  
  242. void roomSetPersistentDirty(ROOM_DATA *room) {
  243.   PERSISTENT_DATA *data = roomGetAuxiliaryData(room, "persistent_data");
  244.   if(roomIsExtracted(room))
  245.     return;
  246.  
  247.   if(data->dirty != TRUE)
  248.     listPut(p_to_save, room);
  249.   data->dirty = TRUE;
  250. }
  251.  
  252. void roomClearPersistentDirty(ROOM_DATA *room) {
  253.   PERSISTENT_DATA *data = roomGetAuxiliaryData(room, "persistent_data");
  254.   data->dirty = FALSE;
  255. }
  256.  
  257. //
  258. // add 'activity' to a persistent room. If a persistent room is active, make it
  259. // automatically load at bootup, so the activity can continue
  260. void roomAddActivity(ROOM_DATA *room) {
  261.   PERSISTENT_DATA *data = roomGetAuxiliaryData(room, "persistent_data");
  262.   data->activity++;
  263.   data->last_use = current_time;
  264.  
  265.   // add us to the list of active rooms
  266.   //***********
  267.   // FINISH ME
  268.   //***********
  269. }
  270.  
  271. void roomRemoveActivity(ROOM_DATA *room) {
  272.   PERSISTENT_DATA *data = roomGetAuxiliaryData(room, "persistent_data");
  273.   data->activity--;
  274.  
  275.   // remove us from the list of active rooms
  276.   if(data->activity == 0) {
  277.     //***********
  278.     // FINISH ME
  279.     //***********
  280.   }
  281. }
  282.  
  283.  
  284.  
  285. //*****************************************************************************
  286. // Python extensions
  287. //*****************************************************************************
  288.  
  289. //
  290. // prepare a persistent room to be saved to disc
  291. PyObject *PyRoom_dirtyPersistence(PyObject *pyroom) {
  292.   ROOM_DATA *room = PyRoom_AsRoom(pyroom);
  293.   if(room == NULL) {
  294.     PyErr_Format(PyExc_TypeError, "tried to dirty nonexistent room.");
  295.     return NULL;
  296.   }
  297.  
  298.   // if we're not persistent, ignore
  299.   if(!roomIsPersistent(room))
  300.     return Py_BuildValue("i", 0);
  301.  
  302.   if(!roomIsExtracted(room))
  303.     roomSetPersistentDirty(room);
  304.  
  305.   return Py_BuildValue("");
  306. }
  307.  
  308. //
  309. // run room_change hooks
  310. PyObject *PyRoom_touch(PyObject *pyroom) {
  311.   ROOM_DATA *room = PyRoom_AsRoom(pyroom);
  312.   if(room == NULL) {
  313.     PyErr_Format(PyExc_TypeError, "tried to touch nonexistent room.");
  314.     return NULL;
  315.   }
  316.  
  317.   roomUpdateLastUse(room);
  318.   // hookRun("room_change", hookBuildInfo("rm", room));
  319.   return Py_BuildValue("");
  320. }
  321.  
  322. //
  323. // unload a persistent room from memory. Will not work if PCs are present.
  324. PyObject *PyRoom_unloadPersistence(PyObject *pyroom) {
  325.   ROOM_DATA *room = PyRoom_AsRoom(pyroom);
  326.   if(room == NULL) {
  327.     PyErr_Format(PyExc_TypeError, "tried to save nonexistent room.");
  328.     return NULL;
  329.   }
  330.  
  331.   // it's not pesistent
  332.   if(!roomIsPersistent(room))
  333.     return Py_BuildValue("i", 0);
  334.  
  335.   // does it contain a PC?
  336.   LIST_ITERATOR *ch_i = newListIterator(roomGetCharacters(room));
  337.   CHAR_DATA       *ch = NULL;
  338.   bool       pc_found = FALSE;
  339.   ITERATE_LIST(ch, ch_i) {
  340.     if(!charIsNPC(ch)) {
  341.       pc_found = TRUE;
  342.       break;
  343.     }
  344.   } deleteListIterator(ch_i);
  345.  
  346.   if(pc_found)
  347.     return Py_BuildValue("i", 0);
  348.  
  349.   // let's sure hope we are properly dirtying everything when room states change
  350.   if(roomIsPersistentDirty(room)) {
  351.     worldStorePersistentRoom(gameworld, roomGetClass(room), room);
  352.     roomClearPersistentDirty(room);
  353.   }
  354.   extract_room(room);
  355.   return Py_BuildValue("");
  356. }
  357.  
  358. PyObject *PyRoom_getdirty(PyObject *self, void *closure) {
  359.   ROOM_DATA *room = PyRoom_AsRoom(self);
  360.   if(room != NULL)  return Py_BuildValue("i", roomIsPersistentDirty(room));
  361.   else              return NULL;
  362. }
  363.  
  364. PyObject *PyRoom_getpersistent(PyObject *self, void *closure) {
  365.   ROOM_DATA *room = PyRoom_AsRoom(self);
  366.   if(room != NULL)  return Py_BuildValue("i", roomIsPersistent(room));
  367.   else              return NULL;
  368. }
  369.  
  370. PyObject *PyRoom_getlastuse(PyObject *self, void *closure) {
  371.   ROOM_DATA *room = PyRoom_AsRoom(self);
  372.   if(room != NULL)  return Py_BuildValue("i", roomGetLastUse(room));
  373.   else              return NULL;
  374. }
  375.  
  376. int PyRoom_setpersistent(PyObject *self, PyObject *arg) {
  377.   ROOM_DATA *room = PyRoom_AsRoom(self);
  378.   if(room == NULL)  
  379.     return -1;
  380.   else if(arg == Py_True)
  381.     roomSetPersistent(room, TRUE);
  382.   else if(arg == Py_False)
  383.     roomSetPersistent(room, FALSE);
  384.   else
  385.     return -1;
  386.   return 0;
  387. }
  388.  
  389.  
  390.  
  391. //*****************************************************************************
  392. // hooks
  393. //*****************************************************************************
  394. void update_persistent_char_to_room(const char *info) {
  395.   CHAR_DATA   *ch = NULL;
  396.   ROOM_DATA *room = NULL;
  397.   hookParseInfo(info, &ch, &room);
  398.   if(!charIsNPC(ch))
  399.     roomUpdateLastUse(room);
  400.   if(charIsNPC(ch) && !bitIsSet(charGetPrfs(ch), "nopersist") &&
  401.      roomIsPersistent(room) && !roomIsExtracted(room))
  402.     roomSetPersistentDirty(room);
  403. }
  404.  
  405. void update_persistent_char_from_room(const char *info) {
  406.   CHAR_DATA   *ch = NULL;
  407.   ROOM_DATA *room = NULL;
  408.   hookParseInfo(info, &ch, &room);
  409.   if(charIsNPC(ch) && !bitIsSet(charGetPrfs(ch), "nopersist") &&
  410.      roomIsPersistent(room) && !roomIsExtracted(room))
  411.     roomSetPersistentDirty(room);
  412. }
  413.  
  414. void update_persistent_obj_to_room(const char *info) {
  415.   OBJ_DATA   *obj = NULL;
  416.   ROOM_DATA *room = NULL;
  417.   hookParseInfo(info, &obj, &room);
  418.   if(roomIsPersistent(room) && !bitIsSet(objGetBits(obj), "nopersist") &&
  419.      !roomIsExtracted(room))
  420.     roomSetPersistentDirty(room);
  421. }
  422.  
  423. void update_persistent_obj_from_room(const char *info) {
  424.   OBJ_DATA   *obj = NULL;
  425.   ROOM_DATA *room = NULL;
  426.   hookParseInfo(info, &obj, &room);
  427.   if(roomIsPersistent(room) && !bitIsSet(objGetBits(obj), "nopersist") &&
  428.      !roomIsExtracted(room))
  429.     roomSetPersistentDirty(room);
  430. }
  431.  
  432. void update_persistent_obj_from_obj(const char *info) {
  433.   OBJ_DATA       *obj = NULL;
  434.   OBJ_DATA *container = NULL;
  435.   ROOM_DATA     *root = NULL;
  436.   hookParseInfo(info, &obj, &container);
  437.   if(container == NULL || obj == NULL)
  438.     return;
  439.  
  440.   root = objGetRootRoom(container);
  441.   if(root == NULL)
  442.     return;
  443.  
  444.   if(roomIsPersistent(root) && !roomIsExtracted(root))
  445.     roomSetPersistentDirty(root);
  446. }
  447.  
  448. void update_persistent_obj_to_obj(const char *info) {
  449.   OBJ_DATA       *obj = NULL;
  450.   OBJ_DATA *container = NULL;
  451.   ROOM_DATA     *root = NULL;
  452.   hookParseInfo(info, &obj, &container);
  453.   if(container == NULL || obj == NULL)
  454.     return;
  455.  
  456.   root = objGetRootRoom(container);
  457.   if(root == NULL)
  458.     return;
  459.  
  460.   if(roomIsPersistent(root) && !roomIsExtracted(root))
  461.     roomSetPersistentDirty(root);
  462. }
  463.  
  464. void update_persistent_room_from_game(const char *info) {
  465.   ROOM_DATA *room = NULL;
  466.   hookParseInfo(info, &room);
  467.   listRemove(p_to_save, room);
  468.  
  469.   // have we been replaced by a non-persistent room?
  470.   ROOM_DATA *new_room = worldGetRoom(gameworld, roomGetClass(room));
  471.   if(roomIsPersistent(room) && new_room != NULL && !roomIsPersistent(new_room)){
  472.     worldClearPersistentRoom(gameworld, roomGetClass(room));
  473.   }
  474. }
  475.  
  476. void update_persistent_room_change(const char *info) {
  477.   ROOM_DATA *room = NULL;
  478.   hookParseInfo(info, &room);
  479.   if(roomIsPersistent(room) && !roomIsExtracted(room))
  480.     roomSetPersistentDirty(room);
  481. }
  482.  
  483. void flush_dirty_hook(const char *info) {
  484.   ROOM_DATA *room = NULL;
  485.   hookParseInfo(info, &room);
  486.   if(!roomIsExtracted(room) && roomIsPersistentDirty(room)) {
  487.     worldStorePersistentRoom(gameworld, roomGetClass(room), room);
  488.     roomClearPersistentDirty(room);
  489.   }
  490. }
  491.  
  492.  
  493.  
  494. //*****************************************************************************
  495. // events
  496. //*****************************************************************************
  497.  
  498. //
  499. // save all of our pending persistent rooms to disc
  500. void flush_persistent_rooms_event(void *owner, void *data, const char *arg) {
  501.   //LIST_ITERATOR *room_i = newListIterator(room_list);
  502.   ROOM_DATA       *room = NULL;
  503.   /*
  504.   ITERATE_LIST(room, room_i) {
  505.     if(roomIsPersistentDirty(room)) {
  506.       worldStorePersistentRoom(gameworld, roomGetClass(room), room);
  507.       roomClearPersistentDirty(room);
  508.     }
  509.   } deleteListIterator(room_i);
  510.   */
  511.  
  512.   while( (room = listPop(p_to_save)) != NULL) {
  513.     worldStorePersistentRoom(gameworld, roomGetClass(room), room);
  514.     roomClearPersistentDirty(room);
  515.   }
  516. }
  517.  
  518. //
  519. // make sure we flush all pending saves when the mud is copyover/shutdown'd
  520. void flush_persistent_hook(const char *info) {
  521.   // cheap hack of a call
  522.   flush_persistent_rooms_event(NULL, NULL, NULL);
  523. }
  524.  
  525.  
  526.  
  527. //*****************************************************************************
  528. // initialization
  529. //*****************************************************************************
  530. void init_persistent(void) {
  531.   p_to_save = newList();
  532.  
  533.   auxiliariesInstall("persistent_data",
  534.              newAuxiliaryFuncs(AUXILIARY_TYPE_ROOM,
  535.                        newPersistentData, deletePersistentData,
  536.                        persistentDataCopyTo, persistentDataCopy,
  537.                        persistentDataStore,persistentDataRead));
  538.  
  539.   // start our flushing of persistent rooms that need to be saved
  540.   start_update(NULL, 1, flush_persistent_rooms_event, NULL,NULL,NULL);
  541.  
  542.   // bitvectors to flag mobs and objs as non-persistent
  543.   bitvectorAddBit("obj_bits",  "nopersist");
  544.   bitvectorAddBit("char_prfs", "nopersist");
  545.  
  546.   // listen for objects and characters entering
  547.   // or leaving rooms. Update those rooms' statuses
  548.   hookAdd("char_to_room",   update_persistent_char_to_room);
  549.   hookAdd("char_from_room", update_persistent_char_from_room);
  550.   hookAdd("obj_to_room",    update_persistent_obj_to_room);
  551.   hookAdd("obj_from_room",  update_persistent_obj_from_room);
  552.   hookAdd("obj_from_obj",   update_persistent_obj_from_obj);
  553.   hookAdd("obj_to_obj",     update_persistent_obj_to_obj);
  554.   // hookAdd("room_from_game", update_persistent_room_from_game);
  555.   hookAdd("room_change",    update_persistent_room_change);
  556.   hookAdd("shutdown",       flush_persistent_hook);
  557.  
  558.   // make sure any dirtied rooms get flushed before they are extracted
  559.   hookAdd("extract_room",   flush_dirty_hook);
  560.  
  561.   // add accessibility to Python
  562.   /*
  563.   PyRoom_addMethod("add_activity",  PyRoom_addActivity,   METH_NOARGS, NULL);
  564.   PyRoom_addMethod("rem_activity",  PyRoom_remActivity,   METH_NOARGS, NULL);
  565.   */
  566.  
  567.   PyRoom_addMethod("touch",         PyRoom_touch,             METH_NOARGS,NULL);
  568.   PyRoom_addMethod("dirty",         PyRoom_dirtyPersistence,  METH_NOARGS,NULL);
  569.   PyRoom_addMethod("unload",        PyRoom_unloadPersistence, METH_NOARGS,NULL);
  570.   PyRoom_addGetSetter("last_use",   PyRoom_getlastuse, NULL, NULL);    
  571.   PyRoom_addGetSetter("persistent",
  572.               PyRoom_getpersistent,    
  573.               PyRoom_setpersistent, NULL);
  574.   PyRoom_addGetSetter("isdirty",    PyRoom_getdirty,   NULL, NULL);
  575. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement