/* -*- LPC -*- */ /* * $Id: report_base.c,v 1.44 2003/07/08 07:12:18 pinkfish Exp $ */ /** * This is the base object for creating typo, bug, idea etc reporting * commands from. You should set the error type of the * object in its create function. The use_last_error flag should * be set for those error report types which will need to use the last * runtime error on the player object. * @example * inherit "/cmds/report_base"; * * void create() { * ::create(); * set_error_type("TYPO"); * } /\* create() *\/ */ #include <creator.h> #include <log.h> #include <command.h> #include <spells.h> #include <user_parser.h> #include <soul.h> #include <error_handler.h> inherit "/cmds/base"; inherit "/cmds/bug_replies"; class errors { int type; string file; string error; string extra; } #define ROOM_BUG 1 #define OBJECT_BUG 2 #define RITUAL_BUG 3 #define SPELL_BUG 4 #define HELP_BUG 5 #define COMMAND_BUG 6 #define GENERAL_BUG 7 #define WEB_BUG 8 #define SYNONYMS "/doc/SYNONYMS" int bug_command(string str); int bug_spell(string str); int bug_ritual(string str); int bug_object(object * obj, string str); int bug_help(string str); int bug_man(string str); int bug_room(); int bug_general(); int bug_soul(string str); private nosave mapping _globals; private nosave string _error_type; private nosave int _use_last_error; void create() { bug_replies::create(); _globals = ([ ]); } /* create() */ /** * This sets the error type name. The error type should be one * of "TYPO", "BUG", "IDEA". * @param type the type to set * @see query_verb() */ void set_error_type(string type) { _error_type = type; } /* set_error_type() */ /** * This sets the use_last_error flag. If this flag is set to a non-zero * value then the last runtime error stored on the player object will * be attached to the error report. * @param error the new value of the last error flag */ void set_use_last_error(int error) { _use_last_error = error; } /* set_use_last_error() */ /** * This method returns the currently set value of the last error flag. * @return the current value of the last error flag */ int query_use_last_error() { return _use_last_error; } /* query_use_last_error() */ /** @ignore yes */ int bug_room() { if (environment(this_player())) { _globals[this_player()] = new (class errors, type: ROOM_BUG, error: "ROOM " + _error_type, file:file_name(environment (this_player ()))); } else { _globals[this_player()] = new (class errors, type: ROOM_BUG, error: "ROOM " + _error_type, file:"/d/mudlib/void"); } this_player()->do_edit(0, "end_of_edit"); return 1; } /* bug_room() */ int bug_special(string which) { _globals[this_player()] = new (class errors, type: GENERAL_BUG, error: "GENERAL " + _error_type, file: "/d/special/"+which+"/BugReports"); this_player()->do_edit(0, "end_of_edit"); return 1; } int bug_misc(string which) { _globals[this_player()] = new (class errors, type: GENERAL_BUG, error: "GENERAL " + _error_type, file: "/obj/"+which+"/BugReports"); this_player()->do_edit(0, "end_of_edit"); return 1; } /** @ignore yes */ int bug_general() { string dir; string *bits; string file; // // For a general bug put it in the domains base directory. // if (!environment(this_player())) { dir = "/d/am/fluff"; } else { dir = file_name(environment(this_player())); } bits = explode(dir, "/"); if (bits[0] == "d") { file = implode(bits[0..1], "/") + "/general"; } else { file = file_name(environment(this_player())); } _globals[this_player()] = new (class errors, type: GENERAL_BUG, error: "GENERAL " + _error_type, file:file); this_player()->do_edit(0, "end_of_edit"); return 1; } /* bug_general() */ /** @ignore yes */ int bug_command(string str) { int i; string dir; string file; string *bits; mixed *junk, *coms; class command cmd; class errors bing; class command_data cmd_data; class command_class cmd_class; mapping temp; bing = new (class errors); coms = ({ }); junk = actions_defined(this_player(), 0, 12); for (i = 0; i < sizeof(junk); i += 2) { if (junk[i] == str) { coms += ({ junk[i], junk[i + 1][0], junk[i + 1][1] }); } } if (sizeof(coms) > 3) { return notify_fail("More than one commands with the name \"" + str + "\" found. Please be more specific.\n"); } if (sizeof(coms)) { bing->file = function_exists((string) coms[2], (object) coms[1]); if (!(bing->file)) { bing->file = base_name((object) coms[1]); str += " (protected method, so it might not be in this file)"; } } else { cmd = new (class command, verb:str); if (CMD_D->HandleStars(cmd) && sizeof((coms = (mixed *) CMD_D->GetPaths(cmd->verb) & (mixed *) this_player()->GetSearchPath()))) { bing->file = coms[0] + "/" + cmd->verb; } else { if ((cmd_class = this_player()->query_parse_command(str))) { //tell_creator("presto", "%O\n", coms); temp = cmd_class->patterns; cmd_data = temp[keys(temp)[0]]; //tell_creator("presto", "%O\n", cmd_data->calls[0]); //tell_creator("presto", "%O\n", values(coms->patterns)); bing->file = base_name(cmd_data->calls[0]); // bing->file = base_name((values(coms->patterns))[0]->calls[0]); // bing->file = base_name(coms[0][OBJS]); } else { if ((coms = SOUL_OBJECT->query_soul_command(str))) { bing->file = "/soul/" + str[0..0] + "/" + str; } else if (_error_type == "IDEA") { //bing->file = "/soul/" + str[0..0] + "/" + str; dir = file_name(environment(this_player())); bits = explode(dir, "/"); if (bits[0] == "d") { file = implode(bits[0..1], "/") + "/general"; } else { file = file_name(environment(this_player())); } bing->file = file; } else { return notify_fail("Command " + str + " not found.\n"); } } } } bing->error = "COMMAND " + _error_type + " " + str; bing->type = COMMAND_BUG; _globals[this_player()] = bing; this_player()->do_edit(0, "end_of_edit"); return 1; } /* bug_command() */ /** @ignore yes */ int bug_help(string str) { mixed *stuff; class errors bing; string tmp; bing = new (class errors); tmp = "/cmds/player/help"->query_synonym(str); if (strlen(tmp)) str = tmp; if (str && str != "") { stuff = "/cmds/player/help"->query_help_on(str); if (!sizeof(stuff)) { // See if it is a soul command. if (!SOUL_OBJECT->query_soul_command(str)) { notify_fail("Could not find the help file '" + str + "'. If you wish " "to suggest a new command use 'idea help'\n"); return 0; } else { bing->file = "/soul/" + str; } } else { sscanf(stuff[0][0], "%*s (%s)", bing->file); } } else { bing->file = "/cmds/player/help"; } bing->error = "HELP " + _error_type + " " + str; bing->type = HELP_BUG; _globals[this_player()] = bing; this_player()->do_edit(0, "end_of_edit"); return 1; } /* bug_help() */ /** @ignore yes */ int bug_soul(string str) { class errors bing; string tmp; bing = new (class errors); if (str) { tmp = sprintf("/soul/%c/%s.s", str[0], str); if (file_size(tmp) < 1) { notify_fail("No such soul command \"" + str + "\".\n"); return 0; } else return bug_command(str); } bing->file = "/soul/ideas"; bing->error = "COMMAND " + _error_type + " " + str; bing->type = COMMAND_BUG; _globals[this_player()] = bing; this_player()->do_edit(0, "end_of_edit"); return 1; } /* bug_help() */ /** @ignore yes */ int bug_ritual(string str) { mapping junk; class errors bing; string sname; mixed* data; string file; str = this_player()->expand_nickname(str); bing = new (class errors); junk = this_player()->query_spells(); // Do a case insensative search. foreach (sname, data in junk) { if (lower_case(sname) == lower_case(str)) { if (sizeof(data) <= S_OBJECT) { // If it has a bad file name, ignore it file = file_name(environment(this_player())); } else { file = data[S_OBJECT]; } } } if (!file) { notify_fail("Ritual " + str + " not found.\n"); map_delete(_globals, this_player()); return 0; } bing->file = file; bing->error = "RITUAL " + _error_type + " " + str; bing->type = RITUAL_BUG; _globals[this_player()] = bing; this_player()->do_edit(0, "end_of_edit"); return 1; } /* bug_ritual() */ /** * This entry point is used to erroring spells without specifically saying * which one. * @param file the file path to use * @param name the name to use */ int bug_web(string url) { class errors bing; bing = new (class errors); bing->file = "/www/fluff"; bing->error = "OBJECT " + _error_type + " " + url; bing->type = WEB_BUG; _globals[this_player()] = bing; this_player()->do_edit("Url: " + url + "\n\n", "end_of_edit"); return 1; } /* bug_spell() */ /** * This entry point is used to erroring spells without specifically saying * which one. * @param file the file path to use * @param name the name to use */ int bug_spell_file(string file, string name) { class errors bing; bing = new (class errors); bing->file = file; bing->error = "SPELL " + _error_type + " " + name; bing->type = SPELL_BUG; _globals[this_player()] = bing; this_player()->do_edit(0, "end_of_edit"); return 1; } /* bug_spell() */ /** @ignore yes */ int bug_spell(string str) { mapping junk; string file; string sname; mixed data; if (str == "wizards" || str == "witches") { file = "/obj/spells/" + str; } else { // Expand the nickname str = this_player()->expand_nickname(str); junk = this_player()->query_spells(); // Do a case insensative search. foreach (sname, data in junk) { if (lower_case(sname) == lower_case(str) && arrayp(data)) { file = data[S_OBJECT]; } } if (!file) { notify_fail("Spell " + str + " not found.\n"); map_delete(_globals, this_player()); return 0; } } bug_spell_file(file, str); return 1; } /* bug_spell() */ /** @ignore yes */ int bug_object_new(string name) { string info; class errors bing; bing = new (class errors); bing->file = base_name(environment(this_player())); bing->error = "OBJECT " + _error_type + " " + name; bing->type = OBJECT_BUG; bing->extra = info; _globals[this_player()] = bing; this_player()->do_edit(0, "end_of_edit"); return 1; } /* bug_object() */ /** @ignore yes */ int bug_object(object * obj, string str) { string info; class errors bing; string name; string short; string* bits; bing = new (class errors); if (sizeof(obj) > 1) { notify_fail("More than one object can be identified with the name " + str + "\n"); map_delete(_globals, this_player()); return 0; } bing->file = base_name(obj[0]); switch (bing->file) { case "/std/room/basic/item": bing->file = base_name(environment(this_player())); info = sprintf("Room item %s.\n\n", str); break; case "/std/bit" : bing->file = "/std/races/happy_bit"; break; case "/std/book" : case "/obj/armour": case "/obj/baggage": case "/obj/clothing": case "/obj/container": case "/obj/food": case "/obj/monster": case "/obj/weapon": case "/std/object": if (obj[0]->query_property("virtual name")) { bing->file = obj[0]->query_property("virtual name"); info = sprintf("VObject: %s, Object: %s\nName: %s, Short: %s\n\n", obj[0]->query_property("virtual name"), bing->file, obj[0]->query_name(), obj[0]->query_short()); break; } else { bing->file = base_name(environment(this_player())); } default: bits = explode(bing->file, "/"); name = bits[0]; if (name == "obj" || name == "std") { // Put it in the room by default. if (sizeof(bits) == 2 || (bits[1] != "food" && bits[1] != "armours" && bits[1] != "weapons" && bits[1] != "amulets" && bits[1] != "monster" && bits[1] != "music" && bits[1] != "plants" && bits[1] != "rings" && bits[1] != "furnitures" && bits[1] != "jewellery" && bits[1] != "wands")) { bing->file = base_name(environment(this_player())); } } if (pointerp(obj[0]->query_name())) { /* doors */ name = (obj[0]->query_name())[0]; } else { name = obj[0]->query_name(); } if (!name) { name = "<Bad name>"; } short = obj[0]->query_short(); if (!short) { short = "<Bad short>"; } info = sprintf("Name: %s, Short: %s\n\n", name, short); break; } bing->error = "OBJECT " + _error_type + " " + str; bing->type = OBJECT_BUG; bing->extra = info; _globals[this_player()] = bing; this_player()->do_edit(0, "end_of_edit"); return 1; } /* bug_object() */ /** @ignore yes */ void end_of_edit(string body) { if (strlen(body)) { int ending; string name, trace = 0; mapping last_error; class errors bing; bing = _globals[this_player()]; if (bing->extra) { body = bing->extra + body; } if (body[<1] != '\n') { ending = 1; } if (environment(this_player())) { // && bing->type != ROOM_BUG) { if (ending) { body += "\n"; ending = 0; } body += sprintf("\nEnvironment: %s (%s)\n", file_name(environment(this_player())), environment(this_player())->short()); } if (_use_last_error) { last_error = (mapping) this_player()->get_last_error(); if (mapp(last_error)) { trace = (string) master()->standard_trace(last_error, 1); this_player()->set_last_error(0); } } if (ending) { body += "\n"; } name = (string) this_player()->query_name(); SMART_LOG->smart_log(bing->error, name, body, trace, bing->file); printf("Thank you for your %s report.\n", lower_case(_error_type)); } else { printf("Not saving %s report, aborting.\n", lower_case(_error_type)); } map_delete(_globals, this_player()); } /* end_of_edit() */ /** @ignore yes */ int clean_up() { return 0; } /* clean_up() */ /** @ignore yes */ void reset() { } /* reset() */ /** @ignore yes */ mixed *query_patterns() { return ({ "command <string'name'>", (: bug_command($4[0]) :), "replies", (: bug_replies(0) :), "replies new", (: bug_replies(1) :), "web <string'url'>", (: bug_web($4[0]) :), "spell <string'name'>", (: bug_spell($4[0]) :), "spell {generic|wizards|witches}", (: bug_spell_file("/obj/spells/" + $4[0], $4[0]) :), "spell new", (: bug_spell_file("/obj/spells/generic", "generic") :), "object new", (: bug_object_new("new") :), "object name <string'name'>", (: bug_object_new($4[0]) :), "object <indirect:object:me-here'name of NPC or item'>", (: bug_object($1, $4[0]) :), "ritual <string'name'>", (: bug_ritual($4[0]) :), "ritual generic", (: bug_spell_file("/obj/rituals/generic", "generic") :), "ritual new", (: bug_spell_file("/obj/rituals/generic", "generic") :), "help <string'subject'>", (: bug_help($4[0]) :), "help", (: bug_help("") :), "soul <string'soul command'>", (: bug_command($4[0]) :), "soul new", (: bug_soul(0) :), "room", (: bug_room() :), "special {" + implode(filter(get_dir("/d/special/"), (: file_size("/d/special/"+$1+"/BugReports") >= 0 :)), "|") + "}", (: bug_special($4[0]) :), "misc {" + implode(filter(get_dir("/obj/"), (: file_size("/obj/"+$1+"/BugReports") >= 0 :)), "|") + "}", (: bug_misc($4[0]) :) }); } /* query_patterns() */