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:
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
As I have been working on the Silverlight UI for Perenthia I find myself constantly adding little things here there to provide more information or easier click-to-action type of functionality. The UI has evolved, for the better I hope, over the past few months from my original design as I have discovered something else that needed to be displayed while hooking it into the server. This seems to be the natural progression of things, at least with the way I work. :)
Here is the latest UI screen shot, this one shows my character viewing the items a merchant has for sale, with my inventory items on the left and the ones the merchant offers on the right. I will do a development update again soon, had to backtrack a bit with the UI.
11/29/2008
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
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 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
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:
9/8/2008
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.
9/6/2008
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
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
I ported some code from the Farseer Physics Engine XNA demos that displayed a debug view of the geom vertices used with the Physics Simulator. This is useful if you want to see where your geoms are being rendered to the screen to make sure you have everything in the right place. I know I need this a lot, helps me to understand where I am placing body geoms.
Anyway, here is the method, the _debug variable is just a Canvas I added at the top of the UI. Keep in mind that this method will slow your game WAY down so be sure and use it to test where geoms are positioned and if everything is moving properly. I use a static variable for the PhysicsSimulator, hence the cleverly named Physics.Simulator.
private void DrawVertices() { _debug.Children.Clear(); int verticeCount = 0; for (int i = 0; i < Physics.Simulator.GeomList.Count; i++) { verticeCount = Physics.Simulator.GeomList[i].LocalVertices.Count; for (int j = 0; j < verticeCount; j++) { Line line = new Line(); line.Fill = new SolidColorBrush(Colors.Transparent); line.Stroke = new SolidColorBrush(Colors.Magenta); line.StrokeThickness = 1; if (j < verticeCount - 1) { line.X1 = Physics.Simulator.GeomList[i].WorldVertices[j].X; line.Y1 = Physics.Simulator.GeomList[i].WorldVertices[j].Y; line.X2 = Physics.Simulator.GeomList[i].WorldVertices[j + 1].X; line.Y2 = Physics.Simulator.GeomList[i].WorldVertices[j + 1].Y; } else { line.X1 = Physics.Simulator.GeomList[i].WorldVertices[j].X; line.Y1 = Physics.Simulator.GeomList[i].WorldVertices[j].Y; line.X2 = Physics.Simulator.GeomList[i].WorldVertices[0].X; line.Y2 = Physics.Simulator.GeomList[i].WorldVertices[0].Y; } _debug.Children.Add(line); } } }
8/21/2008
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
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.
7/27/2008
For those unfamiliar with the fantasy role playing genre; quests are simple to complex tasks that players complete in order to advance, get sweet gear, etc. They usually involved raiding a dungeon, slaying some terrible beast and wondering about the countryside trying to find the entrance to the hidden caverns.
What I hope to accomplish with quests is to provide a goal beyond just slaying monsters. Quests will actually move your Character through the game, while training and advancing them along the way.
One of my primary goals with Perenthia is to provide a different game play experience as players mature within the game. The first phases of the game will include some of the normal grind, killing monsters to get better gear, to level up, to kill monsters, etc. This gives the players immediate satisfaction while getting them used to moving around, using commands, spells, equipping items, buying and selling and everything else that goes along with eradication of the demon beasts of the underworld. The second phase of the game will follow a bit of a storyline, requiring players to venture into dungeons and caverns to retrieve special items they will need in order to move into the next phase of the game. While this involves a lot of the same type of situation, you have a reason to go into the dungeon and complete it, rather than just killing 10 rats because some dude is freaking out. The third phase will require the player to use the items created from the previous phase to solve puzzles, riddles and the like, which will take them into the next phase. The fourth phase will require the player to purchase some form of transportation in order to visit very far off places and located individuals who can further their progress with quests of their own. This will continue in this manner for the remainder of the game. I currently have 4 phases planned and ideas for 3 more that I hope to get written down and planned out before too long, although I will probably wait until after the alpha an beta test phases.
I want to try and provide a fun game and I feel that doing this phased game progress could provide an enjoyable gaming experience. I know when playing other games of the genre I usually burn out in the high levels because I worked so hard to get there and it is just more of the same. I would also like the world to progress forward around the players so that new challenges are introduced to both high and low level characters that are in keeping with the overall storyline. I want players to have as much fun playing the game as I am having making it. :)
I hope to provide a robust questing system that can handle these phases and still maintain a clean and easy way for players to know what to do next. For the alpha I have planned out some quests that take you around the alpha starting area and should advance the alpha players enough so that the next area will introduce another phase in the game experience.
I will do another development update after this weekend.
7/23/2008
I am not quite ready to post the screen shots of the actual game UI, still need to do some cleanup. So, since I promised I would post some screen shots here are a few of my world builder tool. This is a windows application that just reads and writes to the database.
This first screen is the map builder and as you can see I can draw the actual places/rooms in which players will adventure. The map that is displayed will be the Alpha starting map, it consists of a small town, a forest and a small sewer system. The sewer nodes are not displayed because they are one level down.
This is the creature manager window that allows me to create creature "templates", from which actual creature instances can be created and placed onto the map from above. (Using the Add Mobile button).
This is just a shot of the item manager window, displaying some of the items I've added so far, got a long way to go on these. The items also follow the same "template" pattern that the creatures do. I still need to add in support for placing the drop items on Mobiles.
I hope to have the game UI screen shots ready soon.
7/22/2008
I have made so many changes to Perenthia since the alpha/beta release I did last year that I am actually going to take the game back to Alpha and go through the whole process again. Most of the data elements remain the same, the story is the same, the goals, etc. but the server and client pieces have been modified so much its not really the same code base.
Anyway, I am hoping to get a pre-alpha signup form on the site this weekend and get the closed Alpha live by the end of summer.
7/10/2008
I am still plugging along on my games, working mostly on Perenthia and checking out the new stuff in Silverlight 2 beta 2. I want to try and get the Perenthia beta 2 up and running by the middle or end of the summer, free time permitting.
I haven't blogged much lately but I have been working on a lot of stuff. I will try and get some posts together to outline some of the stuff I have taken advantage of in developing Perenthia such as adding files as links in Visual Studio and then using partial classes to separate server and client logic and multi-threading in Silverlight and how to avoid cross thread access, which you will get exceptions on now in SL 2 beta 2. I will also try and get some screen shots of Perenthia, quite a bit has changed since the last screen shot I posted and I would like to get some of the new ones up.
6/10/2008
Rebuilding Pirates! in Silverlight 2 is coming along well. I got it running on my Silverlight game engine I was writing for use with Perenthia, which changed some of the way it worked initially. Anyway, I plan on doing a release once I get a few levels complete. In the mean time here is a screen shot of me testing sailing around.
Since I started over in Silverlight 2 and decided to use my game engine I am having to re-write a good portion of the functionality. I had to re-do movement since some of the structure of events has changed so this screen shot is the ship sailing around.
5/2/2008
The BlogEngine.NET folks have released a security update to BlogEngine. I would recommend downloading it ASAP.
Thanks to Mads for the heads up!
4/28/2008
I thought I would post an image of the map I have been working on for Perenthia. I made this in Photoshop using a variety of brushes and layers. I still have some more that I want to do with it but I am pretty happy with it so far.
4/11/2008
OK, I decided to post a screen shot of the Perenthia interface so far. Keep in mind this is still under development. The main sprite is not the final, just a ripped image from a Flash game called Exile and the background is just a sample I created so I could tell if the camera was following my player.
4/9/2008
After surviving the flu I am back to working on Perenthia. I did some work on Pirates but I am still having some positioning issues that I can't seem to get worked out and may have to rebuild a good portion of the game.
I should have a screen shot or two of the progress on Perenthia in another few days, I will see how it goes.
I have a good portion or rather the majority of the Perenthia Server finished and have been working on the Silverlight front end. I am still not sure whether or not I will be using sockets for communication or a custom IHttpHandler implementation.
4/8/2008
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
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
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
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
I got the chat portion of the game engine working with Silverlight 2.0 tonight. I am just using web services to send the commands back and forth for now. Once I complete incorporating the other portions of the engine into Silverlight I will start playing around with sockets.
3/7/2008
3/5/2008
For my PBBG Engine I started off programming it all for the web and AJAX. Recently I added support for sockets by porting over my socket server code I wrote for a Flash server and incorporating the code into the PBBG Engine. In a previous post about my PBBG Engine Architecture I outlined my plans for how the socket and web pieces would work together. I spent some time today refining my command pipeline and server protocol for the socket side of the engine and will probably utilize that on the web side as well.
I decided on the following format for the data packets that will go to and from the server and client. Since it is just bytes going back and forth and I wanted to keep the data streams as small as possible I decided to abandon the string based command protocol and use this instead:
The first 2 bytes of the packet will be used to store an Int16 (short) value that indicates the command to execute. I have an event being raised from the game engine that allows the implementing libraries to set the game commands. This was a Dictionary<string, ICommand> but I am going to change it to a Dictionary<short, ICommand>. That will save me some bytes back and forth without passing the command names. I can have the clientsend the proper short value for the command typed into the console or just have the links and events in the client supply the proper short value.
The next 4 bytes will be the integer length value of the message. This will allow me to know when this message ends and the next one begins.
All the bytes following up to the length value will be the actual message sent to the client or sent to the server.
The server is already validating commands, I will just need to modify to validate these bytes and watch for buffer over runs. I still need to work out validating that a logged in user is sending the information but I will work out something, maybe have the client send the encrypted authentication key to the server after a connection is opened but before commands can be processed.
3/2/2008
Thought I would go ahead and post a little about the architecture I have set up for my PBBG engine so far. Basically, the engine is a library that other projects can reference to make building PBBGs a little easier. All of the logic for handling incoming requests and parsing commands is contained within the engine. My PBBG engine is kind of like a MUD driver, it requires a MUDLib to define objects, persist data, etc. Some interfaces are defined within the engine library to make it extensible while it also contains base class implementations of those interfaces to make building on top of working logic possible. When the engine starts up it raises a set of events and when it processes commands it also raises events to allow the implementing application to control loading of commands, determining the server implementation and creating command instances.
Not the best diagram in the world but kind of gives you the idea of how it works. The column down the middle are the interfaces defined by then engine, which also define base classes, except for the CommandManager which is just a static class for processing commands.
In addition to the classes here are the Verb abstract class, the ICommand interface and the Command<Verb> instance for creating commands from Verbs. The CommandManager raises an event when it finds the command text that allows an external library to set the ICommand instance that will execute the current command. When the engine starts up though it raises an event that will allow the implementation library to set all the commands into memory rather than creating a new instance with each command.
The IServer interface defines an IConnectionManager property where connections into the server are managed, either socket or http.
On the WEB side, which is actually a namespace ".Web"; the "Server" instance writes out a JavaScript reference to an embedded js file containing the script required to submit commands to the engine using AJAX. A separate RequestHandler class actually parses the input from the AJAX call and then attempts to locate or create an IConnection instance on the Server's IConnectionManager instance. The Server in this namespace actually sends the command to the CommandManager.
On the NET side, which is actually a namespace ".Net"; the "Server" instance actually contains the socket used to listen for incoming connections on a port specified during startup. From there the individual IConnection instances contained in the IConnectionManager instance handle processing the input and formatting it into a command. The IConnection instance contains the connected socket and will send the command to the CommandManager.
Both IConnection instances in both namespaces allow messages to be sent to the client along with custom data such as stats, gold, map data, etc. The difference between the two is that the WEB instance sends all of this back as JSON in the HTTP response and the NET instance sends the messages down the socket as they are added to the IConnection instance and then sends the additional data once the command completes execution.
That is all I have completed at this point on the engine side, it processes commands from the web and the implementation library sitting on top of it handles the commands and persists the data. There is plenty of work on the implementation library side still yet to be completed.
2/26/2008
I haven't decided yet but am considering using Silverlight 2.0 with the next release of Perenthia. I've completed a good portion of my PBBG engine that will drive Perenthia but want to be able to take advantage of some the features of Silverlight 2.0 to provide better user experience. One of the features that really interests me is the socket support for 2.0. Once I get the 2.0 beta 1 bits I am going to start playing around with running my PBBG engine on a socket server application I wrote for Flash. If all goes well I should be able to provide some better multiplayer features in Perenthia such as real time chat and possibly player vs player combat.
Perenthia will still primarily be a text-based game but Silverlight could allow me to bridge the gap between web and client software.
2/24/2008
From the moment you came into our home you were always daddy's buddy. We would sit in my chair and watch TV and you always helped me out in the garage when I was working on something for mommy. | |
You loved to be "The Ranger" when we went out back or to the park. You would run ahead on trails; exploring and taking in all the sights and smells of the forest. | |
You and Sydney loved "Playtime" out back, especially in the snow. She loved chasing you around the yard and loved being chased. | |
You had a gentle soul and an overabundance of patience with the smaller residents of our home. Always easy going and always happy to have a comfortable place to nap. | |
I know I will see you again someday but until that time I will hold your memory in my heart and all the joy you brought to our home. Rest in peace Clifford, you will always be "daddy's bestest buddy". |
2/19/2008
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
Perenthia currently provides "wilderness" areas where monsters roam around waiting to attack players who wander beyond the safety of the towns. I had originally wanted the wilderness to auto generate instead of how it works now where all the wilderness "tiles" are stored in the database. This caused over 150,000 records to have to be entered into the table and really limits expanding the word since I have to basically draw all the tiles.
For the Perenthia Beta 2 I worked out a "Regions" table to store a rectangle x,y,z value. The Regions table also stores the min and max levels required to venture into the region. Towns located within regions also carry a safe indicator so that monsters will not auto generate. Using this concept I only have to create the towns or places of importance and can let the "wilderness" be auto generated.
The auto generated wilderness is infinite in that each wilderness place visited is created and stored on the player record. Wilderness is also exclusive to the player so that monsters generated will be available only to the adventurer. A lot of games have done this with dungeon instances and I thought it was a good idea to implement in Perenthia. There will be ways to interact with others and I still have some work to do to handle group play but wilderness is intended for solo players. Now, given that wilderness is infinite I don't want players getting lost so, the last non-wilderness place they visit will be stored on their player record and if they log out and log back in they will be reset to the previous non-wilderness place. It's kind of a fast way to cheat your way back to town if you get into trouble but I am OK with that. I would rather players leap back to town before their character dies than wander aimlessly around thousands of tiles from civilization. I want the wilderness adventuring to be a fun way to earn gold, not a potential for grief.
Quests may require adventuring through the wilderness to find hidden places so logging out when you are in trouble will cause your quest to take even longer. I think this will be a good trade off because quests will offer greater rewards than adventuring and should require a higher level of mastery.
2/8/2008
Spent some time today working on the UI for Perenthia. For now it will remain AJAX based but in the future I will probably add a Silverlight front end as well. I will post some screenshots of it soon, I want to get the layout complete first.
I also spent some time working through my data model for quests. I still need to do some work but I think it will be flexible enough to allow for adding all manner of quests and adventures.
2/1/2008
In my previous post Memory Management I was talking about storing character and room data in server memory.
For the Character memory management I basically just use a DateTime value that gets updated each time a request is made by the connected player. If the DateTime exceeds a certain limit such as 20 minutes then the character data is saved and removed from memory. If the player just has a long pause between requests their character data will be reloaded if they make another request after the cleanup.
For the Room or Place data I've though about doing the same thing, just store a DateTime value and update it whenever a player performs an action in the room. I am not sure this is the best approach since the only commands that reference the Place object are movement, looking, some inventory and buying commands. Other actions such as casting spells, viewing stats, private chat, etc. do not reference the Place object. Of course, I guess if you are not accessing the Place object no need to have it in memory but once a player attempts to move again I would need to reload the Place. I might just maintain a list in the Place object of all the players currently located there and only remove the object once those references have been removed. I'll try a few things and record what I find.
1/30/2008
For Perenthia I am planning on utilizing server memory as much as possible without crippling the web server. To do that I am going to be maintaining player data and room data in memory for fast access and quicker responses from the interface.
The basic idea is that players login, choose a character and begin playing. This character information will be loaded into memory and persisted to the database when the data is changed, such as when commands are executed. As players move around the rooms they enter will also be persisted into memory to make loading and re-loading rooms faster.
The trick will be effective management of these resources. Character data will be removed after inactivity but I haven't figured out a good scheme for removal of the room data. Hope to have that working before too long though.
Anyway, that is the plan, we'll see how it works out. :)
1/29/2008
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
Andy Beaulieu has posted another great article about Advanced Physics with Silverlight and Farseer. His demo allows you to draw objects that are then associated with a Farseer Physics Body. Andy mentions me in his post and references the code I created to render a Path based on Vertices. Andy's first tutorial "Getting Started with Farseer Physics and Silverlight" was key in helping me understand how to get Farseer working with Silverlight.
Thanks Andy!
1/24/2008
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:
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
1/17/2008
I'm going to put up the Silverlight Pirates prototype in another day or so. The prototype will only include one enemy ship. I was going to get the port menus completed so you could buy ammo and repair your ship but I think I am going to wait until the Silverlight 2.0 beta is released.
I'm not competely happy with the enemy ship AI but I have time to work it out. Once I finish the prototype I am going to wait for Silverlight 2.0 to finish out the game.
1/13/2008
The first screenshot is just sailing around. I was working on positioning the cannons and making sure the mini map was working as planned.
The second screenshot is me docked at the port of this island. Ports will be places to repair your ship, buy cannons, cannon balls and rum.
1/2/2008