Silverlight Development

Packt Publishing, the folks who published our Microsoft Silverlight 4 Business Application Development book have included some of the chapters of that book in a new mashup of their best selling Silverlight books. The new book is called Managing Media and Data in Microsoft Silverlight 4 and features great chapters from several different authors’ Silverlight books, covering the gambit from XAML and layouts to SharePoint integration to REST and RIA services.

In addition, they are also offering a discount on all Microsoft technology books during the month of May so be sure to check that out as well at Packt’s Microsoft Carnival!

Silverlight is still a great technology for business development, it eases deployment issues with distributed software, provides an interactive experience for users via a browser while maintaining the power of C# in the development life cycle.

5/10/2012


Perenthia–Development Diary #2

I have made some decent progress on scaling Perenthia down and re-tooling it for more basic HTML/mobile access. I have the server core complete and am working on the UI components. The first step will be to get the adventuring/combat piece finished and then move on to the crafting system.

The abstract core server pieces I decided to put into a separate library and made the source available on GitHub. I call it “Leo” and it provides the basic game server and provides some interfaces and associated libraries for processing input through the server. Since I am using SignalR for client to server communication I included some code in Leo to provide a input processor and PersistentConnection. The TestConsole project was something I used to initially setup the game libraries. They are now linked into my Perenthia solution and will be updated as I find the need while working on Perenthia.

The Leo source is very early stages and is subject to change frequently. It is also intended primarily for low traffic games such as iOS, Facebook, etc. I will up some instructions once it gets a little more stable.

Here is a screenshot of Perenthia’s new progress so far:

perenthia-ss1

5/4/2012


Testing Facebook Credits with ASP.NET

I ran into some issues attempting to test Facebook Credits with ASP.NET locally during development. I have an MVC3 site that is running in the Azure Development Environment and I wanted to be able to test the credits functionality. I played around with a bunch of settings, etc. and finally got a combination of things working that allows me to test credits locally. Below are the steps I took:

1. Set the web project as the startup project in the Visual Studio solution. Unless you need specific Azure functionality this is much easier since Azure does not want to accept traffic from anything other than localhost and credits testing will not work with localhost.

2. Set your web project to run using IIS or IIS Express bound to http://localhost. (Project Properties -> Web -> Servers) If you want to use IIS Express and have IIS installed you will need to unbind the default web site from port 80. ** I also had to stop the Web Deployment Agent Service as it binds to port 80.

3. I used DynDns.com to provide a host name for my web site and entered this as the domain for both the Canvas Url and Credits Callback Url. The Credits callback will not work with 127.0.0.1 or localhost so you need some external domain. I tried using a made up entry in my HOSTS file but Facebook needs a real address since they initiate the request for the callback.

4. I edited the applicationHost.config file under %userprofile%documentsiisexpress to allow my DynDns name like so (system.applicationHost/sites node):

 

<site name="WebAppName" id="1">

                <application path="/" applicationPool="Clr4IntegratedAppPool">

                    <virtualDirectory path="/" physicalPath="DirToWeb" />

                </application>

                <bindings>

                    <binding protocol="http" bindingInformation="*:80:localhost" />

                    <binding protocol="http" bindingInformation="*:80:dyndnsname" />

                </bindings>

            </site>

 

5. Do any port forwarding on your router and enable port 80 on your firewall.

6. Setup the callback code according to the Facebook Docs, I even used the JS sample code to test.

6/24/2011


Perenthia Chapter 2

After many months of not working on Perenthia I find myself once again desiring to see it complete. The last revision I did on Perenthia was some time ago and involved a Silverlight UI with a C#/SQL 2008 backend.

During the course of development on Perenthia I discovered some of the pitfalls of attempting to develop a role playing game by one self. Mainly, the inability to handle content creation on a large enough scale as to appeal to players and have enough interactivity to make the game fun. I have been thinking lately of ways to reduce that load and still keep the game fun. I have some new ideas I want to put into place and re-release Perenthia with these concepts.

Among the changes I plan to implement will be scaling the game down to function solely in an HTML environment. This will enable me to provide a mobile version that works on all phones with a browser and keep the feature set the same for both phone and web.

I have begun to refactor pieces of the code and am making heavy use of jquery for the character creation screen.

11/30/2010


Countdown to Fall!

5/31/2010


Silverlight and Windows Phone 7 Game

With the release of Silverlight 4 RC the Windows Phone 7 developer tools I wanted to take a stab at building a Silverlight application for both the web and phone to see what kind of differences there are between the two. Except for the inability to use the ChildWindow I was able to build out controls and share them between the two applications. The main differences were in the MainPage.xaml that is created, along with the default styles, when you create a new Silverlight application for the web and Windows Phone 7. Of course,

I decided to create a game (called ShapeAttack) to see how it would perform on the phone emulator. Sad to say the performance on the emulator is very poor but I would imagine that it would be better on the physical device but as I do not own a Windows Phone 7 yet the emulator has to do for now. For that reason I would recommend doing this parallel type of development so you can actually test your application.

What I did was create all of the game code in UserControls, including the main game surface, then I linked the files from the standard Silverlight project into the phone project.

The game is very simple and kind of cheesy :D, just click on the shapes to destroy them. And of course you can download the source code for ShapeAttack(2.8MB) or play ShapeAttack online.

ShapeAttack

3/27/2010


MIX 10 Sessions

Frank LaVigne has posted a very useful tool to download all of the MIX 10 session content.

3/17/2010


Pirates Video

Here is a video of the Pirates game:

[youtube http://www.youtube.com/watch?v=b9LHFS1dKWg&hl=en_US&fs=1&color1=0x2b405b&color2=0x6b8ab6]

3/11/2010


General Purpose Sprite Class

On the heels of some great posts by Bill Reiss on Sprites Part 1 and Sprites Part 2 in Silverlight I wanted to post some general base sprite classes that I use. The classes are intended to be used with the SilverSprite framework.

These classes all exist in an assembly I lovingly call the “Shady Engine” (to explain the namespaces)

The base class I used is ingeniously called Sprite. It implements an interface called ISprite. I added the interface in order to create an interface called IPlayer that the main Game class uses.

ISprite.cs

using System.Windows;using Microsoft.Xna.Framework;namespace Shady.Sprites{    public interface ISprite    {        ISprite Owner { get; set; }        Vector2 Position { get; set; }        double Rotation { get; set; }        System.Windows.Point Scale { get; set; }        double Width { get; set; }        double Height { get; set; }        Rect Bounds { get; }        bool IsActive { get; set; }    }}
.csharpcode, .csharpcode pre{ font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/}.csharpcode pre { margin: 0em; }.csharpcode .rem { color: #008000; }.csharpcode .kwrd { color: #0000ff; }.csharpcode .str { color: #006080; }.csharpcode .op { color: #0000c0; }.csharpcode .preproc { color: #cc6633; }.csharpcode .asp { background-color: #ffff00; }.csharpcode .html { color: #800000; }.csharpcode .attr { color: #ff0000; }.csharpcode .alt{ background-color: #f4f4f4; width: 100%; margin: 0em;}.csharpcode .lnum { color: #606060; }

And here is the Sprite.cs file:

using System;using System.Windows;using System.Windows.Controls;using System.Windows.Markup;using System.Windows.Media;using System.Windows.Media.Imaging;using System.Windows.Shapes;using Microsoft.Xna.Framework;namespace Shady.Sprites{    [TemplatePart(Name = PART_RootElement, Type = typeof(Canvas))]    [TemplatePart(Name = PART_ContentElement, Type = typeof(ContentControl))]    [TemplatePart(Name = PART_DebugCenter, Type = typeof(Ellipse))]    [ContentProperty("Content")]    public class Sprite : Control, ISprite    {        public const string PART_RootElement = "PART_RootElement";        public const string PART_ContentElement = "PART_ContentElement";        public const string PART_DebugCenter = "PART_DebugCenter";        protected Canvas RootElement { get; set; }        protected ContentControl ContentElement { get; set; }        protected Ellipse DebugCenterElement { get; set; }        protected TranslateTransform TranslateTransform { get; set; }        protected RotateTransform RotateTransform { get; set; }        protected ScaleTransform ScaleTransform { get; set; }        protected double HalfWidth = 0;        protected double HalfHeight = 0;        public ISprite Owner { get; set; }        public object Content        {            get { return (object)GetValue(ContentProperty); }            set { SetValue(ContentProperty, value); }        }        public static readonly DependencyProperty ContentProperty = DependencyProperty.Register("Content", typeof(object), typeof(Sprite), new PropertyMetadata(null));        public bool Debug        {            get { return (bool)GetValue(DebugProperty); }            set { SetValue(DebugProperty, value); }        }        public static readonly DependencyProperty DebugProperty = DependencyProperty.Register("Debug", typeof(bool), typeof(Sprite), new PropertyMetadata(false, new PropertyChangedCallback(Sprite.OnDebugPropertyChanged)));        private static void OnDebugPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)        {            var sprite = obj as Sprite;            if (sprite == null)                return;            if (sprite.DebugCenterElement != null)                sprite.DebugCenterElement.Visibility = (bool)e.NewValue ? Visibility.Visible : Visibility.Collapsed;        }        public Vector2 Position        {            get            {                var x = (double)GetValue(Canvas.LeftProperty);                var y = (double)GetValue(Canvas.TopProperty);                return new Vector2((float)x, (float)y);            }            set            {                SetValue(Canvas.LeftProperty, (double)value.X);                SetValue(Canvas.TopProperty, (double)value.Y);            }        }        public virtual double Rotation        {            get { return this.RotateTransform.Angle; }            set { this.RotateTransform.Angle = value; }        }        public System.Windows.Point Scale        {            get { return new System.Windows.Point(this.ScaleTransform.ScaleX, this.ScaleTransform.ScaleY); }            set            {                this.ScaleTransform.ScaleX = value.X;                this.ScaleTransform.ScaleY = value.Y;            }        }        public new double Width        {            get { return base.Width; }            set            {                base.Width = value;                HalfWidth = Width * 0.5;                TranslateTransform.X = -HalfWidth;                if (this.DebugCenterElement != null)                    Canvas.SetLeft(this.DebugCenterElement, HalfWidth);            }        }        public new double Height        {            get { return base.Height; }            set            {                base.Height = value;                HalfHeight = Height * 0.5;                TranslateTransform.Y = -HalfHeight;                if (this.DebugCenterElement != null)                    Canvas.SetTop(this.DebugCenterElement, HalfHeight);            }        }        public Rect Bounds        {            get            {                Vector2 position = this.Position;                return new Rect(position.X - HalfWidth, position.Y - HalfHeight, this.Width, this.Height);            }        }        private WriteableBitmap _bitmap;        protected internal virtual WriteableBitmap Bitmap        {            get            {                if (_bitmap == null && this.ContentElement != null)                {                    var content = this.ContentElement.Content;                    if (content != null && content is Image)                    {                        _bitmap = new WriteableBitmap((int)this.Width, (int)this.Height);                        _bitmap.Render((content as Image), new TranslateTransform());                        _bitmap.Invalidate();                    }                }                return _bitmap;            }        }        private bool _isActive = true;        public bool IsActive        {            get { return _isActive; }            set            {                _isActive = value;                this.Visibility = _isActive ? Visibility.Visible : Visibility.Collapsed;            }        }        public Sprite()        {            this.DefaultStyleKey = typeof(Sprite);            this.TranslateTransform = new TranslateTransform();            this.RotateTransform = new RotateTransform();            this.ScaleTransform = new ScaleTransform();        }        public override void OnApplyTemplate()        {            base.OnApplyTemplate();            this.RootElement = GetTemplateChild(PART_RootElement) as Canvas;            this.ContentElement = GetTemplateChild(PART_ContentElement) as ContentControl;            this.DebugCenterElement = GetTemplateChild(PART_DebugCenter) as Ellipse;            if (DebugCenterElement != null && !Double.IsNaN(this.Width) && !Double.IsNaN(this.Height))            {                Canvas.SetLeft(DebugCenterElement, HalfWidth - 1.5);                Canvas.SetTop(DebugCenterElement, HalfHeight - 1.5);            }            if (this.RootElement != null)            {                var group = new TransformGroup();                group.Children.Add(TranslateTransform);                group.Children.Add(RotateTransform);                group.Children.Add(ScaleTransform);                this.RootElement.RenderTransform = group;                this.RootElement.RenderTransformOrigin = new System.Windows.Point(0, 0); // At 0,0 because the translate transform positions the sprite.            }            this.Initialize();        }        public virtual void Initialize()        {        }        public virtual void Update(GameTime gameTime)        {        }        public virtual void Draw(GameTime gameTime)        {        }        /// <summary>        /// Re-initializes the sprite.        /// </summary>        public virtual void Reset()        {            this.IsActive = true;            this.Owner = null;        }        protected static void OnDependencyPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)        {            var sprite = obj as Sprite;            if (sprite == null) return;            sprite.Initialize();        }    }}
.csharpcode, .csharpcode pre{ font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/}.csharpcode pre { margin: 0em; }.csharpcode .rem { color: #008000; }.csharpcode .kwrd { color: #0000ff; }.csharpcode .str { color: #006080; }.csharpcode .op { color: #0000c0; }.csharpcode .preproc { color: #cc6633; }.csharpcode .asp { background-color: #ffff00; }.csharpcode .html { color: #800000; }.csharpcode .attr { color: #ff0000; }.csharpcode .alt{ background-color: #f4f4f4; width: 100%; margin: 0em;}.csharpcode .lnum { color: #606060; }

Because Sprite is a templated control there is also some XAML to go along with it (You will need to place this in a themes/generic.xaml file):

<Style TargetType="sprites:Sprite">        <Setter Property="Background" Value="{x:Null}"></Setter>        <Setter Property="Foreground" Value="{x:Null}"></Setter>        <Setter Property="Template">            <Setter.Value>                <ControlTemplate TargetType="sprites:Sprite">                    <Canvas x:Name="PART_RootElement" Background="{TemplateBinding Background}">                        <ContentControl x:Name="PART_ContentElement"/>                        <Ellipse x:Name="PART_DebugCenter" Width="3" Height="3" Fill="Red" Visibility="Collapsed"/>                    </Canvas>                </ControlTemplate>            </Setter.Value>        </Setter>    </Style>
.csharpcode, .csharpcode pre{ font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/}.csharpcode pre { margin: 0em; }.csharpcode .rem { color: #008000; }.csharpcode .kwrd { color: #0000ff; }.csharpcode .str { color: #006080; }.csharpcode .op { color: #0000c0; }.csharpcode .preproc { color: #cc6633; }.csharpcode .asp { background-color: #ffff00; }.csharpcode .html { color: #800000; }.csharpcode .attr { color: #ff0000; }.csharpcode .alt{ background-color: #f4f4f4; width: 100%; margin: 0em;}.csharpcode .lnum { color: #606060; }

That is my basic Sprite class, I will post my animated sprite class next.

3/10/2010


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/2010


XNA and Silverlight Development

Mad Laumann has a new post up about the development progress of his game Little Longhorn, a tower defense game written for XNA and Silverlight using the SilverSprite framework. I have been following his progress with the game and have been able to play the early versions of it (both XNA and Silverlight) and have found it quite fun and challenging. The game has grown quite a bit over the last few months with game play and graphics improving all the time. Needless to say Mads is becoming an authority on XNA/Silverlight combination platform development so be sure to check out his blog A Silverlight Playground.

2/1/2010


Silverlight 4 Business Application Development – Beginner’s Guide

For those of you out there wanting to get into Silverlight development but just do not know where to start, Frank LaVigne and I have just about completed our book titled Microsoft Silverlight 4 Business Application Development – Beginner’s Guide(link to pre-order). This book will be ideal for current Windows or ASP.NET developers who want to learn Silverlight and because the book is centered on Business Application Development we will empower you with knowledge so you can recommend Silverlight for the next business project at your company.

Gaining the basics required to develop in Silverlight, make use of data binding, WCF and RIA Services you will be prepared to lead the Silverlight charge at your company. Be warned, once you start developing in Silverlight you will have a hard time going back to normal ASP.NET development.

1/18/2010


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/2009


Game Objects - My Design

I thought I would share one of the core concepts I implemented in Perenthia that has been working quite well. Early on in development I decided to create templates and instances in regards to game objects. A template would define common, unchangable properties of an object whereas an instance would be the opposite. An example would be a Sword; there is a template for a Sword that defines its damage value, price, required skill level, etc. 

I have one table that stores all the objects for the game. Since objects being actively used are loaded into memory and the table is indexed for those queries it runs pretty fast. Since I store all of the objects into one table I make heavy use of the XML data type in SQL 2005 to provide a bunch of XML Serialized Key/Value pairs that define the properties of an object. On the code side of things a dictionary provides the underlying data store for properties of the object. For instance, the Sword example above has a Power property that is used in combination with the wielder's skill in swords to determine damage. The Power property just fetches the Power value from the properties dictionary using the property name. This works a lot like the dependency properties, just my collection serializes to XML for database storage. When an object is loaded from the database the xml from the template is retrieved and added to the object, then the object instance xml is dropped into the object, overriding any template properties of the same name.

This property dictionary also allows me to store whether or not the property belongs to a template or instance. If the property belongs to a template then when the instance is serialized and saved to the database those template properties are excluded. Likewise saving a template does not serialize and save the instance properties. This structure allows for a lot of flexibility with my objects and has been working out quite well. I probably would not have used serialization and xml the way I am if I was doing a typical call to the database qith each request as the extra overhead would not have been worth the effort. However, since my objects are loaded into memory and my saves are background threaded I experience a lot of benefit of an easy to use object.

I shuld note that players character data is stored in a separate table from the core objects table. It follows the same design buy allows me to migrate and change data in the objects table without overwriting player data, since players are essentially the same as mobiles. 

5/15/2009


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/2009


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/2009


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/2009


Microsoft Internet Explorer 8

Microsoft announced the release of Internet Explorer 8 this morning, it is available at http://www.microsoft.com/ie8. With this new browser release Microsoft will position themselves on the top of the browser stack again. They have incorporated a lot of safe browsing and ease of browsing features into the new version that will make it the paramount browser for the normal everyday web surfer.

 EDIT: And then I started using it and now am back to using Chrome... 

3/19/2009


Heading to MIX

I am flying out to MIX in a few hours and I am really excited. This will be my first time at MIX and I am looking forward to all the great sessions regarding Silverlight 3, mixing it up with other industry professionals and just hanging out in Las Vegas. :)

3/17/2009


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/2009


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/2009


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/2009


Going to MIX09

I am planning on attending MIX09 this year, already registered actually. I missed it last year but wanted to be present this time around for the new Silverlight announcements and to meet some of the Silverlight team. Anyway, if you are going I will be there :)

1/27/2009


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/2009


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/2009


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/2008


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/2008


Happy Birthday to MUD

MUD is 30 years old today. For those who don't know what MUD is, it is the forerunner to the virtual worlds and MMORPGs we love and play today.

10/20/2008


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/2008


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/2008


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/2008


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/2008


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/2008


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/2008


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/2008


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/2008


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/2008


Silverlight 2 Sockets

I started working with Sockets in Silverlight 2 tonight and got my Silverlight application connected to the server and sending and receiving commands. I am going to write some code to testing sending commands from the server at the same time commands are coming from the client to see how the app and my code will handle it.

I will post some code as I get further along. Also, I made more progress on getting the Pirates game upgraded to Silverlight 2.

3/12/2008


HttpWebRequest Helper for Silverlight 2

I wrote this helper class to assist with processing an Http Request via Silverlight 2. Since we can't provide a custom class by deriving from BrowserHttpWebRequest because it is internal I wrote this helper that will create and send the request and raise an event when the request completes. I am using it in my current game to send commands to the server and process the responses.

Edit: I revised the class slightly based on finding from Mike Briseno:

Edit: Some folks have experienced issues with this class after installing Silverlight 2 Beta 2. Be sure to check your domain access policy xml file for the new changes in beta 2 but if you experience issues feel free to contact me and we can try and figure them out.

Edit: Added HttpUtility.UrlEncode to the post values before writing them to the stream. Thanks for the suggestion Stefan!

using System;using System.Collections.Generic;using System.IO;using System.Windows;using System.Windows.Browser;using System.Windows.Controls;using System.Windows.Documents;using System.Windows.Ink;using System.Windows.Input;using System.Windows.Media;using System.Windows.Media.Animation;using System.Windows.Shapes;using System.Net;namespace Lionsguard{    public class HttpHelper    {        private HttpWebRequest Request { get; set; }        public Dictionary<string, string> PostValues { get; private set; }        public event HttpResponseCompleteEventHandler ResponseComplete;        private void OnResponseComplete(HttpResponseCompleteEventArgs e)        {            if (this.ResponseComplete != null)            {                this.ResponseComplete(e);            }        }        public HttpHelper(Uri requestUri, string method, params KeyValuePair<string, string>[] postValues)        {            this.Request = (HttpWebRequest)WebRequest.Create(requestUri);            this.Request.ContentType = "application/x-www-form-urlencoded";            this.Request.Method = method;            this.PostValues = new Dictionary<string, string>();            if (postValues != null && postValues.Length > 0)            {                foreach (var item in postValues)                {                    this.PostValues.Add(item.Key, item.Value);                }            }        }        public void Execute()        {            this.Request.BeginGetRequestStream(new AsyncCallback(HttpHelper.BeginRequest), this);        }        private static void BeginRequest(IAsyncResult ar)        {            HttpHelper helper = ar.AsyncState as HttpHelper;            if (helper != null)            {                if (helper.PostValues.Count > 0)                {                    using (StreamWriter writer = new StreamWriter(helper.Request.EndGetRequestStream(ar)))                    {                        foreach (var item in helper.PostValues)                        {                            writer.Write("{0}={1}&", item.Key, HttpUtility.UrlEncode(item.Value));                        }                    }                }                helper.Request.BeginGetResponse(new AsyncCallback(HttpHelper.BeginResponse), helper);            }        }        private static void BeginResponse(IAsyncResult ar)        {            HttpHelper helper = ar.AsyncState as HttpHelper;            if (helper != null)            {                HttpWebResponse response = (HttpWebResponse)helper.Request.EndGetResponse(ar);                if (response != null)                {                    Stream stream = response.GetResponseStream();                    if (stream != null)                    {                        using (StreamReader reader = new StreamReader(stream))                        {                            helper.OnResponseComplete(new HttpResponseCompleteEventArgs(reader.ReadToEnd()));                        }                    }                }            }        }    }    public delegate void HttpResponseCompleteEventHandler(HttpResponseCompleteEventArgs e);    public class HttpResponseCompleteEventArgs : EventArgs    {        public string Response { get; set; }        public HttpResponseCompleteEventArgs(string response)        {            this.Response = response;        }    }}

And this is how I am using it in my app:

        private void ProcessCommand(short cmd, string msg)        {            App app = App.Current as App;            HttpHelper helper = new HttpHelper(app.ServerUri, "POST",                new KeyValuePair<string, string>("authKey", app.AuthKey),                new KeyValuePair<string, string>("cmd", cmd.ToString()),                new KeyValuePair<string, string>("msg", msg));            helper.ResponseComplete += new HttpResponseCompleteEventHandler(this.CommandComplete);            helper.Execute();        }        private void CommandComplete(HttpResponseCompleteEventArgs e)        {            txtAlert.Text = e.Response;        }

For the VB Developers out there David Thiessen has converted my code to VB:

' Usage....'Private Sub ProcessCommand(ByVal cmd As Short, ByVal msg As String)'    Dim app As App = TryCast(App.Current, App)'    Dim helper As New HttpHelper(app.ServerUri, "POST", New KeyValuePair(Of String, String)("authKey", app.AuthKey), New KeyValuePair(Of String, String)("cmd", cmd.ToString()), New KeyValuePair(Of String, String)("msg", msg))'    AddHandler helper.ResponseComplete, AddressOf CommandComplete'    helper.Execute()'End Sub'Private Sub CommandComplete(ByVal e As HttpResponseCompleteEventArgs)'    txtAlert.Text = e.Response'End Sub''' <summary>''' Converted C# code from http://www.cameronalbert.com/post/2008/03/HttpWebRequest-Helper-for-Silverlight-2.aspx''' </summary>''' <remarks></remarks>Public Class HttpHelper    Private Property Request() As HttpWebRequest        Get            Return _Request        End Get        Set(ByVal value As HttpWebRequest)            _Request = value        End Set    End Property    Private _Request As HttpWebRequest    Public Property PostValues() As Dictionary(Of String, String)        Get            Return _PostValues        End Get        Private Set(ByVal value As Dictionary(Of String, String))            _PostValues = value        End Set    End Property    Private _PostValues As Dictionary(Of String, String)    Public Event ResponseComplete As HttpResponseCompleteEventHandler    Private Sub OnResponseComplete(ByVal e As HttpResponseCompleteEventArgs)        RaiseEvent ResponseComplete(e)    End Sub    Public Sub New(ByVal requestUri As Uri, ByVal method As String, ByVal ParamArray postValues As KeyValuePair(Of String, String)())        Me.Request = DirectCast(WebRequest.Create(requestUri), HttpWebRequest)        Me.Request.ContentType = "application/x-www-form-urlencoded"        Me.Request.Method = method        Me.PostValues = New Dictionary(Of String, String)()        For Each item In postValues            Me.PostValues.Add(item.Key, item.Value)        Next    End Sub    Public Sub Execute()        Me.Request.BeginGetRequestStream(New AsyncCallback(AddressOf HttpHelper.BeginRequest), Me)    End Sub    Private Shared Sub BeginRequest(ByVal ar As IAsyncResult)        Dim helper As HttpHelper = TryCast(ar.AsyncState, HttpHelper)        If helper IsNot Nothing Then            Using writer As New StreamWriter(helper.Request.EndGetRequestStream(ar))                For Each item In helper.PostValues                    writer.Write("{0}={1}&", item.Key, HttpUtility.UrlEncode(item.Value))                Next            End Using            helper.Request.BeginGetResponse(New AsyncCallback(AddressOf HttpHelper.BeginResponse), helper)        End If    End Sub    Private Shared Sub BeginResponse(ByVal ar As IAsyncResult)        Dim helper As HttpHelper = TryCast(ar.AsyncState, HttpHelper)        If helper IsNot Nothing Then            Dim response As HttpWebResponse = DirectCast(helper.Request.EndGetResponse(ar), HttpWebResponse)            If response IsNot Nothing Then                Dim stream As Stream = response.GetResponseStream()                If stream IsNot Nothing Then                    Using reader As New StreamReader(stream)                        helper.OnResponseComplete(New HttpResponseCompleteEventArgs(reader.ReadToEnd()))                    End Using                End If            End If        End If    End SubEnd ClassPublic Delegate Sub HttpResponseCompleteEventHandler(ByVal e As HttpResponseCompleteEventArgs)Public Class HttpResponseCompleteEventArgs    Inherits EventArgs    Public Property Response() As String        Get            Return _Response        End Get        Set(ByVal value As String)            _Response = value        End Set    End Property    Private _Response As String    Public Sub New(ByVal response As String)        Me.Response = response    End SubEnd Class


 

3/7/2008


Silverlight 2.0

Silverlight 2.0 is available for download on silverlight.net!

3/5/2008


Processing Commands

I might have mentioned it before but my PBBG engine is basically a commands processor. In other words it accepts commands from the client, processes the information on the server and sends the response back to the clients as JSON. Not really different from a normal web request except it is specific to my PBBG engine and you don't request documents from the server you are telling the server you want your Character to "do" something.

My PBBG engine is essentially just the command processor and the classes surrounding processing commands. I have abstracted it completely from the data source. There are no data providers or data connections at all in the engine. Instead, if it needs to get data it raises events that a deriving library can handle and pass back the required data in the form of simple interfaces. Some assumptions I did make such as ID values defined in interfaces are int values rather than object since I don't design databases with primary keys other than int. :)

One of the things I also did for the CommandManager was to created an abstract Verb class that I can derive from to create command verbs such as move, say, attack, etc. These command verbs can be defined in the deriving library and set in the CommandManager when the game starts up. Another class to assist with commands was a generic Command<T> class where T has to be a derived Verb value. So I can create Command<Move>() and the call an Execute method on the class which in turn creates the Verb instance and executes the code in the Verb class.

This has been working pretty well so far and since the CommandManager raises an event each time if processes a command I can have any number of derived libraries executing their own commands. 

2/13/2008


Adventures in LINQ to SQL

I've been playing around a little with LINQ to SQL for Perenthia and wanted to share a kind of nasty query I just wrote. In Perenthia the typical RPG classes are called Professions. To track these professions I have the following database tables:

The players table links over to the rad_ProfessionLevels table via the ProfessionLevelId foreign key. The rad_ProfessionLevels table also links over the rad_Levels table on the LeveId foreign key. This layout allows me to have professions, define custom names for the levels and give certain levels titles.

When loading up a player record I need to go and get the level number, level name, profession name and title prefix and suffix values from the database. Since I am using LINQ to SQL for my entity classes I wrote the following query to retrieve this information:

 

var titleQuery = from pl in db.ProfessionLevelsjoin p in db.Professions on pl.ProfessionId equals p.ProfessionIdjoin plt in db.ProfessionLevelTitles on pl.ProfessionLevelId equals plt.ProfessionLevelId into profLevelsfrom x in profLevels.DefaultIfEmpty()join t in db.Titles on x.TitleId equals t.TitleId into titlesfrom y in titles.DefaultIfEmpty()join l in db.Levels on pl.LevelId equals l.LevelId into levelsfrom z in levels.DefaultIfEmpty()where pl.ProfessionLevelId == avatar.ProfessionLevelIdselect new { p.ProfessionName, z.LevelNumber, pl.LevelName, y.Prefix, y.Suffix };
 

It's kind of a nasty beast but produces the following SQL query:

 

SELECT [t1].[ProfessionName],[t4].[LevelNumber] AS [LevelNumber],[t0].[LevelName], [t3].[Prefix] AS [Prefix],[t3].[Suffix] AS [Suffix]FROM [dbo].[rad_ProfessionLevels] AS [t0]INNER JOIN [dbo].[rad_Professions] AS [t1]ON [t0].[ProfessionId] = [t1].[ProfessionId]LEFT OUTER JOIN [dbo].[rad_ProfessionLevelTitles] AS [t2]ON [t0].[ProfessionLevelId] = [t2].[ProfessionLevelId]LEFT OUTER JOIN [dbo].[rad_Titles] AS [t3]ON [t2].[TitleId] = [t3].[TitleId]LEFT OUTER JOIN [dbo].[rad_Levels] AS [t4]ON [t0].[LevelId] = [t4].[LevelId]WHERE [t0].[ProfessionLevelId] = @p0
 

The @p0 value is the ProfessionLevelId value stored with the player record.

The LINQ query is nasty because of the three outer joins I have to perform because not all profession levels have titles. It doesn't look pretty but it does work; as I get into more LINQ writing I will probably find a better way to write this but at least I got my data. :)

1/25/2008


Silverlight Pirates! Prototype

The Silverlight Pirates! prototype is online!

Keep in mind that this is a very early prototype and about the only thing you can do is destroy that other ship that is sitting there. I didn't get the enemy AI quite where I wanted it but it will fire on you if you get close. You can sail around and dock at ports, although you can't buy anything yet. Once your ammo runs out you will have to refresh the page to reload the game. You will also have to refresh if the enemy ship kills you.

On my TODO list are:

  • boundaries to keep you from sailing on forever
  • merchant ships to attack
  • better enemy AI so the merchants will run and man-o-war will chase
  • sea monsters
  • whirlpools
  • storms, which should be a big challenge
  • menu for when you are at port to buy ammo or repair your ship
  • gold and/or ammo drops from enemy ships

If you find any funky bugs please let me know and of course, you will need Silverlight 1.1 in order to run the prototype.

Silverlight Pirates! Prototype 

1/23/2008


BlogEngine 1.3

I finally got everything updated for BlogEngine.Net 1.3 so now my comments should work :)

1/17/2008


More Customer Hassles from Giant Companies

Looks like Microsoft wants to join the ranks of companies with really crappy customer service such as Bank of America, Sprint, Comcast, Dell. Full story...

 

 

12/3/2007


SQL 2005 XML Data Type, Stored Procedures and Lists

I've seen a lot of stuff out there regarding the SQL 2005 XML data type but most of it is just regurgitates the MSDN documentation. That's fine and all but what about practical uses of it? Well, I have a practical use sample. In building my persistent browser based game Perenthia I have a concept of a Place. A place is a virtual space in which objects are stored. For Perenthia the places represent the various rooms or tiles players move around on. The place or room has exits defined that allow the player to move from one place to the next. The exits are the typical directions; north, south, up, down, etc. In the database I have a Places table and a PlaceExits table. The Places table stores all the information regarding a place and the PlaceExits table stores the placeId along with a directionId and destinationId so I know what exits are available in any room and what rooms they lead to.

The simplified schema for the places would be:

 Places Tables

 In the stored procedure that retrieves the place information I use the following query snippet in the select clause:

    SELECT

        p.*,

    (
            SELECT
                e.DirectionId        AS "@directionId",
                e.DestinationId        AS "@destinationId"
            FROM
                dbo.PlaceExits e
            WHERE
                e.PlaceId = p.PlaceId
            FOR XML PATH('exit'), ROOT('exits')
        ) AS ExitsXml

    FROM dbo.Places p 

 This creates an XML fragment I can then parse in the application to fill a collection of Exits on the Place object.

When saving place information I pass XML generated from the Exits collection in a stored procedure like so:

CREATE PROCEDURE dbo.Places_SavePlace  (@PlaceId int, @ExitsXml xml)

From within the save procedure I perform an update or insert of the place data and then execute the following sql to insert and update the exits for the current place:

    -- Exits

    -- Process the existing exits first

    UPDATE
        dbo.PlaceExits
    SET
        DestinationId    = e.ex.value('(@destinationId)[1]', 'int')
    FROM
        @ExitsXml.nodes('/exits/exit') as e(ex)
    WHERE
        PlaceId = @PlaceId
        AND DirectionId = e.ex.value('(@directionId)[1]', 'tinyint')

    -- Process any new exits

    INSERT INTO dbo.PlaceExits
    (
        ObjectId, DirectionId, DestinationId
    )
    SELECT
        @PlaceId,
        e.ex.value('(@directionId)[1]', 'tinyint'),
        e.ex.value('(@destinationId)[1]', 'int')
    FROM
        @ExitsXml.nodes('/exits/exit') as e(ex)
    WHERE
        e.ex.value('(@directionId)[1]', 'tinyint') NOT IN
        (
            SELECT DirectionId FROM dbo.PlaceExits WHERE PlaceId = @PlaceId
        )

This is working pretty well and keeps me from having to loop through the exits in the application and make multiple database calls. 

11/14/2007


.NET PBBG Engine

In between updating Perenthia and adding new features I have been pulling parts of the code base into a more generic PBBG Engine I am writing in C#. I started working on it when I upgraded the Knights of the Realm game and used parts of it for Perenthia. I am hoping to put Knights of the Realm Beta 2 on the new engine once I get it finished.

I am building the engine as generic as I can but it will incorporate a base rules set and some basic concepts. The base objects will be Avatars, Places and Things. These objects will contain the properties required to function within the rules set and all objects will derive from a base GameObject class that will provide a properties collection for creating custom properties on derived game objects.

The game will be driven by commands sent from the client. Some objects will handle the commands in the engine framework while other commands will cause events to be raised that deriving implementations can handle and provide custom execution or additional execution of the commands.

The egnine will basically be a commands/rules processor that I will hopefully be able to build a variety of games on. I have plans and ideas for several types of games and do not want to continually build the same thing over and over, hence the PBBG engine. 

10/17/2007


ASP.NET AJAX Server Controls and PBBGs

I ran into a major performance issue with the ASP.NET AJAX Server Controls while testing my persistent browser based game Perenthia. I had initially used update panels fro the various regions on the main game interface such as the player stats, map and chat window. Programming this was simple as I could do everything in the server side code and just send back the results. With the partial page rendering feature of the ASP.NET AJAX Extensions only the update panel html was sent back to the browser.

Once I moved the application into a production environment and had people on there playing and testing the web starting running out of memory, the application was consuming the server memory at an alarming rate. What was happening was that IIS would jump 1 to 2 MB of RAM for each request made by the client, that means every time someone moved on the game map 1 to 2 MB of RAM were being held and not released. I tried a bunch of different stuff from optimizing stored procedures to caching the map data but none of it helped.

I used Firebug to watch the AJAX request and response and to see if I could reduce that down some. The response was simply HTML fragments so I wasn't too worried about that but the request sent the entire ViewState up to the server with each post. The ViewState for the game page could be quite large since I was appending messages to the chat window which was a server control.

I was able to eliminate the ViewState issue with the chat window but the app was still consuming RAM and not releasing it.

I decided to try just a simple JavaScript only AJAX post using the Microsoft AJAX Client Libraries. After doing some basic tests on the chat window I decided to rebuild the game interface using only client side AJAX calls, no update panels. This has resolved the server memory issue, why, I am still not sure, must be something with the way the resources are handled in .NET. I have not encountered the slow downs or crashes from before and the app is actually running a little smoother on the front end as well.

What I settled on was a custom ASHX handler class that handles the commands from the client and returns JSON strings that the UI can then translate and use to update the interface components. I had to rebuild some of the interface components as JavaScript objects but overall it was the right choice. Not too mention that I can now use that same handler with other interfaces such as Flash or Silverlight.

In conclusion, while the AJAX Server Controls might work great for most web sites they are definitely not optimal for a persistent browser based game (PPBG).

10/1/2007


Silverlight Goodness

Silverlight goodness is coming to the D.C. area in the form of a Silverlight Dev Camp. Keep checking Frank's Blog for more details.

9/11/2007


PBBG Mapper

Here is a screenshot of the PBBG mapping tool I wrote for creating the Perenthia world. The screenshot is the City of Angarath, a starting point in the game. The mapper is a  Windows application written in C# 2.0.

PBBG Mapper 

9/11/2007


Perenthia PBBG Flash UI

I've decided to use Flash to provide the UI for my persistent browser based game Perenthia. I went back and forth between Flash and AJAX and even looked into Silverlight a little and Flash just has the maturity needed for a good PBBG interface.  I originally had the main game UI written using AJAX but found it to be a little cumbersome when a lot of activity was occuring on the back end, just too much traffic generated for one user. I looked into Silverlight a little but I am going to wait until the 1.1 version is released so I can program in C# on the backend. Flash seems to be able to provide me with what I need and I wrote custom ASHX handlers on the ASP.NET side to handle commands from the Flash UI. The UI is basically just an advanced MUD client, in that the primary output is text based. However, with Flash I will be able to provide a better map and add some additional graphical features later on down the road. Since the command handler is a custom ASHX handler in .NET I could really allow any type of client to connect, as long as that client can send XML as an HTTP POST and receive the and parse the XML response from the page.

I will post a screen shot of the UI in the next day or so. 

 

8/26/2007


Visual Studio 2008 Sivlerlight JS 1.0 Page ItemTemplate

I created a Visual Studio 2008 ItemTemplate for Silverlight 1.0 JS. The template creates a Page.xml and Page.js files for creating Silverlight 1.0 JS pages in an existing web project. I built this template for adding Silverlight 1.0 XAML pages to an existing ASP.NET project I am building using Visual Studio 2008. The web project is a 2.0 project so I can't reference the 3.5 JScriptSilverlightPage Item Template.

8/24/2007


Visual Studio 2008

I downloaded and installed the Visual Studio 2008 Beta 2 and I am really liking it. Aside from the new .NET 3.5 features the IDE itself is awesome. Visual Studio remains one of the best IDEs out there. If you want some good articles on VS 2008 check out Scott Gu's blog post on VS 2008.

8/2/2007


C# Socket Server for Flash/ActionScript

After doing some research on available socket servers for Flash I decided to write my own in C#. Most of the server out there are written in Java and while there are some open source implementations I prefer to stick with a code base I know and understand.

I got the Flash movie to connect to the C# server yesterday using JSON protocal instead of XML as my transport mechanism. I used a .NET JSON library and an ActionScript 2.0 JSON library to build the objects on both sides. I created a C# class for passing data, serialize as a JSON string and send it the Flash movie. On the ActionScript side I created an AS class that mimics my C# data gram class and use the AS JSON lib to serialize and send that same object structure to the C# server. Working pretty good so far, I am able to login to the server and send and receive messages.

My next steps will be creating a basic flash game that I can use to run around and do battle while sending and receiving messages from the server. 

I have plans to make a Flash based PBBG with real time combat, we'll see how it goes. :) 

7/27/2007


X, Y, Z Coordinate Struct for .NET PBBGs

I created a struct in C# for storing the X, Y and Z coordinates for characters, objects and rooms within my current PBBG project Perenthia. The struct, called Vector, is serializable and can be used as the key value in a sorted or generic dictionary. Here is the code for the class.

[Serializable]    public struct Vector : IEquatable<Vector>    {        public static readonly Vector Empty = new Vector(0, 0, 0);        public Vector(int x, int y, int z)        {            _x = x;            _y = y;            _z = z;        }        public void SetLocation(int x, int y)        {            this.SetLocation(x, y, this.Z);        }        public void SetLocation(int x, int y, int z)        {            _x = x;            _y = y;            _z = z;        }        public Vector Copy()        {            return new Vector(this.X, this.Y, this.Z);        }        public static Vector FromString(string value)        {            if (!String.IsNullOrEmpty(value))            {                string[] parts = value.Split(',');                if (parts != null && parts.Length == 3)                {                    int x, y, z;                    if (Int32.TryParse(parts[0], out x))                    {                        if (Int32.TryParse(parts[1], out y))                        {                            if (Int32.TryParse(parts[2], out z))                            {                                return new Vector(x, y, z);                            }                        }                    }                }            }            return Vector.Empty;        }        #region GetHashCode        public override int GetHashCode()        {            // The Y value should always come first in any kind of sorting, comparison or hashing operations            // followed by X and then Z because typical loops would start with the Y value.            return (this.Y.GetHashCode() + this.X.GetHashCode() + this.Z.GetHashCode());        }        #endregion        #region Equals        public bool Equals(Vector obj)        {            // The Y value should always come first in any kind of sorting, comparison or hashing operations            // followed by X and then Z because typical loops would start with the Y value.            if (obj != null)            {                if (obj.Y == this.Y)                {                    if (obj.X == this.X)                    {                        return (obj.Z == this.Z);                    }                }            }            return false;        }        public override bool Equals(object obj)        {            if (obj is Vector)            {                return this.Equals((Vector)obj);            }            return base.Equals(obj);        }        #endregion        #region ToString        public override string ToString()        {            return String.Format("{0},{1},{2}", _x, _y, _z);        }        public string ToString(bool forDisplay)        {            if (forDisplay)            {                return String.Format("X = {0}, Y = {1}, Z = {2}", _x, _y, _z);            }            return this.ToString();        }        #endregion        #region Operators        public static Vector operator +(Vector v1, Vector v2)        {            return new Vector(v1.X + v2.X, v1.Y + v2.Y, v1.Z + v2.Z);        }        public static Vector operator -(Vector v1, Vector v2)        {            return new Vector(v1.X - v2.X, v1.Y - v2.Y, v1.Z - v2.Z);        }        public static bool operator ==(Vector v1, Vector v2)        {            return v1.Equals(v2);        }        public static bool operator !=(Vector v1, Vector v2)        {            return (!v1.Equals(v2));        }        public static bool operator >=(Vector v1, Vector v2)        {            // The Y value should always come first in any kind of sorting, comparison or hashing operations            // followed by X and then Z because typical loops would start with the Y value.            if (v1.Y >= v2.Y)            {                if (v1.X >= v2.X)                {                    return (v1.Z >= v2.Z);                }            }            return false;        }        public static bool operator <=(Vector v1, Vector v2)        {            // The Y value should always come first in any kind of sorting, comparison or hashing operations            // followed by X and then Z because typical loops would start with the Y value.            if (v1.Y <= v2.Y)            {                if (v1.X <= v2.X)                {                    return (v1.Z <= v2.Z);                }            }            return false;        }        #endregion        #region Properties        private int _x;        public int X        {            get { return _x; }            set { _x = value; }        }        private int _y;        public int Y        {            get { return _y; }            set { _y = value; }        }        private int _z;        public int Z        {            get { return _z; }            set { _z = value; }        }        #endregion        #region IEquatable<Vector> Members        bool IEquatable<Vector>.Equals(Vector other)        {            return this.Equals(other);        }        #endregion    }

7/16/2007


BlogEngine.NET

I have to say that BlogEngine.NET is a great blog utility. I was using .Text for a long time and just never wanted to go through the conversion to Community Server. I just did not need a community, I just wanted a blog. I reviewed a bunch of different blog engines out there and really liked all the features of BlogEngine.NET. If you are looking to setup a blog and want to have control over your data store using providers and have all the latest features like tagging, auto pings, etc. then snag BlogEngine.NET.

7/13/2007


JavaScript Libraries

I found some good javascript libraries for PBBG or any kind of web development.

This one is a graphics library useful for drawing vector graphics to the browser. Works great, just remember to set the position:relative value of the DIV you wish to draw in. :)

DHTML: Draw Line, Ellipse, Oval, Circle, Polyline, Polygon, Triangle with JavaScript

This site has a collection of useful libraries for all manner of web related activities.

Javascript Toolbox: Reusable Libraries And Scripts Plus Information

7/12/2007


PBBG Mapper

I've been working on a tile mapping tool I call PBBG Mapper here lately while working on my persistent browser based games. The tool is a Windows based application written in C# 2.0 and will include some plugin functionality so that other developers can extend it an customize it. The progam saves all tile maps as XML for easy portability and works pretty well. I will post some screenshots once I get it into a full working version.

7/10/2007


The Future of Persistent Browser Based Games

I've been developing persistent browser based games using ASP.NET and AJAX for a little while now but an emerging technology from Microsoft called Silverlight that I believe will re-define how browser games are built for .NET developers. Silverlight is Microsoft's answer to Flash but with advantages for .NET developers in that I can program C# on the backend and have Silverlight on the front end.

My current PBBG Perenthia will be ASP.NET and AJAX but another game I have in the design phase called Aelerion will feature a Silverlight front end using (hopefully) the same game engine I wrote for Perenthia. 

6/30/2007