static async Task ConnectWebsocket() { // Creating a Websocket Connector is as easy as using the right // options object. var connector = new ButtplugWebsocketConnectorOptions( new Uri("ws://localhost:12345/b******g")); var client = new ButtplugClient("Example Client"); await client.ConnectAsync(connector); }
private async void Connect() { connecting = true; var connector = new ButtplugWebsocketConnectorOptions(new Uri(IntifaceAddress)); try { await client.ConnectAsync(connector); await client.StartScanningAsync(); } catch (ButtplugConnectorException e) { Logger.Error(e, "Failed to connect to B******g :("); } finally { connecting = false; } connecting = false; }
public async Task ConnectTask(string aAddress) { var client = new ButtplugClient("VaMSync"); client.DeviceAdded += OnDeviceAdded; client.DeviceRemoved += OnDeviceRemoved; client.ServerDisconnect += OnDisconnect; client.ScanningFinished += OnScanningFinished; try { if (aAddress == null) { await client.ConnectAsync(new ButtplugEmbeddedConnectorOptions()); } else { var connector = new ButtplugWebsocketConnectorOptions(new Uri(aAddress)); await client.ConnectAsync(connector); } _client = client; await Dispatcher.Invoke(async() => { ConnectedHandler?.Invoke(this, new EventArgs()); _connectStatus.Text = $"Connected{(aAddress == null ? ", restart VaMSync to disconnect." : " to Remote B******g Server")}"; OnScanningClick(null, null); _scanningButton.IsEnabled = true; _connectButton.Visibility = Visibility.Collapsed; if (aAddress != null) { _disconnectButton.Visibility = Visibility.Visible; } }); } catch (ButtplugConnectorException ex) { Debug.WriteLine("Connection failed."); // If the exception was thrown after connect, make sure we disconnect. if (_client != null && _client.Connected) { await _client.DisconnectAsync(); _client = null; } Dispatcher.Invoke(() => { _connectStatus.Text = $"Connection failed, please try again."; }); } catch (Exception ex) { Debug.WriteLine($"Did something else fail? {ex})"); // If the exception was thrown after connect, make sure we disconnect. if (_client != null && _client.Connected) { await _client.DisconnectAsync(); _client = null; } Dispatcher.Invoke(() => { _connectStatus.Text = $"Connection failed, please try again."; }); } finally { if (_client == null) { Dispatcher.Invoke(() => { _connectButton.IsEnabled = true; _radioEmbedded.IsEnabled = true; _radioRemote.IsEnabled = true; }); } } }
private async void Connect_Click(object sender, RoutedEventArgs e) { Connect.IsEnabled = false; if (_client != null) { try { await _client.StopScanningAsync(); } catch (Exception) { // no-op: ignore failures, just stop if possible } await _client.DisconnectAsync(); _client = null; var devs = Devices.Keys; foreach (var dev in devs) { Devices.Remove(dev); } ButtplugConnType.IsEnabled = true; ButtplugConnType_SelectionChanged(this, null); Connect.Content = "Connect"; Connect.IsEnabled = true; Scan_Click(this, null); return; } try { switch (((ComboBoxItem)ButtplugConnType.SelectedValue).Content) { case "WebSocket": { ButtplugTarget.IsEnabled = false; ButtplugConnType.IsEnabled = false; ButtplugWebsocketConnectorOptions conn; try { conn = new ButtplugWebsocketConnectorOptions(new Uri(ButtplugTarget.Text)); } catch (UriFormatException e1) { MessageBox.Show($"Uri Error: {e1.Message}", "B******g Error", MessageBoxButton.OK, MessageBoxImage.Error); Connect.IsEnabled = true; ButtplugTarget.IsEnabled = true; ButtplugConnType_SelectionChanged(this, null); return; } _client = new ButtplugClient("NogasmChart"); _client.DeviceAdded += ClientOnDeviceAdded; _client.DeviceRemoved += ClientOnDeviceRemoved; _client.ServerDisconnect += ClientOnServerDisconnect; _client.ErrorReceived += ClientOnErrorReceived; _client.ScanningFinished += ClientOnScanFinished; await _client.ConnectAsync(conn); break; } case "Embedded": { ButtplugTarget.IsEnabled = false; ButtplugConnType.IsEnabled = false; ButtplugEmbeddedConnectorOptions conn = new ButtplugEmbeddedConnectorOptions(); conn.ServerName = "NogasmChart"; _client = new ButtplugClient("NogasmChart"); _client.DeviceAdded += ClientOnDeviceAdded; _client.DeviceRemoved += ClientOnDeviceRemoved; _client.ServerDisconnect += ClientOnServerDisconnect; _client.ErrorReceived += ClientOnErrorReceived; _client.ScanningFinished += ClientOnScanFinished; await _client.ConnectAsync(conn); break; } default: MessageBox.Show("Invalid Connection type!", "B******g Error", MessageBoxButton.OK, MessageBoxImage.Error); Connect.IsEnabled = true; return; } } catch (Exception e1) { MessageBox.Show($"Something went wrong: {e1.Message}", "B******g Error", MessageBoxButton.OK, MessageBoxImage.Error); try { if (_client != null) { await _client.DisconnectAsync(); } } catch (Exception) { // no-op: cleanup only } _client = null; ButtplugConnType.IsEnabled = true; ButtplugConnType_SelectionChanged(this, null); Connect.Content = "Connect"; Connect.IsEnabled = true; Scan_Click(this, null); return; } Connect.Content = "Disconnect"; Connect.IsEnabled = true; try { Scan.IsEnabled = false; await _client.StartScanningAsync(); Scan.Content = "Stop Scanning"; Scan.IsEnabled = true; } catch (Exception e1) { MessageBox.Show($"Something went wrong: {e1.Message}", "B******g Error", MessageBoxButton.OK, MessageBoxImage.Warning); } }
static async Task AwaitExample() { // In CSharp, anything that will block is awaited. For instance, if // we're going to connect to a remote server, that might take some // time due to the network connection quality, or other issues. To // deal with that, we use async/await. // // For now, you can ignore the API calls here, since we're just // talking about how our API works in general. Setting up a // connection is discussed more in the Connecting section of this // document. var connector = new ButtplugWebsocketConnectorOptions( new Uri("ws://localhost:12345/b******g")); var client = new ButtplugClient("Example Client"); // As an example of events, we'll assume the server might send the // client notifications about new devices that it has found. The // client will let us know about this via events. client.DeviceAdded += OnDeviceAdded; // As an example response/reply messages, we'll use our Connect API. // Connecting to a server requires the client and server to send // information back and forth, so we'll await that while those // transfers happen. It is possible for these to be slow, depending // on if network is being used and other factors) // // If something goes wrong, we throw, which breaks out of the await. try { await client.ConnectAsync(connector); } catch (ButtplugConnectorException ex) { Console.WriteLine( "Can't connect to B******g Server, exiting!" + $"Message: {ex.InnerException.Message}"); } catch (ButtplugHandshakeException ex) { Console.WriteLine( "Handshake with B******g Server, exiting!" + $"Message: {ex.InnerException.Message}"); } // There's also no requirement that the tasks returned from these // methods be run immediately. Each method returns a task which will // not run until awaited, so we can store it off and run it later, // run it on the scheduler, etc... // // As a rule, if you don't want to worry about all of the async task // scheduling and what not, you can just use "await" on methods when // you call them and they'll block until return. This is the easiest // way to work sometimes. var startScanningTask = client.StartScanningAsync(); try { await startScanningTask; } catch (ButtplugException ex) { Console.WriteLine( $"Scanning failed: {ex.InnerException.Message}"); } }
// Given a set of Client/Server options, creates a client that either // connects to a server process also started by us, or else to an external // server like Intiface Desktop. // // Throws if server is already running, or if server process fails to start // up, or if client fails to connect for some reason. public static async Task StartProcessAndCreateClient(ButtplugUnityClient client, ButtplugUnityOptions options) { if (options.OutputDebugMessages) { outputDebugMessages = true; } ButtplugUnityHelper.MaybeDebugLog($"Using connection type {options.ConnectorType}."); ButtplugUnityHelper.MaybeDebugLog("Bringing up B******g Server/Client"); // If the server is already up, we can't start it again, but we also can't // hand back a client. Throw. if (ButtplugUnityHelper.serverProcess != null) { ButtplugUnityHelper.MaybeDebugLog("Server already running, throwing"); throw new InvalidOperationException("Server already running."); } var websocketPort = options.WebsocketPort; // We want to start the CLI process without a window, and capture output // from stdout at the moment just to see if it's alive. Crude, but it // does the job of making sure we don't try to connect before the // process spins up. This will change to using the Intiface Protobuf // system in the future. if (options.ConnectorType == ButtplugUnityConnectorType.WebsocketServerProcess) { // If we aren't given a port to use, generate a random one. if (websocketPort == 0) { var rand = new System.Random(); websocketPort = (ushort)rand.Next(10000, 60000); } ButtplugUnityHelper.MaybeDebugLog($"Setting websocket port to {websocketPort}"); var processPath = Path.Combine(Application.streamingAssetsPath, "B******g", "IntifaceCLI.exe"); var arguments = $"--wsinsecureport {websocketPort} --pingtime {options.ServerPingTime}"; if (options.AllowRawMessages) { arguments += " --allowraw"; } try { // Create a new task that will resolve when the server comes up. ButtplugUnityHelper.serverBringupTask = new TaskCompletionSource <bool>(); var serverProcess = new Process(); serverProcess.StartInfo.FileName = processPath; // Don't open a window on starting, and make sure stdout is redirected // so we can catch it for bringup status. serverProcess.StartInfo.CreateNoWindow = true; serverProcess.StartInfo.RedirectStandardOutput = true; serverProcess.StartInfo.UseShellExecute = false; serverProcess.StartInfo.Arguments = arguments; ButtplugUnityHelper.MaybeDebugLog($"Starting task with arguments: {serverProcess.StartInfo.Arguments}"); serverProcess.Exited += ButtplugUnityHelper.OnServerExit; serverProcess.OutputDataReceived += ButtplugUnityHelper.OnServerStart; ButtplugUnityHelper.serverProcess = serverProcess; serverProcess.Start(); serverProcess.BeginOutputReadLine(); ButtplugUnityHelper.MaybeDebugLog("Waiting for task output"); // Wait to get something from the process await ButtplugUnityHelper.serverBringupTask.Task; ButtplugUnityHelper.MaybeDebugLog("Task output received, continuing"); // Reset our bringup task to null now that the process is either up or dead. ButtplugUnityHelper.serverBringupTask = null; if (ButtplugUnityHelper.serverProcess == null) { ButtplugUnityHelper.MaybeDebugLog("Process died before bringup finished."); throw new ApplicationException("ButtplugUnityHelper: Intiface process exited or crashed while coming up."); } } catch (Win32Exception processException) { ButtplugUnityHelper.MaybeDebugLog("Got process exception. If this is IL2CPP, this is expected and Ok. Printing exception and retrying using kernel32 P/Invoke methods."); ButtplugUnityHelper.MaybeDebugLog(processException.ToString()); // This might be a IL2CPP issue, in which case, try to launch the process using those bindings. // // The option here is to hide the window, so we'll flip the context from our option. StartExternalProcess.Start(processPath + " " + arguments, ".", !options.OpenIL2CPPConsoleWindow); } } // For some reason, in Unity 2018/2019 IL2CPP, awaiting our connect call // causes copies internally that end up dropping our sorter, meaning we'll // never get our connection confirmation back. This work everywhere in // Mono, and is fixed in IL2CPP in Unity 2020. // // Why am I doing this work for free. // // Logic tests for "202", meaning this should work back through 2018/2019, // but will futureproof us until Unity 2030 (or until they change their // major versioning scheme again). ButtplugUnityHelper.MaybeDebugLog("Connecting client"); if (options.ConnectorType == ButtplugUnityConnectorType.ExternalWebsocketServer || options.ConnectorType == ButtplugUnityConnectorType.WebsocketServerProcess) { var connector_options = new ButtplugWebsocketConnectorOptions(new Uri($"ws://{options.WebsocketAddress}:{websocketPort}/b******g")); if (Application.unityVersion.Contains("202")) { await client.ConnectAsync(connector_options); } else { client.ConnectAsync(connector_options); await Task.Delay(3000); } } else { var connector_options = new ButtplugEmbeddedConnectorOptions(); connector_options.AllowRawMessages = options.AllowRawMessages; connector_options.MaxPingTime = options.ServerPingTime; if (Application.unityVersion.Contains("202")) { await client.ConnectAsync(connector_options); } else { client.ConnectAsync(connector_options); await Task.Delay(3000); } } ButtplugUnityHelper.MaybeDebugLog("Connected client"); }