Skip to content
Closed

Dev #926

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
af7ed36
docs: add Task Scope and Task Registry domain terms
qdot Jun 10, 2026
1f465ca
fix: Reduce Satisfyer keepalive interval to keep devices connected
blackspherefollower Jun 8, 2026
da23c17
chore: Make the device tester rerun the tests after increasing long p…
blackspherefollower Jun 8, 2026
eb2c78f
chore: cargo fmt
blackspherefollower Jun 8, 2026
37be8e7
feat: Adding support for JoyHub MutantX and Marino
blackspherefollower Jun 13, 2026
0d9adce
feat: Adding support for JoyHub Jason
blackspherefollower Jun 13, 2026
2f4d54d
feat: Adding Lelo SURFER Originals identifier for Surfer 2
blackspherefollower Jun 15, 2026
0c75d5a
feat: Adding support for JoyHub Martino III
blackspherefollower Jun 17, 2026
f1cd85a
OSSM speed support through BLE
RubberyFun Jun 17, 2026
69fbd23
The future Marty!
RubberyFun Nov 26, 2025
94d3101
used the old TX
RubberyFun Dec 3, 2025
a8af984
fix: Correct the initialization of the OSSM
blackspherefollower May 7, 2026
fe1c7b7
feat: Add positional control to ossm
blackspherefollower May 7, 2026
79bdcac
feat: Set parameters after OSSM mode switch
blackspherefollower Jun 16, 2026
e06484b
feat: added BLE protocol for SVAKOM Klitty
SAT-oO Jun 17, 2026
7a4e4da
feat: remove handshake since its unmandatory at startup
SAT-oO Jun 17, 2026
9c84598
refactor: integrated klitty protocol to existing svakom v6 config
SAT-oO Jun 18, 2026
14e2fd7
chore: cleanup unresolved rebase
SAT-oO Jun 22, 2026
00f4a37
feat: Add F-Machine protocol implementation and configuration
penaltybush Mar 9, 2026
28c0ada
Only send commands to bring speed down to one. Zero is just off. Sett…
penaltybush Mar 10, 2026
7518836
Update F-Machine protocol to use buttplug_core::spawn.
penaltybush Jun 14, 2026
567c343
Add F-Machine Alpha configuration to device protocol
penaltybush Jun 14, 2026
2975fa7
Fix buttplug_core imports in FMachine protocol
penaltybush Jun 14, 2026
2fe8600
build: regenerate device configuration for F-Machine
qdot Jul 24, 2026
c10aa9a
Add vibio device protocol support
JJ-69 Mar 15, 2026
dad6e50
build: port Vibio support to current device configuration
qdot Jul 24, 2026
90169d1
feat: add Svakom Fatima Pro support
pktwhisperer Jun 18, 2026
6735b68
feat: New Svakom Emma Neo identifier
blackspherefollower Jul 2, 2026
92cf22c
feat: Adding new JoyHub devices
blackspherefollower Jul 2, 2026
d40d97a
feat: Adding Umove support
blackspherefollower Jul 3, 2026
6863fbc
feat: Adding support for Kiiroo Keon 2 and Spot 2
blackspherefollower Jul 3, 2026
9675afe
chore: clean up linter errors on ossm
blackspherefollower Jul 3, 2026
3b63396
feat: Adding support for Lovense Fizz
blackspherefollower Jul 22, 2026
df766a0
build: regenerate device configuration after protocol merge
qdot Jul 24, 2026
1b38dd7
chore: remove unused F-Machine state
qdot Jul 24, 2026
72d1c6d
feat(devices): add Yiciyuan YCY-FJB-01 / FJB-02 stroker support
SanJerry007 Jul 16, 2026
3b792da
build: regenerate device configuration for Yiciyuan
qdot Jul 24, 2026
9741fec
core: add owner-local task lifecycle
qdot Jul 28, 2026
6f4494c
fix(server): stop commands flush batches and resolve on hardware write
qdot Jul 28, 2026
cbc1b8d
server: own and join device manager tasks
qdot Jul 28, 2026
43458b7
server: own and join device tasks
qdot Jul 28, 2026
bb38c1c
server: preserve disconnect event ordering
qdot Jul 28, 2026
9ee921d
fix(server): replace ping timer drop-spawn with direct cancellation
qdot Jul 29, 2026
ef079cc
chore: apply nightly rustfmt to device module
qdot Jul 29, 2026
a70fafc
fix(server): suppress stale disconnect event on device index collision
qdot Jul 29, 2026
2f02f25
fix(server): restore stop command deduplication lost in stop-ack rework
qdot Jul 29, 2026
e833c6c
docs: cargo fmt must use nightly toolchain
qdot Jun 10, 2026
2445bd9
chore(deps): update workspace dependencies
qdot Jul 25, 2026
be4d24a
fix(server): migrate updated crypto APIs
qdot Jul 25, 2026
e8b7556
fix(engine): update build script for vergen 10
qdot Jul 25, 2026
409bb62
fix: preserve TCode position magnitude width
qdot Jul 26, 2026
05c944e
test(server): cover v1 device message serialization
qdot Jul 26, 2026
6ba7f46
feat(server): implement v4 disconnect message
qdot Jul 26, 2026
91b7c58
chore(release): prepare buttplug 11.0.0 and intiface engine 4.1.0
qdot Jul 29, 2026
3b30922
docs: replace task scope/registry domain terms with task group
qdot Jul 29, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@ cargo build # Debug build
cargo build --release # Release build (LTO enabled)
cargo test # Run all tests
cargo test -p buttplug_server # Run tests for specific crate
cargo fmt --all -- --check # Check formatting
cargo fmt # Auto-format (2-space indent, edition 2024)
cargo +nightly fmt --all -- --check # Check formatting (MUST use nightly)
cargo +nightly fmt # Auto-format (2-space indent, edition 2024)
```

**Formatting gotcha**: rustfmt.toml uses nightly-only options (`imports_layout`, `empty_item_single_line`). Running `cargo fmt` on the STABLE toolchain silently ignores them and rewrites the entire workspace into the wrong style (~190 files of import-collapsing churn). Always use `cargo +nightly fmt`. CI checks formatting with nightly.

**Linux dependencies**: `libudev-dev`, `libusb-1.0-0-dev` (for serial/HID support)

**WASM build**:
Expand Down
4 changes: 4 additions & 0 deletions CONTEXT.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,10 @@ _Avoid_: "Discovery" as a distinct stage from identification — they're the sam
An opt-in broadcast of every output command sent to a **Device** — device index, feature index, output type, and value. Used by frontends (e.g. Intiface Central) to visually display real-time device activity, verify hardware behaviour matches commands, and let developers see what *would* happen with simulated devices. Disabled by default to avoid overhead.
_Avoid_: Treating as internal-only debugging; it's a user-facing observability feature.

**Task Group**:
An owner-local collection of spawned async tasks. A component that spawns long-running tasks (server device manager, individual devices) holds a Task Group, spawns named tasks through it, and on shutdown cancels then joins every task it accepted. Shutdown is single-flight and repeatable: concurrent callers share one completion. There is no global registry — ownership and join guarantees are purely local to the owning component.
_Avoid_: "Task Scope" and "Task Registry" (a removed earlier design built around a process-global registry and hierarchical scope tree); inferring task cleanup from diagnostic state rather than joined completion.

**Command**:
A message from **Client** to **Server** requesting an action — controlling a device, starting a scan, requesting device lists. Always has a non-zero message ID; the server responds with an `Ok` or `Error` using the same ID.
_Avoid_: Referring to server-initiated messages as commands.
Expand Down
6 changes: 6 additions & 0 deletions crates/buttplug/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
# 11.0.0 (2026-07-28)

## Other

- Update buttplug crates to 11.0.0

# 10.0.3 (2026-05-31)

## Features
Expand Down
6 changes: 3 additions & 3 deletions crates/buttplug/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "buttplug"
version = "10.0.3"
version = "11.0.0"

authors = ["Nonpolynomial Labs, LLC <kyle@nonpolynomial.com>"]
description = "Buttplug Intimate Hardware Control Library - Client Library"
Expand All @@ -20,5 +20,5 @@ doctest = true
doc = true

[dependencies]
buttplug_client = { version = "10.0.3", path = "../buttplug_client" }
buttplug_transport_websocket_tungstenite = { version = "10.0.3", path = "../buttplug_transport_websocket_tungstenite"}
buttplug_client = { version = "11.0.0", path = "../buttplug_client" }
buttplug_transport_websocket_tungstenite = { version = "11.0.0", path = "../buttplug_transport_websocket_tungstenite"}
6 changes: 6 additions & 0 deletions crates/buttplug_client/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
# 11.0.0 (2026-07-28)

## Other

- Update buttplug crates to 11.0.0

# 10.0.3 (2026-05-31)

## Features
Expand Down
22 changes: 11 additions & 11 deletions crates/buttplug_client/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "buttplug_client"
version = "10.0.3"
version = "11.0.0"
authors = ["Nonpolynomial Labs, LLC <kyle@nonpolynomial.com>"]
description = "Buttplug Intimate Hardware Control Library - Core Library"
license = "BSD-3-Clause"
Expand All @@ -24,16 +24,16 @@ tokio-runtime = ["buttplug_core/tokio-runtime"]
wasm = ["buttplug_core/wasm"]

[dependencies]
buttplug_core = { version = "10.0.3", path = "../buttplug_core", default-features = false }
futures = "0.3.32"
thiserror = "2.0.18"
log = "0.4.29"
getset = "0.1.6"
tokio = { version = "1.50.0", features = ["macros"] }
dashmap = { version = "6.1.0" }
buttplug_core = { version = "11.0.0", path = "../buttplug_core", default-features = false }
futures = "0.3.33"
thiserror = "2.0.19"
log = "0.4.33"
getset = "0.1.7"
tokio = { version = "1.53.1", features = ["macros"] }
dashmap = { version = "6.2.1" }
tracing = "0.1.44"
serde = { version = "1.0.228", features = ["derive"] }
serde_json = "1.0.149"
jsonschema = { version = "0.45.0", default-features = false }
serde = { version = "1.0.229", features = ["derive"] }
serde_json = "1.0.151"
jsonschema = { version = "0.49.1", default-features = false }
strum = "0.28.0"
strum_macros = "0.28.0"
6 changes: 6 additions & 0 deletions crates/buttplug_client_in_process/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
# 11.0.0 (2026-07-28)

## Other

- Update buttplug crates to 11.0.0

# 10.0.4 (2026-06-01)

## Features
Expand Down
38 changes: 19 additions & 19 deletions crates/buttplug_client_in_process/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "buttplug_client_in_process"
version = "10.0.4"
version = "11.0.0"
authors = ["Nonpolynomial Labs, LLC <kyle@nonpolynomial.com>"]
description = "Buttplug Intimate Hardware Control Library - Core Library"
license = "BSD-3-Clause"
Expand Down Expand Up @@ -32,22 +32,22 @@ tokio-runtime = ["buttplug_core/tokio-runtime", "buttplug_client/tokio-runtime",
wasm = ["buttplug_core/wasm", "buttplug_client/wasm", "buttplug_server/wasm"]

[dependencies]
buttplug_core = { version = "10.0.3", path = "../buttplug_core", default-features = false }
buttplug_client = { version = "10.0.3", path = "../buttplug_client", default-features = false }
buttplug_server = { version = "10.0.4", path = "../buttplug_server", default-features = false }
buttplug_server_device_config = { version = "10.1.1", path = "../buttplug_server_device_config" }
buttplug_server_hwmgr_btleplug = { version = "10.0.4", path = "../buttplug_server_hwmgr_btleplug", optional = true}
buttplug_server_hwmgr_hid = { version = "10.0.4", path = "../buttplug_server_hwmgr_hid", optional = true}
buttplug_server_hwmgr_lovense_connect = { version = "10.0.4", path = "../buttplug_server_hwmgr_lovense_connect", optional = true}
buttplug_server_hwmgr_lovense_dongle = { version = "10.0.4", path = "../buttplug_server_hwmgr_lovense_dongle", optional = true}
buttplug_server_hwmgr_serial = { version = "10.0.4", path = "../buttplug_server_hwmgr_serial", optional = true}
buttplug_server_hwmgr_websocket = { version = "10.0.4", path = "../buttplug_server_hwmgr_websocket", optional = true}
buttplug_server_hwmgr_xinput = { version = "10.0.4", path = "../buttplug_server_hwmgr_xinput", optional = true}
futures = "0.3.32"
futures-util = "0.3.32"
thiserror = "2.0.18"
log = "0.4.29"
getset = "0.1.6"
tokio = { version = "1.50.0", features = ["macros"] }
dashmap = { version = "6.1.0" }
buttplug_core = { version = "11.0.0", path = "../buttplug_core", default-features = false }
buttplug_client = { version = "11.0.0", path = "../buttplug_client", default-features = false }
buttplug_server = { version = "11.0.0", path = "../buttplug_server", default-features = false }
buttplug_server_device_config = { version = "11.0.0", path = "../buttplug_server_device_config" }
buttplug_server_hwmgr_btleplug = { version = "11.0.0", path = "../buttplug_server_hwmgr_btleplug", optional = true}
buttplug_server_hwmgr_hid = { version = "11.0.0", path = "../buttplug_server_hwmgr_hid", optional = true}
buttplug_server_hwmgr_lovense_connect = { version = "11.0.0", path = "../buttplug_server_hwmgr_lovense_connect", optional = true}
buttplug_server_hwmgr_lovense_dongle = { version = "11.0.0", path = "../buttplug_server_hwmgr_lovense_dongle", optional = true}
buttplug_server_hwmgr_serial = { version = "11.0.0", path = "../buttplug_server_hwmgr_serial", optional = true}
buttplug_server_hwmgr_websocket = { version = "11.0.0", path = "../buttplug_server_hwmgr_websocket", optional = true}
buttplug_server_hwmgr_xinput = { version = "11.0.0", path = "../buttplug_server_hwmgr_xinput", optional = true}
futures = "0.3.33"
futures-util = "0.3.33"
thiserror = "2.0.19"
log = "0.4.33"
getset = "0.1.7"
tokio = { version = "1.53.1", features = ["macros"] }
dashmap = { version = "6.2.1" }
tracing = "0.1.44"
11 changes: 11 additions & 0 deletions crates/buttplug_core/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
# 11.0.0 (2026-07-28)

## Breaking Changes

- `AsyncManager::spawn` now returns a `TaskCompletion` handle instead of discarding the runtime join handle. Custom `AsyncManager` implementations must return a completion handle after accepting a task, and must not synchronously drive the submitted future during submission. The `spawn!` macro remains fire-and-forget by explicitly detaching its completion.

## Features

- Add owner-local named `TaskGroup`: atomic close/spawn reservation, coordinated cancellation, panic-safe join, and shared repeatable shutdown
- Add the v4 `Disconnect` client message

# 10.0.3 (2026-05-31)

## Features
Expand Down
36 changes: 19 additions & 17 deletions crates/buttplug_core/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "buttplug_core"
version = "10.0.3"
version = "11.0.0"
authors = ["Nonpolynomial Labs, LLC <kyle@nonpolynomial.com>"]
description = "Buttplug Intimate Hardware Control Library - Core Library"
license = "BSD-3-Clause"
Expand Down Expand Up @@ -30,30 +30,32 @@ targets = []
features = ["default", "unstable"]

[build-dependencies]
serde = "1.0.228"
serde_json = "1.0.149"
jsonschema = { version = "0.45.0", default-features = false }
serde = "1.0.229"
serde_json = "1.0.151"
jsonschema = { version = "0.49.1", default-features = false }

[dependencies]
futures = "0.3.32"
futures-util = "0.3.32"
serde = { version = "1.0.228", features = ["derive"] }
serde_json = "1.0.149"
serde_repr = "0.1.20"
thiserror = "2.0.18"
displaydoc = "0.2.5"
log = "0.4.29"
getset = "0.1.6"
jsonschema = { version = "0.45.0", default-features = false }
futures = "0.3.33"
futures-util = "0.3.33"
serde = { version = "1.0.229", features = ["derive"] }
serde_json = "1.0.151"
serde_repr = "0.1.21"
thiserror = "2.0.19"
displaydoc = "0.2.6"
log = "0.4.33"
getset = "0.1.7"
jsonschema = { version = "0.49.1", default-features = false }
cfg-if = "1.0.4"
tokio = { version = "1.50.0", features = ["sync", "macros"] }
tokio = { version = "1.53.1", features = ["sync", "macros"] }
async-stream = "0.3.6"
strum_macros = "0.28.0"
strum = "0.28.0"
derive_builder = "0.20.2"
enum_dispatch = "0.3"
tracing = "0.1.44"
wasm-bindgen-futures = { version = "0.4.64", optional = true }
wasm-bindgen-futures = { version = "0.4.76", optional = true }
wasmtimer = { version = "0.4.3", optional = true }
smallvec = { version = "1.15.1", features = ["serde", "const_generics"] }
smallvec = { version = "1.15.2", features = ["serde", "const_generics"] }
enumflags2 = "0.7.12"
tokio-util = "0.7.19"
dashmap = "6.2.1"
4 changes: 3 additions & 1 deletion crates/buttplug_core/src/connector/transport/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@
pub mod stream;

use crate::connector::{
ButtplugConnectorError, ButtplugConnectorResultFuture, ButtplugSerializedMessage,
ButtplugConnectorError,
ButtplugConnectorResultFuture,
ButtplugSerializedMessage,
};
use displaydoc::Display;
use futures::future::BoxFuture;
Expand Down
4 changes: 3 additions & 1 deletion crates/buttplug_core/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,9 @@ pub enum ButtplugHandshakeError {
#[error("Expected either a ServerInfo or Error message, received {0}")]
UnexpectedHandshakeMessageReceived(String),
/// Expected a RequestServerInfo message to start connection.
#[error("Expected a RequestServerInfo message to start connection. Message either not received or wrong message received.")]
#[error(
"Expected a RequestServerInfo message to start connection. Message either not received or wrong message received."
)]
RequestServerInfoExpected,
/// Handshake already happened, cannot run handshake again.
#[error("Handshake already happened, cannot run handshake again.")]
Expand Down
47 changes: 47 additions & 0 deletions crates/buttplug_core/src/message/v4/disconnect.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Buttplug Rust Source Code File - See https://buttplug.io for more info.
//
// Copyright 2016-2026 Nonpolynomial Labs LLC. All rights reserved.
//
// Licensed under the BSD 3-Clause license. See LICENSE file in the project root
// for full license information.

use crate::message::simple_client_message;

simple_client_message!(DisconnectV4);

#[cfg(test)]
mod test {
use super::DisconnectV4;
use crate::message::{ButtplugClientMessageV4, ButtplugMessage, ButtplugMessageValidator};

const DISCONNECT_STR: &str = "{\"Disconnect\":{\"Id\":42}}";

#[test]
fn test_disconnect_serialize() {
let mut msg = DisconnectV4::default();
msg.set_id(42);
let wrapped = ButtplugClientMessageV4::Disconnect(msg);
let js = serde_json::to_string(&wrapped).expect("Infallible serialization");
assert_eq!(DISCONNECT_STR, js);
}

#[test]
fn test_disconnect_deserialize() {
let wrapped: ButtplugClientMessageV4 =
serde_json::from_str(DISCONNECT_STR).expect("Valid JSON");
match wrapped {
ButtplugClientMessageV4::Disconnect(msg) => {
assert_eq!(msg.id(), 42);
assert!(msg.is_valid().is_ok());
}
_ => panic!("Expected Disconnect variant"),
}
}

#[test]
fn test_disconnect_rejects_system_id() {
let mut msg = DisconnectV4::default();
msg.set_id(0);
assert!(msg.is_valid().is_err());
}
}
2 changes: 2 additions & 0 deletions crates/buttplug_core/src/message/v4/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

mod device_list;
mod device_message_info;
mod disconnect;
mod input_cmd;
mod input_reading;
mod output_cmd;
Expand All @@ -18,6 +19,7 @@ mod stop_cmd;
pub use {
device_list::DeviceListV4,
device_message_info::DeviceMessageInfoV4,
disconnect::DisconnectV4,
input_cmd::{InputCmdV4, InputCommandType},
input_reading::{InputReadingV4, InputTypeReading, InputValue},
output_cmd::{OutputCmdV4, OutputCommand, OutputHwPositionWithDuration, OutputValue},
Expand Down
3 changes: 3 additions & 0 deletions crates/buttplug_core/src/message/v4/spec_enums.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use crate::message::{
StartScanningV0,
StopCmdV4,
StopScanningV0,
v4::disconnect::DisconnectV4,
v4::input_cmd::InputCmdV4,
};
use enum_dispatch::enum_dispatch;
Expand All @@ -40,6 +41,8 @@ pub enum ButtplugClientMessageV4 {
StopCmd(StopCmdV4),
OutputCmd(OutputCmdV4),
InputCmd(InputCmdV4),
// Connection lifecycle
Disconnect(DisconnectV4),
}

impl ButtplugMessageFinalizer for ButtplugClientMessageV4 {
Expand Down
Loading
Loading