LPMuds.net Forums > General

Starting a mud: worthwhile or not?

<< < (2/2)

Kaylus:

--- Quote ---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.
--- End quote ---

The main issue with this is attribution; if I post to a comment to the game, the following messages on the channel going into a reply to that comment might not be related, so you'd have to have directed message to the character, and this doesn't happen alot on channels. "[chat] Kaylus replies to Jezebel: Hahaha, that's the funniest thing I've heard!!!!"

Of course, it's feasible if you do make such a system, I'd be interested in looking at it. Another idea would be linking a channel into a direct twitter feed of it's own (MojoMud_Chat twitter account, or facebook "interest page" account). With the twitter account you'd have to worry about text limits, and with the facebook account it would be really great to be able to thread/take advantage of comments but you run into the same issue as above.


--- Quote ---People love collections and achievements too.  If you make systems where you can collect bits of things and turn them in for credit and/or earn achievements for doing certain things, AND have that auto-post to their twitter/facebook/etc, it also counts as a plus for most people.
--- End quote ---

That's an excellent idea, since it's the basis of sites like PSN, XboxLive, and even Facebook and G+ gaming systems. At least that mine garner interest for the game and allow an outside competition/some kind of link to the facebook world.

drakkos:
As a data point on how worthwhile an achievement system can be, when I added such a system to Discworld there was a very noticeable jump in player numbers (of around 40 players or so in peak time).  Suddenly we started seeing people who hadn't turned up in ages, and some of them even stayed around once they'd gotten the taste for it again.  We even broke 200 players online at once, which is something that hadn't been done since 2004ish. 

On Discworld it was successful because it gave people who had 'left' a new reason to log on again, but I think Quix and Kaylus are right that new players will largely expect systems like this from the get-go, along with the other accouterments they're familiar with from other gaming contexts.

Drakkos.

drakkos:
On the topic of achievement systems, I wrote this today for my MUD blog:  http://drakkos.co.uk/blog/blog.c?action=filter&blog=mud%20commentary&id=619

amylase:

--- Quote from: Nulvect on September 27, 2011, 04:09:55 AM ---
--- Quote from: quixadhal on September 26, 2011, 04:35:46 PM ---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.

--- End quote ---

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.

--- End quote ---

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: ---/* 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);
}

--- End code ---

amylase:

Another piece here:


--- Code: ---/* 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);
}


--- End code ---

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.

Navigation

[0] Message Index

[*] Previous page

Go to full version