Connectors
The first thing to do when writing a Buttplug application is figuring out how to talk to a Buttplug Server (like Intiface Central). For sake of simplicity, we'll cover websockets in this part of the manual, as this is by far the most common method of connecting clients and servers. Other connection situations and solutions (WebRTC, iroh, etc...) are covered in the cookbook section.
Websocket Connectors
Websockets are the default connector transport for Buttplug. They work as both a transport for desktop applications and web browsers, and have implementations available in most programming popular languages. As the library does not send many messages (maybe 50 per second in busy cases), the overhead of websockets isn't really an issue for the library.
Client implementations made by the Buttplug Core Team will provide a websocket connector for you. You should be able to create the connector, define the server address, and use that to connect. For using Websocket servers, you'll need to provide the user a way to pass in the server address (as this will not always exist on the same machine your software is running on), then you just create the connector object using that address.
When Buttplug's first public server came out in 2017, it used port 12345 as a test value. This stuck with the system, so now most applications that use Intiface or Buttplug use port 12345 for connection by default. Some client applications have gone as far as to hardcore the value, but this is not recommended.
This can sometimes be an issue for users running certain software that collides with the port. We have some recommended fixes in the Intiface Central Documentation.
- Rust
- C#
- Javascript
use buttplug::{
client::ButtplugClient,
core::{
connector::{ButtplugRemoteConnector, ButtplugWebsocketClientTransport, new_json_ws_client_connector},
message::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 = new_json_ws_client_connector("ws://192.168.123.103:12345/buttplug");
let client = ButtplugClient::new("Example Client");
client.connect(connector).await?;
Ok(())
}
using System;
using System.Threading.Tasks;
using Buttplug.Client;
namespace RemoteConnectorExample
{
class Program
{
static async Task ConnectWebsocket()
{
// Creating a Websocket Connector is as easy as using the right
// options object.
var connector = new ButtplugWebsocketConnector(
new Uri("ws://localhost:12345/buttplug"));
var client = new ButtplugClient("Example Client");
await client.ConnectAsync(connector);
}
static void Main(string[] args)
{
ConnectWebsocket().Wait();
}
}
}
// This example assumes Buttplug is brought in as a root namespace, via
// inclusion by a script tag, i.e.
//
// <script lang="javascript"
// src="https://cdn.jsdelivr.net/npm/buttplug@3.0.0/dist/web/buttplug.min.js">
// </script>
//
// If you're trying to load this, change the version to the latest available.
const runWebsocketConnectionExample = async () => {
// This is the default insecure address for Intiface Central (https://intiface.com/central). You
// can connect to it via most browsers.
let address = "ws://localhost:12345"
// After you've created a connector, the connection looks the same no matter what, though the
// errors thrown may be different.
let connector = new Buttplug.ButtplugBrowserWebsocketClientConnector(address);
let client = new Buttplug.ButtplugClient("Websocket Connection Example");
// 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.connect(connector);
}
catch (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.
//
// This could also mean our client is newer than our server, and we need to
// upgrade the server we're connecting to.
console.log(ex);
}
// We're connected, yay!
console.log("Connected!");
setTimeout(async () => {
// And now we disconnect as usual
console.log("Disconnecting");
await client.disconnect();
}, 3000);
};
Security Considerations
Due to basically being impossible to deal with, Intiface Engine/Central does not implement SSL websockets (wss). This required self-signed certificates which rarely worked correctly and caused end-user confusion. Non-secured websockets work for Intiface Central instances running on the same host as web apps, due to localhost security exceptions. Remote browser connections (i.e. browser on desktop, intiface central on phone) may fail due to security requirements. Intiface Central provides a Repeater Mode (basically a proxy) to work with instances where web browsers on a machine other than the Intiface Central is accessing hardware.