using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; using Robust.Shared.Console; using Robust.Shared.IoC; using Robust.Shared.Localization; using Robust.Shared.Utility; namespace Robust.Shared.Configuration { public static class CVarCommandUtil { /// /// Parses a string into an object of the given type. /// /// Thrown if the string could not be parsed into the given type. /// Thrown if the type is not supported. public static object ParseObject(Type type, string input) { if (type == typeof(bool)) { if (bool.TryParse(input, out var val)) return val; if (Parse.TryInt32(input, out var intVal)) { if (intVal == 0) return false; if (intVal == 1) return true; } throw new FormatException($"Could not parse bool value: {input}"); } if (type == typeof(string)) { return input; } if (type == typeof(int)) { return Parse.Int32(input); } if (type == typeof(float)) { return Parse.Float(input); } if (type == typeof(long)) { return long.Parse(input); } if (type == typeof(ushort)) { return ushort.Parse(input); } throw new NotSupportedException(); } internal static IEnumerable GetCVarCompletionOptions(IConfigurationManager cfg) { return cfg.GetRegisteredCVars() .Select(c => new CompletionOption(c, GetCVarValueHint(cfg, c))); } private static string GetCVarValueHint(IConfigurationManager cfg, string cVar) { var flags = cfg.GetCVarFlags(cVar); if ((flags & CVar.CONFIDENTIAL) != 0) return Loc.GetString("cmd-cvar-value-hidden"); var value = cfg.GetCVar(cVar).ToString() ?? ""; if (value.Length > 50) value = $"{value[..51]}…"; return value; } } [SuppressMessage("ReSharper", "StringLiteralTypo")] internal sealed partial class CVarCommand : LocalizedCommands { [Dependency] private IConfigurationManager _cfg = default!; public override string Command => "cvar"; public override void Execute(IConsoleShell shell, string argStr, string[] args) { if (args.Length is < 1 or > 2) { shell.WriteError(Loc.GetString("cmd-cvar-invalid-args")); return; } var name = args[0]; if (name == "?") { var cvars = _cfg.GetRegisteredCVars().OrderBy(c => c); shell.WriteLine(string.Join("\n", cvars)); return; } if (!_cfg.IsCVarRegistered(name)) { shell.WriteError(Loc.GetString("cmd-cvar-not-registered", ("cvar", name))); return; } if (args.Length == 1) { // Read CVar var value = _cfg.GetCVar(name); shell.WriteLine(value.ToString()!); } else { // Write CVar var value = args[1]; var type = _cfg.GetCVarType(name); try { var parsed = CVarCommandUtil.ParseObject(type, value); _cfg.SetCVar(name, parsed); } catch (FormatException) { shell.WriteError(Loc.GetString("cmd-cvar-parse-error", ("type", type))); } } } public override CompletionResult GetCompletion(IConsoleShell shell, string[] args) { if (args.Length == 1) { var helpQuestion = Loc.GetString("cmd-cvar-compl-list"); return CompletionResult.FromHintOptions( CVarCommandUtil.GetCVarCompletionOptions(_cfg) .Union(new[] { new CompletionOption("?", helpQuestion) }) .OrderBy(c => c.Value), Loc.GetString("cmd-cvar-arg-name")); } var cvar = args[0]; if (!_cfg.IsCVarRegistered(cvar)) return CompletionResult.Empty; var type = _cfg.GetCVarType(cvar); return CompletionResult.FromHint($"<{type.Name}>"); } } internal sealed partial class CVarSubsCommand : LocalizedCommands { [Dependency] private IConfigurationManager _cfg = default!; public override string Command => "cvar_subs"; public override void Execute(IConsoleShell shell, string argStr, string[] args) { if (args.Length < 1) { shell.WriteError(Loc.GetString("cmd-cvar_subs-invalid-args")); return; } var name = args[0]; var subs = ((ConfigurationManager)_cfg).GetSubs(name); foreach (var @delegate in subs) { shell.WriteLine(ShowDelegateInfo(@delegate)); } } private static string ShowDelegateInfo(Delegate del) { return $"{del}: {del.Method} -> {del.Target}"; } public override CompletionResult GetCompletion(IConsoleShell shell, string[] args) { if (args.Length == 1) { return CompletionResult.FromHintOptions( _cfg.GetRegisteredCVars() .Select(c => new CompletionOption(c)) .OrderBy(c => c.Value), Loc.GetString("cmd-cvar_subs-arg-name")); } return CompletionResult.Empty; } } internal sealed partial class ConfigMarkRollbackCommand : IConsoleCommand { [Dependency] private IConfigurationManager _cfg = null!; public string Command => "config_rollback_mark"; public string Description => ""; public string Help => ""; public void Execute(IConsoleShell shell, string argStr, string[] args) { if (args.Length is < 1 or > 2) { shell.WriteError(Loc.GetString("cmd-invalid-arg-number-error")); return; } _cfg.MarkForRollback(args[0]); } public CompletionResult GetCompletion(IConsoleShell shell, string[] args) { if (args.Length == 1) { return CompletionResult.FromOptions( CVarCommandUtil.GetCVarCompletionOptions(_cfg) .OrderBy(c => c.Value)); } return CompletionResult.Empty; } } internal sealed partial class ConfigUnmarkRollbackCommand : IConsoleCommand { [Dependency] private IConfigurationManager _cfg = null!; public string Command => "config_rollback_unmark"; public string Description => ""; public string Help => ""; public void Execute(IConsoleShell shell, string argStr, string[] args) { if (args.Length is < 1 or > 2) { shell.WriteError(Loc.GetString("cmd-invalid-arg-number-error")); return; } _cfg.UnmarkForRollback(args[0]); } public CompletionResult GetCompletion(IConsoleShell shell, string[] args) { if (args.Length == 1) { return CompletionResult.FromOptions( CVarCommandUtil.GetCVarCompletionOptions(_cfg) .OrderBy(c => c.Value)); } return CompletionResult.Empty; } } internal sealed partial class ConfigApplyRollbackCommand : IConsoleCommand { [Dependency] private IConfigurationManager _cfg = null!; public string Command => "config_rollback_apply"; public string Description => ""; public string Help => ""; public void Execute(IConsoleShell shell, string argStr, string[] args) { _cfg.ApplyRollback(); } } }