diff --git a/src/hsedit.c b/src/hsedit.c deleted file mode 100755 index 184e662..0000000 --- a/src/hsedit.c +++ /dev/null @@ -1,1134 +0,0 @@ -/************************************************************************** -* File: hsedit.c Part of tbaMUD * -* Usage: Oasis OLC - Houses. * -* * -* Copyright 1996 Harvey Gilpin. 1997-2001 George Greer. * -* Copyright 2007-2010 Stefan Cole and tbaMUD * -**************************************************************************/ - -#include "conf.h" -#include "sysdep.h" -#include "structs.h" -#include "comm.h" -#include "utils.h" -#include "handler.h" -#include "interpreter.h" -#include "db.h" -#include "boards.h" -#include "oasis.h" -#include "genolc.h" -#include "genzon.h" -#include "constants.h" -#include "house.h" -#include "screen.h" - -/* Internal (static) functions */ -static void hsedit_setup_new(struct descriptor_data *d); -static void hsedit_setup_existing(struct descriptor_data *d, struct house_control_data *hse); -static void hsedit_save_to_disk(struct descriptor_data *d); -static void hsedit_save_internally(struct descriptor_data *d); -static void hedit_delete_house(struct descriptor_data *d, int house_vnum); -static void hsedit_disp_flags_menu(struct descriptor_data * d); -static void hsedit_owner_menu(struct descriptor_data *d); -static void hsedit_dir_menu(struct descriptor_data *d); -static void hsedit_disp_type_menu(struct descriptor_data *d); -static void hsedit_disp_guest_menu(struct descriptor_data *d); -static char *hsedit_list_guests(struct house_control_data *thishouse, char *guestlist); -static void hsedit_disp_menu(struct descriptor_data * d); - -/* External Globals */ -extern struct house_control_data *house_control; - -/*------------------------------------------------------------------------*/ -static void hsedit_setup_new(struct descriptor_data *d) -{ - OLC_HOUSE(d) = new_house(); - - OLC_HOUSE(d)->vnum = OLC_NUM(d); - OLC_HOUSE(d)->built_by = GET_IDNUM(d->character); - - OLC_VAL(d) = 0; - hsedit_disp_menu(d); -} - -/*------------------------------------------------------------------------*/ -static void hsedit_setup_existing(struct descriptor_data *d, struct house_control_data *hse) -{ - struct house_control_data *house; - struct guest_data *g, *next_g; - int i; - - /*. Build a copy of the house .*/ - CREATE (house, struct house_control_data, 1); - - /* allocate space for all strings */ - - /* Copy all variables */ - house->vnum = hse->vnum; - house->atrium = hse->atrium; - house->owner = hse->owner; - house->exit_num = hse->exit_num; - house->built_on = hse->built_on; - house->built_by = hse->built_by; - house->mode = hse->mode; - house->last_payment = hse->last_payment; - house->receptionist = hse->receptionist; - house->next = NULL; - - /* Copy the house flags */ - for (i=0; i < HS_ARRAY_MAX; i++) - house->house_flags[i] = hse->house_flags[i]; - - /* Copy the guest list too */ - for (g=hse->guests; g; g = next_g) { - next_g = g->next; - add_house_guest(house, g->id); - } - - /*. Attach house copy to players descriptor .*/ - OLC_HOUSE(d) = house; - OLC_VAL(d) = 0; - hsedit_disp_menu(d); -} - -/*-----------------------------------------------------------1-------------*/ -static void hsedit_save_to_disk(struct descriptor_data *d) -{ - /* Save all houses */ - House_save_control(); -} - -/*-----------------------------------------------------------1-------------*/ -static void hsedit_save_internally(struct descriptor_data *d) -{ - struct house_control_data *hse; - struct guest_data *g, *next_g; - int i; - - hse = find_house(OLC_NUM(d)); - if (hse != NULL) - { - /* This house VNUM is already in the list */ - /* Replace the old data */ - *hse = *OLC_HOUSE(d); - hse->vnum = OLC_HOUSE(d)->vnum; - hse->atrium = OLC_HOUSE(d)->atrium; - hse->owner = OLC_HOUSE(d)->owner; - hse->exit_num = OLC_HOUSE(d)->exit_num; - hse->built_on = OLC_HOUSE(d)->built_on; - hse->built_by = OLC_HOUSE(d)->built_by; - hse->mode = OLC_HOUSE(d)->mode; - hse->last_payment = OLC_HOUSE(d)->last_payment; - hse->receptionist = OLC_HOUSE(d)->receptionist; - - /* Copy flags */ - for (i=0; ihouse_flags[i] = OLC_HOUSE(d)->house_flags[i]; - - /* Replace guest list with new one */ - free_house_guests(hse); - for (g=OLC_HOUSE(d)->guests; g; g = next_g) { - next_g = g->next; - add_house_guest(hse, g->id); - } - } else { - /*. House doesn't exist, hafta add it .*/ - if (count_houses() < MAX_HOUSES) { - hse = add_house(OLC_HOUSE(d)); - } else { - send_to_char(d->character, "MAX House limit reached - Unable to save this house!"); - mudlog(NRM, ADMLVL_IMPL, TRUE, "HSEDIT: Max houses limit reached - Unable to save OLC data"); - hse = NULL; - } - } - /* The new house is stored - now to ensure the roomsflags are correct */ - if (hse) { - set_house(OLC_HOUSE(d)); - } -// olc_add_to_save_list(zone_table[OLC_ZNUM(d)].number, OLC_SAVE_HOUSE); -} - -/*------------------------------------------------------------------------*/ -static void hedit_delete_house(struct descriptor_data *d, int house_vnum) -{ - struct house_control_data *hse, *next_hse = NULL; - room_rnum real_atrium, real_house; - - if ((hse = find_house(house_vnum)) == NULL) { - mudlog(BRF, ADMLVL_IMPL, TRUE, "SYSERR: hsedit: Invalid house vnum in hedit_delete_house\r\n"); - cleanup_olc(d, CLEANUP_STRUCTS); - return; - } - - if ((real_atrium = real_room(hse->atrium)) == NOWHERE) - log("SYSERR: House %d had invalid atrium %d!", house_vnum, hse->atrium); - else - REMOVE_BIT_AR(ROOM_FLAGS(real_atrium), ROOM_ATRIUM); - - if ((real_house = real_room(hse->vnum)) == NOWHERE) - log("SYSERR: House %d had invalid vnum %d!", house_vnum, hse->vnum); - else { - REMOVE_BIT_AR(ROOM_FLAGS(real_house), ROOM_HOUSE); - REMOVE_BIT_AR(ROOM_FLAGS(real_house), ROOM_PRIVATE); - REMOVE_BIT_AR(ROOM_FLAGS(real_house), ROOM_HOUSE_CRASH); - } - - House_delete_file(hse->vnum); - - delete_house_control(hse); - - send_to_char(d->character, "House deleted.\r\n"); - House_save_control(); - - /* - * Now, reset the ROOM_ATRIUM flag on all existing houses' atriums, - * just in case the house we just deleted shared an atrium with another - * house. --JE 9/19/94 - */ - for (hse = house_control; hse; hse = next_hse) { - next_hse = hse->next; - if ((real_atrium = real_room(hse->atrium)) != NOWHERE) - SET_BIT_AR(ROOM_FLAGS(real_atrium), ROOM_ATRIUM); - } - - cleanup_olc(d, CLEANUP_ALL); -} - -/************************************************************************** - HSEdit House OLC Menu functions -**************************************************************************/ - -static void hsedit_disp_flags_menu(struct descriptor_data * d) -{ - int counter, columns = 0; - char buf[MAX_STRING_LENGTH]; - struct char_data *ch = d->character; - - clear_screen(d); - for (counter = 0; counter < HOUSE_NUM_FLAGS; counter++) - { - send_to_char(d->character, "%s%2d%s) %-20.20s ", - QBGRN, counter + 1, QNRM, house_bits[counter]); - if(!(++columns % 2)) - send_to_char(d->character, "\r\n"); - } - sprintbitarray(OLC_HOUSE(d)->house_flags, house_bits, HS_ARRAY_MAX, buf); - send_to_char(d->character, - "\r\nHouse flags: %s%s%s\r\n" - "Enter house flags, 0 to quit : ", QCYN, buf, QNRM); - - OLC_MODE(d) = HSEDIT_FLAGS; -} - -/*------------------------------------------------------------------------*/ -static void hsedit_owner_menu(struct descriptor_data *d) -{ - struct house_control_data *house; - struct char_data *ch = d->character; - char ownr[MAX_NAME_LENGTH+2]; - - house = OLC_HOUSE(d); - - sprintf(ownr, "%s", (get_name_by_id(house->owner) == NULL) ? "" : CAP(get_name_by_id(house->owner))); - - send_to_char(ch, - "%s1%s) Owner Name : %s%s%s\r\n" - "%s2%s) Owner ID : %s%ld%s\r\n" - "%sQ%s) Back to main menu\r\n" - "Enter choice : ", - QBCYN, QNRM, QYEL, ownr, QNRM, - QBCYN, QNRM, QYEL, house->owner, QNRM, - QBCYN, QNRM - ); - - OLC_MODE(d) = HSEDIT_OWNER_MENU; -} - -/*------------------------------------------------------------------------*/ -static void hsedit_dir_menu(struct descriptor_data *d) -{ - struct house_control_data *house; - struct char_data *ch = d->character; - int house_rnum, newroom[NUM_OF_DIRS], i; - char dir[20]; - - house = OLC_HOUSE(d); - house_rnum = real_room(house->vnum); - - if (house_rnum == NOWHERE) - { - /* Should never reach here, as hsedit command specified vnum, but just in case */ - send_to_char(ch, - "%sWARNING%s: %sYou cannot set an atium direction before selecting a valid room vnum%s\r\n" - "(Press Enter)\r\n", QBRED, QNRM, QYEL, QNRM ); - - OLC_MODE(d) = HSEDIT_NOVNUM; - } else { - /* Grab exit rooms */ - for(i=0;ito_room; - else - newroom[i] = NOWHERE; - } - - for (i=0; i" : world[(newroom[i])].name), QNRM); - } - send_to_char(ch, - "%sQ%s) Back to main menu\r\n" - "Enter atrium direction : ", QBCYN, QNRM ); - - OLC_MODE(d) = HSEDIT_DIR_MENU; - } -} - -/*------------------------------------------------------------------------*/ -static void hsedit_disp_type_menu(struct descriptor_data *d) -{ - int counter, columns = 0; - struct char_data *ch = d->character; - - clear_screen(d); - for (counter = 0; counter < NUM_HOUSE_TYPES; counter++) { - send_to_char(d->character, "%s%2d%s) %-20.20s ", - QBCYN, counter, QNRM, house_types[counter]); - if(!(++columns % 2)) - send_to_char(d->character, "\r\n"); - } - send_to_char(d->character, "\r\nEnter house type : "); - OLC_MODE(d) = HSEDIT_TYPE; -} - -/*------------------------------------------------------------------------*/ -static void hsedit_disp_guest_menu(struct descriptor_data *d) -{ - char not_set[128]; - struct char_data *ch = d->character; - struct house_control_data *house; - struct guest_data *g, *next_g; - int count=1; - - house = OLC_HOUSE(d); - - sprintf(not_set, "%s%s", QYEL, QNRM); - - for (g=OLC_HOUSE(d)->guests; g; g = next_g) { - next_g = g->next; - send_to_char(ch, - "%s%2d%s) %s%s%s (%sID: %ld%s)\r\n", - QBCYN, count++, QNRM, - QYEL, get_name_by_id(g->id) == NULL ? not_set : get_name_by_id(g->id), QNRM, - QCYN, (g->id) < 1 ? 0 : (g->id), QNRM); - } - send_to_char(ch, - "%sA%s) Add a guest\r\n" - "%sD%s) Delete a guest\r\n" - "%sC%s) Clear guest list\r\n" - "%sQ%s) Back to main menu\r\n" - "Enter selection (A/D/C/Q): ", - QBCYN, QNRM, - QBCYN, QNRM, - QBCYN, QNRM, - QBCYN, QNRM); - - OLC_MODE(d) = HSEDIT_GUEST_MENU; -} - -/*------------------------------------------------------------------------*/ -static char *hsedit_list_guests(struct house_control_data *thishouse, char *guestlist) -{ - int num_printed; - char *temp; - struct guest_data *g, *next_g = NULL; - - if (count_house_guests(thishouse) == 0) { - sprintf(guestlist, ""); - return(guestlist); - } - - for (num_printed = 0, g = thishouse->guests; g; g = next_g) { - next_g = g->next; - /* Avoid . -gg 6/21/98 */ - if ((temp = get_name_by_id(g->id)) == NULL) - continue; - - num_printed++; - sprintf(guestlist, "%s%c%s ", guestlist, UPPER(*temp), temp + 1); - } - - if (num_printed == 0) - sprintf(guestlist, "all dead"); - - return(guestlist); -} - -/*------------------------------------------------------------------------*/ -/* the main menu */ -static void hsedit_disp_menu(struct descriptor_data * d) -{ - char buf[MAX_STRING_LENGTH], buf1[MAX_STRING_LENGTH], mob_name[MAX_INPUT_LENGTH]; - char built_on[128], last_pay[128], buf2[MAX_STRING_LENGTH], atrium[20]; - char *timestr, no_name[128]; - struct char_data *ch = d->character; - struct house_control_data *house; - mob_rnum recep; - - clear_screen(d); - house = OLC_HOUSE(d); - - if (house->built_on) { - timestr = asctime(localtime(&(house->built_on))); - *(timestr + 10) = '\0'; - strlcpy(built_on, timestr, sizeof(built_on)); - } else - strcpy(built_on, "Unknown"); /* strcpy: OK (for 'strlen("Unknown") < 128') */ - - if (house->last_payment) { - timestr = asctime(localtime(&(house->last_payment))); - *(timestr + 10) = '\0'; - strlcpy(last_pay, timestr, sizeof(last_pay)); - } else - strcpy(last_pay, "None"); /* strcpy: OK (for 'strlen("None") < 128') */ - - if (house->atrium == NOWHERE) { - sprintf(atrium, ""); - } else { - sprintf(atrium, "%d", house->atrium); - } - - *buf2 = '\0'; - sprintbitarray((house->house_flags), house_bits, HS_ARRAY_MAX, buf1); - sprintf(no_name, "%s%s", QCYN, QNRM); - recep = real_mobile(house->receptionist); - sprintf(mob_name, "%s", (recep == NOBODY) ? "" : mob_proto[(recep)].player.short_descr); - sprintf(buf, - "%s-- House OLC Editor for tbaMUD --%s\r\n" - "-- House number : %s[%s%d%s]%s House zone: %s[%s%d%s]%s\r\n" - "%s1%s) Owner : %s%ld%s -- %s%s%s\r\n" - "%s2%s) Atrium : %s%s%s\r\n" - "%s3%s) Direction : %s%s%s\r\n" - "%s4%s) House Type : %s%s%s\r\n" - "%s5%s) Built on : %s%s%s\r\n" - "%s6%s) Payment : %s%s%s\r\n" - "%s7%s) Guests : %s%s%s\r\n" - "%s8%s) Flags : %s%s%s\r\n" - "%s9%s) Receptionist: %s[%s%d%s] %s%s\r\n" - "%sX%s) Delete this house\r\n" - "%sQ%s) Quit\r\n" - "Enter choice : ", - - QGRN, QNRM, - QCYN, QYEL, OLC_NUM(d), QCYN, QNRM, - QCYN, QYEL, zone_table[OLC_ZNUM(d)].number, QCYN, QNRM, - QBCYN, QNRM, QYEL, house->owner, QNRM, QCYN, get_name_by_id(house->owner) == NULL ? no_name : get_name_by_id(house->owner), QNRM, - QBCYN, QNRM, QYEL, atrium, QNRM, - QBCYN, QNRM, QCYN, ((house->exit_num >= 0) && (house->exit_num <= DIR_COUNT)) ? dirs[(house->exit_num)] : "", QNRM, - QBCYN, QNRM, QCYN, house_types[(house->mode)], QNRM, - QBCYN, QNRM, QYEL, built_on, QNRM, - QBCYN, QNRM, QYEL, last_pay, QNRM, - QBCYN, QNRM, QYEL, hsedit_list_guests(house, buf2), QNRM, - QBCYN, QNRM, QYEL, buf1, QNRM, - QBCYN, QNRM, QCYN, QYEL, house->receptionist, QCYN, mob_name, QNRM, - QBCYN, QNRM, - QBCYN, QNRM - ); - send_to_char(d->character, "%s", buf); - - OLC_MODE(d) = HSEDIT_MAIN_MENU; -} - -/************************************************************************** - The main loop -**************************************************************************/ -void hsedit_parse(struct descriptor_data * d, char *arg) -{ - int number=0, id=0, i, room_rnum; - char *tmp; - bool found=FALSE; - - mudlog(CMP, ADMLVL_IMPL, FALSE, "(LOG) hsedit_parse: OLC mode %d", OLC_MODE(d)); - - switch (OLC_MODE(d)) { - case HSEDIT_CONFIRM_SAVESTRING: - switch (*arg) { - case 'y': - case 'Y': - hsedit_save_internally(d); - mudlog(CMP, ADMLVL_BUILDER, TRUE, "OLC: %s edits house %d", GET_NAME(d->character), OLC_NUM(d)); - if (CONFIG_OLC_SAVE) { - hsedit_save_to_disk(d); - write_to_output(d, "House saved to disk.\r\n"); - } else - write_to_output(d, "House saved to memory.\r\n"); - /*. Do NOT free strings! just the room structure .*/ - cleanup_olc(d, CLEANUP_STRUCTS); - break; - case 'n': - case 'N': - /* free everything up, including strings etc */ - cleanup_olc(d, CLEANUP_ALL); - break; - default: - send_to_char(d->character, "Invalid choice!\r\n"); - send_to_char(d->character, "Do you wish to save this house internally? : "); - break; - } - return; - - case HSEDIT_MAIN_MENU: - switch (*arg) { - case 'q': - case 'Q': - if (OLC_VAL(d)) - { /*. Something has been modified .*/ - send_to_char(d->character, "Do you wish to save this house internally? : "); - OLC_MODE(d) = HSEDIT_CONFIRM_SAVESTRING; - } else - cleanup_olc(d, CLEANUP_ALL); - return; - case '1': - hsedit_owner_menu(d); - break; - - case '2': - if ((OLC_HOUSE(d)->vnum == NOWHERE) || (real_room(OLC_HOUSE(d)->vnum) == NOWHERE)) - { - send_to_char(d->character, "ERROR: Invalid house VNUM\r\n(Press Enter)\r\n"); - mudlog(NRM, ADMLVL_GRGOD, TRUE, "SYSERR: Invalid house VNUM in hsedit"); - } - else - { - send_to_char(d->character, "Enter atrium room vnum:"); - OLC_MODE(d) = HSEDIT_ATRIUM; - } - break; - - case '3': - if ((OLC_HOUSE(d)->vnum == NOWHERE) || (real_room(OLC_HOUSE(d)->vnum) == NOWHERE)) - { - send_to_char(d->character, "ERROR: Invalid house VNUM\r\n(Press Enter)\r\n"); - mudlog(NRM, ADMLVL_GRGOD, TRUE, "SYSERR: Invalid house VNUM in hsedit"); - } - else - { - hsedit_dir_menu(d); - } - break; - - case '4': - hsedit_disp_type_menu(d); - break; - - case '5': - send_to_char(d->character, "Set build date to now? (Y/N):"); - OLC_MODE(d) = HSEDIT_BUILD_DATE; - break; - - case '6': - send_to_char(d->character, "Set last payment as now? (Y/N): "); - OLC_MODE(d) = HSEDIT_PAYMENT; - break; - - case '7': - hsedit_disp_guest_menu(d); - break; - - case '8': - hsedit_disp_flags_menu(d); - break; - - case '9': - if (OLC_HOUSE(d)->mode == HOUSE_SHOP) - send_to_char(d->character, "Enter the VNUM of the shopkeeper mobile: "); - else - send_to_char(d->character, "Enter the VNUM of the receptionist mobile: "); - OLC_MODE(d) = HSEDIT_RECEP; - break; - - case 'x': - case 'X': - send_to_char(d->character, "Are you sure you want to delete this house? (Y/N) : "); - OLC_MODE(d) = HSEDIT_DELETE; - break; - - default: - send_to_char(d->character, "Invalid choice!\r\n"); - hsedit_disp_menu(d); - break; - } - return; - - case HSEDIT_OWNER_MENU: - switch (*arg) - { - case '1': - send_to_char(d->character, "Enter the name of the owner : "); - OLC_MODE(d) = HSEDIT_OWNER_NAME; - break; - - case '2': - send_to_char(d->character, "Enter the user id of the owner : "); - OLC_MODE(d) = HSEDIT_OWNER_ID; - break; - - case 'Q': - hsedit_disp_menu(d); - break; - } - return; - break; - - case HSEDIT_OWNER_NAME: - if ((id = get_id_by_name(arg)) < 0) - { - send_to_char(d->character, "There is no such player.\r\n"); - hsedit_owner_menu(d); - return; - } - else - { - OLC_HOUSE(d)->owner = id; - } - break; - - case HSEDIT_OWNER_ID: - id = atoi(arg); - if ((tmp = get_name_by_id(id)) == NULL) - { - send_to_char(d->character, "There is no such player.\r\n"); - hsedit_owner_menu(d); - return; - } - else - { - OLC_HOUSE(d)->owner = id; - } - break; - - case HSEDIT_ATRIUM: - number = atoi(arg); - if (number == 0) - { - /* '0' chosen - go back to main menu */ - hsedit_disp_menu(d); - return; - } - room_rnum = real_room(OLC_HOUSE(d)->vnum); - if (real_room(number) == NOWHERE) - { - send_to_char(d->character, "Room VNUM does not exist.\r\nEnter a valid room VNUM for this atrium (0 to exit) : "); - return; - } - else - { - for (i=0; ito_room == real_room(number)) - { - found=TRUE; - id = i; - } - } - } - - if (found == FALSE) - { - send_to_char(d->character, "Atrium MUST be an adjoining room.\r\nEnter a valid room VNUM for this atrium (0 to exit) : "); - return; - } - else - { - OLC_HOUSE(d)->atrium = number; - OLC_HOUSE(d)->exit_num = id; - } - } - break; - - case HSEDIT_DIR_MENU: - - number = atoi(arg)-1; - - if ((*arg == 'q') || (*arg == 'Q') || (number == -1)) - { - hsedit_disp_menu(d); - return; - } - if ((number < 0) || (number > 5)) - { - send_to_char(d->character, "Invalid choice, Please select a direction (1-6, Q to quit) : "); - return; - } - id = real_room(OLC_HOUSE(d)->vnum); - if (!(world[id].dir_option[number])) - { - send_to_char(d->character, "%sYou cannot set the atrium to a room that doesn't exist!%s\r\n", CBRED(d->character, C_NRM), CCNRM(d->character, C_NRM)); - hsedit_dir_menu(d); - return; - } - else if ((world[id].dir_option[number]->to_room) == NOWHERE) - { - send_to_char(d->character, "%sYou cannot set the atrium to nowhere!%s\r\n", CBRED(d->character, C_NRM), CCNRM(d->character, C_NRM)); - hsedit_dir_menu(d); - return; - } - else - { - OLC_HOUSE(d)->exit_num = number; - - room_rnum = world[id].dir_option[number]->to_room; - OLC_HOUSE(d)->atrium = world[room_rnum].number; - } - break; - - case HSEDIT_NOVNUM: - /* Just an 'enter' keypress - don't do anything */ - break; - - case HSEDIT_BUILD_DATE: - switch (*arg) - { - case 'y': - case 'Y': - OLC_HOUSE(d)->built_on = time(0); - break; - case 'n': - case 'N': - send_to_char(d->character, "Build Date not changed\r\n"); - hsedit_disp_menu(d); - return; - break; - } - break; - - case HSEDIT_DELETE: - switch (*arg) - { - case 'y': - case 'Y': - hedit_delete_house(d, OLC_HOUSE(d)->vnum); - break; - case 'n': - case 'N': - send_to_char(d->character, "House not deleted!\r\n"); - hsedit_disp_menu(d); - return; - break; - } - break; - - case HSEDIT_BUILDER: - if ((id = get_id_by_name(arg)) < 0) - { - send_to_char(d->character, "No such player.\r\n"); - return; - } - else { - OLC_HOUSE(d)->built_by = id; - send_to_char(d->character, "Builder changed.\r\n"); - } - break; - - case HSEDIT_PAYMENT: - switch (*arg) - { - case 'y': - case 'Y': - OLC_HOUSE(d)->last_payment = time(0); - break; - case 'n': - case 'N': - send_to_char(d->character, "Last Payment Date not changed\r\n"); - hsedit_disp_menu(d); - return; - break; - } - break; - - case HSEDIT_GUEST_MENU: - switch (*arg) - { - case 'a': - case 'A': - if (count_house_guests(OLC_HOUSE(d)) > (MAX_GUESTS-1)) - { - send_to_char(d->character, "%sGuest List Full! - delete some before adding more%s\r\nEnter selection (A/D/C/Q) : ", CBRED(d->character, C_NRM), CCNRM(d->character, C_NRM)); - } - else - { - send_to_char(d->character, "Name of guest to add: "); - OLC_MODE(d) = HSEDIT_GUEST_ADD; - } - break; - - case 'd': - case 'D': - if (count_house_guests(OLC_HOUSE(d)) < 1) - { - send_to_char(d->character, "%sGuest List Empty! - add a guest before trying to delete one%s\r\nEnter selection (A/D/C/Q) : ", CBRED(d->character, C_NRM), CCNRM(d->character, C_NRM)); - } - else - { - send_to_char(d->character, "Name of guest to delete : "); - OLC_MODE(d) = HSEDIT_GUEST_DELETE; - } - break; - - case 'c': - case 'C': - send_to_char(d->character, "Clear guest list? (Y/N) : "); - OLC_MODE(d) = HSEDIT_GUEST_CLEAR; - break; - - case 'q': - case 'Q': - hsedit_disp_menu(d); - break; - - default: - send_to_char(d->character, "Invalid choice!\r\n\r\n"); - hsedit_disp_guest_menu(d); - break; - } - return; - break; - - case HSEDIT_GUEST_ADD: - if ((id = get_id_by_name(arg)) < 0) - { - send_to_char(d->character, "No such player.\r\n"); - hsedit_disp_guest_menu(d); - return; - } - else if (id == OLC_HOUSE(d)->owner) - { - send_to_char(d->character, "House owner should not be in the guest list!\r\n"); - hsedit_disp_guest_menu(d); - return; - } - else { - struct guest_data *g, *next_g = NULL; - for (g = OLC_HOUSE(d)->guests; g; g = next_g) - { - next_g = g->next; - if (g->id == id) - { - send_to_char(d->character, "That player is already in the guest list!.\r\n"); - hsedit_disp_guest_menu(d); - return; - } - } - add_house_guest(OLC_HOUSE(d), id); - send_to_char(d->character, "Guest added.\r\n"); - OLC_VAL(d) = 1; - hsedit_disp_guest_menu(d); - return; - } - break; - - case HSEDIT_GUEST_DELETE: - if ((id = get_id_by_name(arg)) < 0) - { - send_to_char(d->character, "No such player.\r\n"); - hsedit_disp_guest_menu(d); - return; - } - else { - struct guest_data *g, *next_g = NULL; - for (g = OLC_HOUSE(d)->guests; g; g = next_g) - { - next_g = g->next; - if (g->id == id) - found = TRUE; - } - if (!found) { - send_to_char(d->character, "That player isn't in the guest list!\r\n"); - hsedit_disp_guest_menu(d); - return; - } - remove_house_guest(OLC_HOUSE(d), id); - send_to_char(d->character, "Guest deleted.\r\n"); - OLC_VAL(d) = 1; - hsedit_disp_guest_menu(d); - return; - } - break; - - case HSEDIT_GUEST_CLEAR: - switch (*arg) - { - case 'n': - case 'N': - send_to_char(d->character, "Invalid choice!"); - return; - - case 'y': - case 'Y': - free_house_guests(OLC_HOUSE(d)); - send_to_char(d->character, "Guest List Cleared!"); - OLC_VAL(d) = 1; - hsedit_disp_guest_menu(d); - break; - - default: - send_to_char(d->character, "Invalid choice!\r\nClear Guest List? (Y/N) : "); - return; - } - break; - - case HSEDIT_TYPE: - number = atoi(arg); - if (number < 0 || number >= NUM_HOUSE_TYPES) { - send_to_char(d->character, "Invalid choice!"); - hsedit_disp_type_menu(d); - return; - } else - OLC_HOUSE(d)->mode = number; - break; - - case HSEDIT_FLAGS: - number = atoi(arg); - if ((number < 0) || (number > HOUSE_NUM_FLAGS)) { - send_to_char(d->character, "That's not a valid choice!\r\n"); - hsedit_disp_flags_menu(d); - } else { - if (number == 0) - break; - else { - /* toggle bits */ - if (IS_SET_AR(OLC_HOUSE(d)->house_flags, (number - 1))) - REMOVE_BIT_AR(OLC_HOUSE(d)->house_flags, (number - 1)); - else - SET_BIT_AR(OLC_HOUSE(d)->house_flags, (number - 1)); - hsedit_disp_flags_menu(d); - } - } - return; - - case HSEDIT_RECEP: - number = atoi(arg); - if (number == 0) - { - /* '0' chosen - go back to main menu */ - hsedit_disp_menu(d); - return; - } - if (real_mobile(number) == NOBODY) - { - send_to_char(d->character, "Mob VNUM does not exist.\r\nEnter a valid mobile VNUM (0 to exit) : "); - return; - } - OLC_HOUSE(d)->receptionist = number; - break; - - default: - /* we should never get here */ - mudlog(BRF,ADMLVL_BUILDER,TRUE,"SYSERR: Reached default case in parse_hsedit"); - break; - } - /*. If we get this far, something has been changed .*/ - OLC_VAL(d) = 1; - hsedit_disp_menu(d); -} - -void hsedit_string_cleanup(struct descriptor_data *d, int terminator) -{ - switch (OLC_MODE(d)) { - /* There are no strings to be edited in houses - if there are any added later, add them here */ - } -} - -ACMD(do_oasis_hsedit) -{ - int number = NOWHERE, save = 0; - struct descriptor_data *d; - char *buf3; - char buf1[MAX_STRING_LENGTH]; - char buf2[MAX_STRING_LENGTH]; - struct house_control_data *hse; - - -/****************************************************************************/ -/** Parse any arguments. **/ -/****************************************************************************/ - buf3 = two_arguments(argument, buf1, buf2); - - -/****************************************************************************/ -/** If there aren't any arguments...grab the number of the current room... **/ -/****************************************************************************/ - if (!*buf1) { - number = GET_ROOM_VNUM(IN_ROOM(ch)); - } else if (!isdigit(*buf1)) { - if (str_cmp("save", buf1) != 0) { - send_to_char(ch, "Yikes! Stop that, someone will get hurt!\r\n"); - return; - } - save = TRUE; - - if (is_number(buf2)) - number = atoi(buf2); - else if (GET_OLC_ZONE(ch) > 0) { - zone_rnum zlok; - - if ((zlok = real_zone(GET_OLC_ZONE(ch))) == NOWHERE) - number = NOWHERE; - else - number = genolc_zone_bottom(zlok); - } - - if (number == NOWHERE) { - send_to_char(ch, "Save which zone?\r\n"); - return; - } - } - - -/****************************************************************************/ -/** If a numeric argument was given, get it. **/ -/****************************************************************************/ - if (number == NOWHERE) - number = atoi(buf1); - - -/****************************************************************************/ -/** Check that whatever it is isn't already being edited. **/ -/****************************************************************************/ - for (d = descriptor_list; d; d = d->next) { - if (STATE(d) == CON_HSEDIT) { - if (d->olc && OLC_NUM(d) == number) { - send_to_char(ch, "That house is currently being edited by %s.\r\n", - PERS(d->character, ch)); - return; - } - } - } - - -/****************************************************************************/ -/** Point d to the builder's descriptor (for easier typing later). **/ -/****************************************************************************/ - d = ch->desc; - - -/****************************************************************************/ -/** Give the descriptor an OLC structure. **/ -/****************************************************************************/ - if (d->olc) { - mudlog(BRF, ADMLVL_IMMORT, TRUE, "SYSERR: do_oasis_hsedit: Player already had olc structure."); - free(d->olc); - } - - CREATE(d->olc, struct oasis_olc_data, 1); - - -/****************************************************************************/ -/** Find the zone. **/ -/****************************************************************************/ - OLC_ZNUM(d) = save ? real_zone(number) : real_zone_by_thing(number); - if (OLC_ZNUM(d) == NOWHERE) { - send_to_char(ch, "Sorry, there is no zone for that number!\r\n"); - - -/**************************************************************************/ -/** Free the descriptor's OLC structure. **/ -/**************************************************************************/ - free(d->olc); - d->olc = NULL; - return; - } - - -/****************************************************************************/ -/** Everyone but IMPLs can only edit zones they have been assigned. **/ -/****************************************************************************/ - if (!can_edit_zone(ch, OLC_ZNUM(d))) { - send_to_char(ch, " You do not have permission to edit zone %d. Try zone %d.\r\n", zone_table[OLC_ZNUM(d)].number, GET_OLC_ZONE(ch));mudlog(BRF, ADMLVL_IMPL, TRUE, "OLC: %s tried to edit zone %d allowed zone %d", - GET_NAME(ch), zone_table[OLC_ZNUM(d)].number, GET_OLC_ZONE(ch)); - - -/**************************************************************************/ -/** Free the descriptor's OLC structure. **/ -/**************************************************************************/ - free(d->olc); - d->olc = NULL; - return; - } - - -/****************************************************************************/ -/** If we need to save, save the houses. **/ -/****************************************************************************/ - if (save) { - send_to_char(ch, "Saving all houses in zone %d.\r\n", - zone_table[OLC_ZNUM(d)].number); - mudlog(CMP, MAX(ADMLVL_BUILDER, GET_INVIS_LEV(ch)), TRUE, - "OLC: %s saves house info for zone %d.", GET_NAME(ch), - zone_table[OLC_ZNUM(d)].number); - - -/**************************************************************************/ -/** Save all the houses in memory before we start. **/ -/**************************************************************************/ - House_save_control(); - - -/**************************************************************************/ -/** Free the descriptor's OLC structure. **/ -/**************************************************************************/ - free(d->olc); - d->olc = NULL; - return; - } - - OLC_NUM(d) = number; - - -/****************************************************************************/ -/** If this is a new house, setup a new house, otherwise setup the **/ -/** existing house. **/ -/****************************************************************************/ - if ((hse = find_house(number)) == NULL) - { - /* Do a quick check to ensure there is room for more */ - if (count_houses() >= MAX_HOUSES) - { - send_to_char(ch, "MAX houses limit reached (%d) - Unable to create more.\r\n", MAX_HOUSES); - mudlog(NRM, ADMLVL_IMPL, TRUE, "HSEDIT: MAX houses limit reached (%d)\r\n", MAX_HOUSES); - return; - } - else - { - hsedit_setup_new(d); - } - } - else - { - hsedit_setup_existing(d, hse); - } - - STATE(d) = CON_HSEDIT; - - -/****************************************************************************/ - /** Send the OLC message to the players in the same room as the -builder. **/ - -/****************************************************************************/ - act("$n starts using OLC.", TRUE, d->character, 0, 0, TO_ROOM); - SET_BIT_AR(PLR_FLAGS(ch), PLR_WRITING); - - -/****************************************************************************/ -/** Log the OLC message. **/ -/****************************************************************************/ - mudlog(CMP, ADMLVL_IMMORT, TRUE, "OLC: (hsedit) %s starts editing zone %d allowed zone %d", - GET_NAME(ch), zone_table[OLC_ZNUM(d)].number, GET_OLC_ZONE(ch)); -} diff --git a/src/mailedit.c b/src/mailedit.c deleted file mode 100755 index d08b9f0..0000000 --- a/src/mailedit.c +++ /dev/null @@ -1,898 +0,0 @@ -/************************************************************************** -* File: mailedit.c Part of tbaMUD * -* Usage: Oasis OLC - Mudmail. * -* * -* Copyright 1996 Harvey Gilpin. 1997-2001 George Greer. 2010 Stefan Cole. * -**************************************************************************/ - -#include "conf.h" -#include "sysdep.h" -#include "structs.h" -#include "utils.h" -#include "interpreter.h" -#include "db.h" -#include "comm.h" -#include "handler.h" -#include "modify.h" -#include "mail.h" -#include "oasis.h" -#include "improved-edit.h" - -/* External functions */ -void list_obj_to_char(struct obj_data *list, struct char_data *ch, int mode, int show); - -/* external global variables */ -extern const struct mail_group mail_groups[]; - -/* utility functions */ -ACMD(do_mailedit) -{ - struct descriptor_data *d; - - d = ch->desc; - - /* Give descriptor an OLC structure. */ - if (d->olc) { - mudlog(BRF, ADMLVL_IMMORT, TRUE, - "SYSERR: do_oasis_medit: Player already had olc structure."); - free(d->olc); - } - - CREATE(d->olc, struct oasis_olc_data, 1); - - OLC_ZNUM(d) = NOWHERE; - OLC_NUM(d) = 0; - - mailedit_setup(d); - mailedit_disp_menu(d); - - /* Display messages to the players in the same room as the - player and also log it. */ - act("$n starts editing mail.", TRUE, ch, 0, 0, TO_ROOM); - SET_BIT_AR(PLR_FLAGS(ch), PLR_MAILING); - SET_BIT_AR(PLR_FLAGS(ch), PLR_WRITING); - STATE(d) = CON_MAILEDIT; - - mudlog(CMP, ADMLVL_IMMORT, TRUE,"MAIL: %s starts editing mudmail in room %d", - GET_NAME(ch), world[IN_ROOM(d->character)].number); -} - -/* No 'new' or 'existing' to edit - just set up the mudmail structures */ -void mailedit_setup(struct descriptor_data *d) -{ - struct mail_edit_data *mail_info; - - /* Allocate a scratch mudmails structure. */ - CREATE(mail_info, struct mail_edit_data, 1); - - mail_info->mail = create_mail(); - mail_info->recipients = NULL; - - OLC_MAIL(d) = mail_info; - - /* Has changed flag. (It hasn't so far, we just made it.) */ - OLC_VAL(d) = FALSE; - -} - -/* clear up memory used by the mudmail structures */ -void mailedit_cleanup(struct descriptor_data *d) -{ - struct mail_edit_data *mail_info; - struct mail_data *single_mail; - struct obj_data *obj; - - mail_info = OLC_MAIL(d); - single_mail = mail_info->mail; - - if (single_mail) { - /* Any leftover attachments should be passed back to the owner */ - while (single_mail->attachment) { - obj = single_mail->attachment; - obj_from_mail(obj); - obj_to_char(obj, d->character); - } - /* Any leftover gold is returned too */ - if ((single_mail->coins > 0) && !IS_SET_AR(single_mail->mail_flags, MAIL_COD)) { - GET_GOLD(d->character) += single_mail->coins; - single_mail->coins = 0; - } - if (single_mail->subject) free(single_mail->subject); - if (single_mail->body) free(single_mail->body); - single_mail = NULL; - free (mail_info->mail); - } - clear_recipients(mail_info); - mail_info = NULL; - free(OLC_MAIL(d)); - OLC_MAIL(d) = NULL; -} - -/* Menu functions */ -/* The 'main menu' - displays inbox, with options */ -void mailedit_disp_menu(struct descriptor_data *d) -{ - get_char_colors(d->character); - clear_screen(d); - - write_to_output(d, "tbaMUD Mail Editor\r\n"); - show_inbox_to_char(d->character); - if (CONFIG_DRAFTS_ALLOWED) - write_to_output(d, "%s(%sC%s)%s Create %s(%sV%s)%s View %s(%sR%s)%s Reply %s(%sF%s)%s Forward \r\n" - "%s(%sE%s)%s Receive %s(%sD%s)%s Delete %s(%sO%s)%s Outbox %s(%sQ%s)%s Quit \r\n", - cyn, yel, cyn, nrm, - cyn, yel, cyn, nrm, - cyn, yel, cyn, nrm, - cyn, yel, cyn, nrm, - cyn, yel, cyn, nrm, - cyn, yel, cyn, nrm, - cyn, yel, cyn, nrm, - cyn, yel, cyn, nrm ); - else - write_to_output(d, "%s(%sC%s)%s Create %s(%sV%s)%s View %s(%sR%s)%s Reply %s(%sF%s)%s Forward \r\n" - "%s(%sE%s)%s Receive %s(%sD%s)%s Delete %s(%sQ%s)%s Quit \r\n", - cyn, yel, cyn, nrm, - cyn, yel, cyn, nrm, - cyn, yel, cyn, nrm, - cyn, yel, cyn, nrm, - cyn, yel, cyn, nrm, - cyn, yel, cyn, nrm, - cyn, yel, cyn, nrm ); - write_to_output(d, "Enter Selection: "); - - OLC_MODE(d) = MAILEDIT_INBOX; -} - -void mailedit_disp_outbox(struct descriptor_data *d) -{ - get_char_colors(d->character); - clear_screen(d); - - write_to_output(d, "tbaMUD Mail Editor\r\n"); - show_outbox_to_char(d->character); - write_to_output(d, "%s(%sE%s)%s Edit %s(%sV%s)%s View %s(%sD%s)%s Delete %s(%sI%s)%s Inbox %s(%sQ%s)%s Quit\r\n", - cyn, yel, cyn, nrm, - cyn, yel, cyn, nrm, - cyn, yel, cyn, nrm, - cyn, yel, cyn, nrm, - cyn, yel, cyn, nrm ); - write_to_output(d, "Enter Selection: "); - - OLC_MODE(d) = MAILEDIT_OUTBOX; -} - -void mailedit_disp_mail(struct descriptor_data *d) -{ - char att[15], gold[25]; - int gold_amt; - - get_char_colors(d->character); - clear_screen(d); - bool can_attach = TRUE; - - if (count_recipients(OLC_MAIL(d)) > 1) can_attach = FALSE; - - if (attachment_count(OLC_MAIL(d)->mail) > 0) - sprintf(att, "%s%d object%s", can_attach ? "@r" : "@y", attachment_count(OLC_MAIL(d)->mail), attachment_count(OLC_MAIL(d)->mail) > 1 ? "s" : ""); - else - sprintf(att, ""); - - if (MAILEDIT_GOLD > 0) { - sprintf(gold, "%s%s coins", can_attach ? "@r" : "@y", add_commas(MAILEDIT_GOLD)); - } else { - sprintf(gold, ""); - } - - gold_amt = CONFIG_STAMP_COST + (attachment_count(OLC_MAIL(d)->mail) * CONFIG_OBJECT_COST); - - write_to_output(d, "tbaMUD Mail Editor\r\n"); - if (GET_LEVEL(d->character) < CONFIG_FREE_MAIL_LEVEL) { - write_to_output(d, "Current cost to send this mail: %s%d%s coins\r\n", yel, gold_amt, nrm); - } else { - write_to_output(d, "Current cost to send this mail: %sFree!%s\r\n", yel, nrm); - } - write_to_output(d, "%sA%s)%s Recipients : %s%s\r\n" - "%sB%s)%s Subject : %s%s\r\n" - "%sC%s)%s Urgency : %s%s\r\n" - "%sD%s)%s Attachments : %s%s\r\n" - "%sE%s)%s Gold : %s%s\r\n" - "%sF%s)%s Gold Mode : %s%s\r\n" - "%sG%s)%s Body Text : \r\n%s%s\r\n" - "%sS%s)%s Send Mail\r\n" - "%sX%s)%s Clear Mail data\r\n" - "%sQ%s)%s Quit without sending\r\n", - yel, cyn, nrm, yel, OLC_MAIL(d)->recipients ? recipient_list(OLC_MAIL(d)) : "", - yel, cyn, nrm, yel, MAILEDIT_SUBJECT == NULL ? "- No Subject -" : MAILEDIT_SUBJECT, - yel, cyn, nrm, yel, MAILEDIT_URGENT ? "@RUrgent!" : "Normal", - yel, cyn, CONFIG_CAN_MAIL_OBJ ? nrm : gry, CONFIG_CAN_MAIL_OBJ ? yel : gry, att, - yel, cyn, CONFIG_CAN_MAIL_GOLD ? nrm : gry, CONFIG_CAN_MAIL_GOLD ? yel : gry, gold, - yel, cyn, nrm, yel, MAILEDIT_COD ? "Cash-on-Delivery (COD)" : "Send Gold", - yel, cyn, nrm, yel, MAILEDIT_BODYTEXT == NULL ? "No Text" : MAILEDIT_BODYTEXT, - yel, cyn, nrm, - yel, cyn, nrm, - yel, cyn, nrm); - - write_to_output(d, "Enter Selection: "); - - OLC_MODE(d) = MAILEDIT_MAILEDIT; -} - -void mailedit_disp_recipients(struct descriptor_data *d) -{ - get_char_colors(d->character); - clear_screen(d); - write_to_output(d, "tbaMUD Mail Recipient Editor\r\n"); - write_to_output(d, "Current Recipient List:\r\n"); - write_to_output(d, "%s%s%s\r\n", yel, OLC_MAIL(d)->recipients ? recipient_list(OLC_MAIL(d)) : "", nrm); - write_to_output(d, "%sA%s)%s Add a Recipient\r\n" - "%sB%s)%s Delete a Recipient\r\n" - "%sC%s)%s Clear all Recipients\r\n" - "%sQ%s)%s Quit (back to Editor)\r\n", - yel, cyn, nrm, - yel, cyn, nrm, - yel, cyn, nrm, - yel, cyn, nrm); - write_to_output(d, "Enter Selection: "); - - OLC_MODE(d) = MAILEDIT_RECIP_MENU; -} - -void mailedit_disp_attachments(struct descriptor_data *d) -{ - get_char_colors(d->character); - clear_screen(d); - write_to_output(d, "tbaMUD Mail Attachment Editor\r\n"); - write_to_output(d, "Current Attached Objects:\r\n"); - list_obj_to_char((OLC_MAIL(d)->mail)->attachment, d->character, 1, TRUE); - write_to_output(d, "\r\n"); - write_to_output(d, "%sA%s)%s Add an Object\r\n" - "%sB%s)%s Remove an Object\r\n" - "%sC%s)%s Remove all Objects\r\n" - "%sQ%s)%s Quit (back to Editor)\r\n", - yel, cyn, nrm, - yel, cyn, nrm, - yel, cyn, nrm, - yel, cyn, nrm); - write_to_output(d, "Enter Selection: "); - - OLC_MODE(d) = MAILEDIT_ATTACH_MENU; -} - -void mailedit_parse(struct descriptor_data *d, char *arg) -{ - int i, num; - long mail_id, diff, gold; - struct mail_data *ml; - struct obj_data *obj; - char *oldtext = NULL; - - switch (OLC_MODE(d)) { - case MAILEDIT_ASK_DRAFT: - switch (*arg) { - case 'y': - case 'Y': - /* Save the mail as a draft. */ - save_as_draft(d->character, OLC_MAIL(d)->mail); - clear_mail_data(OLC_MAIL(d)->mail); - mailedit_disp_outbox(d); - return; - case 'n': - case 'N': - clear_mail_data(OLC_MAIL(d)->mail); - mailedit_disp_menu(d); - return; - default: - write_to_output(d, "Invalid choice!\r\n"); - write_to_output(d, "Do you wish to save this mail as a draft? (Y/N): "); - return; - } - break; - - case MAILEDIT_ASK_QUIT: - switch (*arg) { - case 'y': - case 'Y': - clear_mail_data(OLC_MAIL(d)->mail); - mailedit_disp_outbox(d); - return; - case 'n': - case 'N': - mailedit_disp_mail(d); - return; - default: - write_to_output(d, "Invalid choice!\r\n"); - write_to_output(d, "Do you wish to quit and lose your changes? (Y/N): "); - return; - } - break; - - case MAILEDIT_INBOX: - switch (*arg) { - case 'q': - case 'Q': - if (count_deleted(d->character) > 0) { - - write_to_output(d, "You have mail marked for deletion.\r\n"); - write_to_output(d, "Do you wish to purge all marked mails now? (Y/N)\r\n"); - OLC_MODE(d) = MAILEDIT_PURGE_N_QUIT; - REMOVE_BIT_AR(PLR_FLAGS(d->character), PLR_MAILING); - return; - } - REMOVE_BIT_AR(PLR_FLAGS(d->character), PLR_MAILING); - cleanup_olc(d, CLEANUP_ALL); - return; - case 'c': - case 'C': - if ((GET_LEVEL(d->character) < CONFIG_FREE_MAIL_LEVEL) && (GET_GOLD(d->character) < CONFIG_STAMP_COST)) { - write_to_output(d, "Mails cost %d coins, and you can't afford that!\r\n", CONFIG_STAMP_COST); - mailedit_disp_menu(d); - return; - } - clear_mail_data(OLC_MAIL(d)->mail); - mailedit_disp_mail(d); - break; - case 'v': - case 'V': - write_to_output(d, "Which mail do you wish to view? : "); - OLC_MODE(d) = MAILEDIT_VIEW; - break; - case 'r': - case 'R': - if ((GET_LEVEL(d->character) < CONFIG_FREE_MAIL_LEVEL) && (GET_GOLD(d->character) < CONFIG_STAMP_COST)) { - write_to_output(d, "Mails cost %d coins, and you can't afford that!\r\n", CONFIG_STAMP_COST); - mailedit_disp_menu(d); - return; - } - write_to_output(d, "Which mail do you wish to reply to? : "); - OLC_MODE(d) = MAILEDIT_REPLY; - break; - case 'f': - case 'F': - if ((GET_LEVEL(d->character) < CONFIG_FREE_MAIL_LEVEL) && (GET_GOLD(d->character) < CONFIG_STAMP_COST)) { - write_to_output(d, "Mails cost %d coins, and you can't afford that!\r\n", CONFIG_STAMP_COST); - mailedit_disp_menu(d); - return; - } - write_to_output(d, "Which mail do you wish to forward? : "); - OLC_MODE(d) = MAILEDIT_FORWARD; - break; - case 'd': - case 'D': - write_to_output(d, "Which mail do you wish to delete? : "); - OLC_MODE(d) = MAILEDIT_DELETE; - break; - case 'e': - case 'E': - write_to_output(d, "Which mail do you wish to receive? : "); - OLC_MODE(d) = MAILEDIT_RECEIVE; - break; - case 'o': - case 'O': - mailedit_disp_outbox(d); - break; - default: - write_to_output(d, "Invalid Choice!\r\nEnter Selection : "); - break; - } - break; - - case MAILEDIT_OUTBOX: - switch (*arg) { - case 'q': - case 'Q': - cleanup_olc(d, CLEANUP_ALL); - return; - case 'e': - case 'E': - if ((GET_LEVEL(d->character) < CONFIG_FREE_MAIL_LEVEL) && (GET_GOLD(d->character) < CONFIG_STAMP_COST)) { - write_to_output(d, "Mails cost %d coins, and you can't afford that!\r\n", CONFIG_STAMP_COST); - mailedit_disp_menu(d); - return; - } - write_to_output(d, "Which mail do you wish to edit? : "); - OLC_MODE(d) = MAILEDIT_OUTEDIT; - break; - case 'v': - case 'V': - write_to_output(d, "Which mail do you wish to view? : "); - OLC_MODE(d) = MAILEDIT_OUTVIEW; - break; - case 'i': - case 'I': - mailedit_disp_menu(d); - break; - case 'd': - case 'D': - write_to_output(d, "Which mail do you wish to delete? : "); - OLC_MODE(d) = MAILEDIT_OUTDELETE; - break; - default: - write_to_output(d, "Invalid Choice!\r\nEnter Selection : "); - break; - } - break; - - case MAILEDIT_VIEW: - num = atoi(arg); - if (num == 0) { - mailedit_disp_menu(d); - return; - } - mail_id = get_id_by_inbox_num(d->character, num); - if (mail_id == NO_MAIL) { - write_to_output(d, "Invalid mail number.\r\n"); - mailedit_disp_menu(d); - return; - } - mail_view(d->character, mail_id); - write_to_output(d, "-- Press Enter to Continue! --\r\n"); - OLC_MODE(d) = MAILEDIT_BACK_TO_MENU; - break; - - case MAILEDIT_REPLY: - num = atoi(arg); - if (num == 0) { - mailedit_disp_menu(d); - return; - } - mail_id = get_id_by_inbox_num(d->character, num); - if (mail_id == NO_MAIL) { - write_to_output(d, "Invalid mail number.\r\n"); - mailedit_disp_menu(d); - return; - } - if ((ml = create_mail()) != NULL) { - load_mail(mail_id, ml); - copy_mail(OLC_MAIL(d)->mail, ml, MAIL_COPY_REPLY); - for (i=0; imail_flags[i] = 0; - (OLC_MAIL(d)->mail)->mail_id = NO_MAIL; - (OLC_MAIL(d)->mail)->sender = GET_ID(d->character); - add_recipient(OLC_MAIL(d), ml->sender); - mailedit_disp_mail(d); - } - break; - - case MAILEDIT_FORWARD: - num = atoi(arg); - if (num == 0) { - mailedit_disp_menu(d); - return; - } - mail_id = get_id_by_inbox_num(d->character, num); - if (mail_id == NO_MAIL) { - write_to_output(d, "Invalid mail number.\r\n"); - mailedit_disp_menu(d); - return; - } - if ((ml = create_mail()) != NULL) { - load_mail(mail_id, ml); - copy_mail(OLC_MAIL(d)->mail, ml, MAIL_COPY_FORWARD); - for (i=0; imail_flags[i] = 0; - (OLC_MAIL(d)->mail)->mail_id = NO_MAIL; - (OLC_MAIL(d)->mail)->sender = GET_ID(d->character); - mailedit_disp_mail(d); - } - break; - - case MAILEDIT_DELETE: - num = atoi(arg); - if (num == 0) { - mailedit_disp_menu(d); - return; - } - mail_id = get_id_by_inbox_num(d->character, num); - if (mail_id == NO_MAIL) { - write_to_output(d, "Invalid mail number.\r\n"); - mailedit_disp_menu(d); - return; - } - mail_delmark(d->character, mail_id); - mailedit_disp_mail(d); - break; - - case MAILEDIT_RECEIVE: - num = atoi(arg); - if (num == 0) { - mailedit_disp_menu(d); - return; - } - mail_id = get_id_by_inbox_num(d->character, num); - if (mail_id == NO_MAIL) { - write_to_output(d, "Invalid mail number.\r\n"); - mailedit_disp_menu(d); - return; - } - if (mail_receive(d->character, mail_id)) - write_to_output(d, "Mail received into your inventory.\r\n"); - mailedit_disp_menu(d); - break; - - case MAILEDIT_OUTEDIT: - num = atoi(arg); - if (num == 0) { - mailedit_disp_outbox(d); - return; - } - mail_id = get_id_by_outbox_num(d->character, num); - if (mail_id == NO_MAIL) { - write_to_output(d, "Invalid mail number.\r\n"); - mailedit_disp_outbox(d); - return; - } - if ((ml = create_mail()) != NULL) { - load_mail(mail_id, ml); - copy_mail(OLC_MAIL(d)->mail, ml, MAIL_COPY_NORMAL); - (OLC_MAIL(d)->mail)->sender = GET_ID(d->character); - mailedit_disp_mail(d); - } - break; - - case MAILEDIT_OUTVIEW: - num = atoi(arg); - if (num == 0) { - mailedit_disp_menu(d); - return; - } - mail_id = get_id_by_outbox_num(d->character, num); - if (mail_id == NO_MAIL) { - write_to_output(d, "Invalid mail number.\r\n"); - mailedit_disp_outbox(d); - return; - } - mail_view(d->character, mail_id); - write_to_output(d, "-- Press Enter to Continue! --\r\n"); - OLC_MODE(d) = MAILEDIT_BACK_TO_MENU; - break; - - case MAILEDIT_OUTDELETE: - num = atoi(arg); - if (num == 0) { - mailedit_disp_menu(d); - return; - } - mail_id = get_id_by_outbox_num(d->character, num); - if (mail_id == NO_MAIL) { - write_to_output(d, "Invalid mail number.\r\n"); - mailedit_disp_outbox(d); - return; - } - mail_delmark(d->character, mail_id); - mailedit_disp_mail(d); - break; - - case MAILEDIT_BACK_TO_MENU: - mailedit_disp_menu(d); - break; - - case MAILEDIT_RECIP_MENU: - switch (*arg) { - case 'a': - case 'A': - /* Mail with attachments can only have one recipient */ - if (count_recipients(OLC_MAIL(d)) > 0) { - if (((OLC_MAIL(d)->mail)->attachment) || ((OLC_MAIL(d)->mail)->coins > 0 && !IS_SET_AR((OLC_MAIL(d)->mail)->mail_flags, MAIL_COD))) { - write_to_output(d, "Sorry, mail with attachments cannot be sent to more than one person!\r\n"); - mailedit_disp_recipients(d); - return; - } - } - write_to_output(d, "Enter a player name to add: "); - OLC_MODE(d) = MAILEDIT_ADD_RECIP; - break; - case 'b': - case 'B': - write_to_output(d, "Enter a player name to remove: "); - OLC_MODE(d) = MAILEDIT_DEL_RECIP; - break; - case 'c': - case 'C': - write_to_output(d, "Are you sure you wish to remove ALL recipients? (Y/N): "); - OLC_MODE(d) = MAILEDIT_CLR_RECIP; - break; - case 'q': - case 'Q': - mailedit_disp_mail(d); - break; - } - break; - - case MAILEDIT_ADD_RECIP: - /* check 'special cases' (mail groups) */ - if ((mail_id = get_mail_group_by_name(arg)) == MAIL_TO_NOBODY) { - if ((mail_id = get_id_by_name(arg)) < 0 || !mail_recip_ok(arg)) { - write_to_output(d, "No one by that name is registered here!\r\n"); - mailedit_disp_recipients(d); - return; - } - } - if (mail_id == MAIL_TO_ALL && GET_LEVEL(d->character) < CONFIG_MIN_SEND_TO_ALL) { - write_to_output(d, "Sorry, you don't have sufficient access to send to 'all'\r\n"); - mailedit_disp_recipients(d); - return; - } - add_recipient(OLC_MAIL(d), mail_id); - mailedit_disp_recipients(d); - break; - - case MAILEDIT_DEL_RECIP: - if ((mail_id = get_id_by_name(arg)) < 0 || !mail_recip_ok(arg)) { - write_to_output(d, "No one by that name is registered here!\r\n"); - mailedit_disp_recipients(d); - return; - } - remove_recipient(OLC_MAIL(d), mail_id); - mailedit_disp_recipients(d); - break; - - case MAILEDIT_CLR_RECIP: - switch (*arg) { - case 'y': - case 'Y': - clear_recipients(OLC_MAIL(d)); - mailedit_disp_recipients(d); - break; - case 'n': - case 'N': - mailedit_disp_recipients(d); - break; - default : write_to_output(d, "Invalid choice!\r\nAre you sure you wish to remove ALL recipients? (Y/N): "); - break; - } - break; - - case MAILEDIT_ATTACH_MENU: - switch (*arg) { - case 'a': - case 'A': - /* Mail with attachments can only have one recipient */ - if (count_recipients(OLC_MAIL(d)) > 1) { - write_to_output(d, "Sorry, mail with multiple recipients cannot have attachments!\r\n"); - mailedit_disp_attachments(d); - return; - } - if ((d->character)->carrying == NULL) { - write_to_output(d, "Your inventory seems to be empty - you cannot attach anything.\r\n"); - mailedit_disp_attachments(d); - return; - } - list_attachments_numbered((d->character)->carrying, d->character); - write_to_output(d, "Enter a object to add: "); - OLC_MODE(d) = MAILEDIT_ADD_ATTACH; - break; - case 'b': - case 'B': - if ((OLC_MAIL(d)->mail)->attachment == NULL) { - write_to_output(d, "There are no attachments - you cannot remove anything.\r\n"); - mailedit_disp_attachments(d); - return; - } - list_attachments_numbered((OLC_MAIL(d)->mail)->attachment, d->character); - write_to_output(d, "Enter an object to remove: "); - OLC_MODE(d) = MAILEDIT_DEL_ATTACH; - break; - case 'c': - case 'C': - write_to_output(d, "Are you sure you wish to remove ALL objects? (Y/N): "); - OLC_MODE(d) = MAILEDIT_CLR_ATTACH; - break; - case 'q': - case 'Q': - mailedit_disp_mail(d); - break; - } - break; - - case MAILEDIT_ADD_ATTACH: - num = atoi(arg); - if ((obj = get_attachment_numbered(d->character, (d->character)->carrying, num)) == NULL) { - write_to_output(d, "That object number is not in your inventory\r\n"); - mailedit_disp_attachments(d); - return; - } - obj_from_char(obj); - obj_to_mail(obj, OLC_MAIL(d)->mail); - mailedit_disp_attachments(d); - break; - - case MAILEDIT_DEL_ATTACH: - num = atoi(arg); - if ((obj = get_attachment_numbered(d->character, (OLC_MAIL(d)->mail)->attachment, num)) == NULL) { - write_to_output(d, "That object number is not attached.\r\n"); - mailedit_disp_attachments(d); - return; - } - obj_from_mail(obj); - obj_to_char(obj, d->character); - mailedit_disp_attachments(d); - break; - - case MAILEDIT_CLR_ATTACH: - switch (*arg) { - case 'y': - case 'Y': - while ((OLC_MAIL(d)->mail)->attachment) { - obj = (OLC_MAIL(d)->mail)->attachment; - obj_from_mail(obj); - obj_to_char(obj, d->character); - } - mailedit_disp_attachments(d); - break; - case 'n': - case 'N': - mailedit_disp_attachments(d); - break; - default : write_to_output(d, "Invalid choice!\r\nAre you sure you wish to remove ALL attachments? (Y/N): "); - break; - } - break; - - case MAILEDIT_GET_GOLD: - gold = atol(arg); - diff = (gold - (OLC_MAIL(d)->mail)->coins); - if (diff > GET_GOLD(d->character)) { - write_to_output(d, "@RYou don't have that much gold!@n\r\n"); - mailedit_disp_mail(d); - return; - } - GET_GOLD(d->character) -= diff; - (OLC_MAIL(d)->mail)->coins = gold; - mailedit_disp_mail(d); - break; - - case MAILEDIT_GET_SUBJECT: - if ((OLC_MAIL(d)->mail)->subject) - free ((OLC_MAIL(d)->mail)->subject); - (OLC_MAIL(d)->mail)->subject = strdup(arg); - mailedit_disp_mail(d); - break; - - case MAILEDIT_MAILEDIT: - switch (*arg) { - case 'q': - case 'Q': - if (CONFIG_DRAFTS_ALLOWED) { - write_to_output(d, "Do you wish to save this mail as a draft? (Y/N): "); - OLC_MODE(d) = MAILEDIT_ASK_DRAFT; - } else { - write_to_output(d, "Do you wish to quit and lose your changes? (Y/N): "); - OLC_MODE(d) = MAILEDIT_ASK_QUIT; - } - break; - case 'a': - case 'A': - mailedit_disp_recipients(d); - break; - case 'b': - case 'B': - write_to_output(d, "Enter a subject for this mail: "); - OLC_MODE(d) = MAILEDIT_GET_SUBJECT; - break; - case 'c': - case 'C': - TOGGLE_BIT_AR((OLC_MAIL(d)->mail)->mail_flags, MAIL_URGENT); - mailedit_disp_mail(d); - break; - case 'd': - case 'D': - if (CONFIG_CAN_MAIL_OBJ) { - mailedit_disp_attachments(d); - } else { - write_to_output(d, "@RSorry, object attachments are disabled!@n\r\n"); - mailedit_disp_mail(d); - } - break; - case 'e': - case 'E': - if (CONFIG_CAN_MAIL_GOLD) { - write_to_output(d, "Enter a number of gold coins: "); - OLC_MODE(d) = MAILEDIT_GET_GOLD; - } else { - write_to_output(d, "@RSorry, gold attachments are disabled!@n\r\n"); - mailedit_disp_mail(d); - } - break; - case 'f': - case 'F': - /* Flicking between COD and 'send money', we need to give/take gold from the player */ - if (IS_SET_AR((OLC_MAIL(d)->mail)->mail_flags, MAIL_COD) && ((OLC_MAIL(d)->mail)->coins > 0)) { - diff = (OLC_MAIL(d)->mail)->coins; - if (diff > GET_GOLD(d->character)) { - write_to_output(d, "@RYou don't have enough gold to do that!@n\r\n"); - mailedit_disp_mail(d); - return; - } - GET_GOLD(d->character) -= diff; - } else if (!IS_SET_AR((OLC_MAIL(d)->mail)->mail_flags, MAIL_COD) && ((OLC_MAIL(d)->mail)->coins > 0)) { - diff = (OLC_MAIL(d)->mail)->coins; - GET_GOLD(d->character) += diff; - } - TOGGLE_BIT_AR((OLC_MAIL(d)->mail)->mail_flags, MAIL_COD); - mailedit_disp_mail(d); - break; - case 'g': - case 'G': - OLC_MODE(d) = MAILEDIT_GET_BODYTEXT; - send_editor_help(d); - write_to_output(d, "Enter the main body text for this mail:\r\n\r\n"); - if ((OLC_MAIL(d)->mail)->body) { - write_to_output(d, "%s", (OLC_MAIL(d)->mail)->body); - oldtext = strdup((OLC_MAIL(d)->mail)->body); - } - string_write(d, &(OLC_MAIL(d)->mail)->body, MAX_MAIL_SIZE, 0, oldtext); - break; - case 's': - case 'S': - if (GET_LEVEL(d->character) < CONFIG_FREE_MAIL_LEVEL) { - gold = CONFIG_STAMP_COST + (attachment_count(OLC_MAIL(d)->mail) * CONFIG_OBJECT_COST); - } else { - gold = 0; - } - if (gold > 0 && GET_GOLD(d->character) < gold ) { - write_to_output(d, "This mail costs %ld coins, and you can't afford that!\r\n", gold ); - mailedit_disp_mail(d); - return; - } - (OLC_MAIL(d)->mail)->sender = GET_ID(d->character); - if (perform_send_edited(d->character, OLC_MAIL(d)) == FALSE) { - write_to_output(d, "@RERROR: Unable to send Mail: Please tell an Imm!@n\r\n"); - /* Sending failed - return to editor, so they can choose to save as draft */ - mailedit_disp_mail(d); - return; - } - /* Sending was successful - clean up */ - if (gold) GET_GOLD(d->character) -= gold; - clear_mail_data(OLC_MAIL(d)->mail); - clear_recipients(OLC_MAIL(d)); - write_to_output(d, "Sending of mail successful! Press Enter to continue.\r\n"); - OLC_MODE(d) = MAILEDIT_BACK_TO_MENU; - break; - case 'x': - case 'X': - clear_mail_data(OLC_MAIL(d)->mail); - mailedit_disp_mail(d); - break; - default: - write_to_output(d, "Invalid Choice!\r\nEnter Selection : "); - break; - } - break; - - case MAILEDIT_PURGE_N_QUIT: - switch (*arg) { - case 'y': - case 'Y': - num = purge_marked_mail(d->character); - if (num > 0) { - write_to_output(d, "%d deleted mail%s ha%s been purged.", num, num == 1 ? "" : "s", num == 1 ? "s" : "ve"); - } - cleanup_olc(d, CLEANUP_ALL); - break; - case 'n': - case 'N': - cleanup_olc(d, CLEANUP_ALL); - break; - default : write_to_output(d, "Invalid choice!\r\nDo you wish to purge all marked mails now? (Y/N): "); - break; - } - break; - - default: - write_to_output(d, "Sorry - There appears to be a problem, returning to inbox.\r\n"); - log("SYSERR: mailedit: Invalid submode (%d)", OLC_MODE(d)); - mailedit_disp_menu(d); - break; - } -} - -void mailedit_string_cleanup(struct descriptor_data *d, int terminator) -{ - switch (OLC_MODE(d)) { - case MAILEDIT_GET_BODYTEXT: - mailedit_disp_mail(d); - break; - } -} - - diff --git a/src/protocol.c b/src/protocol.c deleted file mode 100755 index e129ec7..0000000 --- a/src/protocol.c +++ /dev/null @@ -1,2078 +0,0 @@ -/************************************************************************** -* File: protocol.c Part of tbaMUD * -* Usage: MXP, MSDP, ATCP, NAWS, TTYPE, CHARSET, 256-Colors * -* Author: KaVir * -* * -* All rights reserved. See license.doc for complete information. * -* * -* CircleMUD is based on DikuMUD, Copyright (C) 1990, 1991. * -**************************************************************************/ - -/****************************************************************************** - Header files. - ******************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include - -#include "protocol.h" - -/****************************************************************************** - The following section is for Diku/Merc derivatives. Replace as needed. - ******************************************************************************/ - -#include "conf.h" -#include "sysdep.h" -#include "structs.h" -#include "utils.h" -#include "comm.h" - -static void Write( descriptor_t *apDescriptor, const char *apData ) -{ - if ( apDescriptor != NULL && apDescriptor->has_prompt ) - { - if ( apDescriptor->pProtocol->WriteOOB > 0 || - *(apDescriptor->output) == '\0' ) - { - apDescriptor->pProtocol->WriteOOB = 2; - } - } - - write_to_output( apDescriptor, apData ); -} - -static void ReportBug( const char *apText ) -{ - log( apText ); -} - -static void InfoMessage( descriptor_t *apDescriptor, const char *apData ) -{ - Write( apDescriptor, "\t[F210][\toINFO\t[F210]]\tn " ); - Write( apDescriptor, apData ); - apDescriptor->pProtocol->WriteOOB = 0; -} - -/****************************************************************************** - MSDP file-scope variables. - ******************************************************************************/ - -/* These are for the GUI_VARIABLES, my unofficial extension of MSDP. They're - * intended for clients that wish to offer a generic GUI - not as nice as a - * custom GUI, admittedly, but still better than a plain terminal window. - * - * These are per-player so that they can be customised for different characters - * (eg changing 'mana' to 'blood' for vampires). You could even allow players - * to customise the buttons and gauges themselves if you wish. - */ -static const char s_Button1[] = "Help\ahelp"; -static const char s_Button2[] = "Look\alook"; -static const char s_Button3[] = "Score\ahelp"; -static const char s_Button4[] = "Equipment\aequipment"; -static const char s_Button5[] = "Inventory\ainventory"; - -static const char s_Gauge1[] = "Health\ared\aHEALTH\aHEALTH_MAX"; -static const char s_Gauge2[] = "Mana\ablue\aMANA\aMANA_MAX"; -static const char s_Gauge3[] = "Movement\agreen\aMOVEMENT\aMOVEMENT_MAX"; -static const char s_Gauge4[] = "Exp TNL\ayellow\aEXPERIENCE\aEXPERIENCE_MAX"; -static const char s_Gauge5[] = "Opponent\adarkred\aOPPONENT_HEALTH\aOPPONENT_HEALTH_MAX"; - -/****************************************************************************** - MSDP variable table. - ******************************************************************************/ - -/* Macros for readability, but you can remove them if you don't like them */ -#define NUMBER_READ_ONLY false, false, false, false, -1, -1, 0, NULL -#define STRING_READ_ONLY true, false, false, false, -1, -1, 0, NULL -#define NUMBER_IN_THE_RANGE(x,y) false, true, false, false, x, y, 0, NULL -#define BOOLEAN_SET_TO(x) false, true, false, false, 0, 1, x, NULL -#define STRING_WITH_LENGTH_OF(x,y) true, true, false, false, x, y, 0, NULL -#define STRING_WRITE_ONCE(x,y) true, true, true, false, -1, -1, 0, NULL -#define STRING_GUI(x) true, false, false, true, -1, -1, 0, x - -static variable_name_t VariableNameTable[eMSDP_MAX+1] = -{ - /* General */ - { eMSDP_CHARACTER_NAME, "CHARACTER_NAME", STRING_READ_ONLY }, - { eMSDP_SERVER_ID, "SERVER_ID", STRING_READ_ONLY }, - { eMSDP_SERVER_TIME, "SERVER_TIME", NUMBER_READ_ONLY }, - - /* Character */ - { eMSDP_AFFECTS, "AFFECTS", STRING_READ_ONLY }, - { eMSDP_ALIGNMENT, "ALIGNMENT", NUMBER_READ_ONLY }, - { eMSDP_EXPERIENCE, "EXPERIENCE", NUMBER_READ_ONLY }, - { eMSDP_EXPERIENCE_MAX, "EXPERIENCE_MAX", NUMBER_READ_ONLY }, - { eMSDP_EXPERIENCE_TNL, "EXPERIENCE_TNL", NUMBER_READ_ONLY }, - { eMSDP_HEALTH, "HEALTH", NUMBER_READ_ONLY }, - { eMSDP_HEALTH_MAX, "HEALTH_MAX", NUMBER_READ_ONLY }, - { eMSDP_LEVEL, "LEVEL", NUMBER_READ_ONLY }, - { eMSDP_RACE, "RACE", STRING_READ_ONLY }, - { eMSDP_CLASS, "CLASS", STRING_READ_ONLY }, - { eMSDP_MANA, "MANA", NUMBER_READ_ONLY }, - { eMSDP_MANA_MAX, "MANA_MAX", NUMBER_READ_ONLY }, - { eMSDP_WIMPY, "WIMPY", NUMBER_READ_ONLY }, - { eMSDP_PRACTICE, "PRACTICE", NUMBER_READ_ONLY }, - { eMSDP_MONEY, "MONEY", NUMBER_READ_ONLY }, - { eMSDP_MOVEMENT, "MOVEMENT", NUMBER_READ_ONLY }, - { eMSDP_MOVEMENT_MAX, "MOVEMENT_MAX", NUMBER_READ_ONLY }, - { eMSDP_HITROLL, "HITROLL", NUMBER_READ_ONLY }, - { eMSDP_DAMROLL, "DAMROLL", NUMBER_READ_ONLY }, - { eMSDP_AC, "AC", NUMBER_READ_ONLY }, - { eMSDP_STR, "STR", NUMBER_READ_ONLY }, - { eMSDP_INT, "INT", NUMBER_READ_ONLY }, - { eMSDP_WIS, "WIS", NUMBER_READ_ONLY }, - { eMSDP_DEX, "DEX", NUMBER_READ_ONLY }, - { eMSDP_CON, "CON", NUMBER_READ_ONLY }, - { eMSDP_STR_PERM, "STR_PERM", NUMBER_READ_ONLY }, - { eMSDP_INT_PERM, "INT_PERM", NUMBER_READ_ONLY }, - { eMSDP_WIS_PERM, "WIS_PERM", NUMBER_READ_ONLY }, - { eMSDP_DEX_PERM, "DEX_PERM", NUMBER_READ_ONLY }, - { eMSDP_CON_PERM, "CON_PERM", NUMBER_READ_ONLY }, - - /* Combat */ - { eMSDP_OPPONENT_HEALTH, "OPPONENT_HEALTH", NUMBER_READ_ONLY }, - { eMSDP_OPPONENT_HEALTH_MAX,"OPPONENT_HEALTH_MAX",NUMBER_READ_ONLY }, - { eMSDP_OPPONENT_LEVEL, "OPPONENT_LEVEL", NUMBER_READ_ONLY }, - { eMSDP_OPPONENT_NAME, "OPPONENT_NAME", STRING_READ_ONLY }, - - /* World */ - { eMSDP_AREA_NAME, "AREA_NAME", STRING_READ_ONLY }, - { eMSDP_ROOM_EXITS, "ROOM_EXITS", STRING_READ_ONLY }, - { eMSDP_ROOM_NAME, "ROOM_NAME", STRING_READ_ONLY }, - { eMSDP_ROOM_VNUM, "ROOM_VNUM", NUMBER_READ_ONLY }, - { eMSDP_WORLD_TIME, "WORLD_TIME", NUMBER_READ_ONLY }, - - /* Configurable variables */ - { eMSDP_CLIENT_ID, "CLIENT_ID", STRING_WRITE_ONCE(1,40) }, - { eMSDP_CLIENT_VERSION, "CLIENT_VERSION", STRING_WRITE_ONCE(1,40) }, - { eMSDP_PLUGIN_ID, "PLUGIN_ID", STRING_WITH_LENGTH_OF(1,40) }, - { eMSDP_ANSI_COLORS, "ANSI_COLORS", BOOLEAN_SET_TO(1) }, - { eMSDP_XTERM_256_COLORS, "XTERM_256_COLORS", BOOLEAN_SET_TO(0) }, - { eMSDP_UTF_8, "UTF_8", BOOLEAN_SET_TO(0) }, - { eMSDP_SOUND, "SOUND", BOOLEAN_SET_TO(0) }, - { eMSDP_MXP, "MXP", BOOLEAN_SET_TO(0) }, - - /* GUI variables */ - { eMSDP_BUTTON_1, "BUTTON_1", STRING_GUI(s_Button1) }, - { eMSDP_BUTTON_2, "BUTTON_2", STRING_GUI(s_Button2) }, - { eMSDP_BUTTON_3, "BUTTON_3", STRING_GUI(s_Button3) }, - { eMSDP_BUTTON_4, "BUTTON_4", STRING_GUI(s_Button4) }, - { eMSDP_BUTTON_5, "BUTTON_5", STRING_GUI(s_Button5) }, - { eMSDP_GAUGE_1, "GAUGE_1", STRING_GUI(s_Gauge1) }, - { eMSDP_GAUGE_2, "GAUGE_2", STRING_GUI(s_Gauge2) }, - { eMSDP_GAUGE_3, "GAUGE_3", STRING_GUI(s_Gauge3) }, - { eMSDP_GAUGE_4, "GAUGE_4", STRING_GUI(s_Gauge4) }, - { eMSDP_GAUGE_5, "GAUGE_5", STRING_GUI(s_Gauge5) }, - - { eMSDP_MAX, "", 0 } /* This must always be last. */ -}; - -/****************************************************************************** - MSSP file-scope variables. - ******************************************************************************/ - -static int s_Players = 0; -static time_t s_Uptime = 0; - -/****************************************************************************** - Local function prototypes. - ******************************************************************************/ - -static void Negotiate ( descriptor_t *apDescriptor ); -static void PerformHandshake ( descriptor_t *apDescriptor, char aCmd, char aProtocol ); -static void PerformSubnegotiation( descriptor_t *apDescriptor, char aCmd, char *apData, int aSize ); - -static void ParseMSDP ( descriptor_t *apDescriptor, const char *apData ); -static void ExecuteMSDPPair ( descriptor_t *apDescriptor, const char *apVariable, const char *apValue ); - -static void ParseATCP ( descriptor_t *apDescriptor, const char *apData ); - -static void SendMSSP ( descriptor_t *apDescriptor ); - -static char *GetMxpTag ( const char *apTag, const char *apText ); - -static const char *GetAnsiColour ( bool_t abBackground, int aRed, int aGreen, int aBlue ); -static const char *GetRGBColour ( bool_t abBackground, int aRed, int aGreen, int aBlue ); -static bool_t IsValidColour ( const char *apArgument ); - -static bool_t MatchString ( const char *apFirst, const char *apSecond ); -static bool_t PrefixString ( const char *apPart, const char *apWhole ); -static bool_t IsNumber ( const char *apString ); - -/****************************************************************************** - ANSI colour codes. - ******************************************************************************/ - -static const char s_Clean [] = "\033[0;00m"; /* Remove colour */ - -static const char s_DarkBlack [] = "\033[0;30m"; /* Black foreground */ -static const char s_DarkRed [] = "\033[0;31m"; /* Red foreground */ -static const char s_DarkGreen [] = "\033[0;32m"; /* Green foreground */ -static const char s_DarkYellow [] = "\033[0;33m"; /* Yellow foreground */ -static const char s_DarkBlue [] = "\033[0;34m"; /* Blue foreground */ -static const char s_DarkMagenta [] = "\033[0;35m"; /* Magenta foreground */ -static const char s_DarkCyan [] = "\033[0;36m"; /* Cyan foreground */ -static const char s_DarkWhite [] = "\033[0;37m"; /* White foreground */ - -static const char s_BoldBlack [] = "\033[1;30m"; /* Grey foreground */ -static const char s_BoldRed [] = "\033[1;31m"; /* Bright red foreground */ -static const char s_BoldGreen [] = "\033[1;32m"; /* Bright green foreground */ -static const char s_BoldYellow [] = "\033[1;33m"; /* Bright yellow foreground */ -static const char s_BoldBlue [] = "\033[1;34m"; /* Bright blue foreground */ -static const char s_BoldMagenta [] = "\033[1;35m"; /* Bright magenta foreground */ -static const char s_BoldCyan [] = "\033[1;36m"; /* Bright cyan foreground */ -static const char s_BoldWhite [] = "\033[1;37m"; /* Bright white foreground */ - -static const char s_BackBlack [] = "\033[1;40m"; /* Black background */ -static const char s_BackRed [] = "\033[1;41m"; /* Red background */ -static const char s_BackGreen [] = "\033[1;42m"; /* Green background */ -static const char s_BackYellow [] = "\033[1;43m"; /* Yellow background */ -static const char s_BackBlue [] = "\033[1;44m"; /* Blue background */ -static const char s_BackMagenta [] = "\033[1;45m"; /* Magenta background */ -static const char s_BackCyan [] = "\033[1;46m"; /* Cyan background */ -static const char s_BackWhite [] = "\033[1;47m"; /* White background */ - -/****************************************************************************** - Protocol global functions. - ******************************************************************************/ - -protocol_t *ProtocolCreate( void ) -{ - int i; /* Loop counter */ - - /* Called the first time we enter - make sure the table is correct */ - static bool_t bInit = false; - if ( !bInit ) - { - bInit = true; - for ( i = eMSDP_NONE+1; i < eMSDP_MAX; ++i ) - { - if ( VariableNameTable[i].Variable != i ) - { - ReportBug( "MSDP: Variable table does not match the enums in the header.\n" ); - break; - } - } - } - - protocol_t *pProtocol = malloc(sizeof(protocol_t)); - pProtocol->WriteOOB = 0; - pProtocol->bNegotiated = false; - pProtocol->bBlockMXP = false; - pProtocol->bTTYPE = false; - pProtocol->bNAWS = false; - pProtocol->bCHARSET = false; - pProtocol->bMSDP = false; - pProtocol->bATCP = false; - pProtocol->bMSP = false; - pProtocol->bMXP = false; - pProtocol->b256Support = eUNKNOWN; - pProtocol->ScreenWidth = 0; - pProtocol->ScreenHeight = 0; - pProtocol->pMXPVersion = strdup("Unknown"); - pProtocol->pLastTTYPE = NULL; - pProtocol->pVariables = malloc(sizeof(MSDP_t*)*eMSDP_MAX); - - for ( i = eMSDP_NONE+1; i < eMSDP_MAX; ++i ) - { - pProtocol->pVariables[i] = malloc(sizeof(MSDP_t)); - pProtocol->pVariables[i]->bReport = false; - pProtocol->pVariables[i]->bDirty = false; - pProtocol->pVariables[i]->ValueInt = 0; - pProtocol->pVariables[i]->pValueString = NULL; - - if ( VariableNameTable[i].bString ) - { - if ( VariableNameTable[i].pDefault != NULL ) - pProtocol->pVariables[i]->pValueString = strdup(VariableNameTable[i].pDefault); - else if ( VariableNameTable[i].bConfigurable ) - pProtocol->pVariables[i]->pValueString = strdup("Unknown"); - else /* Use an empty string */ - pProtocol->pVariables[i]->pValueString = strdup(""); - } - else if ( VariableNameTable[i].Default != 0 ) - { - pProtocol->pVariables[i]->ValueInt = VariableNameTable[i].Default; - } - } - - return pProtocol; -} - -void ProtocolDestroy( protocol_t *apProtocol ) -{ - int i; /* Loop counter */ - - for ( i = eMSDP_NONE+1; i < eMSDP_MAX; ++i ) - { - free(apProtocol->pVariables[i]->pValueString); - free(apProtocol->pVariables[i]); - } - - free(apProtocol->pVariables); - free(apProtocol->pLastTTYPE); - free(apProtocol->pMXPVersion); - free(apProtocol); -} - -void ProtocolInput( descriptor_t *apDescriptor, char *apData, int aSize, char *apOut ) -{ - static char CmdBuf[PROTOCOL_BUFFER+1]; - static char IacBuf[PROTOCOL_BUFFER+1]; - bool_t bIacMode = false; - int CmdIndex = 0; - int IacIndex = 0; - int Index; - - protocol_t *pProtocol = apDescriptor ? apDescriptor->pProtocol : NULL; - - for ( Index = 0; Index < aSize; ++Index ) - { - /* If we'd overflow the buffer, we just ignore the input */ - if ( CmdIndex >= PROTOCOL_BUFFER || IacIndex >= PROTOCOL_BUFFER ) - { - ReportBug("ProtocolInput: Too much incoming data to store in the buffer.\n"); - return; - } - - /* IAC IAC is treated as a single value of 255 */ - if ( apData[Index] == (char)IAC && apData[Index+1] == (char)IAC ) - { - if ( bIacMode ) - IacBuf[IacIndex++] = (char)IAC; - else /* In-band command */ - CmdBuf[CmdIndex++] = (char)IAC; - Index++; - } - else if ( bIacMode ) - { - /* End subnegotiation. */ - if ( apData[Index] == (char)IAC && apData[Index+1] == (char)SE ) - { - Index++; - bIacMode = false; - IacBuf[IacIndex] = '\0'; - if ( IacIndex >= 2 ) - PerformSubnegotiation( apDescriptor, IacBuf[0], &IacBuf[1], IacIndex-1 ); - IacIndex = 0; - } - else - IacBuf[IacIndex++] = apData[Index]; - } - else if ( apData[Index] == (char)27 && apData[Index+1] == '[' && - isdigit(apData[Index+2]) && apData[Index+3] == 'z' ) - { - char MXPBuffer [1024]; - char *pMXPTag = NULL; - int i = 0; /* Loop counter */ - printf( " came in...\n" ); - Index += 4; /* Skip to the start of the MXP sequence. */ - - while ( Index < aSize && apData[Index] != '>' && i < 1000 ) - { - MXPBuffer[i++] = apData[Index++]; - } - MXPBuffer[i++] = '>'; - MXPBuffer[i] = '\0'; - - if ( ( pMXPTag = GetMxpTag( "CLIENT=", MXPBuffer ) ) != NULL ) - { - /* Overwrite the previous client name - this is harder to fake */ - free(pProtocol->pVariables[eMSDP_CLIENT_ID]->pValueString); - pProtocol->pVariables[eMSDP_CLIENT_ID]->pValueString = strdup(pMXPTag); - } - - if ( ( pMXPTag = GetMxpTag( "VERSION=", MXPBuffer ) ) != NULL ) - { - const char *pClientName = pProtocol->pVariables[eMSDP_CLIENT_ID]->pValueString; - - free(pProtocol->pVariables[eMSDP_CLIENT_VERSION]->pValueString); - pProtocol->pVariables[eMSDP_CLIENT_VERSION]->pValueString = strdup(pMXPTag); - - if ( MatchString( "MUSHCLIENT", pClientName ) ) - { - /* MUSHclient 4.02 and later supports 256 colours. */ - if ( strcmp(pMXPTag, "4.02") >= 0 ) - { - pProtocol->pVariables[eMSDP_XTERM_256_COLORS]->ValueInt = 1; - pProtocol->b256Support = eYES; - } - else /* We know for sure that 256 colours are not supported */ - pProtocol->b256Support = eNO; - } - else if ( MatchString( "CMUD", pClientName ) ) - { - /* CMUD 3.04 and later supports 256 colours. */ - if ( strcmp(pMXPTag, "3.04") >= 0 ) - { - pProtocol->pVariables[eMSDP_XTERM_256_COLORS]->ValueInt = 1; - pProtocol->b256Support = eYES; - } - else /* We know for sure that 256 colours are not supported */ - pProtocol->b256Support = eNO; - } - else if ( MatchString( "ATLANTIS", pClientName ) ) - { - /* Atlantis 0.9.9.0 supports XTerm 256 colours, but it doesn't - * yet have MXP. However MXP is planned, so once it responds - * to a tag we'll know we can use 256 colours. - */ - pProtocol->pVariables[eMSDP_XTERM_256_COLORS]->ValueInt = 1; - pProtocol->b256Support = eYES; - } - } - - if ( ( pMXPTag = GetMxpTag( "MXP=", MXPBuffer ) ) != NULL ) - { - free(pProtocol->pMXPVersion); - pProtocol->pMXPVersion = strdup(pMXPTag); - } - - if ( strcmp(pProtocol->pMXPVersion, "Unknown") ) - { - Write( apDescriptor, "\n" ); - sprintf( MXPBuffer, "MXP version %s detected and enabled. View \t(score\t).\r\n", - pProtocol->pMXPVersion ); - InfoMessage( apDescriptor, MXPBuffer ); - } - } - else /* In-band command */ - { - if ( apData[Index] == (char)IAC ) - { - switch ( apData[Index+1] ) - { - case (char)SB: /* Begin subnegotiation. */ - Index++; - bIacMode = true; - break; - - case (char)DO: /* Handshake. */ - case (char)DONT: - case (char)WILL: - case (char)WONT: - PerformHandshake( apDescriptor, apData[Index+1], apData[Index+2] ); - Index += 2; - break; - - case (char)IAC: /* Two IACs count as one. */ - CmdBuf[CmdIndex++] = (char)IAC; - Index++; - break; - - default: /* Skip it. */ - Index++; - break; - } - } - else - CmdBuf[CmdIndex++] = apData[Index]; - } - } - - /* Terminate the two buffers */ - IacBuf[IacIndex] = '\0'; - CmdBuf[CmdIndex] = '\0'; - - /* Copy the input buffer back to the player. */ - strcat( apOut, CmdBuf ); -} - -const char *ProtocolOutput( descriptor_t *apDescriptor, const char *apData, int *apLength ) -{ - static char Result[MAX_OUTPUT_BUFFER+1]; - const char MSP[] = "!!"; - const char MXPStart[] = "\033[1z<"; - const char MXPStop[] = ">\033[7z"; - const char LinkStart[] = "\033[1z\033[7z"; - const char LinkStop[] = "\033[1z\033[7z"; - bool_t bTerminate = false, bUseMXP = false, bUseMSP = false; - int i = 0, j = 0; /* Index values */ - - protocol_t *pProtocol = apDescriptor ? apDescriptor->pProtocol : NULL; - if ( pProtocol == NULL || apData == NULL ) - return apData; - - /* Strip !!SOUND() triggers if they support MSP or are using sound */ - if ( pProtocol->bMSP || pProtocol->pVariables[eMSDP_SOUND]->ValueInt ) - bUseMSP = true; - - for ( ; i < MAX_OUTPUT_BUFFER && apData[j] != '\0' && !bTerminate && - (*apLength <= 0 || j < *apLength); ++j ) - { - if ( apData[j] == '\t' ) - { - const char *pCopyFrom = NULL; - - switch ( apData[++j] ) - { - case 'n': - pCopyFrom = s_Clean; - break; - case 'r': /* dark red */ - pCopyFrom = ColourRGB(apDescriptor, "F200"); - break; - case 'R': /* light red */ - pCopyFrom = ColourRGB(apDescriptor, "F500"); - break; - case 'g': /* dark green */ - pCopyFrom = ColourRGB(apDescriptor, "F020"); - break; - case 'G': /* light green */ - pCopyFrom = ColourRGB(apDescriptor, "F050"); - break; - case 'y': /* dark yellow */ - pCopyFrom = ColourRGB(apDescriptor, "F220"); - break; - case 'Y': /* light yellow */ - pCopyFrom = ColourRGB(apDescriptor, "F550"); - break; - case 'b': /* dark blue */ - pCopyFrom = ColourRGB(apDescriptor, "F002"); - break; - case 'B': /* light blue */ - pCopyFrom = ColourRGB(apDescriptor, "F005"); - break; - case 'm': /* dark magenta */ - pCopyFrom = ColourRGB(apDescriptor, "F202"); - break; - case 'M': /* light magenta */ - pCopyFrom = ColourRGB(apDescriptor, "F505"); - break; - case 'c': /* dark cyan */ - pCopyFrom = ColourRGB(apDescriptor, "F022"); - break; - case 'C': /* light cyan */ - pCopyFrom = ColourRGB(apDescriptor, "F055"); - break; - case 'w': /* dark white */ - pCopyFrom = ColourRGB(apDescriptor, "F222"); - break; - case 'W': /* light white */ - pCopyFrom = ColourRGB(apDescriptor, "F555"); - break; - case 'o': /* dark orange */ - pCopyFrom = ColourRGB(apDescriptor, "F520"); - break; - case 'O': /* light orange */ - pCopyFrom = ColourRGB(apDescriptor, "F530"); - break; - case '(': /* MXP link */ - if ( !pProtocol->bBlockMXP && pProtocol->pVariables[eMSDP_MXP]->ValueInt ) - pCopyFrom = LinkStart; - break; - case ')': /* MXP link */ - if ( !pProtocol->bBlockMXP && pProtocol->pVariables[eMSDP_MXP]->ValueInt ) - pCopyFrom = LinkStop; - pProtocol->bBlockMXP = false; - break; - case '<': - if ( !pProtocol->bBlockMXP && pProtocol->pVariables[eMSDP_MXP]->ValueInt ) - { - pCopyFrom = MXPStart; - bUseMXP = true; - } - else /* No MXP support, so just strip it out */ - { - while ( apData[j] != '\0' && apData[j] != '>' ) - ++j; - } - pProtocol->bBlockMXP = false; - break; - case '[': - if ( tolower(apData[++j]) == 'u' ) - { - char Buffer[8] = {'\0'}, BugString[256]; - int Index = 0; - int Number = 0; - bool_t bDone = false, bValid = true; - - while ( isdigit(apData[++j]) ) - { - Number *= 10; - Number += (apData[j])-'0'; - } - - if ( apData[j] == '/' ) - ++j; - - while ( apData[j] != '\0' && !bDone ) - { - if ( apData[j] == ']' ) - bDone = true; - else if ( Index < 7 ) - Buffer[Index++] = apData[j++]; - else /* It's too long, so ignore the rest and note the problem */ - { - j++; - bValid = false; - } - } - - if ( !bDone ) - { - sprintf( BugString, "BUG: Unicode substitute '%s' wasn't terminated with ']'.\n", Buffer ); - ReportBug( BugString ); - } - else if ( !bValid ) - { - sprintf( BugString, "BUG: Unicode substitute '%s' truncated. Missing ']'?\n", Buffer ); - ReportBug( BugString ); - } - else if ( pProtocol->pVariables[eMSDP_UTF_8]->ValueInt ) - { - pCopyFrom = UnicodeGet(Number); - } - else /* Display the substitute string */ - { - pCopyFrom = Buffer; - } - - /* Terminate if we've reached the end of the string */ - bTerminate = !bDone; - } - else if ( tolower(apData[j]) == 'f' || tolower(apData[j]) == 'b' ) - { - char Buffer[8] = {'\0'}, BugString[256]; - int Index = 0; - bool_t bDone = false, bValid = true; - - /* Copy the 'f' (foreground) or 'b' (background) */ - Buffer[Index++] = apData[j++]; - - while ( apData[j] != '\0' && !bDone && bValid ) - { - if ( apData[j] == ']' ) - bDone = true; - else if ( Index < 4 ) - Buffer[Index++] = apData[j++]; - else /* It's too long, so drop out - the colour code may still be valid */ - bValid = false; - } - - if ( !bDone || !bValid) - { - sprintf( BugString, "BUG: RGB %sground colour '%s' wasn't terminated with ']'.\n", - (tolower(Buffer[0]) == 'f') ? "fore" : "back", &Buffer[1] ); - ReportBug( BugString ); - } - else if ( !IsValidColour(Buffer) ) - { - sprintf( BugString, "BUG: RGB %sground colour '%s' invalid (each digit must be in the range 0-5).\n", - (tolower(Buffer[0]) == 'f') ? "fore" : "back", &Buffer[1] ); - ReportBug( BugString ); - } - else /* Success */ - { - pCopyFrom = ColourRGB(apDescriptor, Buffer); - } - } - else if ( tolower(apData[j]) == 'x' ) - { - char Buffer[8] = {'\0'}, BugString[256]; - int Index = 0; - bool_t bDone = false, bValid = true; - - ++j; /* Skip the 'x' */ - - while ( apData[j] != '\0' && !bDone ) - { - if ( apData[j] == ']' ) - bDone = true; - else if ( Index < 7 ) - Buffer[Index++] = apData[j++]; - else /* It's too long, so ignore the rest and note the problem */ - { - j++; - bValid = false; - } - } - - if ( !bDone ) - { - sprintf( BugString, "BUG: Required MXP version '%s' wasn't terminated with ']'.\n", Buffer ); - ReportBug( BugString ); - } - else if ( !bValid ) - { - sprintf( BugString, "BUG: Required MXP version '%s' too long. Missing ']'?\n", Buffer ); - ReportBug( BugString ); - } - else if ( !strcmp(pProtocol->pMXPVersion, "Unknown") || - strcmp(pProtocol->pMXPVersion, Buffer) < 0 ) - { - /* Their version of MXP isn't high enough */ - pProtocol->bBlockMXP = true; - } - else /* MXP is sufficient for this tag */ - { - pProtocol->bBlockMXP = false; - } - - /* Terminate if we've reached the end of the string */ - bTerminate = !bDone; - } - break; - case '!': /* Used for in-band MSP sound triggers */ - pCopyFrom = MSP; - break; - case '\0': - bTerminate = true; - break; - default: - break; - } - - /* Copy the colour code, if any. */ - if ( pCopyFrom != NULL ) - { - while ( *pCopyFrom != '\0' && i < MAX_OUTPUT_BUFFER ) - Result[i++] = *pCopyFrom++; - } - } - else if ( bUseMXP && apData[j] == '>' ) - { - const char *pCopyFrom = MXPStop; - while ( *pCopyFrom != '\0' && i < MAX_OUTPUT_BUFFER) - Result[i++] = *pCopyFrom++; - bUseMXP = false; - } - else if ( bUseMSP && j > 0 && apData[j-1] == '!' && apData[j] == '!' && - PrefixString("SOUND(", &apData[j+1]) ) - { - /* Avoid accidental triggering of old-style MSP triggers */ - Result[i++] = '?'; - } - else /* Just copy the character normally */ - { - Result[i++] = apData[j]; - } - } - - /* If we'd overflow the buffer, we don't send any output */ - if ( i >= MAX_OUTPUT_BUFFER ) - { - i = 0; - ReportBug("ProtocolOutput: Too much outgoing data to store in the buffer.\n"); - } - - /* Terminate the string */ - Result[i] = '\0'; - - /* Store the length */ - if ( apLength ) - *apLength = i; - - /* Return the string */ - return Result; -} - -/* Some clients (such as GMud) don't properly handle negotiation, and simply - * display every printable character to the screen. However TTYPE isn't a - * printable character, so we negotiate for it first, and only negotiate for - * other protocols if the client responds with IAC WILL TTYPE or IAC WONT - * TTYPE. Thanks go to Donky on MudBytes for the suggestion. - */ -void ProtocolNegotiate( descriptor_t *apDescriptor ) -{ - static const char DoTTYPE [] = { (char)IAC, (char)DO, TELOPT_TTYPE, '\0' }; - Write(apDescriptor, DoTTYPE); -} - -/****************************************************************************** - Copyover save/load functions. - ******************************************************************************/ - -const char *CopyoverGet( descriptor_t *apDescriptor ) -{ - static char Buffer[64]; - char *pBuffer = Buffer; - protocol_t *pProtocol = apDescriptor ? apDescriptor->pProtocol : NULL; - - if ( pProtocol != NULL ) - { - sprintf(Buffer, "%d/%d", pProtocol->ScreenWidth, pProtocol->ScreenHeight); - - /* Skip to the end */ - while ( *pBuffer != '\0' ) - ++pBuffer; - - if ( pProtocol->bNAWS ) - *pBuffer++ = 'N'; - if ( pProtocol->bMSDP ) - *pBuffer++ = 'M'; - if ( pProtocol->bATCP ) - *pBuffer++ = 'A'; - if ( pProtocol->bMSP ) - *pBuffer++ = 'S'; - if ( pProtocol->pVariables[eMSDP_MXP]->ValueInt ) - *pBuffer++ = 'X'; - if ( pProtocol->pVariables[eMSDP_XTERM_256_COLORS]->ValueInt ) - *pBuffer++ = 'C'; - if ( pProtocol->pVariables[eMSDP_UTF_8]->ValueInt ) - *pBuffer++ = 'U'; - } - - /* Terminate the string */ - *pBuffer = '\0'; - - return Buffer; -} - -void CopyoverSet( descriptor_t *apDescriptor, const char *apData ) -{ - protocol_t *pProtocol = apDescriptor ? apDescriptor->pProtocol : NULL; - - if ( pProtocol != NULL && apData != NULL ) - { - int Width = 0, Height = 0; - bool_t bDoneWidth = false; - int i; /* Loop counter */ - - for ( i = 0; apData[i] != '\0'; ++i ) - { - switch ( apData[i] ) - { - case 'N': - pProtocol->bNAWS = true; - break; - case 'M': - pProtocol->bMSDP = true; - break; - case 'A': - pProtocol->bATCP = true; - break; - case 'S': - pProtocol->bMSP = true; - break; - case 'X': - pProtocol->bMXP = true; - pProtocol->pVariables[eMSDP_MXP]->ValueInt = 1; - break; - case 'C': - pProtocol->pVariables[eMSDP_XTERM_256_COLORS]->ValueInt = 1; - break; - case 'U': - pProtocol->bCHARSET = true; - pProtocol->pVariables[eMSDP_UTF_8]->ValueInt = 1; - break; - default: - if ( apData[i] == '/' ) - bDoneWidth = true; - else if ( isdigit(apData[i]) ) - { - if ( bDoneWidth ) - { - Height *= 10; - Height += (apData[i] - '0'); - } - else /* We're still calculating height */ - { - Width *= 10; - Width += (apData[i] - '0'); - } - } - break; - } - } - - /* Restore the width and height */ - pProtocol->ScreenWidth = Width; - pProtocol->ScreenHeight = Height; - - /* If we're using MSDP or ATCP, we need to renegotiate it so that the - * client can resend the list of variables it wants us to REPORT. - * - * Note that we only use ATCP if MSDP is not supported. - */ - if ( pProtocol->bMSDP ) - { - char WillMSDP [] = { (char)IAC, (char)WILL, TELOPT_MSDP, '\0' }; - Write(apDescriptor, WillMSDP); - } - else if ( pProtocol->bATCP ) - { - char DoATCP [] = { (char)IAC, (char)DO, (char)TELOPT_ATCP, '\0' }; - Write(apDescriptor, DoATCP); - } - - /* Ask the client to send its MXP version again */ - if ( pProtocol->bMXP ) - MXPSendTag( apDescriptor, "" ); - } -} - -/****************************************************************************** - MSDP global functions. - ******************************************************************************/ - -void MSDPUpdate( descriptor_t *apDescriptor ) -{ - int i; /* Loop counter */ - - protocol_t *pProtocol = apDescriptor ? apDescriptor->pProtocol : NULL; - - for ( i = eMSDP_NONE+1; i < eMSDP_MAX; ++i ) - { - if ( pProtocol->pVariables[i]->bReport ) - { - if ( pProtocol->pVariables[i]->bDirty ) - { - MSDPSend( apDescriptor, (variable_t)i ); - pProtocol->pVariables[i]->bDirty = false; - } - } - } -} - -void MSDPSend( descriptor_t *apDescriptor, variable_t aMSDP ) -{ - char MSDPBuffer [1024] = { '\0' }; - - if ( aMSDP > eMSDP_NONE && aMSDP < eMSDP_MAX ) - { - protocol_t *pProtocol = apDescriptor ? apDescriptor->pProtocol : NULL; - - if ( VariableNameTable[aMSDP].bString ) - { - if ( pProtocol->bMSDP ) - { - sprintf( MSDPBuffer, "%c%c%c%c%s%c%s%c%c", - IAC, SB, TELOPT_MSDP, MSDP_VAR, - VariableNameTable[aMSDP].pName, MSDP_VAL, - pProtocol->pVariables[aMSDP]->pValueString, IAC, SE ); - } - else if ( pProtocol->bATCP ) - { - sprintf( MSDPBuffer, "%c%c%cMSDP.%s %s%c%c", - IAC, SB, TELOPT_ATCP, - VariableNameTable[aMSDP].pName, - pProtocol->pVariables[aMSDP]->pValueString, IAC, SE ); - } - } - else /* It's an integer, not a string */ - { - if ( pProtocol->bMSDP ) - { - sprintf( MSDPBuffer, "%c%c%c%c%s%c%d%c%c", - IAC, SB, TELOPT_MSDP, MSDP_VAR, - VariableNameTable[aMSDP].pName, MSDP_VAL, - pProtocol->pVariables[aMSDP]->ValueInt, IAC, SE ); - } - else if ( pProtocol->bATCP ) - { - sprintf( MSDPBuffer, "%c%c%cMSDP.%s %d%c%c", - IAC, SB, TELOPT_ATCP, - VariableNameTable[aMSDP].pName, - pProtocol->pVariables[aMSDP]->ValueInt, IAC, SE ); - } - } - - /* Just in case someone calls this function without checking MSDP/ATCP */ - if ( MSDPBuffer[0] != '\0' ) - Write( apDescriptor, MSDPBuffer ); - } -} - -void MSDPSendPair( descriptor_t *apDescriptor, const char *apVariable, const char *apValue ) -{ - char MSDPBuffer [1024] = { '\0' }; - - if ( apVariable != NULL && apValue != NULL ) - { - protocol_t *pProtocol = apDescriptor ? apDescriptor->pProtocol : NULL; - - if ( pProtocol->bMSDP ) - { - sprintf( MSDPBuffer, "%c%c%c%c%s%c%s%c%c", - IAC, SB, TELOPT_MSDP, MSDP_VAR, apVariable, MSDP_VAL, - apValue, IAC, SE ); - } - else if ( pProtocol->bATCP ) - { - sprintf( MSDPBuffer, "%c%c%cMSDP.%s %s%c%c", - IAC, SB, TELOPT_ATCP, apVariable, apValue, IAC, SE ); - } - - /* Just in case someone calls this function without checking MSDP/ATCP */ - if ( MSDPBuffer[0] != '\0' ) - Write( apDescriptor, MSDPBuffer ); - } -} - -void MSDPSetNumber( descriptor_t *apDescriptor, variable_t aMSDP, int aValue ) -{ - protocol_t *pProtocol = apDescriptor ? apDescriptor->pProtocol : NULL; - - if ( pProtocol != NULL && aMSDP > eMSDP_NONE && aMSDP < eMSDP_MAX ) - { - if ( !VariableNameTable[aMSDP].bString ) - { - if ( pProtocol->pVariables[aMSDP]->ValueInt != aValue ) - { - pProtocol->pVariables[aMSDP]->ValueInt = aValue; - pProtocol->pVariables[aMSDP]->bDirty = true; - } - } - } -} - -void MSDPSetString( descriptor_t *apDescriptor, variable_t aMSDP, char *apValue ) -{ - protocol_t *pProtocol = apDescriptor ? apDescriptor->pProtocol : NULL; - - if ( pProtocol != NULL && apValue != NULL ) - { - if ( VariableNameTable[aMSDP].bString ) - { - if ( strcmp(pProtocol->pVariables[aMSDP]->pValueString, apValue) ) - { - free(pProtocol->pVariables[aMSDP]->pValueString); - pProtocol->pVariables[aMSDP]->pValueString = strdup(apValue); - pProtocol->pVariables[aMSDP]->bDirty = true; - } - } - } -} - -/****************************************************************************** - MSSP global functions. - ******************************************************************************/ - -void MSSPSetPlayers( int aPlayers ) -{ - s_Players = aPlayers; - - if ( s_Uptime == 0 ) - s_Uptime = time(0); -} - -/****************************************************************************** - MXP global functions. - ******************************************************************************/ - -char *MXPCreateTag( descriptor_t *apDescriptor, char *apTag ) -{ - protocol_t *pProtocol = apDescriptor ? apDescriptor->pProtocol : NULL; - - if ( pProtocol != NULL && pProtocol->pVariables[eMSDP_MXP]->ValueInt && - strlen(apTag) < 1000 ) - { - static char MXPBuffer [1024]; - sprintf( MXPBuffer, "\033[1z%s\033[7z", apTag ); - return MXPBuffer; - } - else /* Leave the tag as-is, don't try to MXPify it */ - { - return apTag; - } -} - -void MXPSendTag( descriptor_t *apDescriptor, char *apTag ) -{ - protocol_t *pProtocol = apDescriptor ? apDescriptor->pProtocol : NULL; - - if ( pProtocol != NULL && pProtocol->pVariables[eMSDP_MXP]->ValueInt && - strlen(apTag) < 1000 ) - { - char MXPBuffer [1024]; - sprintf(MXPBuffer, "\033[1z%s\033[7z\r\n", apTag ); - Write(apDescriptor, MXPBuffer); - } -} - -/****************************************************************************** - Sound global functions. - ******************************************************************************/ - -void SoundSend( descriptor_t *apDescriptor, const char *apTrigger ) -{ - const int MaxTriggerLength = 128; /* Used for the buffer size */ - - if ( apDescriptor != NULL && apTrigger != NULL ) - { - protocol_t *pProtocol = apDescriptor ? apDescriptor->pProtocol : NULL; - - if ( pProtocol != NULL && pProtocol->pVariables[eMSDP_SOUND]->ValueInt ) - { - if ( pProtocol->bMSDP || pProtocol->bATCP ) - { - /* Send the sound trigger through MSDP or ATCP */ - MSDPSendPair( apDescriptor, "SOUND", apTrigger ); - } - else if ( strlen(apTrigger) <= MaxTriggerLength ) - { - /* Use an old MSP-style trigger */ - char Buffer[MaxTriggerLength+10]; - sprintf( Buffer, "\t!SOUND(%s)", apTrigger ); - Write(apDescriptor, Buffer); - } - } - } -} - -/****************************************************************************** - Colour global functions. - ******************************************************************************/ - -const char *ColourRGB( descriptor_t *apDescriptor, const char *apRGB ) -{ - protocol_t *pProtocol = apDescriptor ? apDescriptor->pProtocol : NULL; - - if ( pProtocol && pProtocol->pVariables[eMSDP_ANSI_COLORS]->ValueInt ) - { - if ( IsValidColour(apRGB) ) - { - bool_t bBackground = (tolower(apRGB[0]) == 'b'); - int Red = apRGB[1] - '0'; - int Green = apRGB[2] - '0'; - int Blue = apRGB[3] - '0'; - - if ( pProtocol->pVariables[eMSDP_XTERM_256_COLORS]->ValueInt ) - return GetRGBColour( bBackground, Red, Green, Blue ); - else /* Use regular ANSI colour */ - return GetAnsiColour( bBackground, Red, Green, Blue ); - } - else /* Invalid colour - use this to clear any existing colour. */ - { - return s_Clean; - } - } - else /* Don't send any colour, not even clear */ - { - return ""; - } -} - -/****************************************************************************** - UTF-8 global functions. - ******************************************************************************/ - -char *UnicodeGet( int aValue ) -{ - static char Buffer[8]; - char *pString = Buffer; - - UnicodeAdd( &pString, aValue ); - *pString = '\0'; - - return Buffer; -} - -void UnicodeAdd( char **apString, int aValue ) -{ - if ( aValue < 0x80 ) - { - *(*apString)++ = (char)aValue; - } - else if ( aValue < 0x800 ) - { - *(*apString)++ = (char)(0xC0 | (aValue>>6)); - *(*apString)++ = (char)(0x80 | (aValue & 0x3F)); - } - else if ( aValue < 0x10000 ) - { - *(*apString)++ = (char)(0xE0 | (aValue>>12)); - *(*apString)++ = (char)(0x80 | (aValue>>6 & 0x3F)); - *(*apString)++ = (char)(0x80 | (aValue & 0x3F)); - } - else if ( aValue < 0x200000 ) - { - *(*apString)++ = (char)(0xF0 | (aValue>>18)); - *(*apString)++ = (char)(0x80 | (aValue>>12 & 0x3F)); - *(*apString)++ = (char)(0x80 | (aValue>>6 & 0x3F)); - *(*apString)++ = (char)(0x80 | (aValue & 0x3F)); - } -} - -/****************************************************************************** - Local negotiation functions. - ******************************************************************************/ - -static void Negotiate( descriptor_t *apDescriptor ) -{ - protocol_t *pProtocol = apDescriptor->pProtocol; - - if ( pProtocol->bNegotiated ) - { - char RequestTTYPE [] = { (char)IAC, (char)SB, TELOPT_TTYPE, TELOPT_SEND, (char)IAC, (char)SE, '\0' }; - char DoNAWS [] = { (char)IAC, (char)DO, TELOPT_NAWS, '\0' }; - char DoCHARSET [] = { (char)IAC, (char)DO, TELOPT_CHARSET, '\0' }; - char WillMSDP [] = { (char)IAC, (char)WILL, TELOPT_MSDP, '\0' }; - char WillMSSP [] = { (char)IAC, (char)WILL, TELOPT_MSSP, '\0' }; - char DoATCP [] = { (char)IAC, (char)DO, (char)TELOPT_ATCP,'\0' }; - char WillMSP [] = { (char)IAC, (char)WILL, TELOPT_MSP, '\0' }; - char DoMXP [] = { (char)IAC, (char)DO, TELOPT_MXP, '\0' }; - -/* Put this back if your mud already supports MCCP. - char WillMCCP [] = { (char)IAC, (char)WILL, TELOPT_MCCP, '\0' }; -*/ - - /* Request the client type if TTYPE is supported. */ - if ( pProtocol->bTTYPE ) - Write(apDescriptor, RequestTTYPE); - - /* Check for other protocols. */ - Write(apDescriptor, DoNAWS); - Write(apDescriptor, DoCHARSET); - Write(apDescriptor, WillMSDP); - Write(apDescriptor, WillMSSP); - Write(apDescriptor, DoATCP); - Write(apDescriptor, WillMSP); - Write(apDescriptor, DoMXP); - -/* Put this back if your mud already supports MCCP. - Write(apDescriptor, WillMCCP); -*/ - } -} - -static void PerformHandshake( descriptor_t *apDescriptor, char aCmd, char aProtocol ) -{ - bool_t bResult = true; - protocol_t *pProtocol = apDescriptor->pProtocol; - - switch ( aProtocol ) - { - case (char)TELOPT_TTYPE: - if ( aCmd == (char)WILL ) - { - if ( !pProtocol->bNegotiated ) - { - /* Negotiate for the remaining protocols. */ - pProtocol->bNegotiated = true; - pProtocol->bTTYPE = true; - Negotiate(apDescriptor); - } - } - else if ( aCmd == (char)WONT ) - { - if ( !pProtocol->bNegotiated ) - { - /* Still negotiate, as this client obviously knows how to - * correctly respond to negotiation attempts - but we don't - * ask for TTYPE, as it doesn't support it. - */ - pProtocol->bNegotiated = true; - pProtocol->bTTYPE = false; - Negotiate(apDescriptor); - } - } - else /* Anything else is invalid. */ - bResult = false; - break; - - case (char)TELOPT_NAWS: - if ( aCmd == (char)WILL ) - pProtocol->bNAWS = true; - else if ( aCmd == (char)WONT ) - pProtocol->bNAWS = false; - else /* Anything else is invalid. */ - bResult = false; - break; - - case (char)TELOPT_CHARSET: - if ( aCmd == (char)WILL ) - { - char charset_utf8 [] = { (char)IAC, (char)SB, TELOPT_CHARSET, 1, ' ', 'U', 'T', 'F', '-', '8', (char)IAC, (char)SE, '\0' }; - Write(apDescriptor, charset_utf8); - pProtocol->bCHARSET = true; - } - else if ( aCmd == (char)WONT ) - pProtocol->bCHARSET = false; - else /* Anything else is invalid. */ - bResult = false; - break; - - case (char)TELOPT_MSDP: - if ( aCmd == (char)DO ) - { - pProtocol->bMSDP = true; - - /* Identify the mud to the client. */ - MSDPSendPair( apDescriptor, "SERVER_ID", MUD_NAME ); - } - else if ( aCmd == (char)DONT ) - pProtocol->bMSDP = false; - else /* Anything else is invalid. */ - bResult = false; - break; - - case (char)TELOPT_MSSP: - if ( aCmd == (char)DO ) - SendMSSP( apDescriptor ); - else if ( aCmd == (char)DONT ) - ; /* Do nothing. */ - else /* Anything else is invalid. */ - bResult = false; - break; - -/* Put this back if your mud already supports MCCP. - case (char)TELOPT_MCCP: - if ( aCmd == (char)DO ) - CompressStart(); - else if ( aCmd == (char)DONT ) - CompressEnd(); - else // Anything else is invalid. - bResult = false; - break; -*/ - case (char)TELOPT_MSP: - if ( aCmd == (char)DO ) - pProtocol->bMSP = true; - else if ( aCmd == (char)DONT ) - pProtocol->bMSP = false; - else /* Anything else is invalid. */ - bResult = false; - break; - - case (char)TELOPT_MXP: - if ( aCmd == (char)WILL ) - { - /* Enable MXP. */ - const char EnableMXP[] = { (char)IAC, (char)SB, TELOPT_MXP, (char)IAC, (char)SE, '\0' }; - Write(apDescriptor, EnableMXP); - - /* Create a secure channel, and note that MXP is active. */ - Write(apDescriptor, "\033[7z"); - pProtocol->bMXP = true; - pProtocol->pVariables[eMSDP_MXP]->ValueInt = 1; - } - else if ( aCmd == (char)WONT ) - pProtocol->bMXP = false; - else /* Anything else is invalid. */ - bResult = false; - break; - - case (char)TELOPT_ATCP: - if ( aCmd == (char)WILL ) - { - /* If we don't support MSDP, fake it with ATCP */ - if ( !pProtocol->bMSDP ); - { - pProtocol->bATCP = true; - - /* Identify the mud to the client. */ - MSDPSendPair( apDescriptor, "SERVER_ID", MUD_NAME ); - } - } - else if ( aCmd == (char)WONT ) - pProtocol->bATCP = false; - else /* Anything else is invalid. */ - bResult = false; - break; - - default: - bResult = false; - } -} - -static void PerformSubnegotiation( descriptor_t *apDescriptor, char aCmd, char *apData, int aSize ) -{ - protocol_t *pProtocol = apDescriptor->pProtocol; - - switch ( aCmd ) - { - case (char)TELOPT_TTYPE: - if ( pProtocol->bTTYPE ) - { - /* Store the client name. */ - const int MaxClientLength = 64; - char ClientName[MaxClientLength+1]; - int i = 0, j = 1; - - for ( ; apData[j] != '\0' && i < MaxClientLength; ++j ) - { - if ( isprint(apData[j]) ) - ClientName[i++] = apData[j]; - } - ClientName[i] = '\0'; - - /* Store the first TTYPE as the client name */ - if ( !strcmp(pProtocol->pVariables[eMSDP_CLIENT_ID]->pValueString, "Unknown") ) - { - free(pProtocol->pVariables[eMSDP_CLIENT_ID]->pValueString); - pProtocol->pVariables[eMSDP_CLIENT_ID]->pValueString = strdup(ClientName); - } - - /* Cycle through the TTYPEs until we get the same result twice, or - * find ourselves back at the start. - * - * If the client follows RFC1091 properly then it will indicate the - * end of the list by repeating the last response, and then return - * to the top of the list. If you're the trusting type, then feel - * free to remove the second strcmp ;) - */ - if ( pProtocol->pLastTTYPE == NULL || - (strcmp(pProtocol->pLastTTYPE, ClientName) && - strcmp(pProtocol->pVariables[eMSDP_CLIENT_ID]->pValueString, ClientName)) ) - { - char RequestTTYPE [] = { (char)IAC, (char)SB, TELOPT_TTYPE, TELOPT_SEND, (char)IAC, (char)SE, '\0' }; - const char *pStartPos = strstr( ClientName, "-" ); - - /* Store the TTYPE */ - free(pProtocol->pLastTTYPE); - pProtocol->pLastTTYPE = strdup(ClientName); - - /* Look for 256 colour support */ - if ( pStartPos != NULL && MatchString(pStartPos, "-256color") ) - { - /* This is currently the only way to detect support for 256 - * colours in TinTin++, WinTin++ and BlowTorch. - */ - pProtocol->pVariables[eMSDP_XTERM_256_COLORS]->ValueInt = 1; - pProtocol->b256Support = eYES; - } - - /* Request another TTYPE */ - Write(apDescriptor, RequestTTYPE); - } - - if ( PrefixString("Mudlet", ClientName) ) - { - /* Mudlet beta 15 and later supports 256 colours, but we can't - * identify it from the mud - everything prior to 1.1 claims - * to be version 1.0, so we just don't know. - */ - pProtocol->b256Support = eSOMETIMES; - - if ( strlen(ClientName) > 7 ) - { - ClientName[6] = '\0'; - free(pProtocol->pVariables[eMSDP_CLIENT_ID]->pValueString); - pProtocol->pVariables[eMSDP_CLIENT_ID]->pValueString = strdup(ClientName); - free(pProtocol->pVariables[eMSDP_CLIENT_VERSION]->pValueString); - pProtocol->pVariables[eMSDP_CLIENT_VERSION]->pValueString = strdup(ClientName+7); - - /* Mudlet 1.1 and later supports 256 colours. */ - if ( strcmp(pProtocol->pVariables[eMSDP_CLIENT_VERSION]->pValueString, "1.1") >= 0 ) - { - pProtocol->pVariables[eMSDP_XTERM_256_COLORS]->ValueInt = 1; - pProtocol->b256Support = eYES; - } - } - } - else if ( MatchString(ClientName, "EMACS-RINZAI") ) - { - /* We know for certain that this client has support */ - pProtocol->pVariables[eMSDP_XTERM_256_COLORS]->ValueInt = 1; - pProtocol->b256Support = eYES; - } - else if ( MatchString(ClientName, "MUSHCLIENT") || - MatchString(ClientName, "CMUD") || - MatchString(ClientName, "ATLANTIS") || - MatchString(ClientName, "KILDCLIENT") || - MatchString(ClientName, "TINTIN++") || - MatchString(ClientName, "TINYFUGUE") ) - { - /* We know that some versions of this client have support */ - pProtocol->b256Support = eSOMETIMES; - } - else if ( MatchString(ClientName, "ZMUD") ) - { - /* We know for certain that this client does not have support */ - pProtocol->b256Support = eNO; - } - } - break; - - case (char)TELOPT_NAWS: - if ( pProtocol->bNAWS ) - { - /* Store the new width. */ - pProtocol->ScreenWidth = (unsigned char)apData[0]; - pProtocol->ScreenWidth <<= 8; - pProtocol->ScreenWidth += (unsigned char)apData[1]; - - /* Store the new height. */ - pProtocol->ScreenHeight = (unsigned char)apData[2]; - pProtocol->ScreenHeight <<= 8; - pProtocol->ScreenHeight += (unsigned char)apData[3]; - } - break; - - case (char)TELOPT_CHARSET: - if ( pProtocol->bCHARSET ) - { - /* Because we're only asking about UTF-8, we can just check the - * first character. If you ask for more than one CHARSET you'll - * need to read through the results to see which are accepted. - * - * Note that the user must also use a unicode font! - */ - if ( apData[0] == TELOPT_ACCEPTED ) - pProtocol->pVariables[eMSDP_UTF_8]->ValueInt = 1; - } - break; - - case (char)TELOPT_MSDP: - ParseMSDP( apDescriptor, apData ); - break; - - case (char)TELOPT_ATCP: - ParseATCP( apDescriptor, apData ); - break; - - default: /* Unknown subnegotiation, so we simply ignore it. */ - break; - } -} - -/****************************************************************************** - Local MSDP functions. - ******************************************************************************/ - -static void ParseMSDP( descriptor_t *apDescriptor, const char *apData ) -{ - char Variable[MSDP_VAL][MAX_MSDP_SIZE+1] = { {'\0'}, {'\0'} }; - char *pPos = NULL, *pStart = NULL; - - while ( *apData ) - { - switch ( *apData ) - { - case MSDP_VAR: case MSDP_VAL: - pPos = pStart = Variable[*apData++-1]; - break; - default: /* Anything else */ - if ( pPos && pPos-pStart < MAX_MSDP_SIZE ) - { - *pPos++ = *apData; - *pPos = '\0'; - } - - if ( *++apData ) - continue; - } - - ExecuteMSDPPair( apDescriptor, Variable[MSDP_VAR-1], Variable[MSDP_VAL-1] ); - Variable[MSDP_VAL-1][0] = '\0'; - } -} - -static void ExecuteMSDPPair( descriptor_t *apDescriptor, const char *apVariable, const char *apValue ) -{ - if ( apVariable[0] != '\0' && apValue[0] != '\0' ) - { - if ( MatchString(apVariable, "SEND") ) - { - bool_t bDone = false; - int i; /* Loop counter */ - for ( i = eMSDP_NONE+1; i < eMSDP_MAX && !bDone; ++i ) - { - if ( MatchString(apValue, VariableNameTable[i].pName) ) - { - MSDPSend( apDescriptor, (variable_t)i ); - bDone = true; - } - } - } - else if ( MatchString(apVariable, "REPORT") ) - { - bool_t bDone = false; - int i; /* Loop counter */ - for ( i = eMSDP_NONE+1; i < eMSDP_MAX && !bDone; ++i ) - { - if ( MatchString(apValue, VariableNameTable[i].pName) ) - { - apDescriptor->pProtocol->pVariables[i]->bReport = true; - apDescriptor->pProtocol->pVariables[i]->bDirty = true; - bDone = true; - } - } - } - else if ( MatchString(apVariable, "LIST") ) - { - if ( MatchString(apValue, "COMMANDS") ) - { - char MSDPCommands[] = "LIST REPORT SEND"; - - if ( apDescriptor->pProtocol->bMSDP ) - { - int i; /* Loop counter */ - for ( i = 0; MSDPCommands[i] != '\0'; ++i ) - { - if ( MSDPCommands[i] == ' ' ) - MSDPCommands[i] = MSDP_VAL; - } - } - - MSDPSendPair( apDescriptor, "COMMANDS", MSDPCommands ); - } - else if ( MatchString(apValue, "LISTS") ) - { - char MSDPCommands[] = "COMMANDS LISTS VARIABLES CONFIGURABLE_VARIABLES REPORTABLE_VARIABLES GUI_VARIABLES"; - - if ( apDescriptor->pProtocol->bMSDP ) - { - int i; /* Loop counter */ - for ( i = 0; MSDPCommands[i] != '\0'; ++i ) - { - if ( MSDPCommands[i] == ' ' ) - MSDPCommands[i] = MSDP_VAL; - } - } - - MSDPSendPair( apDescriptor, "LISTS", MSDPCommands ); - } - /* Split this into two if some variables aren't REPORTABLE */ - else if ( MatchString(apValue, "VARIABLES") || - MatchString(apValue, "REPORTABLE_VARIABLES") ) - { - const char Separator[] = { apDescriptor->pProtocol->bMSDP ? MSDP_VAL : ' ', '\0' }; - char MSDPCommands[MAX_OUTPUT_BUFFER] = { '\0' }; - int i; /* Loop counter */ - - for ( i = eMSDP_NONE+1; i < eMSDP_MAX; ++i ) - { - if ( !VariableNameTable[i].bGUI ) - { - /* Add the separator between variables */ - if ( MSDPCommands[0] != '\0' ) - { - if ( apDescriptor->pProtocol->bMSDP ) - strcat( MSDPCommands, Separator ); - } - - /* Add the variable to the list */ - strcat( MSDPCommands, VariableNameTable[i].pName ); - } - } - - MSDPSendPair( apDescriptor, apValue, MSDPCommands ); - } - else if ( MatchString(apValue, "CONFIGURABLE_VARIABLES") ) - { - const char Separator[] = { apDescriptor->pProtocol->bMSDP ? MSDP_VAL : ' ', '\0' }; - char MSDPCommands[MAX_OUTPUT_BUFFER] = { '\0' }; - int i; /* Loop counter */ - - for ( i = eMSDP_NONE+1; i < eMSDP_MAX; ++i ) - { - if ( VariableNameTable[i].bConfigurable ) - { - /* Add the separator between variables */ - if ( MSDPCommands[0] != '\0' ) - { - if ( apDescriptor->pProtocol->bMSDP ) - strcat( MSDPCommands, Separator ); - } - - /* Add the variable to the list */ - strcat( MSDPCommands, VariableNameTable[i].pName ); - } - } - - MSDPSendPair( apDescriptor, "CONFIGURABLE_VARIABLES", MSDPCommands ); - } - else if ( MatchString(apValue, "GUI_VARIABLES") ) - { - const char Separator[] = { apDescriptor->pProtocol->bMSDP ? MSDP_VAL : ' ', '\0' }; - char MSDPCommands[MAX_OUTPUT_BUFFER] = { '\0' }; - int i; /* Loop counter */ - - for ( i = eMSDP_NONE+1; i < eMSDP_MAX; ++i ) - { - if ( VariableNameTable[i].bGUI ) - { - /* Add the separator between variables */ - if ( MSDPCommands[0] != '\0' ) - { - if ( apDescriptor->pProtocol->bMSDP ) - strcat( MSDPCommands, Separator ); - } - - /* Add the variable to the list */ - strcat( MSDPCommands, VariableNameTable[i].pName ); - } - } - - MSDPSendPair( apDescriptor, apValue, MSDPCommands ); - } - } - else /* Set any configurable variables */ - { - int i; /* Loop counter */ - - for ( i = eMSDP_NONE+1; i < eMSDP_MAX; ++i ) - { - if ( VariableNameTable[i].bConfigurable ) - { - if ( MatchString(apVariable, VariableNameTable[i].pName) ) - { - if ( VariableNameTable[i].bString ) - { - /* A write-once variable can only be set if the value - * is "Unknown". This is for things like client name, - * where we don't really want the player overwriting a - * proper client name with junk - but on the other hand, - * its possible a client may choose to use MSDP to - * identify itself. - */ - if ( !VariableNameTable[i].bWriteOnce || - !strcmp(apDescriptor->pProtocol->pVariables[i]->pValueString, "Unknown") ) - { - /* Store the new value if it's valid */ - char Buffer[VariableNameTable[i].Max+1]; - int j; /* Loop counter */ - - for ( j = 0; j < VariableNameTable[i].Max && *apValue != '\0'; ++apValue ) - { - if ( isprint(*apValue) ) - Buffer[j++] = *apValue; - } - Buffer[j++] = '\0'; - - if ( j >= VariableNameTable[i].Min ) - { - free(apDescriptor->pProtocol->pVariables[i]->pValueString); - apDescriptor->pProtocol->pVariables[i]->pValueString = strdup(Buffer); - } - } - } - else /* This variable only accepts numeric values */ - { - /* Strip any leading spaces */ - while ( *apValue == ' ' ) - ++apValue; - - if ( *apValue != '\0' && IsNumber(apValue) ) - { - int Value = atoi(apValue); - if ( Value >= VariableNameTable[i].Min && - Value <= VariableNameTable[i].Max ) - { - apDescriptor->pProtocol->pVariables[i]->ValueInt = Value; - } - } - } - } - } - } - } - } -} - -/****************************************************************************** - Local ATCP functions. - ******************************************************************************/ - -static void ParseATCP( descriptor_t *apDescriptor, const char *apData ) -{ - char Variable[MSDP_VAL][MAX_MSDP_SIZE+1] = { {'\0'}, {'\0'} }; - char *pPos = NULL, *pStart = NULL; - - while ( *apData ) - { - switch ( *apData ) - { - case '@': - pPos = pStart = Variable[0]; - apData++; - break; - case ' ': - pPos = pStart = Variable[1]; - apData++; - break; - default: /* Anything else */ - if ( pPos && pPos-pStart < MAX_MSDP_SIZE ) - { - *pPos++ = *apData; - *pPos = '\0'; - } - - if ( *++apData ) - continue; - } - - ExecuteMSDPPair( apDescriptor, Variable[MSDP_VAR-1], Variable[MSDP_VAL-1] ); - Variable[MSDP_VAL-1][0] = '\0'; - } -} - -/****************************************************************************** - Local MSSP functions. - ******************************************************************************/ - -#define MAX_MSSP_BUFFER 4096 -static void SendMSSP( descriptor_t *apDescriptor ) -{ - char MSSPBuffer[MAX_MSSP_BUFFER]; - char MSSPPair[128]; - int SizeBuffer = 3; /* IAC SB MSSP */ - int i; /* Loop counter */ - - char VarPlayers[32], VarUptime[32]; - sprintf( VarPlayers, "%d", s_Players ); - sprintf( VarUptime, "%d", (int)s_Uptime ); - - /* Before updating the following table, please read the MSSP specification: - * - * http://tintin.sourceforge.net/mssp/ - * - * It's important that you use the correct format and spelling for the MSSP - * variables, otherwise crawlers may reject the data as invalid. - */ - MSSP_t MSSPTable[] = - { - /* Required */ - { "NAME", MUD_NAME }, /* Change this in protocol.h */ - { "PLAYERS", VarPlayers }, /* Automatically calculated */ - { "UPTIME" , VarUptime }, /* Automatically calculated */ - - /* Generic */ - { "CRAWL DELAY", "-1" }, -/* - { "HOSTNAME", "" }, - { "PORT", "" }, - { "CODEBASE", "" }, - { "CONTACT", "" }, - { "CREATED", "" }, - { "ICON", "" }, - { "IP", "" }, - { "LANGUAGE", "" }, - { "LOCATION", "" }, - { "MINIMUM AGE", "" }, - { "WEBSITE", "" }, -*/ - /* Categorisation */ -/* - { "FAMILY", "" }, - { "GENRE", "" }, - { "GAMEPLAY", "" }, - { "STATUS", "" }, - { "GAMESYSTEM", "" }, - { "INTERMUD", "" }, - { "SUBGENRE", "" }, -*/ - /* World */ -/* - { "AREAS", "0" }, - { "HELPFILES", "0" }, - { "MOBILES", "0" }, - { "OBJECTS", "0" }, - { "ROOMS", "0" }, - { "CLASSES", "0" }, - { "LEVELS", "0" }, - { "RACES", "0" }, - { "SKILLS", "0" }, -*/ - /* Protocols */ -/* - { "ANSI", "1" }, - { "MCCP", "0" }, - { "MCP", "0" }, - { "MSP", "1" }, - { "MXP", "1" }, - { "PUEBLO", "0" }, - { "VT100", "0" }, - { "XTERM 256 COLORS", "1" }, -*/ - /* Commercial */ -/* - { "PAY TO PLAY", "0" }, - { "PAY FOR PERKS", "0" }, -*/ - /* Hiring */ -/* - { "HIRING BUILDERS", "0" }, - { "HIRING CODERS", "0" }, -*/ - /* Extended variables */ - - /* World */ -/* - { "DBSIZE", "0" }, - { "EXITS", "0" }, - { "EXTRA DESCRIPTIONS", "0" }, - { "MUDPROGS", "0" }, - { "MUDTRIGS", "0" }, - { "RESETS", "0" }, -*/ - /* Game */ -/* - { "ADULT MATERIAL", "0" }, - { "MULTICLASSING", "0" }, - { "NEWBIE FRIENDLY", "0" }, - { "PLAYER CITIES", "0" }, - { "PLAYER CLANS", "0" }, - { "PLAYER CRAFTING", "0" }, - { "PLAYER GUILDS", "0" }, - { "EQUIPMENT SYSTEM", "" }, - { "MULTIPLAYING", "" }, - { "PLAYERKILLING", "" }, - { "QUEST SYSTEM", "" }, - { "ROLEPLAYING", "" }, - { "TRAINING SYSTEM", "" }, - { "WORLD ORIGINALITY", "" }, -*/ - /* Protocols */ -/* - { "ATCP", "1" }, - { "MSDP", "1" }, - { "SSL", "0" }, - { "UTF-8", "1" }, - { "ZMP", "0" }, -*/ - { NULL, NULL } /* This must always be last. */ - }; - - /* Begin the subnegotiation sequence */ - sprintf( MSSPBuffer, "%c%c%c", IAC, SB, TELOPT_MSSP ); - - for ( i = 0; MSSPTable[i].pName != NULL; ++i ) - { - int SizePair; - - /* Retrieve the next MSSP variable/value pair */ - sprintf( MSSPPair, "%c%s%c%s", MSSP_VAR, MSSPTable[i].pName, - MSSP_VAL, MSSPTable[i].pValue ); - - /* Make sure we don't overflow the buffer */ - SizePair = strlen(MSSPPair); - if ( SizePair+SizeBuffer < MAX_MSSP_BUFFER-4 ) - { - strcat( MSSPBuffer, MSSPPair ); - SizeBuffer += SizePair; - } - } - - /* End the subnegotiation sequence */ - sprintf( MSSPPair, "%c%c", IAC, SE ); - strcat( MSSPBuffer, MSSPPair ); - - /* Send the sequence */ - Write( apDescriptor, MSSPBuffer ); -} - -/****************************************************************************** - Local MXP functions. - ******************************************************************************/ - -static char *GetMxpTag( const char *apTag, const char *apText ) -{ - static char MXPBuffer [64]; - const char *pStartPos = strstr(apText, apTag); - - if ( pStartPos != NULL ) - { - const char *pEndPos = apText+strlen(apText); - - pStartPos += strlen(apTag); /* Add length of the tag */ - - if ( pStartPos < pEndPos ) - { - int Index = 0; - - /* Some clients use quotes...and some don't. */ - if ( *pStartPos == '\"' ) - pStartPos++; - - for ( ; pStartPos < pEndPos && Index < 60; ++pStartPos ) - { - char Letter = *pStartPos; - if ( Letter == '.' || isdigit(Letter) || isalpha(Letter) ) - { - MXPBuffer[Index++] = Letter; - } - else /* Return the result */ - { - MXPBuffer[Index] = '\0'; - return MXPBuffer; - } - } - } - } - - /* Return NULL to indicate no tag was found. */ - return NULL; -} - -/****************************************************************************** - Local colour functions. - ******************************************************************************/ - -static const char *GetAnsiColour( bool_t abBackground, int aRed, int aGreen, int aBlue ) -{ - if ( aRed == aGreen && aRed == aBlue && aRed < 2) - return abBackground ? s_BackBlack : aRed >= 1 ? s_BoldBlack : s_DarkBlack; - else if ( aRed == aGreen && aRed == aBlue ) - return abBackground ? s_BackWhite : aRed >= 4 ? s_BoldWhite : s_DarkWhite; - else if ( aRed > aGreen && aRed > aBlue ) - return abBackground ? s_BackRed : aRed >= 3 ? s_BoldRed : s_DarkRed; - else if ( aRed == aGreen && aRed > aBlue ) - return abBackground ? s_BackYellow : aRed >= 3 ? s_BoldYellow : s_DarkYellow; - else if ( aRed == aBlue && aRed > aGreen ) - return abBackground ? s_BackMagenta : aRed >= 3 ? s_BoldMagenta : s_DarkMagenta; - else if ( aGreen > aBlue ) - return abBackground ? s_BackGreen : aGreen >= 3 ? s_BoldGreen : s_DarkGreen; - else if ( aGreen == aBlue ) - return abBackground ? s_BackCyan : aGreen >= 3 ? s_BoldCyan : s_DarkCyan; - else /* aBlue is the highest */ - return abBackground ? s_BackBlue : aBlue >= 3 ? s_BoldBlue : s_DarkBlue; -} - -static const char *GetRGBColour( bool_t abBackground, int aRed, int aGreen, int aBlue ) -{ - static char Result[16]; - int ColVal = 16 + (aRed * 36) + (aGreen * 6) + aBlue; - sprintf( Result, "\033[%c8;5;%c%c%cm", - '3'+abBackground, /* Background */ - '0'+(ColVal/100), /* Red */ - '0'+((ColVal%100)/10), /* Green */ - '0'+(ColVal%10) ); /* Blue */ - return Result; -} - -static bool_t IsValidColour( const char *apArgument ) -{ - int i; /* Loop counter */ - - /* The sequence is 4 bytes, but we can ignore anything after it. */ - if ( apArgument == NULL || strlen(apArgument) < 4 ) - return false; - - /* The first byte indicates foreground/background. */ - if ( tolower(apArgument[0]) != 'f' && tolower(apArgument[0]) != 'b' ) - return false; - - /* The remaining three bytes must each be in the range '0' to '5'. */ - for ( i = 1; i <= 3; ++i ) - { - if ( apArgument[i] < '0' || apArgument[i] > '5' ) - return false; - } - - /* It's a valid foreground or background colour */ - return true; -} - -/****************************************************************************** - Other local functions. - ******************************************************************************/ - -static bool_t MatchString( const char *apFirst, const char *apSecond ) -{ - while ( *apFirst && tolower(*apFirst) == tolower(*apSecond) ) - ++apFirst, ++apSecond; - return ( !*apFirst && !*apSecond ); -} - -static bool_t PrefixString( const char *apPart, const char *apWhole ) -{ - while ( *apPart && *apPart == *apWhole ) - ++apPart, ++apWhole; - return ( !*apPart ); -} - -static bool_t IsNumber( const char *apString ) -{ - while ( *apString && isdigit(*apString) ) - ++apString; - return ( !*apString ); -} diff --git a/src/protocol.h b/src/protocol.h deleted file mode 100755 index 09116e2..0000000 --- a/src/protocol.h +++ /dev/null @@ -1,453 +0,0 @@ -/************************************************************************** -* File: protocol.h Part of tbaMUD * -* Usage: MXP, MSDP, ATCP, NAWS, TTYPE, CHARSET, 256-Colors * -* Author: KaVir * -* * -* All rights reserved. See license.doc for complete information. * -* * -* CircleMUD is based on DikuMUD, Copyright (C) 1990, 1991. * -**************************************************************************/ - -#ifndef PROTOCOL_H -#define PROTOCOL_H - -/****************************************************************************** - Set your MUD_NAME, and change descriptor_t if necessary. - ******************************************************************************/ - -#define MUD_NAME "Unknown MUD" - -typedef struct descriptor_data descriptor_t; - -/****************************************************************************** - Symbolic constants. - ******************************************************************************/ - -#define PROTOCOL_BUFFER 2048 -#define MAX_OUTPUT_BUFFER 8192 - -#define TELOPT_SEND 1 -#define TELOPT_ACCEPTED 2 -#define TELOPT_REJECTED 3 - -#define TELOPT_TTYPE 24 -#define TELOPT_NAWS 31 -#define TELOPT_CHARSET 42 -#define TELOPT_MSDP 69 -#define TELOPT_MSSP 70 -#define TELOPT_MCCP 86 /* This is MCCP version 2 */ -#define TELOPT_MSP 90 -#define TELOPT_MXP 91 -#define TELOPT_ATCP 200 - -#define MSDP_VAR 1 -#define MSDP_VAL 2 -#define MAX_MSDP_SIZE 100 - -#define MSSP_VAR 1 -#define MSSP_VAL 2 - -#define UNICODE_MALE 9794 -#define UNICODE_FEMALE 9792 -#define UNICODE_NEUTER 9791 - -/****************************************************************************** - Types. - ******************************************************************************/ - -typedef enum -{ - false, - true -} bool_t; - -typedef enum -{ - eUNKNOWN, - eNO, - eSOMETIMES, - eYES -} support_t; - -typedef enum -{ - eMSDP_NONE = -1, /* This must always be first. */ - - /* General */ - eMSDP_CHARACTER_NAME, - eMSDP_SERVER_ID, - eMSDP_SERVER_TIME, - - /* Character */ - eMSDP_AFFECTS, - eMSDP_ALIGNMENT, - eMSDP_EXPERIENCE, - eMSDP_EXPERIENCE_MAX, - eMSDP_EXPERIENCE_TNL, - eMSDP_HEALTH, - eMSDP_HEALTH_MAX, - eMSDP_LEVEL, - eMSDP_RACE, - eMSDP_CLASS, - eMSDP_MANA, - eMSDP_MANA_MAX, - eMSDP_WIMPY, - eMSDP_PRACTICE, - eMSDP_MONEY, - eMSDP_MOVEMENT, - eMSDP_MOVEMENT_MAX, - eMSDP_HITROLL, - eMSDP_DAMROLL, - eMSDP_AC, - eMSDP_STR, - eMSDP_INT, - eMSDP_WIS, - eMSDP_DEX, - eMSDP_CON, - eMSDP_STR_PERM, - eMSDP_INT_PERM, - eMSDP_WIS_PERM, - eMSDP_DEX_PERM, - eMSDP_CON_PERM, - - /* Combat */ - eMSDP_OPPONENT_HEALTH, - eMSDP_OPPONENT_HEALTH_MAX, - eMSDP_OPPONENT_LEVEL, - eMSDP_OPPONENT_NAME, - - /* World */ - eMSDP_AREA_NAME, - eMSDP_ROOM_EXITS, - eMSDP_ROOM_NAME, - eMSDP_ROOM_VNUM, - eMSDP_WORLD_TIME, - - /* Configuration */ - eMSDP_CLIENT_ID, - eMSDP_CLIENT_VERSION, - eMSDP_PLUGIN_ID, - eMSDP_ANSI_COLORS, - eMSDP_XTERM_256_COLORS, - eMSDP_UTF_8, - eMSDP_SOUND, - eMSDP_MXP, - - /* GUI variables */ - eMSDP_BUTTON_1, - eMSDP_BUTTON_2, - eMSDP_BUTTON_3, - eMSDP_BUTTON_4, - eMSDP_BUTTON_5, - eMSDP_GAUGE_1, - eMSDP_GAUGE_2, - eMSDP_GAUGE_3, - eMSDP_GAUGE_4, - eMSDP_GAUGE_5, - - eMSDP_MAX /* This must always be last */ -} variable_t; - -typedef struct -{ - variable_t Variable; /* The enum type of this variable */ - char *pName; /* The string name of this variable */ - bool_t bString; /* Is this variable a string or a number? */ - bool_t bConfigurable; /* Can it be configured by the client? */ - bool_t bWriteOnce; /* Can only set this variable once */ - bool_t bGUI; /* It's a special GUI configuration variable */ - int Min; /* The minimum valid value or string length */ - int Max; /* The maximum valid value or string length */ - int Default; /* The default value for a number */ - const char *pDefault; /* The default value for a string */ -} variable_name_t; - -typedef struct -{ - bool_t bReport; /* Is this variable being reported? */ - bool_t bDirty; /* Does this variable need to be sent again? */ - int ValueInt; /* The numeric value of the variable */ - char *pValueString; /* The string value of the variable */ -} MSDP_t; - -typedef struct -{ - const char *pName; /* The name of the MSSP variable */ - const char *pValue; /* The value of the MSSP variable */ -} MSSP_t; - -typedef struct -{ - int WriteOOB; /* Used internally to indicate OOB data */ - bool_t bNegotiated; /* Indicates client successfully negotiated */ - bool_t bBlockMXP; /* Used internally based on MXP version */ - bool_t bTTYPE; /* The client supports TTYPE */ - bool_t bNAWS; /* The client supports NAWS */ - bool_t bCHARSET; /* The client supports CHARSET */ - bool_t bMSDP; /* The client supports MSDP */ - bool_t bATCP; /* The client supports ATCP */ - bool_t bMSP; /* The client supports MSP */ - bool_t bMXP; /* The client supports MXP */ - support_t b256Support; /* The client supports XTerm 256 colors */ - int ScreenWidth; /* The client's screen width */ - int ScreenHeight; /* The client's screen height */ - char *pMXPVersion; /* The version of MXP supported */ - char *pLastTTYPE; /* Used for the cyclic TTYPE check */ - MSDP_t **pVariables; /* The MSDP variables */ -} protocol_t; - -/****************************************************************************** - Protocol functions. - ******************************************************************************/ - -/* Function: ProtocolCreate - * - * Creates, initialises and returns a structure containing protocol data for a - * single user. This should be called when the descriptor is initialised. - */ -protocol_t *ProtocolCreate( void ); - -/* Function: ProtocolDestroy - * - * Frees the memory allocated by the specified structure. This should be - * called just before a descriptor is freed. - */ -void ProtocolDestroy( protocol_t *apProtocol ); - -/* Function: ProtocolNegotiate - * - * Negatiates with the client to see which protocols the user supports, and - * stores the results in the user's protocol structure. Call this when you - * wish to perform negotiation (but only call it once). It is usually called - * either immediately after the user has connected, or just after they have - * entered the game. - */ -void ProtocolNegotiate( descriptor_t *apDescriptor ); - -/* Function: ProtocolInput - * - * Extracts any negotiation sequences from the input buffer, and passes back - * whatever is left for the mud to parse normally. Call this after data has - * been read into the input buffer, before it is used for anything else. - */ -void ProtocolInput( descriptor_t *apDescriptor, char *apData, int aSize, char *apOut ); - -/* Function: ProtocolOutput - * - * This function takes a string, applies colour codes to it, and returns the - * result. It should be called just before writing to the output buffer. - * - * The special character used to indicate the start of a colour sequence is - * '\t' (i.e., a tab, or ASCII character 9). This makes it easy to include - * in help files (as you can literally press the tab key) as well as strings - * (where you can use \t instead). However players can't send tabs (on most - * muds at least), so this stops them from sending colour codes to each other. - * - * The predefined colours are: - * - * n: no colour (switches colour off) - * r: dark red R: bright red - * g: dark green G: bright green - * b: dark blue B: bright blue - * y: dark yellow Y: bright yellow - * m: dark magenta M: bright magenta - * c: dark cyan C: bright cyan - * w: dark white W: bright white - * o: dark orange O: bright orange - * - * So for example "This is \tOorange\tn." will colour the word "orange". You - * can add more colours yourself just by updating the switch statement. - * - * It's also possible to explicitly specify an RGB value, by including the four - * character colour sequence (as used by ColourRGB) within square brackets, eg: - * - * This is a \t[F010]very dark green foreground\tn. - * - * The square brackets can also be used to send unicode characters, like this: - * - * Boat: \t[U9973/B] - * Rook: \t[U9814/C] - * - * For example you might use 'B' to represent a boat on your ASCII map, or a 'C' - * to represent a castle - but players with UTF-8 support would actually see the - * appropriate unicode characters for a boat or a rook (the chess playing piece). - * - * The exact syntax is '\t' (tab), '[', 'U' (indicating unicode), then the decimal - * number of the unicode character (see http://www.unicode.org/charts), then '/' - * followed by the ASCII character/s that should be used if the client doesn't - * support UTF-8. The ASCII sequence can be up to 7 characters in length, but in - * most cases you'll only want it to be one or two characters (so that it has the - * same alignment as the unicode character). - * - * Finally, this function also allows you to embed MXP tags. The easiest and - * safest way to do this is via the ( and ) bracket options: - * - * From here, you can walk \t(north\t). - * - * However it's also possible to include more explicit MSP tags, like this: - * - * The baker offers to sell you a \tpie\t. - * - * Note that the MXP tags will automatically be removed if the user doesn't - * support MXP, but it's very important you remember to close the tags. - */ -const char *ProtocolOutput( descriptor_t *apDescriptor, const char *apData, int *apLength ); - -/****************************************************************************** - Copyover save/load functions. - ******************************************************************************/ - -/* Function: CopyoverGet - * - * Returns the protocol values stored as a short string. If your mud uses - * copyover, you should call this for each player and insert it after their - * name in the temporary text file. - */ -const char *CopyoverGet( descriptor_t *apDescriptor ); - -/* Function: CopyoverSet - * - * Call this function for each player after a copyover, passing in the string - * you added to the temporary text file. This will restore their protocol - * settings, and automatically renegotiate MSDP/ATCP. - * - * Note that the client doesn't recognise a copyover, and therefore refuses to - * renegotiate certain telnet options (to avoid loops), so they really need to - * be saved. However MSDP/ATCP is handled through scripts, and we don't want - * to have to save all of the REPORT variables, so it's easier to renegotiate. - * - * Client name and version are not saved. It is recommended you save these in - * the player file, as then you can grep to collect client usage stats. - */ -void CopyoverSet( descriptor_t *apDescriptor, const char *apData ); - -/****************************************************************************** - MSDP functions. - ******************************************************************************/ - -/* Function: MSDPUpdate - * - * Call this regularly (I'd suggest at least once per second) to flush every - * dirty MSDP variable that has been requested by the client via REPORT. This - * will automatically use ATCP instead if MSDP is not supported by the client. - */ -void MSDPUpdate( descriptor_t *apDescriptor ); - -/* Function: MSDPSend - * - * Send the specified MSDP variable to the player. You shouldn't ever really - * need to do this manually, except perhaps when debugging something. This - * will automatically use ATCP instead if MSDP is not supported by the client. - */ -void MSDPSend( descriptor_t *apDescriptor, variable_t aMSDP ); - -/* Function: MSDPSendPair - * - * Send the specified strings to the user as an MSDP variable/value pair. This - * will automatically use ATCP instead if MSDP is not supported by the client. - */ -void MSDPSendPair( descriptor_t *apDescriptor, const char *apVariable, const char *apValue ); - -/* Function: MSDPSetNumber - * - * Call this whenever an MSDP integer variable has changed. The easiest - * approach is to send every MSDP variable within an update function (and - * this is what the snippet does by default), but if the variable is only - * set in one place you can just move its MDSPSend() call to there. - * - * You can also this function for bools, chars, enums, short ints, etc. - */ -void MSDPSetNumber( descriptor_t *apDescriptor, variable_t aMSDP, int aValue ); - -/* Function: MSDPSetString - * - * Call this whenever an MSDP string variable has changed. The easiest - * approach is to send every MSDP variable within an update function (and - * this is what the snippet does by default), but if the variable is only - * set in one place you can just move its MDSPSend() call to there. - */ -void MSDPSetString( descriptor_t *apDescriptor, variable_t aMSDP, char *apValue ); - -/****************************************************************************** - MSSP functions. - ******************************************************************************/ - -/* Function: MSSPSetPlayers - * - * Stores the current number of players. The first time it's called, it also - * stores the uptime. - */ -void MSSPSetPlayers( int aPlayers ); - -/****************************************************************************** - MXP functions. - ******************************************************************************/ - -/* Function: MXPCreateTag - * - * Puts the specified tag into a secure line, if MXP is supported. If the user - * doesn't support MXP they will see the string unchanged, meaning they will - * see the tags or whatever. You should therefore check for support and - * provide a different sequence for other users, or better yet just embed MXP - * tags for the ProtocolOutput() function. - */ -char *MXPCreateTag( descriptor_t *apDescriptor, char *apTag ); - -/* Function: MXPSendTag - * - * This works like MXPCreateTag, but instead of returning the string it sends - * it directly to the user. This is mainly useful for the tag. - */ -void MXPSendTag( descriptor_t *apDescriptor, char *apTag ); - -/****************************************************************************** - Sound functions. - ******************************************************************************/ - -/* Function: SoundSend - * - * Sends the specified sound trigger to the player, using MSDP or ATCP if - * supported, MSP if not. The trigger string itself is a relative path and - * filename, eg: SoundSend( pDesc, "monster/growl.wav" ); - */ -void SoundSend( descriptor_t *apDescriptor, const char *apTrigger ); - -/****************************************************************************** - Colour functions. - ******************************************************************************/ - -/* Function: ColourRGB - * - * Returns a colour as an escape code, based on the RGB value provided. The - * string must be four characters, where the first is either 'f' for foreground - * or 'b' for background (case insensitive), and the other three characters are - * numeric digits in the range 0 to 5, representing red, green and blue. - * - * For example "F500" returns a red foreground, "B530" an orange background, - * and so on. An invalid colour will clear whatever you've set previously. - * - * If the user doesn't support XTerm 256 colours, this function will return the - * best-fit ANSI colour instead. - * - * If you wish to embed colours in strings, use ProtocolOutput(). - */ -const char *ColourRGB( descriptor_t *apDescriptor, const char *apRGB ); - -/****************************************************************************** - Unicode (UTF-8 conversion) functions. - ******************************************************************************/ - -/* Function: UnicodeGet - * - * Returns the UTF-8 sequence for the specified unicode value. - */ -char *UnicodeGet( int aValue ); - -/* Function: UnicodeAdd - * - * Adds the UTF-8 sequence for the specified unicode value onto the end of the - * string, without adding a NUL character at the end. - */ -void UnicodeAdd( char **apString, int aValue ); - -#endif // PROTOCOL_H