* Added ExtensionLoader, a generic plugin system with internal, external assembly, and source file plugin loading as well as interface binding

* Switched Simian over to ExtensionLoader

git-svn-id: http://libopenmetaverse.googlecode.com/svn/trunk@2258 52acb1d6-8a22-11de-b505-999d5b087335
This commit is contained in:
John Hurliman
2008-10-05 22:05:18 +00:00
parent 4f79c6aa7c
commit 644d415e60
24 changed files with 315 additions and 265 deletions

View File

@@ -1,150 +0,0 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using System.CodeDom.Compiler;
using System.IO;
using OpenMetaverse;
namespace Simian
{
public static class ExtensionLoader
{
/// <summary>
/// Exception thrown when there is a problem with an extension
/// </summary>
public class ExtensionException : Exception
{
public ExtensionException(string message)
: base(message)
{
}
public ExtensionException(string message, Exception innerException)
: base(message, innerException)
{
}
}
/// <summary>Currently loaded extensions</summary>
public static List<ISimianExtension> Extensions;
/// <summary></summary>
public static CodeDomProvider CSCompiler;
/// <summary></summary>
public static CompilerParameters CSCompilerParams;
static ExtensionLoader()
{
Extensions = new List<ISimianExtension>();
CSCompiler = CodeDomProvider.CreateProvider("C#");
CSCompilerParams = new CompilerParameters();
CSCompilerParams.GenerateExecutable = false;
CSCompilerParams.GenerateInMemory = true;
if (System.Diagnostics.Debugger.IsAttached)
CSCompilerParams.IncludeDebugInformation = true;
else
CSCompilerParams.IncludeDebugInformation = false;
CSCompilerParams.ReferencedAssemblies.Add("OpenMetaverseTypes.dll");
CSCompilerParams.ReferencedAssemblies.Add("OpenMetaverse.dll");
CSCompilerParams.ReferencedAssemblies.Add("Simian.exe");
}
/// <summary>
///
/// </summary>
/// <param name="path"></param>
/// <param name="owner"><seealso cref="Simian"/> server that these extensions belong to</param>
public static void LoadAllExtensions(string path, Simian owner)
{
// Load internal extensions
LoadAssemblyExtensions(Assembly.GetExecutingAssembly(), owner);
// Load extensions from external assemblies
List<string> extensionNames = ListExtensionAssemblies(path);
foreach (string name in extensionNames)
LoadAssemblyExtensions(Assembly.LoadFile(name), owner);
// Load extensions from external code files
extensionNames = ListExtensionSourceFiles(path);
foreach (string name in extensionNames)
{
CompilerResults results = CSCompiler.CompileAssemblyFromFile(CSCompilerParams, name);
if (results.Errors.Count == 0)
{
LoadAssemblyExtensions(results.CompiledAssembly, owner);
}
else
{
Logger.Log("Error(s) compiling " + name, Helpers.LogLevel.Error);
foreach (CompilerError error in results.Errors)
Logger.Log(error.ToString(), Helpers.LogLevel.Error);
}
}
}
public static List<string> ListExtensionAssemblies(string path)
{
List<string> plugins = new List<string>();
string[] files = Directory.GetFiles(path, "Simian.*.dll");
foreach (string f in files)
{
try
{
Assembly a = Assembly.LoadFrom(f);
System.Type[] types = a.GetTypes();
foreach (System.Type type in types)
{
if (type.GetInterface("ISimianExtension") != null)
{
plugins.Add(f);
break;
}
}
}
catch (Exception e)
{
Logger.Log(String.Format("Unrecognized extension {0}: {1}", f, e.Message),
Helpers.LogLevel.Warning, e);
}
}
return plugins;
}
public static List<string> ListExtensionSourceFiles(string path)
{
List<string> plugins = new List<string>();
string[] files = Directory.GetFiles(path, "Simian.*.cs");
foreach (string f in files)
{
if (File.ReadAllText(f).Contains("ISimianExtension"))
plugins.Add(f);
}
return plugins;
}
public static void LoadAssemblyExtensions(Assembly assembly, Simian owner)
{
foreach (Type t in assembly.GetTypes())
{
try
{
if (t.GetInterface("ISimianExtension") != null)
{
ConstructorInfo info = t.GetConstructor(new Type[] { typeof(Simian) });
ISimianExtension extension = (ISimianExtension)info.Invoke(new object[] { owner });
Extensions.Add(extension);
}
}
catch (Exception e)
{
Logger.Log("LoadAssemblyExtensions(): " + e.Message, Helpers.LogLevel.Warning);
}
}
}
}
}

View File

@@ -1,11 +1,12 @@
using System;
using System.Collections.Generic;
using ExtensionLoader;
using OpenMetaverse;
using OpenMetaverse.StructuredData;
namespace Simian.Extensions
{
public class AccountManager : ISimianExtension, IAccountProvider, IPersistable
public class AccountManager : IExtension, IAccountProvider, IPersistable
{
public string StoreName { get { return "Accounts"; } }

View File

@@ -1,13 +1,14 @@
using System;
using System.Collections.Generic;
using System.IO;
using ExtensionLoader;
using OpenMetaverse;
using OpenMetaverse.Imaging;
using OpenMetaverse.Packets;
namespace Simian.Extensions
{
public class AssetManager : ISimianExtension, IAssetProvider
public class AssetManager : IExtension, IAssetProvider
{
Simian Server;
Dictionary<UUID, Asset> AssetStore = new Dictionary<UUID, Asset>();

View File

@@ -1,9 +1,10 @@
using System;
using ExtensionLoader;
using OpenMetaverse;
namespace Simian.Extensions
{
public class AuthFreeForAll : ISimianExtension, IAuthenticationProvider
public class AuthFreeForAll : IExtension, IAuthenticationProvider
{
Simian server;

View File

@@ -1,13 +1,14 @@
using System;
using System.Collections.Generic;
using System.Threading;
using ExtensionLoader;
using OpenMetaverse;
using OpenMetaverse.StructuredData;
using OpenMetaverse.Packets;
namespace Simian.Extensions
{
class AvatarManager : ISimianExtension, IAvatarProvider
class AvatarManager : IExtension, IAvatarProvider
{
Simian Server;
int currentWearablesSerialNum = -1;

View File

@@ -1,5 +1,6 @@
using OpenMetaverse;
using OpenMetaverse.Packets;
using ExtensionLoader;
using System;
using System.Collections.Generic;
using System.Text;
@@ -7,7 +8,7 @@ using System.Threading;
namespace Simian.Extensions
{
public class CoarseLocationUpdates : ISimianExtension
public class CoarseLocationUpdates : IExtension
{
Simian Server;
Timer CoarseLocationTimer;

View File

@@ -1,10 +1,11 @@
using System;
using ExtensionLoader;
using OpenMetaverse;
using OpenMetaverse.Packets;
namespace Simian.Extensions
{
public class ConnectionManagement : ISimianExtension
public class ConnectionManagement : IExtension
{
Simian server;

View File

@@ -1,12 +1,12 @@
using OpenMetaverse;
using OpenMetaverse.Packets;
using System;
using System.Collections.Generic;
using System.Text;
using ExtensionLoader;
using OpenMetaverse;
using OpenMetaverse.Packets;
namespace Simian.Extensions
{
public class FriendManager : ISimianExtension
public class FriendManager : IExtension
{
Simian Server;

View File

@@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using System.Drawing;
using System.Threading;
using ExtensionLoader;
using OpenMetaverse;
using OpenMetaverse.Imaging;
using OpenMetaverse.Packets;
@@ -81,7 +82,7 @@ namespace Simian.Extensions
}
}
public class ImageDelivery : ISimianExtension
public class ImageDelivery : IExtension
{
Simian Server;
AssetTexture defaultJP2;

View File

@@ -1,11 +1,12 @@
using System;
using System.Collections.Generic;
using ExtensionLoader;
using OpenMetaverse;
using OpenMetaverse.Packets;
namespace Simian.Extensions
{
public class InventoryManager : ISimianExtension, IInventoryProvider
public class InventoryManager : IExtension, IInventoryProvider
{
Simian Server;

View File

@@ -1,12 +1,12 @@
using OpenMetaverse;
using OpenMetaverse.Packets;
using System;
using System.Collections.Generic;
using System.Text;
using ExtensionLoader;
using OpenMetaverse;
using OpenMetaverse.Packets;
namespace Simian.Extensions
{
public class Messaging : ISimianExtension
public class Messaging : IExtension
{
Simian Server;

View File

@@ -1,12 +1,12 @@
using OpenMetaverse;
using OpenMetaverse.Packets;
using System;
using System.Collections.Generic;
using System.Text;
using ExtensionLoader;
using OpenMetaverse;
using OpenMetaverse.Packets;
namespace Simian.Extensions
{
class Money : ISimianExtension
class Money : IExtension
{
Simian Server;

View File

@@ -1,13 +1,13 @@
using OpenMetaverse;
using OpenMetaverse.Packets;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using ExtensionLoader;
using OpenMetaverse;
using OpenMetaverse.Packets;
namespace Simian.Extensions
{
public class Movement : ISimianExtension
public class Movement : IExtension
{
const int UPDATE_ITERATION = 100; //rate in milliseconds to send ObjectUpdate
const bool ENVIRONMENT_SOUNDS = true; //collision sounds, splashing, etc

View File

@@ -1,13 +1,14 @@
using System;
using System.Collections.Generic;
using System.Threading;
using ExtensionLoader;
using OpenMetaverse;
using OpenMetaverse.Rendering;
using OpenMetaverse.Packets;
namespace Simian.Extensions
{
public class ObjectManager : ISimianExtension
public class ObjectManager : IExtension
{
Simian Server;

View File

@@ -1,11 +1,12 @@
using System;
using System.Collections.Generic;
using ExtensionLoader;
using OpenMetaverse;
using OpenMetaverse.Packets;
namespace Simian.Extensions
{
public class ParcelManager : ISimianExtension, IParcelProvider
public class ParcelManager : IExtension, IParcelProvider
{
Simian server;
Dictionary<int, Parcel> parcels = new Dictionary<int, Parcel>();

View File

@@ -1,11 +1,12 @@
using System;
using System.Collections.Generic;
using ExtensionLoader;
using OpenMetaverse;
using OpenMetaverse.Rendering;
namespace Simian.Extensions
{
public class RenderingPluginMesher : ISimianExtension, IMeshingProvider
public class RenderingPluginMesher : IExtension, IMeshingProvider
{
Simian Server;
IRendering Renderer;

View File

@@ -5,13 +5,14 @@ using System.Drawing.Imaging;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading;
using ExtensionLoader;
using OpenMetaverse;
using OpenMetaverse.Imaging;
using OpenMetaverse.Packets;
namespace Simian.Extensions
{
public class SceneManager : ISimianExtension, ISceneProvider
public class SceneManager : IExtension, ISceneProvider
{
Simian server;
DoubleDictionary<uint, UUID, SimulationObject> sceneObjects = new DoubleDictionary<uint, UUID, SimulationObject>();

View File

@@ -3,6 +3,7 @@ using System.Collections;
using System.Collections.Generic;
using System.Net;
using System.Threading;
using ExtensionLoader;
using OpenMetaverse;
using OpenMetaverse.Packets;
@@ -67,7 +68,7 @@ namespace Simian
}
}
public class UDPManager : ISimianExtension, IUDPProvider
public class UDPManager : IExtension, IUDPProvider
{
Simian Server;
UDPServer udpServer;

View File

@@ -2,12 +2,13 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Xml;
using ExtensionLoader;
using OpenMetaverse;
using OpenMetaverse.StructuredData;
namespace Simian.Extensions
{
public class XMLPersistence : ISimianExtension, IPersistenceProvider
public class XMLPersistence : IExtension, IPersistenceProvider
{
Simian server;

View File

@@ -1,18 +0,0 @@
namespace Simian
{
/// <summary>
/// Abstract base for rendering plugins
/// </summary>
public interface ISimianExtension
{
/// <summary>
/// Called when the simulator is initializing
/// </summary>
void Start();
/// <summary>
/// Called when the simulator is shutting down
/// </summary>
void Stop();
}
}

View File

@@ -5,6 +5,8 @@ using System.IO;
using System.Text;
using System.Xml;
using System.Threading;
using System.Reflection;
using ExtensionLoader;
using OpenMetaverse;
using OpenMetaverse.Capabilities;
using OpenMetaverse.Packets;
@@ -54,16 +56,35 @@ namespace Simian
RegionHandle = Helpers.UIntsToLong(REGION_X, REGION_Y);
// Load all of the extensions
ExtensionLoader.LoadAllExtensions(AppDomain.CurrentDomain.BaseDirectory, this);
foreach (ISimianExtension extension in ExtensionLoader.Extensions)
try
{
// Assign to an interface if possible
TryAssignToInterface(extension);
// Load all of the extensions
List<string> references = new List<string>();
references.Add("OpenMetaverseTypes.dll");
references.Add("OpenMetaverse.dll");
references.Add("Simian.exe");
Dictionary<Type, FieldInfo> assignables = GetInterfaces();
ExtensionLoader<Simian>.LoadAllExtensions(Assembly.GetExecutingAssembly(),
AppDomain.CurrentDomain.BaseDirectory, this, references,
"Simian.*.dll", "Simian.*.cs", this, assignables);
}
catch (ExtensionException ex)
{
Logger.Log("Interface loading failed, shutting down: " + ex.Message, Helpers.LogLevel.Error);
Stop();
return false;
}
foreach (ISimianExtension extension in ExtensionLoader.Extensions)
foreach (IExtension extension in ExtensionLoader<Simian>.Extensions)
{
// Track all of the extensions with persistence
if (extension is IPersistable)
PersistentExtensions.Add((IPersistable)extension);
}
foreach (IExtension extension in ExtensionLoader<Simian>.Extensions)
{
// Start persistance providers after all other extensions
if (!(extension is IPersistenceProvider))
@@ -73,7 +94,7 @@ namespace Simian
}
}
foreach (ISimianExtension extension in ExtensionLoader.Extensions)
foreach (IExtension extension in ExtensionLoader<Simian>.Extensions)
{
// Start the persistance provider(s)
if (extension is IPersistenceProvider)
@@ -83,28 +104,19 @@ namespace Simian
}
}
if (!CheckInterfaces())
{
Logger.Log("Missing interfaces, shutting down", Helpers.LogLevel.Error);
Stop();
return false;
}
else
{
return true;
}
return true;
}
public void Stop()
{
foreach (ISimianExtension extension in ExtensionLoader.Extensions)
foreach (IExtension extension in ExtensionLoader<Simian>.Extensions)
{
// Stop persistance providers first
if (extension is IPersistenceProvider)
extension.Stop();
}
foreach (ISimianExtension extension in ExtensionLoader.Extensions)
foreach (IExtension extension in ExtensionLoader<Simian>.Extensions)
{
// Stop all other extensions
if (!(extension is IPersistenceProvider))
@@ -134,56 +146,17 @@ namespace Simian
UDP.BroadcastPacket(offline, PacketCategory.State);
}
void TryAssignToInterface(ISimianExtension extension)
Dictionary<Type, FieldInfo> GetInterfaces()
{
if (extension is IAuthenticationProvider)
Authentication = (IAuthenticationProvider)extension;
else if (extension is IAccountProvider)
Accounts = (IAccountProvider)extension;
else if (extension is IUDPProvider)
UDP = (IUDPProvider)extension;
else if (extension is ISceneProvider)
Scene = (ISceneProvider)extension;
else if (extension is IAssetProvider)
Assets = (IAssetProvider)extension;
else if (extension is IAvatarProvider)
Avatars = (IAvatarProvider)extension;
else if (extension is IInventoryProvider)
Inventory = (IInventoryProvider)extension;
else if (extension is IParcelProvider)
Parcels = (IParcelProvider)extension;
else if (extension is IMeshingProvider)
Mesher = (IMeshingProvider)extension;
Dictionary<Type, FieldInfo> interfaces = new Dictionary<Type, FieldInfo>();
// Track all of the extensions with persistence
if (extension is IPersistable)
PersistentExtensions.Add((IPersistable)extension);
}
foreach (FieldInfo field in this.GetType().GetFields())
{
if (field.FieldType.IsInterface)
interfaces.Add(field.FieldType, field);
}
bool CheckInterfaces()
{
if (Authentication == null)
Logger.Log("No IAuthenticationProvider interface loaded", Helpers.LogLevel.Error);
else if (Accounts == null)
Logger.Log("No IAccountProvider interface loaded", Helpers.LogLevel.Error);
else if (UDP == null)
Logger.Log("No IUDPProvider interface loaded", Helpers.LogLevel.Error);
else if (Scene == null)
Logger.Log("No ISceneProvider interface loaded", Helpers.LogLevel.Error);
else if (Assets == null)
Logger.Log("No IAssetProvider interface loaded", Helpers.LogLevel.Error);
else if (Avatars == null)
Logger.Log("No IAvatarProvider interface loaded", Helpers.LogLevel.Error);
else if (Inventory == null)
Logger.Log("No IInventoryProvider interface loaded", Helpers.LogLevel.Error);
else if (Parcels == null)
Logger.Log("No IParcelProvider interface loaded", Helpers.LogLevel.Error);
else if (Mesher == null)
Logger.Log("No IMeshingProvider interface loaded", Helpers.LogLevel.Error);
else
return true;
return false;
return interfaces;
}
void InitHttpServer(int port, bool ssl)