using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace MUDClientEssentials
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
//this "fancy" user interface actually communicates with servers through this object
private MUDServerConnection serverConnection;
#region startup, establishing a connection
//obligatory constructor
public MainWindow()
{
InitializeComponent();
}
//when the main window loads, prompt the user for connection info
private void Window_Loaded(object sender, RoutedEventArgs e)
{
bool successfulConnection;
do
{
//prompts
string address = TextInputDialog.PromptUser("Server Address", "Input a MUD server address:");
string portString = TextInputDialog.PromptUser("Server Port", "Enter the server's port number:");
//convert port to int, default to port=23 in the event of any parsing issue
int port;
if (!int.TryParse(portString, out port))
port = 23;
//attempt a connection
successfulConnection = true;
try
{
//tell the user what we're doing first
this.appendText("Attempting a connection to " + address + ", port " + port.ToString() + "...");
//then give it a shot
this.serverConnection = new MUDServerConnection(address, port);
}
//if there's any problem, start over with prompts again
catch (Exception)
{
this.appendText("Connection failed. Please verify your internet connectivity and server information, then try again.");
successfulConnection = false;
}
}
while (!successfulConnection);
//now that we've connected, start listening for messages and disconnections
this.serverConnection.serverMessage += new MUDServerConnection.serverMessageEventHandler(serverConnection_serverMessage);
this.serverConnection.disconnected += new MUDServerConnection.disconnectionEventHandler(serverConnection_disconnected);
this.serverConnection.telnetMessage += new MUDServerConnection.serverTelnetEventHandler(serverConnection_telnetMessage);
}
#endregion
#region receiving server text
//when a telnet message arrives, display it in the special telnet output box
void serverConnection_telnetMessage(string message)
{
//add the new message
this.telnetOutputBox.AppendText(message + System.Environment.NewLine);
//scroll down to ensure it's visible
this.telnetOutputBox.ScrollToEnd();
}
//when a content message arrives, display it in the main output box
void serverConnection_serverMessage(List<MUDTextRun> genericRuns)
{
//convert the generic "MUD Text Runs" to "WPF Runs" so that they can be displayed in the UI
List<Run> wpfRuns = new List<Run>();
{
foreach(MUDTextRun genericRun in genericRuns)
{
Run newRun = new Run(genericRun.Content);
newRun.Foreground = new SolidColorBrush(this.getColor(genericRun.ForegroundColor));
newRun.Background = new SolidColorBrush(this.getColor(genericRun.BackgroundColor));
wpfRuns.Add(newRun);
}
}
//display them
this.appendRuns(wpfRuns.ToArray());
}
//associates an actual color with each of the 15 color numbers used by servers
//any modern client should make these user-customizable!
//the color values used in this color theme come from the ANSI control sequence page on wikipedia. they're garish.
private Color getColor(int colorNumber)
{
switch (colorNumber)
{
//colors 0 through 7 are basic colors
case 0:
return Color.FromRgb(0, 0, 0);
case 1:
return Color.FromRgb(128, 0, 0);
case 2:
return Color.FromRgb(0, 128, 0);
case 3:
return Color.FromRgb(128, 128, 0);
case 4:
return Color.FromRgb(0, 0, 128);
case 5:
return Color.FromRgb(128, 0, 128);
case 6:
return Color.FromRgb(0, 128, 128);
case 7:
return Color.FromRgb(192, 192, 192);
//colors 8 through 15 are "intense" versions of the basic colors above
//in this example, 7 is medium gray, and its corresponding "intense" version at 15 is bright white
case 8:
return Color.FromRgb(128, 128, 128);
case 9:
return Color.FromRgb(255, 0, 0);
case 10:
return Color.FromRgb(0, 255, 0);
case 11:
return Color.FromRgb(255, 255, 0);
case 12:
return Color.FromRgb(0, 0, 255);
case 13:
return Color.FromRgb(255, 0, 255);
case 14:
return Color.FromRgb(0, 255, 255);
default: //case 15
return Color.FromRgb(255, 255, 255);
}
}
//displays plain text in the main output window (by turning it into a WPF run first)
private void appendText(string message)
{
//add a line to the output box
Run run = new Run(message);
run.Foreground = new SolidColorBrush(Colors.White);
run.Background = new SolidColorBrush(Colors.CornflowerBlue);
this.appendRuns(run);
}
//displays rich text in the main output window
private void appendRuns(params Run [] runs)
{
//create a new "paragraph" element, vertically separating this bunch of runs from the previous bunch
Paragraph newParagraph = new Paragraph();
//fill it with the provided runs
newParagraph.Inlines.AddRange(runs);
//add it to the document in the output box
this.outputBox.Document.Blocks.Add(newParagraph);
//automatically scroll to the bottom
ScrollViewer descendantScrollViewer = findScrollViewerDescendant(this.outputBox);
if (descendantScrollViewer != null)
descendantScrollViewer.ScrollToEnd();
}
//helper for above, because FlowDocumentScrollViewer doesn't have a convenient ScrollToEnd() method
//if this looks like black magic, that's because it is (this is not a fun area of WPF)
private static ScrollViewer findScrollViewerDescendant(DependencyObject control)
{
if (control is ScrollViewer) return (control as ScrollViewer);
int childCount = VisualTreeHelper.GetChildrenCount(control);
for (int i = 0; i < childCount; i++)
{
ScrollViewer result = findScrollViewerDescendant(VisualTreeHelper.GetChild(control, i));
if (result != null) return result;
}
return null;
}
#endregion
#region sending text to the server
//when user presses ENTER in the input box, send that text to the server
private void inputBox_KeyDown(object sender, KeyEventArgs e)
{
//if the keystroke was ENTER
if (e.Key == Key.Return)
{
//send text
try
{
this.serverConnection.SendText(this.inputBox.Text);
}
catch
{
this.appendText("Failed to send the below command. Your internet service may have been interrupted, or the server might have shut down.\r\n" + this.inputBox.Text);
}
//clear text for next command entry
this.inputBox.Clear();
}
}
#endregion
#region server disconnection
//when the server disconnects, notify the user via the output box
void serverConnection_disconnected()
{
this.appendText("Disconnected.");
}
#endregion
#region exiting the application
//when the main window closes, make sure the connection is closed
private void Window_Closed(object sender, EventArgs e)
{
//make sure connection is closed
this.serverConnection.Disconnect();
//close the app
Application.Current.Shutdown();
}
#endregion
}
}