2 // Radegast Metaverse Client
3 // Copyright (c) 2009, Radegast Development Team
4 // All rights reserved.
6 // Redistribution and use in source and binary forms, with or without
7 // modification, are permitted provided that the following conditions are met:
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.
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.
32 using System.Collections.Generic;
36 using System.Threading;
37 using System.Reflection;
38 using System.CodeDom.Compiler;
39 using System.Windows.Forms;
41 using Microsoft.CSharp;
46 /// Information about loaded plugin
48 public class PluginInfo
50 /// <summary>File name from which the plugin was loaded, cannot load plugin twice from the same file</summary>
51 public string FileName { get; set; }
52 /// <summary>Plugin class</summary>
53 public IRadegastPlugin Plugin { get; set; }
54 /// <summary>Is plugin started</summary>
55 public bool Started { get; set; }
56 /// <summary>Plugin class</summary>
57 public PluginAttribute Attribures
61 if (Plugin == null) return null;
62 return PluginManager.GetAttributes(Plugin);
68 /// Handles loading Radegast plugins
70 public class PluginManager : IDisposable
72 List<PluginInfo> PluginsLoaded = new List<PluginInfo>();
73 RadegastInstance instance;
76 /// Gets the list of currently loaded plugins
78 public List<PluginInfo> Plugins
87 /// Creates new PluginManager
89 /// <param name="instance">Radegast instance PluginManager is associated with</param>
90 public PluginManager(RadegastInstance instance)
92 this.instance = instance;
96 /// Unloads all plugins
102 List<PluginInfo> unload = new List<PluginInfo>(PluginsLoaded);
103 unload.ForEach(plug =>
113 /// <param name="plug">Plugin to unload</param>
114 public void UnloadPlugin(PluginInfo plug)
119 PluginsLoaded.RemoveAll((PluginInfo info) => { return plug.FileName == info.FileName; });
120 plug.Plugin.StopPlugin(instance);
124 Logger.Log("ERROR in Shutdown Plugin: " + plug + " because " + ex, Helpers.LogLevel.Debug, ex);
129 /// Gets extended atributes for plugin
131 /// <param name="plug">Plugin to lookup extra attributes</param>
132 /// <returns>Extended atributes for plugin</returns>
133 public static PluginAttribute GetAttributes(IRadegastPlugin plug)
135 PluginAttribute a = null;
137 foreach (Attribute attr in Attribute.GetCustomAttributes(plug.GetType()))
139 if (attr is PluginAttribute)
140 a = (PluginAttribute)attr;
145 a = new PluginAttribute();
146 a.Name = plug.GetType().FullName;
153 /// Starts all loaded plugins
155 public void StartPlugins()
159 foreach (PluginInfo plug in PluginsLoaded)
161 Logger.DebugLog("Starting " + plug.Plugin.GetType().FullName);
164 plug.Plugin.StartPlugin(instance);
169 Logger.Log("ERROR in Starting Radegast Plugin: " + plug + " because " + ex, Helpers.LogLevel.Debug);
176 /// Loads a plugin for a precompiled assembly or source file
178 /// <param name="loadFileName">File to load</param>
179 /// <param name="stratPlugins">Start plugins that are found in the assembly</param>
180 public void LoadPluginFile(string loadFileName, bool stratPlugins)
182 if (loadFileName.ToLower().EndsWith(@".cs"))
184 LoadCSharpScriptFile(loadFileName, stratPlugins);
186 else if (loadFileName.ToLower().EndsWith(".dll") || loadFileName.ToLower().EndsWith(".exe"))
190 Assembly assembly = Assembly.LoadFile(loadFileName);
191 LoadAssembly(loadFileName, assembly, stratPlugins);
193 catch (BadImageFormatException)
197 catch (ReflectionTypeLoadException)
199 // Out of date or dlls missing sub dependencies
201 catch (TypeLoadException)
203 // Another version of: Out of date or dlls missing sub dependencies
207 Logger.Log("ERROR in Radegast Plugin: " + loadFileName + " because " + ex, Helpers.LogLevel.Debug);
213 /// Scans and load plugins from Radegast application folder without starting them
215 public void ScanAndLoadPlugins()
217 string dirName = Application.StartupPath;
219 if (!Directory.Exists(dirName)) return;
221 foreach (string loadFileName in Directory.GetFiles(dirName))
223 LoadPluginFile(loadFileName, false);
228 /// Loads and compiles a plugin from a C# source file
230 /// <param name="fileName">Load plugin from this filename</param>
231 /// <param name="startPlugins">Start plugins found in the assembly after complilation</param>
232 public void LoadCSharpScriptFile(string fileName, bool startPlugins)
234 try { LoadCSharpScript(fileName, File.ReadAllText(fileName), startPlugins); }
237 Logger.Log("Failed loading C# script " + fileName + ": ", Helpers.LogLevel.Warning, ex);
242 /// Compiles plugin from string source code
244 /// <param name="fileName">File name from which source was loaded</param>
245 /// <param name="code">Source code</param>
246 /// <param name="startPlugins">Start plugins found in the assembly after complilation</param>
247 public void LoadCSharpScript(string fileName, string code, bool startPlugins)
251 // *** Generate dynamic compiler
252 Dictionary<string, string> loCompilerOptions = new Dictionary<string, string>();
253 loCompilerOptions.Add("CompilerVersion", "v3.5");
254 CSharpCodeProvider loCompiler = new CSharpCodeProvider(loCompilerOptions);
255 CompilerParameters loParameters = new CompilerParameters();
257 // *** Start by adding any referenced assemblies
258 loParameters.ReferencedAssemblies.Add("OpenMetaverse.StructuredData.dll");
259 loParameters.ReferencedAssemblies.Add("OpenMetaverseTypes.dll");
260 loParameters.ReferencedAssemblies.Add("OpenMetaverse.dll");
261 loParameters.ReferencedAssemblies.Add("Radegast.exe");
262 loParameters.ReferencedAssemblies.Add("System.dll");
263 loParameters.ReferencedAssemblies.Add("System.Core.dll");
264 loParameters.ReferencedAssemblies.Add("System.Drawing.dll");
265 loParameters.ReferencedAssemblies.Add("System.Windows.Forms.dll");
267 // *** Load the resulting assembly into memory
268 loParameters.GenerateInMemory = true;
269 loParameters.GenerateExecutable = false;
271 // *** Now compile the whole thing
272 CompilerResults loCompiled =
273 loCompiler.CompileAssemblyFromSource(loParameters, code);
275 // *** Check for compilation erros
276 if (loCompiled.Errors.HasErrors)
278 string lcErrorMsg = "";
279 lcErrorMsg = "Compilation failed: " + loCompiled.Errors.Count.ToString() + " errors:";
281 for (int x = 0; x < loCompiled.Errors.Count; x++)
282 lcErrorMsg += "\r\nLine: " +
283 loCompiled.Errors[x].Line.ToString() + " - " +
284 loCompiled.Errors[x].ErrorText;
286 instance.TabConsole.DisplayNotificationInChat(lcErrorMsg, ChatBufferTextStyle.Alert);
290 instance.TabConsole.DisplayNotificationInChat("Compilation successful.");
291 Assembly loAssembly = loCompiled.CompiledAssembly;
292 LoadAssembly(fileName, loAssembly, startPlugins);
296 Logger.Log("Failed loading C# script: ", Helpers.LogLevel.Warning, ex);
301 /// Scans assembly for supported types
303 /// <param name="loadfilename">File name from which assembly was loaded</param>
304 /// <param name="assembly">Assembly to scan for supported types</param>
305 /// <param name="startPlugins">Start plugins found in the assembly after complilation</param>
306 public void LoadAssembly(string loadfilename, Assembly assembly, bool startPlugins)
308 if (null != PluginsLoaded.Find((PluginInfo info) => { return info.FileName == loadfilename; }))
310 Logger.Log("Plugin already loaded, skipping: " + loadfilename, Helpers.LogLevel.Info);
313 instance.TabConsole.DisplayNotificationInChat("Plugin already loaded, skipping: " + loadfilename);
318 foreach (Type type in assembly.GetTypes())
320 if (typeof(IRadegastPlugin).IsAssignableFrom(type))
322 if (type.IsInterface) continue;
325 IRadegastPlugin plug = null;
326 ConstructorInfo constructorInfo = type.GetConstructor(new Type[] { typeof(RadegastInstance) });
327 if (constructorInfo != null)
328 plug = (IRadegastPlugin)constructorInfo.Invoke(new[] { instance });
331 constructorInfo = type.GetConstructor(new Type[] { });
332 if (constructorInfo != null)
333 plug = (IRadegastPlugin)constructorInfo.Invoke(new object[0]);
336 Logger.Log("ERROR Constructing Radegast Plugin: " + loadfilename + " because " + type + " has no usable constructor.", Helpers.LogLevel.Debug);
340 PluginInfo info = new PluginInfo()
342 FileName = loadfilename,
347 lock (PluginsLoaded) PluginsLoaded.Add(info);
348 if (startPlugins && plug != null)
350 try { plug.StartPlugin(instance); info.Started = true; }
351 catch (Exception ex) { Logger.Log(string.Format("Failed starting plugin {0}:", type), Helpers.LogLevel.Error, ex); }
356 Logger.Log("ERROR Constructing Radegast Plugin: " + loadfilename + " because " + ex,
357 Helpers.LogLevel.Debug);
364 instance.CommandsManager.LoadType(type);
368 Logger.Log("ERROR in Radegast Plugin: " + loadfilename + " Command: " + type +
369 " because " + ex.Message + " " + ex.StackTrace, Helpers.LogLevel.Debug);