@@ -17,7 +17,7 @@ public class App : IDisposable
1717 private readonly ServerInterface _server ;
1818 private bool _clientReady = false ;
1919 private Config _conf ;
20- private RecoilProfile _currentProfile ;
20+ private List < RecoilProfile > _currentProfile = new List < RecoilProfile > { null , null } ;
2121
2222 public App ( ILogger logger )
2323 {
@@ -33,11 +33,20 @@ public void Dispose()
3333 foreach ( var memlib in _memReaders . Values ) memlib . CloseProcess ( ) ;
3434 }
3535
36- public void ChangeProfile ( RecoilProfile rp )
36+ public void ChangeProfile ( int playerIndex , RecoilProfile rp )
3737 {
38- _server . SendMessage ( MessageBuilder . Build ( "profile" , rp ) . AsMessage ( ) ) ;
39- if ( _conf . Global . RecoilOnSwitch ) _server . SendMessage ( MessageBuilder . Build ( "recoil" , null ) . AsMessage ( ) ) ;
40- _currentProfile = rp ;
38+ var rpw = new RecoilProfileWrapper ( )
39+ {
40+ RecoilProfile = rp ,
41+ Player = playerIndex
42+ } ;
43+ _server . SendMessage ( MessageBuilder . Build ( "profile" , rpw ) . AsMessage ( ) ) ;
44+ if ( _conf . Global . RecoilOnSwitch )
45+ _server . SendMessage ( MessageBuilder . Build ( "recoil" , playerIndex ) . AsMessage ( ) ) ;
46+ if ( playerIndex == - 1 )
47+ _currentProfile = new List < RecoilProfile > { rp , rp } ;
48+ else
49+ _currentProfile [ playerIndex ] = rp ;
4150 }
4251
4352 public void InjectionNotification ( )
@@ -106,54 +115,75 @@ public void WindowEventHandler(IntPtr hWinEventHook, uint eventType, IntPtr hwnd
106115 return ;
107116 }
108117
109- dynamic value ;
110- string profName = null ;
111- switch ( matchedGp . Memscan . Type )
118+ var idx = 0 ;
119+ foreach ( var path in matchedGp . Memscan . Paths )
112120 {
113- case "byte" :
114- value = memlib . ReadByte ( matchedGp . Memscan . Code ) ;
115- matchedGp . Memscan . Match . TryGetValue ( value , out profName ) ;
116- break ;
117- case "short" :
118- value = memlib . Read2Byte ( matchedGp . Memscan . Code ) ;
119- matchedGp . Memscan . Match . TryGetValue ( value , out profName ) ;
120- break ;
121- case "int" :
122- value = memlib . ReadInt ( matchedGp . Memscan . Code ) ;
123- matchedGp . Memscan . Match . TryGetValue ( value , out profName ) ;
124- break ;
125- case "uint" :
126- value = memlib . ReadUInt ( matchedGp . Memscan . Code ) ;
127- matchedGp . Memscan . Match . TryGetValue ( value , out profName ) ;
128- break ;
129- default :
130- _logger . Error ( "Unsupported memory scan type: {@Type}" , matchedGp . Memscan . Type ) ;
131- return ;
132- }
121+ dynamic value ;
122+ string profName = null ;
123+ try
124+ {
125+ switch ( matchedGp . Memscan . Type )
126+ {
127+ case "byte" :
128+ value = memlib . ReadByte ( path ) ;
129+ matchedGp . Memscan . Match . TryGetValue ( value , out profName ) ;
130+ break ;
131+ case "short" :
132+ value = memlib . Read2Byte ( path ) ;
133+ matchedGp . Memscan . Match . TryGetValue ( value , out profName ) ;
134+ break ;
135+ case "int" :
136+ value = memlib . ReadInt ( path ) ;
137+ matchedGp . Memscan . Match . TryGetValue ( value , out profName ) ;
138+ break ;
139+ case "uint" :
140+ value = memlib . ReadUInt ( path ) ;
141+ matchedGp . Memscan . Match . TryGetValue ( value , out profName ) ;
142+ break ;
143+ default :
144+ _logger . Error ( "Unsupported memory scan type: {@Type}" ,
145+ matchedGp . Memscan . Type ) ;
146+ return ;
147+ }
148+ }
149+ catch ( Exception e )
150+ {
151+ _logger . Error ( e ,
152+ "[P{@Index}] Exception while reading memory. The application may still be initializing or the pointer path is incorrect: {@Exception}" ,
153+ idx + 1 , e ) ;
154+ continue ;
155+ }
133156
134- if ( ! string . IsNullOrEmpty ( profName ) )
135- {
136- matchedRp = _conf . RecoilProfiles . FirstOrDefault ( p => p . Name == profName ) ;
137- if ( matchedRp == null )
157+ _logger . Debug ( "[P{@Index}] Memory scan result: {@Value} -> {@Profile}" , idx + 1 , value ,
158+ profName ) ;
159+ if ( ! string . IsNullOrEmpty ( profName ) )
160+ {
161+ matchedRp = _conf . RecoilProfiles . FirstOrDefault ( p => p . Name == profName ) ;
162+ if ( matchedRp == null )
163+ {
164+ _logger . Error (
165+ "[{@Game}][MEM][Player {@Index}] {@Value} -> {@Profile} not found. Check your configuration." ,
166+ matchedGp . Name , idx + 1 , value , profName ) ;
167+ continue ;
168+ }
169+ }
170+ else
138171 {
139172 _logger . Error (
140- "[{@Game}][MEM] {@Value} -> {@Profile} not found. Check your configuration." ,
141- matchedGp . Name , value , profName ) ;
173+ "[{@Game}][MEM][Player {@Index}] {@ Value} -> No profile found. Check your configuration." ,
174+ matchedGp . Name , idx + 1 , value ) ;
142175 continue ;
143176 }
144- }
145- else
146- {
147- _logger . Error ( "[{@Game}][MEM] {@Value} -> No profile found. Check your configuration." ,
148- matchedGp . Name , value ) ;
149- continue ;
150- }
151177
152- if ( matchedRp != _currentProfile )
153- {
154- _logger . Information ( "[{@Game}][MEM] {@Value} -> {@Profile}" , matchedGp . Name , value ,
155- matchedRp . Name ) ;
156- ChangeProfile ( matchedRp ) ;
178+ if ( matchedRp != _currentProfile [ idx ] )
179+ {
180+ _logger . Information ( "[{@Game}][MEM][Player {@Index}] {@Value} -> {@Profile}" ,
181+ matchedGp . Name , idx + 1 , value ,
182+ matchedRp . Name ) ;
183+ ChangeProfile ( idx , matchedRp ) ;
184+ }
185+
186+ idx ++ ;
157187 }
158188
159189
@@ -171,10 +201,10 @@ public void WindowEventHandler(IntPtr hWinEventHook, uint eventType, IntPtr hwnd
171201 return ;
172202 }
173203
174- if ( matchedRp != _currentProfile )
204+ if ( matchedRp != _currentProfile [ 0 ] )
175205 {
176206 _logger . Information ( "[{@Game}] {@Profile}" , matchedGp . Name , matchedRp . Name ) ;
177- ChangeProfile ( matchedRp ) ;
207+ ChangeProfile ( - 1 , matchedRp ) ;
178208 }
179209 }
180210 }
@@ -192,22 +222,28 @@ select MessageBuilder.FromMessage(msg))
192222 _clientReady = true ;
193223 break ;
194224 case "recoilack" :
195- if ( ! ( bool ) e . Payload )
196- _logger . Error ( $ "Failed to recoil") ;
225+ var recoilResp = RecoilResponse . FromString ( e . Payload . ToString ( ) ) ;
226+ if ( ! recoilResp . Success )
227+ _logger . Error ( "Failed to recoil: {@Reason}" , recoilResp . Reason ) ;
197228 else
198- _logger . Information ( $ "Recoil ACK - Success ") ;
229+ _logger . Information ( $ "Recoil success ") ;
199230 break ;
200231 case "profileack" :
201- var suc = ( bool ) e . Payload ;
202- if ( ! suc )
203- {
204- _logger . Error ( $ "Failed to apply profile, Sinden may still be initializing - will retry") ;
205- _currentProfile = null ;
206- }
232+ var resp = RecoilProfileResponse . FromString ( e . Payload . ToString ( ) ) ;
233+
234+ if ( ! resp . Success )
235+ switch ( resp . Reason )
236+ {
237+ case "No lightgun at requested index." :
238+ _logger . Error (
239+ $ "Failed to apply profile -> No lightgun detected at requested player index") ;
240+ break ;
241+ default :
242+ _logger . Error ( "Failed to apply profile: {@Reason}" , resp . Reason ) ;
243+ break ;
244+ }
207245 else
208- {
209- _logger . Information ( "Successfully applied profile. {@Profile}" , _currentProfile ) ;
210- }
246+ _logger . Information ( "Successfully applied profiles. {@Profile}" , _currentProfile ) ;
211247
212248 break ;
213249 }
@@ -223,11 +259,7 @@ internal class Program
223259 [ DllImport ( "user32.dll" ) ]
224260 private static extern IntPtr SetWinEventHook ( uint eventMin , uint eventMax , IntPtr hmodWinEventProc ,
225261 WinEventDelegate lpfnWinEventProc , uint idProcess , uint idThread , uint dwFlags ) ;
226- [ DllImport ( "kernel32.dll" , SetLastError = true ) ]
227- [ return : MarshalAs ( UnmanagedType . Bool ) ]
228- static extern bool AllocConsole ( ) ;
229- [ System . Runtime . InteropServices . DllImport ( "kernel32.dll" ) ]
230- private static extern bool AttachConsole ( int dwProcessId ) ;
262+
231263
232264 [ STAThread ]
233265 private static void Main ( string [ ] args )
@@ -236,13 +268,13 @@ private static void Main(string[] args)
236268 try
237269 {
238270 conf = Config . GetInstance ( ) ;
239-
240271 }
241272 catch ( Exception e )
242273 {
243274 // Crash outright if we don't have a working config
244275 throw new InvalidOperationException ( $ "Failed to read configuration: { e } ") ;
245276 }
277+
246278 var mainForm = new AppForm ( conf ) ;
247279 var logger = Logger . CreateDesktopLogger ( conf . Global . Debug , mainForm . WpfRichTextBox ) ;
248280 Config . Logger = logger ;
0 commit comments