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 - amylase

Pages: [1] 2 3 ... 6
1
Open Chat / Re: Merry Christmas and a happy New Year!
« on: October 30, 2015, 11:42:07 am »
Merry X'mas to you too atomic.

For my fellow lpmuds members as well as for those who just happen to stumble across:

I wish you a merry Christmas (in The Netherlands we actually have 1st and 2nd Christmas day... go figure)
and a happy 2008. Careful with the fireworks... coding lpc is harder with less fingers...  ;D

2
Open Chat / Re: New Member saying "Hello"
« on: May 24, 2015, 01:14:45 am »
"Hello and warm greetings fellow dungeners!"
I would like to Introuduce myself as Zadious, Lord of the Plains. Vast Plains that is.
I am a self claimed newbie admin with a DS 2.8 setup and I am here to stay.
My test/learn mud is currently being called "Vast Plains".
I come here with 5 years of mudding experiance, and not one day as a builder or micro admin on any other game. I know the odds are against me, but I am here to learn and have fun. I am not building a mud at this point to be any sort of professional; rather I have outlined a world and created a rough draft that I will slowly work towards.
I am excited to continue learning LPC and the art of designing, so I thought it best to give a proper introduction before I ended up asking a million questions.
I look forward to learning from many of you, and I hope that in time I will be able to contribute to the comunity.
*Zadious tips his hat slightly to the front while bowing slowly. With a simple flick of the wrist, he fanishes from plain sight!*

Hi Zadious. Welcome onboard.

3
General / Re: Starting a mud: worthwhile or not?
« on: May 12, 2013, 12:18:44 am »

Another piece here:

Code: [Select]
/* socket_d.c
 *
 * Tricky @ Rock the Halo
 * 5-JAN-2007
 * Socket daemon
 *
 * Handles MudMode, Stream and Datagram socket connections.
 */

/* Socket types and errors */
#include <socket.h>

/* Set this to a name you know will not be used as an ID */
#define SOCKET_ID "[SOCKET_D]"

/* Lower this if you want less write retries */
#define WRITERETRYLIMIT 20

/* Private vars */
privatev mapping Sockets, Ids;

/* Private funcs */
privatef void close_callback(int);
privatef void internal_close(int);
privatef void listen_callback(int);
privatef void client_read_callback(int, mixed);
privatef void udp_client_read_callback(int, mixed, string);
privatef void server_read_callback(int, mixed);
privatef void udp_server_read_callback(int, mixed, string);
privatef void write_callback(int);
privatef void internal_write(int, mixed);
privatef void log(string, string, string);
privatef void error_log(string, string, int);

/* Public funcs */
varargs int client_create(string, string, int, mapping);
varargs int server_create(string, int, mapping);
varargs void close(mixed, int);
void client_write(mixed, mixed);
void server_write(mixed, mixed, int);
void udp_write(mixed, mixed, string);
mapping query_socket_info();

/* Function name: create
 * Description:   Initialise the object data.
 */
void create()
{
  Sockets = ([
    SOCKET_ID: ([
      "logfile": "socket"
    ])
  ]);

  Ids = ([ ]);

  set_heart_beat(20 / __HEARTBEAT_INTERVAL__);
  log(SOCKET_ID, "Success", "Created.");
}

/* Function name: remove
 * Description:   Closes all sockets and destructs itself.
 */
void remove()
{
  log(SOCKET_ID, "Notice", "Removing all sockets.");

  foreach (string sockID, mapping sock in Sockets)
  {
    if (sockID == SOCKET_ID) continue;

    log(sockID, "Notice", "Removing socket.");
    internal_close(sock["fd"]);
  }

  log(SOCKET_ID, "Warning", "Destructing.");
  destruct();
}

/* Function name: heart_beat
 * Description:   Cleans up orphaned file descriptors.
 */
void heart_beat()
{
  foreach (string sockID, mapping sock in Sockets)
  {
    if (sockID == SOCKET_ID) continue;

    if (objectp(sock["owner"]))
    {
      /* Persistent sockets */
      if (sock["persistent"]) continue;

      /* Non-persistent sockets */
      if (time() - sock["time"] < 3 * 60) continue;
    }

    log(sockID, "Notice", "Removing orphaned socket '" + Ids[sock["fd"]] + "' (" + sock["fd"] + ")");
    internal_close(sock["fd"]);
  }
}

/* Function name: close_callback
 * Description:   Called when the connection terminates unexpectably.
 * Arguments:     fd - Socket file descriptor.
 */
privatef void close_callback(int fd)
{
  mapping sock;
  object  o;
  string  f;
  string  sockID, type;

  if (member_array(fd, keys(Ids)) == -1) return;

  sockID = Ids[fd];
  sock = copy(Sockets[sockID]);
  type = sock["type"];
  log(sockID, "Warning", type + " connection (" + fd + ") terminated.");

  /* Indicate that the socket is closed */
  sock["closed"] = 1;

  o = sock["owner"];
  f = sock["closef"];

  /* Call the user function if one is set up */
  if (stringp(f)) call_other(o, f, fd);

  /* Remove the socket data */
  map_delete(Sockets, sockID);
  map_delete(Ids, fd);
}

/* Function name: internal_close
 * Description:   Internal socket close.
 * Arguments:     fd - Socket file descriptor.
 */
privatef void internal_close(int fd)
{
  mapping sock;
  string  sockID;
  int     sock_return;

  if (member_array(fd, keys(Ids)) == -1) return;

  sockID = Ids[fd];
  sock = copy(Sockets[sockID]);

  if (!mapp(sock))
  {
    /* Remove the socket data */
    map_delete(Sockets, sockID);
    map_delete(Ids, fd);

    return;
  }

  log(SOCKET_ID, "Warning", sock["type"] + " connection (" + sockID + "/" + sock["fd"] + ") closing.");

  if (!sock["closed"] && sock["buffer"])
  {
    call_out("internal_close", 2, fd);

    return;
  }

  if ((!sock["closed"] || sock["closing"]) && ((sock_return = socket_close(sock["fd"])) != EESUCCESS))
    error_log(sockID, sock["type"] + "/internal_close", sock_return);
  else
    log(sockID, "Success", sock["type"] + " connection (" + sock["fd"] + ") closed.");

  /* Remove the socket data */
  map_delete(Sockets, sockID);
  map_delete(Ids, fd);
}

/* Function name: listen_callback
 * Description:   Called when the socket receives an incoming connection.
 * Arguments:     fd - Socket file descriptor.
 */
privatef void listen_callback(int fd)
{
  object  o;
  mapping listenSock, sock;
  string  sockID, incomingSockID, f;
  int     sock_return;

  sockID = Ids[fd];
  listenSock = copy(Sockets[sockID]);

  /* Accept the incoming connection */
  sock_return = socket_accept(listenSock["fd"], "server_read_callback", "write_callback");

  /* Couldn't accept the incoming connection */
  if (sock_return < 0)
  {
    error_log(sockID, "Listen/socket_accept", sock_return);
    internal_close(fd);

    return;
  }

  /* Initialise remote socket data */
  sock = ([
    "fd": sock_return,
    "owner": listenSock["owner"],
    "type": "Remote",
    "blocking": 0,
    "buffer": 0,
    "closing": 0,
    "closed": 0,
    "time": time(),
    "persistent": 1,
    "socketType": listenSock["socketType"],
    "readf": listenSock["readf"],
    "closef": listenSock["closef"],
    "logfile": listenSock["logfile"],
  ]);

  incomingSockID = sockID + "." + sock["fd"];
  Sockets[incomingSockID] = copy(sock);
  Ids[sock["fd"]] = incomingSockID;
  log(sockID, "Success", "Accepted the incoming connection from " + socket_address(sock["fd"]));

  o = listenSock["owner"];
  f = listenSock["listenf"];

  /* Call the user function if one is set up otherwise do nothing */
  if (stringp(f)) call_other(o, f, fd, sock["fd"]);
}

/* Function name: client_read_callback
 * Description:   Called when data arrives on the client socket.
 * Arguments:     fd - Socket file descriptor.
 *                data - Incoming data.
 */
privatef void client_read_callback(int fd, mixed data)
{
  string sockID = Ids[fd];
  object o = Sockets[sockID]["owner"];
  string f = Sockets[sockID]["readf"];

  /* Call the user function if one is set up otherwise do nothing */
  if (stringp(f)) call_other(o, f, fd, data);
}

/* Function name: udp_client_read_callback
 * Description:   Called when data arrives on the UDP client socket.
 * Arguments:     fd - Socket file descriptor.
 *                data - Incoming data.
 *                addr - Address ("IP PORT") of the incoming data.
 */
privatef void udp_client_read_callback(int fd, mixed data, string addr)
{
  string sockID = Ids[fd];
  object o = Sockets[sockID]["owner"];
  string f = Sockets[sockID]["readf"];

  /* Call the user function if one is set up otherwise do nothing */
  if (stringp(f)) call_other(o, f, fd, data, addr);
}

/* Function name: server_read_callback
 * Description:   Called when data arrives on the server socket.
 * Arguments:     fd - Socket file descriptor.
 *                data - Incoming data.
 */
privatef void server_read_callback(int fd, mixed data)
{
  string sockID = Ids[fd];
  object o = Sockets[sockID]["owner"];
  string f = Sockets[sockID]["readf"];

  /* Call the user function if one is set up otherwise do nothing */
  if (stringp(f)) call_other(o, f, fd, data);
}

/* Function name: udp_server_read_callback
 * Description:   Called when data arrives on the UDP server socket.
 * Arguments:     fd - Socket file descriptor.
 *                data - Incoming data.
 *                addr - Address ("IP PORT") of the incoming data.
 */
privatef void udp_server_read_callback(int fd, mixed data, string addr)
{
  string sockID = Ids[fd];
  object o = Sockets[sockID]["owner"];
  string f = Sockets[sockID]["readf"];

  /* Call the user function if one is set up otherwise do nothing */
  if (stringp(f)) call_other(o, f, fd, data, addr);
}

/* Function name: write_callback
 * Description:   Called when data is ready to be written to the socket.
 * Arguments:     fd - Socket file descriptor.
 */
privatef void write_callback(int fd)
{
  string sockID;
  int    sock_return, times;

  if (member_array(fd, keys(Ids)) == -1) return;

  sockID = Ids[fd];

  if (member_array(sockID, keys(Sockets)) == -1 && !Sockets[sockID]) return;

  /* Not blocking at the moment */
  Sockets[sockID]["blocking"] = 0;

  /* If the socket is closed (close_callback called) then clean up */
  /* If we are closing and there is no data in the buffer then close the socket */
  if (Sockets[sockID]["closed"] || (!Sockets[sockID]["buffer"] && Sockets[sockID]["closing"]))
  {
    internal_close(fd);

    return;
  }

  /* Stop trying to write data for a bit */
  if (Sockets[sockID]["writeRetry"] == WRITERETRYLIMIT)
  {
    Sockets[sockID]["blocking"] = 1;
    Sockets[sockID]["writeRetry"] = 0;
    call_out("write_callback", 5, fd);

    return;
  }

  /* Pre-set the socket_write error to EESUCCESS */
  sock_return = EESUCCESS;

  times = 10;

  /* Process when:
   * there is data in the buffer
   * socket_write is successful
   * looped less than 10 times (limit spam)
   */
  while (Sockets[sockID]["buffer"] && sock_return == EESUCCESS && times > 0)
  {
    times--;
#if 0
    /* Turn this on for lots of log messages. */
    log(
      SOCKET_ID,
      "Notice",
      sprintf("Writing on %s/%d: %O",
      sockID,
      Sockets[sockID]["fd"],
      Sockets[sockID]["buffer"][0]
      )
    );
#endif

    if (   Sockets[sockID]["socketType"] == DATAGRAM
        || Sockets[sockID]["socketType"] == DATAGRAM_BINARY)
      sock_return = socket_write(fd, Sockets[sockID]["buffer"][0], Sockets[sockID]["hostport"]);
    else
      sock_return = socket_write(fd, Sockets[sockID]["buffer"][0]);

    /* Write first set of data in buffer to the socket */
    switch (sock_return)
    {
      /* Break out of the switch if successful */
      case EESUCCESS: break;

      /* Data has been buffered and we need to suspend further writes until it is cleared */
      case EECALLBACK:
      {
        Sockets[sockID]["blocking"] = 1;

        break;
      }

      /* The socket is blocked so we need to call write_callback again */
      case EEWOULDBLOCK:
      {
        call_out("write_callback", 1, fd);

        return;
      }

      /* Problem with send */
      case EESEND:
      {
        if (!Sockets[sockID]) return;
        if (member_array("writeRetry", keys(Sockets[sockID])) == -1)
          Sockets[sockID]["writeRetry"] = 0;

        Sockets[sockID]["writeRetry"] = Sockets[sockID]["writeRetry"] + 1;
        call_out("write_callback", 2, fd);

        return;
      }

      /* Flow control has been violated, shouldn't see this */
      case EEALREADY:
      {
        Sockets[sockID]["blocking"] = 1;

        return;
      }

      /* Something went really wrong so we close the socket */
      default:
      {
        error_log(sockID, Sockets[sockID]["type"] + "/socket_write", sock_return);
        internal_close(fd);

        return;
      }

    }

    Sockets[sockID]["writeRetry"] = 0;

    /* Remove the data we have written from the buffer */
    if (sizeof(Sockets[sockID]["buffer"]) == 1)
    {
      Sockets[sockID]["buffer"] = 0;

      /* If the socket is closed (close_callback called) then clean up */
      /* or if we are closing then close the socket */
      if (Sockets[sockID]["closed"] || Sockets[sockID]["closing"])
      {
        internal_close(fd);

        return;
      }
    }
    else Sockets[sockID]["buffer"] = Sockets[sockID]["buffer"][1..<1];
  }
}

/* Function name: internal_write
 * Description:   Internal socket write.
 * Arguments:     fd - Socket file descriptor.
 *                data - Outgoing data.
 */
privatef void internal_write(int fd, mixed data)
{
  string sockID;

  sockID = Ids[fd];

  /* Add data to the buffer */
  if (Sockets[sockID]["buffer"]) Sockets[sockID]["buffer"] += ({ data });
  else Sockets[sockID]["buffer"] = ({ data });

  /* If we are blocking then return otherwise write it */
  if (Sockets[sockID]["blocking"]) return;
  else
  {
    Sockets[sockID]["writeRetry"] = 0;
    write_callback(fd);
  }
}

/* Function name: log
 * Description:   Internal socket log.
 * Arguments:     sockID - Socket ID.
 *                pre - Pre-log message.
 *                str - Log message.
 */
privatef void log(string sockID, string pre, string str)
{
  /* If we have set a log file then append the message to it */
  if (member_array(sockID, keys(Sockets)) != -1)
    log_file(
      Sockets[sockID]["logfile"],
      sprintf("%s [SOCKET_D%s]: %s\n",
      pre,
      (sockID == SOCKET_ID ? "" : "/" + sockID),
      str
      )
    );
}

/* Function name: error_log
 * Description:   Internal socket error log.
 * Arguments:     sockID - Socket ID.
 *                str - Log message.
 *                x - Socket error code.
 */
privatef void error_log(string sockID, string str, int x)
{
  log(sockID, "Error", sprintf("%s - %s", str, socket_error(x)));
}

/* Function name: client_create
 * Description:   Create a socket for clients and connects it to host IP and port.
 * Arguments:     sockID - Unique ID that can be used to reference the socket.
 *                host - Host IP to connect to.
 *                port - Host port number.
 *
 *                Optional arguments:-
 *                extra - mapping of extra arguments:-
 *                  type - Socket Type.
 *                  persistent - Keeps the connection open at all times.
 *                  readf - User defined read callback function.
 *                  closef - User defined close callback function.
 *                  releasef - User defined release callback function.
 *                  logfile - Per-socket log file.
 *
 * Return:        Socket creation success or error code.
 */
varargs int client_create(string sockID, string host, int port, mapping extra)
{
  mapping sock;
  string  readf = 0, closef = 0, releasef = 0, logfile = 0;
  int     sock_return, persistent = 0, type = -1;

  if (undefinedp(sockID) || !stringp(sockID)) return EESOCKET;
  if (undefinedp(host) || !stringp(host)) return EESOCKET;
  if (undefinedp(port) || !intp(port)) return EESOCKET;
  if (member_array(sockID, keys(Sockets)) != -1) return EEISCONN;

  if (!undefinedp(extra))
  {
    if (!undefinedp(extra["type"])) type = extra["type"];
    if (!undefinedp(extra["persistent"])) persistent = extra["persistent"];
    if (!undefinedp(extra["readf"])) readf = extra["readf"];
    if (!undefinedp(extra["closef"])) closef = extra["closef"];
    if (!undefinedp(extra["releasef"])) releasef = extra["releasef"];
    if (!undefinedp(extra["logfile"])) logfile = extra["logfile"];
  }

  /* If we haven't set a socket type then set it to STREAM */
  if (type == -1) type = STREAM;

  /* Initialise client socket data */
  sock = ([
    "fd": -1,
    "owner": previous_object(),
    "type": "Client",
    "blocking": 0,
    "writeRetry": 0,
    "closing": 0,
    "closed": 0,
    "time": time(),
    "socketType": type,
    "persistent": persistent,
    "readf": readf,
    "closef": closef,
    "releasef": releasef,
    "logfile": logfile,
  ]);

  Sockets[sockID] = copy(sock);

  /* Create a socket */
  if (type == DATAGRAM || type == DATAGRAM_BINARY)
    sock_return = socket_create(type, "udp_client_read_callback");
  else
    sock_return = socket_create(type, "client_read_callback", "close_callback");

  /* Couldn't create a socket */
  if (sock_return < 0)
  {
    error_log(sockID, "Client/socket_create", sock_return);
    map_delete(Sockets, sockID);

    return sock_return;
  }

  /* Socket created and return value is a file descriptor we use */
  sock["fd"] = sock_return;
  Ids[sock["fd"]] = sockID;
  log(sockID, "Success", "Created client socket (" + sock["fd"] + ")");

  if (type != DATAGRAM && type != DATAGRAM_BINARY)
  {
    /* Bind the socket to a system selected port */
    /* Binding isn't necessary for clients since the system *should* do it for you */
    sock_return = socket_bind(sock["fd"], 0);

    /* Couldn't bind it */
    if (sock_return < 0)
    {
      error_log(sockID, "Client/socket_bind", sock_return);
      internal_close(sock["fd"]);

      return sock_return;
    }

    log(sockID, "Success", "Client socket bound to a port.");

    /* Now we actually connect the socket to a host and port */
    sock_return = socket_connect(sock["fd"], host + " " + port, "client_read_callback", "write_callback");

    /* Couldn't connect to remote machine */
    if (sock_return < 0)
    {
      error_log(sockID, "Client/socket_connect", sock_return);
      internal_close(sock["fd"]);

      return sock_return;
    }

    log(sockID, "Success", "Connected client to " + host + " " + port);
  }

  Sockets[sockID] = copy(sock);
  log(
    SOCKET_ID,
    "Notice",
    sprintf("Client created - %s/%d: %O", sockID, sock["fd"], Sockets[sockID])
  );

  /* Call the user function if one is set up otherwise do nothing */
  if (stringp(sock["releasef"]))
  {
    sock_return = socket_release(sock["fd"], sock["owner"], sock["releasef"]);

    if (sock_return != EESUCCESS)
    {
      error_log(sockID, "Client/socket_release", sock_return);

      return sock_return;
    }
  }

  /* Return the file descriptor on success */
  return sock["fd"];
}

/* Function name: server_create
 * Description:   Create a socket for a server and bind it to a port.
 * Arguments:     sockID - Unique ID that can be used to reference the socket.
 *                port - Server port number.
 *
 *                Optional arguments:-
 *                extra - mapping of extra arguments:-
 *                  type - Socket Type.
 *                  readf - User defined read callback function.
 *                  closef - User defined close callback function.
 *                  listenf - User defined listen callback function.
 *                  releasef - User defined release callback function.
 *                  logfile - Per-socket log file.
 *
 * Return:        Socket creation success or error code.
 */
varargs int server_create(string sockID, int port, mapping extra)
{
  mapping sock;
  string  readf = 0, closef = 0, listenf = 0, releasef = 0, logfile = 0;
  int     sock_return, type = -1;

  if (undefinedp(sockID) || !stringp(sockID)) return EESOCKET;
  if (undefinedp(port) || !intp(port)) return EESOCKET;
  if (member_array(sockID, keys(Sockets)) != -1) return EEISCONN;

  if (!undefinedp(extra))
  {
    if (!undefinedp(extra["type"])) type = extra["type"];
    if (!undefinedp(extra["readf"])) readf = extra["readf"];
    if (!undefinedp(extra["closef"])) closef = extra["closef"];
    if (!undefinedp(extra["listenf"])) listenf = extra["listenf"];
    if (!undefinedp(extra["releasef"])) releasef = extra["releasef"];
    if (!undefinedp(extra["logfile"])) logfile = extra["logfile"];
  }

  /* If we haven't set a socket type then set it to STREAM */
  if (type == -1) type = STREAM;

  /* Initialise server socket data */
  sock = ([
    "fd": -1,
    "owner": previous_object(),
    "type": "Server",
    "blocking": 0,
    "writeRetry": 0,
    "buffer": 0,
    "closing": 0,
    "closed": 0,
    "time": time(),
    "persistent": 1,
    "socketType": type,
    "readf": readf,
    "closef": closef,
    "listenf": listenf,
    "releasef": releasef,
    "logfile": logfile,
  ]);

  Sockets[sockID] = copy(sock);

  /* Create a socket */
  if (type == DATAGRAM || type == DATAGRAM_BINARY)
    sock_return = socket_create(type, "udp_server_read_callback");
  else
    sock_return = socket_create(type, "server_read_callback", "close_callback");

  /* Couldn't create a socket */
  if (sock_return < 0)
  {
    error_log(sockID, "Server/socket_create", sock_return);
    map_delete(Sockets, sockID);

    return sock_return;
  }

  /* Socket created and return value is a file descriptor we use */
  sock["fd"] = sock_return;
  Ids[sock["fd"]] = sockID;
  log(sockID, "Success", "Created server socket (" + sock["fd"] + ")");

  /* Bind the socket to the supplied port */
  sock_return = socket_bind(sock["fd"], port);

  /* Couldn't bind it */
  if (sock_return < 0)
  {
    error_log(sockID, "Server/socket_bind", sock_return);
    internal_close(sock["fd"]);

    return sock_return;
  }

  log(sockID, "Success", "Server socket bound to port " + port);

  if (type != DATAGRAM && type != DATAGRAM_BINARY)
  {
    /* Now we set up the listen callback */
    sock_return = socket_listen(sock["fd"], "listen_callback");

    /* Couldn't set up the socket to listen */
    if (sock_return < 0)
    {
      error_log(sockID, "Server/socket_listen", sock_return);
      internal_close(sock["fd"]);

      return sock_return;
    }

    log(sockID, "Success", "Server listen callback set up.");
  }

  Sockets[sockID] = copy(sock);
  log(
    SOCKET_ID,
    "Notice",
    sprintf("Server created - %s/%d: %O", sockID, sock["fd"], Sockets[sockID])
  );

  /* Call the user function if one is set up otherwise do nothing */
  if (stringp(sock["releasef"]))
  {
    sock_return = socket_release(sock["fd"], sock["owner"], sock["releasef"]);

    if (sock_return != EESUCCESS)
    {
      error_log(sockID, "Server/socket_release", sock_return);

      return sock_return;
    }
  }

  /* Return the file descriptor on success */
  return sock["fd"];
}

/* Function name: close
 * Description:   Public socket close.
 * Arguments:     fd - Socket file descriptor or ID.
 *
 *                Optional argument:-
 *                delay - Delays the closing of the socket for 'delay' seconds.
 */
varargs void close(mixed fd, int delay)
{
  if (undefinedp(delay)) delay = 0;

  if (intp(fd))
  {
    if (member_array(fd, keys(Ids)) == -1) return;
    if (Sockets[Ids[fd]]["owner"] != previous_object())
    {
      error_log(Ids[fd], "close/sockID '" + Ids[fd] + "' in object '" + Sockets[Ids[fd]]["owner"], EESECURITY);

      return;
    }

    call_out("internal_close", delay, fd);
  }
  else
  if (stringp(fd))
  {
    if (member_array(fd, keys(Sockets)) == -1) return;
    if (Sockets[fd]["owner"] != previous_object())
    {
      error_log(fd, "close/sockID '" + Ids[fd] + "' in object '" + Sockets[fd]["owner"], EESECURITY);

      return;
    }

    call_out("internal_close", delay, Sockets[fd]["fd"]);
  }
}

/* Function name: client_write
 * Description:   Public client socket write.
 * Arguments:     fd - Socket file descriptor.
 *                data - Outgoing data.
 */
void client_write(mixed fd, mixed data)
{
  if (intp(fd))
  {
    if (member_array(fd, keys(Ids)) == -1) return;
    if (Sockets[Ids[fd]]["owner"] != previous_object())
    {
      error_log(Ids[fd], "client_write/sockID '" + Ids[fd] + "' in object '" + Sockets[Ids[fd]]["owner"], EESECURITY);

      return;
    }

    if (   Sockets[Ids[fd]]["socketType"] == DATAGRAM
        || Sockets[Ids[fd]]["socketType"] == DATAGRAM_BINARY)
    {
      error_log(Ids[fd], "client_write/sockID '" + Ids[fd] + "' in object '" + Sockets[Ids[fd]]["owner"], EETYPENOTSUPP);

      return;
    }

    internal_write(fd, data);
  }
  else
  if (stringp(fd))
  {
    if (member_array(fd, keys(Sockets)) == -1) return;
    if (Sockets[fd]["owner"] != previous_object())
    {
      error_log(fd, "client_write/sockID '" + Ids[fd] + "' in object '" + Sockets[fd]["owner"], EESECURITY);

      return;
    }

    if (   Sockets[fd]["socketType"] == DATAGRAM
        || Sockets[fd]["socketType"] == DATAGRAM_BINARY)
    {
      error_log(fd, "client_write/sockID '" + fd + "' in object '" + Sockets[fd]["owner"], EETYPENOTSUPP);

      return;
    }

    internal_write(Sockets[fd]["fd"], data);
  }
}

/* Function name: server_write
 * Description:   Public server socket write.
 * Arguments:     fd - Socket file descriptor.
 *                data - Outgoing data.
 *                close - If set to '0' the connection is kept open after writing the data.
 *                If set to '1' the connection is closed after writing the data.
 */
void server_write(mixed fd, mixed data, int close)
{
  if (undefinedp(close)) close = 0;

  if (intp(fd))
  {
    if (member_array(fd, keys(Ids)) == -1) return;
    if (Sockets[Ids[fd]]["owner"] != previous_object())
    {
      error_log(Ids[fd], "server_write/sockID '" + Ids[fd] + "' in object '" + Sockets[Ids[fd]]["owner"], EESECURITY);

      return;
    }

    if (   Sockets[Ids[fd]]["socketType"] == DATAGRAM
        || Sockets[Ids[fd]]["socketType"] == DATAGRAM_BINARY)
    {
      error_log(Ids[fd], "server_write/sockID '" + Ids[fd] + "' in object '" + Sockets[Ids[fd]]["owner"], EETYPENOTSUPP);

      return;
    }

    Sockets[Ids[fd]]["closing"] = close;
    internal_write(fd, data);
  }
  else
  if (stringp(fd))
  {
    if (member_array(fd, keys(Sockets)) == -1) return;
    if (Sockets[fd]["owner"] != previous_object())
    {
      error_log(fd, "server_write/sockID '" + Ids[fd] + "' in object '" + Sockets[fd]["owner"], EESECURITY);

      return;
    }

    if (   Sockets[fd]["socketType"] == DATAGRAM
        || Sockets[fd]["socketType"] == DATAGRAM_BINARY)
    {
      error_log(fd, "server_write/sockID '" + fd + "' in object '" + Sockets[fd]["owner"], EETYPENOTSUPP);

      return;
    }

    Sockets[fd]["closing"] = close;
    internal_write(Sockets[fd]["fd"], data);
  }
}

/* Function name: udp_write
 * Description:   Public UDP socket write.
 * Arguments:     fd - Socket file descriptor.
 *                data - Outgoing data.
 *                addr - Address ("IP PORT") of the outgoing data.
 */
void udp_write(mixed fd, mixed data, string addr)
{
  if (intp(fd))
  {
    if (member_array(fd, keys(Ids)) == -1) return;
    if (Sockets[Ids[fd]]["owner"] != previous_object())
    {
      error_log(Ids[fd], "udp_write/sockID '" + Ids[fd] + "' in object '" + Sockets[Ids[fd]]["owner"], EESECURITY);

      return;
    }

    if (   Sockets[Ids[fd]]["socketType"] != DATAGRAM
        && Sockets[Ids[fd]]["socketType"] != DATAGRAM_BINARY)
    {
      error_log(Ids[fd], "udp_write/sockID '" + Ids[fd] + "' in object '" + Sockets[Ids[fd]]["owner"], EETYPENOTSUPP);

      return;
    }

    Sockets[Ids[fd]]["hostport"] = addr;
    internal_write(fd, data);
  }
  else
  if (stringp(fd))
  {
    if (member_array(fd, keys(Sockets)) == -1) return;
    if (Sockets[fd]["owner"] != previous_object())
    {
      error_log(fd, "udp_write/sockID '" + Ids[fd] + "' in object '" + Sockets[fd]["owner"], EESECURITY);

      return;
    }

    if (   Sockets[fd]["socketType"] != DATAGRAM
        && Sockets[fd]["socketType"] != DATAGRAM_BINARY)
    {
      error_log(fd, "udp_write/sockID '" + fd + "' in object '" + Sockets[fd]["owner"], EETYPENOTSUPP);

      return;
    }

    Sockets[fd]["hostport"] = addr;
    internal_write(Sockets[fd]["fd"], data);
  }
}

/* Function name: query_socket_info
 * Description:   Queries the internal Sockets mapping.
 * Return:        All connected sockets.
 */
mapping query_socket_info()
{
  mapping tmp = ([ ]);

  foreach (string sockID, mapping sock in Sockets)
  {
    if (sockID == SOCKET_ID) continue;

    tmp[sockID] = sock;
  }

  return copy(tmp);
}


Evennia also already runs IRC/MUD bridge:
http://code.google.com/p/evennia/wiki/IRC

Now we just need someone to port them to current version of Dead Soul.

4
General / Re: Starting a mud: worthwhile or not?
« on: May 12, 2013, 12:12:11 am »
If you were to allow people to hook text feeds from twitter/facebook/etc into in-game channels, and give them a way to post back to these things, it might both give them a reason/excuse to sit around in your MUD and socialize... AND it would perhaps get their friends to wonder what "FooMUD" is.

This. A million times this.

I am going to look into this myself. Now the question is - handle it with LPC sockets and write my own code from scratch, or try to interface with an external python/perl/pike/etc script that uses libraries for the specific protocols. Hmm.

Brilliant idea. I've been waiting for 2 years for someone to echo this idea but never quite had the time to look further into it myself.  Include IRC in that list too. IRC has been around forever and will continue to exist. The other social media come and go.

Here are old codes from Tricky for MUD <-> LPC bridge. Tried contacting him for a port but he seemed busy too.
http://lpmuds.net/forum/index.php?topic=651.0

Code: [Select]
/* socket_d.c
 *
 * Tricky @ Rock the Halo
 * 5-JAN-2007
 * Socket daemon
 *
 * Handles MudMode, Stream and Datagram socket connections.
 */

/* Socket types and errors */
#include <socket.h>

/* Set this to a name you know will not be used as an ID */
#define SOCKET_ID "[SOCKET_D]"

/* Lower this if you want less write retries */
#define WRITERETRYLIMIT 20

/* Private vars */
privatev mapping Sockets, Ids;

/* Private funcs */
privatef void close_callback(int);
privatef void internal_close(int);
privatef void listen_callback(int);
privatef void client_read_callback(int, mixed);
privatef void udp_client_read_callback(int, mixed, string);
privatef void server_read_callback(int, mixed);
privatef void udp_server_read_callback(int, mixed, string);
privatef void write_callback(int);
privatef void internal_write(int, mixed);
privatef void log(string, string, string);
privatef void error_log(string, string, int);

/* Public funcs */
varargs int client_create(string, string, int, mapping);
varargs int server_create(string, int, mapping);
varargs void close(mixed, int);
void client_write(mixed, mixed);
void server_write(mixed, mixed, int);
void udp_write(mixed, mixed, string);
mapping query_socket_info();

/* Function name: create
 * Description:   Initialise the object data.
 */
void create()
{
  Sockets = ([
    SOCKET_ID: ([
      "logfile": "socket"
    ])
  ]);

  Ids = ([ ]);

  set_heart_beat(20 / __HEARTBEAT_INTERVAL__);
  log(SOCKET_ID, "Success", "Created.");
}

/* Function name: remove
 * Description:   Closes all sockets and destructs itself.
 */
void remove()
{
  log(SOCKET_ID, "Notice", "Removing all sockets.");

  foreach (string sockID, mapping sock in Sockets)
  {
    if (sockID == SOCKET_ID) continue;

    log(sockID, "Notice", "Removing socket.");
    internal_close(sock["fd"]);
  }

  log(SOCKET_ID, "Warning", "Destructing.");
  destruct();
}

/* Function name: heart_beat
 * Description:   Cleans up orphaned file descriptors.
 */
void heart_beat()
{
  foreach (string sockID, mapping sock in Sockets)
  {
    if (sockID == SOCKET_ID) continue;

    if (objectp(sock["owner"]))
    {
      /* Persistent sockets */
      if (sock["persistent"]) continue;

      /* Non-persistent sockets */
      if (time() - sock["time"] < 3 * 60) continue;
    }

    log(sockID, "Notice", "Removing orphaned socket '" + Ids[sock["fd"]] + "' (" + sock["fd"] + ")");
    internal_close(sock["fd"]);
  }
}

/* Function name: close_callback
 * Description:   Called when the connection terminates unexpectably.
 * Arguments:     fd - Socket file descriptor.
 */
privatef void close_callback(int fd)
{
  mapping sock;
  object  o;
  string  f;
  string  sockID, type;

  if (member_array(fd, keys(Ids)) == -1) return;

  sockID = Ids[fd];
  sock = copy(Sockets[sockID]);
  type = sock["type"];
  log(sockID, "Warning", type + " connection (" + fd + ") terminated.");

  /* Indicate that the socket is closed */
  sock["closed"] = 1;

  o = sock["owner"];
  f = sock["closef"];

  /* Call the user function if one is set up */
  if (stringp(f)) call_other(o, f, fd);

  /* Remove the socket data */
  map_delete(Sockets, sockID);
  map_delete(Ids, fd);
}

/* Function name: internal_close
 * Description:   Internal socket close.
 * Arguments:     fd - Socket file descriptor.
 */
privatef void internal_close(int fd)
{
  mapping sock;
  string  sockID;
  int     sock_return;

  if (member_array(fd, keys(Ids)) == -1) return;

  sockID = Ids[fd];
  sock = copy(Sockets[sockID]);

  if (!mapp(sock))
  {
    /* Remove the socket data */
    map_delete(Sockets, sockID);
    map_delete(Ids, fd);

    return;
  }

  log(SOCKET_ID, "Warning", sock["type"] + " connection (" + sockID + "/" + sock["fd"] + ") closing.");

  if (!sock["closed"] && sock["buffer"])
  {
    call_out("internal_close", 2, fd);

    return;
  }

  if ((!sock["closed"] || sock["closing"]) && ((sock_return = socket_close(sock["fd"])) != EESUCCESS))
    error_log(sockID, sock["type"] + "/internal_close", sock_return);
  else
    log(sockID, "Success", sock["type"] + " connection (" + sock["fd"] + ") closed.");

  /* Remove the socket data */
  map_delete(Sockets, sockID);
  map_delete(Ids, fd);
}

/* Function name: listen_callback
 * Description:   Called when the socket receives an incoming connection.
 * Arguments:     fd - Socket file descriptor.
 */
privatef void listen_callback(int fd)
{
  object  o;
  mapping listenSock, sock;
  string  sockID, incomingSockID, f;
  int     sock_return;

  sockID = Ids[fd];
  listenSock = copy(Sockets[sockID]);

  /* Accept the incoming connection */
  sock_return = socket_accept(listenSock["fd"], "server_read_callback", "write_callback");

  /* Couldn't accept the incoming connection */
  if (sock_return < 0)
  {
    error_log(sockID, "Listen/socket_accept", sock_return);
    internal_close(fd);

    return;
  }

  /* Initialise remote socket data */
  sock = ([
    "fd": sock_return,
    "owner": listenSock["owner"],
    "type": "Remote",
    "blocking": 0,
    "buffer": 0,
    "closing": 0,
    "closed": 0,
    "time": time(),
    "persistent": 1,
    "socketType": listenSock["socketType"],
    "readf": listenSock["readf"],
    "closef": listenSock["closef"],
    "logfile": listenSock["logfile"],
  ]);

  incomingSockID = sockID + "." + sock["fd"];
  Sockets[incomingSockID] = copy(sock);
  Ids[sock["fd"]] = incomingSockID;
  log(sockID, "Success", "Accepted the incoming connection from " + socket_address(sock["fd"]));

  o = listenSock["owner"];
  f = listenSock["listenf"];

  /* Call the user function if one is set up otherwise do nothing */
  if (stringp(f)) call_other(o, f, fd, sock["fd"]);
}

/* Function name: client_read_callback
 * Description:   Called when data arrives on the client socket.
 * Arguments:     fd - Socket file descriptor.
 *                data - Incoming data.
 */
privatef void client_read_callback(int fd, mixed data)
{
  string sockID = Ids[fd];
  object o = Sockets[sockID]["owner"];
  string f = Sockets[sockID]["readf"];

  /* Call the user function if one is set up otherwise do nothing */
  if (stringp(f)) call_other(o, f, fd, data);
}

/* Function name: udp_client_read_callback
 * Description:   Called when data arrives on the UDP client socket.
 * Arguments:     fd - Socket file descriptor.
 *                data - Incoming data.
 *                addr - Address ("IP PORT") of the incoming data.
 */
privatef void udp_client_read_callback(int fd, mixed data, string addr)
{
  string sockID = Ids[fd];
  object o = Sockets[sockID]["owner"];
  string f = Sockets[sockID]["readf"];

  /* Call the user function if one is set up otherwise do nothing */
  if (stringp(f)) call_other(o, f, fd, data, addr);
}

/* Function name: server_read_callback
 * Description:   Called when data arrives on the server socket.
 * Arguments:     fd - Socket file descriptor.
 *                data - Incoming data.
 */
privatef void server_read_callback(int fd, mixed data)
{
  string sockID = Ids[fd];
  object o = Sockets[sockID]["owner"];
  string f = Sockets[sockID]["readf"];

  /* Call the user function if one is set up otherwise do nothing */
  if (stringp(f)) call_other(o, f, fd, data);
}

/* Function name: udp_server_read_callback
 * Description:   Called when data arrives on the UDP server socket.
 * Arguments:     fd - Socket file descriptor.
 *                data - Incoming data.
 *                addr - Address ("IP PORT") of the incoming data.
 */
privatef void udp_server_read_callback(int fd, mixed data, string addr)
{
  string sockID = Ids[fd];
  object o = Sockets[sockID]["owner"];
  string f = Sockets[sockID]["readf"];

  /* Call the user function if one is set up otherwise do nothing */
  if (stringp(f)) call_other(o, f, fd, data, addr);
}

/* Function name: write_callback
 * Description:   Called when data is ready to be written to the socket.
 * Arguments:     fd - Socket file descriptor.
 */
privatef void write_callback(int fd)
{
  string sockID;
  int    sock_return, times;

  if (member_array(fd, keys(Ids)) == -1) return;

  sockID = Ids[fd];

  if (member_array(sockID, keys(Sockets)) == -1 && !Sockets[sockID]) return;

  /* Not blocking at the moment */
  Sockets[sockID]["blocking"] = 0;

  /* If the socket is closed (close_callback called) then clean up */
  /* If we are closing and there is no data in the buffer then close the socket */
  if (Sockets[sockID]["closed"] || (!Sockets[sockID]["buffer"] && Sockets[sockID]["closing"]))
  {
    internal_close(fd);

    return;
  }

  /* Stop trying to write data for a bit */
  if (Sockets[sockID]["writeRetry"] == WRITERETRYLIMIT)
  {
    Sockets[sockID]["blocking"] = 1;
    Sockets[sockID]["writeRetry"] = 0;
    call_out("write_callback", 5, fd);

    return;
  }

  /* Pre-set the socket_write error to EESUCCESS */
  sock_return = EESUCCESS;

  times = 10;

  /* Process when:
   * there is data in the buffer
   * socket_write is successful
   * looped less than 10 times (limit spam)
   */
  while (Sockets[sockID]["buffer"] && sock_return == EESUCCESS && times > 0)
  {
    times--;
#if 0
    /* Turn this on for lots of log messages. */
    log(
      SOCKET_ID,
      "Notice",
      sprintf("Writing on %s/%d: %O",
      sockID,
      Sockets[sockID]["fd"],
      Sockets[sockID]["buffer"][0]
      )
    );
#endif

    if (   Sockets[sockID]["socketType"] == DATAGRAM
        || Sockets[sockID]["socketType"] == DATAGRAM_BINARY)
      sock_return = socket_write(fd, Sockets[sockID]["buffer"][0], Sockets[sockID]["hostport"]);
    else
      sock_return = socket_write(fd, Sockets[sockID]["buffer"][0]);

    /* Write first set of data in buffer to the socket */
    switch (sock_return)
    {
      /* Break out of the switch if successful */
      case EESUCCESS: break;

      /* Data has been buffered and we need to suspend further writes until it is cleared */
      case EECALLBACK:
      {
        Sockets[sockID]["blocking"] = 1;

        break;
      }

      /* The socket is blocked so we need to call write_callback again */
      case EEWOULDBLOCK:
      {
        call_out("write_callback", 1, fd);

        return;
      }

      /* Problem with send */
      case EESEND:
      {
        if (!Sockets[sockID]) return;
        if (member_array("writeRetry", keys(Sockets[sockID])) == -1)
          Sockets[sockID]["writeRetry"] = 0;

        Sockets[sockID]["writeRetry"] = Sockets[sockID]["writeRetry"] + 1;
        call_out("write_callback", 2, fd);

        return;
      }

      /* Flow control has been violated, shouldn't see this */
      case EEALREADY:
      {
        Sockets[sockID]["blocking"] = 1;

        return;
      }

      /* Something went really wrong so we close the socket */
      default:
      {
        error_log(sockID, Sockets[sockID]["type"] + "/socket_write", sock_return);
        internal_close(fd);

        return;
      }

    }

    Sockets[sockID]["writeRetry"] = 0;

    /* Remove the data we have written from the buffer */
    if (sizeof(Sockets[sockID]["buffer"]) == 1)
    {
      Sockets[sockID]["buffer"] = 0;

      /* If the socket is closed (close_callback called) then clean up */
      /* or if we are closing then close the socket */
      if (Sockets[sockID]["closed"] || Sockets[sockID]["closing"])
      {
        internal_close(fd);

        return;
      }
    }
    else Sockets[sockID]["buffer"] = Sockets[sockID]["buffer"][1..<1];
  }
}

/* Function name: internal_write
 * Description:   Internal socket write.
 * Arguments:     fd - Socket file descriptor.
 *                data - Outgoing data.
 */
privatef void internal_write(int fd, mixed data)
{
  string sockID;

  sockID = Ids[fd];

  /* Add data to the buffer */
  if (Sockets[sockID]["buffer"]) Sockets[sockID]["buffer"] += ({ data });
  else Sockets[sockID]["buffer"] = ({ data });

  /* If we are blocking then return otherwise write it */
  if (Sockets[sockID]["blocking"]) return;
  else
  {
    Sockets[sockID]["writeRetry"] = 0;
    write_callback(fd);
  }
}

/* Function name: log
 * Description:   Internal socket log.
 * Arguments:     sockID - Socket ID.
 *                pre - Pre-log message.
 *                str - Log message.
 */
privatef void log(string sockID, string pre, string str)
{
  /* If we have set a log file then append the message to it */
  if (member_array(sockID, keys(Sockets)) != -1)
    log_file(
      Sockets[sockID]["logfile"],
      sprintf("%s [SOCKET_D%s]: %s\n",
      pre,
      (sockID == SOCKET_ID ? "" : "/" + sockID),
      str
      )
    );
}

/* Function name: error_log
 * Description:   Internal socket error log.
 * Arguments:     sockID - Socket ID.
 *                str - Log message.
 *                x - Socket error code.
 */
privatef void error_log(string sockID, string str, int x)
{
  log(sockID, "Error", sprintf("%s - %s", str, socket_error(x)));
}

/* Function name: client_create
 * Description:   Create a socket for clients and connects it to host IP and port.
 * Arguments:     sockID - Unique ID that can be used to reference the socket.
 *                host - Host IP to connect to.
 *                port - Host port number.
 *
 *                Optional arguments:-
 *                extra - mapping of extra arguments:-
 *                  type - Socket Type.
 *                  persistent - Keeps the connection open at all times.
 *                  readf - User defined read callback function.
 *                  closef - User defined close callback function.
 *                  releasef - User defined release callback function.
 *                  logfile - Per-socket log file.
 *
 * Return:        Socket creation success or error code.
 */
varargs int client_create(string sockID, string host, int port, mapping extra)
{
  mapping sock;
  string  readf = 0, closef = 0, releasef = 0, logfile = 0;
  int     sock_return, persistent = 0, type = -1;

  if (undefinedp(sockID) || !stringp(sockID)) return EESOCKET;
  if (undefinedp(host) || !stringp(host)) return EESOCKET;
  if (undefinedp(port) || !intp(port)) return EESOCKET;
  if (member_array(sockID, keys(Sockets)) != -1) return EEISCONN;

  if (!undefinedp(extra))
  {
    if (!undefinedp(extra["type"])) type = extra["type"];
    if (!undefinedp(extra["persistent"])) persistent = extra["persistent"];
    if (!undefinedp(extra["readf"])) readf = extra["readf"];
    if (!undefinedp(extra["closef"])) closef = extra["closef"];
    if (!undefinedp(extra["releasef"])) releasef = extra["releasef"];
    if (!undefinedp(extra["logfile"])) logfile = extra["logfile"];
  }

  /* If we haven't set a socket type then set it to STREAM */
  if (type == -1) type = STREAM;

  /* Initialise client socket data */
  sock = ([
    "fd": -1,
    "owner": previous_object(),
    "type": "Client",
    "blocking": 0,
    "writeRetry": 0,
    "closing": 0,
    "closed": 0,
    "time": time(),
    "socketType": type,
    "persistent": persistent,
    "readf": readf,
    "closef": closef,
    "releasef": releasef,
    "logfile": logfile,
  ]);

  Sockets[sockID] = copy(sock);

  /* Create a socket */
  if (type == DATAGRAM || type == DATAGRAM_BINARY)
    sock_return = socket_create(type, "udp_client_read_callback");
  else
    sock_return = socket_create(type, "client_read_callback", "close_callback");

  /* Couldn't create a socket */
  if (sock_return < 0)
  {
    error_log(sockID, "Client/socket_create", sock_return);
    map_delete(Sockets, sockID);

    return sock_return;
  }

  /* Socket created and return value is a file descriptor we use */
  sock["fd"] = sock_return;
  Ids[sock["fd"]] = sockID;
  log(sockID, "Success", "Created client socket (" + sock["fd"] + ")");

  if (type != DATAGRAM && type != DATAGRAM_BINARY)
  {
    /* Bind the socket to a system selected port */
    /* Binding isn't necessary for clients since the system *should* do it for you */
    sock_return = socket_bind(sock["fd"], 0);

    /* Couldn't bind it */
    if (sock_return < 0)
    {
      error_log(sockID, "Client/socket_bind", sock_return);
      internal_close(sock["fd"]);

      return sock_return;
    }

    log(sockID, "Success", "Client socket bound to a port.");

    /* Now we actually connect the socket to a host and port */
    sock_return = socket_connect(sock["fd"], host + " " + port, "client_read_callback", "write_callback");

    /* Couldn't connect to remote machine */
    if (sock_return < 0)
    {
      error_log(sockID, "Client/socket_connect", sock_return);
      internal_close(sock["fd"]);

      return sock_return;
    }

    log(sockID, "Success", "Connected client to " + host + " " + port);
  }

  Sockets[sockID] = copy(sock);
  log(
    SOCKET_ID,
    "Notice",
    sprintf("Client created - %s/%d: %O", sockID, sock["fd"], Sockets[sockID])
  );

  /* Call the user function if one is set up otherwise do nothing */
  if (stringp(sock["releasef"]))
  {
    sock_return = socket_release(sock["fd"], sock["owner"], sock["releasef"]);

    if (sock_return != EESUCCESS)
    {
      error_log(sockID, "Client/socket_release", sock_return);

      return sock_return;
    }
  }

  /* Return the file descriptor on success */
  return sock["fd"];
}

/* Function name: server_create
 * Description:   Create a socket for a server and bind it to a port.
 * Arguments:     sockID - Unique ID that can be used to reference the socket.
 *                port - Server port number.
 *
 *                Optional arguments:-
 *                extra - mapping of extra arguments:-
 *                  type - Socket Type.
 *                  readf - User defined read callback function.
 *                  closef - User defined close callback function.
 *                  listenf - User defined listen callback function.
 *                  releasef - User defined release callback function.
 *                  logfile - Per-socket log file.
 *
 * Return:        Socket creation success or error code.
 */
varargs int server_create(string sockID, int port, mapping extra)
{
  mapping sock;
  string  readf = 0, closef = 0, listenf = 0, releasef = 0, logfile = 0;
  int     sock_return, type = -1;

  if (undefinedp(sockID) || !stringp(sockID)) return EESOCKET;
  if (undefinedp(port) || !intp(port)) return EESOCKET;
  if (member_array(sockID, keys(Sockets)) != -1) return EEISCONN;

  if (!undefinedp(extra))
  {
    if (!undefinedp(extra["type"])) type = extra["type"];
    if (!undefinedp(extra["readf"])) readf = extra["readf"];
    if (!undefinedp(extra["closef"])) closef = extra["closef"];
    if (!undefinedp(extra["listenf"])) listenf = extra["listenf"];
    if (!undefinedp(extra["releasef"])) releasef = extra["releasef"];
    if (!undefinedp(extra["logfile"])) logfile = extra["logfile"];
  }

  /* If we haven't set a socket type then set it to STREAM */
  if (type == -1) type = STREAM;

  /* Initialise server socket data */
  sock = ([
    "fd": -1,
    "owner": previous_object(),
    "type": "Server",
    "blocking": 0,
    "writeRetry": 0,
    "buffer": 0,
    "closing": 0,
    "closed": 0,
    "time": time(),
    "persistent": 1,
    "socketType": type,
    "readf": readf,
    "closef": closef,
    "listenf": listenf,
    "releasef": releasef,
    "logfile": logfile,
  ]);

  Sockets[sockID] = copy(sock);

  /* Create a socket */
  if (type == DATAGRAM || type == DATAGRAM_BINARY)
    sock_return = socket_create(type, "udp_server_read_callback");
  else
    sock_return = socket_create(type, "server_read_callback", "close_callback");

  /* Couldn't create a socket */
  if (sock_return < 0)
  {
    error_log(sockID, "Server/socket_create", sock_return);
    map_delete(Sockets, sockID);

    return sock_return;
  }

  /* Socket created and return value is a file descriptor we use */
  sock["fd"] = sock_return;
  Ids[sock["fd"]] = sockID;
  log(sockID, "Success", "Created server socket (" + sock["fd"] + ")");

  /* Bind the socket to the supplied port */
  sock_return = socket_bind(sock["fd"], port);

  /* Couldn't bind it */
  if (sock_return < 0)
  {
    error_log(sockID, "Server/socket_bind", sock_return);
    internal_close(sock["fd"]);

    return sock_return;
  }

  log(sockID, "Success", "Server socket bound to port " + port);

  if (type != DATAGRAM && type != DATAGRAM_BINARY)
  {
    /* Now we set up the listen callback */
    sock_return = socket_listen(sock["fd"], "listen_callback");

    /* Couldn't set up the socket to listen */
    if (sock_return < 0)
    {
      error_log(sockID, "Server/socket_listen", sock_return);
      internal_close(sock["fd"]);

      return sock_return;
    }

    log(sockID, "Success", "Server listen callback set up.");
  }

  Sockets[sockID] = copy(sock);
  log(
    SOCKET_ID,
    "Notice",
    sprintf("Server created - %s/%d: %O", sockID, sock["fd"], Sockets[sockID])
  );

  /* Call the user function if one is set up otherwise do nothing */
  if (stringp(sock["releasef"]))
  {
    sock_return = socket_release(sock["fd"], sock["owner"], sock["releasef"]);

    if (sock_return != EESUCCESS)
    {
      error_log(sockID, "Server/socket_release", sock_return);

      return sock_return;
    }
  }

  /* Return the file descriptor on success */
  return sock["fd"];
}

/* Function name: close
 * Description:   Public socket close.
 * Arguments:     fd - Socket file descriptor or ID.
 *
 *                Optional argument:-
 *                delay - Delays the closing of the socket for 'delay' seconds.
 */
varargs void close(mixed fd, int delay)
{
  if (undefinedp(delay)) delay = 0;

  if (intp(fd))
  {
    if (member_array(fd, keys(Ids)) == -1) return;
    if (Sockets[Ids[fd]]["owner"] != previous_object())
    {
      error_log(Ids[fd], "close/sockID '" + Ids[fd] + "' in object '" + Sockets[Ids[fd]]["owner"], EESECURITY);

      return;
    }

    call_out("internal_close", delay, fd);
  }
  else
  if (stringp(fd))
  {
    if (member_array(fd, keys(Sockets)) == -1) return;
    if (Sockets[fd]["owner"] != previous_object())
    {
      error_log(fd, "close/sockID '" + Ids[fd] + "' in object '" + Sockets[fd]["owner"], EESECURITY);

      return;
    }

    call_out("internal_close", delay, Sockets[fd]["fd"]);
  }
}

/* Function name: client_write
 * Description:   Public client socket write.
 * Arguments:     fd - Socket file descriptor.
 *                data - Outgoing data.
 */
void client_write(mixed fd, mixed data)
{
  if (intp(fd))
  {
    if (member_array(fd, keys(Ids)) == -1) return;
    if (Sockets[Ids[fd]]["owner"] != previous_object())
    {
      error_log(Ids[fd], "client_write/sockID '" + Ids[fd] + "' in object '" + Sockets[Ids[fd]]["owner"], EESECURITY);

      return;
    }

    if (   Sockets[Ids[fd]]["socketType"] == DATAGRAM
        || Sockets[Ids[fd]]["socketType"] == DATAGRAM_BINARY)
    {
      error_log(Ids[fd], "client_write/sockID '" + Ids[fd] + "' in object '" + Sockets[Ids[fd]]["owner"], EETYPENOTSUPP);

      return;
    }

    internal_write(fd, data);
  }
  else
  if (stringp(fd))
  {
    if (member_array(fd, keys(Sockets)) == -1) return;
    if (Sockets[fd]["owner"] != previous_object())
    {
      error_log(fd, "client_write/sockID '" + Ids[fd] + "' in object '" + Sockets[fd]["owner"], EESECURITY);

      return;
    }

    if (   Sockets[fd]["socketType"] == DATAGRAM
        || Sockets[fd]["socketType"] == DATAGRAM_BINARY)
    {
      error_log(fd, "client_write/sockID '" + fd + "' in object '" + Sockets[fd]["owner"], EETYPENOTSUPP);

      return;
    }

    internal_write(Sockets[fd]["fd"], data);
  }
}

/* Function name: server_write
 * Description:   Public server socket write.
 * Arguments:     fd - Socket file descriptor.
 *                data - Outgoing data.
 *                close - If set to '0' the connection is kept open after writing the data.
 *                If set to '1' the connection is closed after writing the data.
 */
void server_write(mixed fd, mixed data, int close)
{
  if (undefinedp(close)) close = 0;

  if (intp(fd))
  {
    if (member_array(fd, keys(Ids)) == -1) return;
    if (Sockets[Ids[fd]]["owner"] != previous_object())
    {
      error_log(Ids[fd], "server_write/sockID '" + Ids[fd] + "' in object '" + Sockets[Ids[fd]]["owner"], EESECURITY);

      return;
    }

    if (   Sockets[Ids[fd]]["socketType"] == DATAGRAM
        || Sockets[Ids[fd]]["socketType"] == DATAGRAM_BINARY)
    {
      error_log(Ids[fd], "server_write/sockID '" + Ids[fd] + "' in object '" + Sockets[Ids[fd]]["owner"], EETYPENOTSUPP);

      return;
    }

    Sockets[Ids[fd]]["closing"] = close;
    internal_write(fd, data);
  }
  else
  if (stringp(fd))
  {
    if (member_array(fd, keys(Sockets)) == -1) return;
    if (Sockets[fd]["owner"] != previous_object())
    {
      error_log(fd, "server_write/sockID '" + Ids[fd] + "' in object '" + Sockets[fd]["owner"], EESECURITY);

      return;
    }

    if (   Sockets[fd]["socketType"] == DATAGRAM
        || Sockets[fd]["socketType"] == DATAGRAM_BINARY)
    {
      error_log(fd, "server_write/sockID '" + fd + "' in object '" + Sockets[fd]["owner"], EETYPENOTSUPP);

      return;
    }

    Sockets[fd]["closing"] = close;
    internal_write(Sockets[fd]["fd"], data);
  }
}

/* Function name: udp_write
 * Description:   Public UDP socket write.
 * Arguments:     fd - Socket file descriptor.
 *                data - Outgoing data.
 *                addr - Address ("IP PORT") of the outgoing data.
 */
void udp_write(mixed fd, mixed data, string addr)
{
  if (intp(fd))
  {
    if (member_array(fd, keys(Ids)) == -1) return;
    if (Sockets[Ids[fd]]["owner"] != previous_object())
    {
      error_log(Ids[fd], "udp_write/sockID '" + Ids[fd] + "' in object '" + Sockets[Ids[fd]]["owner"], EESECURITY);

      return;
    }

    if (   Sockets[Ids[fd]]["socketType"] != DATAGRAM
        && Sockets[Ids[fd]]["socketType"] != DATAGRAM_BINARY)
    {
      error_log(Ids[fd], "udp_write/sockID '" + Ids[fd] + "' in object '" + Sockets[Ids[fd]]["owner"], EETYPENOTSUPP);

      return;
    }

    Sockets[Ids[fd]]["hostport"] = addr;
    internal_write(fd, data);
  }
  else
  if (stringp(fd))
  {
    if (member_array(fd, keys(Sockets)) == -1) return;
    if (Sockets[fd]["owner"] != previous_object())
    {
      error_log(fd, "udp_write/sockID '" + Ids[fd] + "' in object '" + Sockets[fd]["owner"], EESECURITY);

      return;
    }

    if (   Sockets[fd]["socketType"] != DATAGRAM
        && Sockets[fd]["socketType"] != DATAGRAM_BINARY)
    {
      error_log(fd, "udp_write/sockID '" + fd + "' in object '" + Sockets[fd]["owner"], EETYPENOTSUPP);

      return;
    }

    Sockets[fd]["hostport"] = addr;
    internal_write(Sockets[fd]["fd"], data);
  }
}

/* Function name: query_socket_info
 * Description:   Queries the internal Sockets mapping.
 * Return:        All connected sockets.
 */
mapping query_socket_info()
{
  mapping tmp = ([ ]);

  foreach (string sockID, mapping sock in Sockets)
  {
    if (sockID == SOCKET_ID) continue;

    tmp[sockID] = sock;
  }

  return copy(tmp);
}

5
Other / Evennia
« on: August 18, 2012, 05:25:49 am »
Hey guys check out this python geared lib
http://code.google.com/p/evennia/

Seems pretty well documented and high level.  What do people think?

It also uses twisted words to conveniently talk with IRC and intermud channels.
http://code.google.com/p/evennia/wiki/IRC
This bit particularly appeal to me since I am more towards social mud development.

Functional beta release has come out.

6
Code Vault / Re: Chat Timestamp
« on: August 17, 2012, 12:17:07 pm »
Ok, turns out all those lines mentioned were redundant. I have removed all extra time() now. Here is the better version. Enjoy :P

Code: [Select]
/*    /daemon/chat.c
 *    from the Dead Souls Mudlib
 *    daemon to handle all mud chat channels
 *    created by Descartes of Borg 931220
 *
 *    IMC2 support added by Shadyman 2007-Sep-24
 *    Feelings support added by Shadyman 2007-Sep-24
 *    "mapping tags" added by Shadyman 2007-Sep-24
 */

#ifndef LOG_REMOTE_CHANS
#define LOG_REMOTE_CHANS 0
#endif

#ifndef LOG_LOCAL_CHANS
#define LOG_LOCAL_CHANS 1
#endif

#ifndef CHANNEL_PIPES
#define  CHANNEL_PIPES 0
#endif

#include <lib.h>
#include <pov.h>
#include <daemons.h>
#include <origin.h>
#include <message_class.h>
#include "include/chat.h"

inherit LIB_DAEMON;

string suspect,site,chan;
static private mapping Channels;
static private mapping chanlast;

static private string *local_chans = ({});
static private string *remote_chans = ({});
static string *syschans = ({});

static private mapping localchans = ([
        //I3 Channels
        "imud_code": "intercre",
        "imud_gossip": "intergossip",
        "ie_flibcode": "foundation",
        "dead_test4": "ds_test",
        "dead_souls": "ds",

        //IMC2 Channels
        "Server02:igame": "i2game2",
        "Server02:inews": "i2news2",
        "Server01:ibuild": "ibuild2",
        "Server01:ichat": "ichat2",
        "Server01:pchat": "pchat2",
        "Server01:i2game": "i2game2",
        "Server01:i2chat": "i2chat2",
        "Server01:i3chat": "i3chat2",
        "Server01:i2code": "i2code2",
        "Server01:i2news": "i2news2",
        "Server01:imudnews": "imudnews2",
        "Server01:irc": "irc2",
        "Server01:ifree": "ifree2",

        ]);

        static private mapping remotechans = ([
                //I3 Channels
                "intercre": "imud_code",
                "intergossip": "imud_gossip",
                "foundation": "ie_flibcode",
                "dutch": "dutch",
                "ds_test": "dead_test4",
                "ds": "dead_souls",

                //IMC2 Channels
                "i2game2": "Server02:igame",
                "i2news2": "Server02:inews",
                "ibuild2": "Server01:ibuild",
                "ichat2": "Server01:ichat",
                "pchat2": "Server01:pchat",
                "i2game2": "Server01:i2game",
                "i2chat2": "Server01:i2chat",
                "i3chat2": "Server01:i3chat",
                "i2code2": "Server01:i2code",
                "i2news2": "Server01:i2news",
                "imudnews2": "Server01:imudnews",
                "irc2": "Server01:irc",
                "ifree2": "Server01:ifree",
                ]);

static private mapping tags = ([
        "intermud"    : "%^B_BLACK%^WHITE%^",
        "muds"        : "%^B_BLACK%^WHITE%^",
        "connections" : "%^B_BLACK%^BOLD%^WHITE%^",
        "death"       : "%^BOLD%^RED%^",
        "cre"         : "%^BOLD%^GREEN%^",
        "admin"       : "%^BOLD%^MAGENTA%^",
        "newbie"      : "%^BOLD%^B_YELLOW%^",
        "gossip"      : "%^BOLD%^B_BLUE%^",

        "ds"          : "%^YELLOW%^",
        "dchat" :"%^CYAN%^",
        "intergossip" : "%^GREEN%^",
        "intercre"    : "%^ORANGE%^",

        "ibuild2"      : "%^B_RED%^%^YELLOW%^",
        "ichat2"       : "%^B_RED%^%^GREEN%^",
        "pchat2"       : "%^B_RED%^%^BOLD%^GREEN%^",
        "i2game2"      : "%^B_BLUE%^",
        "i2chat2"      : "%^B_GREEN%^",
        "i3chat2"      : "%^B_RED%^",
        "i2code2"      : "%^B_YELLOW%^%^RED%^",
        "i2news2"      : "%^B_YELLOW%^%^BLUE%^",
        "imudnews2"    : "%^B_YELLOW%^%^CYAN%^",
        "irc2"         : "%^B_BLUE%^%^GREEN%^",
        "ifree2"         : "%^B_BLUE%^%^GREEN%^",

        "default"     : "%^BOLD%^BLUE%^",
        "default-IMC2" : "%^BOLD%^WHITE%^%^B_BLUE%^",
        ]);

static void Setup(){
    mixed rchan2 = ({});
    mixed rchan3 = ({});
    remote_chans = ({});
    local_chans = ({"newbie","cre","gossip","admin","error", "intermud",
            "death", "connections", "muds" });
    syschans = ({ "intermud", "death", "connections", "muds" });

    local_chans += CLASSES_D->GetClasses();

    if(find_object(INTERMUD_D)){
        if(arrayp(INTERMUD_D->GetChannels()))
            rchan3 += distinct_array(INTERMUD_D->GetChannels());
    }
    if(find_object(IMC2_D)){
        if(arrayp(IMC2_D->GetChanList()))
            rchan2 += distinct_array(IMC2_D->GetChanList());
    }

    foreach(mixed foo in local_chans){
        if(!stringp(foo)){
            local_chans -= ({ foo });
        }
    }

    rchan3 = sort_array(filter(rchan3, (: stringp($1) :)), 1);
    rchan2 = sort_array(filter(rchan2, (: stringp($1) :)), 1);

    foreach(mixed bar in ({ rchan3, rchan2 })){
        foreach(mixed foo in bar){
            string svr, cnl;
            if(!stringp(foo) || member_array(foo, local_chans) != -1){
                bar -= ({ foo });
                continue;
            }
            if(sscanf(foo, "%s:%s", svr, cnl) == 2){
                if(member_array(cnl, local_chans) == -1 && !localchans[cnl] &&
                  member_array(cnl, rchan3) == -1){
                    localchans[foo] = cnl;
                    remotechans[cnl] = foo;
                }
            }
        }
    }
    remote_chans = distinct_array((rchan3 + rchan2));
    local_chans = distinct_array(local_chans);
}

static void create() {
    object pl;
    string *tmp_arr = ({});
    daemon::create();
    SetNoClean(1);
    Channels = ([]);

    call_out("Setup", 10);

    foreach(string kanal in local_chans + syschans){
        if( !Channels[kanal] ) Channels[kanal] = ({});
    }
    foreach(pl in users()) {
        string *chans;
        string channel;

        if( pl && !(chans = pl->GetChannels()) ) continue;
        foreach(channel in chans) {
            if( !Channels[channel] ) Channels[channel] = ({});
            Channels[channel] = distinct_array(Channels[channel] + ({ pl }));
        }
    }
    foreach( string channel in local_chans ){
        tmp_arr += ({ channel + "emote" });
        tmp_arr += ({ channel + ":" });
    }

    local_chans += tmp_arr;
}

string *AddRemoteChannel(mixed chan){
    string *ret = copy(remote_chans);
    if(base_name(previous_object()) != INTERMUD_D) return ret;
    if(stringp(chan)) chan = ({ chan });
    if(!arrayp(chan)) return ret;
    foreach(string element in chan){
        if(member_array(element, local_chans) != -1){
            chan -= ({ element });
        }
    }
    return copy(remote_chans = distinct_array(remote_chans += chan));
}

string *AddLocalChannel(mixed chan){
    string *ret = copy(local_chans);
    if(base_name(previous_object()) != PARTY_D) return ret;
    if(stringp(chan)) chan = ({ chan });
    if(!arrayp(chan)) return ret;
    foreach(string element in chan){
        if(member_array(element, local_chans) != -1){
            chan -= ({ element });
        }
    }
    return copy(local_chans = distinct_array(local_chans += chan));
}

string *RemoveRemoteChannel(mixed chan){
    string *ret = copy(remote_chans);
    if(base_name(previous_object()) != INTERMUD_D) return ret;
    if(stringp(chan)) chan = ({ chan });
    if(!arrayp(chan)) return ret;
    foreach(string element in chan){
        if(member_array(element, local_chans) != -1){
            chan -= ({ element });
        }
    }
    return copy(remote_chans = distinct_array(remote_chans -= chan));
}

string *RemoveLocalChannel(mixed chan){
    string *ret = copy(local_chans);
    if(base_name(previous_object()) != PARTY_D) return ret;
    if(stringp(chan)) chan = ({ chan });
    if(!arrayp(chan)) return ret;
    foreach(string element in chan){
        if(member_array(element, local_chans) != -1){
            chan -= ({ element });         }
    }     
    return copy(local_chans = distinct_array(local_chans -= chan));
}

varargs string *GetRemoteChannels(int localized){
    mixed *ret = ({});
    if(!localized) return copy(remote_chans);
    foreach(string chan in remote_chans){
        ret += ({ GetLocalChannel(chan) });
    }
    return ret;
}

string decolor(string str){
    string s1 = "", s2, s3, test;
    int tmp = 2;
    if(sscanf(str,"%s<%s>%s",s1,s2,s3) != 3)
        tmp = sscanf(str,"<%s>%s",s2,s3);
    if(tmp != 2) return str;
    else {
        test = s1+"<"+s2+">%^RESET%^"+strip_colours(s3);
        return test;
    }
}

varargs int CanListen(object who, string canal){
    if(!RESTRICTED_INTERMUD) return 1;
    if(canal && member_array(canal, local_chans) != -1) return 1;
    else return imud_privp(who);
}

varargs int CanTalk(object who, string canal){
    if(!RESTRICTED_INTERMUD) return 1;
    if(canal && member_array(canal, local_chans) != -1) return 1;
    else return imud_privp(who);
}

string *eventRegisterMember(string *chans) {
    string *tmp;
    object ob;
    string channel;

    if( !living(ob = previous_object()) ) return ({});
    tmp = ({});
    foreach(channel in chans) {
        /* just check out for secure channels */
        switch(channel) {
            case "admin":
                if( !archp(ob) ) break;
            case "cre": case "intercre": case "intergossip":
                if( !creatorp(ob) ) break;
            default:
            if( !Channels[channel]) Channels[channel] = ({});
            Channels[channel] = distinct_array(Channels[channel] + ({ ob }));
            tmp += ({ channel });
        }
    }
    return tmp;
}

string *eventRemoveMember(string *chans) {
    object ob;
    string channel;

    if( !living(ob = previous_object()) ) return({});
    foreach(channel in chans) {
        if( !Channels[channel] ) continue;
        else Channels[channel] -= ({ ob });
        if( !sizeof(Channels[channel]) ) map_delete(Channels, channel);
    }
    return chans;
}

int cmdLast(string feep){

    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(!sizeof(chanlast[feep]))
    {
        this_player()->eventPrint("That channel has no backlog.", MSG_ERROR);
        return 1;
    }
    if(!CanListen(this_player(),feep)){
        write("You lack privileges to that channel.");
        return 1;
    }
    this_player()->eventPrint(implode(chanlast[feep], "\n"));
    return 1;
}

static int LogIt(string what, string where, string canale){
    if( (member_array(canale,local_chans) != -1 && LOG_LOCAL_CHANS) ||
            ( member_array(GetRemoteChannel(canale),remote_chans) != -1 && LOG_REMOTE_CHANS) ){
        unguarded( (: write_file($(where), $(what)) :) );
        return 1;
    }
    else return 0;
}

varargs int eventAddLast(string feep, string str, string pchan, string pmsg, string pwho)
{
    string plainmsg;
    string Chan=feep;
    if(!chanlast)
        chanlast=([]);
    if(!sizeof(chanlast[Chan]))
        chanlast[Chan] = ({});
    if(sizeof(chanlast[Chan]) == 50)
        chanlast[Chan] = chanlast[Chan][1..sizeof(chanlast[Chan])];
    chanlast[Chan] += ({ str });
    Chan = GetLocalChannel(Chan);

    if (Chan == "death") return 1;

    //Log in either SQL or file
#ifdef MYSQL
    if (MYSQL_D->sql_request("INSERT INTO LOG_CHAT (Channel,Who,What) VALUES (\'"+ escape(pchan) +"\',\'" + escape(pwho) + "\',\'" + escape(pmsg) + "\')") == 0) {
        true();
    }
#endif
    Chan = GetLocalChannel(Chan);
    if(!pchan || pchan == "") pchan = "foo";
    plainmsg = "bar";
    if(pchan) plainmsg = "<" + pchan + "> ";
    if(pmsg) plainmsg += pmsg;
    if(pwho && pwho !="") plainmsg = pwho+" "+plainmsg;
    if(pchan && pchan != "admin"){
        LogIt("["+timestamp()+"] "+plainmsg+"\n", "/log/chan/"+Chan, Chan);
    }
    else {
        LogIt("["+timestamp()+"] "+plainmsg+"\n", "/secure/log/"+Chan, Chan);
    }
    return 1;
}

int cmdChannel(string verb, string str){
    string msg, name, rc, target, targetkey, target_msg, emote_cmd, remains;
    string *exploded;
    mixed array msg_data;
    object ob = 0;
    int i, emote, forcedemote;

    if(grepp(verb,"|")){
        string foo, bar;

        if(CHANNEL_PIPES){
            if(grepp(verb,"|morse")){
                str = morse(str);
                verb = replace_string(verb,"|morse","");
            }

            if(grepp(verb,"|colorize")){
                str = dbz_colors(str);
                verb = replace_string(verb,"|colorize","");
            }

            if(grepp(verb,"|annoy")){
                str = dbz_colors(str,2);
                verb = replace_string(verb,"|annoy","");
            }
            if(grepp(verb,"|file")){
                if(!file_exists(str) || !(str = read_file(str))){
                    write("Can't read that file.");
                    return 0;
                }
                verb = replace_string(verb,"|file","");
            }
        }

        if(sscanf(verb, "%s|%s", foo, bar) == 2) verb = foo;
    }

    if(grepp(verb, ":")){
        string *tmpv = explode(verb, ":");
        if(!sizeof(tmpv)) tmpv = ({"newbie"});
        verb = tmpv[0]+"emote";
        if(sizeof(tmpv) > 1) str = implode(tmpv[1..], ":") + str;
    }

    if(grepp(verb, ";")){
        string *tmpv = explode(verb, ";");
        if(!sizeof(tmpv)) tmpv = ({"newbie"});
        verb = tmpv[0]+"forcedemote";
        if(sizeof(tmpv) > 1) str = implode(tmpv[1..], ";") + str;
    }

    if(sizeof(str) > 2){
        if((str[0..0] == ":" || str[0..0] == ";") &&
                alphap(str[1..1]) && str[2..2] != " "){
            if(str[0..0] == ";" && !grepp(verb,"forcedemote"))
                verb = replace_string(verb,"emote","") + "forcedemote";
            else if(str[0..0] == ":" && !grepp(verb,"emote")) verb += "emote";
            str = str[1..];
        }
    }

    //******LIST******
    //allow "list <chan>" to list users listening
    if( verb == "list" ) {
        string *who;
        string ch, mud;

        if( !str ) return 0;

        //Find the remote listing for a channel on a specific mud
        if( sscanf(str, "%s@%s", ch, mud) == 2 ) {
            mud = trim(mud);
            if(!alphap(last(mud,1))) mud = truncate(mud,1);

            if( !Channels[ch] ) return 0;

            if( member_array(this_player(), Channels[ch]) == -1 ) return 0;

            if( ch == (ch = GetRemoteChannel(ch)) ) {
                if(!creatorp(this_player())){
                    write("Remote channel information is not available to players.");
                    return 1;
                }
            }

            if( !(mud = INTERMUD_D->GetMudName(mud)) ) {
                this_player()->eventPrint(mud_name() + " is not aware of "+
                        "such a place.", MSG_ERROR);
                return 1;
            }

            if(!CanTalk(this_player(),verb)) {
                write("You lack privileges to that channel.");
                return 1;
            }
            SERVICES_D->eventSendChannelWhoRequest(ch, mud);
            this_player()->eventPrint("Remote listing request sent.",
                    MSG_SYSTEM);
            return 1;
        }
        else ch = str;

        //If no such channel, or not a part of that channel, then no list.
        if( !Channels[ch] ) return 0;
        if( member_array(this_player(), Channels[str]) == -1 ) return 0;

        //Build and print the list of listeners
        who = GetChannelList(str);
        msg = "Online: " + implode(who, "   ");
        this_player()->eventPrint(msg, MSG_SYSTEM);
        return 1;
    }
    //******END LIST******


    //All emotes will fall into this IF structure and get tagged
    //as emote = 1 or forcedemote = 1

    //If it's a verb+emote, de-emote the verb, and mark as an emote
    if(grepp(verb, "emote")) {
        //Get the real channel
        if(grepp(verb, "forcedemote")){
            verb = replace_string(verb,"forcedemote","");   
            forcedemote = 1;
        }
        else verb = replace_string(verb,"emote","");
        emote = 1;
    }

    if(!strsrch(str,"^encode")) str = morse("(encoded):  "+str[7..]);

    if(find_object(INTERMUD_D) && !sizeof(remote_chans))
        remote_chans = distinct_array(INTERMUD_D->GetChannels());

    if(member_array(GetRemoteChannel(verb), remote_chans) == -1 &&
            member_array(verb, local_chans) == -1) local_chans += ({ verb });

    //******Access Checks
    //No talking if you're not allowed.
    if ( !CanTalk(this_player(),verb) ) {
        write("You lack privileges to that channel.");
        return 1;
    }
    //Toggle channel blocking
    if ( emote == 1 && ( !str || str == "" ) ) {
        this_player()->SetBlocked(verb + "emote");
        return 1;
    } else if ( !str || str == "" ) {
        this_player()->SetBlocked(verb);
        return 1;
    }
    //Syschans aren't for chatting on, only listening
    if ( member_array(verb, syschans) != -1 ) {
        write("This is not a channel for chatting.");
        return 1;
    }
    //If gagged, you can't talk on channels
    if ( this_player()->GetGagged(verb) ) {
        write("You have gag mode enabled. Type: 'gag off' to talk on channels.");
        return 1;
    }
    //Channel doesn't exist, probably an emote typo
    if(!Channels[verb]) return 0;
    //If not part of the channel, no chatting
    if( member_array(this_player(), Channels[verb]) == -1 ) return 0;
    //If blocked, allow no chatting
    if( this_player()->GetBlocked(verb) ) {
        if( this_player()->GetBlocked("all") ) {
            this_player()->eventPrint("You cannot chat while totally blocked.",
                    MSG_ERROR);
            return 1;
        }
        this_player()->eventPrint("Turn this channel on to talk on it.", MSG_ERROR);
        return 1;
    }
    //******End Access Checks

    //If there's no channel matching now, then it's a typo or wasn't meant for this file to pick up.
    if( !Channels[verb] ) return 0;
    //Find the remote channel's name, based on the local, pretty name
    rc = GetRemoteChannel(verb);
    //Check emotes
    if (emote == 1) {
        exploded = explode(str, " "); //BOOM!!! We have an array of words.
        if (sizeof(exploded) <= 1) {
            emote_cmd = str;
            remains = 0;
        } else {
            emote_cmd = exploded[0];
            remains = implode(exploded[1..]," ");
        }

        //***********************************
        //Find a target for targetted emotes.
        //***********************************
        if( !remains ) { //If no arguments and just one word
            msg_data = SOUL_D->GetChannelEmote(emote_cmd, ""); //Search for a feeling that matches
        } else { //If there is an argument to the emote,
            if( ob = find_living(target = convert_name(remains)) ) {  //If there is a living target
                msg_data = SOUL_D->GetChannelEmote(emote_cmd, "LIV");
                //If it's not there, get the emote's LVS text.
                //if (!msg_data)
                // msg_data = SOUL_D->GetChannelEmote(emote_cmd, "LVS", remains);
            } else if( strsrch(target, "@") == -1 ) { //If no living target
                string array words = explode(remains, " ");
                target = "";
                for(i=0; i<sizeof(words); i++) {
                    target += lower_case(words[i]);
                    if( ob = find_living(target) ) {
                        if( i < sizeof(words)-1 ) {
                            remains = implode(words[(i+1)..], " ");
                        } else {
                            remains = 0;
                        }
                        //If it's not there, get the emote's LVS STR text.
                        if (!msg_data)
                            msg_data = SOUL_D->GetChannelEmote(emote_cmd, "LVS STR", remains);
                        break;
                    }
                }
                if( !ob ) {
                    msg_data = SOUL_D->GetChannelEmote(emote_cmd, "STR", remains);
                    target = 0;
                }

            } else {
                string array words;

                //Find any @'s in the remains.. Should be User@Mud
                i = strsrch(remains, "@", -1);

                //If there's not enough room for a proper name@mud, just do it as a string
                if ( i >= strlen(remains)-1 ) {
                    msg_data = SOUL_D->GetChannelEmote(emote_cmd, "STR", remains);
                    target = 0;
                } else { //Otherwise, call mud and find user
                    string mud,temp;
                    words = explode(remains[(i+1)..], " ");
                    target = remains[0..i];
                    remains = "";
                    while(sizeof(words)) {
                        temp = implode(words, " ");
                        temp = trim(temp);
                        if(!alphap(last(temp,1))) temp = truncate(temp,1);
                        mud = INTERMUD_D->GetMudName(lower_case(temp));
                        if (!mud) mud = IMC2_D->find_mud(lower_case(temp));
                        if( mud ) {
                            target += mud;
                            break;
                        }
                        if( remains == "" ) remains = words[<1];
                        else remains = words[<1] + " " + remains;
                        words = words[0..<2];
                    }

                    //If we couldn't find the mud,
                    if ( !mud ) {
                        msg_data = SOUL_D->GetChannelEmote(emote_cmd, "STR", remains);
                        target = 0;
                    } else {
                        if ( trim(remains) == "" ) {
                            msg_data = SOUL_D->GetChannelEmote(emote_cmd, "LIV");
                        } else {
                            msg_data = SOUL_D->GetChannelEmote(emote_cmd, "LIV STR", remains);
                        }
                    }
                }
            }
        } //Done finding target

        //***********************************
        //Find the target's proper name and target the message at him/her.
        //***********************************

        if ( msg_data ) { //There's a target
            string sgen = this_player()->GetGender();
            string tgen = 0;

            if ( ob ) { //If a local user
                target = ob->GetName();
                tgen = ob->GetGender();
            } else if ( target ) { //If a mud user
                string user, mud;

                sscanf(target, "%s@%s", user, mud);
                targetkey = target;
                tgen = SERVICES_D->GetRemoteGender(user, mud);
                target = SERVICES_D->GetRemoteDisplayName(user, mud);
                if( !target ) target = capitalize(targetkey);
            }

            //Calculate the viewpoint for 3rd parties
            str = create_message(POV_OBSERVER, msg_data[0][0],
                    msg_data[0][1], "$N", sgen, "$O", tgen,
                    msg_data[1]);

            //If it's targetted, calculate the message for the target
            if ( target ) {
                target_msg = create_message(POV_TARGET, msg_data[0][0],
                        msg_data[0][1], "$N", sgen,
                        "$O", tgen, msg_data[1]);
                target_msg = replace_string(target_msg, "$O's", "your");   
            }
        } else { //There's no target. Spurt it out like the user put it in.
            //Forced emotes only allow real emotes, not custom ones.
            if (forcedemote == 1) {
                if ( member_array( emote_cmd,SOUL_D->GetEmotes() ) > -1 ) {
                    write("Invalid syntax. See %^CYAN%^help "+emote_cmd+"%^RESET%^ for a list of usages.");
                    return 1;
                } else {
                    write("No such feeling. See %^CYAN%^help feelings%^RESET%^ for a list of feelings.");
                    return 1;
                }
            } else {
                str = "$N " + str;
                target = 0;
            }
        }
    }

    //If admin or cre channels, Capitalize a person's real name, because admins can be physically hidden
    if( verb == "admin" || verb == "cre" ) {
        if( !(name = this_player()->GetCapName()) )
            name = capitalize(this_player()->GetKeyName());
    }
    else name = this_player()->GetName();
    //Add the "Name" $N to the string
    if(!grepp(str,"$N") && emote) str = "$N "+str;
    //Send locally
    eventSendChannel(name, verb, str, emote, target, target_msg);
    //If it's a remote channel, send it remotely.
    if(member_array(GetRemoteChannel(verb), remote_chans) != -1
            && member_array(verb, local_chans) == -1){
        if (grepp(GetRemoteChannel(verb),":")) { //It's an IMC2 channel
            if(IMC2_D->getonline() != 1){
                return 1;
            }
            name = replace_string(name, " ", "");
            if( ob ) {
                IMC2_D->channel_out(name, rc, replace_string(replace_string(str,"$N ",""),"$O",target), emote);
            } else if ( targetkey ) {
                IMC2_D->channel_out(name, rc, replace_string(replace_string(str,"$N ",""),"$O",targetkey), emote);
            } else {
                IMC2_D->channel_out(name, rc, replace_string(str,"$N ",""), emote);
            }
        } else { //It's an I3 channel
            if( ob ) {
                SERVICES_D->eventSendChannel(name, rc, str, emote, convert_name(target), target_msg);
            } else {
                SERVICES_D->eventSendChannel(name, rc, str, emote, convert_name(targetkey), target_msg);;
            }
        }
    }
    return 1;
}

varargs void eventSendChannel(string who, string ch, string msg, int emote,
        string target, string targmsg) {
    object channeler = find_player(lower_case(who));
    int terminal;
    string prev = base_name(previous_object());
    string pchan,pmsg;
    string chatlayout = "%s %s<%s>%s %s";
    string emotelayout = "%s<%s>%s %s";
    //string chatlayout = "%s says, %s(%s)%s '%s'";
    //string emotelayout = "%s(%s)%s %s";

    if(prev == INSTANCES_D){
        terminal = 1;
    }
    if(prev == SERVICES_D) terminal = 1;
    if(prev == IMC2_D) terminal = 1;
    if(!terminal){
        string rch = GetRemoteChannel(ch);
        if(member_array(rch, remote_chans) == -1){
            INSTANCES_D->eventSendChannel(who,ch,msg,emote,target,targmsg);
        }
    }

    pchan=ch;
    if(!channeler) channeler = this_player();
    if(!strsrch(msg,"-.--. . -. -.-. --- -.. . -.. -.--.- ---...")) msg = unmorse(msg);
    if(targmsg && !strsrch(targmsg,"-.--. . -. -.-. --- -.. . -.. -.--.- ---..."))
        targmsg = unmorse(targmsg);

    if(this_player() && this_player() != channeler) channeler = this_player();

    if(!strsrch(base_name(previous_object()), "/realms/") ||
            !strsrch(base_name(previous_object()), "/open/")) {
        return 0;
    }

    if(member_array(ch, syschans) != -1) {
        emote = 0;
    }
    if(channeler){
        if(!CanTalk(channeler, ch) && member_array(ch, syschans) == -1){
            return;
        }
    }
    if( file_name(previous_object()) == SERVICES_D ||
            file_name(previous_object()) == IMC2_D) {
        ch = GetLocalChannel(ch);
        if( emote && sizeof(who)) msg = replace_string(msg, "$N", who);
    }
    else if( origin() != ORIGIN_LOCAL && previous_object() != master() &&
            file_name(previous_object()) != PARTY_D &&
            file_name(previous_object()) != UPDATE_D &&
            file_name(previous_object()) != INSTANCES_D &&
            member_array(ch, syschans) == -1){
        return;
    }
    prev = file_name(previous_object());
    if(!Channels[ch] && prev != SERVICES_D && prev != INSTANCES_D){
        return;
    }
    if( emote ) {
        object *obs;
        object ob;
        string this_msg, tmp;

        if( target && (ob = find_player(convert_name(target))) ) {
            target = ob->GetName();
        }

        //Colorize emote channels
        if (member_array(lower_case(ch),keys(tags)) >= 0){
            this_msg = tags[lower_case(ch)];
        } else {
            if(member_array(ch, local_chans) < 0 && (prev == IMC2_D ||
                        member_array(ch, (keys(INTERMUD_D->GetChannelList())
                                || ({}))) < 0)){
                this_msg = tags["default-IMC2"]; //Use the default IMC2 entry
            }
            else {
                this_msg = tags["default"]; //Use the default entry
            }
        }

        msg = replace_string(msg, "$N", who);
        if( target ) {
            msg = replace_string(msg, "$O", target);
            targmsg = replace_string(targmsg, "$N", who);
            targmsg = capitalize(replace_string(targmsg, "$O", "you"));
        }

        //Put together the channel emote message
        tmp = sprintf(emotelayout, this_msg, ch, "%^RESET%^", msg);

        //Store message in the history list
        eventAddLast(ch, tmp, pchan, msg);

        if(Channels[ch]){
            obs = filter(Channels[ch], (: $1 && !($1->GetBlocked($(ch))) :));
            foreach(object listener in obs) {
                int ignore;
                if(sscanf(who,"%s@%s",suspect,site) < 2) {
                    suspect = who;
                    site = "@"+mud_name();
                }
                else site = "@"+site;
                if( listener == ob ) continue;
                if(sizeof(listener->GetMuffed()))
                    foreach(string jerk in listener->GetMuffed()){
                        if(jerk && lower_case(suspect) == lower_case(jerk)){
                            ignore = 1;
                        }
                        if(jerk && lower_case(site[1..]) == lower_case(jerk)){
                            ignore = 1;
                        }
                    }
                if(listener->GetNoChanColors()) tmp = decolor(tmp);
                if(!ignore && CanListen(listener,ch) &&
                        !(listener->GetMuted(ch))){
                    listener->eventPrint(tmp, MSG_CHAN);
                }
                ignore = 0;
            }
            if( member_array(ob, obs) != -1 ) {
                if( ob && !(ob->GetBlocked(ch)) ) {
                    int ignore;
                    tmp = sprintf(emotelayout, this_msg, ch, "%^RESET%^", targmsg);
                    if(sizeof(ob->GetMuffed()))
                        foreach(string jerk in ob->GetMuffed()){
                            if(jerk && lower_case(suspect) == lower_case(jerk)) ignore = 1;
                            if(jerk && lower_case(site[1..]) == lower_case(jerk)) ignore = 1;
                        }
                    if(ob->GetNoChanColors()) tmp = decolor(tmp);
                    if(!ignore && CanListen(ob,ch)&& !(ob->GetMuted(ch)))
                        ob->eventPrint(tmp, MSG_CHAN);
                    ignore = 0;
                }
            }
        }
        suspect = "";
        site = "";
    }
    else {
        object *obs;
        string chancolor;

        //Colorize flag
        if (member_array(lower_case(ch),keys(tags)) >= 0) { //If there's an entry for the channel
            chancolor = tags[lower_case(ch)]; //Use it
        } else { //Otherwise
            if(member_array(ch, local_chans) < 0 && (prev == IMC2_D ||
                        member_array(ch, (keys(INTERMUD_D->GetChannelList())
                                || ({}))) < 0)){
                chancolor = tags["default-IMC2"]; //Use the default IMC2 entry
            }
            else {
                chancolor = tags["default"]; //Use the default entry
            }
        }
msg += " [" + ctime(time())[4..18] + "]";
        pmsg = msg;

        //Put together the channel emote message
        msg = sprintf(chatlayout, who, chancolor, ch, "%^RESET%^", pmsg);
        eventAddLast(ch, msg, pchan, pmsg, who);
     
        if(Channels[ch]) {
            obs = filter(Channels[ch], (: $1 && !($1->GetBlocked($(ch))) :));
            foreach(object ob in obs){
                int ignore;
                if(sscanf(who,"%s@%s",suspect,site) < 2) {
                    suspect = who;
                    site = "@"+mud_name();
                }
                else site = "@"+site;

                if(sizeof(ob->GetMuffed()))
                    foreach(string jerk in ob->GetMuffed()){
                        if(jerk && lower_case(suspect) == lower_case(jerk)) ignore = 1;
                        if(jerk && lower_case(site[1..]) == lower_case(jerk)) ignore = 1;
                    }
                if(ob->GetNoChanColors()) msg = decolor(msg);
                if(!ignore && CanListen(ob,ch)&& !(ob->GetMuted(ch)))
                    ob->eventPrint(msg, MSG_CHAN);

                ignore = 0;
                suspect ="";
                site = "";
            }
        }
    }
}

string *GetChannelList(string ch) {
    string *ret;
    object who;

    if( file_name(previous_object()) == SERVICES_D ) ch = GetLocalChannel(ch);
    else if( origin() != ORIGIN_LOCAL ) return ({});
    if( !Channels[ch] ) return ({});
    ret = ({});
    foreach(who in Channels[ch]) {
        if( !who || who->GetInvis() || who->GetBlocked(ch) )
            continue;
        ret += ({ who->GetName() });
    }
    return ret;
}

string *GetLocalChannels(){
    return copy(local_chans);
}

string GetLocalChannel(string ch) {
    if(ch && !strsrch(ch,"server0")){
        ch = replace_string(ch, "server01", "Server01");
        ch = replace_string(ch, "server02", "Server02");
    }
    if (sizeof(localchans[ch])) return localchans[ch];
    else return ch;
}

string GetRemoteChannel(string ch) {
    if (sizeof(remotechans[ch])) return remotechans[ch];
    else return ch;
}

int GetListening(object player, string ch){
    if(!Channels[ch] ||
            member_array(player, Channels[ch]) == -1) return 0;
    return 1;
}

string *GetChannels() { return sort_array(copy(keys(Channels)),1); }
string *GetSystemChannels() { return sort_array(copy(syschans),1); }
mapping GetTags() { return copy(tags); }
string GetTag(string ch) { return tags[ch]; }

7
Code Vault / Re: Chat Timestamp
« on: August 17, 2012, 12:09:44 pm »
Just browsing through the modified code again, looks like I've included a few extra time() here and there:

Lines 865, 831, 873, 902, 903.

Haven't got time to decide which ones to stay, which ones (or all) are redundant.

Timestamp seems to work for now anyway :P

8
Code Vault / Chat Timestamp
« on: August 17, 2012, 11:48:57 am »
Version: Mudlib: Dead Souls 3.6,  Driver: FluffOS v2.23-ds01w               
Aim: Add date and time stamp to chats
File: /lib/secure/daemon/chat.c

Just an updated mod to add timestamp to the end of chat.

What this code does is for example in dschat whenever someone says something, at the end of their sentence there will be date and time in square brackets. Useful when you haven't been in front of the computer for hours or days and come back to scroll back to check on who said what when.

For example normally you get:
Quote
User@Game <channel> chat content.

After modification, you will get this:
Quote
User@Game <channel> chat content. [Aug 18 00:49:49]

Tricky back in 2006 showed us how to do the modification to ds 2.1.1.
See middle of this thread: http://lpmuds.net/smf/index.php?topic=184.msg1072#msg1072
Since then, there's been some changes to /lib/secure/daemon/chat.c
For simplicity sake I include the entire modified chat.c here

Code: [Select]
/*    /daemon/chat.c
 *    from the Dead Souls Mudlib
 *    daemon to handle all mud chat channels
 *    created by Descartes of Borg 931220
 *
 *    IMC2 support added by Shadyman 2007-Sep-24
 *    Feelings support added by Shadyman 2007-Sep-24
 *    "mapping tags" added by Shadyman 2007-Sep-24
 */

#ifndef LOG_REMOTE_CHANS
#define LOG_REMOTE_CHANS 0
#endif

#ifndef LOG_LOCAL_CHANS
#define LOG_LOCAL_CHANS 1
#endif

#ifndef CHANNEL_PIPES
#define  CHANNEL_PIPES 0
#endif

#include <lib.h>
#include <pov.h>
#include <daemons.h>
#include <origin.h>
#include <message_class.h>
#include "include/chat.h"

inherit LIB_DAEMON;

string suspect,site,chan;
static private mapping Channels;
static private mapping chanlast;

static private string *local_chans = ({});
static private string *remote_chans = ({});
static string *syschans = ({});

static private mapping localchans = ([
        //I3 Channels
        "imud_code": "intercre",
        "imud_gossip": "intergossip",
        "ie_flibcode": "foundation",
        "dead_test4": "ds_test",
        "dead_souls": "ds",

        //IMC2 Channels
        "Server02:igame": "i2game2",
        "Server02:inews": "i2news2",
        "Server01:ibuild": "ibuild2",
        "Server01:ichat": "ichat2",
        "Server01:pchat": "pchat2",
        "Server01:i2game": "i2game2",
        "Server01:i2chat": "i2chat2",
        "Server01:i3chat": "i3chat2",
        "Server01:i2code": "i2code2",
        "Server01:i2news": "i2news2",
        "Server01:imudnews": "imudnews2",
        "Server01:irc": "irc2",
        "Server01:ifree": "ifree2",

        ]);

        static private mapping remotechans = ([
                //I3 Channels
                "intercre": "imud_code",
                "intergossip": "imud_gossip",
                "foundation": "ie_flibcode",
                "dutch": "dutch",
                "ds_test": "dead_test4",
                "ds": "dead_souls",

                //IMC2 Channels
                "i2game2": "Server02:igame",
                "i2news2": "Server02:inews",
                "ibuild2": "Server01:ibuild",
                "ichat2": "Server01:ichat",
                "pchat2": "Server01:pchat",
                "i2game2": "Server01:i2game",
                "i2chat2": "Server01:i2chat",
                "i3chat2": "Server01:i3chat",
                "i2code2": "Server01:i2code",
                "i2news2": "Server01:i2news",
                "imudnews2": "Server01:imudnews",
                "irc2": "Server01:irc",
                "ifree2": "Server01:ifree",
                ]);

static private mapping tags = ([
        "intermud"    : "%^B_BLACK%^WHITE%^",
        "muds"        : "%^B_BLACK%^WHITE%^",
        "connections" : "%^B_BLACK%^BOLD%^WHITE%^",
        "death"       : "%^BOLD%^RED%^",
        "cre"         : "%^BOLD%^GREEN%^",
        "admin"       : "%^BOLD%^MAGENTA%^",
        "newbie"      : "%^BOLD%^B_YELLOW%^",
        "gossip"      : "%^BOLD%^B_BLUE%^",

        "ds"          : "%^YELLOW%^",
        "dchat" :"%^CYAN%^",
        "intergossip" : "%^GREEN%^",
        "intercre"    : "%^ORANGE%^",

        "ibuild2"      : "%^B_RED%^%^YELLOW%^",
        "ichat2"       : "%^B_RED%^%^GREEN%^",
        "pchat2"       : "%^B_RED%^%^BOLD%^GREEN%^",
        "i2game2"      : "%^B_BLUE%^",
        "i2chat2"      : "%^B_GREEN%^",
        "i3chat2"      : "%^B_RED%^",
        "i2code2"      : "%^B_YELLOW%^%^RED%^",
        "i2news2"      : "%^B_YELLOW%^%^BLUE%^",
        "imudnews2"    : "%^B_YELLOW%^%^CYAN%^",
        "irc2"         : "%^B_BLUE%^%^GREEN%^",
        "ifree2"         : "%^B_BLUE%^%^GREEN%^",

        "default"     : "%^BOLD%^BLUE%^",
        "default-IMC2" : "%^BOLD%^WHITE%^%^B_BLUE%^",
        ]);

static void Setup(){
    mixed rchan2 = ({});
    mixed rchan3 = ({});
    remote_chans = ({});
    local_chans = ({"newbie","cre","gossip","admin","error", "intermud",
            "death", "connections", "muds" });
    syschans = ({ "intermud", "death", "connections", "muds" });

    local_chans += CLASSES_D->GetClasses();

    if(find_object(INTERMUD_D)){
        if(arrayp(INTERMUD_D->GetChannels()))
            rchan3 += distinct_array(INTERMUD_D->GetChannels());
    }
    if(find_object(IMC2_D)){
        if(arrayp(IMC2_D->GetChanList()))
            rchan2 += distinct_array(IMC2_D->GetChanList());
    }

    foreach(mixed foo in local_chans){
        if(!stringp(foo)){
            local_chans -= ({ foo });
        }
    }

    rchan3 = sort_array(filter(rchan3, (: stringp($1) :)), 1);
    rchan2 = sort_array(filter(rchan2, (: stringp($1) :)), 1);

    foreach(mixed bar in ({ rchan3, rchan2 })){
        foreach(mixed foo in bar){
            string svr, cnl;
            if(!stringp(foo) || member_array(foo, local_chans) != -1){
                bar -= ({ foo });
                continue;
            }
            if(sscanf(foo, "%s:%s", svr, cnl) == 2){
                if(member_array(cnl, local_chans) == -1 && !localchans[cnl] &&
                  member_array(cnl, rchan3) == -1){
                    localchans[foo] = cnl;
                    remotechans[cnl] = foo;
                }
            }
        }
    }
    remote_chans = distinct_array((rchan3 + rchan2));
    local_chans = distinct_array(local_chans);
}

static void create() {
    object pl;
    string *tmp_arr = ({});
    daemon::create();
    SetNoClean(1);
    Channels = ([]);

    call_out("Setup", 10);

    foreach(string kanal in local_chans + syschans){
        if( !Channels[kanal] ) Channels[kanal] = ({});
    }
    foreach(pl in users()) {
        string *chans;
        string channel;

        if( pl && !(chans = pl->GetChannels()) ) continue;
        foreach(channel in chans) {
            if( !Channels[channel] ) Channels[channel] = ({});
            Channels[channel] = distinct_array(Channels[channel] + ({ pl }));
        }
    }
    foreach( string channel in local_chans ){
        tmp_arr += ({ channel + "emote" });
        tmp_arr += ({ channel + ":" });
    }

    local_chans += tmp_arr;
}

string *AddRemoteChannel(mixed chan){
    string *ret = copy(remote_chans);
    if(base_name(previous_object()) != INTERMUD_D) return ret;
    if(stringp(chan)) chan = ({ chan });
    if(!arrayp(chan)) return ret;
    foreach(string element in chan){
        if(member_array(element, local_chans) != -1){
            chan -= ({ element });
        }
    }
    return copy(remote_chans = distinct_array(remote_chans += chan));
}

string *AddLocalChannel(mixed chan){
    string *ret = copy(local_chans);
    if(base_name(previous_object()) != PARTY_D) return ret;
    if(stringp(chan)) chan = ({ chan });
    if(!arrayp(chan)) return ret;
    foreach(string element in chan){
        if(member_array(element, local_chans) != -1){
            chan -= ({ element });
        }
    }
    return copy(local_chans = distinct_array(local_chans += chan));
}

string *RemoveRemoteChannel(mixed chan){
    string *ret = copy(remote_chans);
    if(base_name(previous_object()) != INTERMUD_D) return ret;
    if(stringp(chan)) chan = ({ chan });
    if(!arrayp(chan)) return ret;
    foreach(string element in chan){
        if(member_array(element, local_chans) != -1){
            chan -= ({ element });
        }
    }
    return copy(remote_chans = distinct_array(remote_chans -= chan));
}

string *RemoveLocalChannel(mixed chan){
    string *ret = copy(local_chans);
    if(base_name(previous_object()) != PARTY_D) return ret;
    if(stringp(chan)) chan = ({ chan });
    if(!arrayp(chan)) return ret;
    foreach(string element in chan){
        if(member_array(element, local_chans) != -1){
            chan -= ({ element });         }
    }     
    return copy(local_chans = distinct_array(local_chans -= chan));
}

varargs string *GetRemoteChannels(int localized){
    mixed *ret = ({});
    if(!localized) return copy(remote_chans);
    foreach(string chan in remote_chans){
        ret += ({ GetLocalChannel(chan) });
    }
    return ret;
}

string decolor(string str){
    string s1 = "", s2, s3, test;
    int tmp = 2;
    if(sscanf(str,"%s<%s>%s",s1,s2,s3) != 3)
        tmp = sscanf(str,"<%s>%s",s2,s3);
    if(tmp != 2) return str;
    else {
        test = s1+"<"+s2+">%^RESET%^"+strip_colours(s3);
        return test;
    }
}

varargs int CanListen(object who, string canal){
    if(!RESTRICTED_INTERMUD) return 1;
    if(canal && member_array(canal, local_chans) != -1) return 1;
    else return imud_privp(who);
}

varargs int CanTalk(object who, string canal){
    if(!RESTRICTED_INTERMUD) return 1;
    if(canal && member_array(canal, local_chans) != -1) return 1;
    else return imud_privp(who);
}

string *eventRegisterMember(string *chans) {
    string *tmp;
    object ob;
    string channel;

    if( !living(ob = previous_object()) ) return ({});
    tmp = ({});
    foreach(channel in chans) {
        /* just check out for secure channels */
        switch(channel) {
            case "admin":
                if( !archp(ob) ) break;
            case "cre": case "intercre": case "intergossip":
                if( !creatorp(ob) ) break;
            default:
            if( !Channels[channel]) Channels[channel] = ({});
            Channels[channel] = distinct_array(Channels[channel] + ({ ob }));
            tmp += ({ channel });
        }
    }
    return tmp;
}

string *eventRemoveMember(string *chans) {
    object ob;
    string channel;

    if( !living(ob = previous_object()) ) return({});
    foreach(channel in chans) {
        if( !Channels[channel] ) continue;
        else Channels[channel] -= ({ ob });
        if( !sizeof(Channels[channel]) ) map_delete(Channels, channel);
    }
    return chans;
}

int cmdLast(string feep){

    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(!sizeof(chanlast[feep]))
    {
        this_player()->eventPrint("That channel has no backlog.", MSG_ERROR);
        return 1;
    }
    if(!CanListen(this_player(),feep)){
        write("You lack privileges to that channel.");
        return 1;
    }
    this_player()->eventPrint(implode(chanlast[feep], "\n"));
    return 1;
}

static int LogIt(string what, string where, string canale){
    if( (member_array(canale,local_chans) != -1 && LOG_LOCAL_CHANS) ||
            ( member_array(GetRemoteChannel(canale),remote_chans) != -1 && LOG_REMOTE_CHANS) ){
        unguarded( (: write_file($(where), $(what)) :) );
        return 1;
    }
    else return 0;
}

varargs int eventAddLast(string feep, string str, string pchan, string pmsg, string pwho)
{
    string plainmsg;
    string Chan=feep;
    if(!chanlast)
        chanlast=([]);
    if(!sizeof(chanlast[Chan]))
        chanlast[Chan] = ({});
    if(sizeof(chanlast[Chan]) == 50)
        chanlast[Chan] = chanlast[Chan][1..sizeof(chanlast[Chan])];
    chanlast[Chan] += ({ str });
    Chan = GetLocalChannel(Chan);

    if (Chan == "death") return 1;

    //Log in either SQL or file
#ifdef MYSQL
    if (MYSQL_D->sql_request("INSERT INTO LOG_CHAT (Channel,Who,What) VALUES (\'"+ escape(pchan) +"\',\'" + escape(pwho) + "\',\'" + escape(pmsg) + "\')") == 0) {
        true();
    }
#endif
    Chan = GetLocalChannel(Chan);
    if(!pchan || pchan == "") pchan = "foo";
    plainmsg = "bar";
    if(pchan) plainmsg = "<" + pchan + "> ";
    if(pmsg) plainmsg += pmsg;
    if(pwho && pwho !="") plainmsg = pwho+" "+plainmsg;
    if(pchan && pchan != "admin"){
        LogIt("["+timestamp()+"] "+plainmsg+"\n", "/log/chan/"+Chan, Chan);
    }
    else {
        LogIt("["+timestamp()+"] "+plainmsg+"\n", "/secure/log/"+Chan, Chan);
    }
    return 1;
}

int cmdChannel(string verb, string str){
    string msg, name, rc, target, targetkey, target_msg, emote_cmd, remains;
    string *exploded;
    mixed array msg_data;
    object ob = 0;
    int i, emote, forcedemote;

    if(grepp(verb,"|")){
        string foo, bar;

        if(CHANNEL_PIPES){
            if(grepp(verb,"|morse")){
                str = morse(str);
                verb = replace_string(verb,"|morse","");
            }

            if(grepp(verb,"|colorize")){
                str = dbz_colors(str);
                verb = replace_string(verb,"|colorize","");
            }

            if(grepp(verb,"|annoy")){
                str = dbz_colors(str,2);
                verb = replace_string(verb,"|annoy","");
            }
            if(grepp(verb,"|file")){
                if(!file_exists(str) || !(str = read_file(str))){
                    write("Can't read that file.");
                    return 0;
                }
                verb = replace_string(verb,"|file","");
            }
        }

        if(sscanf(verb, "%s|%s", foo, bar) == 2) verb = foo;
    }

    if(grepp(verb, ":")){
        string *tmpv = explode(verb, ":");
        if(!sizeof(tmpv)) tmpv = ({"newbie"});
        verb = tmpv[0]+"emote";
        if(sizeof(tmpv) > 1) str = implode(tmpv[1..], ":") + str;
    }

    if(grepp(verb, ";")){
        string *tmpv = explode(verb, ";");
        if(!sizeof(tmpv)) tmpv = ({"newbie"});
        verb = tmpv[0]+"forcedemote";
        if(sizeof(tmpv) > 1) str = implode(tmpv[1..], ";") + str;
    }

    if(sizeof(str) > 2){
        if((str[0..0] == ":" || str[0..0] == ";") &&
                alphap(str[1..1]) && str[2..2] != " "){
            if(str[0..0] == ";" && !grepp(verb,"forcedemote"))
                verb = replace_string(verb,"emote","") + "forcedemote";
            else if(str[0..0] == ":" && !grepp(verb,"emote")) verb += "emote";
            str = str[1..];
        }
    }

    //******LIST******
    //allow "list <chan>" to list users listening
    if( verb == "list" ) {
        string *who;
        string ch, mud;

        if( !str ) return 0;

        //Find the remote listing for a channel on a specific mud
        if( sscanf(str, "%s@%s", ch, mud) == 2 ) {
            mud = trim(mud);
            if(!alphap(last(mud,1))) mud = truncate(mud,1);

            if( !Channels[ch] ) return 0;

            if( member_array(this_player(), Channels[ch]) == -1 ) return 0;

            if( ch == (ch = GetRemoteChannel(ch)) ) {
                if(!creatorp(this_player())){
                    write("Remote channel information is not available to players.");
                    return 1;
                }
            }

            if( !(mud = INTERMUD_D->GetMudName(mud)) ) {
                this_player()->eventPrint(mud_name() + " is not aware of "+
                        "such a place.", MSG_ERROR);
                return 1;
            }

            if(!CanTalk(this_player(),verb)) {
                write("You lack privileges to that channel.");
                return 1;
            }
            SERVICES_D->eventSendChannelWhoRequest(ch, mud);
            this_player()->eventPrint("Remote listing request sent.",
                    MSG_SYSTEM);
            return 1;
        }
        else ch = str;

        //If no such channel, or not a part of that channel, then no list.
        if( !Channels[ch] ) return 0;
        if( member_array(this_player(), Channels[str]) == -1 ) return 0;

        //Build and print the list of listeners
        who = GetChannelList(str);
        msg = "Online: " + implode(who, "   ");
        this_player()->eventPrint(msg, MSG_SYSTEM);
        return 1;
    }
    //******END LIST******


    //All emotes will fall into this IF structure and get tagged
    //as emote = 1 or forcedemote = 1

    //If it's a verb+emote, de-emote the verb, and mark as an emote
    if(grepp(verb, "emote")) {
        //Get the real channel
        if(grepp(verb, "forcedemote")){
            verb = replace_string(verb,"forcedemote","");   
            forcedemote = 1;
        }
        else verb = replace_string(verb,"emote","");
        emote = 1;
    }

    if(!strsrch(str,"^encode")) str = morse("(encoded):  "+str[7..]);

    if(find_object(INTERMUD_D) && !sizeof(remote_chans))
        remote_chans = distinct_array(INTERMUD_D->GetChannels());

    if(member_array(GetRemoteChannel(verb), remote_chans) == -1 &&
            member_array(verb, local_chans) == -1) local_chans += ({ verb });

    //******Access Checks
    //No talking if you're not allowed.
    if ( !CanTalk(this_player(),verb) ) {
        write("You lack privileges to that channel.");
        return 1;
    }
    //Toggle channel blocking
    if ( emote == 1 && ( !str || str == "" ) ) {
        this_player()->SetBlocked(verb + "emote");
        return 1;
    } else if ( !str || str == "" ) {
        this_player()->SetBlocked(verb);
        return 1;
    }
    //Syschans aren't for chatting on, only listening
    if ( member_array(verb, syschans) != -1 ) {
        write("This is not a channel for chatting.");
        return 1;
    }
    //If gagged, you can't talk on channels
    if ( this_player()->GetGagged(verb) ) {
        write("You have gag mode enabled. Type: 'gag off' to talk on channels.");
        return 1;
    }
    //Channel doesn't exist, probably an emote typo
    if(!Channels[verb]) return 0;
    //If not part of the channel, no chatting
    if( member_array(this_player(), Channels[verb]) == -1 ) return 0;
    //If blocked, allow no chatting
    if( this_player()->GetBlocked(verb) ) {
        if( this_player()->GetBlocked("all") ) {
            this_player()->eventPrint("You cannot chat while totally blocked.",
                    MSG_ERROR);
            return 1;
        }
        this_player()->eventPrint("Turn this channel on to talk on it.", MSG_ERROR);
        return 1;
    }
    //******End Access Checks

    //If there's no channel matching now, then it's a typo or wasn't meant for this file to pick up.
    if( !Channels[verb] ) return 0;
    //Find the remote channel's name, based on the local, pretty name
    rc = GetRemoteChannel(verb);
    //Check emotes
    if (emote == 1) {
        exploded = explode(str, " "); //BOOM!!! We have an array of words.
        if (sizeof(exploded) <= 1) {
            emote_cmd = str;
            remains = 0;
        } else {
            emote_cmd = exploded[0];
            remains = implode(exploded[1..]," ");
        }

        //***********************************
        //Find a target for targetted emotes.
        //***********************************
        if( !remains ) { //If no arguments and just one word
            msg_data = SOUL_D->GetChannelEmote(emote_cmd, ""); //Search for a feeling that matches
        } else { //If there is an argument to the emote,
            if( ob = find_living(target = convert_name(remains)) ) {  //If there is a living target
                msg_data = SOUL_D->GetChannelEmote(emote_cmd, "LIV");
                //If it's not there, get the emote's LVS text.
                //if (!msg_data)
                // msg_data = SOUL_D->GetChannelEmote(emote_cmd, "LVS", remains);
            } else if( strsrch(target, "@") == -1 ) { //If no living target
                string array words = explode(remains, " ");
                target = "";
                for(i=0; i<sizeof(words); i++) {
                    target += lower_case(words[i]);
                    if( ob = find_living(target) ) {
                        if( i < sizeof(words)-1 ) {
                            remains = implode(words[(i+1)..], " ");
                        } else {
                            remains = 0;
                        }
                        //If it's not there, get the emote's LVS STR text.
                        if (!msg_data)
                            msg_data = SOUL_D->GetChannelEmote(emote_cmd, "LVS STR", remains);
                        break;
                    }
                }
                if( !ob ) {
                    msg_data = SOUL_D->GetChannelEmote(emote_cmd, "STR", remains);
                    target = 0;
                }

            } else {
                string array words;

                //Find any @'s in the remains.. Should be User@Mud
                i = strsrch(remains, "@", -1);

                //If there's not enough room for a proper name@mud, just do it as a string
                if ( i >= strlen(remains)-1 ) {
                    msg_data = SOUL_D->GetChannelEmote(emote_cmd, "STR", remains);
                    target = 0;
                } else { //Otherwise, call mud and find user
                    string mud,temp;
                    words = explode(remains[(i+1)..], " ");
                    target = remains[0..i];
                    remains = "";
                    while(sizeof(words)) {
                        temp = implode(words, " ");
                        temp = trim(temp);
                        if(!alphap(last(temp,1))) temp = truncate(temp,1);
                        mud = INTERMUD_D->GetMudName(lower_case(temp));
                        if (!mud) mud = IMC2_D->find_mud(lower_case(temp));
                        if( mud ) {
                            target += mud;
                            break;
                        }
                        if( remains == "" ) remains = words[<1];
                        else remains = words[<1] + " " + remains;
                        words = words[0..<2];
                    }

                    //If we couldn't find the mud,
                    if ( !mud ) {
                        msg_data = SOUL_D->GetChannelEmote(emote_cmd, "STR", remains);
                        target = 0;
                    } else {
                        if ( trim(remains) == "" ) {
                            msg_data = SOUL_D->GetChannelEmote(emote_cmd, "LIV");
                        } else {
                            msg_data = SOUL_D->GetChannelEmote(emote_cmd, "LIV STR", remains);
                        }
                    }
                }
            }
        } //Done finding target

        //***********************************
        //Find the target's proper name and target the message at him/her.
        //***********************************

        if ( msg_data ) { //There's a target
            string sgen = this_player()->GetGender();
            string tgen = 0;

            if ( ob ) { //If a local user
                target = ob->GetName();
                tgen = ob->GetGender();
            } else if ( target ) { //If a mud user
                string user, mud;

                sscanf(target, "%s@%s", user, mud);
                targetkey = target;
                tgen = SERVICES_D->GetRemoteGender(user, mud);
                target = SERVICES_D->GetRemoteDisplayName(user, mud);
                if( !target ) target = capitalize(targetkey);
            }

            //Calculate the viewpoint for 3rd parties
            str = create_message(POV_OBSERVER, msg_data[0][0],
                    msg_data[0][1], "$N", sgen, "$O", tgen,
                    msg_data[1]);

            //If it's targetted, calculate the message for the target
            if ( target ) {
                target_msg = create_message(POV_TARGET, msg_data[0][0],
                        msg_data[0][1], "$N", sgen,
                        "$O", tgen, msg_data[1]);
                target_msg = replace_string(target_msg, "$O's", "your");   
            }
        } else { //There's no target. Spurt it out like the user put it in.
            //Forced emotes only allow real emotes, not custom ones.
            if (forcedemote == 1) {
                if ( member_array( emote_cmd,SOUL_D->GetEmotes() ) > -1 ) {
                    write("Invalid syntax. See %^CYAN%^help "+emote_cmd+"%^RESET%^ for a list of usages.");
                    return 1;
                } else {
                    write("No such feeling. See %^CYAN%^help feelings%^RESET%^ for a list of feelings.");
                    return 1;
                }
            } else {
                str = "$N " + str;
                target = 0;
            }
        }
    }

    //If admin or cre channels, Capitalize a person's real name, because admins can be physically hidden
    if( verb == "admin" || verb == "cre" ) {
        if( !(name = this_player()->GetCapName()) )
            name = capitalize(this_player()->GetKeyName());
    }
    else name = this_player()->GetName();
    //Add the "Name" $N to the string
    if(!grepp(str,"$N") && emote) str = "$N "+str;
    //Send locally
    eventSendChannel(name, verb, str, emote, target, target_msg);
    //If it's a remote channel, send it remotely.
    if(member_array(GetRemoteChannel(verb), remote_chans) != -1
            && member_array(verb, local_chans) == -1){
        if (grepp(GetRemoteChannel(verb),":")) { //It's an IMC2 channel
            if(IMC2_D->getonline() != 1){
                return 1;
            }
            name = replace_string(name, " ", "");
            if( ob ) {
                IMC2_D->channel_out(name, rc, replace_string(replace_string(str,"$N ",""),"$O",target), emote);
            } else if ( targetkey ) {
                IMC2_D->channel_out(name, rc, replace_string(replace_string(str,"$N ",""),"$O",targetkey), emote);
            } else {
                IMC2_D->channel_out(name, rc, replace_string(str,"$N ",""), emote);
            }
        } else { //It's an I3 channel
            if( ob ) {
                SERVICES_D->eventSendChannel(name, rc, str, emote, convert_name(target), target_msg);
            } else {
                SERVICES_D->eventSendChannel(name, rc, str, emote, convert_name(targetkey), target_msg);;
            }
        }
    }
    return 1;
}

varargs void eventSendChannel(string who, string ch, string msg, int emote,
        string target, string targmsg) {
    object channeler = find_player(lower_case(who));
    int terminal;
    string prev = base_name(previous_object());
    string pchan,pmsg;
    string chatlayout = "%s %s<%s>%s %s";
    string emotelayout = "%s<%s>%s %s";
    //string chatlayout = "%s says, %s(%s)%s '%s'";
    //string emotelayout = "%s(%s)%s %s";

    if(prev == INSTANCES_D){
        terminal = 1;
    }
    if(prev == SERVICES_D) terminal = 1;
    if(prev == IMC2_D) terminal = 1;
    if(!terminal){
        string rch = GetRemoteChannel(ch);
        if(member_array(rch, remote_chans) == -1){
            INSTANCES_D->eventSendChannel(who,ch,msg,emote,target,targmsg);
        }
    }

    pchan=ch;
    if(!channeler) channeler = this_player();
    if(!strsrch(msg,"-.--. . -. -.-. --- -.. . -.. -.--.- ---...")) msg = unmorse(msg);
    if(targmsg && !strsrch(targmsg,"-.--. . -. -.-. --- -.. . -.. -.--.- ---..."))
        targmsg = unmorse(targmsg);

    if(this_player() && this_player() != channeler) channeler = this_player();

    if(!strsrch(base_name(previous_object()), "/realms/") ||
            !strsrch(base_name(previous_object()), "/open/")) {
        return 0;
    }

    if(member_array(ch, syschans) != -1) {
        emote = 0;
    }
    if(channeler){
        if(!CanTalk(channeler, ch) && member_array(ch, syschans) == -1){
            return;
        }
    }
    if( file_name(previous_object()) == SERVICES_D ||
            file_name(previous_object()) == IMC2_D) {
        ch = GetLocalChannel(ch);
        if( emote && sizeof(who)) msg = replace_string(msg, "$N", who);
    }
    else if( origin() != ORIGIN_LOCAL && previous_object() != master() &&
            file_name(previous_object()) != PARTY_D &&
            file_name(previous_object()) != UPDATE_D &&
            file_name(previous_object()) != INSTANCES_D &&
            member_array(ch, syschans) == -1){
        return;
    }
    prev = file_name(previous_object());
    if(!Channels[ch] && prev != SERVICES_D && prev != INSTANCES_D){
        return;
    }
    if( emote ) {
        object *obs;
        object ob;
        string this_msg, tmp;

        if( target && (ob = find_player(convert_name(target))) ) {
            target = ob->GetName();
        }

        //Colorize emote channels
        if (member_array(lower_case(ch),keys(tags)) >= 0){
            this_msg = tags[lower_case(ch)];
        } else {
            if(member_array(ch, local_chans) < 0 && (prev == IMC2_D ||
                        member_array(ch, (keys(INTERMUD_D->GetChannelList())
                                || ({}))) < 0)){
                this_msg = tags["default-IMC2"]; //Use the default IMC2 entry
            }
            else {
                this_msg = tags["default"]; //Use the default entry
            }
        }

        msg = replace_string(msg, "$N", who);
        if( target ) {
            msg = replace_string(msg, "$O", target);
            targmsg = replace_string(targmsg, "$N", who);
            targmsg = capitalize(replace_string(targmsg, "$O", "you"));
        }

        //Put together the channel emote message
        tmp = sprintf(emotelayout, this_msg, ch, "%^RESET%^", msg, time());

        //Store message in the history list
        eventAddLast(ch, tmp, pchan, msg);

        if(Channels[ch]){
            obs = filter(Channels[ch], (: $1 && !($1->GetBlocked($(ch))) :));
            foreach(object listener in obs) {
                int ignore;
                if(sscanf(who,"%s@%s",suspect,site) < 2) {
                    suspect = who;
                    site = "@"+mud_name();
                }
                else site = "@"+site;
                if( listener == ob ) continue;
                if(sizeof(listener->GetMuffed()))
                    foreach(string jerk in listener->GetMuffed()){
                        if(jerk && lower_case(suspect) == lower_case(jerk)){
                            ignore = 1;
                        }
                        if(jerk && lower_case(site[1..]) == lower_case(jerk)){
                            ignore = 1;
                        }
                    }
                if(listener->GetNoChanColors()) tmp = decolor(tmp);
                if(!ignore && CanListen(listener,ch) &&
                        !(listener->GetMuted(ch))){
                    listener->eventPrint(tmp, MSG_CHAN);
                }
                ignore = 0;
            }
            if( member_array(ob, obs) != -1 ) {
                if( ob && !(ob->GetBlocked(ch)) ) {
                    int ignore;
                    tmp = sprintf(emotelayout, this_msg, ch, "%^RESET%^", targmsg, time());
                    if(sizeof(ob->GetMuffed()))
                        foreach(string jerk in ob->GetMuffed()){
                            if(jerk && lower_case(suspect) == lower_case(jerk)) ignore = 1;
                            if(jerk && lower_case(site[1..]) == lower_case(jerk)) ignore = 1;
                        }
                    if(ob->GetNoChanColors()) tmp = decolor(tmp);
                    if(!ignore && CanListen(ob,ch)&& !(ob->GetMuted(ch)))
                        ob->eventPrint(tmp, MSG_CHAN, time());
                    ignore = 0;
                }
            }
        }
        suspect = "";
        site = "";
    }
    else {
        object *obs;
        string chancolor;

        //Colorize flag
        if (member_array(lower_case(ch),keys(tags)) >= 0) { //If there's an entry for the channel
            chancolor = tags[lower_case(ch)]; //Use it
        } else { //Otherwise
            if(member_array(ch, local_chans) < 0 && (prev == IMC2_D ||
                        member_array(ch, (keys(INTERMUD_D->GetChannelList())
                                || ({}))) < 0)){
                chancolor = tags["default-IMC2"]; //Use the default IMC2 entry
            }
            else {
                chancolor = tags["default"]; //Use the default entry
            }
        }
msg += " [" + ctime(time())[4..18] + "]";
        pmsg = msg;

        //Put together the channel emote message
        msg = sprintf(chatlayout, who, chancolor, ch, "%^RESET%^", pmsg, time());
        eventAddLast(ch, msg, pchan, pmsg, who, time());
     
        if(Channels[ch]) {
            obs = filter(Channels[ch], (: $1 && !($1->GetBlocked($(ch))) :));
            foreach(object ob in obs){
                int ignore;
                if(sscanf(who,"%s@%s",suspect,site) < 2) {
                    suspect = who;
                    site = "@"+mud_name();
                }
                else site = "@"+site;

                if(sizeof(ob->GetMuffed()))
                    foreach(string jerk in ob->GetMuffed()){
                        if(jerk && lower_case(suspect) == lower_case(jerk)) ignore = 1;
                        if(jerk && lower_case(site[1..]) == lower_case(jerk)) ignore = 1;
                    }
                if(ob->GetNoChanColors()) msg = decolor(msg);
                if(!ignore && CanListen(ob,ch)&& !(ob->GetMuted(ch)))
                    ob->eventPrint(msg, MSG_CHAN);

                ignore = 0;
                suspect ="";
                site = "";
            }
        }
    }
}

string *GetChannelList(string ch) {
    string *ret;
    object who;

    if( file_name(previous_object()) == SERVICES_D ) ch = GetLocalChannel(ch);
    else if( origin() != ORIGIN_LOCAL ) return ({});
    if( !Channels[ch] ) return ({});
    ret = ({});
    foreach(who in Channels[ch]) {
        if( !who || who->GetInvis() || who->GetBlocked(ch) )
            continue;
        ret += ({ who->GetName() });
    }
    return ret;
}

string *GetLocalChannels(){
    return copy(local_chans);
}

string GetLocalChannel(string ch) {
    if(ch && !strsrch(ch,"server0")){
        ch = replace_string(ch, "server01", "Server01");
        ch = replace_string(ch, "server02", "Server02");
    }
    if (sizeof(localchans[ch])) return localchans[ch];
    else return ch;
}

string GetRemoteChannel(string ch) {
    if (sizeof(remotechans[ch])) return remotechans[ch];
    else return ch;
}

int GetListening(object player, string ch){
    if(!Channels[ch] ||
            member_array(player, Channels[ch]) == -1) return 0;
    return 1;
}

string *GetChannels() { return sort_array(copy(keys(Channels)),1); }
string *GetSystemChannels() { return sort_array(copy(syschans),1); }
mapping GetTags() { return copy(tags); }
string GetTag(string ch) { return tags[ch]; }

Now all you need to do is replace /lib/secure/daemon/chat.c with this code above.
(Note, I may have also added a few more ctime() else where here and there that might be redundant, just erase them if you find them).

Or if you wish to DIY, then just do this:

Find (at around line 899)
Code: [Select]
pmsg = msg;
For date + time stamp, insert before it, this
Code: [Select]
msg += " [" + ctime(time())[4..18] + "]";
For time only stamp (no date), insert this instead:
Code: [Select]
msg += " [" + ctime(time())[11..18] + "]";

9
General / Re: Help on choosing a mudlib
« on: August 03, 2012, 08:57:20 pm »
Hey parham just install Dead Souls and modify it to get started. That's the quickest way. What a lot of people do is they aim too high and try to think of a grand complex game which never gets to be finished. So just get the basics up and running, which Dead Souls readily offers. Then just modify things from there onwards.

Tigwyk raised an interesting question. Text based MUD can potentially benefit blind people greatly since there are graphics. Just need to plug in a text to voice generator. I think some of the existing clients already have the function. AT&T's Natural Voices are good engines and should be able to work on Zugg's MUD client.

10
Open Chat / Merry X'mas and Happy New Year 2012 everyone!
« on: December 03, 2011, 08:35:02 pm »
Time really flies and it's good to see this forum is still active and the lib continued to update thanks for Cratylus and everybody.

Merry X'mas and Happy New Year!  ;)

11
Dead Souls Support / Re: Using Anyterm with Dead Souls
« on: November 02, 2011, 02:48:07 pm »
Thanks for the clear instructions following which, with a bit of modifications and help of a friend, we managed to get it working on CentOS, running Dead Souls 3.0

I read numerous complaints of crashes of Dead Souls 3.2. Has anyone tried the latest 3.6 using with & without Anyterm with much trouble at all? Or have previous problems resolved?

If people are still having problems with 3.2+, I'll just leave mine as it is at 3.0. Otherwise I might try upgrading to 3.6 next week as I think DS3.0 doesn't handle multiple users same IP issue when logging on using Anyterm (fix was introduced to DS3.2 onwards).

12
Other / What's a good open source 2D MMORPG pack?
« on: October 30, 2011, 08:38:24 am »
Hey guys, I am in the middle of integrating DS3.6 to my SMF2 forum via Anyterm.

While I personally enjoy plain text MUD, most of my visitors will prefer at least some 2D graphical interaction. So I am going to also install a 2D MMORPG as an option for users.

What's a good open source 2D MMORPG package I can easily deploy (easily like DS) and use its already built-in default world?
I don't have time to develop anything but just want to install it, get it running, and provide a very basic social gaming platform for people.

Something like Ultima Online sort of staging would be perfect.
Eclipse has semi decent graphics http://www.touchofdeathforums.com/eclipse/media.php

A few other ones I came across include:
Mana World
Elsium
Kizare
OpenTibia
ExtremeWorlds

Other than Eclipse, all of them seem pretty sh1t actually. Any suggestions?

2D MMORPG, easy to install, open source, built-in default world. Preferably active community. Server needs to run on Linux. Client best to offer Windows, Linux and Mac systems, but if only Windows that's fine too. Preferably if the user can just use Explorer or Firefox to log on, otherwise requirement to install a small client software is acceptable too.

Thanks!

13
Code Vault / Re: Horoscope.c with daily Yahoo astrology update
« on: October 27, 2011, 05:01:12 am »
Thanks z993126. Glad somebody is keeping it up to date :D

14
Code Vault / Re: Eliza
« on: June 05, 2011, 02:50:54 pm »
Enjoy. Eliza NPC.

Ported by Scott Phillips, one of the lead programmers for Ultima Online, from BASIC version done by Patricia Danielson and Paul Hashfield. Source code released under GPL with kind permission from Scott Phillips.

Code: [Select]
#include <lib.h>
#include <daemons.h>

/*
eliza.c - A port of the classic Eliza therapist program.

Ported from eliza.bas, which included the following author information:

CREATIVE COMPUTING
MORRISTOWN, NEW JERSEY
ADAPTED FOR IBM PC BY
PATRICIA DANIELSON AND PAUL HASHFIELD

Port by Scott Phillips (Jym@SWmud)
E-mail: grimli333@gmail.com

*/

inherit LIB_SENTIENT;

array keywords = ({
"CAN YOU ",
"CAN I ",
"YOU ARE ",
"YOU'RE ",
"I DON'T ",
"I FEEL ",
"WHY DON'T YOU ",
"WHY CAN'T I ",
"ARE YOU ",
"I CAN'T ",
"I AM ",
"I'M ",
"YOU ",
"I WANT ",
"WHAT ",
"HOW ",
"WHO ",
"WHERE ",
"WHEN ",
"WHY ",
"NAME ",
"CAUSE ",
"SORRY ",
"DREAM ",
"HELLO ",
"HI ",
"MAYBE ",
"NO",
"YOUR ",
"ALWAYS ",
"THINK ",
"ALIKE ",
"YES ",
"FRIEND ",
"COMPUTER",
"NOKEYFOUND",
});

mapping conjugations = ([
" ARE " : " AM ",
" WERE " : " WAS ",
" YOU " : " I ",
" YOUR " : " MY ",
" I'VE " : " YOU'VE ",
" I'M " : " YOU'RE ",
" ME " : " YOU "
]);

array replies = ({
"DON'T YOU BELIEVE THAT I CAN*",
"PERHAPS YOU WOULD LIKE TO BE LIKE ME*",
"YOU WANT ME TO BE ABLE TO*",
"PERHAPS YOU DON'T WANT TO*",
"DO YOU WANT TO BE ABLE TO*",
"WHAT MAKES YOU THINK I AM*",
"DOES IT PLEASE YOU TO BELIEVE I AM*",
"PERHAPS YOU WOULD LIKE TO BE*",
"DO YOU SOMETIMES WISH YOU WERE*",
"DON'T YOU REALLY*",
"WHY DON'T YOU*",
"DO YOU WISH TO BE ABLE TO*",
"DOES THAT TROUBLE YOU*",
"DO YOU OFTEN FEEL*",
"DO YOU OFTEN FEEL*",
"DO YOU ENJOY FEELING*",
"DO YOU REALLY BELIEVE I DON'T*",
"PERHAPS IN GOOD TIME I WILL*",
"DO YOU WANT ME TO*",
"DO YOU THINK YOU SHOULD BE ABLE TO*",
"WHY CAN'T YOU*",
"WHY ARE YOU INTERESTED IN WHETHER OR NOT I AM*",
"WOULD YOU PREFER IF I WERE NOT*",
"PERHAPS IN YOUR FANTASIES I AM*",
"HOW DO YOU KNOW YOU CAN'T*",
"HAVE YOU TRIED?",
"PERHAPS YOU CAN NOW*",
"DID YOU COME TO ME BECAUSE YOU ARE*",
"HOW LONG HAVE YOU BEEN*",
"DO YOU BELIEVE IT IS NORMAL TO BE*",
"DO YOU ENJOY BEING*",
"WE WERE DISCUSSING YOU--NOT ME.",
"OH, I*",
"YOU'RE NOT REALLY TALKING ABOUT ME, ARE YOU?",
"WHAT WOULD IT MEAN TO YOU IF YOU GOT*",
"WHY DO YOU WANT*",
"SUPPOSE YOU SOON GOT*",
"WHAT IF YOU NEVER GOT*",
"I SOMETIMES ALSO WANT*",
"WHY DO YOU ASK?",
"DOES THAT QUESTION INTEREST YOU?",
"WHAT ANSWER WOULD PLEASE YOU THE MOST?",
"WHAT DO YOU THINK?",
"ARE SUCH QUESTIONS ON YOUR MIND OFTEN?",
"WHAT IS IT THAT YOU REALLY WANT TO KNOW?",
"HAVE YOU ASKED ANYONE ELSE?",
"HAVE YOU ASKED SUCH QUESTIONS BEFORE?",
"WHAT ELSE COMES TO MIND WHEN YOU ASK THAT?",
"NAMES DON'T INTEREST ME.",
"I DON'T CARE ABOUT NAMES --PLEASE GO ON.",
"IS THAT THE REAL REASON?",
"DON'T ANY OTHER REASONS COME TO MIND?",
"DOES THAT REASON EXPLAIN ANYTHING ELSE?",
"WHAT OTHER REASONS MIGHT THERE BE?",
"PLEASE DON'T APOLOGIZE!",
"APOLOGIES ARE NOT NECESSARY.",
"WHAT FEELINGS DO YOU HAVE WHEN YOU APOLOGIZE?",
"DON'T BE SO DEFENSIVE!",
"WHAT DOES THAT DREAM SUGGEST TO YOU?",
"DO YOU DREAM OFTEN?",
"WHAT PERSONS APPEAR IN YOUR DREAMS?",
"ARE YOU DISTURBED BY YOUR DREAMS?",
"HOW DO YOU DO ...PLEASE STATE YOUR PROBLEM.",
"YOU DON'T SEEM QUITE CERTAIN.",
"WHY THE UNCERTAIN TONE?",
"CAN'T YOU BE MORE POSITIVE?",
"YOU AREN'T SURE?",
"DON'T YOU KNOW?",
"ARE YOU SAYING NO JUST TO BE NEGATIVE?",
"YOU ARE BEING A BIT NEGATIVE.",
"WHY NOT?",
"ARE YOU SURE?",
"WHY NO?",
"WHY ARE YOU CONCERNED ABOUT MY*",
"WHAT ABOUT YOUR OWN*",
"CAN YOU THINK OF A SPECIFIC EXAMPLE?",
"WHEN?",
"WHAT ARE YOU THINKING OF?",
"REALLY, ALWAYS?",
"DO YOU REALLY THINK SO?",
"BUT YOU ARE NOT SURE YOU*",
"DO YOU DOUBT YOU*",
"IN WHAT WAY?",
"WHAT RESEMBLANCE DO YOU SEE?",
"WHAT DOES THE SIMILARITY SUGGEST TO YOU?",
"WHAT OTHER CONNECTIONS DO YOU SEE?",
"COULD THERE REALLY BE SOME CONNECTION?",
"HOW?",
"YOU SEEM QUITE POSITIVE.",
"ARE YOU SURE?",
"I SEE.",
"I UNDERSTAND.",
"WHY DO YOU BRING UP THE TOPIC OF FRIENDS?",
"DO YOUR FRIENDS WORRY YOU?",
"DO YOUR FRIENDS PICK ON YOU?",
"ARE YOU SURE YOU HAVE ANY FRIENDS?",
"DO YOU IMPOSE ON YOUR FRIENDS?",
"PERHAPS YOUR LOVE FOR FRIENDS WORRIES YOU.",
"DO COMPUTERS WORRY YOU?",
"ARE YOU TALKING ABOUT ME IN PARTICULAR?",
"ARE YOU FRIGHTENED BY MACHINES?",
"WHY DO YOU MENTION COMPUTERS?",
"WHAT DO YOU THINK MACHINES HAVE TO DO WITH YOUR PROBLEM?",
"DON'T YOU THINK COMPUTERS CAN HELP PEOPLE?",
"WHAT IS IT ABOUT MACHINES THAT WORRIES YOU?",
"SAY, DO YOU HAVE ANY PSYCHOLOGICAL PROBLEMS?",
"WHAT DOES THAT SUGGEST TO YOU?",
"I SEE.",
"I'M NOT SURE I UNDERSTAND YOU FULLY.",
"COME COME ELUCIDATE YOUR THOUGHTS.",
"CAN YOU ELABORATE ON THAT?",
"THAT IS QUITE INTERESTING.",
});

string last_question;

// These arrays are not well-documented in the BASIC file.
// They are index by keyword number (1-36) and used to determine
// which responses to use.
// I have gone ahead and parsed out the data from the file into the
// three arrays.
array index_S = ({
1, 4, 6, 6, 10, 14, 17, 20, 22, 25, 28, 28, 32, 35, 40, 40, 40, 40, 40, 40, 49,
51, 55, 59, 63, 63, 64, 69, 74, 76, 80, 83, 90, 93, 99, 106
});

array index_Replies = ({
1, 4, 6, 6, 10, 14, 17, 20, 22, 25, 28, 28, 32, 35, 40, 40, 40, 40, 40, 40, 49,
51, 55, 59, 63, 63, 64, 69, 74, 76, 80, 83, 90, 93, 99, 106
});

array index_N = ({
3, 5, 9, 9, 13, 16, 19, 21, 24, 27, 31, 31, 34, 39, 48, 48, 48, 48, 48, 48, 50,
54, 58, 62, 63, 63, 68, 73, 75, 79, 82, 89, 92, 98, 105, 111
});

void eliza_speak(string phrase)
{
// Potential place to pretty-up the Eliza speech - uncomment to
// make it a little more modern.
//eventForce("say "+capitalize(lower_case(phrase)));

// This is old-school mode, all upper-case.
eventForce("say "+phrase);
}

// Called whenever anyone in the room speaks.  Eliza will only respond if they are
// speaking directly to her. (e.g. ask eliza Why does my mother hate me?)
void talk_response( object who, object targ, string msg, string lang, int cls )
{
int numKeywords, keywordIndex, keywordPos, i;
string keyword;
string postKeyword;
string reply;
if( targ == this_object() )
{
// First, add two spaces to the beginning and end of the string, as per line 201 of the .bas.
string playerInput = upper_case( "  " + msg + "  " );
// The .bas file now says it will remove apostrophes, but that part is REMed out.

// Check for 'SHUT', a specific condition.
if( strsrch( playerInput, "SHUT" ) != -1 )
{
eliza_speak("O.K. IF YOU FEEL THAT WAY I'LL SHUT UP....");
return;
}

// Another special condition, don't let the person ask the same
// question twice.
if( last_question == playerInput )
{
eliza_speak("PLEASE DON'T REPEAT YOURSELF!");
return;
}

// Now, search for any of the keywords in the player's input.
for(i=0; i<sizeof(keywords);i++)
{
if( strsrch( playerInput, keywords[i] ) != -1 )
{
//eventForce("say I found a keyword: "+keywords[i]);
keywordIndex = i+1;
// The basic file looks for keyword 13.  QBASIC starts arrays at 1, not 0.
// this would be either keywords[12] or, for simplicity, i'll just check it
// directly.
if( keywords[i] == "YOU " )
{
// Now see if keyword 29 is present, which is "YOUR "
if( strsrch( playerInput, "YOUR " ) != -1 )
{
// Use YOUR instead if found.
keywordIndex = 29;
}
}

keyword = keywords[keywordIndex-1];

break;
}
}
if(keyword != 0)
{
// Since we found a keyword, we take part of the string and conjugate it,
// using the list of strings to be swapped.
keywordPos = strsrch( playerInput, keyword );
// postKeyword is a variable called C$ in the basic file.  It represents
// everything in the string after the keyword.
postKeyword = playerInput[keywordPos+strlen(keyword)..strlen(playerInput)];

// Now, we replace all of the conjugations found in the string,
// for example: are -> am, your -> my
foreach( string toReplace in keys(conjugations) )
{
if(strsrch( postKeyword, toReplace ) != -1 )
{
postKeyword = replace_string( postKeyword, toReplace, conjugations[toReplace] );
}
else
{
// Only do the reverse replacement if the first one wasn't found.
// Otherwise you'd change YOUR to MY and then MY back to YOUR, which isn't
// terribly productive.
postKeyword = replace_string( postKeyword, conjugations[toReplace], toReplace );
}
}

// Remove all exclamation points for some reason
postKeyword = replace_string( postKeyword, "!", "" );
}
else
{
// No keyword found.
keyword = keywords[35];
keywordIndex = 36;
}

// Now reply using the keyword.
reply = replies[ index_Replies[keywordIndex-1]-1 ];

// Eliza now increments the indices in order to not repeat herself.
// Note this is going to be per-instance of eliza, not per conversation!
// So if she's listening on a chat channel, it might increment rather quickly.
// I doubt the original designer of the .BAS intended it to be used in that way.
// Note, all these -1s are an effort to keep the index data in the arrays
// ONE-based, as QBASIC started its arrays at 1 and LPC starts its at 0.
index_Replies[keywordIndex-1] = index_Replies[keywordIndex-1] + 1;
if(index_Replies[keywordIndex-1] > index_N[keywordIndex-1])
{
// Still not terribly clear on the S index.  I think it might stand
// for Substitution?
index_Replies[keywordIndex-1] = index_S[keywordIndex-1];
}

// If there is no asterix at the end of this reply, it's a statement that does
// not get combined with the user's input.
if( reply[ strlen(reply)-1 ] != '*' )
{
eliza_speak(reply);
}
else
{
//eventForce("say asterix found");
if( postKeyword == "   " )
{
eliza_speak("YOU WILL HAVE TO ELABORATE MORE FOR ME TO HELP YOU");
}
else
{
// Find the last non-punctuation, non-space and trim the string to there.
for(i=strlen(postKeyword)-1; i>0; i--)
{
if(postKeyword[i] != ' ' && postKeyword[i] != '.' && postKeyword[i] != '!' && postKeyword[i] != '?')
{
postKeyword = postKeyword[0..i];
break;
}
}
// Add a question mark, as the only statements are the ones listed with no asterix at the end.
eliza_speak(reply[0..strlen(reply)-2]+" "+postKeyword+"?");
}
}

last_question = playerInput;
}
}

static void create() {
    int i;
    sentient::create();    
    SetKeyName("eliza");
    SetId(({"eliza"}));
    SetShort("Eliza the therapist");
    SetLong("This is Eliza, the therapist.  Ask her anything!");
    SetPolyglot(1);
    SetLanguage("common", 100);
    SetDefaultLanguage("common");    
    SetLevel(1);
    SetRace("human");
    SetGender("female");
    
AddTalkResponse("default", (: talk_response :) );    
}

void init(){
    ::init();
}


15
Open Chat / Happy new year 2011 everyone
« on: December 28, 2010, 02:26:03 am »
Hi everyone and happy new year!

For the past 3 or 4 years I notice it's usually around this time of the year I have the luxury of a couple of weeks off to play around with MUD. Here I am again except this year I'm busier. So just want to say a big hi and wish everyone a good year to come.

Keep up the good work and please keep this lib going!!

Pages: [1] 2 3 ... 6