/* ************************************************************************ * File: objsave.c EmpireMUD AD 1.0 * * Usage: loading/saving player objects for rent and crash-save * * * * All rights reserved. See license.doc for complete information. * * * * Code base by Paul Clarke. EmpireMUD Project, a tbgMUD Production. * * Based upon CircleMUD 3.0, beta patch level 17, by Jeremy Elson. * * * * Copyright (C) 1993, 94 by the Trustees of the Johns Hopkins University * * CircleMUD is based on DikuMUD, Copyright (C) 1990, 1991. * ************************************************************************ */ /* * AutoEQ by Burkhard Knopf <burkhard.knopf@informatik.tu-clausthal.de> */ #include "conf.h" #include "sysdep.h" #include "structs.h" #include "comm.h" #include "handler.h" #include "db.h" #include "interpreter.h" #include "utils.h" /* these factors should be unique integers */ #define RENT_FACTOR 1 #define CRYO_FACTOR 4 #define LOC_INVENTORY 0 #define MAX_BAG_ROWS 5 extern struct player_index_element *player_table; extern int top_of_p_table; extern int obj_file_timeout; /* Extern functions */ ACMD(do_tell); /* local functions */ int Obj_to_store(Object obj, FILE * fl, int location); void update_obj_file(void); int gen_receptionist(Creature ch, Creature recep, int cmd, char *arg, int mode); void Crash_restore_weight(Object obj); void Crash_extract_objs(Object obj); int Crash_is_unrentable(Object obj); void Crash_extract_norents(Object obj); void Crash_rentsave(Creature ch); Object Obj_from_store(struct obj_file_elem object, int *location) { Object obj; int j; *location = 0; if (real_object(object.item_number) >= 0) { obj = read_object(object.item_number, VIRTUAL); *location = object.location; GET_OBJ_VAL(obj, 0) = object.value[0]; GET_OBJ_VAL(obj, 1) = object.value[1]; GET_OBJ_VAL(obj, 2) = object.value[2]; GET_OBJ_EXTRA(obj) = object.extra_flags; GET_OBJ_WEIGHT(obj) = object.weight; GET_OBJ_TIMER(obj) = object.timer; obj->obj_flags.bitvector = object.bitvector; for (j = 0; j < MAX_OBJ_AFFECT; j++) obj->affected[j] = object.affected[j]; return (obj); } else return (NULL); } int Obj_to_store(Object obj, FILE * fl, int location) { int j; struct obj_file_elem object; object.item_number = GET_OBJ_VNUM(obj); object.location = location; object.value[0] = GET_OBJ_VAL(obj, 0); object.value[1] = GET_OBJ_VAL(obj, 1); object.value[2] = GET_OBJ_VAL(obj, 2); object.extra_flags = GET_OBJ_EXTRA(obj); object.weight = GET_OBJ_WEIGHT(obj); object.timer = GET_OBJ_TIMER(obj); object.bitvector = obj->obj_flags.bitvector; for (j = 0; j < MAX_OBJ_AFFECT; j++) object.affected[j] = obj->affected[j]; if (fwrite(&object, sizeof(struct obj_file_elem), 1, fl) < 1) { perror("SYSERR: error writing object in Obj_to_store"); return (0); } return (1); } void auto_equip(Creature ch, Object obj, int *location) { int j; /* Lots of checks... */ if (*location > 0) { /* Was wearing it. */ switch (j = (*location - 1)) { case WEAR_FINGER_R: case WEAR_FINGER_L: if (!CAN_WEAR(obj, ITEM_WEAR_FINGER)) *location = LOC_INVENTORY; break; case WEAR_NECK: if (!CAN_WEAR(obj, ITEM_WEAR_NECK)) *location = LOC_INVENTORY; break; case WEAR_QUIVER: if (!CAN_WEAR(obj, ITEM_WEAR_QUIVER)) *location = LOC_INVENTORY; break; case WEAR_EARS: if (!CAN_WEAR(obj, ITEM_WEAR_EARS)) *location = LOC_INVENTORY; break; case WEAR_BODY: if (!CAN_WEAR(obj, ITEM_WEAR_BODY)) *location = LOC_INVENTORY; break; case WEAR_ARMOR: if (!CAN_WEAR(obj, ITEM_WEAR_ARMOR)) *location = LOC_INVENTORY; break; case WEAR_HEAD: if (!CAN_WEAR(obj, ITEM_WEAR_HEAD)) *location = LOC_INVENTORY; break; case WEAR_LEGS: if (!CAN_WEAR(obj, ITEM_WEAR_LEGS)) *location = LOC_INVENTORY; break; case WEAR_FEET: if (!CAN_WEAR(obj, ITEM_WEAR_FEET)) *location = LOC_INVENTORY; break; case WEAR_HANDS: if (!CAN_WEAR(obj, ITEM_WEAR_HANDS)) *location = LOC_INVENTORY; break; case WEAR_ARMS: if (!CAN_WEAR(obj, ITEM_WEAR_ARMS)) *location = LOC_INVENTORY; break; case WEAR_ABOUT: if (!CAN_WEAR(obj, ITEM_WEAR_ABOUT)) *location = LOC_INVENTORY; break; case WEAR_WAIST: if (!CAN_WEAR(obj, ITEM_WEAR_WAIST)) *location = LOC_INVENTORY; break; case WEAR_SHEATH_1: if (!CAN_WEAR(obj, ITEM_WEAR_SHEATH)) *location = LOC_INVENTORY; break; case WEAR_SHEATH_2: if (!CAN_WEAR(obj, ITEM_WEAR_SHEATH)) *location = LOC_INVENTORY; break; case WEAR_SHEATH_3: if (!CAN_WEAR(obj, ITEM_WEAR_SHEATH)) *location = LOC_INVENTORY; break; case WEAR_IN_SHEATH_1: if (!CAN_WEAR(obj, ITEM_WEAR_WIELD)) *location = LOC_INVENTORY; break; case WEAR_IN_SHEATH_2: if (!CAN_WEAR(obj, ITEM_WEAR_WIELD)) *location = LOC_INVENTORY; break; case WEAR_IN_SHEATH_3: if (!CAN_WEAR(obj, ITEM_WEAR_WIELD)) *location = LOC_INVENTORY; break; case WEAR_WIELD: if (!CAN_WEAR(obj, ITEM_WEAR_WIELD)) *location = LOC_INVENTORY; break; case WEAR_HOLD: if (!CAN_WEAR(obj, ITEM_WEAR_HOLD)) *location = LOC_INVENTORY; break; default: *location = LOC_INVENTORY; } if (*location > 0) { /* Wearable. */ if (!GET_EQ(ch,j)) { equip_char(ch, obj, j); } else { /* Oops, saved a player with double equipment? */ syslog(0, TRUE, "SYSERR: autoeq: '%s' already equipped in position %d.", GET_NAME(ch), *location); *location = LOC_INVENTORY; } } } if (*location <= 0) /* Inventory */ obj_to_char(obj, ch); } int Crash_delete_file(char *name) { char filename[50]; FILE *fl; if (!get_filename(name, filename, CRASH_FILE)) return (0); if (!(fl = fopen(filename, "rb"))) { if (errno != ENOENT) /* if it fails but NOT because of no file */ log("SYSERR: deleting crash file %s (1): %s", filename, strerror(errno)); return (0); } fclose(fl); /* if it fails, NOT because of no file */ if (remove(filename) < 0 && errno != ENOENT) log("SYSERR: deleting crash file %s (2): %s", filename, strerror(errno)); return (1); } int Crash_delete_crashfile(Creature ch) { char fname[MAX_INPUT_LENGTH]; struct rent_info rent; FILE *fl; if (!get_filename(GET_NAME(ch), fname, CRASH_FILE)) return (0); if (!(fl = fopen(fname, "rb"))) { if (errno != ENOENT) /* if it fails, NOT because of no file */ log("SYSERR: checking for crash file %s (3): %s", fname, strerror(errno)); return (0); } if (!feof(fl)) fread(&rent, sizeof(struct rent_info), 1, fl); fclose(fl); if (rent.rentcode == RENT_CRASH) Crash_delete_file(GET_NAME(ch)); return (1); } int Crash_clean_file(char *name) { char fname[MAX_STRING_LENGTH], filetype[20]; struct rent_info rent; FILE *fl; if (!get_filename(name, fname, CRASH_FILE)) return (0); /* * open for write so that permission problems will be flagged now, at boot * time. */ if (!(fl = fopen(fname, "r+b"))) { if (errno != ENOENT) /* if it fails, NOT because of no file */ log("SYSERR: OPENING OBJECT FILE %s (4): %s", fname, strerror(errno)); return (0); } if (!feof(fl)) fread(&rent, sizeof(struct rent_info), 1, fl); fclose(fl); if (rent.time < time(0) - (obj_file_timeout * SECS_PER_REAL_DAY)) { Crash_delete_file(name); switch (rent.rentcode) { case RENT_CRASH: strcpy(filetype, "crash"); break; case RENT_RENTED: strcpy(filetype, "rent"); break; default: strcpy(filetype, "UNKNOWN!"); break; } log(" Deleting %s's %s file.", name, filetype); return (1); } return (0); } void update_obj_file(void) { int i; for (i = 0; i <= top_of_p_table; i++) if (*player_table[i].name) Crash_clean_file(player_table[i].name); } void Crash_listrent(Creature ch, char *name) { FILE *fl; char fname[MAX_INPUT_LENGTH], buf[MAX_STRING_LENGTH]; struct obj_file_elem object; Object obj; struct rent_info rent; if (!get_filename(name, fname, CRASH_FILE)) return; if (!(fl = fopen(fname, "rb"))) { sprintf(buf, "%s has no rent file.\r\n", name); send_to_char(buf, ch); return; } sprintf(buf, "%s\r\n", fname); if (!feof(fl)) fread(&rent, sizeof(struct rent_info), 1, fl); switch (rent.rentcode) { case RENT_RENTED: strcat(buf, "Rent\r\n"); break; case RENT_CRASH: strcat(buf, "Crash\r\n"); break; default: strcat(buf, "Undef\r\n"); break; } while (!feof(fl)) { fread(&object, sizeof(struct obj_file_elem), 1, fl); if (ferror(fl)) { fclose(fl); return; } if (!feof(fl)) if (real_object(object.item_number) > -1) { obj = read_object(object.item_number, VIRTUAL); sprintf(buf + strlen(buf), " [%5d] (%5dau) <%2d> %-20s\r\n", object.item_number, GET_OBJ_RENT(obj), object.location, obj->short_description); extract_obj(obj); if (strlen(buf) > MAX_STRING_LENGTH - 80) { strcat(buf, "** Excessive rent listing. **\r\n"); break; } } } send_to_char(buf, ch); fclose(fl); } int Crash_write_rentcode(Creature ch, FILE * fl, struct rent_info * rent) { if (fwrite(rent, sizeof(struct rent_info), 1, fl) < 1) { perror("SYSERR: writing rent code"); return (0); } return (1); } /* * Return values: * 0 - successful load, keep char in rent room. * 1 - load failure or load of crash items -- put char in temple. */ int Crash_load(Creature ch, int dolog) { FILE *fl; char fname[MAX_STRING_LENGTH]; struct obj_file_elem object; struct rent_info rent; int orig_rent_code, num_objs = 0, j; /* AutoEQ addition. */ Object obj, obj2, cont_row[MAX_BAG_ROWS]; int location; /* Empty all of the container lists (you never know ...) */ for (j = 0; j < MAX_BAG_ROWS; j++) cont_row[j] = NULL; if (!get_filename(GET_NAME(ch), fname, CRASH_FILE)) return (1); if (!(fl = fopen(fname, "r+b"))) { if (errno != ENOENT) { /* if it fails, NOT because of no file */ log("SYSERR: READING OBJECT FILE %s (5): %s", fname, strerror(errno)); send_to_char("\r\n********************* NOTICE *********************\r\n" "There was a problem loading your objects from disk.\r\n" "Contact a God for assistance.\r\n", ch); } if (dolog) { syslog(GET_INVIS_LEV(ch), TRUE, "%s entering game with no equipment.", GET_NAME(ch)); if (GET_INVIS_LEV(ch) == 0) mortlog(ch, "%s has entered the game", PERS(ch, ch, 1)); } return (1); } if (!feof(fl)) fread(&rent, sizeof(struct rent_info), 1, fl); else { log("SYSERR: Crash_load: %s's rent file was empty!", GET_NAME(ch)); return (1); } switch (orig_rent_code = rent.rentcode) { case RENT_RENTED: if (dolog) syslog(GET_INVIS_LEV(ch), TRUE, "%s un-renting and entering game.", GET_NAME(ch)); break; case RENT_CRASH: if (dolog) syslog(GET_INVIS_LEV(ch), TRUE, "%s retrieving crash-saved items and entering game.", GET_NAME(ch)); break; default: syslog(GET_INVIS_LEV(ch), TRUE, "SYSERR: %s entering game with undefined rent code %d.", GET_NAME(ch), rent.rentcode); break; } if (dolog && GET_INVIS_LEV(ch) == 0) mortlog(ch, "%s has entered the game", PERS(ch, ch, 1)); while (!feof(fl)) { fread(&object, sizeof(struct obj_file_elem), 1, fl); if (ferror(fl)) { perror("SYSERR: Reading crash file: Crash_load"); fclose(fl); return (1); } if (feof(fl)) break; ++num_objs; if ((obj = Obj_from_store(object, &location)) == NULL) continue; auto_equip(ch, obj, &location); /* * What to do with a new loaded item: * * If there's a list with location less than 1 below this, then its * container has disappeared from the file so we put the list back into * the character's inventory. (Equipped items are 0 here.) * * If there's a list of contents with location of 1 below this, then we * check if it is a container: * - Yes: Get it from the character, fill it, and give it back so we * have the correct weight. * - No: The container is missing so we put everything back into the * character's inventory. * * For items with negative location, we check if there is already a list * of contents with the same location. If so, we put it there and if not, * we start a new list. * * Since location for contents is < 0, the list indices are switched to * non-negative. * * This looks ugly, but it works. */ if (location > 0) { /* Equipped */ for (j = MAX_BAG_ROWS - 1; j > 0; j--) { if (cont_row[j]) { /* No container, back to inventory. */ for (; cont_row[j]; cont_row[j] = obj2) { obj2 = cont_row[j]->next_content; obj_to_char(cont_row[j], ch); } cont_row[j] = NULL; } } if (cont_row[0]) { /* Content list existing. */ if (GET_OBJ_TYPE(obj) == ITEM_CONTAINER || IS_CORPSE(obj)) { /* Remove object, fill it, equip again. */ obj = unequip_char(ch, location - 1); obj->contains = NULL; /* Should be NULL anyway, but just in case. */ for (; cont_row[0]; cont_row[0] = obj2) { obj2 = cont_row[0]->next_content; obj_to_obj(cont_row[0], obj); } equip_char(ch, obj, location - 1); } else { /* Object isn't container, empty the list. */ for (; cont_row[0]; cont_row[0] = obj2) { obj2 = cont_row[0]->next_content; obj_to_char(cont_row[0], ch); } cont_row[0] = NULL; } } } else { /* location <= 0 */ for (j = MAX_BAG_ROWS - 1; j > -location; j--) { if (cont_row[j]) { /* No container, back to inventory. */ for (; cont_row[j]; cont_row[j] = obj2) { obj2 = cont_row[j]->next_content; obj_to_char(cont_row[j], ch); } cont_row[j] = NULL; } } if (j == -location && cont_row[j]) { /* Content list exists. */ if (GET_OBJ_TYPE(obj) == ITEM_CONTAINER || IS_CORPSE(obj)) { /* Take the item, fill it, and give it back. */ obj_from_char(obj); obj->contains = NULL; for (; cont_row[j]; cont_row[j] = obj2) { obj2 = cont_row[j]->next_content; obj_to_obj(cont_row[j], obj); } obj_to_char(obj, ch); /* Add to inventory first. */ } else { /* Object isn't container, empty content list. */ for (; cont_row[j]; cont_row[j] = obj2) { obj2 = cont_row[j]->next_content; obj_to_char(cont_row[j], ch); } cont_row[j] = NULL; } } if (location < 0 && location >= -MAX_BAG_ROWS) { /* * Let the object be part of the content list but put it at the * list's end. Thus having the items in the same order as before * the character rented. */ obj_from_char(obj); if ((obj2 = cont_row[-location - 1]) != NULL) { while (obj2->next_content) obj2 = obj2->next_content; obj2->next_content = obj; } else cont_row[-location - 1] = obj; } } } /* turn this into a crash file by re-writing the control block */ rent.rentcode = RENT_CRASH; rent.time = time(0); rewind(fl); Crash_write_rentcode(ch, fl, &rent); fclose(fl); if (orig_rent_code == RENT_RENTED) return (0); else return (1); } int Crash_save(Object obj, FILE *fp, int location) { Object tmp; int result; if (obj) { Crash_save(obj->next_content, fp, location); Crash_save(obj->contains, fp, MIN(0, location) - 1); result = Obj_to_store(obj, fp, location); for (tmp = obj->in_obj; tmp; tmp = tmp->in_obj) GET_OBJ_WEIGHT(tmp) -= GET_OBJ_WEIGHT(obj); if (!result) return (0); } return (TRUE); } void Crash_restore_weight(Object obj) { if (obj) { Crash_restore_weight(obj->contains); Crash_restore_weight(obj->next_content); if (obj->in_obj) GET_OBJ_WEIGHT(obj->in_obj) += GET_OBJ_WEIGHT(obj); } } /* * Get !RENT items from equipment to inventory and * extract !RENT out of worn containers. */ void Crash_extract_norent_eq(Creature ch) { int j; for (j = 0; j < NUM_WEARS; j++) { if (GET_EQ(ch, j) == NULL) continue; if (Crash_is_unrentable(GET_EQ(ch, j))) obj_to_char(unequip_char(ch, j), ch); else Crash_extract_norents(GET_EQ(ch, j)); } } void Crash_extract_objs(Object obj) { if (obj) { Crash_extract_objs(obj->contains); Crash_extract_objs(obj->next_content); extract_obj(obj); } } int Crash_is_unrentable(Object obj) { if (!obj) return (0); if (GET_OBJ_RNUM(obj) <= NOTHING) return (1); return (0); } void Crash_extract_norents(Object obj) { if (obj) { Crash_extract_norents(obj->contains); Crash_extract_norents(obj->next_content); if (Crash_is_unrentable(obj)) extract_obj(obj); } } void Crash_crashsave(Creature ch) { char buf[MAX_INPUT_LENGTH]; struct rent_info rent; int j; FILE *fp; if (IS_NPC(ch)) return; if (!get_filename(GET_NAME(ch), buf, CRASH_FILE)) return; if (!(fp = fopen(buf, "wb"))) return; rent.rentcode = RENT_CRASH; rent.time = time(0); if (!Crash_write_rentcode(ch, fp, &rent)) { fclose(fp); return; } for (j = 0; j < NUM_WEARS; j++) if (GET_EQ(ch, j)) { if (!Crash_save(GET_EQ(ch, j), fp, j + 1)) { fclose(fp); return; } Crash_restore_weight(GET_EQ(ch, j)); } if (!Crash_save(ch->carrying, fp, 0)) { fclose(fp); return; } Crash_restore_weight(ch->carrying); fclose(fp); } void Crash_rentsave(Creature ch) { char buf[MAX_INPUT_LENGTH]; struct rent_info rent; int j; FILE *fp; if (IS_NPC(ch)) return; if (!get_filename(GET_NAME(ch), buf, CRASH_FILE)) return; if (!(fp = fopen(buf, "wb"))) return; Crash_extract_norent_eq(ch); Crash_extract_norents(ch->carrying); rent.rentcode = RENT_RENTED; rent.time = time(0); if (!Crash_write_rentcode(ch, fp, &rent)) { fclose(fp); return; } for (j = 0; j < NUM_WEARS; j++) if (GET_EQ(ch, j)) { if (!Crash_save(GET_EQ(ch,j), fp, j + 1)) { fclose(fp); return; } Crash_restore_weight(GET_EQ(ch, j)); Crash_extract_objs(GET_EQ(ch, j)); } if (!Crash_save(ch->carrying, fp, 0)) { fclose(fp); return; } fclose(fp); Crash_extract_objs(ch->carrying); } void Crash_save_all(void) { void write_aliases(Creature ch); Descr d; for (d = descriptor_list; d; d = d->next) { if ((STATE(d) == CON_PLAYING) && !IS_NPC(d->character)) { Crash_crashsave(d->character); write_aliases(d->character); SAVE_CHAR(d->character); } } } /* * objpacks are based ENTIRELY on EmpireMUD's standard object saving/loading * code, but with a little of my own personal finesse. Some comments have * been removed because I think they're excessive, but I've made an attempt * not to remove any credits. Just in case I do, this paragraph indicates * the original authors. Don't be fooled, though. This is my own genius * work. - PC 10/13/00 */ /* Objects in room */ int objpack_save_room(int rnum) { struct rent_info rent[1]; FILE *fp; if (!world[rnum].contents) return 0; sprintf(buf, "%s%d.%s", LIB_OBJPACK, world[rnum].number, SUF_PACK); if (!(fp = fopen(buf, "wb"))) return 0; rent[0].rentcode = RENT_CRASH; rent[0].time = time(0); if (fwrite(rent, sizeof(struct rent_info), 1, fp) < 1) { perror("SYSERR: writing rent code"); fclose(fp); return 0; } if (!Crash_save(world[rnum].contents, fp, 0)) { fclose(fp); return 0; } Crash_restore_weight(world[rnum].contents); fclose(fp); return 1; } void objpack_load_room(int rnum) { FILE *fl; char fname[MAX_STRING_LENGTH]; struct obj_file_elem object; struct rent_info rent; int num_objs = 0, j; /* AutoEQ addition. */ Object obj, obj2, cont_row[MAX_BAG_ROWS]; int location; /* Empty all of the container lists (you never know ...) */ for (j = 0; j < MAX_BAG_ROWS; j++) cont_row[j] = NULL; sprintf(fname, "%s%d.%s", LIB_OBJPACK, world[rnum].number, SUF_PACK); if (!(fl = fopen(fname, "r+b"))) { if (errno != ENOENT) log("SYSERR: READING OBJECT FILE %s (5): %s", fname, strerror(errno)); return; } if (!feof(fl)) fread(&rent, sizeof(struct rent_info), 1, fl); else { log("SYSERR: Crash_load: '%s' rent file was empty!", fname); return; } while (!feof(fl)) { fread(&object, sizeof(struct obj_file_elem), 1, fl); if (ferror(fl)) { perror("SYSERR: Reading crash file: Crash_load"); fclose(fl); return; } if (feof(fl)) break; ++num_objs; if ((obj = Obj_from_store(object, &location)) == NULL) continue; if (location > 0) location = LOC_INVENTORY; /* Not really an inventory, but same idea. */ obj_to_room(obj, rnum); for (j = MAX_BAG_ROWS - 1; j > -location; j--) { if (cont_row[j]) { /* No container, back to room. */ for (; cont_row[j]; cont_row[j] = obj2) { obj2 = cont_row[j]->next_content; obj_to_room(cont_row[j], rnum); } cont_row[j] = NULL; } } if (j == -location && cont_row[j]) { /* Content list exists. */ if (GET_OBJ_TYPE(obj) == ITEM_CONTAINER || IS_CORPSE(obj)) { /* Take the item, fill it, and give it back. */ obj_from_room(obj); obj->contains = NULL; for (; cont_row[j]; cont_row[j] = obj2) { obj2 = cont_row[j]->next_content; obj_to_obj(cont_row[j], obj); } obj_to_room(obj, rnum); /* Add to room first. */ } else { /* Object isn't container, empty content list. */ for (; cont_row[j]; cont_row[j] = obj2) { obj2 = cont_row[j]->next_content; obj_to_room(cont_row[j], rnum); } cont_row[j] = NULL; } } if (location < 0 && location >= -MAX_BAG_ROWS) { obj_from_room(obj); if ((obj2 = cont_row[-location - 1]) != NULL) { while (obj2->next_content) obj2 = obj2->next_content; obj2->next_content = obj; } else cont_row[-location - 1] = obj; } } fclose(fl); }