12/9/2003 : added free_disabled() to free all memory for disable commands 12/9/2003 : now uses standard REMOVE_FROM_LIST() macro + some cosmetic changes 12/6/2003 : fixed a bug that would occur if a disabled command was not found in the command list on boot. This is Erwin S. Andreasen's snippet, originally designed for Merc muds, which I have ported to to CircleMUD 3.1, as well as enhanced it a little. What this does, is it allows immortals to disable or enable any kind of command on the fly, and a list of these commands is even saved in a file (/lib/disabled.cmds), which is read on bootup. This is especially useful when you are in the middle of coding something new, but it turns out to be buggy, this will allow you to disable the particular command, without having to change the code and recompile. Usage: disable - shows disabled commands, if any disable - disable or enable a command. Below is the original, unmodified note included with the original Merc version of the snippet, by the Erwin S. Andreasen: ============================================================================ This snippet was written by Erwin S. Andreasen, erwin@andreasen.org. You may use this code freely, as long as you retain my name in all of the files. You also have to mail me telling that you are using it. I am giving this, hopefully useful, piece of source code to you for free, and all I require from you is some feedback. Please mail me if you find any bugs or have any new ideas or just comments. All my snippets are publically available at: http://www.andreasen.org/ ============================================================================ Here is what needs to be changed in the CircleMUD 3.1 files: File: comm.c =========================== Find the line: log("Clearing other memory."); After it, add: free_disabled(); /* interpreter.c */ File: db.c =========================== Find the lines: log("Renumbering zone table."); renum_zone_table(); And right afterwards add: log("Loading disabled commands list..."); load_disabled(); File: db.h =========================== Find the line: /* public procedures in db.c */ And at the end of all the functions underneath it, add: void load_disabled(void); void save_disabled(void); void free_disabled(void); Then, at the end of the db.h file, add: /* For disabled commands code by Erwin S. Andreasen, */ /* ported to CircleMUD by Myrdred (Alexei Svitkine) */ #define DISABLED_FILE "disabled.cmds" /* disabled commands */ #define END_MARKER "END" /* for load_disabled() and save_disabled() */ typedef struct disabled_data DISABLED_DATA; extern DISABLED_DATA *disabled_first; /* interpreter.c */ /* one disabled command */ struct disabled_data { DISABLED_DATA *next; /* pointer to next node */ struct command_info const *command; /* pointer to the command struct */ char *disabled_by; /* name of disabler */ sh_int level; /* level of disabler */ int subcmd; /* the subcmd, if any */ }; File: interpreter.c =================================== Find all the #includes at the top, and afterwards add: /* local global variables */ DISABLED_DATA *disabled_first = NULL; Then find the line: /* local functions */ And add at the end of the list: int check_disabled(const struct command_info *command); Afterwards, find: ACMD(do_diagnose); And right after it add: ACMD(do_disable); Likewise, after: { "diagnose" , POS_RESTING , do_diagnose , 0, 0 }, Add: { "disable" , POS_DEAD , do_disable , LVL_GRGOD, 0 }, Then proceed to find the lines: if (*cmd_info[cmd].command == '\n') send_to_char(ch, "Huh?!?\r\n"); And right after these, add: else if (check_disabled(&cmd_info[cmd])) /* is it disabled? */ send_to_char(ch, "This command has been temporarily disabled.\r\n"); Then, go to the end of the interpreter.c file, and add all of the following: /* * Code to disable or enable buggy commands on the run, saving * a list of disabled commands to disk. Originally created by * Erwin S. Andreasen (erwin@andreasen.org) for Merc. Ported to * CircleMUD by Alexei Svitkine (Myrdred), isvitkin@sympatico.ca. * * Syntax is: * disable - shows disabled commands * disable - toggles disable status of command * */ ACMD(do_disable) { int i, length; DISABLED_DATA *p, *temp; if (IS_NPC(ch)) { send_to_char(ch, "Monsters can't disable commands, silly.\r\n"); return; } skip_spaces(&argument); if (!*argument) { /* Nothing specified. Show disabled commands. */ if (!disabled_first) /* Any disabled at all ? */ send_to_char(ch, "There are no disabled commands.\r\n"); else { send_to_char(ch, "Commands that are currently disabled:\r\n\r\n" " Command Disabled by Level\r\n" "----------- -------------- -------\r\n"); for (p = disabled_first; p; p = p->next) send_to_char(ch, " %-12s %-12s %3d\r\n", p->command->command, p->disabled_by, p->level); } return; } /* command given - first check if it is one of the disabled commands */ for (length = strlen(argument), p = disabled_first; p ; p = p->next) if (!strncmp(argument, p->command->command, length)) break; if (p) { /* this command is disabled */ /* Was it disabled by a higher level imm? */ if (GET_LEVEL(ch) < p->level) { send_to_char(ch, "This command was disabled by a higher power.\r\n"); return; } REMOVE_FROM_LIST(p, disabled_first, next); send_to_char(ch, "Command '%s' enabled.\r\n", p->command->command); mudlog(BRF, LVL_IMMORT, TRUE, "(GC) %s has enabled the command '%s'.", GET_NAME(ch), p->command->command); free(p->disabled_by); free(p); save_disabled(); /* save to disk */ } else { /* not a disabled command, check if the command exists */ for (length = strlen(argument), i = 0; *cmd_info[i].command != '\n'; i++) if (!strncmp(cmd_info[i].command, argument, length)) if (GET_LEVEL(ch) >= cmd_info[i].minimum_level) break; /* Found? */ if (*cmd_info[i].command == '\n') { send_to_char(ch, "You don't know of any such command.\r\n"); return; } if (!strcmp(cmd_info[i].command, "disable")) { send_to_char (ch, "You cannot disable the disable command.\r\n"); return; } /* Disable the command */ CREATE(p, struct disabled_data, 1); p->command = &cmd_info[i]; p->disabled_by = strdup(GET_NAME(ch)); /* save name of disabler */ p->level = GET_LEVEL(ch); /* save level of disabler */ p->subcmd = cmd_info[i].subcmd; /* the subcommand if any */ p->next = disabled_first; disabled_first = p; /* add before the current first element */ send_to_char(ch, "Command '%s' disabled.\r\n", p->command->command); mudlog(BRF, LVL_IMMORT, TRUE, "(GC) %s has disabled the command '%s'.", GET_NAME(ch), p->command->command); save_disabled(); /* save to disk */ } } /* check if a command is disabled */ int check_disabled(const struct command_info *command) { DISABLED_DATA *p; for (p = disabled_first; p ; p = p->next) if (p->command->command_pointer == command->command_pointer) if (p->command->subcmd == command->subcmd) return TRUE; return FALSE; } /* Load disabled commands */ void load_disabled() { FILE *fp; DISABLED_DATA *p; int i; char line[READ_SIZE], name[MAX_INPUT_LENGTH], temp[MAX_INPUT_LENGTH]; if (disabled_first) free_disabled(); if ((fp = fopen(DISABLED_FILE, "r")) == NULL) return; /* No disabled file.. no disabled commands. */ while (get_line(fp, line)) { if (!str_cmp(line, END_MARKER)) break; /* break loop if we encounter the END_MARKER */ CREATE(p, struct disabled_data, 1); sscanf(line, "%s %d %hd %s", name, &(p->subcmd), &(p->level), temp); /* Find the command in the table */ for (i = 0; *cmd_info[i].command != '\n'; i++) if (!str_cmp(cmd_info[i].command, name)) break; if (*cmd_info[i].command == '\n') { /* command does not exist? */ log("WARNING: load_disabled(): Skipping unknown disabled command - '%s'!", name); free(p); } else { /* add new disabled command */ p->disabled_by = strdup(temp); p->command = &cmd_info[i]; p->next = disabled_first; disabled_first = p; } } fclose(fp); } /* Save disabled commands */ void save_disabled() { FILE *fp; DISABLED_DATA *p; if (!disabled_first) { /* delete file if no commands are disabled */ unlink(DISABLED_FILE); return; } if ((fp = fopen(DISABLED_FILE, "w")) == NULL) { log("SYSERR: Could not open " DISABLED_FILE " for writing"); return; } for (p = disabled_first; p ; p = p->next) fprintf (fp, "%s %d %d %s\n", p->command->command, p->subcmd, p->level, p->disabled_by); fprintf(fp, "%s\n", END_MARKER); fclose(fp); } /* free all disabled commands from memory */ void free_disabled() { DISABLED_DATA *p; while (disabled_first) { p = disabled_first; disabled_first = disabled_first->next; free(p->disabled_by); free(p); } } File: act.informative.c (optional) =========================== If you want players to see commands that are disabled in parentheses on the command list (by typing commands), add the following in act.informative.c: Find: /* extern functions */ And underneath the list of functions, add: int check_disabled(const struct command_info *command); Then, in ACMD(do_commands), find: send_to_char(ch, "%-11s%s", cmd_info[i].command, no++ % 7 == 0 ? "\r\n" : ""); And REPLACE it with the following: //lets check if that command is disabled... if (check_disabled(&cmd_info[i])) sprintf(arg, "(%s)", cmd_info[i].command); else sprintf(arg, "%s", cmd_info[i].command); send_to_char(ch, "%-11s%s", arg, no++ % 7 == 0 ? "\r\n" : ""); =========================================================================== And that's it! If you are going to use this, I only ask that the comments that give Erwin S. Andreasen credit for making the code, and myself for porting it to CircleMUD 3.1, remain with the code, and that you follow Erwin's license, which I have placed at the top of this file. If you are generous, you can also include either Erwin and myself in the credits or on the login screen or whatever, but it is not necessary. If you have any comments about this code, feel free to contact me at isvitkin@sympatico.ca, or Erwin at erwin@andreasen.org. However, be aware that if you have questions about the CircleMUD version (ie this), you should direct them to me, rather than Erwin, as I am the one who ported it to CircleMUD. -Alexei Svitkine (aka Myrdred or Myrd)