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