Show Posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.


Messages - Archaegeo

Pages: 1 [2] 3
16
Lima Lounge / Re: DS2 Lima
« on: January 01, 2008, 02:40:09 pm »
Did anything ever come of this?

17
Code Vault / Re: Autodoc Support
« on: January 01, 2008, 02:08:38 pm »
Just verified. I did convert these from Lima a while back (found the originals) for my previous mud and now for DS.  Below is the text of the USAGE file that is referenced.
Code: [Select]
License:

Permission to use or modify the files provided with this software
distribution is granted for any non-commercial uses, so long as
the following conditions are met:

1) nothing is removed from the headers of the files. 

2) Commercial use of any part of this lib is prohibited, without prior
   consent from the authors of this software.

3) The name of the mudlib is displayed on your title screen and in any
   relevant intermud communications, unless you have permission
   from the authors.

4) Written consent from each author must be provided to use this
   software in ways otherwise contrary to this licence.

The authors of this software are:

John Viega (rust@virginia.edu)
Greg Stein (gstein@svpal.org)
Tim Hollebeek (tim@wfn-shop.princeton.edu)

18
Code Vault / Re: Hook Support for DS
« on: January 01, 2008, 02:08:22 pm »
Just verified. I did convert these from Lima a while back (found the originals) for my previous mud and now for DS.  Below is the text of the USAGE file that is referenced.
Code: [Select]
License:

Permission to use or modify the files provided with this software
distribution is granted for any non-commercial uses, so long as
the following conditions are met:

1) nothing is removed from the headers of the files. 

2) Commercial use of any part of this lib is prohibited, without prior
   consent from the authors of this software.

3) The name of the mudlib is displayed on your title screen and in any
   relevant intermud communications, unless you have permission
   from the authors.

4) Written consent from each author must be provided to use this
   software in ways otherwise contrary to this licence.

The authors of this software are:

John Viega (rust@virginia.edu)
Greg Stein (gstein@svpal.org)
Tim Hollebeek (tim@wfn-shop.princeton.edu)

19
Code Vault / Re: Hook Support for DS
« on: January 01, 2008, 01:59:40 pm »
As far as licensing, I believe I converted this from Lima a while back

20
Code Vault / Re: Autodoc Support
« on: January 01, 2008, 01:57:12 pm »
Hmm, I converted it slightly from Lima I believe, so whatever their license is.

21
Sigh..

Ok, the line in my last post doesnt work because when you do update on your environment, you are moved to the void till its done.

So if you still want to use the update.c patch, do not do the above line about file_name(environment....
instead add:

Code: [Select]
rec = 1;
after line 79
Code: [Select]
        if( !ob ) return "You have no environment.";

22
One final thought on this....

Often you update a place just to reset the room. You wouldnt want those updates to appear in the modified_files file.

Either do not use the update.c part of this system or closely monitor whats in your modified_files file or make it so that:
Code: [Select]
        if(!tmp && !rec && strsrch(args, "/realms/") == -1 && file_name(environment(this_player())) != args)

Your call. You can always just put files into the modified_files file manually.

23
BTW,

If you change line 154 in update.c above to this:
Code: [Select]
       if(!tmp && !rec && strsrch(args, "/realms/") == -1)

then it will not fill up your modified_files file with updates done in the /realms/ directories. If there are other directories you want ignored, you can do them in the same manner.

24
Code Vault / Liveupgrade Modification for DS (Adds minor versioning, sorta)
« on: December 31, 2007, 10:31:38 am »
The following change will modify the Liveupgrade command to look for a file called /secure/upgrades/modified_files when you issue the liveupgrade apply command.

Any filename found in modified_files that is also a part of the upgrade will not be copied over. Instead the new version (retrieved with liveupgrade all) will remain in /secure/upgrades/files for you to manually compare with your existing file.

The second part changes the update command to automatically populate modified_files with any file that has update used on it. It does not do this with a -r arguement because many of the recursed files might not have changed. Using the update.c change is optional, you can always just update the modified_files file manually.

Enjoy.

Change /secure/cmds/admins/liveupgrade.c to:
Code: [Select]
#include <lib.h>
#include <dirs.h>
#include <cfg.h>
#include <save.h>
#include <daemons.h>
#include <network.h>

#define WGET_D "/secure/daemon/wget"
#ifndef WEB_SOURCE
#define WEB_SOURCE "149.152.218.102"
#endif
#ifndef WEB_SOURCE_NAME
#define WEB_SOURCE_NAME "dead-souls.net"
#endif
#define WEB_SOURCE_PORT 80
#ifndef SAVE_LIVEUPGRADE
#define SAVE_LIVEUPGRADE   DIR_SECURE_SAVE "/liveupgrade"
#endif

inherit LIB_DAEMON;

string array allnames = ({});
string array tmpnames;
string reverts_dir, revert_name;
static string upgrade_prefix = "/code/upgrades/"+mudlib_version();
static string reverts_prefix = "/secure/upgrades/reverts/"+mudlib_version();
static string upgrades_txt = "/secure/upgrades/txt";
static string upgrades_files = "/secure/upgrades/files";
static int i = 0;
static int oob = 0;
static object player = 0;
int transver = 0;

void create(){
    daemon::create();
    SetSaveFile(SAVE_LIVEUPGRADE);
    eventRestore(1);
}

int eventBackup(string file){
    string tmp, time_str,short_name,filename;
    if(!file) return 0;
    if(!file_exists(file)) return -1;
    if( !(tmp = read_file(file)) ) return -2; 
    time_str = time()+"."+random_numbers(5);
    short_name = last_string_element(file,"/");
    if(!revert_name){
        revert_name = itoa(time());
    }
    if(!reverts_prefix){
        reverts_prefix = "/secure/upgrades/reverts/"+mudlib_version();
    }
    if(!directory_exists("/secure/upgrades/reverts/")){
        mkdir("/secure/upgrades/reverts");
    }
    if(!directory_exists(reverts_prefix)){
        mkdir(reverts_prefix);
    }
    if(!directory_exists(reverts_prefix+"/"+revert_name)){
        mkdir(reverts_prefix+"/"+revert_name);
    }
    filename = reverts_prefix+"/"+revert_name+
    "/"+short_name+"."+time_str;
    write_file(reverts_prefix+"/"+revert_name+"/"+
      "/bk.db",short_name+"."+time_str+" : "+file+"\n");
    cp(file,filename);
    return 1;
}

int eventRevert(string revert_path){
    string *files;

    if(!revert_path){
        return 0;
    }

    if(!directory_exists(revert_path)){
        return -1;
    }

    if(!file_exists(revert_path+"/bk.db")){
        return -2;
    }

    files = explode(read_file(revert_path+"/bk.db"),"\n");

    if(!files || !sizeof(files)){
        return -3;
    }

    foreach(string line in files){
        string backup, target;
        if(sscanf(line,"%s : %s", backup, target) != 2) continue;
        if(!directory_exists(path_prefix(target))){
            mkdir_recurse(path_prefix(target));
        }
        cp(revert_path+"/"+backup, target);
    }
    return 1;
}

mixed cmd(string str) {
    string mud = "Dead Souls";
    string file;
    string orig_str = str;
    int foo, tmpint = 0;
    mapping NewFiles = ([]);
    object inet = find_object(INET_D);
    string *preload_file = explode(read_file(CFG_PRELOAD),"\n");
    string *safe = ({});
    string notupgraded="";
    mixed *socks = socket_status();

    if(!revert_name){
        revert_name = itoa(time());
    }

    if(!str || str == ""){
        write("Try: help liveupgrade.");
        return 1;
    }

    if(str == "alpha"){
        if(transver){
            transver = 0;
            write("Alpha/Stable upgrades disabled.");
            return 1;
        }
        if(!transver){
            transver = 1;
            write("Alpha/Stable upgrades enabled.");
            return 1;
        }
    }

    if(str == "revert"){
        string *vers = get_dir("/secure/upgrades/reverts/");
        string ver, subver;
        if(!vers || !sizeof(vers)){
            write("There is no previous backup to revert to.");
            return 1;
        }
        else ver = vers[0];
        vers = get_dir("/secure/upgrades/reverts/"+ver+"/");
        if(!vers || !sizeof(vers)){
            write("There is no backup instance to revert to.");
            return 1;
        }
        else subver = "/secure/upgrades/reverts/"+ver+"/"+vers[0];
        eventRevert(subver);
        rename(subver,"/secure/upgrades/bak/"+last_string_element(subver,"/"));
        rmdir(path_prefix(subver));
        write("Reversion complete.");
        return 1;
    }

    foreach(mixed element in socks){
        if(element[1] == "DATA_XFER" && element[4] == WEB_SOURCE+"."+WEB_SOURCE_PORT &&
          str != "cancel"){
            player->eventPrint("A download is still in progress. Please wait until it is complete.");
            return 1;
        }
    }

    if(!player && this_player()) player = this_player();
    allnames = ({});
    if(!player) return 0;

    if( !((int)master()->valid_apply(({ "SECURE" }))) )
        error("Illegal attempt to access liveupgrade: "+get_stack()+" "+identify(previous_object(-1)));

    if(!directory_exists("/secure/upgrades/bak")){
        mkdir("/secure/upgrades/bak");
    }

    if(!directory_exists(reverts_prefix)){
        mkdir(reverts_prefix);
    }

    i = sscanf(mudlib_version(),"2.3a%d",tmpint);
    if(i && tmpint < 12) oob = 1;
    else {
        if(!strsrch(str,"-o ")){
            oob = 1;
            str = replace_string(str,"-o ","");
        }
        else oob = 0;
    }

    i = 0;
    foo = sscanf(str,"%s %s",file, mud);
    if(!foo || foo < 2) file = str;
    if(str == "apply"){
        string *files = ({});
        player->eventPrint("I hope you backed up...\n");
        foreach(string element in get_dir(upgrades_files+"/")){
            if(element == "0^0secure0^0sefun0^0mud_info.c"){
                object thingy = load_object(upgrades_files+"/"+element);
                string vers;
                if(thingy){
                    string current_ver = mudlib_version();
                    vers = thingy->mudlib_version();
                    if(((grepp(vers,"a") && !grepp(current_ver, "a")) ||
                        (!grepp(vers,"a") && grepp(current_ver, "a"))) &&
                      !transver){
                        write("This upgrade would cross stable/alpha "
                          "boundaries, but that has not been enabled "
                          "with \"liveupgrade alpha\" yet.");
                        return 1;
                    }
                }
            }
            files += ({ upgrades_files+"/"+element });
        }
        foreach(string element in files){
            string contents = "";
            NewFiles[element] = replace_string(replace_string(element,"0^0","/"),
              upgrades_files+"/","");
            contents = read_file(element);
            if(!contents) contents = "";
            if(last(contents,1) != "\n") contents += "\n";
            write("Checking: "+NewFiles[element]);
            if(read_file("/secure/upgrades/modified_files"))
                safe =  explode(read_file("/secure/upgrades/modified_files"),"\n");
            if(member_array(NewFiles[element], safe) != -1 ||
            member_array(NewFiles[element][0..<3], safe) != -1)
            {
            notupgraded += NewFiles[element]+"\n";
            continue;
            }
            write_file(element, contents, 1);
            eventBackup(NewFiles[element]);
            if(directory_exists(NewFiles[element])) rm(element);
            else {
                string path = path_prefix(NewFiles[element]);
                if(!directory_exists(path)) mkdir_recurse(path);
                rename(element, NewFiles[element]);
            }
        }
        if(member_array(INET_D,preload_file) == -1 && inet) inet->eventDestruct();
        reload(UPDATE_D);
        RELOAD_D->eventReload(this_object(), 2);
        reload(WGET_D);
        rm("/secure/upgrades/txt/list.txt");
        if(notupgraded!="")
        player->eventPrint("\nThe following files are marked as modified and were not upgraded."+
        " They remain in the /secure/upgrades/files directory. You will need to compare them and"+
        " make changes as appropriate for your mud:\n"+notupgraded);
        player->eventPrint("\nDone. It is now a very good time to reboot the mud.");
        player = 0;
        return 1;
    }
    if(str == "cancel"){
        string *files = ({});
        foreach(string element in get_dir(upgrades_files+"/")){
            files += ({ upgrades_files+"/"+element });
        }
        foreach(string element in files){
            rm(element);
        }
        rm("/secure/upgrades/txt/list.txt");
        player->eventPrint("Cancelled.");
        player = 0;
        RELOAD_D->eventReload(this_object(), 2);
        reload(WGET_D);
        return 1;
    }
    if(oob){
        if(!inet){
            inet = load_object(INET_D);
            player->eventPrint("Starting INET_D.");
            if(member_array(INET_D,preload_file) == -1)
                player->eventPrint("When you complete the upgrade by using the \"apply\" keyword, the "
                  "inet daemon will be shut down, since you do not have it enabled by "
                  "default. Please remember to either apply the upgrades when the downloading "
                  "is complete, or manually shut down INET_D with the command: mudconfig inet stop\n");
        }
        if(!inet){
            player->eventPrint("There is a problem with INET_D. The upgrade will not proceed.");
            return 1;
        }

        if(!INET_D->GetService("oob")){
            player->eventPrint("The OOB service is not enabled. Enabling it now.");
            INET_D->AddService("oob",OFFSET_OOB,LIB_OOB,0);
        }

        if(!INET_D->GetService("oob")){
            player->eventPrint("There was a problem enabling the OOB service. The upgrade will not proceed.");
            return 1;
        }

        if(!INET_D->GetServer("oob")){
            player->eventPrint("The OOB service is not started. Starting it now.");
            INET_D->eventStartServer("oob");
        }

        if(!INET_D->GetServer("oob")){
            player->eventPrint("There was a problem starting the OOB service. The upgrade will not proceed.");
            return 1;
        }

        if(foo < 2) {
            mud = LIVEUPGRADE_SERVER;
            file = str;
        }
        if(!file){
            return this_object()->GetHelp();
        }

        mud = INTERMUD_D->GetMudName(mud);
        if(!mud){
            player->eventPrint("That liveupgrade server appears unavailable.");
            return 1;
        }
    }
    if(file == "all"){
        string tmp = replace_string(upgrades_txt+"/upgrades.txt","/","0^0");
        if(player && this_player() && player != this_player()){
            this_player()->eventPrint("This command is currently locked and in use by "+capitalize(player->GetKeyName())+".");
            return 1;
        }
        else if(this_player()) player = this_player();
        else player = this_object();

        if(WGET_D->GetUpgrading()){
            player->eventPrint("An upgrade in already occurring. Please wait for it to complete.");
            return 1;
        }

        if(!file_exists(upgrades_txt+"/list.txt")){
            player->eventPrint("Downloading updates table. Please wait...");
            rename("/secure/upgrades/files","/secure/upgrades/bak/"+time());
            mkdir("/secure/upgrades/files");
            if(oob){
                OOB_D->GetFile(mud,upgrades_txt+"/upgrades.txt");
            }
            else {
                WGET_D->GetFile(WEB_SOURCE, upgrade_prefix+"/upgrades.txt",WEB_SOURCE_NAME,
                  "/secure/upgrades/txt/list.txt",WEB_SOURCE_PORT);
            }
            call_out( (: cmd :), 5, orig_str);
            return 1;
        }

        tmpnames = explode(read_file(upgrades_txt+"/list.txt"),"\n");
        foreach(string element in tmpnames){
            if(!oob) allnames += ({ "/code/upgrades/"+mudlib_version()+element });
            else allnames += ({ element });
        }

        if(oob){
            OOB_D->eventMajorUpgrade(mud, allnames);
        }
        else {
            WGET_D->eventMajorUpgrade(WEB_SOURCE, allnames,WEB_SOURCE_NAME);
        }
        rm(upgrades_txt+"/list.txt");
        player->eventPrint("Full upgrade begun.");
        player->eventPrint("Please wait until you receive a completion message,  "+
          "then issue the command: liveupgrade apply\n\n");
        player->eventPrint("%^FLASH%^RED%^WARNING! %^BLACK%^WARNING! %^YELLOW%^WARNING! %^RESET%^WARNING!");
        player->eventPrint("You must *always* do a full backup before applying the liveupgrade. "+
          "If the liveupgrade screwed up, and you get garbage files because of connection "+
          "problems, it may be necessary for you to restore from backup to be able to "+
          "start the mud again. You've been warned.");
        return 1;
    }
    if(oob){
        OOB_D->GetFile(mud,file);
        player->eventPrint("Requesting the file \""+file+"\" from "+INTERMUD_D->GetMudName(mud)+".");
    }
    else {
        player->eventPrint("Requesting the file \""+file+"\" from "+WEB_SOURCE);
        WGET_D->GetFile(WEB_SOURCE, upgrade_prefix+file);
    }
    return 1;
}

void eventReceiveReport(string str){
    if(player) player->eventPrint(str);
}

string GetHelp() {
    return ("Syntax: liveupgrade [-o] all\n"
      "        liveupgrade apply\n"
      "        liveupgrade cancel\n"
      "        liveupgrade revert\n"
      "        liveupgrade alpha\n"
      "To use oob updates (not recommended), use the -o flag. The default "
      "is currently an http connection to dead-souls.net, which is vastly "
      "faster and more secure than oob.\n"
      "To upgrade all files to the next appropriate level for your lib version:\n"
      "liveupgrade all\n"
      "Wait until you receive the completion message before finalizing the upgrade. "
      "You can finalize the upgrade by typing:\n"
      "liveupgrade apply\n"
      "This will delete your old copies of files and copy the newly downloaded "
      "ones in their place.\n"
      "NEVER EVER do a liveupgrade without a full backup first.\n"
      "To cancel the liveupgrade process:\n"
      "liveupgrade cancel\n"
      "To restore your mud to the condition it was in prior to the last liveupgrade.\n"
      "liveupgrade revert\n"
      "To enable liveupgrading between alpha and stable versions:\n"
      "liveupgrade alpha\n"
      "");
}

Update to /secure/cmds/creators/update.c (optional)
Code: [Select]
/*    /secure/cmds/creator/update.c
 *    from the Dead Souls LPC Library
 *    destroys an object then reloads it
 *    created by Descartes of Borg 950330
 *    - added recursive update option (blitz 960711)
 *    Version: @(#) update.c 1.2@(#)
 *    Last modified: 96/12/17
 */

#include <lib.h>
#include <rooms.h>

inherit LIB_DAEMON;
inherit LIB_HELP;

#define U_RECURSIVE       (1 << 1)
#define U_INTERACTIVE     (1 << 2)
#define U_AUTOMATED       (1 << 3)

mapping LocationsMap = ([]);

static int eventUpdate(string args, int flags);
int rec;

static void CacheAndCarry(object *obs){
    if(!sizeof(obs)) return;
    foreach(object fellow in obs){
        string ubi = fellow->GetProperty("LastLocation");
        if(ubi) LocationsMap[fellow->GetKeyName()] = ubi;
    }
    obs->eventMove(ROOM_VOID);
}

static void ReturnAndRelease(object *dudes, string file){
    if(!sizeof(dudes)) return;
    if(!file) return;
    dudes->eventMove(file);
    foreach(object fellow in dudes){
        if(sizeof(LocationsMap[fellow->GetKeyName()])){
            fellow->SetProperty("LastLocation",LocationsMap[fellow->GetKeyName()]);
        }
    }
}


static void create() {
    SetHelp("Syntax: <update [-r] [file list]>\n"
      "Destructs the master copy of the file named "
      "and then attempts to reload a new version "
      "of it.\n"
      "The -r flag attempts to update all "
      "files in the target's inheritance tree.\n");
}

mixed cmd(string args) {
    object *obs, ob;
    string *files, *tmpfiles;
    mixed tmp;
    string file;
    int i, flags;
    rec = 0;

    if( sizeof(args) ) {
        string str = args;
        args = "";
        foreach(string foo in explode(str, " ")) {
            if(!sizeof(foo)) continue;
            switch(foo) {
            case "-r" : flags |= U_RECURSIVE; rec = 1; break;
            case "-e" : flags |= U_INTERACTIVE; break;
            case "-a" : flags |= U_AUTOMATED; break;
            default: args += " " + foo;
            }   
        }
    }
    if( args == "" || !args ) {
        if(!this_player()) return "No player.";
        ob = environment(this_player());
        if( !ob ) return "You have no environment.";
        file = base_name(ob);
        this_player()->eventPrint("Updating environment");
        obs = filter(all_inventory(ob), (: userp :));
        if( sizeof(obs) ) CacheAndCarry(obs);
        if( !eventUpdate(base_name(ob), flags) ) {
            obs->eventPrint("You are thrown into the void as your "
              "surroundings violently destruct.");
            return "Error in reloading environment.";
        }
        obs = filter(obs, (: $1 :));
        if( sizeof(obs) ) ReturnAndRelease(obs, file);
        return 1;
    }
    if(this_player()){
        tmpfiles = map(explode(args, " "),
          function(string x) {
              string tmp = (string)this_player()->query_cwd();
              if( x[<2..] != ".c" ) x = x + ".c";
              return absolute_path(tmp, x);
            });
          tmpfiles = map(tmpfiles,
            (: ((file_size($1) == -2) ?
                (($1[<1] == '/') ? ($1 + "*.c") : ($1 + "/*.c")) : $1)
            :));
          i = sizeof(tmpfiles);
          files = ({});
          while(i--) {
              if( sizeof(tmp = (string *)this_player()->wild_card(tmpfiles[i])) )
                  files += tmp;
              else this_player()->eventPrint(tmpfiles[i] + ": File not found.");
          }
          i = sizeof(files);
          while(i--) eventUpdate(files[i], flags);
      }
        return 1;
    }

    static int eventUpdate(string args, int flags) {
        object ob;
        string tmp;

        if( flags & U_RECURSIVE ) {
            string *ancestors;
            int i;

            if( !eventUpdate(args, flags ^ U_RECURSIVE) ) return 0;
            if( !(ob = find_object(args)) ) return 0;
            ancestors = deep_inherit_list(ob);
            if(this_player() && (flags & U_RECURSIVE) && !(flags & U_AUTOMATED)) 
                this_player()->eventPrint("(%^CYAN%^Recursive "
                  "update: " + args + "%^RESET%^)\n");
            i = sizeof(ancestors);
            while(i--) if( !eventUpdate(ancestors[i], flags ^ U_RECURSIVE) ) {
                    if(this_player())
                        this_player()->eventPrint("Recursive update failed.");
                    return 0;
                }       
        }
        if( args[<2..] == ".c" ) args = args[0..<3];
        ob = find_object(args);
        if(!ob) ob = load_object(args);
        if( ob ) {
            if( tmp = catch( ob->eventDestruct()) && this_player() )
                this_player()->eventPrint(args + ": error in eventDestruct()");
            if( ob ) destruct(ob);
            if( ob && this_player())
                this_player()->eventPrint(args + ": Failed to destruct old object.");
        }
        if( args == base_name(this_object()) && this_player() ) {
            this_player()->eventPrint("Cannot reload update after destruct.\n"
              "It will be reloaded at next reference.");
            return 0;
        }
        tmp = catch(call_other(args, "???"));
        if(!tmp && !rec)
        {
        if(!read_file("/secure/upgrades/modified_files"))
        write_file("/secure/upgrades/modified_files", args+"\n");
        else if(member_array(args, explode(read_file("/secure/upgrades/modified_files"), "\n")) == -1)
        write_file("/secure/upgrades/modified_files", args+"\n");
        }
        if(this_player() && !(flags & U_AUTOMATED) ){
            if( !tmp )
                this_player()->eventPrint(args + ": Ok");
            else this_player()->eventPrint(args + ": Error in update\n" + tmp);
        }
        return 1;
    }

25
Code Vault / Hook Support for DS
« on: December 31, 2007, 08:19:29 am »
The following code changes add Hook support for DS. Also provided is a basic Help file and an example of Hook use.

Basically hooks allow you to modify things without having to rely on shadows. They are a cleaner alternative when you do not need the worries of shadows and they allow easier tracking.

First is hooks.h - I placed this in /secure/include/
Code: [Select]
#ifndef HOOKS_H
#define HOOKS_H

#define HOOK_IGNORE        0 // ignore the return values
#define HOOK_SUM           1 // return the sum
#define HOOK_LAND          2 // return 1 if all the hooks return 1
#define HOOK_LOR           3 // return 1 if any hook returns 1
#define HOOK_YES_NO_ERROR  4 // 1, 0, error like the parser

#endif

Next is hooks.c - This is the main hooks support file. It will be inherited by object.c which all things inherit. I placed it in /lib/comp/hooks.c
Code: [Select]
/* Do not remove the headers from this file! see /USAGE for more info. */

#include <hooks.h>
#include <function.h>

//:MODULE
//
//The hook module is included as part of OBJECT, and allows a general method
//of allowing keeping track of and calling hooks, along with a method if
//specifying how multiple hooks should be resolved.

static mapping hooks = ([]);

//:FUNCTION add_hook
//add_hook(string tag, function hook) sets up the function 'hook' to be
//called whenever call_hooks(tag, ...) is done.  Note that if you want
//to remove the tag later, you have to do so with the _exact_ same function
//pointer.
//
//e.g.
//
//function my_hook = (: my_hook_func :);
//
// add_hook("foo", my_hook);
//
// remove_hook("foo", my_hook);
//
void add_hook(string tag, function hook) {
function *tmp;
tmp = ({ hook });
if (hooks[tag]) {
    // Make sure we only have one
    hooks[tag] -= tmp;
    hooks[tag] += tmp;
} else
    hooks[tag] = tmp;
}

//:FUNCTION remove_hook
//
//Remove a hook added with add_hook.  The function pointer passed must be
//the same one that was passed to add_hook.
//
//see: add_hook
void remove_hook(string tag, function hook) {
    if (hooks[tag])
        hooks[tag] -= ({ hook });
    if(!sizeof(hooks[tag]))
        map_delete(hooks,tag);
}

//:FUNCTION hook_state
//
//hook_state(tag, hook, state) Either add or remove a hook based on the
//state 'state'
void hook_state(string tag, mixed hook, int state) {
    if (state)
        add_hook(tag, hook);
    else
        remove_hook(tag, hook);
}

//:FUNCTION call_hooks
//
//Call a set of hooks, with the specified method for resolving multiple
//hooks.  A setting from /include/hooks.h can be used, or a function pointer
//which is appropriate for implode()ing with the return values.
//
//E.g.
//
// call_hooks("foo", (: $1 + $2 :), 2) will return 2 + the sum of the return
//     values of the hooks
//
// but 2 + call_hooks("foo", HOOK_SUM) is faster.
//


varargs mixed call_hooks(string tag, mixed func, mixed start,
  mixed *args) {
    function *hooks_to_call;
    mixed tmp;
    function hook;

    if (hooks_to_call = hooks[tag]) {
        hooks_to_call = filter(hooks_to_call,
          (: !(functionp($1) & FP_OWNER_DESTED) :));
        hooks[tag] = hooks_to_call;

        if(!args) args=({});

        if (!intp(func))
            return implode(map(hooks_to_call, (: evaluate($1, $(args)...) :)),
              func, start);
        switch (func) {
        case HOOK_IGNORE:
            map(hooks_to_call, (: evaluate($1, $(args)...) :));
            return 0;
        case HOOK_SUM:
            foreach (hook in hooks_to_call)
            tmp += evaluate(hook, args...);
            return tmp;
        case HOOK_LAND:
            foreach (hook in hooks_to_call)
            if (!evaluate(hook, args...)) return 0;
            return 1;
        case HOOK_LOR:
            foreach (hook in hooks_to_call)
            if (tmp = evaluate(hook, args...))
                return tmp;
            return 0;
        case HOOK_YES_NO_ERROR:
            foreach (hook in hooks_to_call) {
                tmp = evaluate(hook, args...);
                if (!tmp || stringp(tmp)) return tmp;
            }
            return (start || 1);
        default:
            error("Unknown hook type in call_hooks.\n");
        }
    } else {
        if (!intp(func))
            return start;

        switch (func) {
        case HOOK_IGNORE:
        case HOOK_SUM:
        case HOOK_LOR:
            return 0;
        case HOOK_LAND:
            return 1;
        case HOOK_YES_NO_ERROR:
            return (start || 1);
        default:
            error("Unknown hook type in call_hooks.\n");
        }
    }
}

mapping debug_hooks()
{
    return copy(hooks);
}

Finally is the modification to /lib/comp/object.c to allow it to inherity hooks.c, just add the following line at the top after the other inherits. You can also define a LIB_HOOKS if you want. (I might soon).
Code: [Select]
inherit "/lib/comp/hooks";
Now for an example of use - Modifying stats.
The below change to /lib/genetics.c will allow us to add hooks to any of the normal stats in the form of modify_<statname>
Add to the top of genetics.c:
Code: [Select]
#include <hooks.h>
inherit "/lib/comp/hooks";
Change int GetStatLevel to the following:
Code: [Select]
int GetStatLevel(string stat) {
    int x;

    x = (GetBaseStatLevel(stat) + GetStatBonus(stat));
    switch(stat) {
    case "coordination": case "wisdom":
        x -= GetAlcohol();
    }
// Hook Support
    x += call_hooks("modify_"+stat,HOOK_SUM);
    return x;
}

Now we are set up to modify stats with functions, so lets change the standard wizards hat to give us +10 strength when we put it on.
This is in /domains/default/armor/wizard_hat.c
Code: [Select]
#include <lib.h>
#include <armor_types.h>
#include <damage_types.h>
inherit LIB_ARMOR;

int extra_str();
function extra = (: extra_str :);


int extra_str()
{
        return 10;
}

static void create(){
    armor::create();
    SetKeyName("wizard's hat");
    SetAdjectives( ({"wizard","wizards", "floppy", "large", "conical", "blue"}) );
    SetId( ({"hat"}) );
    SetShort("a wizard's hat");
    SetLong("This is a large, floppy hat with a wide brim all "+
      "around it, and a conical center. It is dark blue in color, "+
      "and is decorated with pictures of yellow moons and stars.");
    SetProperties(([
        "beta" : 2,
      ]));
    SetMass(50);
    SetBaseCost("silver",500);
    SetDamagePoints(100);
    SetArmorType(A_HELMET);
    SetProtection(BLUNT, 20);
    SetProtection(BLADE, 20);
    SetProtection(KNIFE, 20);
}
void init(){
    ::init();
}

//HOOK REMOVE EXAMPLE
mixed eventUnequip(object who){
if(CanUnequip(who))
    environment(this_object())->remove_hook("modify_strength", extra);
::eventUnequip(who);
}

//HOOK ADD EXAMPLE
mixed eventEquip(object who, string array limbs){
   if(CanEquip(who, limbs))
     environment(this_object())->add_hook("modify_strength", extra);
   ::eventEquip(who, limbs);
}

Voila. Now when we put the hat on, +10 str. When we take it off, for any reason, str goes back to normal.

This is a simple example. The hook function could of course do many other things, in this case we add up all of the modify_<statname> hooks in existance whenever GetStatLevel is called.

Here is the hook help file:
Code: [Select]
HOOKS - A Tutorial by Lathander
 -------------------------------
 
 Prototypes
 ----------
 varargs mixed call_hooks  (string tag, mixed kind, mixed start,
                            array args...);
 void          add_hook    (string tag, function hook);
 void          remove_hook (string tag, function hook);
 
 
 Headers
 ----------
 inherit "/std/object/hooks";
 #include <hooks.h>
 
 
    This is just going to be a quick run through the concept and use of
 hooks.  I hope to make it clear, but if its not, let me know and I'll
 see if I can clarify it. 
    After inheriting the hooks file, your object now contains a mapping
 of hooks.  There can be as many hooks as you like.  However, all of the
 hooks are grouped into sets with similar 'tag' names.
 
    The first step to setting up a hook is to establish what it will do
 in its local code.  You do this by adding a call_hooks() function.  In
 our example, we'll do a hook that modify's a player's strength.
 
 // Example Code in a player object.
 // This code does not really exist.
 int get_strength() {
    int strength;
 
    strength = 100;
    ...
    strength = call_hooks("modify_strength",HOOK_FILTER,0,strength);
    ...
    return strength;
 }
 
    In this example we're giving each function that has hooked the
 "modify_strength" tag in this object a chance to modify the player's
 strength.  This gives us the power of letting other objects modify this
 object's strength, without them having to touch this object's code. 
 Once a hook is in place, it rarely has to be changed.
    Whenever call_hooks() is called, it, in turn, calls every hook that
 has been put into this object with a particular tag.
 
    The second step is to insert the actual hook into the object.  In
 this case, we'll have a set of Guantlets of Ogre Power which will insert
 a hook into a player to make them stronger when they wear it.
 
    Here's an example for a set of Gauntlets of Ogre Power. 
 
 void eventWear() {
    ::eventWear();
    environment(this())->add_hook("modify_strength", (: ogre_strength :));
    message("event","You feel power course throu your muscles!\n",you());
 }
 
 int ogre_strength(int strength) {
    return strength*11/10;
 }
 
 void eventRemove() {
    ::eventRemove();
    environment( this() )->
       remove_hook("modify_strength",(: ogre_strength :));
    message("event","You suddenly feel wimpy.\n",you());
 }
 
   
    Now, whenever the player puts on the Gauntlets, the add_hook() is
 called on them.  The add_hook() links the tag "modify_strength" in the
 wearer with the function ogre_strength() in the gauntlets.  Whenever the
 wearer's object executes a call_hooks() with the tag "modify_strength"
 then the ogre_strength() function will get called with the args specified
 in the call_hooks() function.
    Note that I called remove_hook() when the player took off the
 guantlets.  Don't forget to remove the hook or you may get very strange
 results.
 
 mixed kind
 ----------
 
    Now, the call_hooks() function is much more complicated than I made it
 first appear.  The mixed kind parameter to call_hooks() is the key to
 that.  Here's the possible values of kind, and a description of what
 they make call_hooks() do.
 
    HOOK_IGNORE - Calls all hooks, but call_hooks() always returns zero.
 
    HOOK_SUM    - Calls all hooks and adds their return values.  The
                  return value of call_hooks() is the sum of the return
                  values.
 
    HOOK_LAND   - Calls the hooks in a random order.  If *any* of the
                  hooks returns a zero, then call_hooks() returns a zero.
                  (Logical And)

    HOOK_LOR    - Calls the hooks in a random order.  If *any* of the
                  hooks returns a one, then call_hooks() returns a one.
                  (Logical Or)
 
    HOOK_FILTER - Calls all of the hooks in a random order.  The first
                  hook gets the regular args.  For successive functions,
                  however, the first argument is the return value of
                  the previous hook.  call_hooks() returns the value
                  of the last hook.
 
    HOOK_YES_OR_NO - Calls all of the hooks in a random order.  If any
                  function returns a zero or a string, then call_hooks()
                  returns that value and does not process any further
                  hooks.  If all hooks are processed without returning
                  a zero or a string, then a one is returned.
 
    a string    - 'kind' can also be a string.  If it is a string, then
                  the return value of the call_hooks() will be a string
                  with all of the return values of all of the hooks
                  concatenated.  The value of 'kind' will act as a
                  delimiter between strings.
 
                 
 Return value if there are no hooks.
 -----------------------------------
 
    If call_hooks() is called, but there are currently no hooks into that
 object with the given tag value, then the return value is the same as
 'start'.  In other words, start is the defult return value.

26
Code Vault / Re: Channel History Change in DS
« on: December 31, 2007, 07:03:31 am »
Ok, that worked. Think the rectangles were tabs? The indent command in DS stripped it and cleaned it though.

27
Code Vault / Re: Channel History Change in DS
« on: December 31, 2007, 07:02:49 am »
Try three, just so i can figure out whats going on. I dont see an edit ability for previous messages, would be nice if that was turned on.
Code: [Select]
int cmdLast(string feep){
    string history;
    string array chanhist;
    int lines;

    sscanf(feep,"%s %d", feep, lines);
    if(!lines) lines=20;
    if(!chanlast||!Channels[feep]||member_array(this_player(), Channels[feep])==-1){

        this_player()->eventPrint("You are not subscribed to that channel.", MSG_ERROR);
        return 1;
    }
    if(!CanListen(this_player(),feep)){
        write("You lack privileges to that channel.");
        return 1;
    }
    if(feep != "admin")
        history = read_file("/log/chan/"+feep);
    else
        history = read_file("/secure/log/admin");
    if(!history){
        if(!sizeof(chanlast[feep])){
            this_player()->eventPrint("That channel has no backlog.", MSG_ERROR);
            return 1;
        }
        else
            this_player()->eventPrint(implode(chanlast[feep], "\n"));
        return 1;
    }
    if( sizeof(chanhist = explode(history, "\n")) > lines )
        history = implode(chanhist[<lines..], "\n");
    this_player()->eventPrint(history);
    return 1;
}

28
Code Vault / Re: Channel History Change in DS
« on: December 31, 2007, 06:56:01 am »
cmdLast repost
Code: [Select]
int cmdLast(string feep){
  string history;
    string array chanhist;
    int lines;
   
    sscanf(feep,"%s %d", feep, lines);
    if(!lines) lines=20;
    if(!chanlast||!Channels[feep]||member_array(this_player(), Channels[feep])==-1){

        this_player()->eventPrint("You are not subscribed to that channel.", MSG_ERROR);
        return 1;
    }
    if(!CanListen(this_player(),feep)){
        write("You lack privileges to that channel.");
        return 1;
    }
    if(feep != "admin")
    history = read_file("/log/chan/"+feep);
    else
      history = read_file("/secure/log/admin");
    if(!history){
        if(!sizeof(chanlast[feep])){
            this_player()->eventPrint("That channel has no backlog.", MSG_ERROR);
            return 1;
        }
        else
            this_player()->eventPrint(implode(chanlast[feep], "\n"));
    return 1;
    }
    if( sizeof(chanhist = explode(history, "\n")) > lines )
       history = implode(chanhist[<lines..], "\n");
    this_player()->eventPrint(history);
    return 1;
}

29
Code Vault / Re: Channel History Change in DS
« on: December 31, 2007, 06:52:47 am »
Yeah, I see them too, Ill try a repost below and see if they repeat. I do not see CTRL-M's in the mud though.

30
Code Vault / Autodoc Support
« on: December 30, 2007, 07:13:53 pm »
The following code supports an Autodoc system. This has been modified to support Dead Souls lib.
To use it, you just make the daemon and call complete_rebuild() in it. Then it will run once a day. It scans the files on the mud that have changed since its last scan for the following comment tags.  It then makes doc files in /doc/help/autodoc/  directory.

//:MODULE
//This is the description of this module.
//$$ Note: helpsys style directives can be included
//see: another_module

//:FUNCTION funcname
//This is the doc for function funcname

//:COMMAND
//This is the doc for the command

//:HOOK
//This documents a hook called with call_hooks

//:EXAMPLE
//This is an example to illustrate some code.

//:MUDNAME
// This adds MUDNAME code change to the mudlib
// MUDNAME is what is returned by upper_case(mud_name())
// (keep this line blank after comment to make comment file readable)

//:TODO
//What we'd like to do with this in the future

//### Something has to be fixed (take out the spaces between the / / # # # for the correct format)
//### This doesn't need to start at the left margin

Other tags of course could be added, and :HOOK is a coming change I plan to make available.

The main daemon should be named /secure/daemon/doc_d (or anything in /secure/daemon)
Code: [Select]
/* Do not remove the headers from this file! see /USAGE for more info. */

// DOC_D by Rust (rust@virginia.edu)  4-10-95
// Inspired by emacs on-line documentation, which is
// awesome once you know how to use it....  =)
// Some languages like Lisp and Python have doc strings.
// since it wasn't about to be built into the language,
// documentation is done via the preprocessor.
//

/* --------------------------------------------------------------

Rewritten by Beek, MonicaS; the equivalent of the above is now:

 [Start the comment at the left margin; these are indented so this daemon
  doesn't see these examples.]

 //:MODULE
 //This is the description of this module.
 //$$ Note: helpsys style directives can be included
 //see: another_module

 //:FUNCTION funcname
 //This is the doc for function funcname

 //:COMMAND
 //This is the doc for the command

 //:HOOK
 //This documents a hook called with call_hooks

 //:EXAMPLE
 //This is an example to illustrate some code.

 //:MUDNAME
 // This adds AUTODOC_MUDNAME code change to the mudlib
 // MUDNAME is what is returned by upper_case(mud_name())
 // (keep this line blank after comment to make comment file readable)

 //:TODO
 //What we'd like to do with this in the future

 / / # # # Something has to be fixed (take out the spaces between the / / # # # for the correct format)
 / / # # # This doesn't need to start at the left margin

Data is updated nightly and dumped to the /doc/help/autodoc directory
*/
// Modified to work with Dead Souls lib by Archaegeo on 12-30-07


#include <daemons.h>

//:MODULE
//The doc daemon handles finding source files which have been modified and
//updating the appropriate documentation in /doc/help/autodoc.

// Public functions --------------------------------------------------------
int pending() {if(event_pending(this_object())) return 1;return 0;}

private void continue_scan();
private int last_time;
private string *files_to_do, *dirs_to_do;

private void delete_directory(string directory)
{
    mixed *file;
    if(file_size(directory)==-1)
        return;
    foreach(file in get_dir(sprintf("%s/",directory),-1))
    {
        string target=sprintf("%s/%s",directory,file[0]);
        if(file[1]==-2)
            delete_directory(target);
        else
            rm(target);
    }
    rmdir(directory);
}

void make_directories()
{
    /* Assume that if the filesize is -1 that a directory needs to be created */
    if(file_size("/doc/help/autodoc")==-1)
        mkdir("/doc/help/autodoc");
    if(file_size("/doc/help/autodoc/FIXME")==-1)
        mkdir("/doc/help/autodoc/FIXME");
    if(file_size("/doc/help/autodoc/command")==-1)
        mkdir("/doc/help/autodoc/command");
    if(file_size("/doc/help/autodoc/examples")==-1)
        mkdir("/doc/help/autodoc/examples");
    if(file_size("/doc/help/autodoc/functions")==-1)
        mkdir("/doc/help/autodoc/functions");
    if(file_size("/doc/help/autodoc/hook")==-1)
        mkdir("/doc/help/autodoc/hook");
    if(file_size("/doc/help/autodoc/modules")==-1)
        mkdir("/doc/help/autodoc/modules");
    if(file_size("/doc/help/autodoc/todo")==-1)
        mkdir("/doc/help/autodoc/todo");
    if(file_size("/doc/help/autodoc/"+mud_name())==-1)
        mkdir("/doc/help/autodoc/"+mud_name());


//:FUNCTION scan_mudlib
//
// Recursively searches the mudlib for files which have been changed
// since the last time the docs were updated, and recreates the documentation
// for those files.
void scan_mudlib() {
    printf("Starting scan ...\n");
    files_to_do = ({ });
    dirs_to_do = ({ "/" });
    if(!last_time)
    {
        delete_directory("/doc/help/autodoc");
        make_directories();

    }
    continue_scan();
}

//:FUNCTION complete_rebuild
//
// Rebuild all the data, regardless of modification time
void complete_rebuild() {
    last_time = 0;
    scan_mudlib();
}

// Everything below here is
private:
// ---------------------------------------------------------------------


// This list are the directories NOT scanned.
private string * filtered_dirs = ({
  "/realms/", "/news/", "/secure/log/", "/ftp/", "/doc/", "/include/", "/secure/upgrades/",
  "/log/", "/estates/", "/open/", "/save/", "/tmp/", "/secure/save/", "/www/"
  });

string mod_name(string foo) {
    sscanf(foo, "%s.c", foo);
    return foo[strsrch(foo, "/", -1) + 1..];
}

string func_name(string bar) {
    sscanf(bar, "%s(", bar);
    return bar;
}

void process_file(string fname) {
    string file = read_file(fname);
    string line;
    string *lines, *match;
    string outfile = 0;
    int i;

    /* If the file has not been modified since the last time that DOC_D
     * scanned, there is no reason for it to be checked again -- Tigran */
    if(last_time &&
      get_dir(fname,-1)[0][2]<last_time)
        return;

    rm("/doc/help/autodoc/FIXME/" + mod_name(fname));
    rm("/doc/help/autodoc/todo/" + mod_name(fname));
    rm("/doc/help/autodoc/modules/" + mod_name(fname));
    rm("/doc/help/autodoc/command/" + mod_name(fname));
    rm("/doc/help/autodoc/hook/" + mod_name(fname));
    rm("/doc/help/autodoc/"+mud_name()+"/"+ mod_name(fname));
    delete_directory("/doc/help/autodoc/functions/" + mod_name(fname));

    if (!file) return;
    lines = explode(file, "\n");

    while (i < sizeof(lines)) {
        if (regexp(lines[i],"^[ \t]*//###")) {
            outfile = "/doc/help/autodoc/FIXME/" + mod_name(fname);
            write_file(outfile, "FIXME in file "+fname+" line "+(i+1)+":\n\n");
            while (sscanf(lines[i], "%*(^[ \t]*//###)%s", line)) {
                write_file(outfile, line+"\n");
                // line = "";
                i++;
            }
            write_file(outfile, "\nCode:\n"+implode(lines[i..i+3], "\n")+"\n");
            printf("Writing to: %O\n", outfile);
        }
        else if (lines[i][0..2] == "//:") {
            line = lines[i][3..];
            i++;
            if (line == "TODO") {
                outfile = "/doc/help/autodoc/todo/" + mod_name(fname);
                write_file(outfile, "TODO in file "+fname+" line "+i+":\n\n");
                while (lines[i][0..1] == "//") {
                    write_file(outfile, lines[i][2..]+"\n");
                    i++;
                }
            }
            else if (line == "MODULE") {
                outfile = "/doc/help/autodoc/modules/" + mod_name(fname);
                write_file(outfile, "Module "+mod_name(fname)+" (file: "+fname+"):\n\n");
                while (lines[i][0..1] == "//") {
                    write_file(outfile, lines[i][2..]+"\n");
                    i++;
                }
            }
            else if (line == "COMMAND") {
                outfile = "/doc/help/autodoc/command/" + mod_name(fname);
                write_file(outfile,"Command "+mod_name(fname)+" (file: "+fname+"):\n\n");
                while (lines[i][0..1] == "//") {
                    write_file(outfile, lines[i][2..]+"\n");
                    i++;
                }
            }
            else if (sscanf(line, "HOOK %s", line) == 1) {
                outfile = "/doc/help/autodoc/hook/" + line;
                write_file(outfile, "Hook "+line+":\nCalled by module "
                  +mod_name(fname)+" (file: "+fname+")\n\n");
                while (lines[i][0..1] == "//") {
                    write_file(outfile, lines[i][2..]+"\n");
                    i++;
                }
            }
            else if (sscanf(line, "FUNCTION %s", line) == 1) {
                if (func_name(line) != line)
                    log_file("autodoc", "Bad function name: "+fname+" line " + i
                      + ": " + line + "\n");
                seteuid(geteuid());
                mkdir("/doc/help/autodoc/functions/" + mod_name(fname));
                outfile = "/doc/help/autodoc/functions/" + mod_name(fname) + "/" + func_name(line);
                write_file(outfile, "Function "+line+":\nDefined in module "
                  +mod_name(fname)+" (file: "+fname+")\n\n");
                while (lines[i][0..1] == "//") {
                    write_file(outfile, lines[i][2..]+"\n");
                    i++;
                }
                /* regexp() doesn't match any ";", had to replace_string() them */
                match = regexp(map(lines[i..i+19], (: replace_string($1, ";", "#") :)), "\\<"+line+"\\>", 1);
            }
            else if (line == upper_case(mud_name())) {
                outfile = "/doc/help/autodoc/"+mud_name()+"/" + mod_name(fname);
                write_file(outfile,"**** "+fname+" ****\n\n");
                while (lines[i][0..1] == "//") {
                    write_file(outfile, lines[i][2..]+"\n");
                    i++;
                }
            }
            else {
                log_file("autodoc", "Bad header tag: "+fname+" line "+i
                  +": " + line + "\n");
                seteuid(geteuid());
            }
            printf("Writing to: %O\n", outfile);
        }
        else
            i++;
    }
}

void continue_scan() {
    string *files;
    mixed *item;

    for (int i = 0; i < 10; i++) {
        if (sizeof(dirs_to_do)) {
            if(strsrch(dirs_to_do[0],".svn/")!=-1 || strsrch(dirs_to_do[0], "/bak") != -1) {
            dirs_to_do[0..0] = ({ });
            continue;
            }
            files = get_dir(dirs_to_do[0], -1);
            foreach (item in files) {
                if (item[1] == -2) {
                    string dir = dirs_to_do[0] + item[0] + "/";
                    if ( member_array(dir, filtered_dirs) != -1 )
                        continue;
                    printf("Scanning %s ...\n", dirs_to_do[0]);
                    dirs_to_do += ({ dir });
                } else
                if (item[2] > last_time && item[0][<2..<1] == ".c") {
                    files_to_do += ({ dirs_to_do[0] + item[0] });
                }
            }
            dirs_to_do[0..0] = ({ });
        } else
        if (sizeof(files_to_do)) {
            printf("Updating docs for %s ...\n", files_to_do[0]);
            process_file(files_to_do[0]);
            files_to_do[0..0] = ({ });
        } else {
            printf("Done.\n");
            HELP_D->Reload();
            last_time = time();
            return;
        }
    }   
    call_out( (: continue_scan :), 1);
}

void
create()
{
    if(pending()) return;
    else
    {
add_event(base_name(this_object()), base_name(this_object()), "scan_mudlib", ({}), 86400, 1);
        return;
    }
}

The following replaces the /daemon/help.c daemon, it is only necessary if you want the autodoc stuff to show on the help index. It works but I would like to see it support subindexes.
Code: [Select]
/*    /verbs/common/help.c
 *    from the Dead Souls Object Library
 *    created by Descartes of Borg 951021
 *    Version: @(#) help.c 1.15@(#)
 *    Last Modified: 96/12/14
 */

#include <lib.h>
#include <dirs.h>
#include <daemons.h>

inherit LIB_DAEMON;

static private string Error, SeeAlso = "";
static private mapping Indices;

static private void LoadIndices();
string GetHelp(string str);
string GetHelpByIndex(string index, string topic);
string GetTopic(string index, string topic);
varargs string array GetIndices(string topic);

static void create() {
    daemon::create();
    SetNoClean(1);
    Error = 0;
    LoadIndices();
}

int CanAccess(object who, string index) {
    switch(index) {
    case "admin commands":
        return archp(who);

    case "creator commands": case "creator documents":
    case "library objects": case "daemon objects":
        return creatorp(who);

    default:
        return 1;
    }
}

static private void LoadIndices() {
    string array tmp, pmt;
    function f;
    string dir;

    f = function(string str) { return str[0..<3]; };
    Indices = ([]);

    tmp = get_dir(DIR_ADMIN_VERBS + "/*.c") + get_dir(DIR_ADMIN_CMDS + "/*.c")
    + get_dir(DIR_SECURE_ADMIN_CMDS + "/*.c");
    Indices["admin commands"] = map(tmp, f);

    tmp = get_dir(DIR_COMMON_VERBS+"/*.c") +
    get_dir(DIR_COMMON_CMDS + "/*.c") +
    get_dir(DIR_SECURE_COMMON_CMDS + "/*.c") +
    get_dir(DIR_ITEM_VERBS + "/*.c") +
    get_dir(DIR_PLAYER_VERBS+"/*.c") +
    get_dir(DIR_PLAYER_CMDS+"/*.c") +
    get_dir(DIR_ROOM_VERBS + "/*.c") +
    get_dir(DIR_SPELL_VERBS + "/*.c") +
    get_dir(DIR_SECURE_PLAYER_CMDS + "/*.c");
    Indices["commands"] = map(tmp, f);

    tmp = get_dir(DIR_CREATOR_VERBS+"/*.c") + get_dir(DIR_CREATOR_CMDS+"/*.c")
    + get_dir(DIR_SECURE_CREATOR_CMDS + "/*.c");
    Indices["creator commands"] = map(tmp, f);

    tmp = get_dir(DIR_UNDEAD_VERBS "/*.c");
    Indices["undead commands"] = map(tmp, f);

    tmp = SOUL_D->GetEmotes();
    Indices["feelings"] = tmp;

    tmp = filter(map(get_dir(DIR_SPELLS "/*.c"),
        function(string file) {
            file = DIR_SPELLS "/" + file;
            if( file->GetVerb() == "cast" ) {
                return file->GetSpell();
            }
            return 0;
          }), (: $1 :));
      Indices["spells"] = tmp;

      tmp = filter(map(get_dir(DIR_SPELLS "/*.c"),
          function(string file) {
              file = DIR_SPELLS "/" + file;
              if( file->GetVerb() == "pray" ) {
                  return file->GetSpell();
              }
              return 0;
            }), (: $1 :));
        Indices["prayers"] = tmp;

        if( tmp = get_dir(DIR_PLAYER_HELP + "/") )
            Indices["player documents"] = tmp + ({ "soul" });
        else Indices["player documents"] = ({ "soul" });

        if( tmp = get_dir(DIR_CREATOR_HELP "/") )
            Indices["creator documents"] = tmp;
        else Indices["creator document"] = ({});

        if( tmp = (string array)CLASSES_D->GetClasses() )
            Indices["classes"] = tmp;
        else Indices["classes"] = ({});

        if( tmp = (string array)RACES_D->GetRaces(1) )
            Indices["races"] = tmp;
        else Indices["races"] = ({});

        if( tmp = get_dir(DIR_LAW_HELP "/") )
            Indices["law"] = tmp;
        else Indices["law"] = ({});   

        if( tmp = get_dir(DIR_RELIGION_HELP "/") )
            Indices["religion"] = tmp;
        else Indices["religion"] = ({});

        Indices["library objects"] = ({});
        foreach(dir in ({ DIR_LIB, DIR_SECURE_LIB })){
            if( !(tmp = get_dir(dir + "/*.c")) ) continue;
            else Indices["library objects"] +=
                map(tmp, (: $(dir)+"/"+$1[0..<3] :));
        }

        Indices["daemon objects"] = ({});
        foreach(dir in ({ DIR_DAEMONS, DIR_SECURE_DAEMONS })) {
            if( !(tmp = get_dir(dir + "/*.c")) ) continue;
            else Indices["daemon objects"] +=
                map(tmp, (: $(dir)+"/"+$1[0..<3] :));
        }
        Indices["Autodoc documents"] = ({});
        pmt = get_dir("/doc/help/autodoc/");
        foreach(dir in pmt) {
        if(dir=="functions") continue;
        tmp=get_dir("/doc/help/autodoc/"+dir+"/");
        if( !tmp ) continue;
        else if(sizeof(tmp)){
            Indices["Autodoc documents"] += map(tmp, (: $(dir)+"/"+$1[0..] :));
        }
        }
        Indices["Autodoc functions"] = ({});
        pmt = get_dir("/doc/help/autodoc/functions/");
        foreach(dir in pmt) {
        tmp=get_dir("/doc/help/autodoc/functions/"+dir+"/");
        if( !tmp ) continue;
        else if(sizeof(tmp)){
            Indices["Autodoc functions"] += map(tmp, (: $(dir)+"/"+$1[0..] :));
        }
        }
      }

      string GetHelp(string str) {
          string *tmp, choice,ret ="";
          string topic;
          int x;

          Error = 0;
          if( !str || str == "" || str == "help" ) {
              return ("Syntax: <help>\n"
                "        <help index>\n"
                "        <help TOPIC>\n"
                "        <help INDEX TOPIC>\n\n"
                "The special topic, \"help index\", puts you into "
                "a menu driven index of categories for which help exists.\n\n"
                "For players, \"help commands\" will provide an index of "
                "available player commands.\n "
                "For creators, \"help creator commands\" provides an "
                "index of available creator commands.\n\n "
                "Try \"help commands\" "
                "and \"help creator commands\" first. \n\n"
                " ");
          }
          if(member_array(str, CHAT_D->GetChannels()) != -1 &&
            str != "newbie"){
              return "See: help channels";
          }
          if( sscanf(str, "adverbs %s", topic) || str == "adverbs" ) {
              return (string)SOUL_D->GetHelp(str);
          }
          tmp = GetIndices(str);
          if( sizeof(tmp) > 0){
              if( sizeof(tmp) > 1){
                  if(member_array("admin commands",tmp) != -1) choice = "admin commands";
                  else if(member_array("creator commands",tmp) != -1) choice = "creator commands";
                  else if(member_array("player commands",tmp) != -1) choice = "player commands";
              }
              if(!choice) choice = tmp[0];
              tmp -= ({ choice });

              if(sizeof(tmp)) SeeAlso = "\nThere also exists help for \"" + str + "\" under the following "
                  "indices:\n" + implode(tmp, ", ");
              ret = GetHelpByIndex(choice, str);
              if(ret) ret += SeeAlso;
              SeeAlso = "";
              return ret;
          }
          topic = "";
          str = trim(str);
          while( (x = strsrch(str, " ")) != -1 ) {
              if( topic != "" ) {
                  topic = topic + " " + str[0..(x-1)];
              }
              else {
                  topic = str[0..(x-1)];
              }
              str = str[(x+1)..];
              if( Indices[topic] && strlen(str) ) {
                  return GetHelpByIndex(topic, str);
              }
          }
          if(this_player() && adminp(this_player())){
              Error = "Help for the topic \"" + str + "\" could not be found.";
          }
          else {
              Error = "The search for help on the topic \"" + str + "\" yields you no results.";
          }
          return 0;
      }

      string GetHelpByIndex(string index, string topic) {
          mixed array tmparr, fun;
          mapping tmpmap;
          object ob;
          string help, file, tmpstr;

          if( this_player() && !CanAccess(this_player(), index) ) {
              Error = "You do not have access to that information.";
              return 0;
          }
          switch(index) {
          case "admin commands": case "creator commands": case "undead commands":
          case "commands":
              switch(index) {
              case "admin commands":
                  if( file_exists( DIR_ADMIN_VERBS + "/" + topic + ".c") )
                      file = DIR_ADMIN_VERBS + "/" + topic;
                  else if( file_exists( DIR_ADMIN_CMDS + "/" + topic + ".c") )
                      file = DIR_ADMIN_CMDS + "/" + topic;
                  else file = DIR_SECURE_ADMIN_CMDS + "/" + topic;
                  break;

              case "creator commands":
                  if( file_exists( DIR_CREATOR_VERBS + "/" + topic + ".c") )
                      file = DIR_CREATOR_VERBS + "/" + topic;
                  else if( file_exists(DIR_CREATOR_CMDS + "/" + topic + ".c") )
                      file = DIR_CREATOR_CMDS + "/" + topic;
                  else file = DIR_SECURE_CREATOR_CMDS + "/" + topic;
                  break;     

              case "commands":
                  foreach(string directory in ({ DIR_COMMON_VERBS,
                      DIR_COMMON_CMDS,
                      DIR_SECURE_COMMON_CMDS,
                      DIR_ITEM_VERBS,
                      DIR_PLAYER_VERBS,
                      DIR_PLAYER_CMDS,
                      DIR_SECURE_PLAYER_CMDS,
                      DIR_ROOM_VERBS,
                      DIR_SPELL_VERBS })) {
                      if( file_exists(directory + "/" + topic + ".c") ) {
                          file = directory + "/" + topic;
                          break;
                      }
                  }
                  break;

              case "undead commands":
                  file = DIR_UNDEAD_VERBS + "/" + topic;
                  break;     
              }
              if( !file_exists(file + ".c") ) {
                  Error = "No such " + index[0..<2] + " exists.";
                  return 0;
              }
              if( catch(help = file->GetHelp(topic)) ) {
                  Error = "An error occurred in attempting to access help.";
                  return 0;
              }
              if( !help ) {
                  string *syn, *pd;
                  string line;

                  pd = regexp(explode(parse_dump(), "\n"), file[1..]);
                  syn = ({});
                  foreach(line in pd) {
                      sscanf(line, "%*s"+file[1..]+") %s", tmpstr);
                      syn += ({ tmpstr });
                  }
                  if( !sizeof(syn) ) {
                      if( function_exists("help", load_object(file)) ) {
                          Error = " ";
                          file->help();
                          write(SeeAlso);
                          SeeAlso = "";
                          return 0;
                      }
                      Error = "Unable to locate any syntax information on " +
                      topic + ".";
                      return 0;
                  }
                  help = "Syntax: " + topic + " " + syn[0] + "\n";
                  if( sizeof(syn) == 1 ) help += "\n";
                  else {
                      foreach(line in syn[1..])
                      help += "        " + topic + " " + line + "\n";
                      help += "\n";
                  }
                  help += "No detailed documentation exists for this command.";
              }
              help = "Index: %^GREEN%^" + index + "%^RESET%^\n" +
              "Topic: %^GREEN%^" + topic + "%^RESET%^\n\n" + help;
              return help;

          case "player documents": case "creator documents":
          case "law": case "Autodoc documents": case "Autodoc functions":
              switch(index) {
              case "player documents":
                  if( topic == "soul" ) {
                      help = SOUL_D->GetHelp("soul");
                      help = "Index: %^GREEN%^" + index + "%^RESET%^\n" +
                      "Topic: %^GREEN%^" + topic + "%^RESET%^\n\n" + help;
                      return help;
                  }
                  file = DIR_PLAYER_HELP "/" + topic;
                  break;

              case "Autodoc documents":
                  file = "/doc/help/autodoc/" + topic;
                  break;
              case "Autodoc functions":
                  file = "/doc/help/autodoc/functions/" + topic;
                  break;
              case "creator documents":
                  file = DIR_CREATOR_HELP "/" + topic;
                  break;

              case "law":
                  file = DIR_LAW_HELP "/" + topic;
                  break;
              }
              if( !file_exists(file) ) {
                  Error = "No such " + index[0..<2] + " is available.";
                  return 0;
              }
              if( !(help = read_file(file)) ) {
                  Error = "The document " + topic + " was empty.";
                  return 0;
              }
              help = "Index: %^GREEN%^" + index + "%^RESET%^\n" +
              "Topic: %^GREEN%^" + topic + "%^RESET%^\n\n" + help;
              return help;

          case "feelings":
              help = SOUL_D->GetHelp(topic);
              if( !help ) {
                  Error = "No such " + index[0..<2] + " is available.";
                  return 0;
              }
              help = "Index: %^GREEN%^" + index + "%^RESET%^\n" +
              "Topic: %^GREEN%^" + topic + "%^RESET%^\n\n" + help;
              return help;;

          case "library objects":
              topic = GetTopic(index, topic);
              if( !file_exists(topic+".c") ){
                  Error = "No such topic found.";
                  return 0;
              }
              if(  catch(help = topic->GetHelp(topic)) ) {
                  Error = "An error occurred in attempting to access help.";
                  return 0;
              }
              if( !help ) {
                  help = "No synopsis available for this object.\n\n";
              }
              else {
                  help = "Synopsis:\n" + help + "\n\n";
              }
              tmparr = stat(topic + ".c");
              tmpstr = "Object: " + topic + "\n"
              "Last Modified: " + ctime(tmparr[1]) + "\n";
              if( tmparr[2] ) {
                  tmpstr += "Last Loaded: " + ctime(tmparr[2]) + "\n\n";
              }
              tmparr = inherit_list(ob = find_object(topic));
              if( !sizeof(tmparr) ) {
                  tmpstr += "No inherited objects\n\n";
              }
              else {
                  tmpstr += "Inherits:\n" + format_page(tmparr, 4) + "\n";
              }
              tmparr = functions(ob, 1);
              tmpmap = ([]);
              foreach(fun in tmparr) {
                  if( function_exists(fun[0], ob) != topic ) {
                      continue;
                  }
                  if( fun[0] == "#global_init#" ) {
                      continue;
                  }
                  if( tmpmap[fun[0]] ) {
                      continue;
                  }
                  else {
                      tmpmap[fun[0]] = ([ "type" : fun[2],
                        "args" : (fun[1] ? fun[3..] : ({})) ]);
              }
          }
          help = tmpstr + help;
          if( !sizeof(tmparr) ) {
              help += "No functions\n\n";
          }
          else {
              string fnc;

              help += "Functions:\n";
              tmparr = sort_array(keys(tmpmap), 1);
              foreach(fnc in tmparr) {
                  help += tmpmap[fnc]["type"] + fnc + "(" +
                  implode(tmpmap[fnc]["args"], ", ") + ")\n";
              }
          }
          help = "Index: %^GREEN%^" + index + "%^RESET%^\n" +
          "Topic: %^GREEN%^" + topic + "%^RESET%^\n\n" + help;
          return help;

      case "daemon objects":
          topic = GetTopic(index, topic);
          if( !topic || catch(help = topic->GetHelp(topic)) ) {
              Error = "An error occurred in attempting to access help for that.";
              return 0;
          }
          if( !help ) {
              help = "No synopsis available for this object.\n\n";
          }
          else {
              help = "Synopsis:\n" + help + "\n\n";
          }
          tmparr = stat(topic + ".c");
          tmpstr = "Object: " + topic + "\n"
          "Last Modified: " + ctime(tmparr[1]) + "\n";
          if( tmparr[2] ) tmpstr += "Last Loaded: " + ctime(tmparr[2]) + "\n\n";
          tmparr = inherit_list(ob = find_object(topic));
          if( !sizeof(tmparr) ) tmpstr += "No inherited objects\n\n";
          else tmpstr += "Inherits:\n" + format_page(tmparr, 4) + "\n";
          tmparr = functions(ob, 1);
          tmpmap = ([]);
          foreach(fun in tmparr) {
              if( function_exists(fun[0], ob) != topic ) continue;
              if( fun[0] == "#global_init#" ) continue;
              if( tmpmap[fun[0]] ) continue;
              else tmpmap[fun[0]] = ([ "type" : fun[2],
                    "args" : (fun[1] ?  fun[3..] : ({})) ]);
      }
      help = tmpstr + help;
      if( !sizeof(tmparr) ) help += "No functions\n\n";
      else {
          string fnc;

          help += "Functions:\n";
          tmparr = sort_array(keys(tmpmap), 1);
          foreach(fnc in tmparr)
          help += tmpmap[fnc]["type"] + fnc + "(" +
          implode(tmpmap[fnc]["args"], ", ") + ")\n";
      }
      help = "Index: %^GREEN%^" + index + "%^RESET%^\n" +
      "Topic: %^GREEN%^" + topic + "%^RESET%^\n\n" + help;
      return help;   

      case "religons": case "religion":
      if( file_exists(DIR_RELIGION_HELP "/" + topic) ) {
          help = read_file(DIR_RELIGION_HELP "/" + topic);
          help = "Index: %^GREEN%^" + index + "%^RESET%^\n" +
          "Topic: %^GREEN%^" + topic + "%^RESET%^\n\n" + help;
          return help;
      }
      Error = "No such religion exists.";
      return 0;

      case "races":
      if( help = (string)RACES_D->GetHelp(topic) ) {
          help = "Index: %^GREEN%^" + index + "%^RESET%^\n" +
          "Topic: %^GREEN%^" + topic + "%^RESET%^\n\n" + help;
          return help;
      }
      else if( file_exists(DIR_RACE_HELP + "/" + topic) ){
          help = read_file(DIR_RACE_HELP + "/" + topic);
          return help;
      }
      Error = "There is no such race.";
      return 0;

      case "spells": case "prayers":
      ob = SPELLS_D->GetSpell(topic);
      if( !ob ) {
          Error = "No such spell exists.";
          return 0;
      }
      if( !(help = ob->GetHelp(topic)) ) {
          Error = "No help is available for that spell.";
          return 0;
      }
      help = "Index: %^GREEN%^" + index + "%^RESET%^\n" +
      "Topic: %^GREEN%^" + topic + "%^RESET%^\n\n" + help;
      return help;

      case "classes":
      if( help = (string)CLASSES_D->GetHelp(topic) ) {
          help = "Index: %^GREEN%^" + index + "%^RESET%^\n" +
          "Topic: %^GREEN%^" + topic + "%^RESET%^\n\n" + help;
          if( file_exists(DIR_CLASS_HELP + "/" + topic) )
              help += read_file(DIR_CLASS_HELP + "/" + topic);
          return help;
      }
      Error = "No such class exists.";
      return 0;

      default:
      Error = "No help exists for the index " + index + ".";
      return 0;
  }
}

varargs string array GetIndices(string topic) {
    string array topics, val;
    string ind, tmp;

    if( !topic ) {
        return sort_array(keys(Indices), 1);
    }
    topics = ({});
    foreach(ind, val in Indices) {
        if( !CanAccess(this_player(), ind) ) {
            continue;
        }
        tmp = GetTopic(ind, topic);
        if( member_array(tmp, val) != -1 ) {
            topics += ({ ind });
        }
    }
    return sort_array(topics, 1);
}

string GetTopic(string index, string topic) {
    string array dirlist;
    string dir;

    if( index != "library objects" && index != "daemon objects" ) {
        return topic;
    }
    if( index == "library objects" ) {
        dirlist = ({ DIR_LIB, DIR_SECURE_LIB });
    }
    else {
        dirlist = ({ DIR_DAEMONS, DIR_SECURE_DAEMONS });
    }
    if( strlen(topic) > 2 && topic[<2..] == ".c" ) {
        topic = topic[0..<3];
    }
    if( file_exists(topic + ".c") ) {
        return topic;
    }
    foreach(dir in dirlist) {
        if( file_exists(dir + "/" + topic + ".c") ) {
            return dir + "/" + topic;
        }
    }
    return 0;
}

string array GetTopics(string index) {
    string array topics = Indices[index];

    if( !topics ) {
        return 0;
    }
    else {
        return sort_array(topics, 1);
    }
}

string GetLastError() {
    return Error;
}

void Reload() {
    LoadIndices();
}

Pages: 1 [2] 3