Buttplug v6: It’s Like Firefox v4 For Your Butt

After nearly 9 months of development, Buttplug v6 is out today. If you’re curious what this does, or more likely, doesn’t mean for you, this post will lay out what’s in today’s release and what’s yet to come.

This release isn’t really everything I wanted it to be, but in the end it became obvious that the line of yaks requiring shaving to get this out in a “complete” state was going to stretch on for a few more months at minimum. Therefore, Buttplug v6 mostly contains the foundation for the new features I wanted in the new version of the library, and I’ll be building on that through point releases in the upcoming months to turn it into what I feel it’s supposed to be.

I’ll be covering a lot of these topics in their own blog posts over the next few weeks, but for now here’s a summary of what’s new and future plans.

If you just want the gritty details, check the changelogs of the spec and the library itself.

I’m a User/Dev, How Will This Affect Me?

Right now, users of software that employs buttplug and developers working on games/apps that use the library should not expect to see too much different. This release of Buttplug is mostly for the very few people who are not me that want to work on the Buttplug library itself.

I’ve been blocking almost all new features and fixes to the library since December 2021, in order to keep the development branch clean and easy to work with. Unfortunately the planning for v6 wasn’t great, so the features that caused the largest amount of chaos went in first, meaning that rebasing on top of v5 changes quickly became very difficult.

Getting v6 out in its current version releases this block, and allows device protocol developers to get back to actually implementing new protocols and bug fixes. While there may be new releases of Intiface that use Buttplug v6, many of the new features won’t be completely open to developers for at least a few more weeks at minimum (see the FFI section below for more info on this).

For those curious about the Firefox v4 reference in the title of this post: Firefox v4 development lasted way beyond what it was supposed to, due to many of the same issues I had here (check out this C|Net article for some history). Instead of learning from history, I repeated it. Hopefully I can avoid that in the future, though I’m not sure I can roll to a rapid release schedule for Buttplug since this is still basically a single developer project.

Buttplug Spec v3, ScalarCmd and SensorCmd

By far the biggest change in Buttplug v6 will be the release of the new Buttplug v3 Protocol Spec. The Buttplug Protocol is the low level communication specification between Buttplug Servers (which handle hardware connection and control) and Buttplug Clients (the side of the system that applications, games, and other pieces of software use to connect to Buttplug Servers). Very few people work with this level of the system, as it’s usually only seen by library developers writing clients for other platforms/languages. While these changes are opaque to users, the spec defines the capabilities and scalability of the system to new devices, making it the core of what we do as a project and how users finally get the new features they’re asking for.

The spec moves at a glacial pace, with this being the first major change in 2 years, with the last major being 3 years before that. The good news is that this means that what we have in the spec seems to work for a majority of our users at the moment, but what we had in v2 left us little room to expand for the future. Spec v3 aims to fix that with the addition of 2 new messages, as well as the deprecation of 5 messages that those 2 new messages can replace. There is also additional descriptive information available about devices.

The two new messages are:

  • ScalarCmd
    • Instead of just allowing a developer to say “make a device vibrate”, we can now let them say
      “sets a device actuator to a static level”. For instance, the Lovense Max has an air bladder
      that can inflate/deflate, constricting a certain amount. ScalarCmd can now be used to set that.
    • I’ll have a full blog post detailing the design of this message and our command system in
      general soon.
  • SensorCmd
    • Buttplug has had no capability to actually take input from devices so far. There are multiple
      intimate devices that can hand back certain readings, like button presses, accelerometer data,
      and pressure sensing. SensorCmd gives Buttplug a way to take this information in and relay it to developers so they can create more in-depth experiences for their users.

Initial support for these messages will slim, mostly replacing the older, more specific messages (like battery and RSSI) with the new more general ones. However, there are plans to release new versions of the library that will incorporate either new or missing features from hardware in the upcoming weeks.

Additionally, there will now be more descriptions of devices available. Users will now be able to
create their own names for devices (known as “Display Names”) which can be shown alongside or
instead of the device’s Base Name. There will also be text descriptions of devices features, so that
if a device has multiple vibrators or other actuators, users and developers can denote which
features maps to which actuator. There is currently no UI available for this functionality, but it will be added to Intiface Central at some point in the future. See the “Plans for the Future: Mobile” section below for more info.

Better User Device Configuration Capabilities

Thanks to an overhaul of the user device configuration system, users will now also be able to customize devices to their needs. This includes:

  • Reserving device indexes so the same device shows up at the same index every time a client
  • Setting the usable range for a device, like maximum speeds for vibrators or minimum/maximum stroke ranges for strokers.
  • Allow/deny lists for devices, so users can say exactly which devices will/won’t connect to
    Intiface/Buttplug. Great for making sure you aren’t controlling your neighbors vibrator!

As with device feature descriptions, there is currently no UI available for users to set these configuration values yet. See the “Plans for the Future: Mobile” section below for more info.

New Testing System

In “things only I care about but that make me really happy anyways so you get to hear about them too”, there’s a new test system! It uses a simple procedural YAML-based DSL for defining expected message flows. One of the biggest issues in Buttplug pre-v6 was that changing the device system required changing every protocol single test, and as we continue to amass protocol support, time to update kept getting worse and worse. This allows us to easily update the system and only update the test harness, leaving the test scripts as is.

This will also allow us to test backward compatibility, as Buttplug Servers are supposed to be able to talk to ALL versions of the Buttplug Client, so that we never drop support for software someone is currently fucking. This has… only been a half-truth for the past couple of years, as v0/v1 support has been iffy. Now we’ll be able to make sure every release that goes out supports every version.

… At least once the v0/v1 harnesses are written. Those are currently slated for a point release in v6. v2/v3 support are solid though!

Documentation or Lack Thereof

One of the biggest missing features of Buttplug v6 will be any documentation about Buttplug v6. I haven’t updated much of the API documentation, the Developer’s guide still refers to the v2 spec, etc… Right now the public API won’t look too much different, so developers certainly could start using Buttplug v6 with minimal changes, but using everything available like sensors, user configs, and other new features will definitely require more documentation at both the API and Guide levels. This is definitely in the works.

Plans for the Future: New FFI Strategy

Speaking of mistakes: FFI handling in Rust has been a big one!

This is another subject I’ll be expanding on in its own blog post, but for now I’ll just say that the situation for people using Buttplug outside of Rust sucks, and I’m aware (and extra sorry to Unity developers 😐 ).

Trying to extend the native library to be used on both the Client and Server sides has not panned out how I’d like. Buttplug really needs pure language-native Client implementations that don’t depend on Rust (the server will always live in Rust though), and I’ll be looking at getting the system switched back to that soon.

Plans for the Future: Mobile

And you know what also was a mistake? Working with Bluetooth on Desktop!

Ok so I didn’t have quite as much as a choice here but it’s still never exactly been fun.

A good 80% of our support issues these days are weird problems with Bluetooth APIs not working on one desktop platform or another. Either someone has the wrong bluetooth dongle, or it works with some toys but others, or whatever. It’s just a constant barrage of broken and it needs to stop. Sex Toy manufacturers seem to test solely on mobile, so it’s time to move Buttplug and Intiface to mobile too. Buttplug has already been compiled and tested on Android and iOS, so this is mostly a matter of getting UI and build systems together, which is still a tall order but the hard part is done.

Buttplug and Intiface will still support desktop too, but it’ll make life way easier for a lot of our users.

I think. Maybe.

Of course, design of this will get its own blog post too. For someone who usually writes about 1 blog post a year I’m signing myself up for a lot here.

Onward to v6.1

I’m not even considering what v7 is going to look like right now, but rather trying to keep focus on smaller iterations to make things work. Ideally we’ll get v6 documented and debugged, get mobile apps out, then I’m hoping to take some time to actually implement some applications/games with Buttplug after working pretty much solely on the library for over 5 years. Not completely confident that will hold, but I can dream, right?

Looking forward to see what everyone manages to do with the library once it’s usable. Until then, if you still have to bang your head against it while it’s in this new, raw state, please let me know how it goes for you.

Buttplug Rust v3.0 Released: Less is Less

When I released Buttplug Rust v1, I figured I’d be rolling major versions whenever we updated the Buttplug Protocol. Here we are, at major version 3.0, still running the same protocol, but with more surface API changes. So much for those plans.

Why v3.0?

Since the release of Buttplug Rust v1, I’ve been getting steady pings from game developers asking when the Buttplug Unity plugin would be updated, in order to support new hardware as well as IL2CPP compilation. I finally got time to start work on this project a couple of weeks ago. Unfortunately it didn’t get off to a great start.

Using the C# FFI to Buttplug Rust worked fine, everything basically compiled with no real changes outside of some module paths, which was great. However, the development cycle after that basically became unusable. We found we could run Unity in Play Mode once, but any subsequent run would stall.

There’s more info in the related bug, but long story short, Unity’s Mono implementation wasn’t able to properly shut down Buttplug Rust’s async handling runtime. This meant threads got left open, and when Mono tries to reset itself (as is common, to save Unity game developers from having to manage their own state resets), it’d just stall forever.

The fix to this was moving from our current runtime (async-std on top of smol) to Tokio. Tokio provided far more granular runtime lifetime handling and passing, meaning we can easily manage when we bring up and take down the system. We now don’t spin up any runtimes (which brings up a number of threads) until we need to, and we can tear it down once all of our devices and clients disappear.

With that work done, Unity seems to be pretty happy. It compiles Buttplug support to either Mono or IL2CPP, and the development loop seems stable. I also took some time to work more with the fantastic tracing crate to improve logging in the library, making it easier to follow device connection lifetimes. This work also made me realize that my “batteries included” philosophy for Buttplug Rust maybe included a few too many batteries.

What Got Removed

One of the original tenets of Buttplug followed my philosophy about sex toy design: that it should be as quick as possible to start using, and able to run basically standalone while also having the ability to extend where needed. Developers can just get the library and integrate it with their program, not having to worry about how devices connect, how IPC mechanisms works, or other boring details. This hopefully allows for developers to concentrate on implementing interesting things with sex toys.

Sometimes though, this gets overrun by me learning things while implementing the library, so we end up with more features than might really be needed, or that I can support. Optimizing the library for Unity brought up a few things that could be removed.

Removing async-std and ThreadPool Runtimes

Unlike many languages with async capabilities (C#, JS, etc…), Rust’s async execution system is designed in such a way that the language comes without a way to execute tasks. Rather, it depends on outside implementations that can tune to the specific requirements of an application. While this is overall a win for the ecosystem, it can sometimes cause frustration.

Supporting multiple runtimes in Rust is common, but usually for libraries that will have widespread use: network services, database connectors, etc… While there are certainly developers using Rust to develop Buttplug applications, they are a minority compared to developers using FFI libraries, where this runtime selection will be hidden.

Buttplug Rust as it exists now was started in September 2019, slightly before the release of async features in Rust 1.36. At the time, async-std was developing alongside the nightly branch of rust, and looked more like what the async ecosystem does now. Thanks to that, I ended up going with that for our initial execution system, and it has worked great up until our current issues with Unity.

This also functioned as a way for me to learn how to live as a library developer in the new Rust async ecosystem. This was a combination of interesting and frustrating, especially as requirements sometimes differed greatly between libraries.

With subsequent releases of Tokio over the past 18 months, it has grown to integrate with the async facilities of Rust in a more ergonomic way, while still providing more granular control (and the cost of some added complexity sometimes, i.e. runtime handle management). Thanks to this, it now fits Buttplug’s needs better, and comes stocked with some very useful sync primitives and channels, including Broadcast (which I use heavily to simulate the event systems we had in C#/Javascript).

On top of this, most of the async work we still need to happen (Windows Named Pipes, serial ports) is farther along in tokio/mio (as far as I’m aware, and it’s currently stalled), meaning we can just integrate with that when it comes along by using tokio as our main implementation.

After switching Buttplug to Tokio, it became apparent that maintaining 4 runtimes (tokio, async-std, futures crate ThreadPool, and wasm-bindgen) was silly, since I don’t really have users split across those (and many users may not even know why the runtimes are there). Some of our other dependencies like async-tungstenite could handle the differences, but the complexity of the feature system for Buttplug was getting out of hand, as was remembering where to put all of the relevant #[cfg()] calls. I’ve now removed async-std and ThreadPool implementations, leaving us with just Tokio and wasm-bindgen (required for WASM). Ideally it’d be great if we could use Tokio’s executor for everything, but that’s not quite possible as of yet. The current system is managed using an internal system similar to the async_executors crate, which works well enough for abstracting task systems.

This does not mean that Buttplug isn’t usable with other runtimes now. Outside of the Device Communication Managers (which, granted, are possibly the most important part of the library), most of the library is runtime agnostic, and async-std contains tokio compatibility systems that will allow the library to run using its runtime also. This change mostly reduces the amount of thinking I have to do when updating the library and its direct dependencies. This is open source self care more than anything.

Removing Secure Sockets for Buttplug Servers

When Buttplug C# first started in 2017, Chrome allowed for localhost websocket connections via mixed contexts (i.e. https:// website calling through ws:// for localhost websockets). Firefox did not have a feature like this, so we implemented the ability to create self signed certs and load them into the server to run a self signed websocket server for Buttplug. This allowed us to get applications working in Firefox.

Firefox changed this in 2020, now also allowing mixed context localhost connections. With this addition meaning that Chrome, Firefox, and Edge all supported mixed context connections, the need for including the batteries of a secure socket connection went away. Any users that require this feature can still set up a reverse proxy in front the server port, but managing the certificate generation and loading system is well out of scope of Buttplug’s core goals. Removing this will hopefully simplify changes to our connector system in the future, and also removes the burden of keeping cert libraries in lockstep.

For most of the talk of async in this post, the secure socket change is actually what drove the major version update. Removing secure sockets changes our Websocket connection API, which is exposed to developers, and therefore is a breaking change in the general public API. This change only happens in the Rust library, as the server is not exposed in the FFI, so while Rust moves to v3, the FFI APIs will continue on the v1 track.

What’s Next

Now that Buttplug v3 is done, there are quite a few releases to make on top of it:

  • Updating the C# and WASM FFIs, though only C# will see much of a change.
  • Finishing the Buttplug Unity update and releasing v1 of that package
  • Updating Intiface to remove the Secure Socket options

These updates will be happening throughout the next week.

After that, the main focus is going to be more documentation and the implementation of a WebRTC based proxy system. This stems from the rather startling discovery that Buttplug (the library) doesn’t work very well with buttplugs (the sex toy), which I’ll be making another post about soon.

With this proxy system (inspired by the work of xtoys.app), we can start using mobile hardware (phones, linux SoCs like RPis, etc) as connection points, giving users more freedom in where and how they use toys. Alongside this will be more hardware and QoL updates, but the goal for now is making our system usable to the wave of social VR users we’ve seen over the past few months.

Onto v4! But hopefully via many v3 point releases first.