private static async Task LogExample()
        {
            // Set up our log handler to print logs to stdout
            ButtplugFFILog.LogMessage += (obj, msg) =>
            {
                Console.WriteLine(msg);
            };
            // Report everything at level Debug and higher, and since we're reporting to the
            // console, don't use JSON output. (JSON output is handy for log parsing later if
            // you need it.)
            ButtplugFFILog.StartLogHandler(ButtplugLogLevel.Debug, false);

            // If you want to change log levels without recompiling, you can use the Env Logger.
            // Just make sure you don't try to use StartLogHandler and ActivateEnvLogger in the
            // same session, they will conflict with each other and throw errors.
            //
            // To set the env logger filter level, you'll need to set the RUST_LOG environment
            // variable. i.e. in powershell: $env:RUST_LOG="debug"
            //
            // Comment the code above this and uncomment this if you want to try the env logger.
            //
            // ButtplugFFILog.ActivateEnvLogger();

            // Completing our embedded connection should cause log messages to print.
            var connector = new ButtplugEmbeddedConnectorOptions();
            var client    = new ButtplugClient("Example Client");
            await client.ConnectAsync(connector);
        }
        private static async Task RunExample()
        {
            // After you've created a connector, the connection looks the same no
            // matter what, though the errors thrown may be different.
            var connector = new ButtplugEmbeddedConnectorOptions();

            // If you'd like to try a remote network connection, comment the
            // connector line above and uncomment the one below. Note that you'll
            // need to turn off SSL on whatever server you're using.

            // var connector = new ButtplugWebsocketConnector(
            //   new Uri("ws://localhost:12345/b******g"));

            var client = new ButtplugClient("Example Client");

            // Now we connect. If anything goes wrong here, we'll either throw
            //
            // - A ButtplugClientConnectionException if there's a problem with
            //   the Connector, like the network address being wrong, server not
            //   being up, etc.
            // - A ButtplugHandshakeException if there is a client/server version
            //   mismatch.
            try
            {
                await client.ConnectAsync(connector);
            }
            catch (ButtplugConnectorException ex)
            {
                // If our connection failed, because the server wasn't turned on,
                // SSL/TLS wasn't turned off, etc, we'll just print and exit
                // here. This will most likely be a wrapped exception.
                Console.WriteLine(
                    $"Can't connect, exiting! Message: {ex.InnerException.Message}");
                await WaitForKey();

                return;
            }
            catch (ButtplugHandshakeException ex)
            {
                // This means our client is newer than our server, and we need to
                // upgrade the server we're connecting to.
                Console.WriteLine(
                    $"Handshake issue, exiting! Message: {ex.InnerException.Message}");
                await WaitForKey();

                return;
            }

            // We're connected, yay!
            Console.WriteLine("Connected! Check Server for Client Name.");

            await WaitForKey();

            // And now we disconnect as usual
            await client.DisconnectAsync();
        }
        private static async Task RunExample()
        {
            // Let's go back to our embedded connector now, to discuss what the
            // lifetime of a connection looks like.
            //
            // We'll create an embedded connector, but this time we're going to
            // include a maximum ping timeout of 100ms. This is a fairly short
            // timer, but since everything is running in our current process, it
            // should be fine.
            var connector = new ButtplugEmbeddedConnectorOptions();

            connector.MaxPingTime = 100;
            var client = new ButtplugClient("Example Client");

            // Just because the Client takes care of sending the ping message for
            // you doesn't mean that a connection is always perfect. It could be
            // that some code you write blocks the thread that the timer is
            // sending on, or sometimes the client's connection to the server can
            // be severed. In these cases, the client has events we can listen to
            // so we know when either we pinged out, or the server was disconnected.
            client.PingTimeout += (aObj, aEventArgs) =>
                                  Console.WriteLine("B******g ping timeout!");
            client.ServerDisconnect += (aObj, aEventArgs) =>
                                       Console.WriteLine("B******g disconnected!");

            // Let's go ahead and connect.
            await client.ConnectAsync(connector);

            Console.WriteLine("Client connected");

            // If you'd like more information on what's going on, uncomment these 2 lines.

            // client.Log += (aObj, aMsg) => Console.WriteLine(aMsg.Message.LogMessage);
            // await client.RequestLogAsync(ButtplugLogLevel.Debug);

            // If we just sit here and wait, the client and server will happily
            // ping each other internally, so we shouldn't see anything printed
            // outside of the "hit key to continue" message. Our wait function is
            // async, so the event loop still spins and the timer stays happy.
            await WaitForKey();

            // Now we'll kill the timer. You should see both a ping timeout and a
            // disconnected message from the event handlers we set up above.
            Console.WriteLine("Stopping ping timer");
            await WaitForKey();

            // At this point we should already be disconnected, so we'll just
            // show ourselves out.
        }
示例#4
0
        private void ConnectEmbedded()
        {
            // First off, we'll set up our Embedded Connector.
            var connector = new ButtplugEmbeddedConnectorOptions();

            // If we want to change anything after making the options object,
            // we can just access the members. We'll explain more about this
            // in a later chapter.
            connector.ServerName = "New Server Name";

            client = new ButtplugClient("Example Client");

            // Connecting using an embedded connection should never fail.
            client.ConnectAsync(connector);
        }
        private static async Task RunExample()
        {
            var connector = new ButtplugEmbeddedConnectorOptions();
            var client    = new ButtplugClient("Example Client");

            try
            {
                await client.ConnectAsync(connector);
            }
            catch (Exception ex)
            {
                Console.WriteLine("Can't connect, exiting!");
                Console.WriteLine($"Message: {ex.InnerException.Message}");
                await WaitForKey();

                return;
            }
            Console.WriteLine("Connected!");
            // You usually shouldn't run Start/Stop scanning back-to-back like
            // this, but with TestDevice we know our device will be found when we
            // call StartScanning, so we can get away with it.
            await client.StartScanningAsync();

            await client.StopScanningAsync();

            Console.WriteLine("Client currently knows about these devices:");
            foreach (var device in client.Devices)
            {
                Console.WriteLine($"- {device.Name}");
            }

            await WaitForKey();

            foreach (var device in client.Devices)
            {
                Console.WriteLine($"{device.Name} supports these messages:");
                foreach (var msgInfo in device.AllowedMessages)
                {
                    Console.WriteLine($"- {msgInfo.Key.ToString()}");
                    if (msgInfo.Value.FeatureCount != 0)
                    {
                        Console.WriteLine($" - Features: {msgInfo.Value.FeatureCount}");
                    }
                }
            }

            Console.WriteLine("Sending commands");

            // Now that we know the message types for our connected device, we
            // can send a message over! Seeing as we want to stick with the
            // modern generic messages, we'll go with VibrateCmd.
            //
            // There's a couple of ways to send this message.
            var testClientDevice = client.Devices[0];


            // We can use the convenience functions on ButtplugClientDevice to
            // send the message. This version sets all of the motors on a
            // vibrating device to the same speed.
            await testClientDevice.SendVibrateCmd(1.0);

            // If we wanted to just set one motor on and the other off, we could
            // try this version that uses an array. It'll throw an exception if
            // the array isn't the same size as the number of motors available as
            // denoted by FeatureCount, though.
            //
            // You can get the vibrator count using the following code, though we
            // know it's 2 so we don't really have to use it.
            //
            // This vibrateType variable is just used to keep us under 80
            // characters for the dev guide, so don't feel that you have to reassign
            // types like this. I'm just trying to make it so you don't have to
            // horizontally scroll in the manual. :)
            var vibrateType   = ServerMessage.Types.MessageAttributeType.VibrateCmd;
            var vibratorCount =
                testClientDevice.AllowedMessages[vibrateType].FeatureCount;
            await testClientDevice.SendVibrateCmd(new[] { 1.0, 0.0 });

            await WaitForKey();

            // And now we disconnect as usual.
            await client.DisconnectAsync();

            // If we try to send a command to a device after the client has
            // disconnected, we'll get an exception thrown.
            try
            {
                await testClientDevice.SendVibrateCmd(1.0);
            }
            catch (ButtplugConnectorException e)
            {
                Console.WriteLine("Tried to send after disconnection! Exception: ");
                Console.WriteLine(e);
            }
            await WaitForKey();
        }
        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);
            }
        }
示例#7
0
        // 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");
        }