06 Jun, 2010, Idealiad wrote in the 1st comment:
Votes: 0
This is something of a vague topic, but I'm interested in how people set up their network code such that they could replace, say, Telnet with a HTML5 websocket or a COMET messaging layer depending on the client. Does anyone have some examples I could look at?
06 Jun, 2010, Orrin wrote in the 2nd comment:
Votes: 0
The author is a member here so perhaps they will see this thread, but DecafMUD is open source and AFAIK can use either Flash or websockets so you might want to take a look at it as an example.
06 Jun, 2010, Idealiad wrote in the 3rd comment:
Votes: 0
I was just looking at that actually, but my impression was that Decaf is a client rather than a server. Are there any special considerations when working with a server's rather than a client's network layer?
06 Jun, 2010, quixadhal wrote in the 4th comment:
Votes: 0
The simplest way is to make sure you've abstracted the parts you use into functions/methods that are generic.

For example, if you have a player object, you may have a player.send() method to send output to them. That method shouldn't ever touch sockets, but instead should do something more generic, like add the given string to an output buffer. Another method, probably in your main loop, will look at the output buffers of all player objects and send (possibly only part of them) to players who aren't doing something that prevents output (such as editing).

In that function/method, instead of pulling off the buffer and sending it directly out the socket, instead you may want to call a more generic delivery method. Perhaps you have a collection of "Connection" objects, each of which may be connected over a telnet socket, an ssh socket, or something else weird. So, your Connection class has a send() method, and that's where you might need to put the logic.

Something like:

switch(self.ConnectionType) {
case CON_TELNET:
Telnet.send(self, self.SendBuffer);
break;
case CON_SSH;
SSH.send(self, self.SendBuffer);
break;
default:
fprintf(stderr, "ERROR: Invalid Connection Type! Tried to send '%s'\n", self.SendBuffer);
break;
}


Here, Telnet.send() and SSH.send() would be class methods. You could easily replace all that with plain old functions for C.
06 Jun, 2010, David Haley wrote in the 5th comment:
Votes: 0
Quote
Are there any special considerations when working with a server's rather than a client's network layer?

Receiving data is receiving data, no matter whether it's coming from the server or from a client. As far as you're concerned it's coming from "the other end".

You need to separate the code that deals in bytes (the OS socket layer) from the code that reads those bytes as part of a protocol (e.g., the HTML/telnet/XML parser) from the code that takes that data and does something with it (send it to the command interpreter).

I would do this using objects, myself, and not a switch statement as shown above, however it shows the basic idea.
07 Jun, 2010, quixadhal wrote in the 6th comment:
Votes: 0
I did use objects. Connection object send() method passes the data along to either the SSH object's send() method, or the Telnet object's send() method (or spews an error to the console).

At some point, you have to decide which object to send the data to… unless you only instantiate a subclass that only supports one protocol at a time. I guess you could do that, but I don't see it being any more or less clear than a switch statement.

I should probably say that I don't believe factoring everything into as many objects as you can possibly think of is always such a great idea. YMMV. :)
07 Jun, 2010, David Haley wrote in the 7th comment:
Votes: 0
Using class methods isn't really using object orientation; it's basically just functions in a namespace. I meant that I would have a buffer handler that was specialized to telnet buffer handlers, ssh buffer handlers, HTTP buffer handlers, etc. The case statement as given isn't doing anything other than a simple vtable lookup, so one might as well use the abstraction.
07 Jun, 2010, Idealiad wrote in the 8th comment:
Votes: 0
To the forum member that PM'd, apparently your account isn't accepting PMs for some reason; but to reply, yes I am interested – my email is georgeolivergo at yahoo.
10 Jun, 2010, Barm wrote in the 9th comment:
Votes: 0
I've been toying with protocol handling via Python co-routines, where the character stream is piped from one to another. You'd assemble a protocol chain like tinker toys, An inward one for telnet, temporarily add one to dump IO for troubleshooting – that kind of thing. My goal is to de-couple things enough so you can use the same API to play via the console or from a WebSocket app. My inspiration was this awesome blog post by Eli Bendersky:

http://eli.thegreenplace.net/2009/08/29/...

It's promising, but there's still a few kludgey parts I need to hammer out.


Here's a simple example that converts all the possible variations of Telnet line terminations to Python style '\n':

from netboa.coroutine import coroutine


_CR = '\r' # Carriage Return or ASCII 10
_LF = '\n' # Line Feed (Python newline) or ASCII 13
_NULL = '\x00' # Null Character


@coroutine
def t2p_eol_proto(client, target):
"""
Protocol to convert inward Telnet line terminators to Python-style
newlines.

Normally, Telnet lines are terminated with a CR-LF pair, however
some application may use CR-NULL or just a LF so we need to cope
with all three.
"""
while True:
byte = (yield)
if byte == _CR:
next_byte = (yield)
if next_byte == _LF or next_byte == _NULL:
target.send(_LF)
else:
## weirdness, but not our place to argue
target.send(_CR)
target.send(next_byte)
else:
target.send(byte)
10 Jun, 2010, ProjectMoon wrote in the 10th comment:
Votes: 0
RingMUD uses interfaces to do it. Currently there is only an implementation for Telnet. Later on, there will be an SSHCommunicator, WebSocketCommunicator, RMICommunicator, CrocodileCommunicator, or whatever else I can dream up. (Really, only the first two are likely, though I am considering crocodiles heavily)
19 Aug, 2010, Barm wrote in the 11th comment:
Votes: 0
I'm working on a complete re-write of my single-threaded, asynchronous Python server and I'm at a point when this is the exact topic I need to make some decisions on. The new code will allow you to run multiple services on the same server where each service listens on a different port and can have completely different protocols and handlers. So it would be easy to have Telnet, WebSocket, and InterMUD Chat going all at once.

On connect, the service returns a client object with a fairly simple API; send(), get_char() (returns one character), and get_input() (returns the entire input buffer).

Protocols are processed by coroutines that operate on the send and recv character streams. Protocols can be chained. For example, I have a Telnet IAC protocol that feeds a Telnet to Python line-conversion protocol (as above) that feeds the client's input buffer. This chaining is setup by your choice of client factory called by the service object.

Decoupling the protocol from the client sounds great on paper, but I'm always going to have to contend with state changes and events related to the protocol itself. For example, a telnet client requests that we begin to echo input. We can't handle it automatically, because it might be for a password field where we echo '*' for each character. I think I'm going to have the user provide a on_event() handler and call it with the tuple (client, event, value). So our echo example would be on_event(client, 'telnet_echo', True).

The coroutines can maintain simple states internally – that's what they are great at. For values that need to be shared between the send and receive chains, or nested coroutines, or for lookup by the user, I'll probably add a dedicated protocol dictionary to the client since it gets passed to each protocol at creation.

I am feeling a bit beyond the pale so any input is appreciated.
19 Aug, 2010, David Haley wrote in the 12th comment:
Votes: 0
I think you have a decent approach: do as much internally as you can, but expose callback/event/etc. interfaces to the user in case they care about an internal state change. As you say, sometimes what one layer is doing affects what the layer above/below should be doing, although I don't think that this is often the case.
19 Aug, 2010, Rudha wrote in the 13th comment:
Votes: 0
It might be worth determining what variables are common between these handlers and putting them into a common pool, or perhaps encapsulating it in a class, though that may be something you've done and just not mentioned. Its important both on a design level and in the interests of maintaining proper variable scope - things that are used by everything should be available to everything without needing to duplicate data.

The password example is a little bit of a pickle, however: In telnet we generally squelch echoes altogether when we have a password handler. This is to eliminate the possibility of someone determining the size of the password, and to generally be as secure as we can be.

The codes for that are squelch and unsquelch in telnetlib, incidentally.

I'm not sure exactly what you're asking for input on though. How to handle events? What exactly do you mean when you say 'events'?

Maya/Rudha
19 Aug, 2010, Barm wrote in the 14th comment:
Votes: 0
My previous approach was completely Telnet-centric. Each client was a mini Telnet server that the coder polled for lines of input. The only two events were on_connect() and on_disconnect(). If the player changed his window size (and supported NAWS), client.columns and client.rows were updated automatically.

For this version, the player's terminal app becomes abstract. For example, let's say we're running two services; Telnet and Jabber. I don't think it's good design to have client.rows as a member property as the concept of rows and columns are meaningless to an instant messaging client. I'm thinking out loud (in print) here. I'm inclined to handle it as client.protocol['telnet_rows'] where the onus is on the programmer to handle protocol specific details. The challenge is to provide something consistent without forcing the programmer into a lowest common denominator of features. It might not have any real value beyond being a framework for someone to extend towards specific protocols. I'm trying to picture offering a character based vi-like editor to a Telnet users and … what … to a IM user at the same time?

What kind of API would you like from a server that supports multiple client applications?
19 Aug, 2010, David Haley wrote in the 15th comment:
Votes: 0
I think it would be useful to define clear use cases before trying to design the perfectly decoupled network stack. To some extent, you need to know what device you're rendering to in order to know what data to present in the first place. Just compare websites' normal and mobile versions: the mobile versions are hand-crafted knowing what the target device's characteristics are.

Would you really be sending a full character-based interface to a Telnet user and a "something" to an IM user?

Some kinds of data can be left as more high-level formatting specifications that the end protocol uses or discards. Color codes are a good example of this; you can specify colors as RGB and then do the appropriate translation to ANSI, xterm, HTML, whatever, depending on the end device.

But really, I don't think it's possible to keep things entirely generic and agnostic of the end medium's characteristics; it really matters if you're talking to a "dumb" terminal or a web page, for instance.
19 Aug, 2010, Barm wrote in the 16th comment:
Votes: 0
Quote
Would you really be sending a full character-based interface to a Telnet user and a "something" to an IM user?


I could see rich Telnet and Web connections for content creation with Jabber and InterMUD chat clients for simple message passing. Or maybe a secondary login service for admins that stays up when the public MUD is down. But I agree that trying to provide equivalent game experience to everybody is not practical.

Quote
Some kinds of data can be left as more high-level formatting specifications that the end protocol uses or discards. Color codes are a good example of this; you can specify colors as RGB and then do the appropriate translation to ANSI, xterm, HTML, whatever, depending on the end device.


Wouldn't that be sweet? I'd love to wrap output in something like JSON so that you can click "Would you like to buy sword?" and your client automatically types "buy sword from vendor\n" for you. But now we're talking Flash, WebSockets, or some other custom client.

Quote
But really, I don't think it's possible to keep things entirely generic and agnostic of the end medium's characteristics; it really matters if you're talking to a "dumb" terminal or a web page, for instance.


I totally agree.
19 Aug, 2010, Rudha wrote in the 17th comment:
Votes: 0
Quote
Wouldn't that be sweet? I'd love to wrap output in something like JSON so that you can click "Would you like to buy sword?" and your client automatically types "buy sword from vendor\n" for you. But now we're talking Flash, WebSockets, or some other custom client.


You can do this with MXP, if you wanted to implement something like that. Personally, I find implementing MXP to be not worth the trouble.
19 Aug, 2010, David Haley wrote in the 18th comment:
Votes: 0
MXP is pretty easy to implement, at least in my experience. It makes certain things quite a bit easier, like being able to mstat a mob and change things with a simple click. It happens to be a substandard protocol, but that's a different story…

Quote
I could see rich Telnet and Web connections for content creation with Jabber and InterMUD chat clients for simple message passing. Or maybe a secondary login service for admins that stays up when the public MUD is down. But I agree that trying to provide equivalent game experience to everybody is not practical.

These seem reasonable to me. But how much of the complex features you were talking about are needed for these?
19 Aug, 2010, Runter wrote in the 19th comment:
Votes: 0
The implementation gap between various clients makes using MXP quite difficult.

Only the most basic mxp commands are implemented across the few clientsbin parody.
19 Aug, 2010, Rudha wrote in the 20th comment:
Votes: 0
Runter said:
The implementation gap between various clients makes using MXP quite difficult.


This, and the fact that as David said its something of a substandard protocol, are what get my knickers in a twist, so to speak :P

Maya/Rudha
0.0/21