<?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 : Terrarium, Plug-In Framework</title><link>http://weblogs.asp.net/justin_rogers/archive/tags/Terrarium/Plug-In+Framework/default.aspx</link><description>Tags: Terrarium, 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/Artificial+Intelligence/default.aspx">Artificial Intelligence</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/Security/default.aspx">Security</category><category domain="http://weblogs.asp.net/justin_rogers/archive/tags/Terrarium/default.aspx">Terrarium</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/Artificial+Intelligence/default.aspx">Artificial Intelligence</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/Terrarium/default.aspx">Terrarium</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/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/Terrarium/default.aspx">Terrarium</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/CLR+Internals/default.aspx">CLR Internals</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/Performance/default.aspx">Performance</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/Security/default.aspx">Security</category><category domain="http://weblogs.asp.net/justin_rogers/archive/tags/Terrarium/default.aspx">Terrarium</category></item><item><title>First article in a series on plug-in frameworks in .NET</title><link>http://weblogs.asp.net/justin_rogers/archive/2004/01/21/61045.aspx</link><pubDate>Wed, 21 Jan 2004 11:14:00 GMT</pubDate><guid isPermaLink="false">c06e2b9d-981a-45b4-a55f-ab0d8bbfdc1c:61045</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=61045</wfw:commentRss><comments>http://weblogs.asp.net/justin_rogers/archive/2004/01/21/61045.aspx#comments</comments><description>&lt;P&gt;This is the first article in a series of about 5-10 articles on a plug-in framework I'm developing.&amp;nbsp; Don't run away and hide yet, the intent of the articles is to end with an intense study of providing a security system to allow arbitrary user code to run.&amp;nbsp; I'll be borrowing ideas from the .NET Terrarium, existing plug-in architectures, and my own work on several image editor applications with plug-in interfaces.&lt;/P&gt;
&lt;P&gt;The first article is a rather long-winded run-down of plug-in marking within an assembly.&amp;nbsp; Most of the stuff may seem common sense, but I've seen a number of plug-in framework implementations and everyone is doing something different.&amp;nbsp; This is the same thing that happens in the unmanaged world, but you would think there would be a more unified process using managed code.&amp;nbsp; I really think there is, so I've defined all the ways you can mark types in an assembly as plug-ins and weighed the pro and cons of each method along with source code to demonstrate how you would use each, the performance, and how they can be used together to provide a rock solid marking implementation.&lt;/P&gt;
&lt;P dir=ltr style="MARGIN-RIGHT: 0px"&gt;&lt;A href="http://weblogs.asp.net/justin_rogers/articles/61042.aspx"&gt;Plug-in Framework (Part1): Marking Types for Consumption&lt;/A&gt; &lt;/P&gt;&lt;img src="http://weblogs.asp.net/aggbug.aspx?PostID=61045" 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><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/WinForms/default.aspx">WinForms</category></item></channel></rss>