Esempio n. 1
0
        internal async Task <int> Main()
        {
            var plugin = new Name(this.Plugin);

            Definition definition;

            try
            {
                Environment.CurrentDirectory = PathManager.FindResource();

                definition = Definition.Load(Program.DefinitionFile);
            }
            catch (FileNotFoundException ex)
            {
                Console.WriteLine(ex.Message);
                Console.WriteLine("Use `nfpm init` to setup NFive in this directory");

                return(1);
            }

            definition.Dependencies?.Remove(plugin);

            var venderPath = Path.Combine(Environment.CurrentDirectory, Program.PluginPath, plugin.Vendor);

            if (Directory.Exists(venderPath))
            {
                var projectPath = Path.Combine(venderPath, plugin.Project);

                if (Directory.Exists(projectPath))
                {
                    Directory.Delete(projectPath, true);
                }

                if (!Directory.EnumerateFileSystemEntries(venderPath).Any())
                {
                    Directory.Delete(venderPath);
                }
            }

            // TODO: Remove orphaned child dependencies

            var graph = new DefinitionGraph();
            await graph.Build(definition);

            await graph.Apply();

            definition.Save(Program.DefinitionFile);
            graph.Save();
            ResourceGenerator.Serialize(graph).Save();

            return(0);
        }
Esempio n. 2
0
        internal async Task <int> Main()
        {
            Definition definition;

            try
            {
                Environment.CurrentDirectory = PathManager.FindResource();

                definition = Definition.Load(Program.DefinitionFile);
            }
            catch (FileNotFoundException ex)
            {
                Console.WriteLine(ex.Message);
                Console.WriteLine("Use `nfpm init` to setup NFive in this directory");

                return(1);
            }

            DefinitionGraph graph;

            try
            {
                graph = DefinitionGraph.Load();
            }
            catch (FileNotFoundException ex)
            {
                Console.WriteLine(ex.Message);
                Console.WriteLine("Use `nfpm install` to install some dependencies first");

                return(1);
            }
            catch (Exception ex)
            {
                Console.WriteLine("Unable to build definition graph (PANIC):", Color.Red);
                Console.WriteLine(ex.Message);
                if (ex.InnerException != null)
                {
                    Console.WriteLine(ex.InnerException.Message);
                }

                return(1);
            }



            return(await Task.FromResult(0));
        }
Esempio n. 3
0
        public override async Task <int> Main()
        {
            var definition = LoadDefinition(this.Verbose);

            foreach (var plugin in this.Plugins)
            {
                var name = new Name(plugin);                 // TODO: Handle

                if (definition.Dependencies == null || !definition.Dependencies.ContainsKey(name))
                {
                    continue;
                }

                if (!this.Quiet)
                {
                    Console.WriteLine("- ", name.ToString().White());
                }

                definition.Dependencies.Remove(name);

                Directory.Delete(Path.Combine(Environment.CurrentDirectory, ConfigurationManager.PluginPath, name.Vendor, name.Project), true);
            }

            var graph = new DefinitionGraph();
            await graph.Apply(definition);

            definition.Save(ConfigurationManager.DefinitionFile);
            graph.Save();

            if (PathManager.IsResource())
            {
                ResourceGenerator.Serialize(graph);
            }

            return(0);
        }
Esempio n. 4
0
        /// <summary>
        /// Serializes the specified <see cref="DefinitionGraph"/> to a FiveM Lua resource file format.
        /// </summary>
        /// <param name="graph">The definition graph to serialize.</param>
        /// <returns>The definition graph in FiveM Lua resource file format.</returns>
        public static ResourceString Serialize(DefinitionGraph graph)
        {
            var output = new StringBuilder();

            WriteLine(ref output, "-- FiveM resource definition");
            WriteLine(ref output, "-- This file is automatically generated with `nfpm install` - any manual changes will be lost");
            WriteLine(ref output);

            WriteLine(ref output, "resource_manifest_version", ManifestVersion.ToString());
            WriteLine(ref output);

            WriteLine(ref output, "server_scripts {");
            WriteLine(ref output, "\t-- NFive");
            foreach (var file in DefaultSeverScripts)
            {
                WriteLine(ref output, $"\t'{file}',");
            }
            WriteLine(ref output, "}");
            WriteLine(ref output);

            WriteLine(ref output, "client_scripts {");
            foreach (var plugin in graph.Definitions.Where(d => d.Client?.Include?.Count > 0 || d.Client?.Main?.Count > 0))
            {
                WriteLine(ref output, $"\t-- {plugin.Name}@{plugin.Version}");
                if (plugin.Client?.Include != null)
                {
                    foreach (var file in plugin.Client.Include)
                    {
                        WriteLine(ref output, $"\t'{Path.Combine(Program.PluginPath, plugin.Name.Vendor, plugin.Name.Project, file).Replace(Path.DirectorySeparatorChar, '/')}.net.dll',");
                    }
                }
                if (plugin.Client?.Main != null)
                {
                    foreach (var file in plugin.Client.Main)
                    {
                        WriteLine(ref output, $"\t'{Path.Combine(Program.PluginPath, plugin.Name.Vendor, plugin.Name.Project, file).Replace(Path.DirectorySeparatorChar, '/')}.net.dll',");
                    }
                }
                WriteLine(ref output);
            }
            WriteLine(ref output, "\t-- NFive");
            foreach (var file in DefaultClientScripts)
            {
                WriteLine(ref output, $"\t'{file}',");
            }
            WriteLine(ref output, "}");
            WriteLine(ref output);

            WriteLine(ref output, "files {");
            WriteLine(ref output, "\t-- NFive");
            foreach (var file in DefaultClientFiles)
            {
                WriteLine(ref output, $"\t'{file}',");
            }
            foreach (var plugin in graph.Definitions.Where(d => d.Client?.Files?.Count > 0))
            {
                WriteLine(ref output);
                WriteLine(ref output, $"\t-- {plugin.Name}@{plugin.Version}");
                foreach (var file in plugin.Client.Files)
                {
                    WriteLine(ref output, $"\t'{Path.Combine(Program.PluginPath, plugin.Name.Vendor, plugin.Name.Project, file).Replace(Path.DirectorySeparatorChar, '/')}',");
                }
            }
            WriteLine(ref output, "}");

            var loadscreen = graph.Definitions.FirstOrDefault(d => d.Client?.Loadscreen != null);

            if (loadscreen != null)
            {
                WriteLine(ref output);
                WriteLine(ref output, $"-- {loadscreen.Name}@{loadscreen.Version}");
                WriteLine(ref output, "loadscreen", Path.Combine(Program.PluginPath, loadscreen.Name.Vendor, loadscreen.Name.Project, loadscreen.Client.Loadscreen).Replace(Path.DirectorySeparatorChar, '/'));
            }

            var ui = graph.Definitions.FirstOrDefault(d => d.Client?.Ui != null);

            if (ui != null)
            {
                WriteLine(ref output);
                WriteLine(ref output, $"-- {ui.Name}@{ui.Version}");
                WriteLine(ref output, "ui_page", Path.Combine(Program.PluginPath, ui.Name.Vendor, ui.Name.Project, ui.Client.Ui).Replace(Path.DirectorySeparatorChar, '/'));
            }

            return(new ResourceString(output.ToString()));
        }
Esempio n. 5
0
        public override async Task <int> Main()
        {
            var definition = LoadDefinition();
            var graph      = LoadGraph();

            // New plugins
            if (this.Plugins.Any())
            {
                foreach (var plugin in this.Plugins)
                {
                    var input = plugin;

                    // Local install
                    if (Directory.Exists(plugin) && File.Exists(Path.Combine(plugin, ConfigurationManager.DefinitionFile)))
                    {
                        var path = Path.GetFullPath(plugin);

                        var pluginDefinition = Plugin.Load(Path.Combine(path, ConfigurationManager.DefinitionFile));

                        if (definition.Repositories == null)
                        {
                            definition.Repositories = new List <Repository>();
                        }

                        definition.Repositories.RemoveAll(r => r.Name == pluginDefinition.Name);
                        definition.Repositories.Add(new Repository
                        {
                            Name = pluginDefinition.Name,
                            Type = "local",
                            Path = path
                        });

                        input = pluginDefinition.Name;
                    }

                    var parts = input.Split(new[] { '@' }, 2);
                    var name  = new Name(parts[0].Trim());

                    var versionInput = parts.Length == 2 ? parts[1].Trim() : "*";

                    Models.VersionRange range   = null;
                    Version             version = null;
                    PartialVersion      partial = null;

                    try
                    {
                        partial = new PartialVersion(versionInput);
                    }
                    catch (Exception)
                    {
                        // ignored
                    }

                    var isSpecific = partial?.Major != null && partial.Minor.HasValue && partial.Patch.HasValue;

                    try
                    {
                        range = new Models.VersionRange(versionInput);
                    }
                    catch (Exception)
                    {
                        // ignored
                    }

                    try
                    {
                        version = new Version(versionInput);
                    }
                    catch (Exception)
                    {
                        // ignored
                    }

                    List <Version> versions;
                    try
                    {
                        var adapter = new AdapterBuilder(name, definition).Adapter();
                        versions = (await adapter.GetVersions()).ToList();
                    }
                    catch (WebException ex) when((ex.Response as HttpWebResponse)?.StatusCode == HttpStatusCode.NotFound)
                    {
                        Console.WriteLine("Error ".DarkRed(), $"{name}".Red(), " not found.".DarkRed());

                        return(1);
                    }

                    var versionMatch = range.MaxSatisfying(versions);
                    if (versionMatch == null)
                    {
                        Console.WriteLine("Error ".DarkRed(), $"{name}@{range}".Red(), " not found, available versions: ".DarkRed(), string.Join(" ", versions.Select(v => v.ToString())).Red());

                        return(1);
                    }

                    if (definition.Dependencies == null)
                    {
                        definition.Dependencies = new Dictionary <Name, SDK.Core.Plugins.VersionRange>();
                    }
                    definition.Dependencies[name] = new Models.VersionRange("^" + (isSpecific ? partial.ToZeroVersion() : version ?? versionMatch));

                    Console.WriteLine("+ ", $"{name}@{definition.Dependencies[name]}".White());
                }

                graph = new DefinitionGraph();
                await graph.Apply(definition);

                definition.Save(ConfigurationManager.DefinitionFile);
                graph.Save();
            }
            else
            {
                if (graph != null)
                {
                    await graph.Apply();
                }
                else
                {
                    graph = new DefinitionGraph();

                    await graph.Apply(definition);

                    graph.Save();
                }
            }

            if (PathManager.IsResource())
            {
                ResourceGenerator.Serialize(graph);
            }

            return(0);
        }
Esempio n. 6
0
        public Program()
        {
            // Set the AppDomain working directory to the current resource root
            Environment.CurrentDirectory = FileManager.ResolveResourcePath();

            var config = ConfigurationManager.Load <CoreConfiguration>("nfive.yml");

            ServerConfiguration.LogLevel = config.Log.Level;
            API.SetMapName(config.Display.Map);
            API.SetGameType(config.Display.Map);

            // Setup RPC handlers
            RpcManager.Configure(this.EventHandlers);

            var events = new EventManager();

            // Load core controllers
            this.controllers.Add(new DatabaseController(new Logger("Database"), events, new RpcHandler(), ConfigurationManager.Load <DatabaseConfiguration>("database.yml")));

            // Resolve dependencies
            var graph = DefinitionGraph.Load("nfive.lock");

            // Load plugins into the AppDomain
            foreach (var plugin in graph.Definitions)
            {
                this.logger.Info($"Loading {plugin.FullName}");

                // Load include files
                foreach (string includeName in plugin.Server?.Include ?? new List <string>())
                {
                    string includeFile = Path.Combine("plugins", plugin.Name.Vendor, plugin.Name.Project, $"{includeName}.dll");
                    if (!File.Exists(includeFile))
                    {
                        throw new FileNotFoundException(includeFile);
                    }

                    AppDomain.CurrentDomain.Load(File.ReadAllBytes(includeFile));
                }

                // Load main files
                foreach (string mainName in plugin.Server?.Main ?? new List <string>())
                {
                    string mainFile = Path.Combine("plugins", plugin.Name.Vendor, plugin.Name.Project, $"{mainName}.net.dll");
                    if (!File.Exists(mainFile))
                    {
                        throw new FileNotFoundException(mainFile);
                    }

                    var types = Assembly.LoadFrom(mainFile).GetTypes().Where(t => !t.IsAbstract && t.IsClass).ToList();

                    //this.logger.Debug($"{mainName}: {types.Count} {string.Join(Environment.NewLine, types)}");

                    // Find migrations
                    foreach (Type migrationType in types.Where(t => t.BaseType != null && t.BaseType.IsGenericType && t.BaseType.GetGenericTypeDefinition() == typeof(MigrationConfiguration <>)))
                    {
                        var configuration = (DbMigrationsConfiguration)Activator.CreateInstance(migrationType);
                        var migrator      = new DbMigrator(configuration);

                        if (!migrator.GetPendingMigrations().Any())
                        {
                            continue;
                        }

                        if (!ServerConfiguration.AutomaticMigrations)
                        {
                            throw new MigrationsPendingException($"Plugin {plugin.FullName} has pending migrations but automatic migrations are disabled");
                        }

                        this.logger.Debug($"{mainName}: Running migrations {string.Join(", ", migrator.GetPendingMigrations())}");

                        migrator.Update();
                    }

                    // Find controllers
                    foreach (Type controllerType in types.Where(t => t.IsSubclassOf(typeof(Controller)) || t.IsSubclassOf(typeof(ConfigurableController <>))))
                    {
                        List <object> constructorArgs = new List <object>
                        {
                            new Logger($"Plugin|{plugin.Name}"),
                            events,
                            new RpcHandler()
                        };

                        // Check if controller is configurable
                        if (controllerType.BaseType != null && controllerType.BaseType.IsGenericType && controllerType.BaseType.GetGenericTypeDefinition() == typeof(ConfigurableController <>))
                        {
                            // Get controller configuration type
                            Type configurationType = controllerType.BaseType.GetGenericArguments()[0];

                            var configurationObject = (ControllerConfiguration)Activator.CreateInstance(configurationType);

                            if (!File.Exists(Path.Combine("config", plugin.Name.Vendor, plugin.Name.Project, $"{configurationObject.FileName}.yml")))
                            {
                                Directory.CreateDirectory(Path.Combine("config", plugin.Name.Vendor, plugin.Name.Project));

                                var yml = new SerializerBuilder()
                                          .WithNamingConvention(new UnderscoredNamingConvention())
                                          .EmitDefaults()
                                          .WithTypeInspector(i => new PluginTypeInspector(i))
                                          .Build()
                                          .Serialize(Activator.CreateInstance(configurationType));

                                File.WriteAllText(Path.Combine("config", plugin.Name.Vendor, plugin.Name.Project, $"{configurationObject.FileName}.yml"), yml);
                            }

                            // Load configuration
                            object configuration = ConfigurationManager.Load(plugin.Name, configurationObject.FileName, configurationType);

                            constructorArgs.Add(configuration);
                        }

                        // Construct controller instance
                        Controller controller = (Controller)Activator.CreateInstance(controllerType, constructorArgs.ToArray());

                        this.controllers.Add(controller);
                    }
                }
            }

            events.Raise("serverInitialized");

            this.logger.Info($"{graph.Definitions.Count} plugins loaded, {this.controllers.Count} controller(s) created");
        }
Esempio n. 7
0
        private async void Startup()
        {
            // Print exception messages in English
            Thread.CurrentThread.CurrentUICulture     = CultureInfo.InvariantCulture;
            CultureInfo.DefaultThreadCurrentUICulture = CultureInfo.InvariantCulture;

            // Set the AppDomain working directory to the current resource root
            Environment.CurrentDirectory = Path.GetFullPath(API.GetResourcePath(API.GetCurrentResourceName()));

            new Logger().Info($"NFive {typeof(Program).Assembly.GetCustomAttributes<AssemblyInformationalVersionAttribute>().First().InformationalVersion}");

            // TODO: Check and warn if local CitizenFX.Core.Server.dll is found

            var config = ConfigurationManager.Load <CoreConfiguration>("core.yml");

            // Use configured culture for output
            Thread.CurrentThread.CurrentCulture     = config.Locale.Culture.First();
            CultureInfo.DefaultThreadCurrentCulture = config.Locale.Culture.First();

            ServerConfiguration.Locale    = config.Locale;
            ServerLogConfiguration.Output = config.Log.Output;

            var logger = new Logger(config.Log.Core);

            API.SetGameType(config.Display.Game);
            API.SetMapName(config.Display.Map);

            // Setup RPC handlers
            RpcManager.Configure(config.Log.Comms, this.EventHandlers, this.Players);

            var events = new EventManager(config.Log.Comms);
            var comms  = new CommunicationManager(events);
            var rcon   = new RconManager(comms);

            // Load core controllers
            try
            {
                var dbController = new DatabaseController(new Logger(config.Log.Core, "Database"), ConfigurationManager.Load <DatabaseConfiguration>("database.yml"), comms);
                await dbController.Loaded();

                this.controllers.Add(new Name("NFive/Database"), new List <Controller> {
                    dbController
                });
            }
            catch (Exception ex)
            {
                logger.Error(ex, "Database connection error");
                logger.Warn("Fatal error, exiting");
                Environment.Exit(1);
            }

            var eventController = new EventController(new Logger(config.Log.Core, "FiveM"), comms);
            await eventController.Loaded();

            this.controllers.Add(new Name("NFive/RawEvents"), new List <Controller> {
                eventController
            });

            var sessionController = new SessionController(new Logger(config.Log.Core, "Session"), ConfigurationManager.Load <SessionConfiguration>("session.yml"), comms);
            await sessionController.Loaded();

            this.controllers.Add(new Name("NFive/Session"), new List <Controller> {
                sessionController
            });

            // Resolve dependencies
            var graph = DefinitionGraph.Load();

            // IoC
            var assemblies = new List <Assembly>();

            assemblies.AddRange(graph.Plugins.Where(p => p.Server?.Include != null).SelectMany(p => p.Server.Include.Select(i => Assembly.LoadFrom(Path.Combine("plugins", p.Name.Vendor, p.Name.Project, $"{i}.net.dll")))));
            assemblies.AddRange(graph.Plugins.Where(p => p.Server?.Main != null).SelectMany(p => p.Server.Main.Select(m => Assembly.LoadFrom(Path.Combine("plugins", p.Name.Vendor, p.Name.Project, $"{m}.net.dll")))));

            var registrar = new ContainerRegistrar();

            registrar.RegisterService <ILogger>(s => new Logger());
            registrar.RegisterInstance <IRconManager>(rcon);
            registrar.RegisterInstance <IBaseScriptProxy>(new BaseScriptProxy(this.EventHandlers, this.Exports, this.Players));
            registrar.RegisterInstance <ICommunicationManager>(comms);
            registrar.RegisterInstance <IClientList>(new ClientList(new Logger(config.Log.Core, "ClientList"), comms));
            registrar.RegisterPluginComponents(assemblies.Distinct());

            // DI
            var container = registrar.Build();

            var pluginDefaultLogLevel = config.Log.Plugins.ContainsKey("default") ? config.Log.Plugins["default"] : LogLevel.Info;

            // Load plugins into the AppDomain
            foreach (var plugin in graph.Plugins)
            {
                logger.Info($"Loading {plugin.FullName}");

                // Load include files
                foreach (var includeName in plugin.Server?.Include ?? new List <string>())
                {
                    var includeFile = Path.Combine("plugins", plugin.Name.Vendor, plugin.Name.Project, $"{includeName}.net.dll");
                    if (!File.Exists(includeFile))
                    {
                        throw new FileNotFoundException(includeFile);
                    }

                    AppDomain.CurrentDomain.Load(File.ReadAllBytes(includeFile));
                }

                // Load main files
                foreach (var mainName in plugin.Server?.Main ?? new List <string>())
                {
                    var mainFile = Path.Combine("plugins", plugin.Name.Vendor, plugin.Name.Project, $"{mainName}.net.dll");
                    if (!File.Exists(mainFile))
                    {
                        throw new FileNotFoundException(mainFile);
                    }

                    var asm = Assembly.LoadFrom(mainFile);

                    var sdkVersion = asm.GetCustomAttribute <ServerPluginAttribute>();

                    if (sdkVersion == null)
                    {
                        throw new Exception("Unable to load outdated SDK plugin");                         // TODO
                    }

                    if (sdkVersion.Target != SDK.Server.SDK.Version)
                    {
                        throw new Exception("Unable to load outdated SDK plugin");
                    }

                    var types = Assembly.LoadFrom(mainFile).GetTypes().Where(t => !t.IsAbstract && t.IsClass).ToList();

                    // Find migrations
                    foreach (var migrationType in types.Where(t => t.BaseType != null && t.BaseType.IsGenericType && t.BaseType.GetGenericTypeDefinition() == typeof(MigrationConfiguration <>)))
                    {
                        var configuration = (DbMigrationsConfiguration)Activator.CreateInstance(migrationType);
                        var migrator      = new DbMigrator(configuration);

                        if (!migrator.GetPendingMigrations().Any())
                        {
                            continue;
                        }

                        if (!ServerConfiguration.AutomaticMigrations)
                        {
                            throw new MigrationsPendingException($"Plugin {plugin.FullName} has pending migrations but automatic migrations are disabled");
                        }

                        foreach (var migration in migrator.GetPendingMigrations())
                        {
                            new Logger(config.Log.Core, "Database").Debug($"[{mainName}] Running migration: {migration}");

                            migrator.Update(migration);
                        }
                    }

                    // Find controllers
                    foreach (var controllerType in types.Where(t => t.IsSubclassOf(typeof(Controller)) || t.IsSubclassOf(typeof(ConfigurableController <>))))
                    {
                        var logLevel = config.Log.Plugins.ContainsKey(plugin.Name) ? config.Log.Plugins[plugin.Name] : pluginDefaultLogLevel;

                        var constructorArgs = new List <object>
                        {
                            new Logger(logLevel, plugin.Name)
                        };

                        // Check if controller is configurable
                        if (controllerType.BaseType != null && controllerType.BaseType.IsGenericType && controllerType.BaseType.GetGenericTypeDefinition() == typeof(ConfigurableController <>))
                        {
                            // Initialize the controller configuration
                            constructorArgs.Add(ConfigurationManager.InitializeConfig(plugin.Name, controllerType.BaseType.GetGenericArguments()[0]));
                        }

                        // Resolve IoC arguments
                        constructorArgs.AddRange(controllerType.GetConstructors()[0].GetParameters().Skip(constructorArgs.Count).Select(p => container.Resolve(p.ParameterType)));

                        Controller controller = null;

                        try
                        {
                            // Construct controller instance
                            controller = (Controller)Activator.CreateInstance(controllerType, constructorArgs.ToArray());
                        }
                        catch (Exception ex)
                        {
                            // TODO: Dispose of controller

                            logger.Error(ex, $"Unhandled exception in plugin {plugin.FullName}");
                        }

                        if (controller == null)
                        {
                            continue;
                        }

                        try
                        {
                            await controller.Loaded();

                            if (!this.controllers.ContainsKey(plugin.Name))
                            {
                                this.controllers.Add(plugin.Name, new List <Controller>());
                            }
                            this.controllers[plugin.Name].Add(controller);
                        }
                        catch (Exception ex)
                        {
                            // TODO: Dispose of controller

                            logger.Error(ex, $"Unhandled exception loading plugin {plugin.FullName}");
                        }
                    }
                }
            }

            await Task.WhenAll(this.controllers.SelectMany(c => c.Value).Select(s => s.Started()));

            rcon.Controllers = this.controllers;

            comms.Event(CoreEvents.ClientPlugins).FromClients().OnRequest(e => e.Reply(graph.Plugins));

            logger.Debug($"{graph.Plugins.Count.ToString(CultureInfo.InvariantCulture)} plugin(s) loaded, {this.controllers.Count.ToString(CultureInfo.InvariantCulture)} controller(s) created");

            comms.Event(ServerEvents.ServerInitialized).ToServer().Emit();

            logger.Info("Server ready");
        }
Esempio n. 8
0
        public Program()
        {
            // Set the AppDomain working directory to the current resource root
            Environment.CurrentDirectory = FileManager.ResolveResourcePath();

            var config = ConfigurationManager.Load <CoreConfiguration>("nfive.yml");

            var logger = new Logger(config.Log.Core);

            //ServerConfiguration.LogLevel = config.Log.Level;
            API.SetGameType(config.Display.Game);
            API.SetMapName(config.Display.Map);

            // Setup RPC handlers
            RpcManager.Configure(config.Log.Rpc, this.EventHandlers);

            var events = new EventManager(config.Log.Events);
            var rcon   = new RconManager(new RpcHandler());

            // Load core controllers
            this.controllers.Add(new Name("NFive/Server"), new List <Controller>());

            var dbController = new DatabaseController(new Logger(config.Log.Core, "Database"), events, new RpcHandler(), rcon, ConfigurationManager.Load <DatabaseConfiguration>("database.yml"));

            this.controllers[new Name("NFive/Server")].Add(dbController);

            // Resolve dependencies
            var graph = DefinitionGraph.Load();

            var pluginDefaultLogLevel = config.Log.Plugins.ContainsKey("default") ? config.Log.Plugins["default"] : LogLevel.Info;

            // Load plugins into the AppDomain
            foreach (var plugin in graph.Plugins)
            {
                logger.Info($"Loading {plugin.FullName}");

                // Load include files
                foreach (var includeName in plugin.Server?.Include ?? new List <string>())
                {
                    var includeFile = Path.Combine("plugins", plugin.Name.Vendor, plugin.Name.Project, $"{includeName}.net.dll");
                    if (!File.Exists(includeFile))
                    {
                        throw new FileNotFoundException(includeFile);
                    }

                    AppDomain.CurrentDomain.Load(File.ReadAllBytes(includeFile));
                }

                // Load main files
                foreach (var mainName in plugin.Server?.Main ?? new List <string>())
                {
                    var mainFile = Path.Combine("plugins", plugin.Name.Vendor, plugin.Name.Project, $"{mainName}.net.dll");
                    if (!File.Exists(mainFile))
                    {
                        throw new FileNotFoundException(mainFile);
                    }

                    var types = Assembly.LoadFrom(mainFile).GetTypes().Where(t => !t.IsAbstract && t.IsClass).ToList();

                    //logger.Debug($"{mainName}: {types.Count} {string.Join(Environment.NewLine, types)}");

                    // Find migrations
                    foreach (var migrationType in types.Where(t => t.BaseType != null && t.BaseType.IsGenericType && t.BaseType.GetGenericTypeDefinition() == typeof(MigrationConfiguration <>)))
                    {
                        var configuration = (DbMigrationsConfiguration)Activator.CreateInstance(migrationType);
                        var migrator      = new DbMigrator(configuration);

                        if (!migrator.GetPendingMigrations().Any())
                        {
                            continue;
                        }

                        if (!ServerConfiguration.AutomaticMigrations)
                        {
                            throw new MigrationsPendingException($"Plugin {plugin.FullName} has pending migrations but automatic migrations are disabled");
                        }

                        logger.Debug($"{mainName}: Running migrations {string.Join(", ", migrator.GetPendingMigrations())}");

                        migrator.Update();
                    }

                    // Find controllers
                    foreach (var controllerType in types.Where(t => t.IsSubclassOf(typeof(Controller)) || t.IsSubclassOf(typeof(ConfigurableController <>))))
                    {
                        var logLevel = config.Log.Plugins.ContainsKey(plugin.Name) ? config.Log.Plugins[plugin.Name] : pluginDefaultLogLevel;

                        var constructorArgs = new List <object>
                        {
                            new Logger(logLevel, plugin.Name),
                            events,
                            new RpcHandler(),
                            rcon
                        };

                        // Check if controller is configurable
                        if (controllerType.BaseType != null && controllerType.BaseType.IsGenericType && controllerType.BaseType.GetGenericTypeDefinition() == typeof(ConfigurableController <>))
                        {
                            // Initialize the controller configuration
                            constructorArgs.Add(ConfigurationManager.InitializeConfig(plugin.Name, controllerType.BaseType.GetGenericArguments()[0]));
                        }

                        // Construct controller instance
                        var controller = (Controller)Activator.CreateInstance(controllerType, constructorArgs.ToArray());

                        if (!this.controllers.ContainsKey(plugin.Name))
                        {
                            this.controllers.Add(plugin.Name, new List <Controller>());
                        }
                        this.controllers[plugin.Name].Add(controller);
                    }
                }
            }

            rcon.Controllers = this.controllers;

            new RpcHandler().Event(SDK.Core.Rpc.RpcEvents.ClientPlugins).On(e => e.Reply(graph.Plugins));

            events.Raise(SDK.Server.Events.ServerEvents.ServerInitialized);

            logger.Info($"{graph.Plugins.Count} plugins loaded, {this.controllers.Count} controller(s) created");
        }
Esempio n. 9
0
        private async void Startup()
        {
            // Set the AppDomain working directory to the current resource root
            Environment.CurrentDirectory = Path.GetFullPath(API.GetResourcePath(API.GetCurrentResourceName()));

            Logger.Initialize();
            new Logger().Info($"NFive {typeof(Program).Assembly.GetCustomAttributes<AssemblyInformationalVersionAttribute>().First().InformationalVersion}");

            var config = ConfigurationManager.Load <CoreConfiguration>("nfive.yml");

            ServerLogConfiguration.Output = config.Log.Output;
            //ServerConfiguration.LogLevel = config.Log.Level;

            var logger = new Logger(config.Log.Core);

            API.SetGameType(config.Display.Game);
            API.SetMapName(config.Display.Map);

            // Setup RPC handlers
            RpcManager.Configure(config.Log.Rpc, this.EventHandlers);

            // Client log mirroring
            new RpcHandler().Event("nfive:log:mirror").On(new Action <IRpcEvent, DateTime, LogLevel, string, string>((e, dt, level, prefix, message) =>
            {
                new Logger(LogLevel.Trace, $"Client#{e.Client.Handle}|{prefix}").Log(message, level);
            }));

            var events = new EventManager(config.Log.Events);
            var rcon   = new RconManager(new RpcHandler());

            // Load core controllers
            var dbController = new DatabaseController(new Logger(config.Log.Core, "Database"), events, new RpcHandler(), rcon, ConfigurationManager.Load <DatabaseConfiguration>("database.yml"));
            await dbController.Loaded();

            this.controllers.Add(new Name("NFive/Database"), new List <Controller> {
                dbController
            });

            var sessionController = new SessionController(new Logger(config.Log.Core, "Session"), events, new RpcHandler(), rcon, ConfigurationManager.Load <SessionConfiguration>("session.yml"));
            await sessionController.Loaded();

            this.controllers.Add(new Name("NFive/Session"), new List <Controller> {
                sessionController
            });

            // Resolve dependencies
            var graph = DefinitionGraph.Load();

            var pluginDefaultLogLevel = config.Log.Plugins.ContainsKey("default") ? config.Log.Plugins["default"] : LogLevel.Info;

            // IoC
            var assemblies = new List <Assembly>();

            assemblies.AddRange(graph.Plugins.Where(p => p.Server?.Include != null).SelectMany(p => p.Server.Include.Select(i => Assembly.LoadFrom(Path.Combine("plugins", p.Name.Vendor, p.Name.Project, $"{i}.net.dll")))));
            assemblies.AddRange(graph.Plugins.Where(p => p.Server?.Main != null).SelectMany(p => p.Server.Main.Select(m => Assembly.LoadFrom(Path.Combine("plugins", p.Name.Vendor, p.Name.Project, $"{m}.net.dll")))));

            var registrar = new ContainerRegistrar();

            registrar.RegisterService <ILogger>(s => new Logger());
            registrar.RegisterType <IRpcHandler, RpcHandler>();
            registrar.RegisterInstance <IEventManager>(events);
            registrar.RegisterInstance <IRconManager>(rcon);
            registrar.RegisterInstance <IClientList>(new ClientList(new Logger(config.Log.Core, "ClientList"), new RpcHandler()));
            registrar.RegisterSdkComponents(assemblies.Distinct());

            // DI
            var container = registrar.Build();

            // Load plugins into the AppDomain
            foreach (var plugin in graph.Plugins)
            {
                logger.Info($"Loading {plugin.FullName}");

                // Load include files
                foreach (var includeName in plugin.Server?.Include ?? new List <string>())
                {
                    var includeFile = Path.Combine("plugins", plugin.Name.Vendor, plugin.Name.Project, $"{includeName}.net.dll");
                    if (!File.Exists(includeFile))
                    {
                        throw new FileNotFoundException(includeFile);
                    }

                    AppDomain.CurrentDomain.Load(File.ReadAllBytes(includeFile));
                }

                // Load main files
                foreach (var mainName in plugin.Server?.Main ?? new List <string>())
                {
                    var mainFile = Path.Combine("plugins", plugin.Name.Vendor, plugin.Name.Project, $"{mainName}.net.dll");
                    if (!File.Exists(mainFile))
                    {
                        throw new FileNotFoundException(mainFile);
                    }

                    var types = Assembly.LoadFrom(mainFile).GetTypes().Where(t => !t.IsAbstract && t.IsClass).ToList();

                    // Find migrations
                    foreach (var migrationType in types.Where(t => t.BaseType != null && t.BaseType.IsGenericType && t.BaseType.GetGenericTypeDefinition() == typeof(MigrationConfiguration <>)))
                    {
                        var configuration = (DbMigrationsConfiguration)Activator.CreateInstance(migrationType);
                        var migrator      = new DbMigrator(configuration);

                        if (!migrator.GetPendingMigrations().Any())
                        {
                            continue;
                        }

                        if (!ServerConfiguration.AutomaticMigrations)
                        {
                            throw new MigrationsPendingException($"Plugin {plugin.FullName} has pending migrations but automatic migrations are disabled");
                        }

                        foreach (var migration in migrator.GetPendingMigrations())
                        {
                            new Logger(config.Log.Core, "Database").Debug($"[{mainName}] Running migration: {migration}");

                            migrator.Update(migration);
                        }
                    }

                    // Find controllers
                    foreach (var controllerType in types.Where(t => t.IsSubclassOf(typeof(Controller)) || t.IsSubclassOf(typeof(ConfigurableController <>))))
                    {
                        var logLevel = config.Log.Plugins.ContainsKey(plugin.Name) ? config.Log.Plugins[plugin.Name] : pluginDefaultLogLevel;

                        var constructorArgs = new List <object>
                        {
                            new Logger(logLevel, plugin.Name),
                            events,
                            new RpcHandler(),
                            rcon
                        };

                        // Check if controller is configurable
                        if (controllerType.BaseType != null && controllerType.BaseType.IsGenericType && controllerType.BaseType.GetGenericTypeDefinition() == typeof(ConfigurableController <>))
                        {
                            // Initialize the controller configuration
                            constructorArgs.Add(ConfigurationManager.InitializeConfig(plugin.Name, controllerType.BaseType.GetGenericArguments()[0]));
                        }

                        // Resolve IoC arguments
                        constructorArgs.AddRange(controllerType.GetConstructors()[0].GetParameters().Skip(constructorArgs.Count).Select(p => container.Resolve(p.ParameterType)));

                        Controller controller = null;

                        try
                        {
                            // Construct controller instance
                            controller = (Controller)Activator.CreateInstance(controllerType, constructorArgs.ToArray());
                        }
                        catch (Exception ex)
                        {
                            // TODO: Dispose of controller

                            logger.Error(ex, $"Unhandled exception in plugin {plugin.FullName}");
                        }

                        if (controller == null)
                        {
                            continue;
                        }

                        try
                        {
                            await controller.Loaded();

                            if (!this.controllers.ContainsKey(plugin.Name))
                            {
                                this.controllers.Add(plugin.Name, new List <Controller>());
                            }
                            this.controllers[plugin.Name].Add(controller);
                        }
                        catch (Exception ex)
                        {
                            // TODO: Dispose of controller

                            logger.Error(ex, $"Unhandled exception loading plugin {plugin.FullName}");
                        }
                    }
                }
            }

#pragma warning disable 4014
            foreach (var controller in this.controllers.SelectMany(c => c.Value))
            {
                controller.Started();
            }
#pragma warning restore 4014

            rcon.Controllers = this.controllers;

            new RpcHandler().Event(SDK.Core.Rpc.RpcEvents.ClientPlugins).On(e => e.Reply(graph.Plugins));

            events.Raise(ServerEvents.ServerInitialized);

            logger.Debug($"{graph.Plugins.Count} plugin(s) loaded, {this.controllers.Count} controller(s) created");
        }
Esempio n. 10
0
        /// <summary>
        /// Serializes the specified <see cref="DefinitionGraph"/> to a FiveM Lua resource file format.
        /// </summary>
        /// <param name="graph">The definition graph to serialize.</param>
        /// <returns>The definition graph in FiveM Lua resource file format.</returns>
        public static void Serialize(DefinitionGraph graph)
        {
            var output = new StringBuilder();

            output.AppendLine("-- FiveM resource definition");
            output.AppendLine("-- This file is automatically generated with `nfpm install` - any manual changes will be lost");
            output.AppendLine();

            output.AppendLine($"resource_manifest_version '{ManifestVersion.ToString()}'");
            output.AppendLine();

            output.AppendLine("server_scripts {");
            output.AppendLine("\t-- NFive");
            foreach (var file in DefaultSeverScripts)
            {
                output.AppendLine($"\t'{file}',");
            }
            output.AppendLine("}");
            output.AppendLine();

            output.AppendLine("client_scripts {");
            foreach (var plugin in graph.Plugins.Where(d => d.Client?.Include?.Count > 0 || d.Client?.Main?.Count > 0))
            {
                output.AppendLine($"\t-- {plugin.Name}@{plugin.Version}");
                if (plugin.Client?.Include != null)
                {
                    foreach (var file in plugin.Client.Include)
                    {
                        output.AppendLine($"\t'{Path.Combine(ConfigurationManager.PluginPath, plugin.Name.Vendor, plugin.Name.Project, file).Replace(Path.DirectorySeparatorChar, '/')}.net.dll',");
                    }
                }
                if (plugin.Client?.Main != null)
                {
                    foreach (var file in plugin.Client.Main)
                    {
                        output.AppendLine($"\t'{Path.Combine(ConfigurationManager.PluginPath, plugin.Name.Vendor, plugin.Name.Project, file).Replace(Path.DirectorySeparatorChar, '/')}.net.dll',");
                    }
                }
                output.AppendLine();
            }
            output.AppendLine("\t-- NFive");
            foreach (var file in DefaultClientScripts)
            {
                output.AppendLine($"\t'{file}',");
            }
            output.AppendLine("}");
            output.AppendLine();

            output.AppendLine("files {");
            output.AppendLine("\t-- NFive");
            foreach (var file in DefaultClientFiles)
            {
                output.AppendLine($"\t'{file}',");
            }
            foreach (var plugin in graph.Plugins.Where(d => d.Client?.Files?.Count > 0 || d.Client?.Overlays?.Count > 0))
            {
                output.AppendLine();
                output.AppendLine($"\t-- {plugin.Name}@{plugin.Version}");
                foreach (var file in plugin.Client.Files)
                {
                    output.AppendLine($"\t'{Path.Combine(ConfigurationManager.PluginPath, plugin.Name.Vendor, plugin.Name.Project, file).Replace(Path.DirectorySeparatorChar, '/')}',");
                }
            }
            output.AppendLine("}");

            var loadscreen = graph.Plugins.FirstOrDefault(d => d.Client?.LoadingScreen != null);

            if (loadscreen != null)
            {
                output.AppendLine();
                output.AppendLine($"-- {loadscreen.Name}@{loadscreen.Version}");
                output.AppendLine($"loadscreen '{Path.Combine(ConfigurationManager.PluginPath, loadscreen.Name.Vendor, loadscreen.Name.Project, loadscreen.Client.LoadingScreen).Replace(Path.DirectorySeparatorChar, '/')}'");
            }

            output.AppendLine();
            output.AppendLine("-- NFive");
            output.AppendLine("ui_page 'index.html'");

            File.WriteAllText(Path.Combine(Environment.CurrentDirectory, ConfigurationManager.ResourceFile), output.ToString());
        }
Esempio n. 11
0
        internal async Task <int> Main()
        {
            Definition definition;

            try
            {
                Environment.CurrentDirectory = PathManager.FindResource();

                definition = Definition.Load(Program.DefinitionFile);
            }
            catch (FileNotFoundException ex)
            {
                Console.WriteLine(ex.Message);
                Console.WriteLine("Use `nfpm init` to setup NFive in this directory");

                return(1);
            }

            DefinitionGraph graph;

            try
            {
                graph = DefinitionGraph.Load();
            }
            catch (FileNotFoundException)
            {
                graph = null;
            }
            catch (Exception ex)
            {
                Console.WriteLine("Unable to build definition graph (PANIC):", Color.Red);
                Console.WriteLine(ex.Message);
                if (ex.InnerException != null)
                {
                    Console.WriteLine(ex.InnerException.Message);
                }

                return(1);
            }

            if (!this.Plugins.Any())
            {
                if (graph != null)
                {
                    await graph.Apply();

                    if (PathManager.IsResource())
                    {
                        ResourceGenerator.Serialize(graph).Save();
                    }
                }
                else
                {
                    graph = new DefinitionGraph();
                    await graph.Build(definition);

                    await graph.Apply();

                    graph.Save();

                    if (PathManager.IsResource())
                    {
                        ResourceGenerator.Serialize(graph).Save();
                    }
                }

                return(0);
            }

            foreach (var plugin in this.Plugins)
            {
                var  parts = plugin.Split(new[] { '@' }, 2);
                Name name;
                var  version = new VersionRange("*");

                try
                {
                    name = new Name(parts[0]);
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex);
                    return(1);
                }

                if (parts.Length == 2)
                {
                    try
                    {
                        version = new VersionRange(parts[1]);
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine(ex);
                        return(1);
                    }
                }

                if (definition.Dependencies == null)
                {
                    definition.Dependencies = new Dictionary <Name, VersionRange>();
                }

                if (definition.Dependencies.ContainsKey(name))
                {
                    definition.Dependencies[name] = version;
                }
                else
                {
                    definition.Dependencies.Add(name, version);
                }
            }

            graph = new DefinitionGraph();
            await graph.Build(definition);

            await graph.Apply();

            definition.Save(Program.DefinitionFile);
            graph.Save();

            if (PathManager.IsResource())
            {
                ResourceGenerator.Serialize(graph).Save();
            }

            return(0);
        }