OSDN Git Service

Added plugin attribute class, and marked plugins with meta data.
[radegast/radegast.git] / Radegast / Core / PluginInterface / PluginManager.cs
1 // 
2 // Radegast Metaverse Client
3 // Copyright (c) 2009, Radegast Development Team
4 // All rights reserved.
5 // 
6 // Redistribution and use in source and binary forms, with or without
7 // modification, are permitted provided that the following conditions are met:
8 // 
9 //     * Redistributions of source code must retain the above copyright notice,
10 //       this list of conditions and the following disclaimer.
11 //     * Redistributions in binary form must reproduce the above copyright
12 //       notice, this list of conditions and the following disclaimer in the
13 //       documentation and/or other materials provided with the distribution.
14 //     * Neither the name of the application "Radegast", nor the names of its
15 //       contributors may be used to endorse or promote products derived from
16 //       this software without specific prior written permission.
17 // 
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22 // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24 // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26 // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 //
29 // $Id$
30 //
31 using System;
32 using System.Collections.Generic;
33 using System.Linq;
34 using System.Text;
35 using System.IO;
36 using System.Threading;
37 using System.Reflection;
38 using System.CodeDom.Compiler;
39 using System.Windows.Forms;
40 using OpenMetaverse;
41 using Microsoft.CSharp;
42
43 namespace Radegast
44 {
45     /// <summary>
46     /// Handles loading Radegast plugins
47     /// </summary>
48     public class PluginManager : IDisposable
49     {
50         List<IRadegastPlugin> PluginsLoaded = new List<IRadegastPlugin>();
51         RadegastInstance instance;
52
53         public PluginManager(RadegastInstance instance)
54         {
55             this.instance = instance;
56         }
57
58         public void Dispose()
59         {
60             lock (PluginsLoaded)
61             {
62                 List<IRadegastPlugin> unload = new List<IRadegastPlugin>(PluginsLoaded);
63                 unload.ForEach(plug =>
64                 {
65                     PluginsLoaded.Remove(plug);
66                     try
67                     {
68                         plug.StopPlugin(instance);
69                     }
70                     catch (Exception ex)
71                     {
72                         Logger.Log("ERROR in Shutdown Plugin: " + plug + " because " + ex, Helpers.LogLevel.Debug, ex);
73                     }
74                 });
75             }
76         }
77
78         public static PluginAttribute GetAttributes(IRadegastPlugin plug)
79         {
80             PluginAttribute a = null;
81
82             foreach(Attribute attr in Attribute.GetCustomAttributes(plug.GetType()))
83             {
84                 if (attr is PluginAttribute)
85                     a = (PluginAttribute)attr;
86             }
87
88             if (a == null)
89             {
90                 a = new PluginAttribute();
91                 a.Name = plug.GetType().FullName;
92             }
93
94             return a;
95         }
96
97         public void StartPlugins()
98         {
99             lock (PluginsLoaded)
100             {
101                 foreach (IRadegastPlugin plug in PluginsLoaded)
102                 {
103                     try
104                     {
105                         plug.StartPlugin(instance);
106                     }
107                     catch (Exception ex)
108                     {
109                         Logger.Log("ERROR in Starting Radegast Plugin: " + plug + " because " + ex, Helpers.LogLevel.Debug);
110                     }
111                 }
112             }
113         }
114
115         public void ScanAndLoadPlugins()
116         {
117             string dirName = Application.StartupPath;
118
119             if (!Directory.Exists(dirName)) return;
120
121             foreach (string loadfilename in Directory.GetFiles(dirName))
122             {
123                 if (loadfilename.ToLower().EndsWith(".dll") || loadfilename.ToLower().EndsWith(".exe"))
124                 {
125                     try
126                     {
127                         Assembly assembly = Assembly.LoadFile(loadfilename);
128                         LoadAssembly(loadfilename, assembly);
129                     }
130                     catch (BadImageFormatException)
131                     {
132                         // non .NET .dlls
133                     }
134                     catch (ReflectionTypeLoadException)
135                     {
136                         // Out of date or dlls missing sub dependencies
137                     }
138                     catch (TypeLoadException)
139                     {
140                         // Another version of: Out of date or dlls missing sub dependencies
141                     }
142                     catch (Exception ex)
143                     {
144                         Logger.Log("ERROR in Radegast Plugin: " + loadfilename + " because " + ex, Helpers.LogLevel.Debug);
145                     }
146                 }
147             }
148         }
149
150         public void LoadCSharpScriptFile(string filename)
151         {
152             try { LoadCSharpScript(File.ReadAllText(filename)); }
153             catch (Exception ex)
154             {
155                 Logger.Log("Failed loading C# script " + filename + ": ", Helpers.LogLevel.Warning, ex);
156             }
157         }
158
159         public void LoadCSharpScript(string code)
160         {
161             ThreadPool.QueueUserWorkItem(sender =>
162             {
163                 try
164                 {
165                     // *** Generate dynamic compiler
166                     Dictionary<string, string> loCompilerOptions = new Dictionary<string, string>();
167                     loCompilerOptions.Add("CompilerVersion", "v3.5");
168                     CSharpCodeProvider loCompiler = new CSharpCodeProvider(loCompilerOptions);
169                     CompilerParameters loParameters = new CompilerParameters();
170
171                     // *** Start by adding any referenced assemblies
172                     loParameters.ReferencedAssemblies.Add("OpenMetaverse.StructuredData.dll");
173                     loParameters.ReferencedAssemblies.Add("OpenMetaverseTypes.dll");
174                     loParameters.ReferencedAssemblies.Add("OpenMetaverse.dll");
175                     loParameters.ReferencedAssemblies.Add("Radegast.exe");
176                     loParameters.ReferencedAssemblies.Add("System.dll");
177                     loParameters.ReferencedAssemblies.Add("System.Core.dll");
178                     loParameters.ReferencedAssemblies.Add("System.Drawing.dll");
179                     loParameters.ReferencedAssemblies.Add("System.Windows.Forms.dll");
180
181                     // *** Load the resulting assembly into memory
182                     loParameters.GenerateInMemory = true;
183                     loParameters.GenerateExecutable = false;
184
185                     // *** Now compile the whole thing
186                     CompilerResults loCompiled =
187                             loCompiler.CompileAssemblyFromSource(loParameters, code);
188
189                     // *** Check for compilation erros
190                     if (loCompiled.Errors.HasErrors)
191                     {
192                         string lcErrorMsg = "";
193                         lcErrorMsg = "Compilation failed: " + loCompiled.Errors.Count.ToString() + " errors:";
194
195                         for (int x = 0; x < loCompiled.Errors.Count; x++)
196                             lcErrorMsg += "\r\nLine: " +
197                                          loCompiled.Errors[x].Line.ToString() + " - " +
198                                          loCompiled.Errors[x].ErrorText;
199
200                         instance.TabConsole.DisplayNotificationInChat(lcErrorMsg, ChatBufferTextStyle.Alert);
201                         return;
202                     }
203
204                     instance.TabConsole.DisplayNotificationInChat("Compilation successful.");
205                     Assembly loAssembly = loCompiled.CompiledAssembly;
206                     instance.MainForm.Invoke(new MethodInvoker(() => LoadAssembly("Dynamically compiled", loAssembly, true)));
207                 }
208                 catch (Exception ex)
209                 {
210                     Logger.Log("Failed loading C# script: ", Helpers.LogLevel.Warning, ex);
211                 }
212             });
213         }
214
215         public void LoadAssembly(string loadfilename, Assembly assembly)
216         {
217             LoadAssembly(loadfilename, assembly, false);
218         }
219
220         public void LoadAssembly(string loadfilename, Assembly assembly, bool startPlugins)
221         {
222             foreach (Type type in assembly.GetTypes())
223             {
224                 if (typeof(IRadegastPlugin).IsAssignableFrom(type))
225                 {
226                     if (type.IsInterface) continue;
227                     try
228                     {
229                         IRadegastPlugin plug = null;
230                         ConstructorInfo constructorInfo = type.GetConstructor(new Type[] { typeof(RadegastInstance) });
231                         if (constructorInfo != null)
232                             plug = (IRadegastPlugin)constructorInfo.Invoke(new[] { instance });
233                         else
234                         {
235                             constructorInfo = type.GetConstructor(new Type[] { });
236                             if (constructorInfo != null)
237                                 plug = (IRadegastPlugin)constructorInfo.Invoke(new object[0]);
238                             else
239                             {
240                                 Logger.Log("ERROR Constructing Radegast Plugin: " + loadfilename + " because " + type + " has no usable constructor.", Helpers.LogLevel.Debug);
241                                 continue;
242                             }
243                         }
244                         lock (PluginsLoaded) PluginsLoaded.Add(plug);
245                         if (startPlugins && plug != null)
246                         {
247                             try { plug.StartPlugin(instance); }
248                             catch (Exception ex) { Logger.Log(string.Format("Failed starting plugin {0}:", type), Helpers.LogLevel.Error, ex); }
249                         }
250                     }
251                     catch (Exception ex)
252                     {
253                         Logger.Log("ERROR Constructing Radegast Plugin: " + loadfilename + " because " + ex,
254                                    Helpers.LogLevel.Debug);
255                     }
256                 }
257                 else
258                 {
259                     try
260                     {
261                         instance.CommandsManager.LoadType(type);
262                     }
263                     catch (Exception ex)
264                     {
265                         Logger.Log("ERROR in Radegast Plugin: " + loadfilename + " Command: " + type +
266                                    " because " + ex.Message + " " + ex.StackTrace, Helpers.LogLevel.Debug);
267                     }
268                 }
269             }
270         }
271     }
272 }