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.


Topics - Archaegeo

Pages: [1]
1
Lima Lounge / Lima login flowpath
« on: February 18, 2008, 01:31:39 pm »
Lima Login Flowpath

On player connection to mud, driver calls
object connect()
in /secure/master.c
->    attempts to return the USER_OB as defined by get_player_fname() in master.c
currently this is /secure/user.c as defined in /include/mudlib.h

Driver then calls
private nomask void logon()
in /secure/user.c from its inherited file /secure/user/login.c
->    this calls login_handle_logon(INITIAL_PROMPT, 0); in login.c which handles login using a switch command

NEW PLAYER
private nomask varargs
void login_handle_logon(int state, mixed extra, string arg)
->    This handles displaying the welcome info, getting name and passwords.
Control is passed to userinfo_handle_logon() in /secure/user/userinfo.c after confirming password.

protected nomask varargs
void userinfo_handle_logon(int state, mixed extra, string arg)
->   This gathers gender, email, real name, and webpage then control is passed to sw_body_handle_new_logon() in /secure/user/sw_body.c

protected nomask void sw_body_handle_new_logon()
->   This autowiz's if that flag is set, set the players priv's, and then calls create_body() in sw_body

void create_body()
->    If races are used, this gets the list and passes control to got_entry() in sw_body to let player choose their race.

void got_entry(function when_done, string line)
->   This handles the racial select, or relists them, or provides help information on a race. control is then passed to incarnate() in sw_body.c

private nomask void incarnate(int is_new, string bfn)
->   This function does several things. First it creates a body for them based on their racial pick.
Then it calls reset_parse_info in master.c which calls parse_refresh() (Not sure what this does)
Then it has the LAST_LOGIN_D log my name and ip
Then it sets my gender in the body
Then it saves me
Then it starts my shell
Then it calls enter_game in /std/body.c
->    This sets up cmd hooks, moves me to start room (defined in /std/body/start.c), announces entrance, makes me do a look and updates my health
    Then it runs a .login script for my shell, if I am a wizard
Then it calls init_stats in the /std/m_bodystats.c
Then it saves me again
Then it calls initialize_user which is back in /secure/user/login.c which initializes channels and shows the didlog

Now I am in game in the startroom playing.

2
Dead Souls Support / Undocumented E/S/Lfun identification
« on: January 11, 2008, 11:29:08 pm »
The following snippet of code will show you which of the funs in /secure/sefun/sefun.h are not documented via man (ie in /doc/sefuns/ or /doc/efun/all/ or /doc/lfun/all).

So out of the 270 functions in sefun.h in 2.6.2, about 150 are missing documentation (but are still useful).

Code: [Select]
int d;
string b;
string fun;
string *funs;
string *sdi=get_dir("/doc/sefun/");
string *edi=get_dir("/doc/efun/all/");
string *ldi=get_dir("/doc/lfun/all/");
string fi=read_file("/secure/sefun/sefun.h");
funs=explode(fi, "\n");
foreach(fun in funs){
   if(strsrch(fun, ";") != -1 && strsrch(fun, "//")==-1){
      b=fun[0..strsrch(fun,"(")-1];
      b=b[strsrch(b, "",-1)+1..];
      sscanf(b, "*%s",b);
   }
   if(member_array(b, sdi) == -1 && member_array(b, edi) == -1 && member_array(b, ldi) == -1){
      write(b);d++;
   }
}
write("Total: "+d);

3
Dead Souls Support / New Player Login Flowpath
« on: January 10, 2008, 05:04:29 pm »
Just cause I figure there are more folks than me out there who like to know such things, this is the flowpath a new player goes through during loing (hence where you would branch them off or change things to affect such things as racial choices, stat generation, etc).

Code: [Select]
Player Login flowpath

NEW PLAYER PATH
Driver calls connect() in master.c
/secure/daemon/master.c
object connect(int port)

Driver calls logon() in object returned by connect call
/secure/lib/connect.c
varargs static void logon(mixed foo)
static void InputName(string namen, string kill_me)
static void eventCreatePlayer(string cap)
static void ConfirmName(string ans, string cap)
static void AgeCheck(string ans, string cap)
static void CreatePassword(string pass, string cap)
static void ConfirmPassword(string control, string cap, string pass)
static void InputGender(string str, string cap)
static void InputCapName(string name, string cap)
static void InputEmail(string email)
static void InputRealName(string rname)
static void InputRace(string str)
static void cmdPick(string args)
void eventCompleteChar()
Player->SetRace(raza)
Player->SetProperty("brand_spanking_new",1)
Player->AddCurrency("silver",random(100)+57)
Player->SetRace(raza)
Player->SetPassword(Passwort)
Player->SetGender(Gendre)
Player->SetCapName(CapNamen)
Player->SetEmail(E_Mail)
Player->SetRealName(TrueName)
Player->SetLanguage("Common",100)
Player->SetDefaultLanguage("Common")
this_player()->SetWimpy(20)
this_player()->SetTerminal("ansi")
PLAYERS_D->AddPlayerInfo(Name)
static private void eventEnterGame()

4
Code Vault / Bits - No, not the naught type
« on: January 07, 2008, 09:12:17 am »
Ok, some folks might appreciate a little example of why Bits are good to use.  The below example is for your standard room. This is not in DS, just an example, and is not public domain, but is so simple that you can make something like it without copying it.

Ok, so first I have a room.h file where I define my bits.
Code: [Select]
/******************************************************************************
File: room.h
Desc: Headers, bit, and flag settings for rooms.
******************************************************************************/

// Room bit inheritable
// The room_bits object has all room specific bit and flag settings.
#define ROOM_BITS               "/lib/room/room_bits"

// These are toggled bits for each room.  Usage is:
// void RoomBits_SetBit(int bit);   // set a bit
// void RoomBits_ClearBit(int bit); // clear a bit
// int RoomBits_TestBit(int bit);   // test if a bit is set
#define ROOM_BIT_SAFE_ROOM      0       // Room is safe: no combat, no spells
#define ROOM_BIT_INDOORS        1       // (unused)
#define ROOM_BIT_NOMOB          2       // NPC's cannot enter
#define ROOM_BIT_TUNNEL         3       // Only one player in room at a time
#define ROOM_BIT_NOSMELL        4       // Scents are not left in the room
#define ROOM_BIT_NOPRINTS       5       // No prints are left in the room
#define ROOM_BIT_NOTRACK        6       // Nothing can be tracked
#define ROOM_BIT_PRIVATE        7       // Can't teleport/trans into room
#define ROOM_BIT_NO_ATTACK      8       // Room is no attack
#define ROOM_BIT_NO_MAGIC       9       // Room is no magic
#define ROOM_BIT_NO_STEAL       10      // Room is no steal
#define ROOM_BIT_NO_SUMMON      11      // Room is no summon
#define ROOM_BIT_NO_TELEPORT    12      // Room is no teleport
#define ROOM_BIT_NO_SCRY        13      // Room is no scry
#define ROOM_BIT_SOUNDPROOF     14      // Room is cut off from lines, tells
#define ROOM_BIT_DEATH          15      // Player dies if they enter
#define ROOM_BIT_NO_AIR         16      // Gasp!  Need air to breathe!

Now that they are defined, when you create a room_bits.c that your LIB_ROOM (or base room) would inherit:
Code: [Select]
/********************************************************************
File: room_bits.c
Written By: Nevin
Desc: File to control room bit and flag settings in one location.
Much cleaner than before and provides for a standard method of
setting information for rooms.
********************************************************************/

#include <room.h>

string roomBits = "";           // room bit string var

/********************************************************************
Room Bit Code
********************************************************************/
// Sets a room bit
void RoomBits_SetBit(int bit)
{
   roomBits = set_bit(roomBits, bit);
}

// Clears a room bit
void RoomBits_ClearBit(int bit)
{
   roomBits = clear_bit(roomBits, bit);
}

// Tests a room bit
int RoomBits_TestBit(int bit)
{
   return test_bit(roomBits, bit);
}

Why create the room_bits.c? You don't have to, you can just include it in your base room, this is just for cleanliness sake.

Ok, so what do we gain by doing this? It saves a lot of memory over time, since one bit can hold 64 values (0 to 63) in just one character in the string.

Plus, in the old way, for each of the properties in rooms.h you would either need a seperate function (ie getIndeors() setIndoors() etc) or you would need to set each property (set_property("indoors",1));

By using bits though, you get one simple call to set them and one simple call to check em, and it takes up less memory and just looks cleaner. (RoomBits_SetBit(ROOM_BIT_NO_ATTACK);)

You see if a bit is set with RoomBits_TestBit(ROOM_BIT_NO_ATTACK) and you can clear it with the clear_bit call.  This also makes code a lot easier to read as well.

Just my 0.02

5
Code Vault / Census Daemon for DS
« on: January 03, 2008, 08:56:54 am »
The following code and code changes add the support of a census daemon. This will let your players know when peak usage is each day (in case they want to play when more or less players are on) and it keeps track of new users and the high usage for the mud. It prints the results in a ascii graphical format with different colors based on the total users each hour or each day.

Syntax:
census - Returns the daily usage by hour
census m - Returns the monthly usage by day
census n - Returns the names of everyone who logged on yesterday.

First is the census daemon.  Put it in /daemon/census_d.c
Code: [Select]
// Census tool.
#include <dirs.h>

int totusers();
int pending() {if(event_pending(this_object())) return 1;return 0;}
int nexttot() {add_event(base_name(this_object()), base_name(this_object()), "totusers", ({}), 300, 1);return 1;}
int lastran, lastday, maxday, halfmax;
int *daily;
int *monthly;
string *names,*newbie, *oldnames, *oldnewbie;
int forever;
void login(string name);
void newuser(string name);

int *query_daily() {return daily;}
int *query_monthly() {return monthly;}
int query_forever() {return forever;}
int query_maxday();
string *query_names() {return oldnames;}
string *query_newbie() {return oldnewbie;}

int query_maxday() {
   int *a,b,c,d;
      a=query_daily();
      for(b=0,c=sizeof(a);b<c;b++) {
      if(a[b]==0) continue;
      if(a[b] > d)
         d=a[b];
      }
      return d;
   }

string query_lastran() {return ("lastran "+ctime(lastran)+"lastday "+ctime(lastday));}

void create() {

  if(!event_pending(this_object())) {
     add_event(base_name(this_object()), base_name(this_object()), "totusers", ({}), 300, 1);
  }
  names=({});
  newbie=({});
  oldnames=({});
  oldnewbie=({});
  daily = allocate(48);
  monthly = allocate(31);
  if(file_exists(DIR_SAVE+"/usage.o"))
    restore_object(DIR_SAVE+"/usage");
  return;
}

void fix_monthly(){
    monthly = allocate(31);
    this_object()->update_usage();
    return;
}


void login(string name) {
   if(!name) return;
   if(member_array(name,names) != -1) return;
   if(!names) names = ({});
   names+=({name});
   return;
}

void newbie(string name) {
   if(!name) return;
   if(member_array(name, newbie) != -1) return;
   if(!newbie) newbie = ({});
   newbie += ({name});
   return;
}

string graph(int a) {
   string sym;
   switch(a) {
      case 20: sym = "%^BOLD%^%^RED%^*";break;
      case 16..19: sym = "%^BOLD%^%^WHITE%^*";break;
      case 12..15: sym = "%^BOLD%^%^YELLOW%^*";break;
      case 8..11: sym = "%^GREEN%^*";break;
      case 4..7: sym = "%^CYAN%^*"; break;
      case 0..3: sym = "%^BLUE%^*";break;
      default: sym =  "%^BOLD%^%^WHITE%^^";break;
   }
   return sym;
}


string display_daily() {

   string disp;
   int i,j;
   disp = "";
   i=20;
   while(i>0) {
      for(j=0;j<24;j++) {
         if(daily[j] > 3*i)
         disp += graph(i);
         else disp += " ";
         if(daily[j+24] > 3*i)
         disp += graph(i);
         else disp += " ";
         disp += " ";
      }
      disp += "\n";
      i--;
   }
   disp += "%^RESET%^";
   for(j=0;j<24;j++) {
      if(j < 10) disp += "0"+j+" ";
      else disp += "" + j + " ";
   }
      disp +="\n";
   return disp;
}

string display_monthly() {

   string disp;
   int i,j;
   disp = "";
   i=20;
   while(i>0) {
      for(j=1;j<31;j++) {
         if(monthly[j] > 3*i)
         disp += graph(i);
         else disp += " ";
         disp += " ";
      }
      disp += "\n";
      i--;
   }
   disp += "%^RESET%^";
   return disp;
}
int query_day() {
   int day;
   sscanf(ctime(time()),"%*s %*s %d %*d:%*d:%*s %*s", day);
   return day;
}

varargs void update_usage(int frc) {
    int hour, min, day;

    sscanf(ctime(time()),"%*s %*s %d %d:%d:%*s %*s", day, hour, min);
    if(min > 30 && min < 60 )  hour = hour+24;
    if(sizeof(users()) > halfmax) daily[hour]=(int)sizeof(users());
    else daily[hour]=halfmax;
    maxday = query_maxday();
    if(maxday > forever) forever = maxday;
    lastran = time();
    halfmax = 0;
    if( (time() > (lastday + 86400)) || frc) {
      monthly[day]=(int)maxday;
      maxday = 0;
      oldnames = names;
      oldnewbie = newbie;
      names = ({});
      newbie = ({});
      lastday = time();
    }
    save_object(DIR_SAVE+"/usage");
    return;
}

int totusers() {
  halfmax = sizeof(users());
  call_out("nexttot",300);
  if(time() > (lastran+1600) ) update_usage();
  return 1;
}

Next is the census command, put it in /cmds/players/census.c
Code: [Select]
/*  /cmds/players/census.c
 *  Made for Dead Souls lib
 *  Created by Archaegeo
 *  Do not remove this header
 */


#define USAGE "/daemon/census_d"


mixed cmd(string arg) {
   int *a, b, c, d, e, f, g;
   string *nm, dys;
   if(arg=="m") {
      message("info","\n"+USAGE->display_monthly(), this_player());
      c=USAGE->query_day();
      dys="";
      for(b=0;b<31;b++) {
         if((b+1)==c) {
            if(b<9) dys +="%^YELLOW%^"+(b+1)+" %^RESET%^";
            else if(b>8 && b<19) dys +="%^YELLOW%^"+(1)+" %^RESET%^";
            else if(b<29) dys +="%^YELLOW%^"+(2)+" %^RESET%^";
            else dys +="%^YELLOW%^"+(3)+" %^RESET%^";
         }
         else {
            if(b<9) dys +="%^RESET%^"+(b+1)+" ";
            else if(b>8 && b<19) dys +="%^RESET%^"+(1)+" ";
            else if(b<29) dys +="%^RESET%^"+(2)+" ";
            else dys +="%^RESET%^"+(3)+" ";
         }
      }
      message("info",dys+"\n"
           "                  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1\n",this_player());
      message("info",
      "                            Daily Highs for Past Month\n", this_player());
   }
   else if(arg == "n") {
      message("info","The following people were online yesterday.",this_player());
      nm=USAGE->query_names();
      write(format_page(nm, 5) +"\n");
   }
   else {
   message("info","\n"+USAGE->display_daily(), this_player());
   message("info",
   "                            Daily Census Chart\n", this_player());
   }
   a=USAGE->query_monthly();
   d=0;
   g=0;
   for(b=0,c=sizeof(a);b<c;b++) {
      if(a[b]==0) continue;
      if(a[b] > d) {
         d=a[b];
         g=b;
      }
      if(a[b] > 0) e++;
      f+=a[b];
   }
   b=USAGE->query_forever();
   write("            New Users Yesterday:     "+sizeof((USAGE->query_newbie())));
   write("            Unique Logins Yesterday: "+sizeof((USAGE->query_names())));
   write("            High in last 24 hours:   "+USAGE->query_maxday());
   write("            Daily Average:           "+(f/e)+"\n            High for Month:          "+d+" players, "+(USAGE->query_day()-g)+" days ago.");
   write("            Highest Recorded Usage:  "+b+"\n");
   return 1;
}

int help() {
message("help",
"This command is used to see mud usage over the last 24 hour "
"period.  You can use this to plan your play time depending "
"if you want to be on at the same time as the most or least "
"folks.  Usage: census\n<census m> will display a monthly chart."
"\n<census n> will display all folks who have logged in during past 24 hours.", this_player());
return 1;
}

Finally you will want to make two changes to /secure/lib/connect.c (WARNING, IF YOU BREAK CONNECT.C YOU WILL NOT BE ABLE TO LOGIN, SO BACK IT UP FIRST).

First in static private void eventEnterGame(), add the following line just before the call_out at the end of the function
Code: [Select]
    "/daemon/census_d"->login(Name);

Next in void eventCompleteChar() add the following line just before the call_out at the end of this function
Code: [Select]
    "/daemon/census_d"->newbie(Name);

All done.

6
Code Vault / Apropos command for DS
« on: January 01, 2008, 08:41:59 pm »
The following code adds apropos support for DS. Note that this is limited, and will only search through the help indexed files.
Syntax: apropos <name>

I.E.
apropos scan

returns:
[creator commands]:  scan
[Autodoc functions]:  doc_d/scan_mudlib

This will really help folks looking for information and searching for help since you do not need to know the full help name or page through all the docs.  Also, if you use the autodoc system I posted earlier frequently, it makes scanning it quicker too.

Add the following function anywhere in /daemon/help.c
Code: [Select]
mapping AllIndices(){
  mapping ret;
  ret = Indices;
  return ret;
}

Make the following command /secure/cmds/creators/apropos.c
Code: [Select]
/* Do not remove the headers from this file! see /USAGE for more info. */
// Modified from Lima 1.05b by Archaegeo on 1/1/08 for Dead Souls Lib

#include <lib.h>
#include <daemons.h>
#define MIN_LEN 3

mixed apropos(string s);

mixed cmd(string s) {
    string yt;
    if (!s)
    {
        write("apropos <string>\n\n"
          "Returns information on which mudlib functions contain the\n"
          "keyword passed, including a short description.\n");
        return;
    }
    if (strlen(s)<MIN_LEN)
    {
        write("Please find a bigger search criteria (min. "+MIN_LEN+
          " letters).\n");
        return;
    }
    yt=apropos(s);
    if (yt=="\n")
        write("No help files match your word.\n");
    this_player()->eventPrint(yt);
    return 1;
}

//:COMMAND
//Returns information on which help files contain the
//keyword passed, including a short description.

mixed apropos(string s)
{
    mapping filer=([]);
    mapping topics;
    string output="";
    string *st;
    string pwd;

    topics=HELP_D->AllIndices();
    if(!topics)
    {
        error("Apropos whines about help_d not giving any info out.\n");
        return;
    }
    foreach (string key,string *files in topics)
    {
        foreach(string d in topics[key]) {
            if (strsrch(d,s)!=-1)
            {   
                output += "[" + key + "]:  " + d +"\n";
            }
        }
    }
    output+="\n";
    return output;
}

string GetHelp(string str) {
    return ("Syntax: <apropos <name>>\n\n"
      "Pages through the help file names that have been indexed for <name>.\n"+
      "See also: help index");
}

Enjoy

7
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;
    }

8
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.

9
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();
}

10
Code Vault / Channel History Change in DS
« on: December 28, 2007, 10:10:15 pm »
The below changes support the following syntax:
hist <channel> [lines]

Where lines will default to 20.

This change will read the log of the channel if present rather than the stored information which is lost over reboots and updates to chat.c, also will let you control how much you see.

Change cmdLast in /secure/daemon/chat.c to:
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;
}

Change hist.c in /cmds/players/hist.c entirely to:
Code: [Select]
// Modified 12-29-08 by Archaegeo to support reading channel logs
#include <lib.h>
#include <daemons.h>
#include <message_class.h>

inherit LIB_DAEMON;

mixed cmd(string args) {
    if(!args){
        this_player()->eventPrint("Syntax: <hist <channel> [lines]>");
        return 1;
    }

    this_player()->eventPrint("Retrieving history...");

    if(args == "tell"){
        load_object("/secure/cmds/players/tell")->cmd("hist");
        return 1;
    }

    return CHAT_D->cmdLast(args);
    return 1;
}

string GetHelp(string topic) {
    return ("Syntax: <hist <channel> [lines]>\n"
      "Gives you the hist of a channel.\nThe default length if lines not specified is 20.\n"
      "If <tell> is the channel, no lines arguement is used.");
}

Pages: [1]