11using MegaCrit . Sts2 . Core . DevConsole ;
22using MegaCrit . Sts2 . Core . DevConsole . ConsoleCommands ;
33using MegaCrit . Sts2 . Core . Entities . Players ;
4+ using STS2RitsuLib . Settings ;
45
56namespace STS2RitsuLib . Diagnostics . Commands
67{
@@ -10,17 +11,19 @@ namespace STS2RitsuLib.Diagnostics.Commands
1011 /// </summary>
1112 public sealed class RitsuLibConsoleCmd : AbstractConsoleCmd
1213 {
13- private static readonly string [ ] RootCommands = [ "selfcheck" ] ;
14+ private static readonly string [ ] RootCommands = [ "selfcheck" , "settings" ] ;
1415 private static readonly string [ ] SelfCheckActions = [ "run" , "open-output" ] ;
16+ private static readonly string [ ] SettingsActions = [ "open" ] ;
1517
1618 /// <inheritdoc />
1719 public override string CmdName => "ritsulib" ;
1820
1921 /// <inheritdoc />
20- public override string Args => "selfcheck run|open-output" ;
22+ public override string Args =>
23+ "selfcheck run|open-output OR settings open <modId> [pageId] [sectionId] [entryId]" ;
2124
2225 /// <inheritdoc />
23- public override string Description => "RitsuLib tools: selfcheck run/open-output." ;
26+ public override string Description => "RitsuLib tools: selfcheck run/open-output; settings open ." ;
2427
2528 /// <inheritdoc />
2629 public override bool IsNetworked => false ;
@@ -34,6 +37,9 @@ public override CompletionResult GetArgumentCompletions(Player? player, string[]
3437 return CompleteArgument ( RootCommands , [ ] , partial , CompletionType . Subcommand ) ;
3538 }
3639
40+ if ( args [ 0 ] . Equals ( "settings" , StringComparison . OrdinalIgnoreCase ) )
41+ return CompleteSettingsArguments ( args ) ;
42+
3743 if ( ! args [ 0 ] . Equals ( "selfcheck" , StringComparison . OrdinalIgnoreCase ) )
3844 return base . GetArgumentCompletions ( player , args ) ;
3945 {
@@ -46,8 +52,11 @@ public override CompletionResult GetArgumentCompletions(Player? player, string[]
4652 /// <inheritdoc />
4753 public override CmdResult Process ( Player ? issuingPlayer , string [ ] args )
4854 {
55+ if ( args . Length >= 1 && args [ 0 ] . Equals ( "settings" , StringComparison . OrdinalIgnoreCase ) )
56+ return ProcessSettings ( args ) ;
57+
4958 if ( args . Length < 2 || ! args [ 0 ] . Equals ( "selfcheck" , StringComparison . OrdinalIgnoreCase ) )
50- return new ( false , "Usage: ritsulib selfcheck run|open-output" ) ;
59+ return new ( false , UsageText ( ) ) ;
5160
5261 if ( args [ 1 ] . Equals ( "run" , StringComparison . OrdinalIgnoreCase ) )
5362 {
@@ -56,9 +65,144 @@ public override CmdResult Process(Player? issuingPlayer, string[] args)
5665 }
5766
5867 if ( ! args [ 1 ] . Equals ( "open-output" , StringComparison . OrdinalIgnoreCase ) )
59- return new ( false , "Usage: ritsulib selfcheck run|open-output" ) ;
68+ return new ( false , UsageText ( ) ) ;
6069 SelfCheckBundleCoordinator . TryOpenOutputFolderFromSettings ( ) ;
6170 return new ( true , "Requested to open RitsuLib self-check output folder." ) ;
6271 }
72+
73+ private static CmdResult ProcessSettings ( string [ ] args )
74+ {
75+ if ( args . Length < 3 || args . Length > 6 || ! args [ 1 ] . Equals ( "open" , StringComparison . OrdinalIgnoreCase ) )
76+ return new ( false , UsageText ( ) ) ;
77+
78+ var result = ModSettingsNavigator . RequestOpenByIds (
79+ args [ 2 ] ,
80+ GetOptionalArg ( args , 3 ) ,
81+ GetOptionalArg ( args , 4 ) ,
82+ GetOptionalArg ( args , 5 ) ) ;
83+ return new ( result . Success , result . Message ) ;
84+ }
85+
86+ private CompletionResult CompleteSettingsArguments ( string [ ] args )
87+ {
88+ var partial = args [ ^ 1 ] ;
89+ var completed = args . Take ( args . Length - 1 ) . ToArray ( ) ;
90+ if ( args . Length <= 2 )
91+ return CompleteArgument ( SettingsActions , completed , partial , CompletionType . Subcommand ) ;
92+
93+ if ( ! args [ 1 ] . Equals ( "open" , StringComparison . OrdinalIgnoreCase ) )
94+ return base . GetArgumentCompletions ( null , args ) ;
95+
96+ return args . Length switch
97+ {
98+ 3 => CompleteArgument ( GetModIdCandidates ( ) , completed , partial ) ,
99+ 4 => CompleteArgument ( GetPageIdCandidates ( args [ 2 ] ) , completed , partial ) ,
100+ 5 => CompleteArgument ( GetSectionIdCandidates ( args [ 2 ] , args [ 3 ] ) , completed , partial ) ,
101+ 6 => CompleteArgument ( GetEntryIdCandidates ( args [ 2 ] , args [ 3 ] , args [ 4 ] ) , completed , partial ) ,
102+ _ => base . GetArgumentCompletions ( null , args ) ,
103+ } ;
104+ }
105+
106+ private static string ? GetOptionalArg ( string [ ] args , int index )
107+ {
108+ return args . Length <= index || string . IsNullOrWhiteSpace ( args [ index ] ) ? null : args [ index ] ;
109+ }
110+
111+ private static string UsageText ( )
112+ {
113+ return
114+ "Usage: ritsulib selfcheck run|open-output OR ritsulib settings open <modId> [pageId] [sectionId] [entryId]" ;
115+ }
116+
117+ private static string [ ] GetModIdCandidates ( )
118+ {
119+ RefreshSettingsPagesForCompletion ( ) ;
120+ return ModSettingsRegistry . GetPages ( )
121+ . Where ( IsPageVisible )
122+ . Select ( static page => page . ModId )
123+ . Distinct ( StringComparer . OrdinalIgnoreCase )
124+ . Order ( StringComparer . OrdinalIgnoreCase )
125+ . ToArray ( ) ;
126+ }
127+
128+ private static string [ ] GetPageIdCandidates ( string modId )
129+ {
130+ RefreshSettingsPagesForCompletion ( ) ;
131+ return ModSettingsRegistry . GetPages ( )
132+ . Where ( page => string . Equals ( page . ModId , modId , StringComparison . OrdinalIgnoreCase ) )
133+ . Where ( IsPageVisible )
134+ . Select ( static page => page . Id )
135+ . Order ( StringComparer . OrdinalIgnoreCase )
136+ . ToArray ( ) ;
137+ }
138+
139+ private static string [ ] GetSectionIdCandidates ( string modId , string pageId )
140+ {
141+ RefreshSettingsPagesForCompletion ( ) ;
142+ return ModSettingsRegistry . TryGetPage ( modId , pageId , out var page ) && page != null && IsPageVisible ( page )
143+ ? page . Sections . Where ( IsSectionVisible ) . Select ( static section => section . Id )
144+ . Order ( StringComparer . OrdinalIgnoreCase ) . ToArray ( )
145+ : [ ] ;
146+ }
147+
148+ private static string [ ] GetEntryIdCandidates ( string modId , string pageId , string sectionId )
149+ {
150+ RefreshSettingsPagesForCompletion ( ) ;
151+ if ( ! ModSettingsRegistry . TryGetPage ( modId , pageId , out var page ) || page == null || ! IsPageVisible ( page ) )
152+ return [ ] ;
153+
154+ var section = page . Sections . FirstOrDefault ( s => string . Equals ( s . Id , sectionId ,
155+ StringComparison . OrdinalIgnoreCase ) ) ;
156+ return section == null || ! IsSectionVisible ( section )
157+ ? [ ]
158+ : section . Entries . Where ( IsEntryVisible ) . Select ( static entry => entry . Id )
159+ . Order ( StringComparer . OrdinalIgnoreCase ) . ToArray ( ) ;
160+ }
161+
162+ private static void RefreshSettingsPagesForCompletion ( )
163+ {
164+ try
165+ {
166+ RitsuLibModSettingsBootstrap . EnsureFrameworkPagesRegistered ( ) ;
167+ ModSettingsMirrorRegistrarBootstrap . TryRegisterMirroredPages ( ) ;
168+ RitsuLibModSettingsBootstrap . RefreshDynamicPages ( ) ;
169+ }
170+ catch
171+ {
172+ // Completion is best-effort; command execution reports concrete failures.
173+ }
174+ }
175+
176+ private static bool IsPageVisible ( ModSettingsPage page )
177+ {
178+ return ModSettingsHostSurfaceResolver . IsVisibleOnCurrentHost ( page . VisibleOnHostSurfaces ) &&
179+ SafePredicate ( page . VisibleWhen ) ;
180+ }
181+
182+ private static bool IsSectionVisible ( ModSettingsSection section )
183+ {
184+ return ModSettingsHostSurfaceResolver . IsVisibleOnCurrentHost ( section . VisibleOnHostSurfaces ) &&
185+ SafePredicate ( section . VisibleWhen ) ;
186+ }
187+
188+ private static bool IsEntryVisible ( ModSettingsEntryDefinition entry )
189+ {
190+ return SafePredicate ( entry . VisibilityPredicate ) ;
191+ }
192+
193+ private static bool SafePredicate ( Func < bool > ? predicate )
194+ {
195+ if ( predicate == null )
196+ return true ;
197+
198+ try
199+ {
200+ return predicate ( ) ;
201+ }
202+ catch
203+ {
204+ return true ;
205+ }
206+ }
63207 }
64208}
0 commit comments