/************************************************************************ Realms of Aurealis James Rhone aka Vall of RoA boards.c Handling of runtime configurable bulletin boards. (At some near pt. these will be OLCable.) Most functions and structures in this file have been altered to work with the new system beginning on 12/29/97 -jtrhone. ******** Heavily modified and expanded ******** *** BE AWARE OF ALL RIGHTS AND RESERVATIONS *** ******** Heavily modified and expanded ******** All rights reserved henceforth. Please note that no guarantees are associated with any code from Realms of Aurealis. All code which has been released to the general public has been done so with an 'as is' pretense. RoA is based on both Diku and CircleMUD and ALL licenses from both *MUST* be adhered to as well as the RoA license. *** Read, Learn, Understand, Improve *** *************************************************************************/ #include "conf.h" #include "sysdep.h" #include "structures.h" #include "utils.h" #include "comm.h" #include "db.h" #include "boards.h" #include "interpreter.h" #include "handler.h" #include "lists.h" #include "acmd.h" #include "global.h" #include "descmenu.h" // local global variables binfotype board_info[MAX_BOARDS]; bmsginfo msg_index[MAX_BOARDS][MAX_BOARD_MESSAGES]; bindex master_index[MAX_MSGS]; // assemble filename into fname and return void get_board_fname(int bnum, char *fname) { sprintf(fname, "%s/%d.brd", BOARD_DIR, B_VNUM(bnum)); } // return a ptr the proto of this board obdata *get_board_proto(int bnum) { int rnum; rnum = real_object(B_VNUM(bnum)); if (rnum < 0) { sprintf(buf2, "SYSERR: Boards: B%d, invalid vnum.", bnum); mudlog(buf2, BRF, LEV_IMM, TRUE); return NULL; } return (&obj_proto[rnum]); } // assign a board some characteristics void assign_board(int bnum, int vnum, int read, int write, int remove) { obdata *ob; B_VNUM(bnum) = vnum; READ_LVL(bnum) = read; WRITE_LVL(bnum) = write; REMOVE_LVL(bnum) = remove; // now fill in rest... B_MSGS(bnum) = 0; B_VALID(bnum) = TRUE; // make sure proto is a BOARD type ob = get_board_proto(bnum); ITEM_TYPE(ob) = ITEM_BOARD; } // if the BOARD_CONFIG_FILE is not found, or there is an error loading // it, we will fill up the board info structure with default info void use_default_boards(void) { mudlog("SYSUPD: Board config file not found, using defaults.", BRF, LEV_IMM, TRUE); // # vnum read write remove assign_board(0, 3099, 1, 1, LEV_GOD); assign_board(1, 3098, LEV_IMM, LEV_IMM, LEV_AIMP); assign_board(2, 3097, LEV_IMM, LEV_FREEZE, LEV_IMPL); assign_board(3, 3096, 1, 1, LEV_IMM); assign_board(4, 1201, LEV_IMM, LEV_IMM, LEV_GOD); assign_board(5, 1202, LEV_IMM, LEV_IMM, LEV_GOD); assign_board(6, 1203, 1, LEV_IMM, LEV_AIMP); assign_board(7, 1204, 1, 1, 1 ); assign_board(8, 1205, 1, LEV_AIMP, LEV_AIMP); } // fill the board_info structure with values from config file // return TRUE if successful, FALSE otherwise BOOL load_board_config_from_file(void) { char letter = '\0'; FILE *fp; int bnum, tmp1, tmp2, tmp3, tmp4; if (!(fp = fopen(BOARD_CONFIG_FILE, "rt"))) return 0; bnum = 0; while (TRUE) { letter=fread_letter(fp); ungetc(letter, fp); if (bnum >= MAX_BOARDS || letter == '$' || letter == '~') break; fscanf(fp, "%d %d %d %d\n", &tmp1, &tmp2, &tmp3, &tmp4); assign_board(bnum, tmp1, tmp2, tmp3, tmp4); bnum++; } fclose(fp); mudlog("SYSUPD: Board configuration read from file.", BUG, LEV_IMM, TRUE); return TRUE; } // attempt to load board configuration file, if fail, use the defaults void load_board_config(void) { if (!load_board_config_from_file()) use_default_boards(); } // save the board configuration to disk void save_board_config(void) { FILE *fp; int bnum; if (!(fp = fopen(BOARD_CONFIG_FILE, "wt"))) { mudlog("SYSERR: Unable to save board config file.", BRF, LEV_IMM, TRUE); return; } for (bnum = 0; bnum < MAX_BOARDS && B_VALID(bnum); bnum++) fprintf(fp, "%d %d %d %d\n", B_VNUM(bnum), READ_LVL(bnum), WRITE_LVL(bnum), REMOVE_LVL(bnum)); // stick on an EOF marker... fprintf(fp, "$~\n"); fclose(fp); mudlog("SYSUPD: Board configuration saved.", BUG, LEV_IMM, TRUE); } ACMD(do_save_boards) { if (IS_NPC(ch)) return; save_board_config(); send_to_char("Ok.\n\r",ch); } ACMD(do_blist) { int bnum; if (IS_NPC(ch)) return; strcpy(buf, "%B%6Board Info Table%0:\n\r"); for (bnum = 0; bnum < MAX_BOARDS && B_VALID(bnum); bnum++) sprintf(buf+strlen(buf), "%%6Vnum%%0:%d, %%6Read%%0:%d, %%6Write%%0:%d, %%6Remove%%0:%d\n\r", B_VNUM(bnum), READ_LVL(bnum), WRITE_LVL(bnum), REMOVE_LVL(bnum)); page_string(ch->desc, buf, 1); } BOOL board_in_table(int vnum) { int bnum; for (bnum = 0; bnum < MAX_BOARDS && B_VALID(bnum); bnum++) if (B_VNUM(bnum) == vnum) return TRUE; return FALSE; } void add_board(int vnum, int read, int write, int remove) { int bnum; for (bnum = 0; bnum < MAX_BOARDS && B_VALID(bnum); bnum++) ; if (bnum >= MAX_BOARDS) { mudlog("SYSERR: Board table filled up... unable to add.",BRF, LEV_IMM, TRUE); return; } assign_board(bnum, vnum, read, write, remove); } // add a board to the board table ACMD(do_badd) { obdata *ob; char *argu = argument; int rnum, vnum, read, write, remove; char arg1[MAX_STRING_LENGTH], arg2[MAX_STRING_LENGTH]; char arg3[MAX_STRING_LENGTH], arg4[MAX_STRING_LENGTH]; if (IS_NPC(ch)) return; skip_spaces(&argu); if (!*argu) { send_to_char("Usage: badd <board vnum> <min to read> <min to write> <min to remove>.\n\r",ch); return; } // first, make sure vnum exists and is NOT a board half_chop(argu, arg1, argu); if (!is_number(arg1) || (rnum = real_object((vnum = atoi(arg1)))) < 0) { send_to_char("Usage: badd <board vnum> <min to read> <min to write> <min to remove>.\n\r",ch); return; } ob = &obj_proto[rnum]; if (ITEM_TYPE(ob) != ITEM_BOARD) { send_to_char("Item must be of type BOARD.\n\r",ch); return; } if (board_in_table(vnum)) { send_to_char("That board is already in the board table.\n\r",ch); return; } half_chop(argu, arg2, argu); if (!*arg2 || !is_number(arg2) || (read = atoi(arg2)) < 1 || read > LEV_IMPL) { send_to_char("Usage: badd <board vnum> <min to read> <min to write> <min to remove>.\n\r",ch); return; } half_chop(argu, arg3, argu); if (!*arg3 || !is_number(arg3) || (write = atoi(arg3)) < 1 || write > LEV_IMPL) { send_to_char("Usage: badd <board vnum> <min to read> <min to write> <min to remove>.\n\r",ch); return; } half_chop(argu, arg4, argu); if (!*arg4 || !is_number(arg4) || (remove = atoi(arg4)) < 1 || remove > LEV_IMPL) { send_to_char("Usage: badd <board vnum> <min to read> <min to write> <min to remove>.\n\r",ch); return; } // okie, find first open slot in the table and add add_board(vnum, read, write, remove); do_save_boards(ch, "", 0, 0); } // find an available slot in the global board array int find_slot(void) { int i; for (i = 0; i < MAX_MSGS; i++) if (!master_index[i].used) { master_index[i].used = TRUE; return i; } return -1; } // return TRUE if there is a board in here... BOOL board_in_room(int rnum) { obdata *ob; for (ob = world[rnum].contents; ob; ob=ob->next_content) if (ITEM_TYPE(ob) == ITEM_BOARD) return TRUE; return FALSE; } // find a match between objects in room and boards in room // NOW use VNUMs int find_board(chdata *ch) { obdata *obj; int bnum; for (obj = world[ch->in_room].contents; obj; obj = obj->next_content) if (ITEM_TYPE(obj) == ITEM_BOARD) for (bnum = 0; bnum < MAX_BOARDS && B_VALID(bnum); bnum++) if (B_VNUM(bnum) == GET_OBJ_VNUM(obj)) return bnum; return -1; } // use memset now... 12/29/97 -jtrhone void clear_master_index(void) { memset((char *)master_index, 0, sizeof(bindex) * MAX_MSGS); } // initialize the boards... void init_boards(void) { int bnum, msg; char buf[MAX_INPUT_LENGTH]; clear_master_index(); load_board_config(); for (bnum = 0; bnum < MAX_BOARDS && B_VALID(bnum); bnum++) { if (real_object(B_VNUM(bnum)) < 0) { sprintf(buf, "SYSERR: Fatal board error: board vnum %d does not exist!", B_VNUM(msg)); log(buf); B_VALID(bnum) = FALSE; continue; } for (msg = 0; msg < MAX_BOARD_MESSAGES; msg++) { memset(&(MESSAGE(bnum, msg)), '\0', sizeof(bmsginfo)); MSG_SLOTNUM(bnum, msg) = -1; } Board_load_board(bnum); } } // direct command to erase a message (used to be handled by do_remove) 4/14/98 -jtrhone ACMD(do_erase) { if (board_in_room(ch->in_room) && board_interaction(ch, argument, get_command("erase"))) return; send_to_char("You can't find anything to erase.\n\r",ch); } // used to be hard coded SPECIAL(gen_board)... // not anymore 12/30/97 -jtrhone // boards are now initiated at BOOTUP (look in db.c) int board_interaction(chdata *ch, char *arg, int cmd) { int bnum; if ((bnum = find_board(ch)) == -1) { sprintf(buf, "SYSERR: Degenerate board... via %s.", GET_NAME(ch)); mudlog(buf, BRF, LEV_IMM, TRUE); return 0; } if (!B_VALID(bnum)) { send_to_char("Board appears to be invalid... report to an immortal.\n\r",ch); return 0; } if (CMD_IS(cmd, "write")) { Board_write_message(bnum, ch, arg); return 1; } else if (CMD_IS(cmd, "look")) return (Board_show_board(bnum, ch, arg)); else if (CMD_IS(cmd, "read")) return (Board_display_msg(bnum, ch, arg)); else if (CMD_IS(cmd, "erase")) return (Board_remove_msg(bnum, ch, arg)); else return 0; } // a descriptor menu for easier board writing and allowance of roa client usage 7/18/98 -jtrhone // will get called after do_var_string_arg finishes and pops and calls ROA_DESCMENU(doneposting) { Board_save_board(BOARD_WRITING(d->character)); BOARD_WRITING(d->character) = -1; REMOVE_BIT(PLR_FLAGS(d->character), PLR_WRITING); send_to_char("Board posting complete.\n\r", d->character); // no more menus... DESCMENU_PROMPT(d) = NULL; DESCMENU_HANDLER(d) = NULL; DESCMENU_DEPTH(d) = 0; } // ok, startem writing a message void Board_write_message(int bnum, chdata *ch, char *arg) { char *tmstr, buf[MAX_INPUT_LENGTH], buf2[MAX_INPUT_LENGTH]; int len; long ct; if (IS_NPC(ch)) return; if (GET_LEVEL(ch) < WRITE_LVL(bnum)) { send_to_char("You are not holy enough to write on this board.\n\r", ch); return; } if (B_MSGS(bnum) >= MAX_BOARD_MESSAGES) { send_to_char("The board is full.\n\r", ch); return; } // get the next available slot in the master_index if ((NEW_MSG_INDEX(bnum).slot_num = find_slot()) == -1) { send_to_char("No empty message slots available...\n\r", ch); mudlog("SYSERR: Boards: No slots left in master index...", BRF, LEV_IMM, TRUE); return; } skip_spaces(&arg); if (!*arg) { send_to_char("You must have a message heading.\n\r", ch); return; } // get a timestamp ct = time(0); tmstr = (char *) asctime(localtime(&ct)); // wax the newline *(tmstr + strlen(tmstr) - 1) = '\0'; // construct the one line heading sprintf(buf2, "(%s)", GET_NAME(ch)); sprintf(buf, "%6.10s %-12s :: %s", tmstr, buf2, arg); len = strlen(buf) + 1; if (!(NEW_MSG_INDEX(bnum).heading = (char *)malloc(sizeof(char)*len))) { send_to_char("No memory available for message heading...\n\r", ch); mudlog("SYSERR: Boards: No memory available for message heading...", BRF, LEV_IMM, TRUE); return; } strcpy(NEW_MSG_INDEX(bnum).heading, buf); NEW_MSG_INDEX(bnum).heading[len-1] = '\0'; NEW_MSG_INDEX(bnum).level = GET_LEVEL(ch); act("$n starts to write a message.", TRUE, ch, 0, 0, TO_ROOM); SET_BIT(PLR_FLAGS(ch), PLR_WRITING); BOARD_WRITING(ch) = bnum; descmenu_next(ch->desc, doneposting); do_var_string_arg_desc(ch->desc, "Write your message. Terminate with a '@':\n\r", &(master_index[NEW_MSG_INDEX(bnum).slot_num].msg), MAX_MESSAGE_LENGTH); B_MSGS(bnum)++; } // if someone looks at a board they see..... int Board_show_board(int bnum, chdata *ch, char *arg) { int msg; obdata *ob; if (GET_LEVEL(ch) < READ_LVL(bnum)) { send_to_char("You are unable to decipher any meaning from those words.\n\r", ch); return 1; } if (!(ob = get_board_proto(bnum))) return 0; act("$n studies $p.", TRUE, ch, ob, 0, TO_ROOM); strcpy(buf, "This item can be used as a board. See %Bhelp boards%0 for more info.\n\r"); if (!B_MSGS(bnum)) { str_cat(buf, "There is nothing written on $p.\n\r", MAX_STRING_LENGTH, "Board_show_board"); act(buf, TRUE, ch, ob, 0, TO_CHAR); return 1; } else { sprintf(buf + strlen(buf), "There are %d messages on %s.\n\r", B_MSGS(bnum), ob->shdesc); for (msg = 0; msg < B_MSGS(bnum); msg++) { if (MSG_HEADING(bnum, msg)) sprintf(buf + strlen(buf), "%-2d : %s\n\r", msg + 1, MSG_HEADING(bnum, msg)); else { sprintf(buf2, "SYSERR: Boards: B%d, M%d missing header.", bnum, msg); mudlog(buf2, BRF, LEV_IMM, TRUE); send_to_char("Sorry, the board isn't working.\n\r", ch); return 1; } } } page_string(ch->desc, buf, 1); return 1; } // show msg to character int Board_display_msg(int bnum, chdata *ch, char *arg) { char number[MAX_STRING_LENGTH], buffer[MAX_STRING_LENGTH]; int msg, ind; if (!ch->desc) return 0; one_argument(arg, number); if (!*number || !isdigit(*number)) return 0; if (!(msg = atoi(number))) return 0; if (GET_LEVEL(ch) < READ_LVL(bnum)) { send_to_char("You are unable to decipher any meaning from those words.\n\r", ch); return 1; } if (!B_MSGS(bnum)) { send_to_char("The board is empty!\n\r", ch); return(1); } if (msg < 1 || msg > B_MSGS(bnum)) { send_to_char("That message exists only in your imagination.\n\r", ch); return(1); } ind = msg - 1; if (MSG_SLOTNUM(bnum, ind) < 0 || MSG_SLOTNUM(bnum, ind) >= MAX_MSGS) { send_to_char("Sorry, the board is not working.\n\r", ch); sprintf(buf, "SYSERR: Boards: B%d, M%d invalid slotnum.",bnum, ind); mudlog(buf, BRF, LEV_IMM, TRUE); return 1; } if (!(MSG_HEADING(bnum, ind))) { send_to_char("That message appears to be screwed up.\n\r", ch); sprintf(buf, "SYSERR: Boards: B%d, M%d missing heading.",bnum, ind); mudlog(buf, BRF, LEV_IMM, TRUE); return 1; } if (!(master_index[MSG_SLOTNUM(bnum, ind)].used)) { send_to_char("That message seems to be empty.\n\r", ch); sprintf(buf, "SYSERR: Boards: B%d, M%d empty message block.",bnum, ind); mudlog(buf, BRF, LEV_IMM, TRUE); return 1; } sprintf(buffer, "Message %d : %s\n\r\n\r%s\n\r", msg, MSG_HEADING(bnum, ind), master_index[MSG_SLOTNUM(bnum, ind)].msg); /* REMIND me what message i just read!! RoA James Rhone*/ sprintf(buf, "\n\rEnd of message #%d.\n\r", msg); str_cat(buffer, buf, MAX_STRING_LENGTH, "Board_display_msg"); page_string(ch->desc, buffer, 1); return 1; } // when character removes a message... int Board_remove_msg(int bnum, chdata *ch, char *arg) { int ind, msg, slot_num; char number[MAX_INPUT_LENGTH], buf[MAX_INPUT_LENGTH]; dsdata *d; if (IS_NPC(ch)) return 0; one_argument(arg, number); if (!*number || !isdigit(*number)) return 0; if (!(msg = atoi(number))) return(0); if (!B_MSGS(bnum)) { send_to_char("The board is empty!\n\r", ch); return 1; } if (msg < 1 || msg > B_MSGS(bnum)) { send_to_char("That message exists only in your imagination..\n\r", ch); return 1; } ind = msg - 1; if (!MSG_HEADING(bnum, ind)) { send_to_char("That message appears to be screwed up.\n\r", ch); sprintf(buf, "SYSERR: Boards: B%d, M%d missing heading.",bnum, ind); mudlog(buf, BRF, LEV_IMM, TRUE); return 1; } sprintf(buf, "(%s)", GET_NAME(ch)); if (GET_LEVEL(ch) < REMOVE_LVL(bnum) && !(strstr(MSG_HEADING(bnum, ind), buf))) { send_to_char("You are unable to remove other's messages.\n\r", ch); return 1; } if (GET_LEVEL(ch) < MSG_LEVEL(bnum, ind)) { send_to_char("You cannot remove that message.\n\r", ch); return 1; } slot_num = MSG_SLOTNUM(bnum, ind); if (slot_num < 0 || slot_num >= MAX_MSGS) { sprintf(buf, "SYSERR: Boards: B%d, M%d invalid slotnum in master index.",bnum, ind); mudlog(buf, BRF, LEV_IMM, TRUE); send_to_char("That message is majorly screwed up.\n\r", ch); return 1; } // scan people connected to make sure nobody is currently writing this message for (d = descriptor_list; d; d = d->next) if (!d->connected && d->str == &(master_index[slot_num].msg)) { send_to_char("At least wait until the author is finished before removing it!\n\r", ch); return 1; } master_index[slot_num].used = FALSE; FREENULL(master_index[slot_num].msg); FREENULL(MSG_HEADING(bnum, ind)); for ( ; ind < B_MSGS(bnum) - 1; ind++) { MSG_HEADING(bnum, ind) = MSG_HEADING(bnum, ind + 1); MSG_SLOTNUM(bnum, ind) = MSG_SLOTNUM(bnum, ind + 1); MSG_LEVEL(bnum, ind) = MSG_LEVEL(bnum, ind + 1); } B_MSGS(bnum)--; send_to_char("Message removed.\n\r", ch); sprintf(buf, "$n just removed message %d.", msg); act(buf, TRUE, ch, 0, 0, TO_ROOM); Board_save_board(bnum); return 1; } // save an individual board to disk... void Board_save_board(int bnum) { FILE *fl; int msg; char fname[MAX_INPUT_LENGTH], *tmp1 = 0, *tmp2 = 0; get_board_fname(bnum, fname); // if no messages, simply remove the file... if (B_MSGS(bnum) <= 0) { unlink(fname); return; } if (!(fl = fopen(fname, "wb"))) { sprintf(buf, "SYSERR: Boards: B%d error writing to file...",bnum); mudlog(buf, BRF, LEV_IMM, TRUE); return; } // first, write the number of messages to file fwrite(&(B_MSGS(bnum)), sizeof(int), 1, fl); for (msg = 0; msg < B_MSGS(bnum); msg++) { if ((tmp1 = MSG_HEADING(bnum, msg))) msg_index[bnum][msg].heading_len = strlen(tmp1) + 1; else msg_index[bnum][msg].heading_len = 0; if (MSG_SLOTNUM(bnum, msg) < 0 || MSG_SLOTNUM(bnum, msg) >= MAX_MSGS || (!(tmp2 = master_index[MSG_SLOTNUM(bnum, msg)].msg))) msg_index[bnum][msg].message_len = 0; else msg_index[bnum][msg].message_len = strlen(tmp2) + 1; // for each message, write the message info out fwrite(&(msg_index[bnum][msg]), sizeof(bmsginfo), 1, fl); // if we have a heading, write out the heading if (tmp1) fwrite(tmp1, sizeof(char), msg_index[bnum][msg].heading_len, fl); // if we have a message, write out the message if (tmp2) fwrite(tmp2, sizeof(char), msg_index[bnum][msg].message_len, fl); } // we're done... fclose(fl); } // load an individual board from disk... void Board_load_board(int bnum) { FILE *fl; int msg, len1 = 0, len2 = 0; char fname[MAX_INPUT_LENGTH], *tmp = 0; get_board_fname(bnum, fname); // no error, just dont load the board if (!(fl = fopen(fname, "rb"))) { return; } fread(&(B_MSGS(bnum)), sizeof(int), 1, fl); if (B_MSGS(bnum) < 1 || B_MSGS(bnum) > MAX_BOARD_MESSAGES) { sprintf(buf, "SYSERR: Boards: B%d corrupt, resetting...",bnum); mudlog(buf, BRF, LEV_IMM, TRUE); Board_reset_board(bnum); return; } for (msg = 0; msg < B_MSGS(bnum); msg++) { fread(&(msg_index[bnum][msg]), sizeof(bmsginfo), 1, fl); if (!(len1 = msg_index[bnum][msg].heading_len)) { sprintf(buf, "SYSERR: Boards: B%d M%d corrupt, resetting...",bnum, msg); mudlog(buf, BRF, LEV_IMM, TRUE); Board_reset_board(bnum); return; } // allocate and read into CREATE(MSG_HEADING(bnum, msg), char, len1); fread(MSG_HEADING(bnum, msg), sizeof(char), len1, fl); if ((len2 = msg_index[bnum][msg].message_len)) { if ((MSG_SLOTNUM(bnum, msg) = find_slot()) == -1) { sprintf(buf, "SYSERR: Boards: B%d M%d no slot available, resetting.", bnum, msg); mudlog(buf, BRF, LEV_IMM, TRUE); Board_reset_board(bnum); return; } // allocate for message block and read into CREATE(tmp, char, len2); fread(tmp, sizeof(char), len2, fl); // ok, assign master index this message txt master_index[MSG_SLOTNUM(bnum, msg)].msg = tmp; } } /* NULL out remaining heading pointers -RoA 9/1/97 */ for ( ; msg < MAX_BOARD_MESSAGES; msg++) MSG_HEADING(bnum, msg) = NULL; fclose(fl); } // in case of an error, reset a particular board void Board_reset_board(int bnum) { int msg; char fname[MAX_INPUT_LENGTH]; for (msg = 0; msg < B_MSGS(bnum) - 1; msg++) { FREENULL(MSG_HEADING(bnum, msg)); FREENULL(master_index[MSG_SLOTNUM(bnum, msg)].msg); master_index[MSG_SLOTNUM(bnum, msg)].used = FALSE; memset(&(msg_index[bnum][msg]), '\0', sizeof(bmsginfo)); msg_index[bnum][msg].slot_num = -1; } B_MSGS(bnum) = 0; get_board_fname(bnum, fname); unlink(fname); } // free all the boards... void free_the_boards(void) { int i, bnum; for (bnum = 0; bnum < MAX_BOARDS && B_VALID(bnum); bnum++) for (i = 0; i < B_MSGS(bnum); i++) { FREENULL(MSG_HEADING(bnum, i)); FREENULL(master_index[MSG_SLOTNUM(bnum, i)].msg); } }