Choosing a Connector

The first thing to do when writing a Buttplug application is figuring out where the server is going to live.

  • Inside the Application (aka Embedded)
    • You'll bundle a buttplug server with your application. Easier to distribute and create UI for, but also means if the Buttplug libraries change or upgrade, you may need to do the same. This can also tie your application to a single platform (which may or may not be an issue), if you redistribute libraries that only run on a single platform. See the README for the Buttplug implementation you're using for more info on this.
  • Outside the Application
    • You'll allow the user to connect to a Buttplug server they're running on their system, outside of your application (this usually means the user will be running Intiface Desktop (opens new window)). This usually means you'll connect via some IPC method, like pipes or websockets. Buttplug Client Connector libraries usually implement the system specific transports for you, so it's just a matter of presenting the user with a UI that lets them connect as they need.
  • Both!
    • Bundling a server with your application and allowing users to connect to an outside server is the most complete option, though also the most complicated. Buttplug libraries are usually distributed with Client and Server capabilities, so you can embed a server if that's simple for you, and allow the user to connect out if you don't feel like upgrading later. This may require more in-depth UI work to let the user know what's going on.

Embedded Servers and Connectors

An Embedded server means both the client and server are part of the application are you building. While doing this ends up being more convenient for the user in some ways, as they have less setup to do and choices to make, there are a few drawbacks, including:

  • If the libraries upgrade (which is how we usually deal with new hardware/protocol support), you'll need to upgrade your app too.
  • This may tie you to a certain platform, i.e. if you're using Windows libraries, your application might only run on windows. This all depends on the library you're using, though.
  • You'll may to set up the server yourself in your application.

There's not really much to cover about the first two problems, they're just part of the choice you make in using this method. For now, we'll assume you'll want to set up a default Server with no Device Communication Managers, connection watchdogs, etc... We'll cover those options in the Winning Ways section.

This process is outlined in the code example below.

  • Rust
  • C#
  • Javascript
use buttplug::{
  client::ButtplugClient,
  connector::ButtplugInProcessClientConnector,
  server::{comm_managers::btleplug::BtlePlugCommunicationManager, ButtplugServerOptions},
};

#[allow(dead_code)]
async fn main_the_hard_way() -> anyhow::Result<()> {
  // First off, we'll set up our Embedded Connector.
  let connector = ButtplugInProcessClientConnector::default();
  // This is how we add Bluetooth manually. (We could also do this with any other communication manager.)
  connector
    .server_ref()
    .add_comm_manager::<BtlePlugCommunicationManager>()?;

  let client = ButtplugClient::new("Example Client");
  client.connect(connector).await?;

  Ok(())
}

#[tokio::main]
async fn main() -> anyhow::Result<()> {
  // This is the easy way, it sets up an embedded server with everything set up automatically
  let client = ButtplugClient::new("Example Client");
  client.connect_in_process(&ButtplugServerOptions::default()).await?;

  Ok(())
}

External Servers and Connectors

For using external servers, such as Websocket or IPC Servers, the process is slightly different. You'll need to provide the user a way to pass in the server address, then you just create the connector object using that address.

  • Rust
  • C#
  • Javascript
use buttplug::{
  client::ButtplugClient,
  connector::{ButtplugRemoteConnector, ButtplugWebsocketClientTransport},
  core::messages::serializer::ButtplugClientJSONSerializer,
};

#[tokio::main]
async fn main() -> anyhow::Result<()> {
  // To create a Websocket Connector, you need the websocket address and some generics fuckery.
  let connector: ButtplugRemoteConnector<_, ButtplugClientJSONSerializer, _, _> =
    ButtplugRemoteConnector::new(ButtplugWebsocketClientTransport::new_insecure_connector(
      "ws://localhost:12345/buttplug",
    ));

  let client = ButtplugClient::new("Example Client");
  client.connect(connector).await?;

  Ok(())
}