using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Xml.Serialization;
using System.Net.Sockets;
using System.Windows.Threading;
using System.Xml;
using System.Security.Cryptography;
using System.Text.RegularExpressions;
using System.Globalization;
using System.Windows;
using System.Windows.Documents;
namespace MUDClientEssentials
{
public class MUDServerConnection
{
//.NET's TCP Client connection, and a buffer to temporarily hold incoming/outgoing text
private TcpClient connection = new TcpClient();
private byte[] buffer = new byte[2500];
//disconnection callback and handler definition
public event disconnectionEventHandler disconnected;
public delegate void disconnectionEventHandler();
//incoming message callback and handler definition
public event serverMessageEventHandler serverMessage;
public delegate void serverMessageEventHandler(List<MUDTextRun> runs);
//incoming telnet control sequence callback and handler definition
public event serverTelnetEventHandler telnetMessage;
public delegate void serverTelnetEventHandler(string message);
//a parser/decoder for ANSI control sequences, to give text color and potentially other styling
ANSIColorParser ansiColorParser = new ANSIColorParser();
//a parser for Telnet control sequences, which responds to any server messages as required by the Telnet protocol rules
TelnetParser telnetParser;
#region initialization, connection
public MUDServerConnection(string address, int port)
{
//try to connect (may throw exceptions, to be handled by caller)
this.connection.Connect(address, port);
//if successful
if (this.connection.Connected)
{
//initialize the telnet parser
this.telnetParser = new TelnetParser(this.connection);
//start listening for new text
connection.Client.BeginReceive(this.buffer, 0, this.buffer.Length, SocketFlags.None, new AsyncCallback(this.handleServerMessage), null);
//send a WILL NAWS (negotiate about window size)
this.telnetParser.sendTelnetBytes((byte)Telnet.WILL, (byte)Telnet.NAWS);
}
}
#endregion
#region incoming text handler
//called when receiving any message
void handleServerMessage(IAsyncResult result)
{
//get length of data in buffer
int receivedCount;
try
{
receivedCount = connection.Client.EndReceive(result);
}
catch
{
//if there was any issue reading the server text, ignore the message (what else can we do?)
return;
}
//0 bytes received means the server disconnected
if (receivedCount == 0)
{
this.Disconnect();
return;
}
//list of bytes which aren't telnet sequences
//ultimately, this will be the original buffer minus any telnet messages from the server
List<string> telnetMessages;
List<byte> contentBytes = this.telnetParser.HandleAndRemoveTelnetBytes(this.buffer, receivedCount, out telnetMessages);
//report any telnet sequences seen to the caller
App.Current.Dispatcher.BeginInvoke(new Action(delegate
{
foreach (string telnetMessage in telnetMessages)
{
//fire the "received a server message" event
this.telnetMessage(telnetMessage);
}
}));
//now we've filtered-out and responded accordingly to any telnet data.
//next, convert the actual MUD content of the message from ASCII to Unicode
string message = AsciiDecoder.AsciiToUnicode(contentBytes.ToArray(), contentBytes.Count);
//run the following on the main thread so that calling code doesn't have to think about threading
if (this.serverMessage != null)
{
App.Current.Dispatcher.BeginInvoke(new Action(delegate
{
//pass the message to the mudTranslator to parse any ANSI control sequences (colors!)
List<MUDTextRun> runs = this.ansiColorParser.Translate(message);
//fire the "received a server message" event with the runs to be displayed
this.serverMessage(runs);
}));
}
//now that we're done with this message, listen for the next message
connection.Client.BeginReceive(this.buffer, 0, this.buffer.Length, SocketFlags.None, new AsyncCallback(this.handleServerMessage), null);
}
#endregion
#region outgoing text
public void SendText(string text)
{
//if not connected, do nothing
if (!this.connection.Connected) return;
//add carriage return and line feed
text = text + "\r\n";
//convert from Unicode to ASCII
Encoder encoder = System.Text.Encoding.ASCII.GetEncoder();
char[] charArray = text.ToCharArray();
int count = encoder.GetByteCount(charArray, 0, charArray.Length, true);
byte[] outputBuffer = new byte[count];
encoder.GetBytes(charArray, 0, charArray.Length, outputBuffer, 0, true);
//send to server
this.connection.Client.Send(outputBuffer);
}
#endregion
#region disconnect
internal void Disconnect()
{
//if not connected, do nothing
if (!this.connection.Connected) return;
//close the connection
this.connection.Close();
//initialize a new object
this.connection = new TcpClient();
//fire disconnection notification event on main UI thread
if (this.disconnected != null)
{
App.Current.Dispatcher.BeginInvoke(new Action(delegate
{
this.disconnected.Invoke();
}));
}
}
#endregion
}
}