using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.Immutable; using System.IO; using System.Linq; using System.Net.Mime; using System.Threading; using System.Threading.Tasks; using Barotrauma.LuaCs; using Barotrauma.LuaCs.Configuration; using Barotrauma.LuaCs.Data; using Barotrauma.LuaCs.Events; using Barotrauma.LuaCs.Services; using Barotrauma.LuaCs.Services.Compatibility; using Barotrauma.LuaCs.Services.Processing; using Barotrauma.LuaCs.Services.Safe; using Barotrauma.Networking; using Barotrauma.Steam; using FluentResults; using ImpromptuInterface; using Microsoft.Toolkit.Diagnostics; namespace Barotrauma { internal delegate void LuaCsMessageLogger(string message); internal delegate void LuaCsErrorHandler(Exception ex, LuaCsMessageOrigin origin); internal delegate void LuaCsExceptionHandler(Exception ex, LuaCsMessageOrigin origin); partial class LuaCsSetup : IDisposable, IEventScreenSelected, IEventAllPackageListChanged, IEventEnabledPackageListChanged, IEventReloadAllPackages { public LuaCsSetup() { // == startup _servicesProvider = new ServicesProvider(); RegisterServices(_servicesProvider); if (!ValidateLuaCsContent()) { Logger.LogError($"{nameof(LuaCsSetup)}: ModConfigXml missing. Unable to continue."); throw new ApplicationException($"{nameof(LuaCsSetup)}: Lua ModConfig.xml is missing. Unable to continue."); } SubscribeToLuaCsEvents(); _runStateMachine = SetupStateMachine(); //LoadLuaCsConfig(); return; // == end StateMachine SetupStateMachine() { return new StateMachine(false, RunState.Unloaded, RunStateUnloaded_OnEnter, null) .AddState(RunState.LoadedNoExec, RunStateLoadedNoExec_OnEnter, RunStateLoadedNoExec_OnExit) .AddState(RunState.Running, RunStateRunning_OnEnter, RunStateRunning_OnExit); // ReSharper disable InconsistentNaming void RunStateUnloaded_OnEnter(State currentState) { } void RunStateLoadedNoExec_OnEnter(State currentState) { } void RunStateLoadedNoExec_OnExit(State currentState) { } void RunStateRunning_OnEnter(State currentState) { } void RunStateRunning_OnExit(State currentState) { } // ReSharper restore InconsistentNaming } } bool ValidateLuaCsContent() { #if DEBUG // TODO: we just wanna boot for now return true; #endif // check if /Content/Lua/ModConfig.xml exists // if not, try to copy it from the Local Mods folder // if not, try to copy it from the Workshop Mods folder // if that fails, throw an error and exit. throw new NotImplementedException(); } void RegisterServices(IServicesProvider servicesProvider) { servicesProvider.RegisterServiceType(ServiceLifetime.Singleton); servicesProvider.RegisterServiceType(ServiceLifetime.Singleton); servicesProvider.RegisterServiceType(ServiceLifetime.Transient); servicesProvider.RegisterServiceType(ServiceLifetime.Singleton); servicesProvider.RegisterServiceType(ServiceLifetime.Singleton); servicesProvider.RegisterServiceType(ServiceLifetime.Singleton); servicesProvider.RegisterServiceType(ServiceLifetime.Singleton); servicesProvider.RegisterServiceType(ServiceLifetime.Transient); servicesProvider.RegisterServiceType(ServiceLifetime.Singleton); // TODO: IConfigService // TODO: INetworkingService // TODO: [Resource Converter/Parser Services] servicesProvider.RegisterServiceType(ServiceLifetime.Transient); // service config data servicesProvider.RegisterServiceType(ServiceLifetime.Singleton); servicesProvider.RegisterServiceType(ServiceLifetime.Singleton); servicesProvider.RegisterServiceType(ServiceLifetime.Singleton); // gen IL servicesProvider.Compile(); } void SubscribeToLuaCsEvents() { EventService.Subscribe(this); // game state hook in EventService.Subscribe(this); EventService.Subscribe(this); EventService.Subscribe(this); } #region CONST_DEF #if SERVER public const bool IsServer = true; #else public const bool IsServer = false; #endif public const bool IsClient = !IsServer; #endregion #region Services_ConfigVars /* * === Singleton Services */ private readonly IServicesProvider _servicesProvider; public PerformanceCounterService PerformanceCounter => _servicesProvider.GetService(); public ILoggerService Logger => _servicesProvider.GetService(); public IConfigService ConfigService => _servicesProvider.GetService(); public IPackageManagementService PackageManagementService => _servicesProvider.GetService(); public IPluginManagementService PluginManagementService => _servicesProvider.GetService(); public ILuaScriptManagementService LuaScriptManagementService => _servicesProvider.GetService(); public INetworkingService NetworkingService => _servicesProvider.GetService(); public IEventService EventService => _servicesProvider.GetService(); public LuaGame Game => _servicesProvider.GetService(); #region LuaCsInternal /* * === Config Vars */ /// /// Whether C# plugin code is enabled. /// internal IConfigEntry IsCsEnabled { get; private set; } /// /// Whether mods marked as 'forced' or 'always load' should only be loaded if they're in the enabled mods list. /// internal IConfigEntry TreatForcedModsAsNormal { get; private set; } /// /// Whether the lua script runner from Workshop package should be used over the in-built version. /// internal IConfigEntry PreferToUseWorkshopLuaSetup { get; private set; } /// /// Whether the popup error GUI should be hidden/suppressed. /// internal IConfigEntry DisableErrorGUIOverlay { get; private set; } /// /// Whether usernames are anonymized or show in logs. /// internal IConfigEntry HideUserNamesInLogs { get; private set; } /// /// The SteamId of the Workshop LuaCs CPackage in use, if available. /// internal IConfigEntry LuaForBarotraumaSteamId { get; private set; } /// /// TODO: @evilfactory@users.noreply.github.com /// internal IConfigEntry RestrictMessageSize { get; private set; } /// /// The local save path for all local data storage for mods. /// internal IConfigEntry LocalDataSavePath { get; private set; } #endregion /** * == Ops Vars */ private RunState _runState; /// /// The current run state of all services managed by LuaCs. /// public RunState CurrentRunState { get => _runState; private set => _runState = value; } private readonly StateMachine _runStateMachine; private readonly ConcurrentQueue _toLoad = new(); private readonly ConcurrentQueue _toUnload = new(); #endregion #region LegacyRedirects public ILuaCsHook Hook => this.EventService; #endregion public void Dispose() { try { SetRunState(RunState.Unloaded); } catch (Exception e) { Logger.LogError(e.Message); } try { DisposeLuaCsConfig(); PluginManagementService.Dispose(); LuaScriptManagementService.Dispose(); ConfigService.Dispose(); PackageManagementService.Dispose(); // TODO: Add all missing services. //NetworkingService.Dispose(); EventService.Dispose(); _servicesProvider.DisposeAndReset(); } catch (Exception e) { Console.WriteLine(e); throw; } GC.SuppressFinalize(this); } /// /// Handles changes in game states tracked by screen changes. /// /// The new game screen. public partial void OnScreenSelected(Screen screen); public void OnAllPackageListChanged(IEnumerable corePackages, IEnumerable regularPackages) { UpdateLoadedPackagesList(); } public void OnEnabledPackageListChanged(CorePackage corePackage, IEnumerable regularPackages) { UpdateLoadedPackagesList(); } public void OnReloadAllPackages() { if (CurrentRunState <= RunState.Unloaded) return; var state = CurrentRunState; SetRunState(RunState.Unloaded); SetRunState(CurrentRunState); } public void ForceRunState(RunState newState) { if (CurrentRunState == newState) return; SetRunState(newState); } private void UpdateLoadedPackagesList() { throw new NotImplementedException($"Rewrite the loading state system."); var newPackSet = ContentPackageManager.AllPackages .Union(ContentPackageManager.EnabledPackages.All) .ToHashSet(); var currPackSet = PackageManagementService.GetAllLoadedPackages().ToHashSet(); var toAdd = newPackSet.Except(currPackSet); var toRemove = currPackSet.Except(newPackSet); foreach (var package in toAdd) _toLoad.Enqueue(package); foreach (var package in toRemove) _toUnload.Enqueue(package); //ProcessPackagesListDifferences(); } void SetRunState(RunState newRunState) { if (CurrentRunState == newRunState) return; } void LoadLuaCsConfig() { IsCsEnabled = ConfigService.TryGetConfig>(ContentPackageManager.VanillaCorePackage, "IsCsEnabled", out var val1) ? val1 : throw new NullReferenceException($"{nameof(IsCsEnabled)} cannot be loaded."); TreatForcedModsAsNormal = ConfigService.TryGetConfig>(ContentPackageManager.VanillaCorePackage, "TreatForcedModsAsNormal", out var val2) ? val2 : throw new NullReferenceException($"{nameof(TreatForcedModsAsNormal)} cannot be loaded."); DisableErrorGUIOverlay = ConfigService.TryGetConfig>(ContentPackageManager.VanillaCorePackage, "DisableErrorGUIOverlay", out var val3) ? val3 : throw new NullReferenceException($"{nameof(DisableErrorGUIOverlay)} cannot be loaded."); HideUserNamesInLogs = ConfigService.TryGetConfig>(ContentPackageManager.VanillaCorePackage, "HideUserNamesInLogs", out var val4) ? val4 : throw new NullReferenceException($"{nameof(HideUserNamesInLogs)} cannot be loaded."); LuaForBarotraumaSteamId = ConfigService.TryGetConfig>(ContentPackageManager.VanillaCorePackage, "LuaForBarotraumaSteamId", out var val5) ? val5 : throw new NullReferenceException($"{nameof(LuaForBarotraumaSteamId)} cannot be loaded."); RestrictMessageSize = ConfigService.TryGetConfig>(ContentPackageManager.VanillaCorePackage, "RestrictMessageSize", out var val7) ? val7 : throw new NullReferenceException($"{nameof(RestrictMessageSize)} cannot be loaded."); } void DisposeLuaCsConfig() { IsCsEnabled = null; TreatForcedModsAsNormal = null; DisableErrorGUIOverlay = null; HideUserNamesInLogs = null; LuaForBarotraumaSteamId = null; RestrictMessageSize = null; } } /// /// Specifies the current run state of the LuaCs Modding System. /// [Important]Enum State values ordering must be in the form of (lower state) === (higher state) /// public enum RunState : byte { /// /// No assets are loaded, code execution suspended. /// Unloaded = 0, /// /// Loaded mod configs, settings and assets. No code execution. /// LoadedNoExec = 1, /// /// All assets loaded, code execution is active. /// Running = 2 } }