<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="http://weblogs.asp.net/utility/FeedStylesheets/rss.xsl" media="screen"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:wfw="http://wellformedweb.org/CommentAPI/"><channel><title>Justin Rogers : Plug-In Framework</title><link>http://weblogs.asp.net/justin_rogers/archive/tags/Plug-In+Framework/default.aspx</link><description>Tags: Plug-In Framework</description><dc:language>en</dc:language><generator>CommunityServer 2007 SP1 (Build: 20510.895)</generator><item><title>Implementing AI wars where code is the primary asset. (focus on Terrarium)</title><link>http://weblogs.asp.net/justin_rogers/archive/2004/09/28/235153.aspx</link><pubDate>Tue, 28 Sep 2004 11:44:00 GMT</pubDate><guid isPermaLink="false">c06e2b9d-981a-45b4-a55f-ab0d8bbfdc1c:235153</guid><dc:creator>Justin Rogers</dc:creator><author>Justin Rogers</author><slash:comments>4</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://weblogs.asp.net/justin_rogers/rsscomments.aspx?PostID=235153</wfw:commentRss><comments>http://weblogs.asp.net/justin_rogers/archive/2004/09/28/235153.aspx#comments</comments><description>&lt;p&gt;The one thing I've taken away from the .NET Terrarium in terms of AI development is that players value their code over everything else. Protecting code from being viewed by other users is extremely difficult and hard to implement. After all, the focus of a distributed AI game is to pass the intelligence all over the place, to multiple machines, so it can operate across a large number of machines and potentially become the dominant system. If the code leaves your machine, it becomes possible that other users are going to steal your code, plain and simple. You can try trusting some other environments, perhaps a central server that hosts your code, but even then your code exists somewhere that you aren't in charge of basic protection.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Code as an Asset:&lt;br /&gt;&lt;/strong&gt;The development of new AI in the Terrarium was driven by the decompilation and examination of existing code. To show you how pervasive code dissassembly was, we introduced an Animal Farm, or location where users could share code. Even with this central repository of code, we constantly saw users working in ILDasm and ILAsm to produce verbatim copies of existing AI. The existing code as considered an asset by not only the developer, but also by the rest of the players. Controlling that asset meant possibly controlling the Terrarium's game world. Code is such an asset that users will go to any length to get their hands on it.&lt;/p&gt; &lt;p&gt;What is the best way to protect your assets? Well, don't create a situation where your assets fall into the hands of other users. Why do you think ASP .NET is so popular and Windows Forms is still riding coat tails? People can protect their code assets when it comes to server side coding, but find it much harder to protect code on the client. This is a common problem. So if you want code protection, you need to host your assembly either at a central server level or from your own machine. If you follow these guidelines then users will be more likely to participate and generate professional level AI while still protecting their investment in programming the code.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Execution Time as an Asset:&lt;br /&gt;&lt;/strong&gt;We've identified three places to run the AI. You can operate at the local client level, at the server level, or at a distributed client level. Identifying execution time as an asset, load balancing across the greater distributed network of all clients is going to yield the best results in terms of execution time. The resources are apparently limitless as long as you can maintain a large user base. In reality, users aren't always connected. The only 100% up-time resource is going to be your server. You can run AI on the server, but then you run the risk of not having enough execution time to schedule the AI as the game becomes more and more popular. You can offload this risk by allowing users to host their own personal servers and implementing relatively simple queueing routines.&lt;/p&gt; &lt;p&gt;The most limited location to run the AI is on the local client. Each user that produces AI hosts a networkable interface&amp;nbsp;through which their AI can operate. This is the most resource consumptive option because it entails replicating a large amount of game state down to the local client and the propagation of AI commands back up to wherever the game is being hosted. Your AI is bound by the amount of processor time you want to give it. Further, you can only host a single instance of your AI at a time (potentially more in a more robust hosting environment).&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Up-Time as an Asset:&lt;br /&gt;&lt;/strong&gt;Looking at our three models again, the natural next step is examining up-time. If you are programming an AI it is truly an investment of time and effort, but should it also be an investment of your own computer time? In the distributed model it up-time becomes a non-issue as long as the server is available to server AI assemblies down to distributed clients that do all of the processing. The server model is fairly identical in that your AI is playable as long as the server is up and people are selecting it as an opponent. The problem with local client interfaces is that your AI is only around as long as you are. You have to maintain an interface for people to connect through.&lt;/p&gt; &lt;p&gt;Can we solve the up-time problem for local only hosting? Yes, if we trust someone else to host our code for us. This is a sticky point, but we can share a hosting location with a number of friends, family, colleagues where we each host an AI off of the same box. This box might be a web server or similar online resource, and once the AI is loaded, it now acts as an AI interface in addition to it's other roles. A third option is the pay for play option where you AI is hosted by the game originator for a small maintenance fee proportional to keeping their server up and running with your AI on it.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Penalties:&lt;br /&gt;&lt;/strong&gt;This is really important and something a lot of people don't think about. How many different ways can code hose a machine? Plenty right? You can get stuck in an infinite loop and never return, you can run a machine out of memory, you can screw up a static initializer, you write finalizers in your code to hang the GC. Who should be penalized for bad code? If you allow AI to run on the server or distributed clients then everyone is going to pay the price for bad code. Forcing code to run at the local client level means only the original AI developer is going to pay the penalty for bad code.&lt;/p&gt; &lt;p&gt;Can you get around all of those issues in server or distributed hosting? Sure, but I offer the following advice. Fixing the hosting problems comes in the form of adding constraints. These constraints are based on usage scenarios and known patterns of attack. They leverage existing code protection features as much as possible. In the end, you have a system capable of doing nearly all of the things the original system could do, minus a select few things that are considered unsafe. From the other end of the spectrum, you can explicitly enable features one by one. This direction is safer, but much more limited. It is akin to a custom scripting language because you have full control over what the underlying code is capable of. .NET doesn't currently offer good facilities to make this a possibility. You can't explicitly enable access to only certain classes, and disable all other access. You can't hook into the memory management functionality and suppress creation of objects once the component has reached it's limits. They may be available as Whidbey roles around in the form of hosting protections, but that will be a code-path much more difficult to code than the average .NET programmer will be able to handle.&lt;/p&gt; &lt;p&gt;At the end of the day the penalties should probably be placed right back on the AI developer. In a real game, AI developers are just as responsible for bugs as the rest of the development team. In fact, if there is a bug in the AI, it often takes down the entire game. In a developer game the model is switched because the AI now becomes an unknown and the AI developer may not bear the brunt of poorly written code. In fact, there are issues with developer code in the Terrarium that only manifest after the code has traversed several machines and is widely available when an odd code-path is run through.&lt;/p&gt; &lt;p&gt;I'm leaning more and more towards a local client hosting model for developer based AI games for a number of reasons. I think I've mentioned the potential network models that allow AI and Networked players to operate almost transparently by taking advantage of a network protocol. Now stack the asset protection on top of that along with the ability to remove advanced code hosting features and you have yourself a definite winner.&lt;/p&gt;&lt;img src="http://weblogs.asp.net/aggbug.aspx?PostID=235153" width="1" height="1"&gt;</description><category domain="http://weblogs.asp.net/justin_rogers/archive/tags/Terrarium/default.aspx">Terrarium</category><category domain="http://weblogs.asp.net/justin_rogers/archive/tags/Games4+.NET/default.aspx">Games4 .NET</category><category domain="http://weblogs.asp.net/justin_rogers/archive/tags/Security/default.aspx">Security</category><category domain="http://weblogs.asp.net/justin_rogers/archive/tags/Plug-In+Framework/default.aspx">Plug-In Framework</category><category domain="http://weblogs.asp.net/justin_rogers/archive/tags/Artificial+Intelligence/default.aspx">Artificial Intelligence</category></item><item><title>Multi-cast delegates are potential trojan horses for protected eventing...</title><link>http://weblogs.asp.net/justin_rogers/archive/2004/09/22/233226.aspx</link><pubDate>Thu, 23 Sep 2004 06:42:00 GMT</pubDate><guid isPermaLink="false">c06e2b9d-981a-45b4-a55f-ab0d8bbfdc1c:233226</guid><dc:creator>Justin Rogers</dc:creator><author>Justin Rogers</author><slash:comments>2</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://weblogs.asp.net/justin_rogers/rsscomments.aspx?PostID=233226</wfw:commentRss><comments>http://weblogs.asp.net/justin_rogers/archive/2004/09/22/233226.aspx#comments</comments><description>&lt;p&gt;I posted on some security options for eventing when you are using custom storage. While I stopped short of full examining the potential of the various systems, I also stopped short on pointing out some additional security concerns. Here is the previous posting: &lt;a id="_fb93f261f3475753_HomePageDays_DaysList__ctl0_DayItem_DayList__ctl0_TitleUrl" href="/justin_rogers/archive/2004/09/22/233128.aspx"&gt;Some security considerations for systems with events.&lt;/a&gt;&lt;/p&gt; &lt;p&gt;The offending pointer was in regards to protecting the event model through type checking the Target of the delegates before adding them. Well, the code really isn't as accurate as it could be. What you have to realize is that all delegates defined in C# are multi-cast delegates. What in the hell is a multi-cast delegate? Well, it is a linked list of delegates with each containing a pointer ot the previous one. A reverse linked list would be a good way to describe it.&lt;/p&gt; &lt;p&gt;Why do they reverse it? Well, think about that for a minute. The most common way of combining delegates is to add a single new delegate to a long list of existing delegates. By taking the new delegate and setting its previous pointer to the existing delegate list, you've effectively reduced appending a delegate to a single operation.&lt;/p&gt; &lt;p&gt;Then how in the hell do you execute a reversed list in the proper order? Well, they bash the stack in order to make it work by recursively calling the invocation method back up to the beginning of the list. Once all of the methods have executed the stack unwinds down to the last entry and they are done. They also have to order the list correctly for GetInvocationList. For this they run through and get an accurate count, then fill the array from last to first.&lt;/p&gt; &lt;p&gt;So what in the hell did I do wrong? Well, by checking the &lt;u&gt;Target&lt;/u&gt; I am checking the type of the last added delegate, but I'm not checking the types of all of the delegates. With that said, a user can hide multiple delegates that are invalid on the front end of a valid delegate. This would look something like:&lt;/p&gt; &lt;blockquote dir="ltr" style="MARGIN-RIGHT: 0px"&gt; &lt;blockquote dir="ltr" style="MARGIN-RIGHT: 0px"&gt; &lt;p&gt;EventHandler handler = new EventHandler(this.Foo);&lt;br /&gt;handler = (EventHandler) Delegate.Combine(handler, new EventHandler(allow.Foo));&lt;br /&gt;cea.Woot += handler;&lt;/p&gt;&lt;/blockquote&gt;&lt;/blockquote&gt; &lt;p dir="ltr"&gt;The code is incomplete above, but the idea is to create an event handler pointing to your current instance, say an instance of BadClass, but then combine the delegate with another delegate pointing to an instance of GoodClass. Hard to believe that something so simple completely destroys our initial security model. You can get around this problem by fully examining the invocation list of a particular delegate and then adding only those delegates that are valid to your existing storage. Knowhing more about the storage, we can also use our own linked list to combine delegates together and then we can remove calls to GetInvocationList and perhaps eek some additional performance out of systems that rely heavily on eventing.&lt;/p&gt; &lt;blockquote dir="ltr" style="MARGIN-RIGHT: 0px"&gt; &lt;blockquote dir="ltr" style="MARGIN-RIGHT: 0px"&gt; &lt;p dir="ltr"&gt;using System;&lt;/p&gt; &lt;p dir="ltr"&gt;public class Runner {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; private static void Main(string[] args) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ControlledEventAttachment cea = new ControlledEventAttachment();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Console.WriteLine("Firing");&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; cea.Fire();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Console.WriteLine();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Console.WriteLine("Attaching");&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Allowable allow = new Allowable();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; allow.Attach(cea);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Console.WriteLine("Firing");&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; cea.Fire();&lt;/p&gt; &lt;p dir="ltr"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Console.WriteLine();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Console.WriteLine("Attaching");&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Hidden hide = new Hidden();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; hide.Attach(cea);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Console.WriteLine("Firing");&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; cea.Fire();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;}&lt;/p&gt; &lt;p dir="ltr"&gt;public class ControlledEventAttachment {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; private EventHandler woot;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public event EventHandler Woot {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; add {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if ( value.Target is Allowable ) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; woot = (EventHandler) Delegate.Combine(woot, value);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; remove {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; woot = (EventHandler) Delegate.Remove(woot, value);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public void Fire() {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if ( woot != null ) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; woot(this, null);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;}&lt;/p&gt; &lt;p dir="ltr"&gt;public class Allowable {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public void Foo(object sender, EventArgs e) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Console.WriteLine("Allowable");&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/p&gt; &lt;p dir="ltr"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public void Attach(ControlledEventAttachment cea) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; cea.Woot += new EventHandler(this.Foo);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;}&lt;/p&gt; &lt;p dir="ltr"&gt;public class Hidden {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public Allowable allow = new Allowable();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public void Attach(ControlledEventAttachment cea) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; EventHandler handler = new EventHandler(this.Foo);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; handler = (EventHandler) Delegate.Combine(handler, new EventHandler(allow.Foo));&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; cea.Woot += handler;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; cea.Woot += new EventHandler(this.Foo2);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/p&gt; &lt;p dir="ltr"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public void Foo(object sender, EventArgs e) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Console.WriteLine("Hidden");&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public void Foo2(object sender, EventArgs e) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Console.WriteLine("Not So Hidden");&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;}&lt;/p&gt;&lt;/blockquote&gt;&lt;/blockquote&gt;&lt;img src="http://weblogs.asp.net/aggbug.aspx?PostID=233226" width="1" height="1"&gt;</description><category domain="http://weblogs.asp.net/justin_rogers/archive/tags/Security/default.aspx">Security</category><category domain="http://weblogs.asp.net/justin_rogers/archive/tags/CLR+Internals/default.aspx">CLR Internals</category><category domain="http://weblogs.asp.net/justin_rogers/archive/tags/Plug-In+Framework/default.aspx">Plug-In Framework</category></item><item><title>Some security considerations for systems with events.</title><link>http://weblogs.asp.net/justin_rogers/archive/2004/09/22/233128.aspx</link><pubDate>Thu, 23 Sep 2004 00:54:00 GMT</pubDate><guid isPermaLink="false">c06e2b9d-981a-45b4-a55f-ab0d8bbfdc1c:233128</guid><dc:creator>Justin Rogers</dc:creator><author>Justin Rogers</author><slash:comments>6</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://weblogs.asp.net/justin_rogers/rsscomments.aspx?PostID=233128</wfw:commentRss><comments>http://weblogs.asp.net/justin_rogers/archive/2004/09/22/233128.aspx#comments</comments><description>&lt;p&gt;For just a moment, relax your guard and don't think about the common usages of eventing that occur every day. The quick answer to solving any security concerns is to do a code review, run your application in a debugger to find offending code, and claim that since you own all of the source you don't need extra measures to protect yourself. I'm fine with that. This model is for plug-in architectures that allow eventing as a core messaging feature. We'll examine the concepts of messaging and break-downs in the system from a couple of different angles.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;No Twosies&lt;br /&gt;&lt;/strong&gt;This is a prominent issue. User code identifies a location where they need to add an event handler for something and this location is possibly re-entrant. Normally, you might call this a mistake, but I think it is a mistake that can be easily rectified by some custom event code. Not only that, but user code fixes for these issues require that the offending code be replaced, a state variable protect whether or not the event has been added, or finding the proper location to remove the event. All of these can be difficult compared to the custom event code.&lt;/p&gt; &lt;blockquote dir="ltr" style="MARGIN-RIGHT: 0px"&gt; &lt;blockquote dir="ltr" style="MARGIN-RIGHT: 0px"&gt; &lt;p&gt;add {&amp;nbsp;customEvent = (EventHandler) Delegate.Combine(Delegate.RemoveAll(customEvent, value), value); }&lt;/p&gt;&lt;/blockquote&gt;&lt;/blockquote&gt; &lt;p dir="ltr"&gt;We end up using removal code to ensure that the delegate doesn't already exist before adding the new delegate. We can also use locks to ensure multi-threaded protection during these operations.&lt;/p&gt; &lt;p dir="ltr"&gt;&lt;strong&gt;First Come, First Serve&lt;br /&gt;&lt;/strong&gt;Another prominent issue. If you always know your event is only going to be consumed by one object at a time, then you can easily protect yourself from adding multiple handlers. This is generally found in places where it only makes sense for one piece of code to handle an event. Have you ever seen what the effects of 10 paint event handlers? Again, you might call this poor code, but each time you want to paint a new glyph, you might think it smart to add a new event handler while the option is checked in place of yet another branching block. Let's ensure we keep it to one at a time.&lt;/p&gt; &lt;blockquote dir="ltr" style="MARGIN-RIGHT: 0px"&gt; &lt;blockquote dir="ltr" style="MARGIN-RIGHT: 0px"&gt; &lt;p dir="ltr"&gt;add { if ( customEvent == null ) { customEvent = value; } }&lt;br /&gt;remove { if ( customEvent == value ) { customEvent = null; } }&lt;/p&gt;&lt;/blockquote&gt;&lt;/blockquote&gt; &lt;p dir="ltr"&gt;&lt;strong&gt;What is the Password?&lt;br /&gt;&lt;/strong&gt;The majority of the events you see are communal beasts of multi-casting, but you may not always want someone to be able to add themselves as an event source. Here is a game related scenario that might be of interest... As AI players walk around the world, they look for event sources. These sources are objects in the world that might perform some actions and they give notification before they do. A naively programmed AI tries adding event sources that don't have any impact on them, they simply aren't in the effect zone, and so that AI builds up more information about the world and uses more resources than the other AI. On the same note, pushing the requirements of adding events to the eventing zones themselves, means that AI not even interested will be notified and thus again resources are wasted. The solution is to enforce some sort of password.&lt;/p&gt; &lt;blockquote dir="ltr" style="MARGIN-RIGHT: 0px"&gt; &lt;blockquote dir="ltr" style="MARGIN-RIGHT: 0px"&gt; &lt;p dir="ltr"&gt;private event FireEventHandler BelchFlames {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; add { &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Player p = value.Target as Player;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if ( p&amp;nbsp;== null ||&amp;nbsp;p is Mech ) { return; } // Only add players and non mechs that are affected&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;belchFlames = (FireEventHandler) Delegate.Combine(Delegate.RemoveAll(belchFlames, value),&amp;nbsp;value);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; remove {...}&lt;br /&gt;}&lt;/p&gt;&lt;/blockquote&gt;&lt;/blockquote&gt; &lt;p dir="ltr"&gt;&lt;strong&gt;Get Out of Here!&lt;br /&gt;&lt;/strong&gt;Another odd moment in the life of an event is when you realize that when it fires and when the delegates are called are two different times. The rationale here is another game concept. While the game engine needs events from each game object to schedule&amp;nbsp;things to happen, AI components need to run in a specialized sand-box, timed, and most likely run at a different time from when the original event first fired. One of the larger mistakes in AI programming is nesting scripts too deeply and events are a form of nesting. An example would be a group leader subscribed to picking up base communications that in turn relays the information to the group participants. That is three levels already and you might not be prepared for all that mayhem when the event is fired.&lt;/p&gt; &lt;blockquote dir="ltr" style="MARGIN-RIGHT: 0px"&gt; &lt;blockquote dir="ltr" style="MARGIN-RIGHT: 0px"&gt; &lt;p dir="ltr"&gt;add {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; if ( value.Target is GameEngine ) { gameEngineSink = value; }&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; else&amp;nbsp;{ fullSink = (EventHandler) Delegate.Combine(fullSink, value); }&lt;br /&gt;}&lt;/p&gt;&lt;/blockquote&gt;&lt;/blockquote&gt; &lt;p dir="ltr"&gt;Notice how we collect events from different sources (by examining the Target) into different collections. The game engine, when it wants to be notified of a particular event, will be placed in a specialized holder, while the rest of the objects get collected into a secondary holder. We'll pass the secondary holder off to the game engine at some point and allow it to call each of the objects from within the timing sand-box.&lt;/p&gt; &lt;p dir="ltr"&gt;&lt;strong&gt;I'm Tired of Waiting!&lt;br /&gt;&lt;/strong&gt;While the above solves locality issues, it doesn't necessarily solve concurrency issues. While running AI code in the sand-box is definitely a great thing to do, we may also want a bunch of code to run in response to an event while we finish some other processing. You can toss individual delegates off onto separate threads. These are single-cast, non-combinable delegates that you get from calling &lt;u&gt;GetInvocationList&lt;/u&gt;. You have to be far more careful with respect to thread-safety and the operations in the delegates really need to be designed so they can run asynchronously while the rest of the application continues on.&lt;/p&gt; &lt;blockquote dir="ltr" style="MARGIN-RIGHT: 0px"&gt; &lt;blockquote dir="ltr" style="MARGIN-RIGHT: 0px"&gt; &lt;p dir="ltr"&gt;private void OnMessageReceived(MessageInfo mi) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; if ( messageReceived != null ) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Delegate[] callbacks = messageReceived.GetInvocationList();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; for(int i&amp;nbsp;= 0; i &amp;lt; callbacks.Length; i++) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ThreadPool.QueueUserWorkItem(new WaitCallback(RunMessage), new Package(callbacks[i], mi));&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;}&lt;br /&gt;private void RunMessage(object state) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Package p = state as Package;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; if ( p != null ) { p.Callback.DynamicInvoke( new object[] { p.MessageInfo } ); }&lt;br /&gt;}&lt;/p&gt;&lt;/blockquote&gt;&lt;/blockquote&gt; &lt;p dir="ltr"&gt;Now, as messages are received, we'll quickly dispatch them off to other threads to be handled. If the other threads are running neural networks, doing character recognition, or running variations of a specific algorithm, they can update their internal state and return the threads to the pool after they are complete. This never stops the rest of the game from doing its thing and back-ups in the thread pool can result in some auxillary code to throw out messages until things catch up.&lt;/p&gt; &lt;p dir="ltr"&gt;&lt;strong&gt;Quit Throwing Stuff at Me!&lt;br /&gt;&lt;/strong&gt;The code running in an event handler can crash and burn just like any other code. If this happens during a normal event invocation, you may not process other, operable events. One option is to break out the invocation lists and call each delegate safely, while removing the offending delegates that are causing problems. I think this is an excellent pattern, especially in plug-in architectures where you expect plug-ins to surface errors through exceptions since they have no other manner of indicating an exceptional situation.&lt;/p&gt; &lt;p dir="ltr"&gt;A secondary issue would be disposed objects. A Form for instance can be in an unusable state, but still be the target of a delegate. Since most event oriented code is terrible about removing or unhooking events (where do you put the code, did you put it in the right place, does it cover all code-paths that lead to an unusable object, is the remote object still alive even) it might be smart to protect against such a common mistake.&lt;/p&gt; &lt;blockquote dir="ltr" style="MARGIN-RIGHT: 0px"&gt; &lt;blockquote dir="ltr" style="MARGIN-RIGHT: 0px"&gt; &lt;p dir="ltr"&gt;private void OnFilterImage(Bitmap b) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; if ( messageReceived != null ) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Delegate[] callbacks = messageReceived.GetInvocationList();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; for(int i&amp;nbsp;= 0; i &amp;lt; callbacks.Length; i++) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; try {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; callbacks[i].DynamicInvoke(new object[] { this, b });&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; } catch {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;messageReceived = (BitmapHandler) Delegate.Remove(messageReceived, callbacks[i]);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;}&lt;/p&gt;&lt;/blockquote&gt;&lt;/blockquote&gt; &lt;p dir="ltr"&gt;Now, if you have an error in a single filter, the rest won't affect you. You can even notify the user in the catch block, and ask if they would like to proceed with the rest of the events, especially if this is a run of sequential filters that all need to complete to achieve a desired effect. However, if you've been processing for 10 minutes, and this is the last filter, you may just want to get it over with and finish the process.&lt;/p&gt; &lt;p dir="ltr"&gt;All of these considerations are for systems where events are considered important. You can fix each of the issues in turn by removing events and using another pattern entirely. In fact in most cases I don't use events myself. The direction I'm taking here is that of a highly dynamic plug-in oriented architecture, where eventing solves many information and messaging problems without a lot of code.&lt;/p&gt;&lt;img src="http://weblogs.asp.net/aggbug.aspx?PostID=233128" width="1" height="1"&gt;</description><category domain="http://weblogs.asp.net/justin_rogers/archive/tags/Security/default.aspx">Security</category><category domain="http://weblogs.asp.net/justin_rogers/archive/tags/CLR+Internals/default.aspx">CLR Internals</category><category domain="http://weblogs.asp.net/justin_rogers/archive/tags/Plug-In+Framework/default.aspx">Plug-In Framework</category></item><item><title>Terrarium: Did we do the right thing with the creature event model?</title><link>http://weblogs.asp.net/justin_rogers/archive/2004/09/04/225731.aspx</link><pubDate>Sun, 05 Sep 2004 00:51:00 GMT</pubDate><guid isPermaLink="false">c06e2b9d-981a-45b4-a55f-ab0d8bbfdc1c:225731</guid><dc:creator>Justin Rogers</dc:creator><author>Justin Rogers</author><slash:comments>0</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://weblogs.asp.net/justin_rogers/rsscomments.aspx?PostID=225731</wfw:commentRss><comments>http://weblogs.asp.net/justin_rogers/archive/2004/09/04/225731.aspx#comments</comments><description>&lt;p&gt;&lt;strong&gt;History&lt;br /&gt;&lt;/strong&gt;Why did we use the eventing model? Well, because it made sense that creatures only needed to spend processing time on things they were curious about. It seemed like it might make things easier because a basic creature could hook only one event while a more complex creature could hook many events. It was also a great demonstration of how you would build pluggable apps within the .NET Framework. However, at some point we broke the model a bit and added some virtual methods for serialization and deserialization of creature data.&lt;/p&gt; &lt;p&gt;At no point did we consider revisiting the model and changing it out for another model that might be more appropriate. The backwards compatibility mindset was always in place and we didn't want people to have to recode their creatures entirely as versions changed. It also became rather difficult to change the pattern away from the 10 or so events and large eventing infrastructure that was in place. With that in mind we kept the pattern.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Pattern&lt;br /&gt;&lt;/strong&gt;Our pattern was to simulate the concept of discrete inputs. Certain things may or may not happen in a given round, and other things may not happen for the entire life-time of the creature. No reason to check for these conditions every tick of the game loop. We created several separate events that comprised certain worldy actions such as attacking, defending, eating, etc... We also needed the creatures to be notified even if nothing of importance happened. This ensures the creature gets some time to process irregardless of the state of the world. These are the Load and Idle events. The Load happens before all other events are called, while the Idle happens after all other events are called. Load allows you to set things up and prepare for action, Idle lets you operate after world notifications have been propagated. Easy enough right?&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Complexity&lt;br /&gt;&lt;/strong&gt;We definitely made some mistakes. First, there are a lot of events. Many creature authors are curious about which events they need and the order the events happen in. We have some documentation, but that doesn't help when you are first picking up the Terrarium for the first time. You want things to be as plain and simple as possible. Another level of complexity is added in that many developers wind up doing work in each event that may or may not overwrite work done in other events. You could blame this on the person writing the code, I rather blame it on the model and blame myself for allowing it.&lt;/p&gt; &lt;p&gt;Additionally there is the problem of storing information about discrete events having happened. The author could store the information, but we already store it within the base class. This isn't immediately obvious. I mean why would we store the information for you and give you an event that gives you the same information? It makes more sense for you to be notified that it is your turn to process and then allow you to investigate the state of the world.&lt;/p&gt; &lt;p&gt;What about time-slicing? Since we time-slice you, each event we call is taking a bit more of your time. Additionally it is harder to load balance your time across ten events than it would be across a single event. You have to store the amount of time spent in each of your events in order to get an accurate measure of how much time you used and how much you have left to process. For path-finding algorithms you may want to make the most effective use of your time by knowing about how much time you have left, even if the measure is somehow relative.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;No Guarantees&lt;br /&gt;&lt;/strong&gt;We don't guarantee that your events will fire. In fact, we can skip you if you are taking too long, and the only way you know is by examining a special property that tells you for how long you were skipped. Any in process events continue processing, so while you aren't being notified, your move may complete or you may get teleported. If you are relying on the eventing model to keep your private state in check you are in for a rude awakening.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Single Cast Events&lt;br /&gt;&lt;/strong&gt;Having single-cast events isn't very useful in our model as you can tell from some of the downsides. In fact, in trying to demonstrate a plug-in model, we desperately failed. Normally, plug-ins will hook up to some global events that exist on the game engine&amp;nbsp;and not exhibit a bunch of local events that are only consumed by the plug-in.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Fixing the Model&lt;br /&gt;&lt;/strong&gt;The model is quite easy to fix. Simply don't use events. There is no reason to use events in our model, rather with a base-class already in place we simply supply a single callback method, virtual of course, for the creature to implement all logic. This drastically reduces the complexity of the code required to implement the creature into a single entry point program. While you might think that authors couldn't code creatures that were nearly as functional as before, they actually have more time to operate now and have a less complex model to program against. As I'm making these changes to the source it simplifies other portions of the engine and helps refine the layout of the interfaces between the creature base, the engine, and the UI.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Working With What You Have&lt;br /&gt;&lt;/strong&gt;You have some options already as a creature developer. By writing a simple base class that manages all of the events for you and stores local information in a very minimalistic format, you can write your derived creatures to use the new local information from within a single method that is fired by the Idle event. At one point we had a work-item open to ship this class within OrganismBase itself. The idea would have been to have the Animal class for anyone interested in programming the old model, but also a new SingleCallAnimal supporting the new, simpler syntax.&lt;/p&gt;&lt;img src="http://weblogs.asp.net/aggbug.aspx?PostID=225731" width="1" height="1"&gt;</description><category domain="http://weblogs.asp.net/justin_rogers/archive/tags/Terrarium/default.aspx">Terrarium</category><category domain="http://weblogs.asp.net/justin_rogers/archive/tags/Games4+.NET/default.aspx">Games4 .NET</category><category domain="http://weblogs.asp.net/justin_rogers/archive/tags/Plug-In+Framework/default.aspx">Plug-In Framework</category><category domain="http://weblogs.asp.net/justin_rogers/archive/tags/Software+Design/default.aspx">Software Design</category><category domain="http://weblogs.asp.net/justin_rogers/archive/tags/Artificial+Intelligence/default.aspx">Artificial Intelligence</category></item><item><title>.NET Immutability Tip #3: Protect your properties AND your methods.</title><link>http://weblogs.asp.net/justin_rogers/archive/2004/05/03/125326.aspx</link><pubDate>Mon, 03 May 2004 23:15:00 GMT</pubDate><guid isPermaLink="false">c06e2b9d-981a-45b4-a55f-ab0d8bbfdc1c:125326</guid><dc:creator>Justin Rogers</dc:creator><author>Justin Rogers</author><slash:comments>2</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://weblogs.asp.net/justin_rogers/rsscomments.aspx?PostID=125326</wfw:commentRss><comments>http://weblogs.asp.net/justin_rogers/archive/2004/05/03/125326.aspx#comments</comments><description>&lt;P&gt;A common immutability practice is to simply protect the property setter with an immutability flag.&amp;nbsp; Take a simple class that has a single integer field for to back our property and a single boolean flag to mark it immutable.&lt;/P&gt;
&lt;BLOCKQUOTE dir=ltr style="MARGIN-RIGHT: 0px"&gt;
&lt;BLOCKQUOTE dir=ltr style="MARGIN-RIGHT: 0px"&gt;
&lt;P&gt;using System;&lt;/P&gt;
&lt;P&gt;public class SimplyImmutable {&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; private int field1 = 0;&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; private bool immutable = false;&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public SimplyImmutable() {&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public int Field1 { &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; get { return field1; } &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; set { &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if ( !immutable ) {&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; field1 = value;&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;BR&gt;}&lt;/P&gt;&lt;/BLOCKQUOTE&gt;&lt;/BLOCKQUOTE&gt;
&lt;P dir=ltr&gt;As soon as the immutable flag is set, you can no longer set the field value.&amp;nbsp; This is nice.&amp;nbsp; Time to add a few methods.&amp;nbsp; First we need a MakeImmutable method.&amp;nbsp; This will set our flag.&amp;nbsp; Normally, our application will set this before giving any other code access to the class.&amp;nbsp; Second, we'll add an IncrementField method, but we'll make a mistake, so see if you catch it.&lt;/P&gt;
&lt;BLOCKQUOTE dir=ltr style="MARGIN-RIGHT: 0px"&gt;
&lt;BLOCKQUOTE dir=ltr style="MARGIN-RIGHT: 0px"&gt;
&lt;P dir=ltr&gt;public void MakeImmutable() {&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; immutable = true;&lt;BR&gt;}&lt;/P&gt;
&lt;P dir=ltr&gt;public void IncrementField() {&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; field1++;&lt;BR&gt;}&lt;/P&gt;&lt;/BLOCKQUOTE&gt;&lt;/BLOCKQUOTE&gt;
&lt;P dir=ltr&gt;See the mistake yet?&amp;nbsp; We accessed field1 directly, and didn't use the immutability flag.&amp;nbsp; This problem goes away if you use the property instead of the private field or if you use the immutability flag.&amp;nbsp; In this small example the problem is easy to spot.&amp;nbsp; However, in a larger class with several properties, many backing fields, and maybe 10 or more methods, you can fail to find issues early in the game.&amp;nbsp; The gem here is to use the property inside of our methods so that we don't have to write a bunch of extra code in each of them to handle immutability.&lt;/P&gt;
&lt;BLOCKQUOTE dir=ltr style="MARGIN-RIGHT: 0px"&gt;
&lt;BLOCKQUOTE dir=ltr style="MARGIN-RIGHT: 0px"&gt;
&lt;P dir=ltr&gt;public void IncrementField() {&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Field1++;&lt;BR&gt;}&lt;/P&gt;&lt;/BLOCKQUOTE&gt;&lt;/BLOCKQUOTE&gt;
&lt;P dir=ltr&gt;What happens if you are setting the Field1 property multiple times?&amp;nbsp; You start to incur the wrath of the flag check on each set.&amp;nbsp; If this happens, then go ahead and move your immutability check into your method and then convert over to using the private field again.&amp;nbsp; Note if you do this process by first using the property and then only later converting over to use the private field and the immutability flag, you can do it on an as needed basis and ensure that you are protecting yourself.&lt;/P&gt;
&lt;BLOCKQUOTE dir=ltr style="MARGIN-RIGHT: 0px"&gt;
&lt;BLOCKQUOTE dir=ltr style="MARGIN-RIGHT: 0px"&gt;
&lt;P dir=ltr&gt;public void IncrementField() {&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; if ( !immutable ) { // Work with field multiple times, if not then just use the property instead }&lt;BR&gt;}&lt;/P&gt;&lt;/BLOCKQUOTE&gt;&lt;/BLOCKQUOTE&gt;
&lt;P dir=ltr style="MARGIN-RIGHT: 0px"&gt;This tip has many natural extensions into other gotchas that I'll cover later.&amp;nbsp; Think for a bit about making our fields protected so they can be accessed from derived classes, and how that might introduce resource access issues.&amp;nbsp; I'll be examining this abstraction in the next tip.&lt;/P&gt;&lt;img src="http://weblogs.asp.net/aggbug.aspx?PostID=125326" width="1" height="1"&gt;</description><category domain="http://weblogs.asp.net/justin_rogers/archive/tags/Security/default.aspx">Security</category><category domain="http://weblogs.asp.net/justin_rogers/archive/tags/Plug-In+Framework/default.aspx">Plug-In Framework</category><category domain="http://weblogs.asp.net/justin_rogers/archive/tags/Quick+Tips/default.aspx">Quick Tips</category><category domain="http://weblogs.asp.net/justin_rogers/archive/tags/Software+Design/default.aspx">Software Design</category></item><item><title>[Terrarium] How do we pass creature assemblies around?</title><link>http://weblogs.asp.net/justin_rogers/archive/2004/04/23/119268.aspx</link><pubDate>Fri, 23 Apr 2004 23:56:00 GMT</pubDate><guid isPermaLink="false">c06e2b9d-981a-45b4-a55f-ab0d8bbfdc1c:119268</guid><dc:creator>Justin Rogers</dc:creator><author>Justin Rogers</author><slash:comments>0</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://weblogs.asp.net/justin_rogers/rsscomments.aspx?PostID=119268</wfw:commentRss><comments>http://weblogs.asp.net/justin_rogers/archive/2004/04/23/119268.aspx#comments</comments><description>&lt;P&gt;This is the primary feature of the .NET Terrarium.&amp;nbsp; The ability to safely and easily pass around creature assemblies and run them on end user machines.&amp;nbsp; There are several considerations for this process including security, transport, loading, and storage.&amp;nbsp; I've already talked about the Terrarium Model and security over in the &lt;A id=CategoryEntryList.ascx_EntryStoryList_Entries__ctl0_TitleUrl href="/justin_rogers/articles/67907.aspx"&gt;&lt;FONT color=#0000ff&gt;Plug-In Framework (Foreward): Defining the end goal&lt;/FONT&gt;&lt;/A&gt;, so I'll be talking about the later three now.&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Transport&lt;BR&gt;&lt;/STRONG&gt;Transport can be very easy (WebRequest), or fairly difficult (Socket).&amp;nbsp; Today, you can easily use web services or web requests to grab information off of a central server, and the Terrarium does this all the time.&amp;nbsp; We have several services that go out and grab extinct creature assemblies off of the web server.&amp;nbsp; This is probably the easiest way to get a creature assembly.&lt;/P&gt;
&lt;P&gt;The P2P methods are a bit more difficult.&amp;nbsp; We actually created a miniature web server HttpWebListener (thanks to Lance Olson) and use that for P2P interaction.&amp;nbsp; So are we really using Socket?&amp;nbsp; Yeah, but not the way you would think.&amp;nbsp; Each client still issues basic WebRequests for data from each peer, but we host those little miniature HTTP servers in order to intercept the commands.&amp;nbsp; This is actually pretty cool and I'm sure everyone will enjoy having the source code at their fingertips for this little gem pretty soon.&lt;/P&gt;
&lt;P&gt;Since HTTP is a stateless protocol I can tell you now that we have to implement a message based system whenever we communicate with other peers.&amp;nbsp; All of the messages originate on one side of the pipe (the client) and will change depending on the response from the listener.&amp;nbsp; This is a fairly easy process, made quite complicated by requring multiple connections, one for each message, but overall it isn't that bad.&amp;nbsp; Sample messages would be, do you have assembly A?&amp;nbsp; Here is assembly A, did you get it okay?&amp;nbsp; Here is a new creature, are you receiving it okay?&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Storage&lt;BR&gt;&lt;/STRONG&gt;Now that we have the assemblies getting tossed around we have to store them somewhere.&amp;nbsp; We can't store them in the same place as our executable.&amp;nbsp;&amp;nbsp; We can't store them in Program Files and still act under &amp;#8220;Run as Normal User&amp;#8221;.&amp;nbsp; We can't really store them anywhere, except for the users local settings.&amp;nbsp; Ah, we found a spot.&amp;nbsp; Since we have to store assemblies off in some remote directory, how do we go about loading creature assemblies when they are requested?&lt;/P&gt;
&lt;P&gt;That is where our assembly caching logic comes in.&amp;nbsp; The basic process starts inside of the game logic.&amp;nbsp; You see the game logic knows about the assembly caching logic, and so rather than calling an Assembly.Load on a creature's full name, we simply call into your assembly cache and pull out an assembly.&amp;nbsp; This works great.&amp;nbsp; However, there are still times when an Assembly.Load might get called outside of our code (deserialization) and we need to be aware of that so we can return the assembly when probing fails.&amp;nbsp; For that we hook AssemblyResolve, verify that the assembly being requested isn't one of our own, and finally look things up in the assembly cache.&lt;/P&gt;
&lt;P&gt;There is one hang-up here.&amp;nbsp; We never make use of private paths, so the run-time doesn't control the probing, we do.&amp;nbsp; We get a lot more storage potential out of this, including monitoring how many assemblies are currently loaded, how many are in the assembly cache, and finally the ability to run clean-up logic if we want to.&amp;nbsp; We also don't automatically download the assemblies if they aren't already in the assembly cache.&amp;nbsp; We could make a web request and search for the assembly on the server, however, we rely on other game logic to ensure the assembly is there.&lt;/P&gt;
&lt;P&gt;To note some additional security we've layered on our cache.&amp;nbsp; Each time the Terrarium starts, the cache directory is dynamically changed to some random directory name.&amp;nbsp; No brute force, I know where your assemblies are hidden, attacks.&amp;nbsp; This prevents users from storing things in assemblies and then taking advantage of them later through email, URLs on their website, or any number of other hacker routes.&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Loading&lt;BR&gt;&lt;/STRONG&gt;This is the most complex aspect of assembly use that we have.&amp;nbsp; Not only do we have many layers of code access security protection, but we grep the assembly IL for bad constructs, load the assembly in multiple ways to prevent context caching&amp;nbsp;of assemblies, and a few other goodies.&amp;nbsp; I've detailed the code access security items and IL grepping within Plug-In goals document, so you can see the real deal there.&amp;nbsp; I'll talk a bit about loading assemblies for the first time and the process they go through.&lt;/P&gt;
&lt;P&gt;Loading an assembly for the first time is a pretty big deal.&amp;nbsp; We obviously run the IL grep over the assmebly before we do anything, but once that process happens, we have a number of other checks that have to be run.&amp;nbsp; Namely, we have to get a bunch of assembly attributes off of the type, make sure the image isn't bad, etc...&amp;nbsp; Simply enumerating the IL won't give us this full verification that everything is kosher, and we don't want to lock the file on disk.&amp;nbsp; So we use the byte[] overloads of the Load method.&amp;nbsp; The method supports loading of just the assembly, or what most people don't know, debug symbols as well.&amp;nbsp; Took some time to get all of this code written correctly to allow all of that precious debug information to make it to it's final location in the assembly cache.&lt;/P&gt;
&lt;P&gt;Once the assembly is verified, we check it against the Species service to make sure it is unique, add it to the list of creatures, and store it's assembly remotely.&amp;nbsp; If this is a new creature for sure, we save the assembly into the assembly cache (copying symbols if necessary), and load the assembly again.&amp;nbsp; This time for real, since this will be the assembly codebase that we use for creating creatures.&lt;/P&gt;
&lt;P&gt;The entire process is fairly quick.&amp;nbsp; Later as the application shuts down and restarts, we'll be responsible for loading creature assemblies over and over again.&amp;nbsp; They'll always come out of the assembly cache from there on out.&amp;nbsp; Some hang-ups do occur.&amp;nbsp; If the original assembly (before copy) was located in the probing path, a call to Assembly.Load by the serialization code might load the wrong assembly.&amp;nbsp; We prevented this by some ingenious serialization, but I'll talk about that later.&amp;nbsp; You also have to make sure all of your code loads from the assembly cache as well, and doesn't call Load, else that probing issue will catch you sooner or later.&lt;/P&gt;&lt;img src="http://weblogs.asp.net/aggbug.aspx?PostID=119268" width="1" height="1"&gt;</description><category domain="http://weblogs.asp.net/justin_rogers/archive/tags/Terrarium/default.aspx">Terrarium</category><category domain="http://weblogs.asp.net/justin_rogers/archive/tags/Games4+.NET/default.aspx">Games4 .NET</category><category domain="http://weblogs.asp.net/justin_rogers/archive/tags/Plug-In+Framework/default.aspx">Plug-In Framework</category></item><item><title>[Plug-in Framework] Adendum to the goals document</title><link>http://weblogs.asp.net/justin_rogers/archive/2004/04/19/116352.aspx</link><pubDate>Tue, 20 Apr 2004 01:12:00 GMT</pubDate><guid isPermaLink="false">c06e2b9d-981a-45b4-a55f-ab0d8bbfdc1c:116352</guid><dc:creator>Justin Rogers</dc:creator><author>Justin Rogers</author><slash:comments>0</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://weblogs.asp.net/justin_rogers/rsscomments.aspx?PostID=116352</wfw:commentRss><comments>http://weblogs.asp.net/justin_rogers/archive/2004/04/19/116352.aspx#comments</comments><description>&lt;P&gt;I've added the Terrarium model and World Builder model definitions.&amp;nbsp; The Terrarium model is based on the hardships in implementing the code hosting technology for the .NET Terrarium and the World Builder model is focused on slightly improving on the Terrarium model by extending the IL Verifier and talking about the use of factory types to manage memory allocations for types.&lt;/P&gt;
&lt;P&gt;&lt;A id=CategoryEntryList.ascx_EntryStoryList_Entries__ctl0_TitleUrl HREF="/justin_rogers/articles/67907.aspx"&gt;Plug-In Framework (Foreward): Defining the end goal&lt;/A&gt;&lt;/P&gt;&lt;img src="http://weblogs.asp.net/aggbug.aspx?PostID=116352" width="1" height="1"&gt;</description><category domain="http://weblogs.asp.net/justin_rogers/archive/tags/Plug-In+Framework/default.aspx">Plug-In Framework</category></item><item><title>.NET Immutability Tip #2: Be careful of unprotected types in the executable.</title><link>http://weblogs.asp.net/justin_rogers/archive/2004/04/10/111101.aspx</link><pubDate>Sun, 11 Apr 2004 05:48:00 GMT</pubDate><guid isPermaLink="false">c06e2b9d-981a-45b4-a55f-ab0d8bbfdc1c:111101</guid><dc:creator>Justin Rogers</dc:creator><author>Justin Rogers</author><slash:comments>0</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://weblogs.asp.net/justin_rogers/rsscomments.aspx?PostID=111101</wfw:commentRss><comments>http://weblogs.asp.net/justin_rogers/archive/2004/04/10/111101.aspx#comments</comments><description>&lt;P&gt;I've seen this happen quite often.&amp;nbsp; When developers create their immutable types, they assume the only view the user will ever have is of the type through some interface.&amp;nbsp; Normally the interfaces are defined before-hand and included in some library that the user is supposed to link to.&amp;nbsp; This can easily be demonstrated by mocking up an &lt;STRONG&gt;IWorldView&lt;/STRONG&gt; interface and then using this in our user code.&lt;/P&gt;
&lt;BLOCKQUOTE dir=ltr style="MARGIN-RIGHT: 0px"&gt;
&lt;BLOCKQUOTE dir=ltr style="MARGIN-RIGHT: 0px"&gt;
&lt;P&gt;// Interfaces.dll&lt;BR&gt;public interface IWorldView {&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; int WorldSizeX { get; }&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; int WorldSizeY { get; }&lt;BR&gt;}&lt;BR&gt;&lt;BR&gt;// UserPlugin.dll&lt;BR&gt;public class UserClassConsumingIWorldView {&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public void EngineCallMe(IWorldView iwv) {&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; // do some work&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; int area = iwv.WorldSizeX * iwv.WorldSizeY;&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;BR&gt;}&lt;/P&gt;&lt;/BLOCKQUOTE&gt;&lt;/BLOCKQUOTE&gt;
&lt;P dir=ltr&gt;From the code above, you would assume that the user sees the world through IWorldView goggles.&amp;nbsp; That might not be the case.&amp;nbsp; When they develop their plug-in they have access to your executable as well.&amp;nbsp; While some .NET languages won't let you link against executables, C# will.&amp;nbsp; This can lead to some access elevation in that the user might be able to case IWorldView to a type with more access.&lt;/P&gt;
&lt;BLOCKQUOTE dir=ltr style="MARGIN-RIGHT: 0px"&gt;
&lt;BLOCKQUOTE dir=ltr style="MARGIN-RIGHT: 0px"&gt;
&lt;P dir=ltr&gt;using System;&lt;/P&gt;
&lt;P dir=ltr&gt;// Game.exe&lt;BR&gt;public class WorldView : IWorldView {&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; private int _worldSizeX;&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; private int _worldSizeY;&lt;/P&gt;
&lt;P dir=ltr&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public int WorldSizeX { get { return this._worldSizeX; } }&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public int WorldSizeY { get { return this._worldSizeY; } }&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public void ResetWorldSize(int x, int y) {&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; this._worldSizeX = x;&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; this._worldSizeY = y;&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; private static void Main() { }&lt;BR&gt;}&lt;/P&gt;&lt;/BLOCKQUOTE&gt;&lt;/BLOCKQUOTE&gt;
&lt;P dir=ltr style="MARGIN-RIGHT: 0px"&gt;Woops, we defined our WorldView class as &lt;STRONG&gt;public&lt;/STRONG&gt; making it available to the outside world.&amp;nbsp; Now the UserPlugin can cast to this guy and make use of ResetWorldSize().&lt;/P&gt;
&lt;BLOCKQUOTE dir=ltr style="MARGIN-RIGHT: 0px"&gt;
&lt;BLOCKQUOTE dir=ltr style="MARGIN-RIGHT: 0px"&gt;
&lt;P dir=ltr style="MARGIN-RIGHT: 0px"&gt;using System;&lt;/P&gt;
&lt;P dir=ltr style="MARGIN-RIGHT: 0px"&gt;public class UserClassConsumingIWorldView {&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public void EngineCallMe(IWorldView iwv) {&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; // do some work&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; int area = iwv.WorldSizeX * iwv.WorldSizeY;&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ((WorldView) iwv).ResetWorldSize(0, 0);&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;BR&gt;}&lt;/P&gt;&lt;/BLOCKQUOTE&gt;&lt;/BLOCKQUOTE&gt;
&lt;P dir=ltr style="MARGIN-RIGHT: 0px"&gt;You won't make this mistake twice, believe me.&amp;nbsp; Making the WorldView class &lt;STRONG&gt;internal&lt;/STRONG&gt; will fix any issues with this possible casting and put you on your way.&amp;nbsp; In this case we assumed immutability of the world view because we thought we had control over the user's access when we really didn't through a common programming mistake.&amp;nbsp; Users in the VB world might never recognize this error, since they wouldn't be able to include Game.exe as a reference to begin with.&lt;/P&gt;&lt;img src="http://weblogs.asp.net/aggbug.aspx?PostID=111101" width="1" height="1"&gt;</description><category domain="http://weblogs.asp.net/justin_rogers/archive/tags/Security/default.aspx">Security</category><category domain="http://weblogs.asp.net/justin_rogers/archive/tags/Plug-In+Framework/default.aspx">Plug-In Framework</category><category domain="http://weblogs.asp.net/justin_rogers/archive/tags/Quick+Tips/default.aspx">Quick Tips</category><category domain="http://weblogs.asp.net/justin_rogers/archive/tags/Software+Design/default.aspx">Software Design</category></item><item><title>Joel's Lightweight Code Gen spells SUWEET for small scripting languages in games.</title><link>http://weblogs.asp.net/justin_rogers/archive/2004/04/10/110899.aspx</link><pubDate>Sat, 10 Apr 2004 18:46:00 GMT</pubDate><guid isPermaLink="false">c06e2b9d-981a-45b4-a55f-ab0d8bbfdc1c:110899</guid><dc:creator>Justin Rogers</dc:creator><author>Justin Rogers</author><slash:comments>1</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://weblogs.asp.net/justin_rogers/rsscomments.aspx?PostID=110899</wfw:commentRss><comments>http://weblogs.asp.net/justin_rogers/archive/2004/04/10/110899.aspx#comments</comments><description>&lt;P&gt;Reading Joel's blog and having lunch with him are two different things.&amp;nbsp; You never really see all of the possibilities of a technology until you see the twinkle in someone's eye and realize that the technology might be slightly more powerful than you originally realized.&amp;nbsp; Today I want to cover function based scripting languages and how these can easily be implemented using the LCG inside of any existing MUD or MMOG system.&lt;/P&gt;
&lt;P&gt;The syntax for small scripting languages is not similar to what you see with C# and VB .NET.&amp;nbsp; Small scripting languages are generally functional and not necessarily centered around the creation of objects.&amp;nbsp; In the game world, a developer might create new functions in order to perform specific actions.&amp;nbsp; Each of these functions is then attached to a game world object and fired as part of an invocation list as needed.&amp;nbsp; Code is not used to define everything in the game world.&amp;nbsp; Invocation lists might be stored in a data file with a list of object id's, and their attached function id's.&amp;nbsp; Object's might not even be hard objects, they might just be scalar data or structs.&amp;nbsp; The data something contains, might also not readily identify it's object type, but instead the functions it handles.&amp;nbsp; Let's take a sample case.&lt;/P&gt;
&lt;BLOCKQUOTE dir=ltr style="MARGIN-RIGHT: 0px"&gt;
&lt;BLOCKQUOTE dir=ltr style="MARGIN-RIGHT: 0px"&gt;
&lt;P&gt;&amp;gt; You see a mossy rock in front of you.&lt;/P&gt;&lt;/BLOCKQUOTE&gt;&lt;/BLOCKQUOTE&gt;
&lt;P dir=ltr&gt;Mossy rock?&amp;nbsp; Why is it mossy?&amp;nbsp; Why is it called a rock?&amp;nbsp; You might think someone created a mossy rock object (MossyRockObject : MUDObject) somewhere and compiled it into the game.&amp;nbsp; This is probably not the case.&amp;nbsp; More likely is that they attached the GetName function and that function is diving into the database or some other data file to get the name of the object.&amp;nbsp; Is mossy even part of the game?&amp;nbsp; Or is that something else?&amp;nbsp; Maybe the GetName method is calling a GetAge function or the game engine is calling a GetAge function.&amp;nbsp; In this case the possibilities of getting a name could chain together a large number of similar functions that all return pieces of the objects name.&lt;/P&gt;
&lt;BLOCKQUOTE dir=ltr style="MARGIN-RIGHT: 0px"&gt;
&lt;BLOCKQUOTE dir=ltr style="MARGIN-RIGHT: 0px"&gt;
&lt;P dir=ltr&gt;// Note all functions are assumed to turn return an Object data type and the&lt;BR&gt;// engine is capable of handling the rest.&amp;nbsp; I'm making this very JScript like.&lt;BR&gt;function GetName(ObjectID) {&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; var name = DatabaseName(ObjectID);&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; return name;&lt;BR&gt;}&lt;/P&gt;&lt;/BLOCKQUOTE&gt;&lt;/BLOCKQUOTE&gt;
&lt;P dir=ltr&gt;Now, I'd actually have to attach that function after I made it.&amp;nbsp; Each function could be in it's own file.&amp;nbsp; I could store them in XML.&amp;nbsp; I could pre-parse them into some byte-code type structure that I could quickly convert to IL later.&amp;nbsp; Lots of options.&amp;nbsp; So we add a command to our MUD or MMOG that allows us to attach a function to an object.&amp;nbsp; Remember that mossy rock?&amp;nbsp; Well, in our case we simply told the game system to create a new MUDStaticObject.&amp;nbsp; This object is derived from MUDObject and has properties that make it act as a static object within the game world (aka a rock isn't a mobile).&amp;nbsp; Once we create the object, we have an ID.&amp;nbsp; The UpgradeObject {id} {function name} is now capable of attaching a new function.&amp;nbsp; The system is loose.&amp;nbsp; The new function is now something that can be called by name, or it can be added to any of the objects invocation lists.&amp;nbsp; An invocation list is important, because we want the game engine to be able to generically support something like name's, but not necessarily only through the GetName function (note we could have allowed many instances of the GetName function, one per object, but I'm choosing invocation lists instead ;-).&lt;/P&gt;
&lt;P dir=ltr&gt;An invocation list for our object looks something like, invocationListNames = { functionIdGetName }.&amp;nbsp; Note there is only a single ID when grabbing a name.&amp;nbsp; We might not even call this a list but instead an invocation bind point so that we have the ability to limit the number of functions for this particular feature to just one.&amp;nbsp; If we wanted to upgrade our name from say &amp;#8220;rock&amp;#8221; in the database to &amp;#8220;mossy rock&amp;#8221;, we just swap out the methods.&lt;/P&gt;
&lt;BLOCKQUOTE dir=ltr style="MARGIN-RIGHT: 0px"&gt;
&lt;BLOCKQUOTE dir=ltr style="MARGIN-RIGHT: 0px"&gt;
&lt;P dir=ltr&gt;function GetExtendedName(ObjectID) {&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; object = GetObjectByID(ObjectID);&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; name&amp;nbsp;= object.GetName(ObjectID);&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;age = object.GetAge(ObjectID);&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; prefix = &amp;#8220;shiny &amp;#8220;;&lt;BR&gt;&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; switch(age) {&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ....&amp;nbsp; // Change prefix&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; return prefix + name;&lt;BR&gt;}&lt;/P&gt;&lt;/BLOCKQUOTE&gt;&lt;/BLOCKQUOTE&gt;
&lt;P dir=ltr&gt;That was easy.&amp;nbsp; Dynamic methods make the above possible.&amp;nbsp; My MUD can take textual input through a world builder, create this new method, and then at run-time, when I compile the method I can convert my script to the appropriate IL to make things happen.&amp;nbsp; In the case above, I'd still have to use UpgradeObject to attach GetExtendedName and then add it to the invocation bind point for grabbing somethings description.&amp;nbsp; That isn't a big move.&amp;nbsp; Then we use some global functions GetObjectByID to surface a structure capable letting us call methods that we think might exist on our object.&amp;nbsp; We could have just called GetName, but what if that function didn't exist on the object?&amp;nbsp; With the object. syntax we are adding some checks in there to make sure that method has been attached.&amp;nbsp; If not you can exit out of the script or do whatever else is necessary.&lt;/P&gt;
&lt;P dir=ltr&gt;I'm getting pumped guys.&amp;nbsp; This is the kind of interface I've been waiting on for a long time.&amp;nbsp; It is the type of interface that will allow Terrarium to become more functional and provide an easier to replicate sand-box.&amp;nbsp; It will certainly create an easier to create plug-in framework environment.&amp;nbsp; Whidbey is Whidbey though, and that means some time before you get to produce production level apps on this stuff.&amp;nbsp; I'm going to try and go back and fulfill a promise I made months ago to actually complete my plug-in framework documentation that would allow you to create strong plug-in environments today.&lt;/P&gt;
&lt;P dir=ltr&gt;&lt;STRONG&gt;References:&lt;BR&gt;[1] &lt;A id=viewpost.ascx_TitleUrl href="http://blogs.msdn.com/joelpob/archive/2004/04/09/110631.aspx"&gt;A fun lunch with a couple of Rotor fans&lt;/A&gt; &lt;BR&gt;[2] &lt;A id=_7c97432336b_HomePageDays_DaysList__ctl4_DayItem_DayList__ctl0_TitleUrl href="http://blogs.msdn.com/joelpob/archive/2004/03/31/105282.aspx"&gt;&lt;FONT color=#223355&gt;hello, world... LCG (Lightweight Code Gen) style!&lt;/FONT&gt;&lt;/A&gt;&lt;BR&gt;[3] &lt;A id=_7c97432336b_HomePageDays_DaysList__ctl3_DayItem_DayList__ctl0_TitleUrl href="http://blogs.msdn.com/joelpob/archive/2004/04/01/105862.aspx"&gt;&lt;FONT color=#223355&gt;Late-bound invocation notes - CallVirt, Delegates, DynamicMethod, InvokeMember.&lt;/FONT&gt;&lt;/A&gt;&lt;/STRONG&gt;&lt;/P&gt;
&lt;P dir=ltr&gt;Note that I didn't actually cover 3.&amp;nbsp; However, this article provides an EXTREMELY fast solution to some common scripting problems.&amp;nbsp; I'll try and blog about why later when I get more time.&lt;/P&gt;&lt;img src="http://weblogs.asp.net/aggbug.aspx?PostID=110899" width="1" height="1"&gt;</description><category domain="http://weblogs.asp.net/justin_rogers/archive/tags/Terrarium/default.aspx">Terrarium</category><category domain="http://weblogs.asp.net/justin_rogers/archive/tags/Games4+.NET/default.aspx">Games4 .NET</category><category domain="http://weblogs.asp.net/justin_rogers/archive/tags/Security/default.aspx">Security</category><category domain="http://weblogs.asp.net/justin_rogers/archive/tags/CLR+Internals/default.aspx">CLR Internals</category><category domain="http://weblogs.asp.net/justin_rogers/archive/tags/Plug-In+Framework/default.aspx">Plug-In Framework</category><category domain="http://weblogs.asp.net/justin_rogers/archive/tags/Performance/default.aspx">Performance</category></item><item><title>.NET Immutability Tip #1: Nothing is immutable.</title><link>http://weblogs.asp.net/justin_rogers/archive/2004/04/08/110248.aspx</link><pubDate>Fri, 09 Apr 2004 05:24:00 GMT</pubDate><guid isPermaLink="false">c06e2b9d-981a-45b4-a55f-ab0d8bbfdc1c:110248</guid><dc:creator>Justin Rogers</dc:creator><author>Justin Rogers</author><slash:comments>0</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://weblogs.asp.net/justin_rogers/rsscomments.aspx?PostID=110248</wfw:commentRss><comments>http://weblogs.asp.net/justin_rogers/archive/2004/04/08/110248.aspx#comments</comments><description>&lt;P&gt;I figured I'd start with the obvious.&amp;nbsp; You can never control a machine 100%, so there is always the opportunity that whatever systems of protection you have in place, they can be overcome.&amp;nbsp; This same principle applies to security and cheating systems as well.&lt;/P&gt;
&lt;P&gt;Things start to become mutable in .NET under several circumstances.&amp;nbsp; There are always the platform methods of interacting with memory, for instance grabbing the physical address of a variable and setting the data on it.&amp;nbsp; Then there are the debugger tools.&amp;nbsp; These make the process of interacting with memory a bit easier, and if you are being run under a debugger, you can throw immutability out the window.&amp;nbsp; Finally, .NET offers private reflection.&amp;nbsp; I'd say the majority of code that gets run, is run under security that would allow private reflection.&amp;nbsp; Maybe I should lock my dev machine down a bit more, but I've even grown to rely on various private reflection techniques to gain performance improvements in my app (see &lt;A href="http://weblogs.asp.net/justin_rogers/archive/2004/03/31/105296.aspx"&gt;ImageFast&lt;/A&gt;).&lt;/P&gt;
&lt;P&gt;Even saying all that, under normal circumstances the paradigm does calm down a bit.&amp;nbsp; The system does allow us to protect our data and gain immutability of sorts through visibility modifiers, denial of advanced code execution and reflection rights, usage of the readonly flag, and special design considerations.&amp;nbsp; The remainder of the tips in this series will focus on ways to ensure immutability given normal circumstances rather than assuming extraordinary circumstances.&amp;nbsp; I'll try to&amp;nbsp;base the tips on usable scenarios such as the plug-ins, general software design, and gaming logic in addition to provide thorough code samples describing each technique.&amp;nbsp; (note: if I get enough response to republish the tip series in VB, I will.&amp;nbsp; I plan on aggregating all of these tips together into a single article location anyway, and providing code samples in multiple languages will be easy)&lt;/P&gt;&lt;img src="http://weblogs.asp.net/aggbug.aspx?PostID=110248" width="1" height="1"&gt;</description><category domain="http://weblogs.asp.net/justin_rogers/archive/tags/Security/default.aspx">Security</category><category domain="http://weblogs.asp.net/justin_rogers/archive/tags/CLR+Internals/default.aspx">CLR Internals</category><category domain="http://weblogs.asp.net/justin_rogers/archive/tags/Plug-In+Framework/default.aspx">Plug-In Framework</category><category domain="http://weblogs.asp.net/justin_rogers/archive/tags/Quick+Tips/default.aspx">Quick Tips</category><category domain="http://weblogs.asp.net/justin_rogers/archive/tags/Software+Design/default.aspx">Software Design</category></item></channel></rss>