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); }
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)); }
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); }
/// <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())); }
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); }
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"); }
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"); }
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"); }
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"); }
/// <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()); }
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); }