Ejemplo n.º 1
0
        /// <summary>
        /// Loads the Resource from the specified <see cref="Stream"/>. You shouldn't need this method in almost all cases.
        /// Only use it when you know exactly what you're doing. Consider requesting the Resource from the <see cref="ContentProvider"/> instead.
        /// </summary>
        /// <typeparam name="T">
        /// Desired Type of the returned reference. Does not affect the loaded Resource in any way - it is simply returned as T.
        /// Results in returning null if the loaded Resource's Type isn't assignable to T.
        /// </typeparam>
        /// <param name="str">The stream to load the Resource from.</param>
        /// <param name="resPath">The path that is assumed as the loaded Resource's origin.</param>
        /// <param name="loadCallback">An optional callback that is invoked right after loading the Resource, but before initializing it.</param>
        /// <param name="initResource">
        /// Specifies whether or not the Resource is initialized by calling <see cref="Resource.OnLoaded"/>. Never attempt to use
        /// uninitialized Resources or register them in the <see cref="ContentProvider"/>.
        /// </param>
        /// <returns>The Resource that has been loaded.</returns>
        public static T Load <T>(Serializer formatter, string resPath = null, Action <T> loadCallback = null, bool initResource = true) where T : Resource
        {
            T newContent = null;

            try
            {
                Resource res = formatter.ReadObject <Resource>();
                if (res == null)
                {
                    throw new Exception("Deserializing Resource failed.");
                }

                res.initState = InitState.Initializing;
                res.path      = resPath;
                if (loadCallback != null)
                {
                    loadCallback(res as T);                       // Callback before initializing.
                }
                if (initResource)
                {
                    Init(res);
                }
                newContent = res as T;
            }
            catch (Exception e)
            {
                Logs.Core.WriteError("Can't load {0} from '{1}', because an error occurred: {3}{2}",
                                     LogFormat.Type(typeof(T)),
                                     resPath ?? formatter.ToString(),
                                     LogFormat.Exception(e),
                                     Environment.NewLine);
            }

            return(newContent);
        }
        /// <summary>
        /// Disposes all loaded plugins and discards all related content / data.
        /// </summary>
        public void ClearPlugins()
        {
            T[] oldPlugins = this.LoadedPlugins.ToArray();
            if (oldPlugins.Length == 0)
            {
                return;
            }

            foreach (T plugin in oldPlugins)
            {
                this.disposedPlugins.Add(plugin.PluginAssembly);
            }
            this.OnPluginsRemoving(oldPlugins);
            foreach (T plugin in oldPlugins)
            {
                try
                {
                    plugin.Dispose();
                }
                catch (Exception e)
                {
                    this.pluginLog.WriteError("Error disposing plugin {1}: {0}", LogFormat.Exception(e), plugin.AssemblyName);
                }
            }
            this.OnPluginsRemoved(oldPlugins);
            this.pluginRegistry.Clear();
        }
        protected T LoadPlugin(string pluginFilePath)
        {
            // Check for already loaded plugins first
            string asmName = PathOp.GetFileNameWithoutExtension(pluginFilePath);
            T      plugin  = this.pluginRegistry.Values.FirstOrDefault(p => p.AssemblyName == asmName);

            if (plugin != null)
            {
                return(plugin);
            }

            // Load the assembly from the specified path
            Assembly pluginAssembly = null;

            try
            {
                pluginAssembly = this.assemblyLoader.LoadAssembly(pluginFilePath);
            }
            catch (Exception e)
            {
                this.pluginLog.WriteError("Error loading plugin Assembly: {0}", LogFormat.Exception(e));
                plugin = null;
            }

            // If we succeeded, register the loaded assembly as a plugin
            if (pluginAssembly != null)
            {
                plugin = this.LoadPlugin(pluginAssembly, pluginFilePath);
            }

            return(plugin);
        }
Ejemplo n.º 4
0
        internal static void ShutdownBackend <T>(ref T backend) where T : class, ICoheeBackend
        {
            if (backend == null)
            {
                return;
            }

            Logs.Core.Write("Shutting down {0}...", backend.Name);
            Logs.Core.PushIndent();
            {
                try
                {
                    backend.Shutdown();

                    TypeInfo backendType = backend.GetType().GetTypeInfo();
                    corePluginManager.UnlockPlugin(backendType.Assembly);

                    backend = null;
                }
                catch (Exception e)
                {
                    Logs.Core.WriteError("Failed: {0}", LogFormat.Exception(e));
                }
            }
            Logs.Core.PopIndent();
        }
Ejemplo n.º 5
0
 /// <summary>
 /// Returns a string that can be used for representing a <see cref="System.Reflection.TypeInfo"/> in log entries.
 /// </summary>
 /// <param name="type"></param>
 /// <returns></returns>
 public static string Type(TypeInfo type)
 {
     if (type == null)
     {
         return("null");
     }
     return(LogFormat.Type(type.AsType()));
 }
        /// <summary>
        /// Reloads the specified plugin. Does not initialize it.
        /// </summary>
        /// <param name="pluginFilePath"></param>
        public T ReloadPlugin(string pluginFilePath)
        {
            // If we're trying to reload an active backend plugin, stop
            foreach (var pair in this.pluginRegistry)
            {
                T plugin = pair.Value;
                if (PathOp.ArePathsEqual(plugin.FilePath, pluginFilePath))
                {
                    foreach (Assembly lockedAssembly in this.lockedPlugins)
                    {
                        if (plugin.PluginAssembly == lockedAssembly)
                        {
                            this.pluginLog.WriteError(
                                "Can't reload plugin {0}, because it has been locked by the runtime. " +
                                "This usually happens for plugins that implement a currently active backend.",
                                LogFormat.Assembly(lockedAssembly));
                            return(null);
                        }
                    }
                    break;
                }
            }

            // Load the updated plugin Assembly
            Assembly pluginAssembly = null;

            try
            {
                pluginAssembly = this.assemblyLoader.LoadAssembly(pluginFilePath);
            }
            catch (Exception e)
            {
                this.pluginLog.WriteError("Error loading plugin Assembly: {0}", LogFormat.Exception(e));
                return(null);
            }

            // If we're overwriting an old plugin here, add the old version to the "disposed" blacklist
            string assemblyName = pluginAssembly.GetShortAssemblyName();
            T      oldPlugin;

            if (this.pluginRegistry.TryGetValue(assemblyName, out oldPlugin))
            {
                this.pluginRegistry.Remove(assemblyName);
                this.disposedPlugins.Add(oldPlugin.PluginAssembly);
                this.OnPluginsRemoving(new[] { oldPlugin });
                oldPlugin.Dispose();
            }

            // Load the new plugin from the updated Assembly
            T updatedPlugin = this.LoadPlugin(pluginAssembly, pluginFilePath);

            // Discard temporary plugin-related data (cached Types, etc.)
            this.OnPluginsRemoved(new[] { oldPlugin });

            return(updatedPlugin);
        }
 /// <summary>
 /// Initializes the specified plugin. This concludes a manual plugin load or reload operation
 /// using API like <see cref="LoadPlugin(Assembly, string)"/> and <see cref="ReloadPlugin"/>.
 /// </summary>
 /// <param name="plugin"></param>
 public void InitPlugin(T plugin)
 {
     try
     {
         this.OnInitPlugin(plugin);
         this.OnPluginsReady(new[] { plugin });
     }
     catch (Exception e)
     {
         this.pluginLog.WriteError("Error initializing plugin {1}: {0}", LogFormat.Exception(e), plugin.AssemblyName);
         this.RemovePlugin(plugin);
     }
 }
 public override string ToString()
 {
     if (this.SkipIfExists > 0)
     {
         return(string.Format("Skip {0} if {1} exists",
                              this.SkipIfExists,
                              LogFormat.Type(this.RequiredType)));
     }
     else
     {
         return(string.Format("Require {0} or create {1}",
                              LogFormat.Type(this.RequiredType),
                              LogFormat.Type(this.CreateType)));
     }
 }
        /// <summary>
        /// Adds an already loaded plugin Assembly to the internal Cohee T registry.
        /// You shouldn't need to call this method in general, since Cohee manages its plugins
        /// automatically.
        /// </summary>
        /// <remarks>
        /// This method can be useful in certain cases when it is necessary to treat an Assembly as a
        /// Cohee plugin, even though it isn't located in the Plugins folder, or is not available
        /// as a file at all. A typical case for this is Unit Testing where the testing Assembly may
        /// specify additional Duality types such as Components, Resources, etc.
        /// </remarks>
        /// <param name="pluginAssembly"></param>
        /// <param name="pluginFilePath"></param>
        /// <returns></returns>
        public T LoadPlugin(Assembly pluginAssembly, string pluginFilePath)
        {
            this.disposedPlugins.Remove(pluginAssembly);

            string asmName = pluginAssembly.GetShortAssemblyName();
            T      plugin  = this.pluginRegistry.Values.FirstOrDefault(p => p.AssemblyName == asmName);

            if (plugin != null)
            {
                return(plugin);
            }

            try
            {
                TypeInfo pluginType = pluginAssembly.ExportedTypes
                                      .Select(t => t.GetTypeInfo())
                                      .FirstOrDefault(t => typeof(T).GetTypeInfo().IsAssignableFrom(t));

                if (pluginType == null)
                {
                    throw new Exception(string.Format(
                                            "Plugin does not contain a public {0} class.",
                                            typeof(T).Name));
                }

                plugin = (T)pluginType.CreateInstanceOf();

                if (plugin == null)
                {
                    throw new Exception(string.Format(
                                            "Failed to instantiate {0} class.",
                                            LogFormat.Type(pluginType.GetType())));
                }

                plugin.FilePath = pluginFilePath;
                plugin.FileHash = this.assemblyLoader.GetAssemblyHash(pluginFilePath);

                this.pluginRegistry.Add(plugin.AssemblyName, plugin);
            }
            catch (Exception e)
            {
                this.pluginLog.WriteError("Error loading plugin: {0}", LogFormat.Exception(e));
                this.disposedPlugins.Add(pluginAssembly);
                plugin = null;
            }

            return(plugin);
        }
Ejemplo n.º 10
0
        /// <summary>
        /// Returns a string that can be used for representing an exception in log entries.
        /// It usually does not include the full call stack and is significantly shorter than
        /// an <see cref="System.Exception">Exceptions</see> ToString method.
        /// </summary>
        /// <param name="e"></param>
        /// <returns></returns>
        public static string Exception(Exception e, bool callStack = true)
        {
            if (e == null)
            {
                return("null");
            }

            string eName = LogFormat.Type(e.GetType());

            return(string.Format(System.Globalization.CultureInfo.InvariantCulture,
                                 "{0}: {1}{3}CallStack:{3}{2}",
                                 eName,
                                 e.Message,
                                 e.StackTrace,
                                 Environment.NewLine));
        }
        protected void RemovePlugin(T plugin)
        {
            // Dispose plugin and discard plugin related data
            this.disposedPlugins.Add(plugin.PluginAssembly);
            this.OnPluginsRemoving(new[] { plugin });
            this.pluginRegistry.Remove(plugin.AssemblyName);
            try
            {
                plugin.Dispose();
            }
            catch (Exception e)
            {
                this.pluginLog.WriteError("Error disposing plugin {1}: {0}", LogFormat.Exception(e), plugin.AssemblyName);
            }

            // Discard temporary plugin-related data (cached Types, etc.)
            this.OnPluginsRemoved(new[] { plugin });
        }
            public void EnsureCreationChain(ComponentRequirementMap map)
            {
                if (this.initCreationChain == RecursiveInit.Initialized)
                {
                    return;
                }
                if (this.initCreationChain == RecursiveInit.InProgress)
                {
                    Logs.Core.WriteWarning(
                        "Detected a cyclic Component requirement in {0}. Requirements can not be ensured for cyclic dependencies.",
                        LogFormat.Type(this.Component));
                    return;
                }

                this.initCreationChain = RecursiveInit.InProgress;
                this.InitCreationChain(map);
                this.initCreationChain = RecursiveInit.Initialized;
            }
Ejemplo n.º 13
0
 private bool CheckedOnSaved(string saveAsPath)
 {
     if (this.initState != InitState.Initialized)
     {
         return(true);
     }
     try
     {
         this.OnSaved(saveAsPath);
         if (ResourceSaved != null)
         {
             ResourceSaved(this, new ResourceSaveEventArgs(this, saveAsPath));
         }
         return(true);
     }
     catch (Exception e)
     {
         Logs.Core.WriteError("OnSaved() of {0} failed: {1}", this, LogFormat.Exception(e));
         return(false);
     }
 }
Ejemplo n.º 14
0
        /// <summary>
        /// Clears the specified constraint graph of all loops.
        /// </summary>
        /// <param name="graph"></param>
        private static void ResolveConstraintLoops(Dictionary <Type, List <OrderConstraint> > graph)
        {
            while (true)
            {
                List <OrderConstraint> loop = FindConstraintLoop(graph);
                if (loop == null)
                {
                    return;
                }

                // Found a loop? Find the weakest link in it
                OrderConstraint weakestLink = loop[0];
                for (int i = 1; i < loop.Count; i++)
                {
                    OrderConstraint link = loop[i];
                    if ((int)link.Priority < (int)weakestLink.Priority)
                    {
                        weakestLink = link;
                    }
                }

                // If the loops weakest link was an explicit constraint, log a warning
                if ((int)weakestLink.Priority >= (int)ConstraintPriority.ExplicitWeak)
                {
                    Logs.Core.WriteWarning(
                        "Found a loop in the component execution order constraint graph. Ignoring the weakest constraint " +
                        "({0} must be executed before {1}). Please check your ExecutionOrder attributes.",
                        LogFormat.Type(weakestLink.FirstType),
                        LogFormat.Type(weakestLink.LastType));
                }

                // Remove the weakest link
                List <OrderConstraint> links = graph[weakestLink.FirstType];
                links.Remove(weakestLink);
                if (links.Count == 0)
                {
                    graph.Remove(weakestLink.FirstType);
                }
            }
        }
Ejemplo n.º 15
0
        private string FormatMessage(string format, object[] obj)
        {
            if (obj == null || obj.Length == 0)
            {
                return(format);
            }
            string msg;

            try
            {
                msg = string.Format(System.Globalization.CultureInfo.InvariantCulture, format, obj);
            }
            catch (Exception e)
            {
                // Don't allow log message formatting to throw unhandled exceptions,
                // because they would result in another log - and probably more exceptions.

                // Instead, embed format, arguments and the exception in the resulting
                // log message, so the user can retrieve all necessary information for
                // fixing his log call.
                msg = format + Environment.NewLine;
                if (obj != null)
                {
                    try
                    {
                        msg += obj.ToString(", ") + Environment.NewLine;
                    }
                    catch (Exception)
                    {
                        msg += "(Error in ToString call)" + Environment.NewLine;
                    }
                }
                msg += LogFormat.Exception(e);
            }
            return(msg);
        }
Ejemplo n.º 16
0
        /// <summary>
        /// Returns an existing <see cref="ProfileCounter"/> with the specified name.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="name">The <see cref="ProfileCounter"/> name to use for this measurement. For nested measurements, use path strings, e.g. "ParentCounter\ChildCounter"</param>
        /// <returns></returns>
        public static T GetCounter <T>(string name) where T : ProfileCounter
        {
            if (name == null)
            {
                return(null);
            }

            ProfileCounter c;

            if (!counterMap.TryGetValue(name, out c))
            {
                return(null);
            }

            T cc = c as T;

            if (cc == null)
            {
                throw new InvalidOperationException(string.Format("The specified performance counter '{0}' is not a {1}.", name, LogFormat.Type(typeof(T))));
            }
            return(cc);
        }
Ejemplo n.º 17
0
        internal static void InitBackend <T>(out T target, Func <Type, IEnumerable <TypeInfo> > typeFinder = null) where T : class, ICoheeBackend
        {
            if (typeFinder == null)
            {
                typeFinder = GetAvailCoheeTypes;
            }

            Logs.Core.Write("Initializing {0}...", LogFormat.Type(typeof(T)));
            Logs.Core.PushIndent();

            // Generate a list of available backends for evaluation
            List <ICoheeBackend> backends = new List <ICoheeBackend>();

            foreach (TypeInfo backendType in typeFinder(typeof(ICoheeBackend)))
            {
                if (backendType.IsInterface)
                {
                    continue;
                }
                if (backendType.IsAbstract)
                {
                    continue;
                }
                if (!backendType.IsClass)
                {
                    continue;
                }
                if (!typeof(T).GetTypeInfo().IsAssignableFrom(backendType))
                {
                    continue;
                }

                ICoheeBackend backend = backendType.CreateInstanceOf() as ICoheeBackend;
                if (backend == null)
                {
                    Logs.Core.WriteWarning("Unable to create an instance of {0}. Skipping it.", backendType.FullName);
                    continue;
                }
                backends.Add(backend);
            }

            // Sort backends from best to worst
            backends.StableSort((a, b) => b.Priority > a.Priority ? 1 : -1);

            // Try to initialize each one and select the first that works
            T selectedBackend = null;

            foreach (T backend in backends)
            {
                if (appData != null &&
                    appData.SkipBackends != null &&
                    appData.SkipBackends.Any(s => string.Equals(s, backend.Id, StringComparison.OrdinalIgnoreCase)))
                {
                    Logs.Core.Write("Backend '{0}' skipped because of AppData settings.", backend.Name);
                    continue;
                }

                bool available = false;
                try
                {
                    available = backend.CheckAvailable();
                    if (!available)
                    {
                        Logs.Core.Write("Backend '{0}' reports to be unavailable. Skipping it.", backend.Name);
                    }
                }
                catch (Exception e)
                {
                    available = false;
                    Logs.Core.WriteWarning("Backend '{0}' failed the availability check with an exception: {1}", backend.Name, LogFormat.Exception(e));
                }
                if (!available)
                {
                    continue;
                }

                Logs.Core.Write("{0}...", backend.Name);
                Logs.Core.PushIndent();
                {
                    try
                    {
                        backend.Init();
                        selectedBackend = backend;
                    }
                    catch (Exception e)
                    {
                        Logs.Core.WriteError("Failed: {0}", LogFormat.Exception(e));
                    }
                }
                Logs.Core.PopIndent();

                if (selectedBackend != null)
                {
                    break;
                }
            }

            // If we found a proper backend and initialized it, add it to the list of active backends
            if (selectedBackend != null)
            {
                target = selectedBackend;

                TypeInfo selectedBackendType = selectedBackend.GetType().GetTypeInfo();
                corePluginManager.LockPlugin(selectedBackendType.Assembly);
            }
            else
            {
                target = null;
            }

            Logs.Core.PopIndent();
        }
Ejemplo n.º 18
0
        private static CreateMethod CreateObjectActivator(TypeInfo typeInfo, out object firstResult)
        {
            Exception lastError = null;
            CreateMethod activator;
            firstResult = null;

            // Filter out non-instantiatable Types
            if (typeInfo.IsAbstract || typeInfo.IsInterface || typeInfo.IsGenericTypeDefinition)
            {
                activator = nullObjectActivator;
            }
            // If the caller wants a string, just return an empty one
            else if (typeInfo.AsType() == typeof(string))
            {
                activator = () => "";
            }
            // If the caller wants an array, create an empty one
            else if (typeInfo.IsArray && typeInfo.GetArrayRank() == 1)
            {
                activator = () => Array.CreateInstance(typeInfo.GetElementType(), 0);
            }
            // For structs, boxing a default(T) is sufficient
            else if (typeInfo.IsValueType)
            {
                var lambda = Expression.Lambda<CreateMethod>(Expression.Convert(Expression.Default(typeInfo.AsType()), typeof(object)));
                activator = lambda.Compile();
            }
            else
            {
                activator = nullObjectActivator;

                // Retrieve constructors, sorted from trivial to parameter-rich
                ConstructorInfo[] constructors = typeInfo.DeclaredConstructors
                    .Where(c => !c.IsStatic)
                    .Select(c => new { Info = c, ParamCount = c.GetParameters().Length })
                    .OrderBy(s => s.ParamCount)
                    .Select(s => s.Info)
                    .ToArray();

                foreach (ConstructorInfo con in constructors)
                {
                    // Prepare constructor argument values - just use default(T) for all of them.
                    ParameterInfo[] conParams = con.GetParameters();
                    Expression[] args = new Expression[conParams.Length];
                    for (int i = 0; i < args.Length; i++)
                    {
                        Type paramType = conParams[i].ParameterType;
                        args[i] = Expression.Default(paramType);
                    }

                    // Compile a lambda method invoking the constructor
                    var lambda = Expression.Lambda<CreateMethod>(Expression.New(con, args));
                    activator = lambda.Compile();

                    // Does it work?
                    firstResult = CheckActivator(activator, out lastError);
                    if (firstResult != null)
                        break;
                }

                // If there were no suitable constructors, log a generic warning.
                if (constructors.Length == 0)
                {
                    Logs.Core.WriteWarning(
                        "Failed to create object of Type {0}. Make sure there is a trivial constructor.",
                        LogFormat.Type(typeInfo));
                }
            }

            // Test whether our activation method really works, replace with dummy if not
            if (firstResult == null)
            {
                // If we didn't yet try to create an object instance or value, do it now.
                if (lastError == null)
                {
                    firstResult = CheckActivator(activator, out lastError);
                }

                // If there was an error / Exception thrown while creating the object, inform someone.
                if (lastError != null)
                {
                    // If it's a problem in a static constructor, get the inner exception to know what's actually wrong.
                    if (lastError is TypeInitializationException)
                    {
                        Logs.Core.WriteError("Failed to initialize Type {0}: {1}",
                            LogFormat.Type(typeInfo),
                            LogFormat.Exception(lastError.InnerException));
                    }
                    // Otherwise, just do a regular error log.
                    else
                    {
                        Logs.Core.WriteError("Failed to create object of Type {0}: {1}",
                            LogFormat.Type(typeInfo),
                            LogFormat.Exception(lastError));
                    }
                }
            }

            // If we still don't have anything, just use a dummy.
            if (firstResult == null)
                activator = nullObjectActivator;

            return activator;
        }
Ejemplo n.º 19
0
        private static Resource ResolveContent(string path)
        {
            if (CoheeApp.ExecContext == CoheeApp.ExecutionContext.Terminated)
            {
                return(null);
            }
            if (string.IsNullOrEmpty(path) || ResourceResolve == null)
            {
                return(null);
            }

            ResourceResolveEventArgs args = new ResourceResolveEventArgs(path);

            try
            {
                ResourceResolve(null, args);
            }
            catch (Exception e)
            {
                Logs.Core.WriteError("An error occurred in custom ResourceResolve code: {0}", LogFormat.Exception(e));
            }

            if (args.Handled)
            {
                if (string.IsNullOrEmpty(args.Result.Path))
                {
                    args.Result.Path = path;
                }
                AddContent(path, args.Result);
                return(args.Result);
            }
            else
            {
                return(null);
            }
        }
Ejemplo n.º 20
0
        /// <summary>
        /// Initializes this CoheeApp. Should be called before performing any operations within Cohee.
        /// </summary>
        /// <param name="context">The <see cref="ExecutionContext"/> in which Duality runs.</param>
        /// <param name="commandLineArgs">
        /// Command line arguments to run this CoheeApp with.
        /// Usually these are just the ones from the host application, passed on.
        /// </param>
        public static void Init(ExecutionEnvironment env, ExecutionContext context, IAssemblyLoader assemLoader, string[] commandLineArgs)
        {
            if (initialized)
            {
                return;
            }

            // Process command line options
            if (commandLineArgs != null)
            {
                // Enter debug mode
                if (commandLineArgs.Contains(CmdArgDebug))
                {
                    System.Diagnostics.Debugger.Launch();
                }
                // Run from editor
                if (commandLineArgs.Contains(CmdArgEditor))
                {
                    runFromEditor = true;
                }
            }

            // If the core was compiled in debug mode and a debugger is attached, log
            // to the Debug channel, so we can put the VS output window to good use.
            #if DEBUG
            bool isDebugging = System.Diagnostics.Debugger.IsAttached;
            if (isDebugging)
            {
                // Only add a new Debug output if we don't already have one, and don't
                // log to a Console channel either. VS will automatically redirect Console
                // output to the Output window when debugging a non-Console application,
                // and we don't want to end up with double log entries.
                bool hasDebugOut   = Logs.GlobalOutput.OfType <DebugLogOutput>().Any();
                bool hasConsoleOut = Logs.GlobalOutput.OfType <TextWriterLogOutput>().Any(w => w.GetType().Name.Contains("Console"));
                if (!hasDebugOut && !hasConsoleOut)
                {
                    Logs.AddGlobalOutput(new DebugLogOutput());
                }
            }
            #endif

            environment = env;
            execContext = context;

            // Initialize the plugin manager
            {
                assemblyLoader = assemLoader ?? new Cohee.Backend.Dummy.DummyAssemblyLoader();
                Logs.Core.Write("Using '{0}' to load plugins.", assemblyLoader.GetType().Name);

                assemblyLoader.Init();

                // Log assembly loading data for diagnostic purposes
                {
                    Logs.Core.Write("Currently Loaded Assemblies:" + Environment.NewLine + "{0}",
                                    assemblyLoader.LoadedAssemblies.ToString(
                                        assembly => "  " + LogFormat.Assembly(assembly),
                                        Environment.NewLine));
                    Logs.Core.Write("Plugin Base Directories:" + Environment.NewLine + "{0}",
                                    assemblyLoader.BaseDirectories.ToString(
                                        path => "  " + path,
                                        Environment.NewLine));
                    Logs.Core.Write("Available Assembly Paths:" + Environment.NewLine + "{0}",
                                    assemblyLoader.AvailableAssemblyPaths.ToString(
                                        path => "  " + path,
                                        Environment.NewLine));
                }

                corePluginManager.Init(assemblyLoader);
                corePluginManager.PluginsRemoving += pluginManager_PluginsRemoving;
                corePluginManager.PluginsRemoved  += pluginManager_PluginsRemoved;
            }

            // Load all plugins. This needs to be done first, so backends and Types can be located.
            corePluginManager.LoadPlugins();

            // Initialize the system backend for system info and file system access
            InitBackend(out systemBackend);

            // Load application and user data and submit a change event, so all settings are applied
            LoadAppData();
            LoadUserData();
            OnAppDataChanged();
            OnUserDataChanged();

            // Initialize the graphics backend
            InitBackend(out graphicsBackend);

            // Initialize the audio backend
            InitBackend(out audioBackend);
            //sound = new SoundDevice();

            // Initialize all core plugins, this may allocate Resources or establish references between plugins
            corePluginManager.InitPlugins();

            initialized = true;

            // Write environment specs as a debug log
            Logs.Core.Write(
                "DualityApp initialized" + Environment.NewLine +
                "Debug Mode: {0}" + Environment.NewLine +
                "Command line arguments: {1}",
                System.Diagnostics.Debugger.IsAttached,
                commandLineArgs != null ? commandLineArgs.ToString(", ") : "null");
        }