Yar, I do be re-visiting Pirates!

One of the first games I started building in Silverlight I called Pirates! Since working on Perenthia and various other tasks I have not re-visited the game for a long time. I really would like to get this game finished so I have to decided to spend some time working on it. I hope to include some videos soon that show the game in varied stages of development. The first screen shot displays what currently exists after implementing some path finding and the SilverSprite library:

Here is the post from the game blog: Pirates Game in Silverlight

3/10/10


A Silverlight Games Star Field

Here is a really nice large star field in Silverlight if you are creating a space based game, or even if you are not it is still pretty cool. :)

8/10/09


Silverlight and Multi-Player Games

I have been working on a multi-player library for Silverlight games over the past few months, well, off and on working on it while continuing Perenthia development.

I hope to be able to get the code on CodePlex before too much longer along with a tutorial on how to use it and a demo game.

The library will start with Duplex services but I do have plans to implement sockets in the future. Silverlight’s duplex and socket support is sufficient for RPGs and RTS or really any kind of turn based game but until Silverlight can support UDP sockets it is probably not suited to real time action games.

The goal is that the library can be used with any game engine such as SilverSprite or PlayBits and that it will provide easy to setup and use networking capabilities for your game.

The library is coming along well and I am building a demo game to help with implementation and testing that I hope to have finished in the next week or so.

8/9/09


Perenthia World Builder in Silverlight

I have been working to modify the tool I wrote for building the Perenthia world. The tool started life as a WinForms application, migrated to a WPF application but is now a part of the Silverlight Client UI. Since all of the actions are role based and controlled with permission on the server having it in the main client UI allows me to make use of all of the framework already in place for Perenthia.

The world builder UI connects to the server via WCF services tailored for world building. What this will also give me is the ability to open up the world builder to allow users to create custom dungeons, towns, etc.

Some features of the world builder are a background map of the world so I know where to place rooms, zoom capabilities that scale the map, draggable map and the ability to drag items, creatures, NPCs, etc. onto rooms on the map and configure the properties of each.

4/29/09


Silverlight Window Control

Here is a draggable Window control I wrote in Silverlight for Perenthia. A screen shot of the Window being used can be seen at http://cameronalbert.com/post/2008/11/19/Silverlight-Game-Controls.aspx

Here is the XAML from my generics.xaml file:

 

<!-- Default style for Lionsguard.Window -->
	<Style TargetType="lg:Window">
		<Setter Property="Background" Value="#99000000" />
		<Setter Property="Foreground" Value="#FFFFFFFF" />
		<Setter Property="BorderBrush" Value="#FFC38312" />
		<Setter Property="Template">
			<Setter.Value>
				<ControlTemplate TargetType="lg:Window">
					<Grid x:Name="RootElement">
						<Border HorizontalAlignment="Stretch" Margin="0,24,0,0" Padding="4,4,4,4" VerticalAlignment="Stretch" BorderThickness="2,2,2,2" CornerRadius="0,0,4,4">
							<ContentControl x:Name="ContentElement" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch"/>
						</Border>
						<Border Height="24" HorizontalAlignment="Stretch" Margin="0,0,0,0" VerticalAlignment="Top" CornerRadius="4,4,0,0" BorderThickness="2,2,2,0" x:Name="TitleBarElement" />
						<Button Height="20" HorizontalAlignment="Right" Margin="0,3,3,0" VerticalAlignment="Top" Width="20" Content="X" FontWeight="Bold" FontFamily="Trebuchet MS" FontSize="14" x:Name="CloseButtonElement"/>
						<TextBlock Height="20" Margin="8,3,27,0" VerticalAlignment="Top" Text="Title" TextWrapping="Wrap" x:Name="TitleLabelElement" FontFamily="Georgia" FontSize="16" FontWeight="Bold" HorizontalAlignment="Left"/>
					</Grid>
				</ControlTemplate>
			</Setter.Value>
		</Setter>
	</Style>

 

And here is the code for the class:

 

using System;
using System.Collections.Generic;
using System.Net;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Markup;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

namespace Lionsguard
{
	[TemplatePart(Name = "RootElement", Type = typeof(FrameworkElement))]
	[TemplatePart(Name = "ContentElement", Type = typeof(ContentControl))]
	[TemplatePart(Name = "TitleBarElement", Type = typeof(Border))]
	[TemplatePart(Name = "CloseButtonElement", Type = typeof(Button))]
	[TemplatePart(Name = "TitleLabelElement", Type = typeof(TextBlock))]
	[ContentProperty("Content")]
	public class Window : Control
	{
		public FrameworkElement RootElement { get; set; }
		private ContentControl ContentElement { get; set; }
		private Border TitleBarElement { get; set; }
		private Button CloseButtonElement { get; set; }
		private TextBlock TitleLabelElement { get; set; }

		private Point MousePosition { get; set; }

		/// <summary>
		/// Gets or sets the Content of the Window.
		/// </summary>
		public UIElement Content
		{
			get { return (UIElement)GetValue(ContentProperty); }
			set { SetValue(ContentProperty, value); }
		}
		public static readonly DependencyProperty ContentProperty = DependencyProperty.Register("Content", typeof(UIElement), typeof(Window), new PropertyMetadata(null, new PropertyChangedCallback(Window.OnContentPropertyChanged)));
		private static void OnContentPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
		{
			(obj as Window).SetControlValues();
		}

		/// <summary>
		/// Gets or sets the Title displayed in the title bar of the window.
		/// </summary>
		public string Title
		{
			get { return (string)GetValue(TitleProperty); }
			set { SetValue(TitleProperty, value); }
		}
		public static readonly DependencyProperty TitleProperty = DependencyProperty.Register("Title", typeof(string), typeof(Window), new PropertyMetadata("Title", new PropertyChangedCallback(Window.OnTitlePropertyChanged)));
		private static void OnTitlePropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
		{
			(obj as Window).SetControlValues();
		}

		/// <summary>
		/// Gets or sets a value indicating whether or not the close button of the title bar should be displayed.
		/// </summary>
		public bool ShowCloseButton
		{
			get { return (bool)GetValue(ShowCloseButtonProperty); }
			set { SetValue(ShowCloseButtonProperty, value); }	
		}
		public static readonly DependencyProperty ShowCloseButtonProperty = DependencyProperty.Register("ShowCloseButton", typeof(bool), typeof(Window), new PropertyMetadata(true, new PropertyChangedCallback(Window.OnShowCloseButtonPropertyChanged)));
		private static void OnShowCloseButtonPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
		{
			(obj as Window).SetControlValues();
		}

		/// <summary>
		/// Gets a value indicating whether or not the window is currently open and visible.
		/// </summary>
		public bool IsOpen
		{
			get { return this.Visibility == Visibility.Visible; }
		}

		/// <summary>
		/// An event that is raised when the window is closed.
		/// </summary>
		public event EventHandler Closed = delegate { };

		/// <summary>
		/// Initializes a new instance of the Lionsguard.Window class.
		/// </summary>
		public Window()
		{
			this.DefaultStyleKey = typeof(Window);
		}

		public override void OnApplyTemplate()
		{
			base.OnApplyTemplate();

			this.RootElement = base.GetTemplateChild("RootElement") as FrameworkElement;
			this.ContentElement = base.GetTemplateChild("ContentElement") as ContentControl;
			this.TitleBarElement = base.GetTemplateChild("TitleBarElement") as Border;
			this.CloseButtonElement = base.GetTemplateChild("CloseButtonElement") as Button;
			this.TitleLabelElement = base.GetTemplateChild("TitleLabelElement") as TextBlock;
			
			if (this.RootElement != null)
			{
				this.RootElement.MouseLeftButtonDown += new MouseButtonEventHandler(OnRootElementMouseLeftButtonDown);

				this.TitleBarElement.MouseLeftButtonDown += new MouseButtonEventHandler(OnTitleBarMouseLeftButtonDown);
				this.TitleBarElement.MouseEnter += new MouseEventHandler(OnTitleBarMouseEnter);
				this.TitleBarElement.MouseLeave += new MouseEventHandler(OnTitleBarMouseLeave);

				this.TitleLabelElement.MouseLeftButtonDown += new MouseButtonEventHandler(OnTitleBarMouseLeftButtonDown);
				this.TitleLabelElement.MouseEnter += new MouseEventHandler(OnTitleBarMouseEnter);
				this.TitleLabelElement.MouseLeave += new MouseEventHandler(OnTitleBarMouseLeave);

				this.CloseButtonElement.Click += new RoutedEventHandler(OnCloseButtonClick);

				this.SetControlValues();
			}
		}

		private void SetControlValues()
		{
			if (this.ContentElement != null && this.Content != null)
			{
				this.ContentElement.Content = this.Content;
			}
			if (this.TitleLabelElement != null)
			{
				this.TitleLabelElement.Text = this.Title;
				ToolTipService.SetToolTip(this.TitleLabelElement, this.Title);
			}
			if (this.CloseButtonElement != null)
			{
				if (this.ShowCloseButton) this.CloseButtonElement.Visibility = Visibility.Visible;
				else this.CloseButtonElement.Visibility = Visibility.Collapsed;
			}
		}

		private void OnRootElementMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
		{
			// Set the Z-Index of the window to the top most position.
			this.BringToFront();
		}

		private void OnCloseButtonClick(object sender, RoutedEventArgs e)
		{
			this.Close();
		}

		private void OnTitleBarMouseLeave(object sender, MouseEventArgs e)
		{
			this.Cursor = Cursors.Arrow;
		}

		private void OnTitleBarMouseEnter(object sender, MouseEventArgs e)
		{
			this.Cursor = Cursors.Hand;
		}

		private void OnTitleBarMouseMove(object sender, MouseEventArgs e)
		{
			Point position = e.GetPosition(null);

			// Prevent the mouse from moving outside the bounds of the parent canvas.
			if (position.X <= 30) position.X = 30;
			if (position.Y <= 30) position.Y = 30;

			Canvas parent = this.Parent as Canvas;
			if (parent != null)
			{
				if (position.X >= (parent.Width - 30)) position.X = parent.Width - 30;
				if (position.Y >= (parent.Height - 30)) position.Y = parent.Height - 30;
			}

			System.Diagnostics.Debug.WriteLine("position = {0}", position);

			double deltaX = position.X - this.MousePosition.X;
			double deltaY = position.Y - this.MousePosition.Y;
			
			Point newPosition = new Point(
				((double)this.GetValue(Canvas.LeftProperty)) + deltaX,
				((double)this.GetValue(Canvas.TopProperty)) + deltaY);

			this.SetValue(Canvas.LeftProperty, newPosition.X);
			this.SetValue(Canvas.TopProperty, newPosition.Y);

			this.MousePosition = position;
		}

		private void OnTitleBarMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
		{
			((UIElement)sender).ReleaseMouseCapture();

			this.Cursor = Cursors.Arrow;

			this.TitleBarElement.MouseLeftButtonUp -= new MouseButtonEventHandler(OnTitleBarMouseLeftButtonUp);
			this.TitleBarElement.MouseMove -= new MouseEventHandler(OnTitleBarMouseMove);
			this.TitleLabelElement.MouseLeftButtonUp -= new MouseButtonEventHandler(OnTitleBarMouseLeftButtonUp);
			this.TitleLabelElement.MouseMove -= new MouseEventHandler(OnTitleBarMouseMove);
		}

		private void OnTitleBarMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
		{
			this.MousePosition = e.GetPosition(null);
			this.Cursor = Cursors.Hand;

			((UIElement)sender).CaptureMouse();

			this.TitleBarElement.MouseLeftButtonUp += new MouseButtonEventHandler(OnTitleBarMouseLeftButtonUp);
			this.TitleBarElement.MouseMove += new MouseEventHandler(OnTitleBarMouseMove);
			this.TitleLabelElement.MouseLeftButtonUp += new MouseButtonEventHandler(OnTitleBarMouseLeftButtonUp);
			this.TitleLabelElement.MouseMove += new MouseEventHandler(OnTitleBarMouseMove);
		}

		/// <summary>
		/// Causes the current window to be re-z-indexed to the top most window.
		/// </summary>
		protected void BringToFront()
		{
			// Search the top most "Window" and swap z-indexes.
			Panel parent = this.Parent as Panel; // Panel is the base for Canvas, Grid and StackPanel.
			if (parent != null)
			{
				int currentZIndex = (int)this.GetValue(Canvas.ZIndexProperty);
				var child = (from c in parent.Children where c is Window select c as Window).OrderByDescending(c => (int)c.GetValue(Canvas.ZIndexProperty)).FirstOrDefault();
				if (child != null)
				{
					int topZIndex = (int)child.GetValue(Canvas.ZIndexProperty);
					if (topZIndex == 0) topZIndex = 1; // If the value has not been set then just default it to 1.
					if (topZIndex > currentZIndex)
					{
						this.SetValue(Canvas.ZIndexProperty, topZIndex);
						child.SetValue(Canvas.ZIndexProperty, currentZIndex);
					}
				}
			}
		}

		public void Show()
		{
			this.Visibility = Visibility.Visible;
			// If content is present show that as well.
			if (this.Content != null) this.Content.Visibility = Visibility.Visible;
		}

		public void Close()
		{
			this.Visibility = Visibility.Collapsed;
			// If content is present hide that as well.
			if (this.Content != null) this.Content.Visibility = Visibility.Collapsed;
			this.Closed(this, EventArgs.Empty);
		}

		public void ToggleWindow()
		{
			if (this.Visibility == Visibility.Visible)
			{
				this.Close();
			}
			else
			{
				this.Show();
			}
		}
	}
}

 

This is how I am using it within the Pernethia UI (The content of window is actually the Character Sheet as displayed in the screenshot from above):

Namespace: xmlns:Lionsguard="clr-namespace:Lionsguard;assembly=Lionsguard.Silverlight"

 

<Lionsguard:Window Height="375" x:Name="diagCharacterSheet" Visibility="Collapsed" Width="634" Canvas.Left="8" Canvas.Top="19" Title="Character Sheet" Style="{StaticResource WindowStyle}" d:IsLocked="True">
    		<Perenthia_Dialogs:CharacterSheetDialog x:Name="diagCharacterSheetContent" SkillChanged="diagCharacterSheetContent_SkillChanged"/>
		</Lionsguard:Window>

 

Enjoy!

 

4/22/09


Silverlight Drag and Drop Manager

I wrote some classes for handling drag and drop operations in Silverlight that I use within Perenthia. I thought I would go ahead and share them since I found them to be very useful in getting drag and drag working quickly in new projects.

The Drag and Drag classes consist of two interfaces (IDroppable, IDropContainer) and a manager class (DragDropManager). The interfaces are defined as such:

namespace Lionsguard
{
    /// <summary>
    /// Defines an object that can be dragged and dropped via the DragDropManager.
    /// </summary>
	public interface IDroppable
	{
		/// <summary>
		/// An event that is raised when the IDroppable item begins the drag operation.
		/// </summary>
		event BeginDragEventHandler BeginDrag;

		/// <summary>
		/// Gets the UIElement that will server as the cursor replacement when dragging the current object.
		/// </summary>
		/// <returns></returns>
		UIElement GetDragCursor();
	}

    /// <summary>
    /// Defines an object that accept IDroppable items as part of a drop operation.
    /// </summary>
	public interface IDropContainer
	{
		/// <summary>
		/// Handles the drop of the specified IDroppable item onto the container.
		/// </summary>
		/// <param name="item"></param>
		void Drop(IDroppable item);
	}
}

The DragDropManager (and and associated delegate and event args class) are defined as such:

namespace Lionsguard
{
    /// <summary>
    /// Represents a class used to manage drag and drop items.
    /// </summary>
	public class DragDropManager
	{
		private Panel Host { get; set; }
		private Point MousePosition { get; set; }
		private UIElement DragCursor { get; set; }	
		private bool HasProcessedEndDrag { get; set; }
		private IDroppable Item { get; set; }	

        /// <summary>
        /// Initializes a new instance of the DragDropManager class.
        /// </summary>
        /// <param name="host">The hosting panel where the drag cursor instance will be added.</param>
		public DragDropManager(Panel host)
		{
			this.Host = host;
			this.Host.LostFocus += new RoutedEventHandler(OnHostLostFocus);
		}

		private void OnHostLostFocus(object sender, RoutedEventArgs e)
		{
			EndDrag(false);
		}

		private void OnDragCursorLostMouseCapture(object sender, MouseEventArgs e)
		{
			EndDrag(false);
		}

		private void OnDragCursorLostFocus(object sender, RoutedEventArgs e)
		{
			EndDrag(false);
		}

		private void OnDragCursorMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
		{
			((UIElement)sender).ReleaseMouseCapture();

			this.MousePosition = e.GetPosition(null);

			this.EndDrag(true);
		}

		private void OnDragCursorMouseMove(object sender, MouseEventArgs e)
		{
			Point position = e.GetPosition(null);

			double deltaX = position.X - this.MousePosition.X;
			double deltaY = position.Y - this.MousePosition.Y;

			Point newPosition = new Point(
				((double)this.DragCursor.GetValue(Canvas.LeftProperty)) + deltaX,
				((double)this.DragCursor.GetValue(Canvas.TopProperty)) + deltaY);

			this.DragCursor.SetValue(Canvas.LeftProperty, newPosition.X);
			this.DragCursor.SetValue(Canvas.TopProperty, newPosition.Y);

			this.MousePosition = position;
		}

        /// <summary>
        /// Begins a drag operation for the specified IDroppable instance starting at the specified mousePosition.
        /// </summary>
        /// <param name="droppable">The IDroppable instance to being dragging.</param>
        /// <param name="mousePosition">The Point at which the drag operation will begin.</param>
		public void BeginDrag(IDroppable droppable, Point mousePosition)
		{
			if (droppable != null && this.Host != null)
			{
				System.Diagnostics.Debug.WriteLine("Begin Drag...");
				this.Item = droppable;

				this.DragCursor = droppable.GetDragCursor();
				this.DragCursor.LostFocus += new RoutedEventHandler(OnDragCursorLostFocus);
				this.DragCursor.LostMouseCapture += new MouseEventHandler(OnDragCursorLostMouseCapture);

				// Add the drag cursor to the host control and set the z-index very high.
				this.DragCursor.SetValue(Canvas.ZIndexProperty, 5000);
				this.Host.Children.Add(this.DragCursor);
				this.Host.Cursor = Cursors.Hand;

				this.MousePosition = mousePosition;
				this.DragCursor.SetValue(Canvas.LeftProperty, this.MousePosition.X);
				this.DragCursor.SetValue(Canvas.TopProperty, this.MousePosition.Y);
				this.DragCursor.Visibility = Visibility.Visible;

				this.HasProcessedEndDrag = false;

				this.DragCursor.CaptureMouse();

				this.DragCursor.MouseLeftButtonUp += new MouseButtonEventHandler(OnDragCursorMouseLeftButtonUp);
				this.DragCursor.MouseMove += new MouseEventHandler(OnDragCursorMouseMove);
			}
		}

		private void EndDrag(bool processDrop)
		{
			// This prevents the mouse up event or the end drag event firing more than once from causing the
			// events to bubble up and the drop be handled more than once.
			System.Diagnostics.Debug.WriteLine("End Drag...");
			if (this.HasProcessedEndDrag) return;
			this.HasProcessedEndDrag = true;

			// Reshow the cursor and hide the source image.
			this.Host.Cursor = Cursors.Arrow;

			// Hide the drag cursor.
			if (this.DragCursor != null)
			{
				this.DragCursor.Visibility = Visibility.Collapsed;

                this.DragCursor.LostFocus -= new RoutedEventHandler(OnDragCursorLostFocus);
                this.DragCursor.LostMouseCapture -= new MouseEventHandler(OnDragCursorLostMouseCapture);
				this.DragCursor.MouseLeftButtonUp -= new MouseButtonEventHandler(OnDragCursorMouseLeftButtonUp);
				this.DragCursor.MouseMove -= new MouseEventHandler(OnDragCursorMouseMove);
			}

			if (processDrop)
			{
				System.Diagnostics.Debug.WriteLine("Process Drop...");
				// Search within host for an IDropContainer instance within the area of the current mouse position.
				var container = (from v in VisualTreeHelper.FindElementsInHostCoordinates(this.MousePosition, this.Host)
								where (v as IDropContainer) != null
								select v as IDropContainer).FirstOrDefault();
				if (container != null)
				{
					System.Diagnostics.Debug.WriteLine("Drop...");
					container.Drop(this.Item);
				}
			}
		}
	}

    /// <summary>
    /// Provides a delegate for handling IDroppable.BeginDrag events.
    /// </summary>
    /// <param name="sender">The IDroppable initiating the drag operation.</param>
    /// <param name="e">Event data for the operation.</param>
	public delegate void BeginDragEventHandler(object sender, BeginDragEventArgs e);

    /// <summary>
    /// Represents event data for a BeginDrag event.
    /// </summary>
	public class BeginDragEventArgs : EventArgs
	{
        /// <summary>
        /// Gets or sets the IDroppable initiating the event.
        /// </summary>
		public IDroppable Droppable { get; set; }

        /// <summary>
        /// Gets or sets the Point at which the drag operation should begin.
        /// </summary>
		public Point MousePosition { get; set; }	
	}
}
To use the manager is your project have the controls wyou wish to drag and drop implement the IDroppable interface and the destinations for the drop operation should implement the IDropContainer. One control could implement both if you have operations where items can be dragged into other items of the same type.
Here is an example of the IDroppable and IDropContainer implementation (Both of which are in the same user control).
The GetDragCursor method creates the item so it looks like you are dragging it across the screen, you can have any kind of cursor replacement UIElement you want with this.

namespace Perenthia.Controls
{
public partial class AvatarListItem : UserControl, IDroppable, IDropContainer
{

#region IDroppable Members

public event BeginDragEventHandler BeginDrag = delegate { };

public UIElement GetDragCursor()
{
Image img = new Image();
img.Source = this.GetImageSource();
img.Width = 16;
img.Height = 16;
Border border = new Border();
border.Background = Brushes.DialogFillBrush;
border.BorderBrush = Brushes.BorderBrush;
border.Padding = new Thickness(2);
border.Width = 18;
border.Height = 18;
border.Child = img;
return border;
}

#endregion

private void NameLabel_MouseLeave(object sender, MouseEventArgs e)
{
if (_details != null)
{
_details.Hide();
}
PopupManager.Remove();
}

#region IDropContainer Members

public void Drop(IDroppable item)
{
// Do stuff when the object is dropped...
}

#endregion
}
}

And here is an example of the usage of the drag and drop manager (Contained within a silverlight Page class):

I declare an instance of the DragDropManager when my control is loaded (I use a Canvas for LayoutRoot as I find the drag operation to be smoother with a Canvas).
private void AdminScreen_Loaded(object sender, RoutedEventArgs e)
{
_dragDropManager = new DragDropManager(LayoutRoot);
}

When my IDroppable control instance is created I register a listener to the BeginDrag event:
avatarItem.BeginDrag += new BeginDragEventHandler(OnDroppableBeginDrag);

When the IDroppable raises the BeginDrag event, on a left mouse button click, this method is called which causes the DragDropManager to being the drag operation for the current IDroppable.

private void OnDroppableBeginDrag(object sender, BeginDragEventArgs e)
		{
			if (_dragDropManager != null)
			{
				_dragDropManager.BeginDrag(e.Droppable, e.MousePosition);
			}
		}

When an IDroppable is dropped onto the very same control this method is executed:

#region IDropContainer Members
		public void Drop(IDroppable item)
		{
                    // Do stuff when the object is dropped...
		}
		#endregion

This is pretty much all there is to it.

 

4/21/09


Working on a Silverlight Multi-Player Library

I have been porting out the multi-player aspects of Perenthia into a separate library I hope to get up on CodePlex before too long. What the library will provide is some interfaces and classes related to handling multi-player environments. A central static class will manage connected clients and three protocol classes for HTTP, Polling Duplex and Sockets will use the static class to retrieve connected clients and push information down to them. A simple set of "tags" are used to send information back and forth and some providers allow for defining your own implementation of handling commands and encrypting data.

The library will be focused on providing an easy way to get setup and implement a multi-player structure. You will still need our own game engine and associated components.

As soon as I get it cleaned up a little and tested out I will put it up on CodePlex and provide a tutorial and sample application showing how to implement and use the component.

3/12/09


Silverlight Dependency Property Snippet

I created this snippet for Dependency Properties in Silverlight. If you are creating custom Silverlight controls you might find this snippet useful. I stored my code snippets here "My DocumentsVisual Studio 2008Code SnippetsVisual C#My Code Snippets" so all you need to do is copy the file to this location. Visual Studio should load the snippet the next time you start it up. If it does not then you can go into "Tools", "Code Snippets Manager" and load it up manually.

Now when you type "propsl" and hit tab you will get a stubbed out property like below, where tabbing through the items allows you to change the type and name of the property, set the owning class and provide a default value.

public int MyProperty
{
	get { return (int)GetValue(MyPropertyProperty); }
	set { SetValue(MyPropertyProperty, value); }
}
public static readonly DependencyProperty MyPropertyProperty = 
     DependencyProperty.Register("MyProperty", typeof(int), typeof(MyClass), 
     new PropertyMetadata(null, new PropertyChangedCallback(MyClass.OnMyPropertyPropertyChanged)));
private static void OnMyPropertyPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{

Here is the code for the snippet and a link to the file:

<?xml version="1.0" encoding="utf-8" ?>
<CodeSnippets  xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
    <CodeSnippet Format="1.0.0">
        <Header>
            <Title>propsl</Title>
            <Shortcut>propsl</Shortcut>
            <Description>Code snippet for an automatically implemented dependency property in Silverlight.</Description>
            <Author>Cameron Albert</Author>
            <SnippetTypes>
                <SnippetType>Expansion</SnippetType>
            </SnippetTypes>
        </Header>
        <Snippet>
            <Declarations>
                <Literal>
                    <ID>type</ID>
                    <ToolTip>Property type</ToolTip>
                    <Default>int</Default>
                </Literal>
                <Literal>
                    <ID>property</ID>
                    <ToolTip>Property name</ToolTip>
                    <Default>MyProperty</Default>
                </Literal>
                <Literal>
                    <ID>ownerClass</ID>
                    <ToolTip>Owner Class</ToolTip>
                    <Default>MyClass</Default>
                </Literal>
                <Literal>
                    <ID>defaultValue</ID>
                    <ToolTip>Default Value</ToolTip>
                    <Default>null</Default>
                </Literal>
            </Declarations>
            <Code Language="csharp">
                <![CDATA[public $type$ $property$
            {
                get { return ($type$)GetValue($property$Property); }
                set { SetValue($property$Property, value); }
            }
            public static readonly DependencyProperty $property$Property = DependencyProperty.Register("$property$", typeof($type$), typeof($ownerClass$), new PropertyMetadata($defaultValue$, new PropertyChangedCallback($ownerClass$.On$property$PropertyChanged)));
            private static void On$property$PropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
            {
            }
            $end$]]>
            </Code>
        </Snippet>
    </CodeSnippet>
</CodeSnippets>

Silverlight Dependency Property Snippet

 

3/9/09


Perenthia Fan Site

Perenthia has a fan site, it is called Beyond Angarath, Angarath being a primary city in within the game world. This is exciting for me and extremely cool! It is very rewarding to see people enjoying your creation enough to put work into a fan site for that creation. The forums have been getting more active lately with folks trying out the Alpha and giving lots of feedback and suggestions. I hope stay close with the community in building this game; I have a vision for it but ultimately I want people to play it and enjoy playing it.

2/19/09


Silverlight for Multi-Player Games

I am hoping to start writing some articles about building multi-player games in Silverlight. I am planning on charting my development progress and ideas in my design and development of Perenthia.

Silverlight is a great platform for .NET developers to be able to write multi-player browser based games. You get a great tool for building a UI, animations, sounds, etc. and can program everything in your .NET language of choice. Silverlight 3 is supposed to support 3D so that will open it up even more for some great games.

Over the next few months, in between working on Perenthia, I am going to work on the first of a series of articles outlining how I built Perenthia. I will start with the Server component, outlining the core communication architecture and design decisions.

Keep checking back for more information.

2/16/09


Perenthia Armorial Version 1.0

I uploaded the Perenthia Armorial Version 1.0 to the Perenthia Alpha site tonight. The Perenthia Armorial is a way to provide external access to Perenthia information. The first version includes the Player Bar feature, where you pass a player name to the service and renders back HTML with player details.

The call to http://alpha.perenthia.com/Armorial/Armorial.ashx?n=Aldarian renders the following:

1/18/09


Perenthia Alpha Testing

I did a Character Creation, Movement and Chat test last night with the Perenthia Alpha players. The tests went well, I chatted with a few players, found some bugs and was very encouraged by the whole thing. The next test should start next weekend and will include some combat/spell tests. After that we will do some questing tests, travel tests (boats and stuff) and then I will open up more of the world for the alpha players to help resolve other issues not covered by the main feature tests.

I am enjoying Silverlight as the client UI, it provides a Windows like interface with ability to be run in a browser.

1/16/09


Client/Server Communication Data Formats

While developing Perenthia I tried several different communication formats from sending JSON serialized objects back and forth to sending byte arrays containing mostly integers indicating what should be loaded and displayed, what commands to execute etc. What I eventually settled on was a bit of a hybrid. I've created a simple set of "tags" that can be sent to and from the server. The tags are nothing more than pipe delimited strings contained within curly braces and there are a limited number of tags that provide simplicity yet flexibility with the data that is transmitted. The tags are represented in C# by tag objects allow me to create them, query them, etc.

A simple command tag for the SAY command might look like this: {CMD|SAY|"Hello World!"}

I wrote a custom tag writer class that parses the tag objects into strings to be sent to the client and likewise a tag reader that reads the strings sent from the client and parses them into tag objects.

The client can only send commands to the server but the server sends commands, messages and objects to the client. The commands are all the same, the CMD text then the one word name of the command and then a list of arguments, messages are system, chat and tell messages but objects have a bit more information. For instance, an object tag encompasses several different types starting with a base ACTOR, a PLACE, a PLAYER and a PROP or property. The ACTOR, PLACE and PLAYER tags all define the ID, Name and Descriptions of the objects, with some additional data per type but the PROP tag defines the object id of its owner and a name/value pair. An example of a PLAYER tag with properties for x, y and z coordinates might be:

{OBJ|PLAYER|3756|"Aldarian"|"My character description..."}

{OBJ|PROP|3756|"X"|45}

{OBJ|PROP|3756|"Y"|26}

{OBJ|PROP|3756|"Z"|0}

The client can find and parse the player tag and then find the properties associated with that player instance. The way the server works now it will send the full player tag once logged in and a character is selected and then from then on out it just sends property updates.

Using this type of tag structure allows quite a bit of flexibility for the client. I could choose to write a very simple client that only displays MSG tags, much like a MUD, I could write a simple 2D interface client and display and move sprites about the map using the properties from the server, etc. Once Perenthia is up and running I will probably post some information on the tags and how to talk with the server should someone feel the need to write their own client. :)

1/5/09


Almost Alpha Time!

For those waiting, Perenthia is almost ready for alpha. I have a few more things to complete and then the first phase of the Alpha will be ready. Hopefully you have had time to look over the various races, attributes and skills on the Perenthia web site and will be ready to create a character and begin your journey.

Here is a list of the remaining tasks I need to complete before Alpha, this does not include testing and fixing bugs with these features, just implementing them:

  • Spellcasting (Basic framework is in place, just need to define some spells and test them out)
  • Monster drops, including currency, gold, silver, etc. (They don't drop anything right now, stingy)
  • Help (Just need the framework in place as the help will get compiled during the alpha)
  • Quests (May not finish these for the first phase of alpha, want to try and keep it simple to start)
  • Some UI cleanup (got some windows not responding to updates from the server)
  • Content (Need to finish adding monsters to the dungeon, just the one dungeon for alpha)

I thought I would include a couple of screen shots along with this update. The first is a shot of the map window of the world builder tool I wrote for managing the places and things within the Perenthia world. This is the shot of the Alpha testing area, a small village called "Delcor". This view is actually a windows forms application with a WPF (ElementHost) control for rendering the map. The little red dots are NPCs that I can actually drag around into other rooms on the map after I create them, makes for moving the monsters in the dungeon a lot easier. :)

This second shot is the game user interface showing the same map that I was working on in the screen shot above.


12/24/08


Evolution of a Game UI

As I have been working on the Silverlight UI for Perenthia I find myself constantly adding little things here there to provide more information or easier click-to-action type of functionality. The UI has evolved, for the better I hope, over the past few months from my original design as I have discovered something else that needed to be displayed while hooking it into the server. This seems to be the natural progression of things, at least with the way I work. :)

Here is the latest UI screen shot, this one shows my character viewing the items a merchant has for sale, with my inventory items on the left and the ones the merchant offers on the right. I will do a development update again soon, had to backtrack a bit with the UI.

11/29/08


Silverlight Game Controls

While building the Silverlight UI for Perenthia I have been building some re-usable game controls that I plan on posting before too long. One of the controls I created is a Window control that is just a control that provides a title bar with a close button and provides a Content section to add custom controls. I needed it to provide menu windows or panels but it could be used outside of games for just about anything. The control is draggable, as long as the parent control is a Canvas and will position itself as the top window when clicked, provided other windows exist in the same parent.

The Window control is featured in the screen shot below, the "Spellbook", "Character Sheet" and "Adenturer's"** are the windows. They contain custom controls that determine the content of the windows. The small icons at the very bottom of the screen shot are Slot controls that allow for a background image, primary image and object Item to be set on them. They also respond to clicks so click events can be handled on them. The ChatPanel under the windows is also a re-usable control and allows for adding any type of UIElement as content for the main chat area, not just text.

I hope to get these controls commented and the source up soon.

** The "Adventurer's" window is actually "Adventurer's Backpack", have a font sizing issue :)

11/19/08


Silverlight 2 Release

Silverlight 2 has officially been released. I've upgraded both Joust and Cameron's Dungeon as well as the Perenthia client. Work on Perenthia continues, albeit a little slow but it does continue. :)

10/14/08


Joust : A Silverlight 2 Game - Source Code

I didn't get a chance to work on Joust any more before the contest deadline and won't have enough time in the next hour and half. Anyway, as promised here is the source code:

Joust Source Code

9/8/08


Joust : A Silverlight 2 Game

I was working on a simple sports game for the Team Zone Sports Silverlight 2 Game Contest. The rules specified a sports theme but did not specify a century or the type of sports so I created a jousting game. I haven't completely finished the game, it doesn't have everything I wanted it to have and the horse kind of drives like a car :) The dust trail behind the horse actually adds to the car effect!

The game is Silverlight 2, C# and makes use of the Farseer Physics engine.

Anyway, I am hoping to get it cleaned up a little more before the contest submission deadline but not sure that is going to happen given my current workload.

I will post the source once the submission deadline is over, I want to try and get it completely finished by then but if not I will post the source anyway.

Play Joust

9/6/08


Scripting for Games

Scripting in games is not a new concept, most games do this on some level. Scripting allows game objects to execute code that is not compiled as part of the object. In the realm of roe playing games scripting gives objects the ability to react to the world around them. For instance, an object could execute script whenever a player gets near it, allowing a vender to hawk their wares, creatures to attack, etc. There are a variety of scripting languages currently being implement in games such as Lua and Python. Recently I have been researching these two for possible addition to Perenthia to provide me the ability to add custom behavior to objects and NPCs.

Edit: Thanks to some helpful insight and information from Michael Foord I was able to get an IronPython sample working. The sample adds scripting support to an object; the code is very basic but accomplishes the task. Thanks Michael!

Here is a snippet of a console app using the latest IronPython Beta4:

using System;using System.Collections;using System.Collections.Generic;using System.IO;using System.Reflection;using System.Scripting;using IronPython;using IronPython.Hosting;using IronPython.Runtime;using IronPython.Runtime.Exceptions;using Microsoft.Scripting;using Microsoft.Scripting.Hosting;namespace IronPythonGameScripting{    class Program    {        static void Main(string[] args)        {            try            {                Actor player = new Actor("Cam");                Actor mob = new Actor("Mob");                mob.Script = @"class Mob:    def onEncounter(self, *value):        value[0].Name = ""Mr Groovy""";                Console.WriteLine("Player name was '{0}'", player.Name);                mob.Execute(Actor.EVENT_ON_ENCOUNTER, player);                Console.WriteLine("Player name is now '{0}'", player.Name);                mob.Execute("Test", player);            }            catch (Exception ex)            {                Console.WriteLine(ex.ToString());            }            finally            {                Console.WriteLine("Press any key to exit...");                Console.ReadKey();            }        }    }    public static class Engine    {        private static ScriptEngine _engine;        private static ScriptScope _scope;        static Engine()        {            _engine = PythonEngine.CurrentEngine;            _scope = _engine.CreateScope();        }        public static void Initialize()        {        }        public static void Execute(string script)        {            ScriptSource source = _engine.CreateScriptSourceFromString(script, SourceCodeKind.Statements);            source.Execute(_scope);        }        public static void Execute(string script, string className, string methodName, params object[] args)        {            ObjectOperations ops = _engine.Operations;            ScriptSource source = _engine.CreateScriptSourceFromString(script, SourceCodeKind.Statements);            source.Execute(_scope);            if (_scope.ContainsVariable(className))            {                object @class = _scope.GetVariable(className);                object instance = ops.Call(@class);                if (instance != null)                {                    if (ops.ContainsMember(instance, methodName))                    {                        object method = ops.GetMember(instance, methodName);                        ops.Call(method, args);                    }                }            }        }    }    public class Actor    {        public const string EVENT_ON_ENCOUNTER = "onEncounter";        public string Name { get; set; }        public string Script { get; set; }        public Actor(string name)        {            this.Name = name;        }        public void Execute(string methodName, params object[] args)        {            Engine.Execute(this.Script, "Mob", methodName, args);        }    }}

 

As it turns out I was attempting to use the IronPython libraries compiled against the Silverlight framework rather than the libraries compiled against the normal .NET framework. I was also using an older beta 1 version of the IronPython library which may have attributed to the exceptions I was seeing. Got to make sure you reference the correct assemblies when working in both .NET and Silverlight. Surprised this is the first time I've done this...

I reviewed IronPython because it has Silverlight support with the DLR but it is really designed to be used instead of C# not as a scripting language executed from within C#. With Lua though I may be able to get it working although it still seems like everything was designed to simply execute the game in lua scripts using C# as the core instead of augmenting C# classes.

I really want to just be able to raise events on the mobile objects and have instances handle those events in a custom way. I certainly don't want to compile C# on the fly either as that has its own set of issues. Compiling C# on the fly is like running an exe for each script that is executed and would be way too costly in both execution time to compile and memory consumption.

Also, since both the client and server will need to execute scripts the scripting language needs to work in both Silverlight and ASP.NET.

Perenthia currently handles commands using a Dictionary<int, Command> where the int is command value, from a list of constants, and the Command is a method pointer (delegate). When the game loads up the first time the list of commands is constructed and the methods pointers are set to methods that related to the command. An example would be a chat command. When the command comes in and is validated the method pointer is found using this dictionary and then executed in the context of the connected player. I was thinking of doing the same thing with mobiles and the actions they can take and respond to based on the world around them. It is a bit more complex with mobiles than commands since commands do a specific thing whereas every mobile could execute a different block of code for a given event. I still have some work to do on it but this will probably be what I use in place of scripting. I loose the flexibility of writing quick scripts, changing them on the fly, etc. but gain the ability to have it work within my code framework, rely on only .NET and work in both Silverlight and ASP.NET.

8/26/08


Silverlight Game Contest

I thought I would take a little break from working on Perenthia and take part in a Silverlight Game Contest for a sports theme game. This will push out the alpha release date a little but I need a little change of pace.

I am a little late in deciding to do this but figured I would give it a try and see if I can come up with something fun by September 8th. I have an idea for a simple game, hopefully I can make it fun to play because I know it will be fun to build.

Once the game is finished I will also post the source code.

8/23/08


Silverlight 2 Farseer Physics Geom Visual Helper Method

I ported some code from the Farseer Physics Engine XNA demos that displayed  a debug view of the geom vertices used with the Physics Simulator. This is useful if you want to see where your geoms are being rendered to the screen to make sure you have everything in the right place. I know I need this a lot, helps me to understand where I am placing body geoms.

Anyway, here is the method, the _debug variable is just a Canvas I added at the top of the UI. Keep in mind that this method will slow your game WAY down so be sure and use it to test where geoms are positioned and if everything is moving properly. I use a static variable for the PhysicsSimulator, hence the cleverly named Physics.Simulator.

        private void DrawVertices()        {            _debug.Children.Clear();            int verticeCount = 0;            for (int i = 0; i < Physics.Simulator.GeomList.Count; i++)            {                verticeCount = Physics.Simulator.GeomList[i].LocalVertices.Count;                for (int j = 0; j < verticeCount; j++)                {                    Line line = new Line();                    line.Fill = new SolidColorBrush(Colors.Transparent);                    line.Stroke = new SolidColorBrush(Colors.Magenta);                    line.StrokeThickness = 1;                    if (j < verticeCount - 1)                    {                        line.X1 = Physics.Simulator.GeomList[i].WorldVertices[j].X;                        line.Y1 = Physics.Simulator.GeomList[i].WorldVertices[j].Y;                        line.X2 = Physics.Simulator.GeomList[i].WorldVertices[j + 1].X;                        line.Y2 = Physics.Simulator.GeomList[i].WorldVertices[j + 1].Y;                    }                    else                    {                        line.X1 = Physics.Simulator.GeomList[i].WorldVertices[j].X;                        line.Y1 = Physics.Simulator.GeomList[i].WorldVertices[j].Y;                        line.X2 = Physics.Simulator.GeomList[i].WorldVertices[0].X;                        line.Y2 = Physics.Simulator.GeomList[i].WorldVertices[0].Y;                    }                    _debug.Children.Add(line);                }            }        }

8/21/08


More Perenthia Screen Shots

Here are a few more screen shots of the Perenthia UI. The three screen shots are dialog windows that popup as a result of clicking on icons or buttons in UI. They are use a reusable control I created that has the title bar and X button and then just has a ContentControl for the guts of the dialog. The dialog window is draggable and does the hide and show thing. I need to do some cleanup on it and then I will post the source. I will also post the source for the stat bars shown on the skills dialog screen shot.

Also, I am hoping to have some time to blog about some of what I am doing with the overall game engine. I wanted to provide a development update but haven't moved the development along all that much due to working out some networking issues and trying to clean up some of the code.

The goal for Alpha is to get a working zone complete with quests, monsters, npcs, etc. However, I may do some smaller tests with the alpha registrants before all of this is complete. I was thinking of setting up a tiny zone just for testing where players could login, create a Character and accept a quest, which would be to enter a room where enemies spawn randomly and in various numbers. This would allow me to test a lot of things in one place; Character Creation, Quests, Spawning, Combat and NPC interaction.

Anyway, here are the screen shots; this first one is the Character Sheet that will display your character stats, equipped items and skills. The empty box will show the skills once I bind them to it. :)

This next screen is the actual skills screen where you can see you skill rank and all available skills. If you have Skill Points you can increase or learn new skills from this window.

This last image is just the backpack dialog, all I have is a starting candle right now. :)

8/9/08


Perenthia UI Screen Shot

As promised here is a screen shot of the main game UI, click on the image for a full size version. The top left is the current player's avatar and details. The map in the top right corner shows you the current zone, in relation to your z-index. The icons scattered around open other windows that allow you to view your character sheet, skills, spells, backpack, etc. The interface is built in Silverlight 2 Beta 2.

Perenthia UI

7/27/08


Quests

For those unfamiliar with the fantasy role playing genre; quests are simple to complex tasks that players complete in order to advance, get sweet gear, etc. They usually involved raiding a dungeon, slaying some terrible beast and wondering about the countryside trying to find the entrance to the hidden caverns.

What I hope to accomplish with quests is to provide a goal beyond just slaying monsters. Quests will actually move your Character through the game, while training and advancing them along the way.

One of my primary goals with Perenthia is to provide a different game play experience as players mature within the game. The first phases of the game will include some of the normal grind, killing monsters to get better gear, to level up, to kill monsters, etc. This gives the players immediate satisfaction while getting them used to moving around, using commands, spells, equipping items, buying and selling and everything else that goes along with eradication of the demon beasts of the underworld. The second phase of the game will follow a bit of a storyline, requiring players to venture into dungeons and caverns to retrieve special items they will need in order to move into the next phase of the game. While this involves a lot of the same type of situation, you have a reason to go into the dungeon and complete it, rather than just killing 10 rats because some dude is freaking out. The third phase will require the player to use the items created from the previous phase to solve puzzles, riddles and the like, which will take them into the next phase. The fourth phase will require the player to purchase some form of transportation in order to visit very far off places and located individuals who can further their progress with quests of their own. This will continue in this manner for the remainder of the game. I currently have 4 phases planned and ideas for 3 more that I hope to get written down and planned out before too long, although I will probably wait until after the alpha an beta test phases.

I want to try and provide a fun game and I feel that doing this phased game progress could provide an enjoyable gaming experience. I know when playing other games of the genre I usually burn out in the high levels because I worked so hard to get there and it is just more of the same. I would also like the world to progress forward around the players so that new challenges are introduced to both high and low level characters that are in keeping with the overall storyline. I want players to have as much fun playing the game as I am having making it. :)

I hope to provide a robust questing system that can handle these phases and still maintain a clean and easy way for players to know what to do next. For the alpha I have planned out some quests that take you around the alpha starting area and should advance the alpha players enough so that the next area will introduce another phase in the game experience.

I will do another development update after this weekend.

7/23/08


Perenthia World Builder Screen Shots

I am not quite ready to post the screen shots of the actual game UI, still need to do some cleanup. So, since I promised I would post some screen shots here are a few of my world builder tool. This is a windows application that just reads and writes to the database.

This first screen is the map builder and as you can see I can draw the actual places/rooms in which players will adventure. The map that is displayed will be the Alpha starting map, it consists of a small town, a forest and a small sewer system. The sewer nodes are not displayed because they are one level down.

This is the creature manager window that allows me to create creature "templates", from which actual creature instances can be created and placed onto the map from above. (Using the Add Mobile button).

This is just a shot of the item manager window, displaying some of the items I've added so far, got a long way to go on these. The items also follow the same "template" pattern that the creatures do. I still need to add in support for placing the drop items on Mobiles.

I hope to have the game UI screen shots ready soon.

7/22/08


Just an Update

I am still plugging along on my games, working mostly on Perenthia and checking out the new stuff in Silverlight 2 beta 2. I want to try and get the Perenthia beta 2 up and running by the middle or end of the summer, free time permitting.

 I haven't blogged much lately but I have been working on a lot of stuff. I will try and get some posts together to outline some of the stuff I have taken advantage of in developing Perenthia such as adding files as links in Visual Studio and then using partial classes to separate server and client logic and multi-threading in Silverlight and how to avoid cross thread access, which you will get exceptions on now in SL 2 beta 2. I will also try and get some screen shots of Perenthia, quite a bit has changed since the last screen shot I posted and I would like to get some of the new ones up.

6/10/08


Pirates! Progress

Rebuilding Pirates! in Silverlight 2 is coming along well. I got it running on my Silverlight game engine I was writing for use with Perenthia, which changed some of the way it worked initially. Anyway, I plan on doing a release once I get a few levels complete. In the mean time here is a screen shot of me testing sailing around.

Since I started over in Silverlight 2 and decided to use my game engine I am having to re-write a good portion of the functionality. I had to re-do movement since some of the structure of events has changed so this screen shot is the ship sailing around.

5/2/08


Perenthia Screen Shot

OK, I decided to post a screen shot of the Perenthia interface so far. Keep in mind this is still under development. The main sprite is not the final, just a ripped image from a Flash game called Exile and the background is just a sample I created so I could tell if the camera was following my player.

 

4/9/08


Perenthia and Pirates

After surviving the flu I am back to working on Perenthia. I did some work on Pirates but I am still having some positioning issues that I can't seem to get worked out and may have to rebuild a good portion of the game.

I should have a screen shot or two of the progress on Perenthia in another few days, I will see how it goes.

I have a good portion or rather the majority of the Perenthia Server finished and have been working on the Silverlight front end. I am still not sure whether or not I will be using sockets for communication or a custom IHttpHandler implementation.

4/8/08


Silverlight 2 and Cross Domain Web Calls

Frank LaVigne has written a nice Silverlight 2 Cross Domain Web Proxy Utility for making any type of web calls from Silverlight 2. This a great utility if you want to serve images or outside content to your Silverlight 2 apps but do not have the ability to setup a domain policy file on the content hosts server.

3/18/08


Multi-threading in Silverlight

I've been working with Sockets in Silverlight over the past couple of days, in between getting the Pirates game to a workable Silverlight 2 state. One thing I came across today which sent me round and round was BeginInvoke. Anyone who has programmed multi-threaded applications in Windows has used this, mostly to execute code on the UI thread from events.

Anyway, controls in Silverlight 2 do not have the Invoke or BeginInvoke methods but controls do have a Dispatcher reference which lives in System.Windows.Threading. WPF programmers are probably already used to this but it was new for me. The Dispatcher allows you to check to see if you can access the current object on the current thread.

One thing to note about the Dispatcher is that it has a method called CheckAccess() that returns true if you can access the control on the current thread of false if you are not on the control's thread. The CheckAccess() method is decorated with a EditorBrowsableAttribute and has it's value set to Never. This prevents us from being able to see the method via intellisense in Visual Studio but does not prevent the method from being used. Not sure why the folks at MS wanted to hide this method and the documentation does not provide an explanation. The DependancyObject class also provides a CheckAccess method that is also hidden from the editor and just calls the Dispatcher method of the current object. Maybe it's not documented because it might be removed in the future, it is in beta after all.

So, to sum up, if you need to be able to invoke methods on a control's thread from a background thread you can do the following: (snippet from socket testing)

        private delegate void AppendTextDelegate(string text);        public void AppendText(string text)        {            if (txtChat.CheckAccess())            {                // Append the message to the chat buffer.                _content.Append(text).Append(Environment.NewLine);                // Re-post the contents of the chat buffer to the chat text and scroll to                // the bottom.                txtChat.Content = _content.ToString();                txtChat.ScrollToVerticalOffset(txtChat.ScrollableHeight);            }            else            {                txtChat.Dispatcher.BeginInvoke(new AppendTextDelegate(this.AppendText), text);            }        }

The _content control is just a StringBuilder that actually holds the content from the chat control.

Again, Silverlight 2 is in beta 1 so maybe this will change in the future.

3/14/08