Example #1
0
        internal static void ShutdownBackend <T>(ref T backend) where T : class, IDualityBackend
        {
            if (backend == null)
            {
                return;
            }

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

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

                    backend = null;
                }
                catch (Exception e)
                {
                    Logs.Core.WriteError("Failed: {0}", LogFormat.Exception(e));
                }
            }
            Logs.Core.PopIndent();
        }
Example #2
0
        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);
        }
Example #3
0
        /// <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();
        }
Example #4
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="formatter"></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);
        }
Example #5
0
        /// <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);
        }
Example #6
0
 /// <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);
     }
 }
Example #7
0
        /// <summary>
        /// Adds an already loaded plugin Assembly to the internal Duality T registry.
        /// You shouldn't need to call this method in general, since Duality manages its plugins
        /// automatically.
        /// </summary>
        /// <remarks>
        /// This method can be useful in certain cases when it is necessary to treat an Assembly as a
        /// Duality 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);
        }
Example #8
0
 /// <summary>
 /// When executed from within the editor environment, this method wraps the specified
 /// action in a safe try-catch block in order to be able to recover gracefully. In regular
 /// game execution, it will simply invoke the action without safety measures.
 /// </summary>
 /// <param name="action"></param>
 public static void EditorGuard(Action action)
 {
     if (ExecEnvironment == ExecutionEnvironment.Editor)
     {
         try
         {
             action();
         }
         catch (Exception e)
         {
             Logs.Editor.WriteError("An error occurred: {0}", LogFormat.Exception(e));
         }
     }
     else
     {
         action();
     }
 }
Example #9
0
        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 });
        }
Example #10
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);
     }
 }
Example #11
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);
        }
Example #12
0
        internal static void InitBackend <T>(out T target, Func <Type, IEnumerable <TypeInfo> > typeFinder = null) where T : class, IDualityBackend
        {
            if (typeFinder == null)
            {
                typeFinder = GetAvailDualityTypes;
            }

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

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

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

                IDualityBackend backend = backendType.CreateInstanceOf() as IDualityBackend;
                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 (DualityApp.AppData.Instance?.SkipBackends != null &&
                    DualityApp.AppData.Instance.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();
                pluginManager.LockPlugin(selectedBackendType.Assembly);
            }
            else
            {
                target = null;
            }

            Logs.Core.PopIndent();
        }
Example #13
0
        private static Resource ResolveContent(string path)
        {
            if (DualityApp.ExecContext == DualityApp.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);
            }
        }
Example #14
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);
        }