1- using System ;
2- using System . IO ;
3- using System . Threading ;
4- using System . Threading . Tasks ;
5- using Buttplug . Core . Logging ;
1+ using Buttplug . Core . Logging ;
62using Buttplug . Devices . Configuration ;
73using Buttplug . Server ;
8- using Buttplug . Server . Connectors . WebsocketServer ;
94using Buttplug . Server . Connectors ;
5+ using Buttplug . Server . Connectors . WebsocketServer ;
106using Google . Protobuf ;
7+ using System ;
8+ using System . IO ;
9+ using System . Threading ;
10+ using System . Threading . Tasks ;
1111
1212namespace IntifaceCLI
1313{
14- class ServerCLI
14+ internal class ServerCLI
1515 {
1616 private bool _useProtobufOutput ;
17- public bool ServerReady { get ; }
1817 private DeviceManager _deviceManager ;
1918 private readonly Stream _stdout = Console . OpenStandardOutput ( ) ;
20- private TaskCompletionSource < bool > _exitWait = new TaskCompletionSource < bool > ( ) ;
21- private CancellationTokenSource _stdinTokenSource = new CancellationTokenSource ( ) ;
19+ private TaskCompletionSource < bool > _disconnectWait = new TaskCompletionSource < bool > ( ) ;
20+ private readonly CancellationTokenSource _stdinTokenSource = new CancellationTokenSource ( ) ;
2221 private Task _stdioTask ;
23-
22+ private bool _shouldExit ;
2423
2524 // Simple server that exposes device manager, since we'll need to chain device managers
2625 // through it for this. This is required because Windows 10 has problems disconnecting from
2726 // BLE devices without completely stopping and restarting processes. :(
28- class CLIServer : ButtplugServer
27+ private class CLIServer : ButtplugServer
2928 {
3029 public DeviceManager DeviceManager => _deviceManager ;
3130 public IButtplugLogManager LogManager => BpLogManager ;
@@ -36,32 +35,37 @@ public CLIServer(string aServerName, uint aMaxPingTime, DeviceManager aDevMgr)
3635 }
3736 }
3837
39-
4038 public ServerCLI ( )
4139 {
42-
40+ Console . CancelKeyPress += ( aObj , aEvent ) =>
41+ {
42+ PrintProcessLog ( "Console exit called." ) ;
43+ _shouldExit = true ;
44+ _disconnectWait . SetResult ( true ) ;
45+ } ;
4346 }
4447
4548 private void ReadStdio ( )
4649 {
47- using ( Stream stdin = Console . OpenStandardInput ( ) )
50+ using ( var stdin = Console . OpenStandardInput ( ) )
4851 {
4952 // Largest message we can receive is 1mb, so just allocate that now.
5053 var buf = new byte [ 1024768 ] ;
5154
52- while ( ! _exitWait . Task . IsCompleted )
55+ while ( ! _disconnectWait . Task . IsCompleted && ! _shouldExit )
5356 {
5457 try
5558 {
5659 var msg = ServerControlMessage . Parser . ParseDelimitedFrom ( stdin ) ;
5760
58- if ( msg == null || msg . Stop == null )
61+ if ( msg ? . Stop == null && ! _shouldExit )
5962 {
6063 continue ;
6164 }
6265
6366 _stdinTokenSource . Cancel ( ) ;
64- _exitWait ? . SetResult ( true ) ;
67+ _disconnectWait ? . SetResult ( true ) ;
68+ _shouldExit = true ;
6569 break ;
6670 }
6771 catch ( InvalidProtocolBufferException )
@@ -97,15 +101,13 @@ private void PrintProcessLog(string aLogMsg)
97101
98102 public void RunServer ( Options aOptions )
99103 {
100- Console . CancelKeyPress += ( obj , ev ) => { _exitWait . SetResult ( true ) ; } ;
101-
102104 if ( aOptions . Version )
103105 {
104106 Console . WriteLine ( "1" ) ;
105107 return ;
106108 }
107109
108- _useProtobufOutput = aOptions . GuiPipe ;
110+ _useProtobufOutput = aOptions . FrontendPipe ;
109111 if ( _useProtobufOutput )
110112 {
111113 _stdioTask = new Task ( ReadStdio ) ;
@@ -133,13 +135,7 @@ public void RunServer(Options aOptions)
133135 DeviceConfigurationManager . Manager . LoadUserConfigurationFile ( aOptions . UserDeviceConfigFile ) ;
134136 }
135137
136- if ( aOptions . UseWebsocketServer && aOptions . UseIpcServer )
137- {
138- PrintProcessLog ( "ERROR: Can't run websocket server and IPC server at the same time!" ) ;
139- return ;
140- }
141-
142- if ( ! aOptions . UseWebsocketServer && ! aOptions . UseIpcServer )
138+ if ( aOptions . WebsocketServerInsecurePort == 0 && aOptions . WebsocketServerSecurePort == 0 && ! aOptions . UseIpcServer )
143139 {
144140 PrintProcessLog ( "ERROR: Must specify either IPC server or Websocket server!" ) ;
145141 return ;
@@ -158,6 +154,8 @@ public void RunServer(Options aOptions)
158154 ButtplugServer ServerFactory ( )
159155 {
160156 var server = new CLIServer ( aOptions . ServerName , ( uint ) aOptions . PingTime , _deviceManager ) ;
157+
158+ // Pull out the device manager for reuse later.
161159 if ( _deviceManager == null )
162160 {
163161 _deviceManager = server . DeviceManager ;
@@ -171,44 +169,103 @@ ButtplugServer ServerFactory()
171169 } ) ;
172170 }
173171
172+ server . ClientConnected += ( aObj , aEvent ) =>
173+ {
174+ if ( _useProtobufOutput )
175+ {
176+ SendProcessMessage ( new ServerProcessMessage
177+ {
178+ ClientConnected = new ServerProcessMessage . Types . ClientConnected
179+ {
180+ ClientName = server . ClientName
181+ }
182+ } ) ;
183+ }
184+ else
185+ {
186+ Console . WriteLine ( $ "Client connected: { server . ClientName } ") ;
187+ }
188+ } ;
189+
174190 return server ;
175191 }
176192
177- var ipcServer = new ButtplugIPCServer ( ) ;
178- var insecureWebsocketServer = new ButtplugWebsocketServer ( ) ;
179- var secureWebsocketServer = new ButtplugWebsocketServer ( ) ;
193+ ButtplugIPCServer ipcServer = null ;
194+ ButtplugWebsocketServer insecureWebsocketServer = null ;
195+ ButtplugWebsocketServer secureWebsocketServer = null ;
180196
181- if ( aOptions . UseWebsocketServer )
197+ if ( aOptions . WebsocketServerInsecurePort != 0 )
182198 {
183- if ( aOptions . WebsocketServerInsecurePort != 0 )
184- {
185- insecureWebsocketServer . StartServerAsync ( ServerFactory , 1 , aOptions . WebsocketServerInsecurePort , ! aOptions . WebsocketServerAllInterfaces ) . Wait ( ) ;
186- insecureWebsocketServer . ConnectionClosed += ( aSender , aArgs ) => { _exitWait . SetResult ( true ) ; } ;
187- PrintProcessLog ( "Insecure websocket Server now running..." ) ;
188- }
189- if ( aOptions . WebsocketServerSecurePort != 0 && aOptions . CertFile != null && aOptions . PrivFile != null )
190- {
191- secureWebsocketServer . StartServerAsync ( ServerFactory , 1 , aOptions . WebsocketServerSecurePort , ! aOptions . WebsocketServerAllInterfaces , aOptions . CertFile , aOptions . PrivFile ) . Wait ( ) ;
192- secureWebsocketServer . ConnectionClosed += ( aSender , aArgs ) => { _exitWait . SetResult ( true ) ; } ;
193- PrintProcessLog ( "Secure websocket Server now running..." ) ;
194- }
199+ insecureWebsocketServer = new ButtplugWebsocketServer ( ) ;
200+ insecureWebsocketServer . StartServerAsync ( ServerFactory , 1 , aOptions . WebsocketServerInsecurePort ,
201+ ! aOptions . WebsocketServerAllInterfaces ) . Wait ( ) ;
202+ insecureWebsocketServer . ConnectionClosed += ( aSender , aArgs ) => { _disconnectWait . SetResult ( true ) ; } ;
203+ PrintProcessLog ( "Insecure websocket Server now running..." ) ;
195204 }
196- else if ( aOptions . UseIpcServer )
205+
206+ if ( aOptions . WebsocketServerSecurePort != 0 && aOptions . CertFile != null &&
207+ aOptions . PrivFile != null )
197208 {
209+ secureWebsocketServer = new ButtplugWebsocketServer ( ) ;
210+ secureWebsocketServer . StartServerAsync ( ServerFactory , 1 , aOptions . WebsocketServerSecurePort ,
211+ ! aOptions . WebsocketServerAllInterfaces , aOptions . CertFile , aOptions . PrivFile ) . Wait ( ) ;
212+ secureWebsocketServer . ConnectionClosed += ( aSender , aArgs ) => { _disconnectWait . SetResult ( true ) ; } ;
213+ PrintProcessLog ( "Secure websocket Server now running..." ) ;
214+ }
215+
216+ if ( aOptions . UseIpcServer )
217+ {
218+ ipcServer = new ButtplugIPCServer ( ) ;
198219 ipcServer . StartServer ( ServerFactory , aOptions . IpcPipe ) ;
199- ipcServer . ConnectionClosed += ( aSender , aArgs ) => { _exitWait . SetResult ( true ) ; } ;
220+ ipcServer . ConnectionClosed += ( aSender , aArgs ) => { _disconnectWait . SetResult ( true ) ; } ;
200221 PrintProcessLog ( "IPC Server now running..." ) ;
201222 }
202223
203- // Now that all server possibilities are up and running, if we have
204- // a pipe, let the parent program know we've started.
224+ // Now that all server possibilities are up and running, if we have a pipe, let the
225+ // parent program know we've started.
205226 if ( _useProtobufOutput )
206227 {
207- var msg = new ServerProcessMessage { ProcessStarted = new ServerProcessMessage . Types . ProcessStarted ( ) } ;
228+ var msg = new ServerProcessMessage
229+ { ProcessStarted = new ServerProcessMessage . Types . ProcessStarted ( ) } ;
208230 SendProcessMessage ( msg ) ;
209231 }
232+ else
233+ {
234+ Console . WriteLine ( "Server started, waiting for client connection." ) ;
235+ }
236+
237+ do
238+ {
239+ _disconnectWait . Task . Wait ( ) ;
240+
241+ if ( ipcServer != null && ipcServer . Connected )
242+ {
243+ ipcServer ? . Disconnect ( ) ;
244+ }
245+
246+ if ( insecureWebsocketServer != null && insecureWebsocketServer . Connected )
247+ {
248+ insecureWebsocketServer ? . DisconnectAsync ( ) . Wait ( ) ;
249+ }
250+
251+ if ( secureWebsocketServer != null && secureWebsocketServer . Connected )
252+ {
253+ secureWebsocketServer ? . DisconnectAsync ( ) . Wait ( ) ;
254+ }
255+
256+ if ( _useProtobufOutput )
257+ {
258+ var msg = new ServerProcessMessage
259+ { ClientDisconnected = new ServerProcessMessage . Types . ClientDisconnected ( ) } ;
260+ SendProcessMessage ( msg ) ;
261+ }
262+ else
263+ {
264+ Console . WriteLine ( "Client disconnected." ) ;
265+ }
266+ _disconnectWait = new TaskCompletionSource < bool > ( ) ;
267+ } while ( aOptions . StayOpen && ! _shouldExit ) ;
210268
211- _exitWait . Task . Wait ( ) ;
212269 if ( ! _useProtobufOutput )
213270 {
214271 return ;
@@ -218,10 +275,13 @@ ButtplugServer ServerFactory()
218275
219276 if ( _useProtobufOutput )
220277 {
221- var exitMsg = new ServerProcessMessage ( ) ;
222- exitMsg . ProcessEnded = new ServerProcessMessage . Types . ProcessEnded ( ) ;
278+ var exitMsg = new ServerProcessMessage
279+ {
280+ ProcessEnded = new ServerProcessMessage . Types . ProcessEnded ( )
281+ } ;
223282 SendProcessMessage ( exitMsg ) ;
224283 }
284+
225285 _stdinTokenSource . Cancel ( ) ;
226286 }
227287 }
0 commit comments