25 Feb, 2009, Quincunxian wrote in the 1st comment:
Votes: 0
I'm a 'reasonably' decent VB.net programmer who has wandered into the murky depths of dealing with connections to MUD servers for the first time.
In my searching of the net on how to actually connect to a server "Idiots guide thereof" it seems that the information at hand either is very specific to a connection issue that someone is having or assumes some basic level of knowledge that I don't have. (some people it seems, are born with Telnet protocals genetically imprinted)
I can set up the sockets but any of the commands that I issue either get the finger from the server or appear out of a black hole on the other side of the known universe.
My specific request is to build ( yet another MUD client ) mainly for myself but will make the code available for any who want it, with a few features that I find lacking in the collection of clients that I have used.
Specifically the Triggers, Aliases and programmable Macros that all seem to have some limitation that I find personally frustrating - the ability for the client to issue stat/score commands on your behalf and then parse the results into a GUI representation - ability to validate current inventory and let you know where things are especially for games that allow you to have multiple containers - I can never remember where I put my sandwiches *sigh* - general things along those lines.
If someone can either tell me or point me in the right direction for information I would be most grateful
I would also package this professionally into a MSI for those who install on the dark side.
25 Feb, 2009, Vassi wrote in the 2nd comment:
Votes: 0
So you have the socket connection part down, it sounds like. Your next step is to deal with the telnet protocol, assuming you want to talk to other people's MUDs.

I'll rustle up some links for you. How's your ability to read C#? I have some Telnet code that you may find useful, but you'll have to translate it to VB.NET yourself, it shouldn't be hard since I mostly used Dictionaries.
26 Feb, 2009, Quincunxian wrote in the 3rd comment:
Votes: 0
Translation from C# is not a problem and have done some of that before - as long as the structure is based around the .net framework you can 'almost' do a cut & paste with some minor clean up.
Even so , I can read the logic most of the times and make the translation - I don't mind doing the hard yards as long as I know which direction the the race is being run.

Thanks greatly for your offer of assistance.

Cheers,
Quin
26 Feb, 2009, Barm wrote in the 4th comment:
Votes: 0
Quote
I can set up the sockets but any of the commands that I issue either get the finger from the server or appear out of a black hole on the other side of the known universe.

You might be sending non-terminated (or C-style zero-terminated strings). The Telnet spec calls for CR + LF to terminate input. See RFC 845 at: http://www.faqs.org/rfcs/rfc854.html . In which case, the server is simply waiting for you to finish your input. Try tacking "\n\r" to each line of text before sending it out the socket.

BTW, this does not apply to Telnet command sequences like "IAC WILL ECHO" or "IAC DO NAWS". I'm taking about just getting player input to the mud server.

Cheers.
26 Feb, 2009, David Haley wrote in the 5th comment:
Votes: 0
Quote
"\n\r"

Actually, it's the other way around. Carriage return = "return carriage to beginning" = \r. Line feed = "feed a new line" = \n.
26 Feb, 2009, elanthis wrote in the 6th comment:
Votes: 0
I used to have a tutorial online for implementing TELNET. Not sure what happened to it.

The gist of it is pretty simple. TELNET is best implemented as a state machine, where you have the following states: TEXT, IAC, DO, DONT, WILL, WONT, SB, SB_IAC.

For each byte you receive you look at the current state and decide what to do, easily implemented as a switch() statement in C#. The state transitions are as follows:

STATE : BYTE -> NEW STATE (action)
TEXT : IAC -> IAC (begin telnet command)
TEXT : anything -> TEXT (regular text input from the remote end)
IAC : IAC -> TEXT (escaped IAC byte)
iAC : DO -> DO (start DO negotiation)
DO : anything -> TEXT (DO option code, process)
IAC : DONT -> DONT (start DONT negotiation)
DONT : anything -> TEXT (DONT option code, process)
IAC : WILL -> WILL (start WILL negotiation)
WILL : anything -> TEXT (WILL option code, process)
IAC : WONT -> WONT (start WONT negotiation)
WONT : anything -> TEXT (WONT option code, process)
IAC : SB -> SB (begin sub-request)
SB : IAC -> SB_IAC (sub-request end/escape sequence)
SB_IAC : IAC -> SB (escaped IAC byte)
SB_IAC : SE -> TEXT (completed subrequest, process data)
SB_IAC : anything -> TEXT (invalid sub-request, abort)
IAC : anything -> TEXT (one of several IAC commands, most are not used in MUDs, safely ignored usually)

At taht point it's just a matter of figurinig out which options you support and responding to them appropriately.

The biggest mistak eI see is that people try to implement TELNET as something other than a state machine, which is wrong. It's not technically mandatory to use a state machine, but implementing it any other way is going to be very difficult to do correctly. Far too many MUDs try to process a whole TELNET commands inside the current socket read buffer in one go, which is clearly wrong. Go for the state machine and it's easy to get right.
26 Feb, 2009, David Haley wrote in the 7th comment:
Votes: 0
A lot of things are best represented as state machines: telnet, parsers for fairly simple languages, player connections to a MUD, … I'm not sure why state machines are so rare; I guess it's because a lot of people just aren't aware of exactly what they are.
26 Feb, 2009, Barm wrote in the 8th comment:
Votes: 0
David Haley said:
Actually, it's the other way around. Carriage return = "return carriage to beginning" = \r. Line feed = "feed a new line" = \n.


Yep, you're right:
>>> ord('\n')
10
>>> ord('\r')
13


010      012    00A   00001010       LF    (Line Feed)
013 015 00D 00001101 CR (Carriage Return)


Make that "\r\n"
26 Feb, 2009, David Haley wrote in the 9th comment:
Votes: 0
The main reason I know that one off the top of my head is that we've had some fairly interminable discussions about proper usage of line endings :tongue:
26 Feb, 2009, Barm wrote in the 10th comment:
Votes: 0
26 Feb, 2009, Barm wrote in the 11th comment:
Votes: 0
David Haley said:
The main reason I know that one off the top of my head is that we've had some fairly interminable discussions about proper usage of line endings :tongue:


Hey, it was a good catch. Apparently, I'm using "\newline" as a really bad mnemonic. :)
27 Feb, 2009, David Haley wrote in the 12th comment:
Votes: 0
I remember it because for some reason CRLF just sounds better than LFCR, and then I remember that CR (\r) means go to the beginning of the line. Maybe this way of thinking isn't helpful for other people, though… :wink:
27 Feb, 2009, quixadhal wrote in the 13th comment:
Votes: 0
Hey! My favorite topic! CRLF *rabblerabblerabble* :)

I'm old enough to remember real typewriters (both IBM Selectric and good old manual typewriters that don't need electricity!). When you hit the big old lever on the left-hand side, it does it the "wrong" way, in that it first ratchets the platen (which feeds the paper), and then pushes the platen all the way right (which positions the keys/ball/etc at the left edge of the paper).

So, just remember, TELNET is *NOT* a typewriter, and you're all set.

EDIT: Oh, and thanks for the state machine layout elanthis! I think it's quite handy to see the transitions set out in one place. Much easier than gouging your eyes out on the RFC specs anyways. :)
27 Feb, 2009, elanthis wrote in the 14th comment:
Votes: 0
Barm said:
I'm pretty sure this is the tutorial Elanthis was referring to:
http://www.advogato.org/person/elanthis/...


So it is. … it's really not that great, is it? I should write a new one.
27 Feb, 2009, Vassi wrote in the 15th comment:
Votes: 0
The C# implementation I have is basically based on the python implementation that elanthis had done, so if you just use what he said you will become strong. I realized after I offered you the code that my current telnet classes are kind of meant to work with the handler system my codebase has so unfortunately it has a lot of dependencies that keep them from being copy&paste useful to you. However, I put the main three files up in the paste-bin as you may find something useful to cannibalize. The biggest 'problem' is that the TelnetProtocol class, which does the actual filtering, assumes that it owns a connection object and a TelnetSession object.

Kind of late for me, so forgive me if I don't make any sense. Here are the links:

http://www.mudbytes.net/pastebin-17727 - This is the base interface, might help you understand which functions are meant to be called externally as entry points.

http://www.mudbytes.net/pastebin-17728 - This is the TelnetOption and the TelnetSession class.
The TelnetOption class simply represents an option, and whether that option is enabled and\or supported. Supported meaning that the server is willing to negotiate it. TelnetSession uses TelnetOption classes to keep tabs on what is enabled or supported where. I've been told it's overkill, but it seems to work fine for what I want. It's overly documented since I tend to get busy for months at a time before I come back to my code.

http://www.mudbytes.net/pastebin-17726 - This is the TelnetProtocol class, which contains the state machine and the filtering (i.e. reject non-printable ASCII input.) I haven't updated it for UTF yet, so the filtering strips out any non-printable ascii characters (but only when in 'normal' mode so they are preserved in IAC or Subnegotations, etc)

I realize I'm just throwing code at you so I apologize, I haven't had time to prepare an actual library yet. Also, there's quite a bit of console output on there that I used for debugging when building it, eventually that will get replaced with conditional logging tracers via log4net.

Edit: Since i'm throwing code at you, here's an ANSI colorizer:
http://www.mudbytes.net/pastebin-17729
27 Feb, 2009, Quincunxian wrote in the 16th comment:
Votes: 0
Greetings and thanks for this discussion and information everyone.
I've progressed more in the last 48 hours from links and info here than in about 2 weeks of googling this to death.
I'm going to go through this bit by bit and with a bit of trial and error (mmmmm make that a bit of trial and lots of errors ) , I will piece all this together. I'll keep an eye on this thread to see if anyone else has something to add but I've got enough to go on with for now.
I'll let you know of my successes ( hopefully ) in the next week or so ( being optimistic ).

again guys - thanks for your assistance !
27 Feb, 2009, Vassi wrote in the 17th comment:
Votes: 0
Bacon, always add bacon.

Other than that, if you get interested in any of the code generation stuff or reflection let me know, I've spent quite a bit of time working on those angles so you might find some of it useful.
04 Feb, 2010, Nick Gammon wrote in the 18th comment:
Votes: 0
elanthis said:
IAC : SB -> SB (begin sub-request)
SB : IAC -> SB_IAC (sub-request end/escape sequence)
SB_IAC : IAC -> SB (escaped IAC byte)
SB_IAC : SE -> TEXT (completed subrequest, process data)
SB_IAC : anything -> TEXT (invalid sub-request, abort)


I am having a lengthy discussion about this on my forum:

http://www.gammon.com.au/forum/?id=10043

In particular I am having trouble with your last state transition: SB_IAC : anything -> TEXT (invalid sub-request, abort)

Given that you are in a subnegotiation, and you have (after the IAC SB <type>) zero or more bytes followed by IAC x, where x is NOT either IAC or SE, what is your justification for saying "(invalid sub-request, abort)"?

As I explain in some length on the above link, you could make out a case for:

  • Processing the subnegotiation as received so far
  • Treating the "lone" IAC as part of the subnegotiation (I know it should be doubled, but it also should not be there *not* followed by an SE)
  • Dropping the IAC and proceeding with the subnegotiation
  • Dropping IAC x and proceeding with the subnegotiation
  • Treating the IAC x as starting a *new* negotiation, and thus terminating the previous one and starting a new one - possibly processing the previous negotiation as valid, and possibly not.


Even if one follows your suggested behaviour of treating IAC x as invalid, and aborting the subnegotiation, do we now process x as part of the new text phase, or do we drop *both* the IAC and the x? And if so, on what grounds?

I'm not trying to be difficult, at least your list is about the only one I have found so far that addresses the problem, but a simple assertion on a forum isn't really a standard, so I would be pleased if you could refer to where this action is justified.
0.0/18