/* ************************************************************************ * file: boards.c, Implementation of boards. Part of CircleMUD * * Usage : Improved Bulletin boards. * * Written by: Jeremy "Ras" Elson. (jelson@server.cs.jhu.edu) * * Copyright (C) 1990, 1993 - see 'license.doc' for complete information. * * Portions of this code are part of CircleMUD * * Copyright (C) 1993 The Trustees of the Johns Hopkins University * ************************************************************************* */ /* FEATURES & INSTALLATION INSTRUCTIONS *********************************** This board code has many improvements over the infamously buggy standard Diku board code. Features include: - Arbitrary number of boards handled by one set of generalized routines. Adding a new board is as easy as adding another entry to an array. - Bug-free operation -- no more mixed messages! - Safe removal of messages while other messages are being written. - Does not allow messages to be removed by someone of a level less than the poster's level. To install: 0. Edit your makefile so that boards.c is compiled and linked into the server. 1. In spec_assign.c, declare the specproc "gen_board". Give ALL boards the gen_board specproc. 2. In boards.h, change the constants CMD_READ, CMD_WRITE, CMD_REMOVE, and CMD_LOOK to the correct command numbers for your mud's interpreter. 3. In boards.h, change NUM_OF_BOARDS to reflect how many different types of boards you have. Change MAX_BOARD_MESSAGES to the maximum number of messages postable before people start to get a 'board is full' message. 4. Follow the instructions for adding a new board (below) to correctly define the board_info array (also below). Make sure you define an entry in this array for each object that you gave the gen_board specproc in step 1. Send comments, bug reports, help requests, etc. to Jeremy Elson (jelson@server.cs.jhu.edu). Enjoy! ************************************************************************/ /* TO ADD A NEW BOARD, simply follow our easy 3-step program: 1 - Create a new board object in the object files 2 - Increase the NUM_OF_BOARDS constant in board.h 3 - Add a new line to the board_info array below. The fields, in order, are: Board's virtual number. Min level one must be to look at this board or read messages on it. Min level one must be to post a message to the board. Min level one must be to remove other people's messages from this board (but you can always remove your own message). Filename of this board, in quotes. Last field must always be 0. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ctype.h> #include "structs.h" #include "utils.h" #include "comm.h" #include "db.h" #include "boards.h" #include "interpreter.h" extern struct room_data *world; extern struct descriptor_data *descriptor_list; void Board_save_board(int board_type); void Board_load_board(int board_type); void Board_reset_board(int board_num); void Board_write_message(int board_type, struct char_data *ch, char *arg); /* format: vnum, read lvl, write lvl, remove lvl, filename, 0 at end Be sure to also change NUM_OF_BOARDS in board.h */ struct board_info_type board_info[NUM_OF_BOARDS] = { { 3099, 0, 0, LEVEL_GOD, "boards/Board.mort" , 0 }, { 3098, LEVEL_IMMORT, LEVEL_IMMORT, LEVEL_GRGOD, "boards/Board.immort" , 0 }, { 3097, LEVEL_IMMORT, LEVEL_FREEZE, LEVEL_IMPL, "boards/Board.freeze" , 0 }, { 3096, 0, 0, LEVEL_IMMORT, "boards/Board.social" , 0 }, }; char *msg_storage[INDEX_SIZE]; int msg_storage_taken[INDEX_SIZE]; int num_of_msgs[NUM_OF_BOARDS]; struct board_msginfo msg_index[NUM_OF_BOARDS][MAX_BOARD_MESSAGES]; int find_slot(void) { int i; for (i = 0; i < INDEX_SIZE; i++) if (!msg_storage_taken[i]) { msg_storage_taken[i] = 1; return i; } return - 1; } /* search the room ch is standing in to find which board he's looking at */ int find_board(struct char_data *ch) { struct obj_data *obj; int i; for (obj = world[ch->in_room].contents; obj; obj = obj->next_content) for (i = 0; i < NUM_OF_BOARDS; i++) if (RNUM(i) == obj->item_number) return i; return - 1; } void init_boards(void) { int i, j, fatal_error = 0; char buf[256]; for (i = 0; i < INDEX_SIZE; i++) { msg_storage[i] = 0; msg_storage_taken[i] = 0; } for (i = 0; i < NUM_OF_BOARDS; i++) { if ((RNUM(i) = real_object(VNUM(i))) == -1) { sprintf(buf, "SYSERR: Fatal board error: board vnum %d does not exist!", VNUM(i)); log(buf); fatal_error = 1; } num_of_msgs[i] = 0; for (j = 0; j < MAX_BOARD_MESSAGES; j++) { memset(&(msg_index[i][j]), '\0', sizeof(struct board_msginfo )); msg_index[i][j].slot_num = -1; } Board_load_board(i); } if (fatal_error) exit(0); } SPECIAL(gen_board) { int board_type; static int loaded = 0; if (!ch->desc) return 0; if (cmd != CMD_WRITE && cmd != CMD_LOOK && cmd != CMD_READ && cmd != CMD_REMOVE) return 0; if (!loaded) { init_boards(); loaded = 1; } if ((board_type = find_board(ch)) == -1) { log("SYSERR: degenerate board! (what the hell..)"); return 0; } switch (cmd) { case CMD_WRITE: Board_write_message(board_type, ch, arg); return 1; break; case CMD_LOOK: Board_save_board(board_type); return(Board_show_board(board_type, ch, arg)); break; case CMD_READ: return(Board_display_msg(board_type, ch, arg)); break; case CMD_REMOVE: return(Board_remove_msg(board_type, ch, arg)); break; default: return 0; break; } } void Board_write_message(int board_type, struct char_data *ch, char *arg) { char *tmstr; int ct, len; char buf[MAX_INPUT_LENGTH], buf2[MAX_INPUT_LENGTH]; if (GET_LEVEL(ch) < WRITE_LVL(board_type)) { send_to_char("You are not holy enough to write on this board.\n\r", ch); return; } if (num_of_msgs[board_type] >= MAX_BOARD_MESSAGES) { send_to_char("The board is full.\n\r", ch); return; } if ((NEW_MSG_INDEX(board_type).slot_num = find_slot()) == -1) { send_to_char("The board is malfunctioning - sorry.\n\r", ch); log("SYSERR: Board: failed to find empty slot on write."); return; } /* skip blanks */ for (; isspace(*arg); arg++) ; if (!*arg) { send_to_char("We must have a headline!\n\r", ch); return; } ct = time(0); tmstr = (char *) asctime(localtime(&ct)); *(tmstr + strlen(tmstr) - 1) = '\0'; sprintf(buf2, "(%s)", GET_NAME(ch)); sprintf(buf, "%6.10s %-12s :: %s", tmstr, buf2, arg); len = strlen(buf) + 1; if (!(NEW_MSG_INDEX(board_type).heading = (char *)malloc(sizeof(char)*len))) { send_to_char("The board is malfunctioning - sorry.\n\r", ch); return; } strcpy(NEW_MSG_INDEX(board_type).heading, buf); NEW_MSG_INDEX(board_type).heading[len-1] = '\0'; NEW_MSG_INDEX(board_type).level = GET_LEVEL(ch); send_to_char("Write your message. Terminate with a @.\n\r\n\r", ch); act("$n starts to write a message.", TRUE, ch, 0, 0, TO_ROOM); if (!IS_NPC(ch)) SET_BIT(PLR_FLAGS(ch), PLR_WRITING); ch->desc->str = &(msg_storage[NEW_MSG_INDEX(board_type).slot_num]); ch->desc->max_str = MAX_MESSAGE_LENGTH; num_of_msgs[board_type]++; } int Board_show_board(int board_type, struct char_data *ch, char *arg) { int i; char tmp[MAX_STRING_LENGTH], buf[MAX_STRING_LENGTH]; if (!ch->desc) return 0; one_argument(arg, tmp); if (!*tmp || !isname(tmp, "board bulletin")) return 0; if (GET_LEVEL(ch) < READ_LVL(board_type)) { send_to_char("You try but fail to understand the holy words.\n\r", ch); return 1; } act("$n studies the board.", TRUE, ch, 0, 0, TO_ROOM); strcpy(buf, "This is a bulletin board. Usage: READ/REMOVE <messg #>, WRITE <header>.\n\r" "You will need to look at the board to save your message.\n\r"); if (!num_of_msgs[board_type]) strcat(buf, "The board is empty.\n\r"); else { sprintf(buf + strlen(buf), "There are %d messages on the board.\n\r", num_of_msgs[board_type]); for (i = 0; i < num_of_msgs[board_type]; i++) { if (MSG_HEADING(board_type, i)) sprintf(buf + strlen(buf), "%-2d : %s\n\r", i + 1, MSG_HEADING(board_type, i)); else { log("SYSERR: The board is fubar'd."); send_to_char("Sorry, the board isn't working.\n\r", ch); return 1; } } } page_string(ch->desc, buf, 1); return 1; } int Board_display_msg(int board_type, struct char_data *ch, char *arg) { char number[MAX_STRING_LENGTH], buffer[MAX_STRING_LENGTH]; int msg, ind; one_argument(arg, number); if (!*number || !isdigit(*number)) return 0; if (!(msg = atoi(number))) return 0; if (GET_LEVEL(ch) < READ_LVL(board_type)) { send_to_char("You try but fail to understand the holy words.\n\r", ch); return 1; } if (!num_of_msgs[board_type]) { send_to_char("The board is empty!\n\r", ch); return(1); } if (msg < 1 || msg > num_of_msgs[board_type]) { send_to_char("That message exists only in your imagination..\n\r", ch); return(1); } ind = msg - 1; if (MSG_SLOTNUM(board_type, ind) < 0 || MSG_SLOTNUM(board_type, ind) >= INDEX_SIZE) { send_to_char("Sorry, the board is not working.\n\r", ch); log("SYSERR: Board is screwed up."); return 1; } if (!(MSG_HEADING(board_type, ind))) { send_to_char("That message appears to be screwed up.\n\r", ch); return 1; } if (!(msg_storage[MSG_SLOTNUM(board_type, ind)])) { send_to_char("That message seems to be empty.\n\r", ch); return 1; } sprintf(buffer, "Message %d : %s\n\r\n\r%s\n\r", msg, MSG_HEADING(board_type, ind), msg_storage[MSG_SLOTNUM(board_type, ind)]); page_string(ch->desc, buffer, 1); return 1; } int Board_remove_msg(int board_type, struct char_data *ch, char *arg) { int ind, msg, slot_num; char number[MAX_INPUT_LENGTH], buf[MAX_INPUT_LENGTH]; struct descriptor_data *d; one_argument(arg, number); if (!*number || !isdigit(*number)) return 0; if (!(msg = atoi(number))) return(0); if (!num_of_msgs[board_type]) { send_to_char("The board is empty!\n\r", ch); return 1; } if (msg < 1 || msg > num_of_msgs[board_type]) { send_to_char("That message exists only in your imagination..\n\r", ch); return 1; } ind = msg - 1; if (!MSG_HEADING(board_type, ind)) { send_to_char("That message appears to be screwed up.\n\r", ch); return 1; } sprintf(buf, "(%s)", GET_NAME(ch)); if (GET_LEVEL(ch) < REMOVE_LVL(board_type) && !(strstr(MSG_HEADING(board_type, ind), buf))) { send_to_char("You are not holy enough to remove other people's messages.\n\r", ch); return 1; } if (GET_LEVEL(ch) < MSG_LEVEL(board_type, ind)) { send_to_char("You can't remove a message holier than yourself.\n\r", ch); return 1; } slot_num = MSG_SLOTNUM(board_type, ind); if (slot_num < 0 || slot_num >= INDEX_SIZE) { log("SYSERR: The board is seriously screwed up."); send_to_char("That message is majorly screwed up.\n\r", ch); return 1; } for (d = descriptor_list; d; d = d->next) if (!d->connected && d->str == &(msg_storage[slot_num])) { send_to_char("At least wait until the author is finished before removing it!\n\r", ch); return 1; } if (msg_storage[slot_num]) free(msg_storage[slot_num]); msg_storage[slot_num] = 0; msg_storage_taken[slot_num] = 0; if (MSG_HEADING(board_type, ind)) free(MSG_HEADING(board_type, ind)); for (; ind < num_of_msgs[board_type] - 1; ind++) { MSG_HEADING(board_type, ind) = MSG_HEADING(board_type, ind + 1); MSG_SLOTNUM(board_type, ind) = MSG_SLOTNUM(board_type, ind + 1); MSG_LEVEL(board_type, ind) = MSG_LEVEL(board_type, ind + 1); } num_of_msgs[board_type]--; send_to_char("Message removed.\n\r", ch); sprintf(buf, "$n just removed message %d.", msg); act(buf, FALSE, ch, 0, 0, TO_ROOM); Board_save_board(board_type); return 1; } void Board_save_board(int board_type) { FILE * fl; int i; char *tmp1 = 0, *tmp2 = 0; if (!num_of_msgs[board_type]) { unlink(FILENAME(board_type)); return; } if (!(fl = fopen(FILENAME(board_type), "wb"))) { perror("Error writing board"); return; } fwrite(&(num_of_msgs[board_type]), sizeof(int), 1, fl); for (i = 0; i < num_of_msgs[board_type]; i++) { if (tmp1 = MSG_HEADING(board_type, i)) msg_index[board_type][i].heading_len = strlen(tmp1) + 1; else msg_index[board_type][i].heading_len = 0; if (MSG_SLOTNUM(board_type, i) < 0 || MSG_SLOTNUM(board_type, i) >= INDEX_SIZE || (!(tmp2 = msg_storage[MSG_SLOTNUM(board_type, i)]))) msg_index[board_type][i].message_len = 0; else msg_index[board_type][i].message_len = strlen(tmp2) + 1; fwrite(&(msg_index[board_type][i]), sizeof(struct board_msginfo ), 1, fl); if (tmp1) fwrite(tmp1, sizeof(char), msg_index[board_type][i].heading_len, fl); if (tmp2) fwrite(tmp2, sizeof(char), msg_index[board_type][i].message_len, fl); } fclose(fl); } void Board_load_board(int board_type) { FILE * fl; int i, len1 = 0, len2 = 0; char *tmp1 = 0, *tmp2 = 0; if (!(fl = fopen(FILENAME(board_type), "rb"))) { perror("Error reading board"); return; } fread(&(num_of_msgs[board_type]), sizeof(int), 1, fl); if (num_of_msgs[board_type] < 1 || num_of_msgs[board_type] > MAX_BOARD_MESSAGES) { log("SYSERR: Board file corrupt. Resetting."); Board_reset_board(board_type); return; } for (i = 0; i < num_of_msgs[board_type]; i++) { fread(&(msg_index[board_type][i]), sizeof(struct board_msginfo ), 1, fl); if (!(len1 = msg_index[board_type][i].heading_len)) { log("SYSERR: Board file corrupt! Resetting."); Board_reset_board(board_type); return; } if (!(tmp1 = (char *)malloc(sizeof(char) *len1))) { log("SYSERR: Error - malloc failed for board header"); exit(1); } fread(tmp1, sizeof(char), len1, fl); MSG_HEADING(board_type, i) = tmp1; if (len2 = msg_index[board_type][i].message_len) { if ((MSG_SLOTNUM(board_type, i) = find_slot()) == -1) { log("SYSERR: Out of slots booting board! Resetting.."); Board_reset_board(board_type); return; } if (!(tmp2 = (char *)malloc(sizeof(char) *len2))) { log("SYSERR: malloc failed for board text"); exit(1); } fread(tmp2, sizeof(char), len2, fl); msg_storage[MSG_SLOTNUM(board_type, i)] = tmp2; } } fclose(fl); } void Board_reset_board(int board_type) { int i; for (i = 0; i < MAX_BOARD_MESSAGES; i++) { if (MSG_HEADING(board_type, i)) free (MSG_HEADING(board_type, i)); if (msg_storage[MSG_SLOTNUM(board_type, i)]) free (msg_storage[MSG_SLOTNUM(board_type, i)]); msg_storage_taken[MSG_SLOTNUM(board_type, i)] = 0; memset(&(msg_index[board_type][i]), '\0', sizeof(struct board_msginfo )); msg_index[board_type][i].slot_num = -1; } num_of_msgs[board_type] = 0; unlink(FILENAME(board_type)); }