Connecting to a Buttplug Server
Once you've created a connector, it's time to connect to a server!
As all connector setup was done via the Connector setup, this is now just down to dealing with whether the connection process actually worked or not.
- For embedded connectors in the reference implementation, connection will always succeed, as long as the client and server are from the same library version (See below for issues involving compatibility). Hopefully, that is a situation that will rarely if ever happen.
- For external connectors, connections can fail due to usual connection issues (wrong address, server not up, network not on, etc...). There is also a chance that the client and server could have a version mismatch. We'll cover this in the next section.
- Rust
- C#
- Javascript
use buttplug::{
client::{ButtplugClient, ButtplugClientError},
core::{
connector::{ButtplugRemoteClientConnector, ButtplugWebsocketClientTransport, new_json_ws_client_connector},
errors::ButtplugError,
message::serializer::ButtplugClientJSONSerializer,
},
};
use tokio::io::{self, AsyncBufReadExt, BufReader};
async fn wait_for_input() {
BufReader::new(io::stdin())
.lines()
.next_line()
.await
.unwrap();
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
// After you've created a connector, the connection looks the same no
// matter what, though the errors thrown may be different.
let connector = new_json_ws_client_connector("ws://127.0.0.1:12345");
// Now we connect. If anything goes wrong here, we'll get an Err with either
//
// - A ButtplugClientConnectionError if there's a problem with
// the Connector, like the network address being wrong, server not
// being up, etc.
// - A ButtplugHandshakeError if there is a client/server version
// mismatch.
let client = ButtplugClient::new("Example Client");
if let Err(e) = client.connect(connector).await {
match e {
ButtplugClientError::ButtplugConnectorError(error) => {
// 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.
println!("Can't connect, exiting! Message: {}", error);
wait_for_input().await;
return Ok(());
}
ButtplugClientError::ButtplugError(error) => match error {
ButtplugError::ButtplugHandshakeError(error) => {
// This means our client is newer than our server, and we need to
// upgrade the server we're connecting to.
println!("Handshake issue, exiting! Message: {}", error);
wait_for_input().await;
return Ok(());
}
error => {
println!("Unexpected error type! {}", error);
wait_for_input().await;
return Ok(());
}
},
}
};
// We're connected, yay!
println!("Connected! Check Server for Client Name.");
wait_for_input().await;
// And now we disconnect as usual
client.disconnect().await?;
Ok(())
}
using System;
using System.Threading.Tasks;
using Buttplug.Core;
using Buttplug.Client;
namespace ConnectionExample
{
class Program
{
// Waits for the user to hit a key, so they can read whatever log
// messages might have been printed.
private static async Task WaitForKey()
{
Console.WriteLine("Press any key to continue.");
while (!Console.KeyAvailable)
{
await Task.Delay(1);
}
Console.ReadKey(true);
}
private static async Task RunExample()
{
// First, we create our Client
var client = new ButtplugClient("Example Client");
// After you've created a connector, the connection looks the same no
// matter what, though the errors thrown may be different.
var connector = new ButtplugWebsocketConnector(new Uri("ws://127.0.0.1:12345"));
// 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 (ButtplugClientConnectorException 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 void Main()
{
RunExample().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);
};
Client/Server Compatibility
To keep up with new hardware capabilities and the needs of users, the Buttplug protocol spec is versioned. Any time any message changes, the version number of the spec is incremented by 1. When the Connect function is called from a reference client, a "handshake" process occurs where the Client and Server trade their spec version with each other.
Maintaining backward compatibility with applications that may be abandoned (something that is rare in sex software) is a core tenant of Buttplug. Buttplug reference servers try to support all versions of the spec. Older clients should always be able to connect to newer servers.
If a client is running a newer version of the spec than the server, a connection error will be thrown, because otherwise the client can send messages that the server doesn't know what to do with. In this case, it is assumed that there will be a new version of the server library or software available that the client can upgrade to.
What to Expect on Successful Connect
Once you're connected, you really don't need to think about whether you're using an embedded or remote connector anymore. In most cases, connectors are only used for the initial connection setup, then you can pretty much forget about them after that. Everything will look exactly the same across all connector types from here on out.
So now that you know how to get a Buttplug session running, you're ready to enumerate and control devices!