This blog has moved http://www.sharplogic.com/blogs/ed

This blog has moved here<!--meta http-equiv="refresh" content="0;url=http://www.sharplogic.com/blogs/ed"-->

Hooray--Sort Of

I was able to hack around inside my already hacked-together turn-based strategy app so that it can load AI from custom assemblies. It actually works in a pretty easy way now where you create a class that derives from a base class (AIUser) in the main library assembly. After implementing a few abstract methods you build it and drop the assembly in the same folder as the main library assembly. Then, if you want one of the game's armies to use your new AI, you point their "AI" property as defined in the game's XML file, and run it. The change is pretty simple, such as from

<AI>TBSDefaultAI.TBSDefaultAI</AI>

to

<AI>LandOnlyAI.LandOnlyAI</AI>

Unfortunately, I ran into a few things I'm not too sure how to handle correctly. It seems like you can't cast a dynamically instantiated object into a specific type. For example, I tried to do:

Assembly assembly = Assembly.LoadFile(assemblyPath);

m_AIUser aiUser = (AIUser) assembly.CreateInstance(className, true);

where assemblyPath and className are strings passed into the method and AIUser is the abstract base class of the object created in CreateInstance (but from another assembly). I wanted to just be able to call the abstract methods on the base class cast of the new object in the same way I would call any other, and have them fulfilled by the overriden implementations in the dynamic object. I couldn't seem to hack that in, so I ended up using the Type object and invoking the methods and properties on the object in a very formal way: 

this.m_Type = assembly.GetType(className);

this.m_Type.GetProperty("ActionedUnit").GetValue(this.m_AIUser, null);

Maybe there's an easy way to actually do this cleanly that I've overlooked, but in the meantime I've built a simple proxy class that derives from the AIUser class and does all of this assembly and type work behind the scenes for the consuming portion of the game. I guess I could have a method called "public AIUser GetMe()" that just does a "return this;" and pretend like I was able to easily cast the object from the beginning. Then again, I'm probably just not reading the documentation properly.

I'll probably be able to get the code up by lunch on Monday.

Posted: Jul 26 2003, 05:28 PM by EdKaim | with 3 comment(s)
Filed under:

Comments

Roy Osherove said:

Hey Ed. Actually, there is a more elegant way of doing this: Use interfaces. I've written an article that uses interfaces to dynamically load assemblies. You can then use the interfaces to get the abstract object out and so on, or you can just use the interface itself. Article 1: Load plugins by interfaces: http://dotnetweblogs.com/rosherove/story/7610.aspx Article 2: Load assemblies dynamically that contain plugins: http://weblogs.asp.net/rosherove/story/8048.aspx Hope this helps. Regards, Roy Osherove
# July 27, 2003 9:05 AM

Erv Walter said:

I think this is related to the fact that you loaded the assembly with Assembly.LoadFile(). When an assembly is loaded this way, you'll have all kinds of trouble. Suzanne Cook talks about this here: http://blogs.gotdotnet.com/suzcook/permalink.aspx/d5c5e14a-3612-4af1-a9b7-0a144c8dbf16 I believe you will have much better luck if you don't try to load the assembly yourself. If the assembly is in locatable by the runtime, the best approach seems to be to have the "assembly qualified type name" in your config file and then use: Type myType = Type.GetType(qualifiedTypeName); AIUser myAIUser = (AIUser) Activator.CreateInstance(myType); Good Luck! This project looks very cool.
# July 27, 2003 4:59 PM

Ed Kaim said:

Awesome! Thanks for all the quick replies. I'm going to post the project up as a GDN workspace and then link to it from here. This way anyone can jump in and build anything they'd like to see as well.
# July 28, 2003 4:08 AM
Leave a Comment

(required) 

(required) 

(optional)

(required)