tags:

views:

537

answers:

6

Me and my friend, are writing an IRC C# bot, and we are looking for a way to include a module system.. so that people can write modules, load them into the bot. Here is the end result im looking for..

Basically, the bot uses Regex to split up all raw data from the server, and then triggers an event with the data. Then, another class that has subscribed to the events, and has all the voids that get hit with different things, for example a message to a channel would trigger

OnChannelMessage(OnChannelEventArgs e)
{
}

and in OnChannelEventArgs would be the channel, the nick of the sender message etc...

I basically want to have a plugin system so that people can build there modules, then load / unload them, whenever they want, when the bot loads, or while its running. and for how the plugin format is, id like to be able to compile the .cs files on the fly, and in the plugin.cs file, would be a few things...

  1. what event to capture, for example, OnChannelMessage, and the Channeldata in OnChannelEventArgs
  2. what to do when this information is given,
  3. a help text in (that i can call from inside the main bot.. so say a string, help = "this is the help for this plugin" that can be returned at any time without actually calling the plugin)
  4. what the plugin name is etc

Thanks for any ideas + comments, code examples welcomed! (please) :)

Hope to hear some input soon :)

+3  A: 

Please have a look a microsoft's Managed Extensibility Framework (MEF) - It proposes to do some of what you want, and provides a lot of other features:

Google results for 'managed extensibility framework'

Aviad P.
+1 it also might be interesting to note that it will be part of .NET 4.0
Wim Coenen
+9  A: 

The Managed Extensibility Framework (MEF) provides exactly what you are looking for except they call it a Plugin architecture. You could provide a common interface which you users could build on and then MEF could scan a directory for dlls and load any Exports dynamically.

For example your plugin authors could create something like

[Export(typeof(IModule))]
public class MyModule : IModule
{
   void HandleMessage(ChannelEventArgs e) {}
}

Then your code could look something like this:

void OnChannelMessage(ChannelEventArgs e)
{
  foreach(IModule module in Modules)
  {
     module.HandleMessage(e);
  }
}

[Import]
public IEnumerable<IModule> Modules { get; set; }
bendewey
Is there a way to do something like this without using any type of external Frameworks?
Tommy
This is open source, so you can look, but with the `FileSystemWatcher`, `Assembly.Load`, and some reflection you should be able to model something similar fairly easily.
bendewey
You could write the framework code yourself... Why re-invent the wheel though??
Paolo
The Import side would be alot more difficult then the export side. You may want to avoid the setter injection piece and just use a helper function to get the imports instead.
bendewey
@Paolo agreed, especially since this is being shipped with .NET4
bendewey
+4  A: 

I've used stuff like this on projects before, but it's been a while. There are probably frameworks out there that do this kind of stuff for you.

To write your own plug-in architecture, basically you want to define an interface for all the modules to implement, and put this in an assembly shared by both your program and the modules:

public interface IModule
{
     //functions/properties/events of a module
}

Then your implementors will code their modules to this assembly, preferably with a default constructor.

public class SomeModule : IModule {} ///stuff

In your program (supposing your modules will be compiled into their own assemblies) you load a reference an assembly containing a module, find the types that implement the module interface, and instantiate them:

var moduleAssembly = System.Reflection.Assembly.LoadFrom("assembly file");
var moduleTypes = moduleAssembly.GetTypes().Where(t => 
   t.GetInterfaces().Contains(typeof(IModule)));

var modules = moduleTypes.Select( type =>
            {   
               return  (IModule) Activator.CreateInstance(type);
            });

If you want to compile the code on the fly: you create a compiler object, tell it what assemblies to reference (System and the one that contains IModule, plus any other references needed), tell it to compile the source file into an assembly. From that, you get the exported types, filter keeping those which implement the IModule, and instantiate them.

//I made up an IModule in namespace IMod, with string property S
string dynamicCS = @"using System; namespace DYN 
             { public class Mod : IMod.IModule { public string S 
                 { get { return \"Im a module\"; } } } }";

var compiler = new Microsoft.CSharp.CSharpCodeProvider().CreateCompiler();
var options = new System.CodeDom.Compiler.CompilerParameters(
       new string[]{"System.dll", "IMod.dll"});

var dynamicAssembly= compiler.CompileAssemblyFromSource(options, dynamicCS);
//you need to check it for errors here

var dynamicModuleTypes = dynamicAssembly.CompiledAssembly.GetExportedTypes()
    .Where(t => t.GetInterfaces().Contains(typeof(IMod.IModule)));
var dynamicModules = dynModType.Select(t => (IMod.IModule)Activator.CreateInstance(t));

Look up tutorials on plug-in architectures and loading dynamic assmeblies to get a better feel for doing this kind of stuff. This is just the start. Once you get the basics down, you can start doing some really cool things.

As for handling the metadata (module X is named YYY and should handle events A,B and C): Try to work this into your type system. You could make up different interfaces for the different functions/events, or you could put all the functions on one interface, and put attributes (you'd declare these in the shared assembly) on the module classes, using the attributes to declare which events the module should subscribe to. Basically you want to make it as simple as possible for people to write modules for your system.

 enum ModuleTypes { ChannelMessage, AnotherEvent, .... }

 [Shared.Handles(ModuleTypes.ChannelMessage)]
 [Shared.Handles(ModuleTypes.AnotherEvent)]
 class SomeModule : IModule { ... }

or

 //this is a finer-grained way of doing it
 class ChannelMessageLogger : IChannelMessage {}
 class PrivateMessageAutoReply : IPrivateMessage {}

have fun!

dan
I would highly recommend using existing plugin frameworks like MEF or System.AddIns; they'll save you a lot of plumbing headaches.
Judah Himango
Me too, but the OP asked for a non-framework solution. Plus the basics are nice to know
dan
A: 

Check out WCF, since it provides flexible solutions for communication.

Danny Varod
+1  A: 

There is another option in the hearth of .NET 3.5 called Managed AddIn Framework (MAF). Check the following links out.

Sample and Tools: http://clraddins.codeplex.com/

MAF Blog: http://blogs.msdn.com/clraddins/

A good article: http://blogs.microsoft.co.il/blogs/bursteg/archive/2007/08/13/Build-AddIns-with-System-AddIn.aspx

Mehdi Golchin
A: 

MEF - Managed Extensibility Framework is the buzzword right now - I think that would be the best fit.

If that doesnt do the trick you could also look up on Prism, they have another approach to implement modules.

Mark Pearl