Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- //*****************************************************************************
- //
- // persistent.c
- //
- // handles all the goings-on for persistent rooms. If a room is to be loaded,
- // first check if it has a persistent copy on disk. Read that in. Otherwise,
- // run the rproto as usual. When a persistent room's state changes, make sure
- // it is saved to disk. When persistent rooms need to be loaded back up after
- // a copyover or reboot, make sure that happens.
- //
- //*****************************************************************************
- #include "../mud.h"
- #include "../utils.h"
- #include "../world.h"
- #include "../auxiliary.h"
- #include "../storage.h"
- #include "../room.h"
- #include "../handler.h"
- #include "../hooks.h"
- #include "../event.h"
- #include "../character.h"
- #include "../object.h"
- #include "../bitvector.h"
- //*****************************************************************************
- // mandatory modules
- //*****************************************************************************
- #include "../scripts/scripts.h"
- #include "../scripts/pyroom.h"
- //*****************************************************************************
- // auxiliary data
- //*****************************************************************************
- typedef struct {
- bool dirty; // do we need to be saved?
- bool persistent; // are we persistent or not?
- int activity; // how many 'things' are going on in us? If activity is
- // > 0, we have to make sure we force-load at startup
- time_t last_use; // the last time someone entered our room
- } PERSISTENT_DATA;
- PERSISTENT_DATA *newPersistentData(void) {
- PERSISTENT_DATA *data = malloc(sizeof(PERSISTENT_DATA));
- data->persistent = FALSE;
- data->activity = 0;
- data->last_use = current_time;
- data->dirty = FALSE;
- return data;
- }
- void deletePersistentData(PERSISTENT_DATA *data) {
- free(data);
- }
- void persistentDataCopyTo(PERSISTENT_DATA *from, PERSISTENT_DATA *to) {
- *to = *from;
- }
- PERSISTENT_DATA *persistentDataCopy(PERSISTENT_DATA *data) {
- PERSISTENT_DATA *newdata = newPersistentData();
- persistentDataCopyTo(data, newdata);
- return newdata;
- }
- STORAGE_SET *persistentDataStore(PERSISTENT_DATA *data) {
- STORAGE_SET *set = new_storage_set();
- store_bool(set, "persistent", data->persistent);
- store_int(set, "activity", data->activity);
- return set;
- }
- PERSISTENT_DATA *persistentDataRead(STORAGE_SET *set) {
- PERSISTENT_DATA *data = newPersistentData();
- data->persistent = read_bool(set, "persistent");
- data->activity = read_int(set, "activity");
- return data;
- }
- //*****************************************************************************
- // local functions
- //*****************************************************************************
- LIST *p_to_save = NULL; // our list of persistent rooms to save to disk
- // at 1 million rooms, this should mean 1000000 / (64 * 64) = 244 files/folder
- #define WORLD_BINS 64
- //*****************************************************************************
- // interaction with the database of persistent rooms
- //*****************************************************************************
- bool persistentRoomExists(WORLD_DATA *world, const char *key) {
- static char fname[MAX_BUFFER];
- if(!*key)
- return FALSE;
- *fname = '\0';
- sprintf(fname, "%s/persistent/%lu/%lu/%s",
- worldGetPath(world),
- pearson_hash8_1(key) % WORLD_BINS,
- pearson_hash8_2(key) % WORLD_BINS,
- key);
- return file_exists(fname);
- }
- //
- // store a room in the persistent database
- void worldClearPersistentRoom(WORLD_DATA *world, const char *key) {
- static char fname[MAX_BUFFER];
- if(!*key)
- return;
- *fname = '\0';
- sprintf(fname, "%s/persistent/%lu/%lu/%s",
- worldGetPath(world),
- pearson_hash8_1(key) % WORLD_BINS,
- pearson_hash8_2(key) % WORLD_BINS,
- key);
- log_string("Clearing persistent room, %s at %s", key, fname);
- if(file_exists(fname))
- unlink(fname);
- }
- //
- // store a room in the persistent database
- void worldStorePersistentRoom(WORLD_DATA *world, const char *key,
- ROOM_DATA *room) {
- static char fname[MAX_BUFFER];
- static char dir1[SMALL_BUFFER];
- static char dir2[SMALL_BUFFER];
- if(!*key)
- return;
- unsigned long hash1 = pearson_hash8_1(key) % WORLD_BINS;
- unsigned long hash2 = pearson_hash8_2(key) % WORLD_BINS;
- *fname = '\0';
- *dir1 = '\0';
- *dir2 = '\0';
- // make sure our hash bins exist
- sprintf(dir1, "%s/persistent/%lu",
- worldGetPath(world), hash1);
- if(!dir_exists(dir1))
- mkdir(dir1, S_IRWXU | S_IRWXG);
- // and the second one as well
- sprintf(dir2, "%s/persistent/%lu/%lu",
- worldGetPath(world), hash1, hash2);
- if(!dir_exists(dir2))
- mkdir(dir2, S_IRWXU | S_IRWXG);
- // now, store the room
- sprintf(fname, "%s/persistent/%lu/%lu/%s",
- worldGetPath(world), hash1, hash2, key);
- STORAGE_SET *set = roomStore(room);
- storage_write(set, fname);
- storage_close(set);
- // log_string("stored persistent room :: %s", fname);
- }
- //
- // pre-emptively, we're preparing for very large persistent world (1mil+rooms).
- // Something like this, it would be real nice to have database storage for.
- // Alas, we're using flat files... so we're going to have to do some pretty
- // creative hashing, so the folders don't overflow and become impossible to
- // access. make two layers of directories, each with 500 folders. That will
- // give us 4 room files to a folder, for a 1mil room persistent world.
- ROOM_DATA *worldGetPersistentRoom(WORLD_DATA *world, const char *key) {
- static char fname[MAX_BUFFER];
- if(!*key)
- return NULL;
- *fname = '\0';
- sprintf(fname, "%s/persistent/%lu/%lu/%s",
- worldGetPath(world),
- pearson_hash8_1(key) % WORLD_BINS,
- pearson_hash8_2(key) % WORLD_BINS,
- key);
- if(!file_exists(fname))
- return NULL;
- else {
- // log_string("%-30s get persistent: %s", key, fname);
- STORAGE_SET *set = storage_read(fname);
- ROOM_DATA *room = roomRead(set);
- storage_close(set);
- worldPutRoom(world, key, room);
- room_to_game(room);
- return room;
- }
- }
- //*****************************************************************************
- // interaction with the persistent aux data
- //*****************************************************************************
- void roomUpdateLastUse(ROOM_DATA *room) {
- PERSISTENT_DATA *data = roomGetAuxiliaryData(room, "persistent_data");
- data->last_use = current_time;
- }
- time_t roomGetLastUse(ROOM_DATA *room) {
- PERSISTENT_DATA *data = roomGetAuxiliaryData(room, "persistent_data");
- return data->last_use;
- }
- void roomSetPersistent(ROOM_DATA *room, bool val) {
- PERSISTENT_DATA *data = roomGetAuxiliaryData(room, "persistent_data");
- // if it was persistent before and not now, clear our database entry
- if(data->persistent == TRUE && val == FALSE) {
- worldClearPersistentRoom(gameworld, roomGetClass(room));
- if(listIn(p_to_save, room)) {
- listRemove(p_to_save, room);
- }
- }
- data->persistent = val;
- }
- bool roomIsPersistent(ROOM_DATA *room) {
- PERSISTENT_DATA *data = roomGetAuxiliaryData(room, "persistent_data");
- return data->persistent;
- }
- bool roomIsPersistentDirty(ROOM_DATA *room) {
- PERSISTENT_DATA *data = roomGetAuxiliaryData(room, "persistent_data");
- return data->dirty;
- }
- void roomSetPersistentDirty(ROOM_DATA *room) {
- PERSISTENT_DATA *data = roomGetAuxiliaryData(room, "persistent_data");
- if(roomIsExtracted(room))
- return;
- if(data->dirty != TRUE)
- listPut(p_to_save, room);
- data->dirty = TRUE;
- }
- void roomClearPersistentDirty(ROOM_DATA *room) {
- PERSISTENT_DATA *data = roomGetAuxiliaryData(room, "persistent_data");
- data->dirty = FALSE;
- }
- //
- // add 'activity' to a persistent room. If a persistent room is active, make it
- // automatically load at bootup, so the activity can continue
- void roomAddActivity(ROOM_DATA *room) {
- PERSISTENT_DATA *data = roomGetAuxiliaryData(room, "persistent_data");
- data->activity++;
- data->last_use = current_time;
- // add us to the list of active rooms
- //***********
- // FINISH ME
- //***********
- }
- void roomRemoveActivity(ROOM_DATA *room) {
- PERSISTENT_DATA *data = roomGetAuxiliaryData(room, "persistent_data");
- data->activity--;
- // remove us from the list of active rooms
- if(data->activity == 0) {
- //***********
- // FINISH ME
- //***********
- }
- }
- //*****************************************************************************
- // Python extensions
- //*****************************************************************************
- //
- // prepare a persistent room to be saved to disc
- PyObject *PyRoom_dirtyPersistence(PyObject *pyroom) {
- ROOM_DATA *room = PyRoom_AsRoom(pyroom);
- if(room == NULL) {
- PyErr_Format(PyExc_TypeError, "tried to dirty nonexistent room.");
- return NULL;
- }
- // if we're not persistent, ignore
- if(!roomIsPersistent(room))
- return Py_BuildValue("i", 0);
- if(!roomIsExtracted(room))
- roomSetPersistentDirty(room);
- return Py_BuildValue("");
- }
- //
- // run room_change hooks
- PyObject *PyRoom_touch(PyObject *pyroom) {
- ROOM_DATA *room = PyRoom_AsRoom(pyroom);
- if(room == NULL) {
- PyErr_Format(PyExc_TypeError, "tried to touch nonexistent room.");
- return NULL;
- }
- roomUpdateLastUse(room);
- // hookRun("room_change", hookBuildInfo("rm", room));
- return Py_BuildValue("");
- }
- //
- // unload a persistent room from memory. Will not work if PCs are present.
- PyObject *PyRoom_unloadPersistence(PyObject *pyroom) {
- ROOM_DATA *room = PyRoom_AsRoom(pyroom);
- if(room == NULL) {
- PyErr_Format(PyExc_TypeError, "tried to save nonexistent room.");
- return NULL;
- }
- // it's not pesistent
- if(!roomIsPersistent(room))
- return Py_BuildValue("i", 0);
- // does it contain a PC?
- LIST_ITERATOR *ch_i = newListIterator(roomGetCharacters(room));
- CHAR_DATA *ch = NULL;
- bool pc_found = FALSE;
- ITERATE_LIST(ch, ch_i) {
- if(!charIsNPC(ch)) {
- pc_found = TRUE;
- break;
- }
- } deleteListIterator(ch_i);
- if(pc_found)
- return Py_BuildValue("i", 0);
- // let's sure hope we are properly dirtying everything when room states change
- if(roomIsPersistentDirty(room)) {
- worldStorePersistentRoom(gameworld, roomGetClass(room), room);
- roomClearPersistentDirty(room);
- }
- extract_room(room);
- return Py_BuildValue("");
- }
- PyObject *PyRoom_getdirty(PyObject *self, void *closure) {
- ROOM_DATA *room = PyRoom_AsRoom(self);
- if(room != NULL) return Py_BuildValue("i", roomIsPersistentDirty(room));
- else return NULL;
- }
- PyObject *PyRoom_getpersistent(PyObject *self, void *closure) {
- ROOM_DATA *room = PyRoom_AsRoom(self);
- if(room != NULL) return Py_BuildValue("i", roomIsPersistent(room));
- else return NULL;
- }
- PyObject *PyRoom_getlastuse(PyObject *self, void *closure) {
- ROOM_DATA *room = PyRoom_AsRoom(self);
- if(room != NULL) return Py_BuildValue("i", roomGetLastUse(room));
- else return NULL;
- }
- int PyRoom_setpersistent(PyObject *self, PyObject *arg) {
- ROOM_DATA *room = PyRoom_AsRoom(self);
- if(room == NULL)
- return -1;
- else if(arg == Py_True)
- roomSetPersistent(room, TRUE);
- else if(arg == Py_False)
- roomSetPersistent(room, FALSE);
- else
- return -1;
- return 0;
- }
- //*****************************************************************************
- // hooks
- //*****************************************************************************
- void update_persistent_char_to_room(const char *info) {
- CHAR_DATA *ch = NULL;
- ROOM_DATA *room = NULL;
- hookParseInfo(info, &ch, &room);
- if(!charIsNPC(ch))
- roomUpdateLastUse(room);
- if(charIsNPC(ch) && !bitIsSet(charGetPrfs(ch), "nopersist") &&
- roomIsPersistent(room) && !roomIsExtracted(room))
- roomSetPersistentDirty(room);
- }
- void update_persistent_char_from_room(const char *info) {
- CHAR_DATA *ch = NULL;
- ROOM_DATA *room = NULL;
- hookParseInfo(info, &ch, &room);
- if(charIsNPC(ch) && !bitIsSet(charGetPrfs(ch), "nopersist") &&
- roomIsPersistent(room) && !roomIsExtracted(room))
- roomSetPersistentDirty(room);
- }
- void update_persistent_obj_to_room(const char *info) {
- OBJ_DATA *obj = NULL;
- ROOM_DATA *room = NULL;
- hookParseInfo(info, &obj, &room);
- if(roomIsPersistent(room) && !bitIsSet(objGetBits(obj), "nopersist") &&
- !roomIsExtracted(room))
- roomSetPersistentDirty(room);
- }
- void update_persistent_obj_from_room(const char *info) {
- OBJ_DATA *obj = NULL;
- ROOM_DATA *room = NULL;
- hookParseInfo(info, &obj, &room);
- if(roomIsPersistent(room) && !bitIsSet(objGetBits(obj), "nopersist") &&
- !roomIsExtracted(room))
- roomSetPersistentDirty(room);
- }
- void update_persistent_obj_from_obj(const char *info) {
- OBJ_DATA *obj = NULL;
- OBJ_DATA *container = NULL;
- ROOM_DATA *root = NULL;
- hookParseInfo(info, &obj, &container);
- if(container == NULL || obj == NULL)
- return;
- root = objGetRootRoom(container);
- if(root == NULL)
- return;
- if(roomIsPersistent(root) && !roomIsExtracted(root))
- roomSetPersistentDirty(root);
- }
- void update_persistent_obj_to_obj(const char *info) {
- OBJ_DATA *obj = NULL;
- OBJ_DATA *container = NULL;
- ROOM_DATA *root = NULL;
- hookParseInfo(info, &obj, &container);
- if(container == NULL || obj == NULL)
- return;
- root = objGetRootRoom(container);
- if(root == NULL)
- return;
- if(roomIsPersistent(root) && !roomIsExtracted(root))
- roomSetPersistentDirty(root);
- }
- void update_persistent_room_from_game(const char *info) {
- ROOM_DATA *room = NULL;
- hookParseInfo(info, &room);
- listRemove(p_to_save, room);
- // have we been replaced by a non-persistent room?
- ROOM_DATA *new_room = worldGetRoom(gameworld, roomGetClass(room));
- if(roomIsPersistent(room) && new_room != NULL && !roomIsPersistent(new_room)){
- worldClearPersistentRoom(gameworld, roomGetClass(room));
- }
- }
- void update_persistent_room_change(const char *info) {
- ROOM_DATA *room = NULL;
- hookParseInfo(info, &room);
- if(roomIsPersistent(room) && !roomIsExtracted(room))
- roomSetPersistentDirty(room);
- }
- void flush_dirty_hook(const char *info) {
- ROOM_DATA *room = NULL;
- hookParseInfo(info, &room);
- if(!roomIsExtracted(room) && roomIsPersistentDirty(room)) {
- worldStorePersistentRoom(gameworld, roomGetClass(room), room);
- roomClearPersistentDirty(room);
- }
- }
- //*****************************************************************************
- // events
- //*****************************************************************************
- //
- // save all of our pending persistent rooms to disc
- void flush_persistent_rooms_event(void *owner, void *data, const char *arg) {
- //LIST_ITERATOR *room_i = newListIterator(room_list);
- ROOM_DATA *room = NULL;
- /*
- ITERATE_LIST(room, room_i) {
- if(roomIsPersistentDirty(room)) {
- worldStorePersistentRoom(gameworld, roomGetClass(room), room);
- roomClearPersistentDirty(room);
- }
- } deleteListIterator(room_i);
- */
- while( (room = listPop(p_to_save)) != NULL) {
- worldStorePersistentRoom(gameworld, roomGetClass(room), room);
- roomClearPersistentDirty(room);
- }
- }
- //
- // make sure we flush all pending saves when the mud is copyover/shutdown'd
- void flush_persistent_hook(const char *info) {
- // cheap hack of a call
- flush_persistent_rooms_event(NULL, NULL, NULL);
- }
- //*****************************************************************************
- // initialization
- //*****************************************************************************
- void init_persistent(void) {
- p_to_save = newList();
- auxiliariesInstall("persistent_data",
- newAuxiliaryFuncs(AUXILIARY_TYPE_ROOM,
- newPersistentData, deletePersistentData,
- persistentDataCopyTo, persistentDataCopy,
- persistentDataStore,persistentDataRead));
- // start our flushing of persistent rooms that need to be saved
- start_update(NULL, 1, flush_persistent_rooms_event, NULL,NULL,NULL);
- // bitvectors to flag mobs and objs as non-persistent
- bitvectorAddBit("obj_bits", "nopersist");
- bitvectorAddBit("char_prfs", "nopersist");
- // listen for objects and characters entering
- // or leaving rooms. Update those rooms' statuses
- hookAdd("char_to_room", update_persistent_char_to_room);
- hookAdd("char_from_room", update_persistent_char_from_room);
- hookAdd("obj_to_room", update_persistent_obj_to_room);
- hookAdd("obj_from_room", update_persistent_obj_from_room);
- hookAdd("obj_from_obj", update_persistent_obj_from_obj);
- hookAdd("obj_to_obj", update_persistent_obj_to_obj);
- // hookAdd("room_from_game", update_persistent_room_from_game);
- hookAdd("room_change", update_persistent_room_change);
- hookAdd("shutdown", flush_persistent_hook);
- // make sure any dirtied rooms get flushed before they are extracted
- hookAdd("extract_room", flush_dirty_hook);
- // add accessibility to Python
- /*
- PyRoom_addMethod("add_activity", PyRoom_addActivity, METH_NOARGS, NULL);
- PyRoom_addMethod("rem_activity", PyRoom_remActivity, METH_NOARGS, NULL);
- */
- PyRoom_addMethod("touch", PyRoom_touch, METH_NOARGS,NULL);
- PyRoom_addMethod("dirty", PyRoom_dirtyPersistence, METH_NOARGS,NULL);
- PyRoom_addMethod("unload", PyRoom_unloadPersistence, METH_NOARGS,NULL);
- PyRoom_addGetSetter("last_use", PyRoom_getlastuse, NULL, NULL);
- PyRoom_addGetSetter("persistent",
- PyRoom_getpersistent,
- PyRoom_setpersistent, NULL);
- PyRoom_addGetSetter("isdirty", PyRoom_getdirty, NULL, NULL);
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement