From d9d980122d72df68fa1c5db3cb112476d27b0827 Mon Sep 17 00:00:00 2001 From: MapleWheels Date: Tue, 24 Feb 2026 15:26:49 -0500 Subject: [PATCH] - Moved all console commands to their respective services and added via injector service. - Fixed LuaCs IsCsEnabled prompt not working. - Add settings profiles support. --- .../ClientSource/DebugConsole.cs | 20 --- .../ClientSource/LuaCs/LuaCsSetup.cs | 34 +++- .../SharedSource/DebugConsole.cs | 88 +-------- .../SharedSource/LuaCs/LuaCsSetup.cs | 3 +- .../LuaCs/_Services/ConfigService.cs | 169 +++++++++++++++++- .../LuaCs/_Services/ConsoleCommandsService.cs | 61 +++++++ .../_Services/LuaScriptManagementService.cs | 32 +++- .../_Services/_Interfaces/IConfigService.cs | 1 + .../_Interfaces/IConsoleCommandsService.cs | 11 ++ 9 files changed, 298 insertions(+), 121 deletions(-) create mode 100644 Barotrauma/BarotraumaShared/SharedSource/LuaCs/_Services/ConsoleCommandsService.cs create mode 100644 Barotrauma/BarotraumaShared/SharedSource/LuaCs/_Services/_Interfaces/IConsoleCommandsService.cs diff --git a/Barotrauma/BarotraumaClient/ClientSource/DebugConsole.cs b/Barotrauma/BarotraumaClient/ClientSource/DebugConsole.cs index 38e2bb6f7..46fa01e72 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/DebugConsole.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/DebugConsole.cs @@ -4240,28 +4240,8 @@ namespace Barotrauma var result = GameMain.LuaCs.LuaScriptManagementService.DoString(string.Join(" ", args)); GameMain.LuaCs.Logger.LogResults(result.ToResult()); })); - - commands.Add(new Command("cl_reloadlua|cl_reloadcs|cl_reloadluacs", "Re-initializes the LuaCs environment.", (string[] args) => - { - GameMain.LuaCs.EventService.PublishEvent(sub => sub.OnReloadAllPackages()); - })); - - commands.Add(new Command("cl_toggleluadebug", "Toggles the MoonSharp Debug Server.", (string[] args) => - { - int port = 41912; - - if (args.Length > 0) - { - int.TryParse(args[0], out port); - } - - throw new NotImplementedException(); - //GameMain.LuaCs.ToggleDebugger(port); - })); } - - private static void ReloadWearables(Character character, int variant = 0) { foreach (var limb in character.AnimController.Limbs) diff --git a/Barotrauma/BarotraumaClient/ClientSource/LuaCs/LuaCsSetup.cs b/Barotrauma/BarotraumaClient/ClientSource/LuaCs/LuaCsSetup.cs index d1fb680a0..31c633b81 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/LuaCs/LuaCsSetup.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/LuaCs/LuaCsSetup.cs @@ -53,15 +53,39 @@ namespace Barotrauma msg.Buttons[0].OnClicked = (GUIButton button, object obj) => { - this.IsCsEnabled = true; - isCsValueChanged = true; - return true; + try + { + this.IsCsEnabled = true; + isCsValueChanged = true; + CoroutineManager.Invoke(() => + { + if (CurrentRunState >= RunState.Running) + { + var currentRunState = CurrentRunState; + SetRunState(RunState.LoadedNoExec); + SetRunState(currentRunState); + } + }, 0f); + return true; + } + finally + { + msg.Close(); + } }; msg.Buttons[1].OnClicked = (GUIButton button, object obj) => { - this.IsCsEnabled = false; - return true; + try + { + // avoid a TOCTOU scenario. + this.IsCsEnabled = false; + return true; + } + finally + { + msg.Close(); + } }; return isCsValueChanged; diff --git a/Barotrauma/BarotraumaShared/SharedSource/DebugConsole.cs b/Barotrauma/BarotraumaShared/SharedSource/DebugConsole.cs index 51cf0444f..bca95cd66 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/DebugConsole.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/DebugConsole.cs @@ -2311,93 +2311,7 @@ namespace Barotrauma NewMessage($"Start item set changed to \"{AutoItemPlacer.DefaultStartItemSet}\""); }, isCheat: false)); - commands.Add(new Command("cfg_getvalue", "cfg_getvalue [Content Package] [InternalName] [ValueString]: gets a config value.", (string[] args) => - { - if (args.Length < 1) - { - ThrowError("Please specify the name of the package to set the config."); - return; - } - - if (args.Length < 2) - { - ThrowError("Please specify the name of the config."); - return; - } - - var package = ContentPackageManager.RegularPackages.FirstOrDefault(p => p.Name == args[0]); - if (package == null) - { - ThrowError($"Could not find the package {args[0]}!"); - return; - } - - string internalName = args[1]; - - if (!GameMain.LuaCs.ConfigService.TryGetConfig(package, internalName, out LuaCs.Data.ISettingBase setting)) - { - ThrowError($"Could not get config with name {internalName}"); - return; - } - - NewMessage($"config {internalName} value is {setting.GetStringValue()}", Color.Green); - }, getValidArgs: () => new[] - { - ContentPackageManager.RegularPackages.Select(p => p.Name).ToArray() - })); - - commands.Add(new Command("cfg_setvalue", "cfg_setvalue [Content Package] [InternalName] [ValueString]: sets a config.", (string[] args) => - { - if (args.Length < 1) - { - ThrowError("Please specify the name of the package to set the config."); - return; - } - - if (args.Length < 2) - { - ThrowError("Please specify the name of the config."); - return; - } - - if (args.Length < 3) - { - ThrowError("Please specify the value to set the config to."); - return; - } - - var package = ContentPackageManager.RegularPackages.FirstOrDefault(p => p.Name == args[0]); - if (package == null) - { - ThrowError($"Could not find the package {args[0]}!"); - return; - } - - string internalName = args[1]; - string valueString = args[2]; - - if (!GameMain.LuaCs.ConfigService.TryGetConfig(package, internalName, out LuaCs.Data.ISettingBase setting)) - { - ThrowError($"Could not get config with name {internalName}"); - return; - } - - if (setting.TrySetValue(valueString)) - { - NewMessage($"Set config {internalName} value to {valueString}", Color.Green); - if (GameMain.LuaCs.ConfigService.SaveConfigValue(setting) is { IsFailed: true } res) - { - NewMessage($"Failed to save new config data to disk. Reasons: {res.ToString()}"); - } - } - else - { - ThrowError($"Failed to set config value"); - } - }, getValidArgs: () => new[] - { - ContentPackageManager.RegularPackages.Select(p => p.Name).ToArray() - })); + //"dummy commands" that only exist so that the server can give clients permissions to use them //TODO: alphabetical order? diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/LuaCsSetup.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/LuaCsSetup.cs index b7e5b1f3b..f81efda0d 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/LuaCsSetup.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/LuaCsSetup.cs @@ -181,7 +181,8 @@ namespace Barotrauma servicesProvider.RegisterServiceType(ServiceLifetime.Singleton); servicesProvider.RegisterServiceType(ServiceLifetime.Transient); servicesProvider.RegisterServiceType(ServiceLifetime.Singleton); - + servicesProvider.RegisterServiceType(ServiceLifetime.Transient); + // Extension/Sub Services servicesProvider.RegisterServiceType(ServiceLifetime.Transient); servicesProvider.RegisterServiceType(ServiceLifetime.Transient); diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/_Services/ConfigService.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/_Services/ConfigService.cs index df3ac12d9..d83690172 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/_Services/ConfigService.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/_Services/ConfigService.cs @@ -14,6 +14,7 @@ using Barotrauma.LuaCs.Events; using Barotrauma.LuaCs; using FluentResults; using Microsoft.Toolkit.Diagnostics; +using Microsoft.Xna.Framework; namespace Barotrauma.LuaCs; @@ -71,12 +72,14 @@ public sealed partial class ConfigService : IConfigService _settingsInstances.Clear(); _instanceFactory.Clear(); _settingsInstancesByPackage.Clear(); + _commandsService.Dispose(); _storageService = null; _logger = null; _eventService = null; _configInfoParserService = null; _configProfileInfoParserService = null; + _commandsService = null; } public FluentResults.Result Reset() @@ -136,7 +139,7 @@ public sealed partial class ConfigService : IConfigService private IStorageService _storageService; private ILoggerService _logger; private IEventService _eventService; - private ILuaCsInfoProvider _luaCsInfoProvider; + private IConsoleCommandsService _commandsService; private IParserServiceOneToManyAsync _configInfoParserService; private IParserServiceOneToManyAsync _configProfileInfoParserService; @@ -145,16 +148,143 @@ public sealed partial class ConfigService : IConfigService IParserServiceOneToManyAsync configInfoParserService, IParserServiceOneToManyAsync configProfileInfoParserService, IEventService eventService, - ILuaCsInfoProvider luaCsInfoProvider) + IConsoleCommandsService commandsService) { _logger = logger; _storageService = storageService; _configInfoParserService = configInfoParserService; _configProfileInfoParserService = configProfileInfoParserService; _eventService = eventService; - _luaCsInfoProvider = luaCsInfoProvider; + _commandsService = commandsService; _storageService.UseCaching = true; + InjectCommands(commandsService); + } + + private void InjectCommands(IConsoleCommandsService commandsService) + { + commandsService.RegisterCommand("cfg_getvalue", "cfg_getvalue [Content Package] [InternalName] [ValueString]: gets a config value.", (string[] args) => + { + if (args.Length < 1) + { + _logger.LogError("Please specify the name of the package to set the config."); + return; + } + + if (args.Length < 2) + { + _logger.LogError("Please specify the name of the config."); + return; + } + + var package = ContentPackageManager.RegularPackages.FirstOrDefault(p => p.Name == args[0]); + if (package == null) + { + _logger.LogError($"Could not find the package {args[0]}!"); + return; + } + + string internalName = args[1]; + + if (!TryGetConfig(package, internalName, out ISettingBase setting)) + { + _logger.LogError($"Could not get config with name {internalName}"); + return; + } + + _logger.LogMessage($"config {internalName} value is {setting.GetStringValue()}", Color.Green); + }, getValidArgs: () => new[] + { + ContentPackageManager.RegularPackages.Select(p => p.Name).ToArray() + }); + + commandsService.RegisterCommand("cfg_setvalue", "cfg_setvalue [Content Package] [InternalName] [ValueString]: sets a config.", (string[] args) => + { + if (args.Length < 1) + { + _logger.LogError("Please specify the name of the package to set the config."); + return; + } + + if (args.Length < 2) + { + _logger.LogError("Please specify the name of the config."); + return; + } + + if (args.Length < 3) + { + _logger.LogError("Please specify the value to set the config to."); + return; + } + + var package = ContentPackageManager.RegularPackages.FirstOrDefault(p => p.Name == args[0]); + if (package == null) + { + _logger.LogError($"Could not find the package {args[0]}!"); + return; + } + + string internalName = args[1]; + string valueString = args[2]; + + if (!TryGetConfig(package, internalName, out ISettingBase setting)) + { + _logger.LogError($"Could not get config with name {internalName}"); + return; + } + + if (setting.TrySetValue(valueString)) + { + _logger.LogMessage($"Set config {internalName} value to {valueString}", Color.Green); + if (SaveConfigValue(setting) is { IsFailed: true } res) + { + _logger.LogMessage($"Failed to save new config data to disk. Reasons: {res.ToString()}"); + } + } + else + { + _logger.LogError($"Failed to set config value"); + } + }, getValidArgs: () => new[] + { + ContentPackageManager.RegularPackages.Select(p => p.Name).ToArray() + }); + + commandsService.RegisterCommand("cfg_setprofile", "cfg_setprofile [ContentPackage] [InternalProfileName]", + (string[] args) => + { + if (args.Length < 1 || args[0].IsNullOrWhiteSpace()) + { + _logger.LogError("Please specify the name of the package of the profile."); + return; + } + + if (args.Length < 2 || args[1].IsNullOrWhiteSpace()) + { + _logger.LogError("Please specify the name of the profile."); + return; + } + + var package = ContentPackageManager.RegularPackages.FirstOrDefault(p => p.Name == args[0], null); + if (package == null) + { + _logger.LogError($"Could not find the package {args[0]}!"); + return; + } + + var res = ApplyConfigProfile(package, args[1]); + if (res.IsFailed) + { + _logger.LogError($"Errors while applying profile {args[1]}!"); + _logger.LogResults(res); + return; + } + _logger.Log($"Profile {args[1]} applied successfully!", Color.Green); + }, getValidArgs: () => new[] + { + ContentPackageManager.RegularPackages.Select(p => p.Name).ToArray() + }, false); } @@ -357,7 +487,38 @@ public sealed partial class ConfigService : IConfigService return ret; } - + + public FluentResults.Result ApplyConfigProfile(ContentPackage package, string internalName) + { + Guard.IsNotNull(package, nameof(package)); + Guard.IsNotNullOrWhiteSpace(internalName, nameof(internalName)); + using var _ = _operationLock.AcquireReaderLock().ConfigureAwait(false).GetAwaiter().GetResult(); + IService.CheckDisposed(this); + + if (!_settingsProfiles.TryGetValue((package, internalName), out var setting)) + { + return FluentResults.Result.Fail($"{nameof(ApplyConfigProfile)}: Could not find profile [{package.Name}.{internalName}]"); + } + + var result = new FluentResults.Result(); + + foreach (var profileValue in setting.ProfileValues) + { + if (!_settingsInstances.TryGetValue((package, profileValue.SettingName), out var instance)) + { + result.WithError(new Error($"{nameof(ApplyConfigProfile)}: Could not find setting [{profileValue.SettingName}].")); + continue; + } + + if (!instance.TrySetValue(profileValue.Element)) + { + result.WithError(new Error($"{nameof(ApplyConfigProfile)}: Failed to set value for [{profileValue.SettingName}].")); + } + } + + return result; + } + public FluentResults.Result SaveConfigValue(ISettingBase setting) { XDocument cpCfgValues; diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/_Services/ConsoleCommandsService.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/_Services/ConsoleCommandsService.cs new file mode 100644 index 000000000..a6754ad2a --- /dev/null +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/_Services/ConsoleCommandsService.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Collections.Immutable; + +namespace Barotrauma.LuaCs; + +public class ConsoleCommandsService : IConsoleCommandsService +{ + private readonly ConcurrentDictionary _registeredCommands = new(); + + public void Dispose() + { + if (!ModUtils.Threading.CheckIfClearAndSetBool(ref _isDisposed)) + { + return; + } + foreach (var cmd in _registeredCommands.Values.ToImmutableArray()) + { + DebugConsole.Commands.Remove(cmd); + } + _registeredCommands.Clear(); + } + + private int _isDisposed = 0; + public bool IsDisposed + { + get => ModUtils.Threading.GetBool(ref _isDisposed); + private set => ModUtils.Threading.SetBool(ref _isDisposed, value); + } + public FluentResults.Result RegisterCommand(string name, string help, Action onExecute, Func getValidArgs = null, bool isCheat = false) + { + IService.CheckDisposed(this); + var cmd = new DebugConsole.Command(name, help, onExecute, getValidArgs, isCheat); + if (!_registeredCommands.TryAdd(name, cmd)) + { + return FluentResults.Result.Fail($"{nameof(RegisterCommand)}: A command with the name '{name}' is already added."); + } + DebugConsole.Commands.Add(cmd); + return FluentResults.Result.Ok(); + } + + public void RemoveCommand(string name) + { + IService.CheckDisposed(this); + if (_registeredCommands.TryRemove(name, out DebugConsole.Command cmd)) + { + DebugConsole.Commands.Remove(cmd); + } + } + + public void RemoveRegisteredCommands() + { + IService.CheckDisposed(this); + foreach (var cmd in _registeredCommands.Values.ToImmutableArray()) + { + DebugConsole.Commands.Remove(cmd); + } + _registeredCommands.Clear(); + } +} diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/_Services/LuaScriptManagementService.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/_Services/LuaScriptManagementService.cs index cceb83a13..43a140e1d 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/_Services/LuaScriptManagementService.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/_Services/LuaScriptManagementService.cs @@ -50,6 +50,7 @@ class LuaScriptManagementService : ILuaScriptManagementService, ILuaDataService private readonly IDefaultLuaRegistrar _defaultLuaRegistrar; private readonly IPluginManagementService _pluginManagementService; private readonly INetworkingService _networkingService; + private readonly IConsoleCommandsService _commandsService; //private readonly ILuaCsUtility _luaCsUtility; public LuaScriptManagementService( @@ -64,7 +65,8 @@ class LuaScriptManagementService : ILuaScriptManagementService, ILuaDataService LuaGame luaGame, IEventService eventService, //ILuaCsUtility luaCsUtility, - ILuaCsTimer luaCsTimer + ILuaCsTimer luaCsTimer, + IConsoleCommandsService commandsService ) { _luaScriptLoader = loader; @@ -78,13 +80,33 @@ class LuaScriptManagementService : ILuaScriptManagementService, ILuaDataService _luaGame = luaGame; _eventService = eventService; - //_luaCsNetworking = luaCsNetworking; - //_luaCsUtility = luaCsUtility; + _commandsService = commandsService; _luaCsTimer = luaCsTimer; RegisterLuaEvents(); } + private void RegisterConsoleCommands(IConsoleCommandsService commands) + { + commands.RegisterCommand("cl_reloadlua|cl_reloadcs|cl_reloadluacs", "Re-initializes the LuaCs environment.", (string[] args) => + { + GameMain.LuaCs.EventService.PublishEvent(sub => sub.OnReloadAllPackages()); + }); + + commands.RegisterCommand("cl_toggleluadebug", "Toggles the MoonSharp Debug Server.", (string[] args) => + { + int port = 41912; + + if (args.Length > 0) + { + int.TryParse(args[0], out port); + } + + throw new NotImplementedException(); + //GameMain.LuaCs.ToggleDebugger(port); + }); + } + public bool IsDisposed { get; private set; } public async Task LoadScriptResourcesAsync(ImmutableArray resourcesInfo) @@ -400,6 +422,7 @@ class LuaScriptManagementService : ILuaScriptManagementService, ILuaDataService public FluentResults.Result Reset() { + IService.CheckDisposed(this); _luaScriptLoader.ClearCaches(); _userDataService.Reset(); _luaCsTimer.Reset(); @@ -409,9 +432,10 @@ class LuaScriptManagementService : ILuaScriptManagementService, ILuaDataService public void Dispose() { + IsDisposed = true; _userDataService.Dispose(); _luaScriptLoader.Dispose(); - IsDisposed = true; + _commandsService.Dispose(); } public object? GetGlobalTableValue(string tableName) diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/_Services/_Interfaces/IConfigService.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/_Services/_Interfaces/IConfigService.cs index 7e271c625..83f8a3d43 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/_Services/_Interfaces/IConfigService.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/_Services/_Interfaces/IConfigService.cs @@ -19,6 +19,7 @@ public partial interface IConfigService : IReusableService, ILuaConfigService Task LoadConfigsProfilesAsync(ImmutableArray configProfileResources); FluentResults.Result LoadSavedValueForConfig(ISettingBase setting); FluentResults.Result LoadSavedConfigsValues(); + FluentResults.Result ApplyConfigProfile(ContentPackage package, string internalName); FluentResults.Result SaveConfigValue(ISettingBase setting); FluentResults.Result DisposePackageData(ContentPackage package); FluentResults.Result DisposeAllPackageData(); diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/_Services/_Interfaces/IConsoleCommandsService.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/_Services/_Interfaces/IConsoleCommandsService.cs new file mode 100644 index 000000000..998411b93 --- /dev/null +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/_Services/_Interfaces/IConsoleCommandsService.cs @@ -0,0 +1,11 @@ +using System; + +namespace Barotrauma.LuaCs; + +public interface IConsoleCommandsService : IService +{ + FluentResults.Result RegisterCommand(string name, string help, Action onExecute, Func getValidArgs = null, + bool isCheat = false); + void RemoveCommand(string name); + void RemoveRegisteredCommands(); +}