Beispiel #1
0
        private async void Startup()
        {
            // Setup RPC handlers
            RpcManager.Configure(this.EventHandlers);

            var ticks  = new TickManager(c => this.Tick += c, c => this.Tick -= c);
            var events = new EventManager();
            var rpc    = new RpcHandler();
            var nui    = new NuiManager(this.EventHandlers);

            var user = await rpc.Event("clientInitialize").Request <User>("1.0.0");

            foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
            {
                if (assembly.GetCustomAttribute <ClientPluginAttribute>() == null)
                {
                    continue;
                }

                this.logger.Info(assembly.GetName().Name);

                foreach (var type in assembly.GetTypes().Where(t => !t.IsAbstract && t.IsSubclassOf(typeof(Service))))
                {
                    this.logger.Info($"\t{type.FullName}");

                    var service = (Service)Activator.CreateInstance(type, new Logger($"Plugin|{type.Name}"), ticks, events, rpc, nui, user);
                    await service.Loaded();

                    this.services.Add(service);
                }
            }

            this.logger.Info("Plugins loaded");

#pragma warning disable 4014
            foreach (var service in this.services)
            {
                service.Started();
            }
#pragma warning restore 4014

            this.logger.Info("Plugins started");

            rpc.Event("clientInitialized").Trigger();
        }
Beispiel #2
0
        private async void Startup()
        {
            this.logger.Info($"NFive {typeof(Program).Assembly.GetCustomAttributes<AssemblyInformationalVersionAttribute>().First().InformationalVersion}");

            // Setup RPC handlers
            RpcManager.Configure(this.EventHandlers);
            var rpc = new RpcHandler();

            var ticks    = new TickManager(c => this.Tick += c, c => this.Tick -= c);
            var events   = new EventManager();
            var commands = new CommandManager(rpc);
            var nui      = new NuiManager(this.EventHandlers);

            // Initial connection
            //var configuration = await rpc.Event(SDK.Core.Rpc.RpcEvents.ClientInitialize).Request<ClientConfiguration>(typeof(Program).Assembly.GetName().Version);
            var config = await rpc.Event(SDK.Core.Rpc.RpcEvents.ClientInitialize).Request <User, LogLevel, LogLevel>(typeof(Program).Assembly.GetName().Version.ToString());

            ClientConfiguration.ConsoleLogLevel = config.Item2;
            ClientConfiguration.MirrorLogLevel  = config.Item3;

            var plugins = await rpc.Event(SDK.Core.Rpc.RpcEvents.ClientPlugins).Request <List <Plugin> >();

            foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
            {
                if (assembly.GetCustomAttribute <ClientPluginAttribute>() == null)
                {
                    continue;
                }

                var plugin = plugins.FirstOrDefault(p => p.Client?.Main?.FirstOrDefault(m => m + ".net" == assembly.GetName().Name) != null);

                if (plugin == null)
                {
                    this.logger.Debug("Skipping " + assembly.GetName().Name);
                    continue;
                }

                this.logger.Info(plugin.FullName);
                this.logger.Info($"\t{assembly.GetName().Name}");

                foreach (var type in assembly.GetTypes().Where(t => !t.IsAbstract && t.IsSubclassOf(typeof(Service))))
                {
                    this.logger.Info($"\t\t{type.FullName}");

                    var service = (Service)Activator.CreateInstance(type, new Logger($"Plugin|{type.Name}"), ticks, events, rpc, commands, new OverlayManager(plugin.Name, nui), config.Item1);
                    await service.Loaded();

                    this.services.Add(service);
                }
            }

            // Forward raw FiveM events
            this.EventHandlers.Add("gameEventTriggered", new Action <string, List <object> >((s, a) => events.Raise("gameEventTriggered", s, a)));
            this.EventHandlers.Add("populationPedCreating", new Action <float, float, float, uint, object>((x, y, z, model, setters) => events.Raise("populationPedCreating", new PedSpawnOptions(x, y, z, model, setters))));

            this.logger.Info("Plugins loaded");

#pragma warning disable 4014
            foreach (var service in this.services)
            {
                service.Started();
            }
#pragma warning restore 4014

            this.logger.Info("Plugins started");

            rpc.Event(SDK.Core.Rpc.RpcEvents.ClientInitialized).Trigger();

            foreach (var service in this.services)
            {
                await service.HoldFocus();
            }
        }
Beispiel #3
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");
        }
Beispiel #4
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");
        }
Beispiel #5
0
        private async void Startup()
        {
            // Setup RPC handlers
            RpcManager.Configure(this.EventHandlers);

            var ticks    = new TickManager(c => this.Tick += c, c => this.Tick -= c);
            var events   = new EventManager();
            var rpc      = new RpcHandler();
            var commands = new CommandManager(rpc);
            var nui      = new NuiManager(this.EventHandlers);

            var user = await rpc.Event(SDK.Core.Rpc.RpcEvents.ClientInitialize).Request <User>("1.0.0");

            var plugins = await rpc.Event(SDK.Core.Rpc.RpcEvents.ClientPlugins).Request <List <Plugin> >();

            foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
            {
                if (assembly.GetCustomAttribute <ClientPluginAttribute>() == null)
                {
                    continue;
                }

                var plugin = plugins.FirstOrDefault(p => p.Client?.Main?.FirstOrDefault(m => m + ".net" == assembly.GetName().Name) != null);

                if (plugin == null)
                {
                    this.logger.Debug("Skipping " + assembly.GetName().Name);
                    continue;
                }

                this.logger.Info(plugin.FullName);
                this.logger.Info($"\t{assembly.GetName().Name}");

                foreach (var type in assembly.GetTypes().Where(t => !t.IsAbstract && t.IsSubclassOf(typeof(Service))))
                {
                    this.logger.Info($"\t\t{type.FullName}");

                    var service = (Service)Activator.CreateInstance(type, new Logger($"Plugin|{type.Name}"), ticks, events, rpc, commands, new OverlayManager(plugin.Name, nui), user);
                    await service.Loaded();

                    this.services.Add(service);
                }
            }

            this.logger.Info("Plugins loaded");

#pragma warning disable 4014
            foreach (var service in this.services)
            {
                service.Started();
            }
#pragma warning restore 4014

            this.logger.Info("Plugins started");

            foreach (var service in this.services)
            {
                await service.HoldFocus();
            }

            rpc.Event(SDK.Core.Rpc.RpcEvents.ClientInitialized).Trigger();
        }
Beispiel #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");

            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");
        }
Beispiel #7
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");
        }
Beispiel #8
0
        private async void Startup()
        {
            // Print exception messages in English
            // TODO: Test
            Thread.CurrentThread.CurrentUICulture = CultureInfo.InvariantCulture;

            var asm = GetType().Assembly;

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

            var ticks    = new TickManager(c => this.Tick += c, c => this.Tick -= c);
            var events   = new EventManager();
            var comms    = new CommunicationManager(events);
            var commands = new CommandManager(comms);
            var nui      = new NuiManager(this.EventHandlers);

            // Initial connection
            var config = await comms.Event(CoreEvents.ClientInitialize).ToServer().Request <User, Tuple <LogLevel, LogLevel>, Tuple <List <string>, string> >(asm.GetName().Version.ToString());

            ClientConfiguration.Log.ConsoleLogLevel = config.Item2.Item1;
            ClientConfiguration.Log.MirrorLogLevel  = config.Item2.Item2;
            ClientConfiguration.Locale.Culture      = config.Item3.Item1.Select(c => new CultureInfo(c)).ToList();
            ClientConfiguration.Locale.TimeZone     = TimeZoneInfo.Utc;         // TODO: ??? + store IANA timezone

            // Use configured culture for output
            // TODO: Test
            Thread.CurrentThread.CurrentCulture = ClientConfiguration.Locale.Culture.First();

            // Configure overlays
            nui.Emit(new
            {
                @event = "nfive:config",
                data   = new
                {
                    locale   = ClientConfiguration.Locale.Culture.First().Name,
                    currency = new RegionInfo(ClientConfiguration.Locale.Culture.First().Name).ISOCurrencySymbol,
                    timezone = config.Item3.Item2
                }
            });

            // Forward raw FiveM events
            //this.EventHandlers.Add("gameEventTriggered", new Action<string, List<object>>((s, a) => events.Emit("gameEventTriggered", s, a)));
            //this.EventHandlers.Add("populationPedCreating", new Action<float, float, float, uint, object>((x, y, z, model, setters) => events.Emit("populationPedCreating", new PedSpawnOptions(x, y, z, model, setters))));
            //RpcManager.OnRaw("onClientResourceStart", new Action<Player, string>(OnClientResourceStartRaw));
            //RpcManager.OnRaw("onClientResourceStop", new Action<Player, string>(OnClientResourceStopRaw));
            //RpcManager.OnRaw("gameEventTriggered", new Action<Player, string, List<dynamic>>(OnGameEventTriggeredRaw));
            //RpcManager.OnRaw(FiveMClientEvents.PopulationPedCreating, new Action<float, float, float, uint, IPopulationPedCreatingSetter>(OnPopulationPedCreatingRaw));

            // Provide raw BaseScript types to services
            Service.EventHandlers = this.EventHandlers;
            Service.Exports       = this.Exports;
            Service.Players       = this.Players;

            var plugins = await comms.Event(CoreEvents.ClientPlugins).ToServer().Request <List <Plugin> >();

            foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
            {
                if (assembly.GetCustomAttribute <ClientPluginAttribute>() == null)
                {
                    continue;
                }

                var plugin = plugins.FirstOrDefault(p => p.Client?.Main?.FirstOrDefault(m => $"{m}.net" == assembly.GetName().Name) != null);

                if (plugin == null)
                {
                    this.logger.Warn($"Skipping {assembly.GetName().Name}, no client plugin");
                    continue;
                }

                this.logger.Info(plugin.FullName);

                foreach (var type in assembly.GetTypes().Where(t => !t.IsAbstract && t.IsSubclassOf(typeof(Service))))
                {
                    var service = (Service)Activator.CreateInstance(type, new Logger($"Plugin|{type.Name}"), ticks, comms, commands, new OverlayManager(plugin.Name, nui), config.Item1);
                    await service.Loaded();

                    this.services.Add(service);
                }
            }

            this.logger.Info("Plugins loaded");

            await Task.WhenAll(this.services.Select(s => s.Started()));

            this.logger.Info("Plugins started");

            comms.Event(CoreEvents.ClientInitialized).ToServer().Emit();

            this.logger.Info("Client ready");

            foreach (var service in this.services)
            {
                await service.HoldFocus();
            }
        }