Now that we have a syntax for defining state aware objects, we need the code that wires these things up for us automatically. This consists of the following:
StateMethod AttributeStateMethodHandler AttributeAStateObject base class to wire up our methodsStateMethod AttributeThis attribute simply marks the method as a state aware method. As previously illustrated, there is a lot of manual footwork to get all the plumbing in place for a method marked as such. I’m still trying to figure out whether I can simplify that some more and let the baseclass do the work. The other option is a code generator that spits out the appropriate block into separate file as a partial class definition.
The method delegate argument that the construtor takes is the delegate that the method calls to get its work done.
namespace Droog.Stately { [AttributeUsage(AttributeTargets.Method)] public class StateMethod : Attribute { string methodDelegateName; public StateMethod(string methodDelegateName) { this.methodDelegateName = methodDelegateName; } public string MethodDelegateName { get { return methodDelegateName; } } } }
StateMethodHandler AttributeThis attribute tags methods as handlers for state methods. As such they must have the same signature (although they can be public, private, protected, whatever) as the actual method, or more to the point, the same signature as the delegate that the state method calls. It’s conceivable that the state method adds another argument, although i can’t really see why.
There are two types of StateMethodHandlers: Default and state specific.
namespace Droog.Stately { [AttributeUsage(AttributeTargets.Method)] public class StateMethodHandler : Attribute { string methodName; byte state; bool isDefault; public StateMethodHandler(string methodName, byte state) { this.methodName = methodName; this.state = state; this.isDefault = false; } public StateMethodHandler(string methodName) { this.methodName = methodName; this.isDefault = true; } public bool IsDefault { get { return isDefault; } } public string MethodName { get { return methodName; } } public byte State { get { return state; } } } }
AStateObject abstract base classThis is a very simplistic implementation. Note that it assumes that the derived class does everything right. I.e. if you forgot the default handler it would die unceremonously, if you had multiple handlers declared for the same things, it would just take the last one, etc.
It also handles only Methods. It should probably be extended to at least properties. But extending it to event handlers could be pretty powerful as well.
The mechanism tries to do as much as possible at first instantiation to both reduce the overhead and allow it to fail early instead of at call time, should the definition be wrong. If performance was a problem, the creation of the delegates could be moved to instantiation as well, and done at each instantiation. It would create an increased overhead for instantiation and a larger memory footprint, so it’s a modification that needs to be dictated by use.
namespace Droog.Stately { public class AStateObject { /// <summary> /// Internal collection for storing the mappings for state delegation. /// </summary> private class StateDelegateMap { MethodInfo[] stateDelegateMethodInfo = new MethodInfo[10]; MethodInfo invokingMethodInfo; FieldInfo delegateFieldInfo; MethodInfo delegateMethodInfo; public MethodInfo this[byte idx] { get { CheckBounds(idx); return stateDelegateMethodInfo[idx]; } set { CheckBounds(idx); stateDelegateMethodInfo[idx] = value; } } private void CheckBounds(byte idx) { if (idx >= stateDelegateMethodInfo.Length) { MethodInfo[] newStorage = new MethodInfo[idx + 1]; stateDelegateMethodInfo.CopyTo(newStorage, 0); stateDelegateMethodInfo = newStorage; } } public MethodInfo InvokingMethodInfo { get { return invokingMethodInfo; } set { invokingMethodInfo = value; } } public FieldInfo DelegateFieldInfo { get { return delegateFieldInfo; } set { delegateFieldInfo = value; } } public MethodInfo DefaultDelegateMethodInfo { get { return delegateMethodInfo; } set { delegateMethodInfo = value; } } } /// <summary> /// Internal Dictionary for mapping methods to delegation maps. /// This is really just a shorthand for a typed dictionary /// </summary> private class StateMethodMap : Dictionary<string, StateDelegateMap> { } /// <summary> /// Static storage for our mappings. Base class stores them all /// and lazy-initialized them the first time an instance of /// a Type is created /// </summary> static Dictionary<Type, StateMethodMap> stateDelegationMap = new Dictionary<Type, StateMethodMap>(); protected byte internalStateId; protected AStateObject() { InitStateHandlers(); } /// <summary> /// The internal representation of states as bytes. It's up /// to the deriving class to decide whether an Accessor should /// be exposed and that datatype (recommeneded enum) is used /// to represent the class's states. /// </summary> protected byte InternalStateId { get { return internalStateId; } set { internalStateId = value; InitState(); } } /// <summary> /// This creates the static mapping of state methods to possible /// delegates. It does not create delegates because they are tied /// to instances and therefore have to be created for each /// instance. This creation happens in <see cref="InitState"/> /// </summary> private void InitStateHandlers() { Type t = GetType(); if (stateDelegationMap.ContainsKey(t)) { // we already generated this map, so we can skip this step return; } MethodInfo[] methods = t.GetMethods( BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public); StateMethodMap methodMap = new StateMethodMap(); stateDelegationMap.Add(t, methodMap); // find all state methods for this type foreach (MethodInfo m in methods) { foreach (StateMethod attr in m.GetCustomAttributes(typeof(StateMethod), true)) { FieldInfo delegateField = t.GetField( attr.MethodDelegateName, BindingFlags.Instance | BindingFlags.NonPublic); StateDelegateMap delegateMap = new StateDelegateMap(); delegateMap.InvokingMethodInfo = m; delegateMap.DelegateFieldInfo = delegateField; methodMap.Add(m.Name, delegateMap); } } // find all state delegates for this type foreach (MethodInfo m in methods) { foreach (StateMethodHandler attr in m.GetCustomAttributes(typeof(StateMethodHandler), true)) { if (methodMap.ContainsKey(attr.MethodName)) { StateDelegateMap delegateMap = methodMap[attr.MethodName]; if (attr.IsDefault) { // default method handler delegateMap.DefaultDelegateMethodInfo = m; } else { // state specific method handler delegateMap[attr.State] = m; } } } } } /// <summary> /// This gets called every time we change the stateId and creates the /// appropriate delegates from the static mapping /// </summary> private void InitState() { StateMethodMap methodMap = stateDelegationMap[GetType()]; foreach (StateDelegateMap delegateMap in methodMap.Values) { MethodInfo delegateMethodInfo = null; if (delegateMap[InternalStateId] != null) { // got a state specific method, let's map it delegateMethodInfo = delegateMap[InternalStateId]; } else { // no state specific method, use the default delegateMethodInfo = delegateMap.DefaultDelegateMethodInfo; } Delegate stateDelegate = Delegate.CreateDelegate( delegateMap.DelegateFieldInfo.FieldType, this, delegateMethodInfo); delegateMap.DelegateFieldInfo.SetValue(this, stateDelegate); } } } }
Now we have a fairly simple framework to create objects in our simulation that can alter their behavior depending on their state. As I start writing code with this framework, it’ll probably get fleshed out a bit more and I’ll find out whether the design is sustainable, since right now it’s a clean-room design more than anything else.
Having defined the approach for the stateful framework, let’s tackle the design goals:
Ideally we would like our code so look something like this:
public class Bot : AStateObject { public virtual void Touch() { //default implementation } virtual state(BotStates.Idle) { public virtual void Touch() { // Idle specific implementation } } } public class GuardBot : Bot { public override void Touch() { // override of default implementation } virtual state(BotStates.Guarding) { public virtual void Touch() { // Guarding specific implementation } } }
Since that’s not possible without altering the language, let’s see what we can do to approximate that behavior.
I’m just going to get this out of the way first. For the initial version, i’m handling the serializable portion by simple making the abstract base class ISerializable and putting the burden on the subclass to be serializable. .NET already has great facilities for serializing and it won’t be hard to retrofit the base class with the methods for automating the saving of state later, as long as we just insist on ISerializable.
Since we can’t just create new keywords in the language, what can we do? Well, to start, a large part of the motivation behind stateful programming in UnrealScript can be alleviated by using events. I.e. a common example is a Touch() function. I.e. the object responds to an external event. Well, instead of creating Touch() in different parts of the code, you could just have a Touched event in the class and subscribe and unsubscribe different handlers depending on the state.
While events will play a large part of any complex game logic, the approach for this framework doesn’t use events. Instead we use the language feature that makes events possible, i.e. Delegates. Sure we could have a case statement in each stateful Method that branches on the state, but that makes for error-prone and hard to maintain code. Instead each stateful method could simply call a state handler, which is initialized with the appropriate delegate on state change. Now we have a single set of plumbing for setting up all code paths for each state.
But that still leaves a lot of code at state change to attach the right delegates to each method per state change. Instead of doing this manually, we can use Attributes to identify the appropriate handlers and Reflection to discover state methods and the matching state specific handlers.
The resulting code looks like this:
public class Bot : AStateObject { // Start: Definition of the Stateful method protected delegate void TouchDelegate(); protected TouchDelegate touchDelegate; [StateMethod("touchDelegate")] public void Touch() { touchDelegate(); } // End: Definition of the Stateful method // Tagged as default handler [StateMethodHandler("Touch")] protected virtual void Default_Touch() { //default implementation } // Tagged as Idle state handler [StateMethodHandler("Touch", (byte)BotStates.Idle)] protected virtual void Idle_Touch() { // Idle specific implementation } } public class GuardBot : Bot { // normal override mechanism works in our state code as well protected override void Default_Touch() { // override of default implementation } // Tagged as Idle state handler [StateMethodHandler("Touch", (byte)BotStates.Guarding)] protected virtual void Guarding_Touch() { // Guarding specific implementation } }
This covers pretty much all the bases, but let me just point out the ones specific to state handling.
First we have to separate the state method from its implemenation and then tag it as such. That’s how we get:
protected delegate void TouchDelegate(); protected TouchDelegate touchDelegate; [StateMethod("touchDelegate")] public void Touch() { touchDelegate(); }
Clearly a lot of busy work for a simple method. But it’s busy work a code generator could easily move to a partial class. The StateMethod attribute both tags the method and provides information to our framework which delegate needs to be wired up.
Now we’re free to define our state specific implemenation:
[StateMethodHandler("Touch", (byte)BotStates.Idle)] protected virtual void Idle_Touch() { // Idle specific implementation }
The name Idle_Touch is convention and does not affect execution. Its purpose serves both to make sure we get a unique method name and that its easily recognizable by someone trying to subclass the class. It’s the StateMethodHandler attribute that tells the framework that the method is the Idle handler for Touch(). Why are we casting our state enum to byte? I’ll cover that when I talk about type-safe states.
Since our state specific methods are really just normal methods that have been tagged as handlers, inheritance proceeds in the regular fashion. Marked as virtual, any subclass can override any state handler. Since Intellisense won’t tell you which method is tagged to what state, the naming convention of <StateName>_<StateMethodName> once again becomes useful.
In addition to the StateMethodHandler(string methodName, byte state) constructor, there also exists StateMethodHandler(string methodName). Methods tagged as such are the default handlers:
[StateMethodHandler("Touch")] protected virtual void Default_Touch() { //default implementation }
This last goal is a bit tricky and the solution is only somewhat satisfying. My desire is to have states that are type-safe and discoverable enumerations of states. Naturally enums come to mind. But there are a number of disadvantages to enums:
The first we address by hiding state in the bowels of the framework simply as a byte and requiring the subclasses to define their own state enum/code> and casting them. Hence the byte/code> cast in the StateMethodHandler attribute. (If you have more than 256 states, you probably have something a bit more complex than should be handled on simple single valued state changes).
The second is a trade-off for being a compile-time checked framework. I’d rather have my enums to be machine-discoverable and fixed than some dynamic value that only becomes meaningful at runtime. I’ll go out on a limb and say that if you are subclassing an existing member of your game’s simulation, it should only contain the existing states because the simulation is unlikely to ever set your new states. If you do need more states, maybe your class isn’t a subclass at all. You probably have two game objects that share common code, but different states, so they instead have a common ancestor (which doesn’t define states). Then your two classes become siblings, each defining their own states.
If you really must extend the states of your base class, you can still handle the last case by simply marking your override of the State accessor as new and taking on the responsibility of doing all the casting.
So that covers the syntax of the implemenentation for a simple stateful framework. Next I’ll cover the guts that implements that syntax in State-aware programming in C#, part III.
This is going to be a multi-parter, simply to allow for some organizing and avoid the giant scrolling page.
One thing I’ve always liked about UnrealScript was its inclusion of states as a first class citizen in the language. It’s something that just made sense for the problem that UnrealScript tries to solve. After doing some Unreal modding, I thought about building a game scripting language of my own and contacted Tim Sweeney for his advice. I don’t know if he just didn’t get as much mail as he does now, or if he is just one of the incredibly helpful guys in the industry, but he got back to me in a day and gave me some good advice on language design and generally told me to bone up on the Java Virtual Machine design docs — which is where he got a lot of his ideas for UnrealScript.
While fun, that project didn’t get very far, but I still keep coming back to first-class stateful programming from time to time. Now that XNA is out seems like the perfect time to revisit this, because writing your game logic in .NET would almost certainly benefit from the approach of UnrealScript.
There are three paths of approach to this problem that I think are all viable:
state() vs. state keywords and the lack of static methods. But it would be a major undertaking, so it’s likely to be the ultimate, not initial, approach.
So what are my design goals for this stateful C# framework?
Bot is Idle a different code path for Hit() should be called than if the Bot was in state Attacking.
See State-aware programming in C#, part II for the syntax that implements this design.